DIRECTORY
BasicTime USING[ToNSTime],
CardTab USING[Create, Fetch, Insert, Ref, Store],
Commander USING[CommandProc, Register],
CommandTool USING[ArgumentVector, Parse],
Convert USING[RopeFromCard],
ConvertUnsafe USING[SubString, SubStringToRope],
CountedVM USING[Handle, SimpleAllocate],
IO USING[card, GetLength, PutF, PutFR, PutRope, rope, RopeFromROS, ROS, SetIndex, STREAM, UnsafeGetBlock, Value],
MobAccess,
MobDefs USING[Base, FTSelf, MobBase, MTIndex, NullVersion, SGHandle, SGNull, VersionID, VersionStamp],
MobMapper USING[AlterMob],
MobUtilDefs USING[MobHandle, MobObject],
PBasics USING[BITLSHIFT, BITRSHIFT, BITXOR, LongNumber],
PFS USING [UniqueID, PathFromRope, RopeFromPath],
PFSNames USING [PATH],
RefTab USING[Create, Fetch, Key, Insert, Ref],
Rope USING[Cat, Equal, ROPE],
Symbols,
SymbolSegment USING[Base, biases, bodyType, ctxType, ExtFirst, extType, FGHeader, htType, ltType, mdType, seType, ssType, stType, STHeader, treeType, VersionID],
SymbolTablePrivate USING[SymbolTableBaseRep],
SystemInterface USING[FileSet, CirioFile, GetStreamForFile, ReleaseStreamForFile, GetFileInfo, CirioFileInfo, CreateFileSet, GetCirioFile, CloseFileSet, GetNameOfFile],
VM USING[PagesForBytes, WordsForPages];
MobAccessImpl:
CEDAR
PROGRAM
IMPORTS BasicTime, CardTab, Commander, CommandTool, Convert, ConvertUnsafe, CountedVM,
IO, MobMapper, PBasics,
PFS, RefTab, Rope, SystemInterface,
VM
EXPORTS MobAccess =
BEGIN
MobError: PUBLIC ERROR[msg: Rope.ROPE] = CODE;
Note: one can learn a lot by looking at [PCedar2.0]<Mimosa>MobMapperImpl.mesa
MobCookies
This implementation holds them open forever. We probably want to do better. In principle, they need only be in memory during an ExamineMob call.
MobCookie: TYPE = REF MobCookieBody;
MobCookieBody:
PUBLIC
TYPE =
RECORD[
file: SystemInterface.CirioFile,
the following fields are valid only when the mob is in memory
mobHandle: MobUtilDefs.MobHandle, -- contains some of the table bases
mobBase: MobDefs.MobBase,
sym: SymbolTableBase,
the following values are used for correcting coded index values that appear in DotO files
leftShift: CARD,
rightShift: CARD,
the following value is used to accelerate validity checks on coded btis.
recentBtiParent: Symbols.BTIndex ← Symbols.BTFirst,
the following fields are valid at all times
seiTable: RefTab.Ref,
ctxTable: RefTab.Ref,
btTable: RefTab.Ref,
mdTable: CardTab.Ref,
stringTable: CardTab.Ref,
lostSeis: CardTab.Ref
];
GetFileForMobCookie:
PUBLIC
PROC [mc: MobCookie]
RETURNS [SystemInterface.CirioFile]
= {RETURN [mc.file]};
CreateMobCookie:
PUBLIC
PROC[f: SystemInterface.CirioFile]
RETURNS[MobCookie] = {
newCookie: MobCookie ← NEW[MobCookieBody];
stream: IO.STREAM ← SystemInterface.GetStreamForFile[f];
ctxh: CTXH; ctxr: CTXR; seh: SEH;
BEGIN ENABLE UNWIND => SystemInterface.ReleaseStreamForFile[f, stream];
newCookie.file ← f;
newCookie.seiTable ← RefTab.Create[equal: SEIKeyEqualProc, hash: SEIKeyHashProc];
newCookie.ctxTable ← RefTab.Create[equal: CTXKeyEqualProc, hash: CTXKeyHashProc];
newCookie.btTable ← RefTab.Create[equal: BTKeyEqualProc, hash: BTKeyHashProc];
newCookie.mdTable ← CardTab.Create[];
newCookie.stringTable ← CardTab.Create[];
newCookie.lostSeis ← CardTab.Create[];
the following could be delayed until an ExamineMob call
but it must be protected by an
UNWIND => SystemInterface.ReleaseStreamForFile[f, stream]
TRUSTED {
[newCookie.mobHandle, newCookie.mobBase] ← ReadMob[stream]};
TRUSTED{newCookie.sym ← GetSymbols[newCookie.mobBase];
newCookie.leftShift ←
SELECT
TRUE
FROM
BITS[UNIT] >= newCookie.mobBase.format.bitsPerUnit[0] => 0,
BITS[UNIT] = 8 AND newCookie.mobBase.format.bitsPerUnit[0] = 16 => 1,
ENDCASE => ERROR MobError["can't adjust mob"];
newCookie.rightShift ←
SELECT
TRUE
FROM
BITS[UNIT] <= newCookie.mobBase.format.bitsPerUnit[0] => 0,
BITS[UNIT] = 16 AND newCookie.mobBase.format.bitsPerUnit[0] = 8 => 1,
ENDCASE => ERROR MobError["can't adjust mob"]};
END;
SystemInterface.ReleaseStreamForFile[f, stream];
ctxh ← ReallyMakeCTXH[newCookie, Symbols.CTXNull];
ctxr ← FetchCTXR[ctxh];
IF ctxr#NIL THEN seh ← ctxr.seList;
WHILE seh#
NIL
DO
ser: SER ~ FetchSER[seh];
WITH ser.body
SELECT
FROM
idser:
REF id BodySE => {
IF idser.idCtx=
NIL
OR idser.idCtx=ctxh
THEN [] ← newCookie.lostSeis.Store[LOOPHOLE[seh.sei], $T];
seh ← idser.ctxLink};
ENDCASE => NULL;
ENDLOOP;
RETURN[newCookie]};
IsLost:
PUBLIC
PROC[seh:
SEH]
RETURNS[
BOOL]
~ {RETURN [seh.mob.lostSeis.Fetch[LOOPHOLE[seh.sei]].found]};
ReadMobVersionStamp:
PUBLIC
PROC[mob: MobCookie]
RETURNS[MobDefs.VersionStamp] =
BEGIN
versionStamp: MobDefs.VersionStamp ← ALL[0];
LookForVersionStamp:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
{versionStamp ← mobBase.version};
ExamineMob[mob, LookForVersionStamp];
RETURN[versionStamp];
END;
ReadSourceVersionStamp:
PUBLIC
PROC[mob: MobCookie]
RETURNS[MobDefs.VersionStamp] =
BEGIN
versionStamp: MobDefs.VersionStamp ← ALL[0];
LookForSourceVersionStamp:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
{versionStamp ← mobBase.sourceVersion};
ExamineMob[mob, LookForSourceVersionStamp];
RETURN[versionStamp];
END;
for the convenience of clients
this is adapted from [PCedar2.0]<MakeDo>CcDeps.mesa MobStampFromTime
ComputeSourceVersionStamp:
PUBLIC
PROC[mesaSourceFile: SystemInterface.CirioFile]
RETURNS[MobDefs.VersionStamp] =
BEGIN
fileInfo: SystemInterface.CirioFileInfo ← SystemInterface.GetFileInfo[mesaSourceFile];
vs: MobDefs.VersionStamp ← MobDefs.NullVersion;
vs[0] ← BasicTime.ToNSTime[fileInfo.uniqueID.egmt.time];
RETURN[vs];
END;
ExamineMob:
PROC[cookie: MobCookie, examiner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase]] =
{examiner[cookie.mobHandle, cookie.mobBase, cookie.sym]};
VSKeyEqualProc:
PROC[key1, key2: RefTab.Key]
RETURNS[
BOOL] =
BEGIN
vs1AsKey: REF MobDefs.VersionStamp ← NARROW[key1];
vs2AsKey: REF MobDefs.VersionStamp ← NARROW[key2];
RETURN[vs1AsKey^ = vs2AsKey^];
VSKeyHashProc:
PROC[key: RefTab.Key]
RETURNS[
CARDINAL] =
TRUSTED
BEGIN
vsAsKey: REF MobDefs.VersionStamp ← NARROW[key];
word0: PBasics.LongNumber ← LOOPHOLE[vsAsKey^[0]];
word1: PBasics.LongNumber ← LOOPHOLE[vsAsKey^[1]];
RETURN[PBasics.BITXOR[PBasics.BITXOR[word0.lo, word0.hi], PBasics.BITXOR[word1.lo, word1.hi]]];
END;
The following is copied from [PCedar1.2]<Cinder>MobUtilities.mesa
bytesPerWord: CARD = BYTES[WORD];
bytesPerVMWord: CARD = BYTES[WORD];
ReadMob:
PROC [stream:
IO.
STREAM]
RETURNS [h: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase] =
TRUSTED {
bytes: INT ← IO.GetLength[stream];
allocatedBytes: CARD ← VM.WordsForPages[VM.PagesForBytes[bytes]] * bytesPerVMWord;
vmh: CountedVM.Handle;
IF bytes = 0 THEN ERROR MobError["empty mob file"];
vmh ← CountedVM.SimpleAllocate[allocatedBytes/bytesPerWord];
PBasics.Fill[where: vmh.pointer, nWords: allocatedWord32s, value: CARD.LAST];
IO.SetIndex[stream, 0];
[] ←
IO.UnsafeGetBlock[
self: stream,
block: [base: vmh.pointer, startIndex: 0, count: bytes]];
mobBase ← LOOPHOLE[vmh.pointer];
IF MobMapper.AlterMob[mobBase,
LOOPHOLE[mobBase], bytes/
BYTES[
UNIT]] = badVersion
THEN
ERROR MobError["MobMapper.AlterMob failed in MobUtilities.ReadMob"];
IF mobBase.versionIdent # MobDefs.VersionID
THEN MobError["not a MOB file"];
actually, MobMapper.AlterMob will already have made the above check, so we should never fail.
h ←
NEW[MobUtilDefs.MobObject ← [
bHeader: mobBase,
countedVMHandle: vmh,
bases: [
ctb: LOOPHOLE[mobBase + mobBase.ctOffset.units],
mtb: LOOPHOLE[mobBase + mobBase.mtOffset.units],
lfb: LOOPHOLE[mobBase + mobBase.lfOffset.units],
rfb: LOOPHOLE[mobBase + mobBase.rfOffset.units],
tfb: LOOPHOLE[mobBase + mobBase.tfOffset.units],
etb: LOOPHOLE[mobBase + mobBase.expOffset.units],
itb: LOOPHOLE[mobBase + mobBase.impOffset.units],
sgb: LOOPHOLE[mobBase + mobBase.sgOffset.units],
ftb: LOOPHOLE[mobBase + mobBase.ftOffset.units],
ssb: LOOPHOLE[mobBase + mobBase.ssOffset.units],
evb: LOOPHOLE[mobBase + mobBase.evOffset.units],
tyb: LOOPHOLE[mobBase + mobBase.typOffset.units],
tmb: LOOPHOLE[mobBase + mobBase.tmOffset.units],
ntb: LOOPHOLE[mobBase + mobBase.ntOffset.units],
spb: LOOPHOLE[mobBase + mobBase.spOffset.units],
fpb: LOOPHOLE[mobBase + mobBase.fpOffset.units]
],
limits: [
ct: mobBase.ctLimit,
mt: mobBase.mtLimit,
et: mobBase.expLimit,
it: mobBase.impLimit,
sg: mobBase.sgLimit,
ft: mobBase.ftLimit,
tm: mobBase.tmLimit,
nt: mobBase.ntLimit,
sp: mobBase.spLimit,
fp: mobBase.fpLimit]
]];
};
Following is adapted from [PCedar1.2]<XLister>KLister.mesa ProcessSymbols
SymbolTableBase: TYPE = REF SymbolTableBaseRep;
SymbolTableBaseRep: TYPE = SymbolTablePrivate.SymbolTableBaseRep;
GetSymbols:
PROC[mob: MobDefs.MobBase]
RETURNS[SymbolTableBase] =
TRUSTED
BEGIN
sgb: MobDefs.Base = LOOPHOLE[mob+mob.sgOffset.units];
mtb: MobDefs.Base = LOOPHOLE[mob+mob.mtOffset.units];
sgh: MobDefs.SGHandle =
IF mtb[
FIRST[MobDefs.MTIndex]].sseg = MobDefs.SGNull
THEN ERROR MobError["NoSymbols"]
ELSE @sgb[mtb[FIRST[MobDefs.MTIndex]].sseg];
stb: LONG POINTER TO SymbolSegment.STHeader = LOOPHOLE[mob+sgh.base.units];
IF mob.nConfigs # 0 THEN ERROR MobError["Can't get symbols from a config"];
IF sgh.file # MobDefs.FTSelf OR sgh.units.units = 0 THEN ERROR MobError["funny looking mob"]; --NoSymbols;
IF stb.versionIdent # SymbolSegment.VersionID THEN ERROR MobError["invalid-looking mob"]; -- WrongSymbolsVersion;
RETURN[InstallSymbolTable[stb]]
END;
InstallSymbolTable:
PROC [node:
LONG
POINTER]
RETURNS [SymbolTableBase] =
TRUSTED {
b: LONG POINTER = node;
tB: SymbolSegment.Base = LOOPHOLE[b];
p: LONG POINTER TO SymbolSegment.STHeader = b;
base: SymbolTableBase ← NEW[SymbolTableBaseRep];
base.cacheInfo ← LOOPHOLE[node];
base.hashVec ← b+p.hvBlock.offset;
base.htb ← tB + p.htBlock.offset - SymbolSegment.biases[SymbolSegment.htType];
base.ssb ← b + p.ssBlock.offset - SymbolSegment.biases[SymbolSegment.ssType];
base.seb ← tB + p.seBlock.offset - SymbolSegment.biases[SymbolSegment.seType];
base.ctxb ← tB + p.ctxBlock.offset - SymbolSegment.biases[SymbolSegment.ctxType];
base.mdb ← tB + p.mdBlock.offset - SymbolSegment.biases[SymbolSegment.mdType];
base.bb ← tB + p.bodyBlock.offset - SymbolSegment.biases[SymbolSegment.bodyType];
base.tb ← tB + p.treeBlock.offset - SymbolSegment.biases[SymbolSegment.treeType];
base.ltb ← tB + p.litBlock.offset - SymbolSegment.biases[SymbolSegment.ltType];
base.stb ← tB + p.sLitBlock.offset - SymbolSegment.biases[SymbolSegment.stType];
base.extb ← tB + p.extBlock.offset - SymbolSegment.biases[SymbolSegment.extType];
base.mdLimit ← Symbols.MDFirst + p.mdBlock.size;
base.extLimit ← SymbolSegment.ExtFirst + p.extBlock.size;
base.mainCtx ← p.outerCtx; base.stHandle ← p;
IF p.fgRelBase = 0
OR p.fgCount = 0
THEN {
base.sourceFile ← NIL;
base.fgTable ← NIL;
}
ELSE {
q: LONG POINTER TO SymbolSegment.FGHeader = LOOPHOLE[b + p.fgRelBase];
source: LONG STRING = LOOPHOLE[q + SIZE[SymbolSegment.FGHeader[0]]
- SIZE[StringBody[0]]];
base.sourceFile ← source;
base.fgTable ← DESCRIPTOR[q + q.offset, q.length];
};
RETURN [base];
};
Body Tables
BTR: TYPE = MobAccess.BTR;
BTRBody: TYPE = MobAccess.BTRBody;
BodyLinkRcd: TYPE = MobAccess.BodyLinkRcd;
BTH: TYPE = REF BTHBody;
BTHBody:
PUBLIC
TYPE =
RECORD[
mob: MobCookie,
bti: Symbols.BTIndex,
btr: BTR];
BTHDetails:
PUBLIC
PROC [bth:
BTH]
RETURNS [mob: MobCookie, bti: Symbols.BTIndex]
~ {RETURN [bth.mob, bth.bti]};
MakeBTH:
PUBLIC
PROC[mob: MobCookie, bti: Symbols.BTIndex]
RETURNS[
BTH] =
BEGIN
IF bti = Symbols.BTNull THEN RETURN[NIL]
ELSE
BEGIN
btiAsKey: REF Symbols.BTIndex ← NEW[Symbols.BTIndex ← bti];
bth: BTH ← NARROW[RefTab.Fetch[mob.btTable, btiAsKey].val];
IF bth # NIL THEN RETURN[bth];
bth ← NEW[BTHBody←[mob, bti, NIL]];
IF NOT RefTab.Insert[mob.btTable, btiAsKey, bth] THEN ERROR MobError["MobAccessImpl confused (by concurrent access?)"];
RETURN[bth]
END
END;
See numerous comments on MakeSEHFromCodedSei. They apply here with slight variations.
MakeBTHFromCodedBti:
PUBLIC
PROC[mob: MobCookie, codedBti:
CARD]
RETURNS[
BTH] =
BEGIN
tentativeResult: BTH ← NIL;
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
shiftedBti:
CARD ←
SELECT
TRUE
FROM
mob.leftShift # 0 => PBasics.BITLSHIFT[codedBti, mob.leftShift],
mob.rightShift # 0 => PBasics.BITRSHIFT[codedBti, mob.rightShift],
ENDCASE => codedBti;
tentativeBti: Symbols.BTIndex ←
LOOPHOLE[shiftedBti];
This Loophole is effectively what SymbolOpsExtras.CBTIndexFromCard does.
tentativeBtiAsKey: REF Symbols.BTIndex ← NEW[Symbols.BTIndex ← tentativeBti];
tentativeResult ← NARROW[RefTab.Fetch[mob.btTable, tentativeBtiAsKey].val];
IF tentativeResult =
NIL
THEN
-- we have to build it from scratch
BEGIN
[] ← CheckTentativeBti[tentativeBti, mob, sym, 0
! BadTentativeBti => {GOTO failure}];
tentativeResult ← MakeBTH[mob, tentativeBti];
EXITS
failure => NULL; -- thus, leaving result = NIL
END;
END;
ExamineMob[mob, Inner];
RETURN[tentativeResult];
END;
GetRootBTH:
PUBLIC
PROC[mob: MobCookie]
RETURNS[
BTH]
~ {RETURN MakeBTH[mob, Symbols.BTFirst]};
we accept this BTI if it bounds checks, we can find our way to the root from it, and we can find our way back to it from the root. We maintain an accelerator in the cookie, so that we do not necessarily have to go all the way back to the root each time.
CheckTentativeBti:
PROC[tentativeBti: Symbols.BTIndex, mob: MobCookie, sym: SymbolTableBase, checkDepth:
CARD]
RETURNS[Symbols.BTIndex] =
TRUSTED
BEGIN
IF checkDepth > 15
THEN BadTentativeBti[tentativeBti];
I believe that Mobs have an even more conservative limit than 15
[] ← BoundsCheckTentativeBti[tentativeBti, sym];
IF tentativeBti = Symbols.BTFirst THEN RETURN[tentativeBti];
IF tentativeBti = mob.recentBtiParent THEN RETURN[tentativeBti];
we are not the root, nor are we a recent parent, so find our parent
BEGIN
counter: INT ← 0;
scan: Symbols.BTIndex ← tentativeBti;
parent: Symbols.BTIndex;
DO
IF sym.bb[scan].link.which = parent THEN EXIT;
IF counter > 1000
THEN BadTentativeBti[tentativeBti];
more than 1000 blocks in a module?
counter ← counter + 1;
IF sym.bb[scan].link.which = sibling
THEN
{scan ← BoundsCheckTentativeBti[sym.bb[scan].link.index, sym]; LOOP};
BadTentativeBti[tentativeBti];
ENDLOOP;
now see if we can find ourselves from our supposed parent
parent ← CheckTentativeBti[sym.bb[scan].link.index, mob, sym, checkDepth+1];
scan ← BoundsCheckTentativeBti[sym.bb[parent].firstSon, sym];
DO
IF counter > 1000
THEN BadTentativeBti[tentativeBti];
more than 1000 blocks in a module?
counter ← counter + 1;
IF scan = tentativeBti
THEN
{mob.recentBtiParent ← parent; RETURN[tentativeBti]};
IF sym.bb[scan].link.which = parent THEN BadTentativeBti[tentativeBti];
IF sym.bb[scan].link.which = sibling
THEN
scan ← BoundsCheckTentativeBti[sym.bb[scan].link.index, sym];
ENDLOOP;
END;
END;
BoundsCheckTentativeBti:
PROC[tentativeBti: Symbols.BTIndex, sym: SymbolTableBase]
RETURNS[Symbols.BTIndex] =
TRUSTED
BEGIN -- this must act like a relative pointer that lands within the body table
addr:
CARD ←
LOOPHOLE[sym.bb,
CARD] +
LOOPHOLE[tentativeBti,
CARD];
this is what mob.sym.bb[card] would deliver
tableBase: CARD ← LOOPHOLE[sym.stHandle, CARD] + LOOPHOLE[sym.stHandle.bodyBlock.offset, CARD];
IF addr < tableBase
THEN
addr too low
BadTentativeBti[tentativeBti];
IF addr >= tableBase+sym.stHandle.bodyBlock.size
THEN
addr far too high (this check will not prevent a bad pointer that is too close to the end of the table)
BadTentativeBti[tentativeBti];
RETURN[tentativeBti];
END;
BadTentativeBti: ERROR[Symbols.BTIndex] = CODE;
GenCallableBodies:
PUBLIC
PROC[mob: MobCookie, for:
PROC[callableBody:
BTH]] =
TRUSTED
BEGIN
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
Visit:
PROC[bti: Symbols.BTIndex] =
TRUSTED
{IF sym.bb[bti].kind = Callable THEN for[MakeBTH[mob, bti]]};
ScanSubTree:
PROC[treeRoot: Symbols.BTIndex] =
TRUSTED
BEGIN
IF sym.bb[treeRoot].firstSon # Symbols.BTNull
THEN
FOR scan: Symbols.BTIndex ← sym.bb[treeRoot].firstSon, sym.bb[scan].link.index
WHILE scan # treeRoot
DO
ScanSubTree[scan];
Visit[scan];
ENDLOOP;
END;
ScanSubTree[Symbols.RootBti];
Visit[Symbols.RootBti];
END;
ExamineMob[mob, Inner];
END;
GetParentOfBTH:
PUBLIC
PROC[bth:
BTH]
RETURNS[
BTH] =
BEGIN
result: BTH ← NIL; -- tentative
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
IF bth.bti = Symbols.RootBti THEN {result ← NIL}
ELSE
BEGIN
x: Symbols.BTIndex ← bth.bti;
WHILE sym.bb[x].link.which # parent DO x ← sym.bb[x].link.index ENDLOOP;
result ← MakeBTH[bth.mob, sym.bb[x].link.index];
END;
END;
ExamineMob[bth.mob, Inner];
RETURN[result];
END;
GetCodedBTIFromBTH:
PUBLIC
PROC[bth:
BTH]
RETURNS[
CARD] =
BEGIN
result: CARD ← 0;
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
Note: we shift in the opposite direction from that which we would shift during decoding
result ←
SELECT
TRUE
FROM
bth.mob.leftShift # 0 => PBasics.BITRSHIFT[LOOPHOLE[bth.bti], bth.mob.leftShift],
bth.mob.rightShift # 0 => PBasics.BITLSHIFT[LOOPHOLE[bth.bti], bth.mob.rightShift],
ENDCASE => LOOPHOLE[bth.bti];
END;
ExamineMob[bth.mob, Inner];
RETURN[result]
END;
BTKeyEqualProc:
PROC[key1, key2: RefTab.Key]
RETURNS [
BOOL] =
BEGIN
bti1Ref: REF Symbols.BTIndex ← NARROW[key1];
bti2Ref: REF Symbols.BTIndex ← NARROW[key2];
RETURN[bti1Ref^ = bti2Ref^];
END;
BTKeyHashProc:
PROC[key: RefTab.Key]
RETURNS [
CARDINAL] =
BEGIN
btiRef: REF Symbols.BTIndex ← NARROW[key];
asLongNumber: Basics.LongNumber ← LOOPHOLE[btiRef^];
RETURN[Basics.BITXOR[asLongNumber.lo, asLongNumber.hi]];
END;
FetchBTR:
PUBLIC
PROC[bth:
BTH]
RETURNS[
BTR] =
BEGIN
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
bth.btr ←
NEW[BTRBody←[
link: [
which: IF sym.bb[bth.bti].link.which = sibling THEN sibling ELSE parent,
index: MakeBTH[bth.mob, sym.bb[bth.bti].link.index]],
firstSon: MakeBTH[bth.mob, sym.bb[bth.bti].firstSon],
type: MakeSEH[bth.mob, sym.bb[bth.bti].type],
localCtx: MakeCTXH[bth.mob, sym.bb[bth.bti].localCtx],
sourceIndex: sym.bb[bth.bti].sourceIndex,
info: MakeBTInfo[bth, mobHandle, mobBase, sym],
level: sym.bb[bth.bti].level,
class: sym.bb[bth.bti].class,
extension: MakeBTExtension[bth, mobHandle, mobBase, sym]]];
END;
IF bth = NIL THEN RETURN[NIL];
IF bth.btr = NIL THEN ExamineMob[bth.mob, Inner];
RETURN[bth.btr];
END;
MakeBTInfo:
PROC[bth:
BTH, mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase]
RETURNS[
REF MobAccess.BodyInfo] =
TRUSTED
BEGIN
WITH info: sym.bb[bth.bti].info
SELECT
FROM
Internal =>
RETURN[NEW[Internal MobAccess.BodyInfo ← [Internal[info.frameSize]]]];
External =>
RETURN[
NEW[External MobAccess.BodyInfo ← [External[
bytes: info.bytes,
startIndex: info.startIndex,
indexLength: info.indexLength]]]];
ENDCASE => ERROR MobError["invalid BodyInfo.mark"];
END;
MakeBTExtension:
PROC[bth:
BTH, mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase]
RETURNS[
REF MobAccess.BTRExtension] =
TRUSTED
BEGIN
WITH extension: sym.bb[bth.bti]
SELECT
FROM
Callable =>
RETURN[
NEW[Callable MobAccess.BTRExtension ← [Callable[
id: MakeSEH[bth.mob, extension.id],
ioType: MakeSEH[bth.mob, extension.ioType],
frameOffset: extension.frameOffset,
entryIndex: extension.entryIndex,
hints: [extension.hints.safe, extension.hints.argUpdated, extension.hints.nameSafe, extension.hints.noStrings],
entry: extension.entry,
internal: extension.internal,
inline: extension.inline,
monitored: extension.monitored,
noXfers: extension.noXfers,
resident: extension.resident,
kind:
SELECT extension.kind
FROM
Outer => Outer,
Inner => Inner,
Catch => Catch,
Other => Other,
ENDCASE => ERROR MobError["invalid Callable BodyRecord BodyKind"] ]]]];
Other =>
RETURN[NEW[Other MobAccess.BTRExtension ← [Other[extension.relOffset]]]];
ENDCASE => ERROR MobError["invalid BodyRecord kind"];
END;
GenBodiesWithDepth:
PROC[mob: MobCookie, body:
BTH, baseDepth:
INT, for:
PROC[body:
BTH, depth:
INT]] =
TRUSTED
BEGIN
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
Visit:
PROC[bti: Symbols.BTIndex] =
TRUSTED
BEGIN
bth: BTH ← MakeBTH[mob, bti];
for[bth, baseDepth+1];
GenBodiesWithDepth[mob, bth, baseDepth+1, for];
END;
ScanSubTree:
PROC[treeRoot: Symbols.BTIndex] =
TRUSTED
BEGIN
IF sym.bb[treeRoot].firstSon # Symbols.BTNull
THEN
FOR scan: Symbols.BTIndex ← sym.bb[treeRoot].firstSon, sym.bb[scan].link.index
WHILE scan # treeRoot
DO
ScanSubTree[scan];
Visit[scan];
ENDLOOP;
END;
ScanSubTree[body.bti];
END;
ExamineMob[mob, Inner];
END;
MobBodyTreeToRope:
PROC[mob: MobCookie]
RETURNS[Rope.
ROPE] =
{RETURN[BodyTreeToRope[mob, MakeBTH[mob, Symbols.RootBti]]]};
BodyTreeToRope:
PROC[mob: MobCookie, body:
BTH]
RETURNS[Rope.
ROPE] =
BEGIN
rope: Rope.ROPE ← NIL;
eachBody:
PROC[body:
BTH, depth:
INT] =
BEGIN
rope ← Rope.Cat[rope, BodyToRope[body, depth], "\N"];
END;
eachBody[body, 0];
GenBodiesWithDepth[mob, body, 1, eachBody];
RETURN[rope];
END;
BodyToRope:
PROC[body:
BTH, depth:
INT]
RETURNS[Rope.
ROPE] =
BEGIN
btr: BTR ← FetchBTR[body];
st: IO.STREAM ← IO.ROS[];
putDepth: PROC[d: INT𡤀] = {FOR I: INT IN [0..depth+d) DO IO.PutF[st, " "] ENDLOOP};
rope:
PROC[r: Rope.
ROPE, d:
INT ← 0] =
{putDepth[d]; IO.PutRope[st, r]};
put:
PROC[d:
INT, f: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]]] =
BEGIN
putDepth[d];
IO.PutF[st, f, v1, v2, v3, v4, v5];
END;
rope["body"];
put[0, " source index = %g\N", IO.card[btr.sourceIndex]];
rope[CTXToRope[btr.localCtx, depth+5]];
RETURN[IO.RopeFromROS[st]];
END;
Module Directory
MDR: TYPE = MobAccess.MDR;
MDRBody: TYPE = MobAccess.MDRBody;
MDH: TYPE = REF MDHBody;
MDHBody:
PUBLIC
TYPE =
RECORD[
mob: MobCookie,
mdi: Symbols.MDIndex,
mdr: MDR];
MakeMDH:
PUBLIC
PROC[mob: MobCookie, mdi: Symbols.MDIndex]
RETURNS[
MDH] =
BEGIN
IF mdi = Symbols.MDNull THEN RETURN[NIL]
ELSE
BEGIN
mdiAsKey: CARD ~ LOOPHOLE[mdi];
mdh: MDH ← NARROW[CardTab.Fetch[mob.mdTable, mdiAsKey].val];
IF mdh #
NIL
THEN
RETURN[mdh];
mdh ← NEW[MDHBody←[mob, mdi, NIL]];
IF NOT CardTab.Insert[mob.mdTable, mdiAsKey, mdh] THEN ERROR MobError["MobAccessImpl confused (by concurrent access?)"];
RETURN[mdh]
END
END;
FetchMDR:
PUBLIC
PROC[mdh:
MDH]
RETURNS[
MDR] =
BEGIN
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
raw: LONG POINTER TO Symbols.MDRecord ~ @sym.mdb[mdh.mdi];
mdh.mdr ←
NEW[MDRBody←[
stamp: raw.stamp,
moduleId: RopeForName[mdh.mob, mobHandle, mobBase, sym, raw.moduleId],
fileId: RopeForName[mdh.mob, mobHandle, mobBase, sym, raw.fileId],
shared: raw.shared,
exported: raw.exported,
ctx: MakeCTXH[mdh.mob, raw.ctx],
defaultImport: MakeCTXH[mdh.mob, raw.defaultImport]
]];
END;
IF mdh = NIL THEN RETURN[NIL];
IF mdh.mdr = NIL THEN ExamineMob[mdh.mob, Inner];
RETURN[mdh.mdr];
END;
GetMobForMDH:
PUBLIC
PROC[mdh:
MDH]
RETURNS[MobCookie] =
{RETURN[mdh.mob]};
GetMdiForMDH:
PUBLIC
PROC[mdh:
MDH]
RETURNS[Symbols.MDIndex] =
{RETURN[mdh.mdi]};
Context Tables
CTXR: TYPE = MobAccess.CTXR;
CTXRBody: TYPE = MobAccess.CTXRBody;
CTXH: TYPE = REF CTXHBody;
CTXHBody:
PUBLIC
TYPE =
RECORD[
mob: MobCookie,
ctx: Symbols.CTXIndex,
ctxr: CTXR];
MakeCTXH:
PUBLIC
PROC[mob: MobCookie, ctx: Symbols.CTXIndex]
RETURNS[
CTXH] = {
IF ctx = Symbols.CTXNull
THEN RETURN[NIL]
ELSE RETURN ReallyMakeCTXH[mob, ctx]};
ReallyMakeCTXH:
PROC[mob: MobCookie, ctx: Symbols.CTXIndex]
RETURNS[
CTXH] = {
ctxAsKey: REF Symbols.CTXIndex ← NEW[Symbols.CTXIndex ← ctx];
ctxh: CTXH ← NARROW[RefTab.Fetch[mob.ctxTable, ctxAsKey].val];
IF ctxh #
NIL
THEN
RETURN[ctxh];
ctxh ← NEW[CTXHBody←[mob, ctx, NIL]];
IF NOT RefTab.Insert[mob.ctxTable, ctxAsKey, ctxh] THEN ERROR MobError["MobAccessImpl confused (by concurrent access?)"];
RETURN[ctxh]};
MakeStdCtxh:
PUBLIC
PROC[mob: MobCookie]
RETURNS[
CTXH]
~ {RETURN MakeCTXH[mob, Symbols.FirstStandardCtx]};
BoundsCheckTentativeCTX:
PROC[tentativeCTX: Symbols.CTXIndex, sym: SymbolTableBase]
RETURNS[Symbols.CTXIndex] =
TRUSTED
BEGIN
addr:
CARD ←
LOOPHOLE[sym.ctxb,
CARD] +
LOOPHOLE[tentativeCTX,
CARD];
this is what mob.sym.seb[card] would deliver
tableBase: CARD ← LOOPHOLE[sym.stHandle, CARD] + LOOPHOLE[sym.stHandle.ctxBlock.offset, CARD];
IF tentativeCTX = Symbols.CTXNull THEN BadTentativeCTX[tentativeCTX];
IF addr < tableBase
THEN
addr too low
BadTentativeCTX[tentativeCTX];
IF addr >= tableBase+sym.stHandle.ctxBlock.size
THEN
addr far too high (this check will not prevent a bad pointer that is too close to the end of the table)
BadTentativeCTX[tentativeCTX];
RETURN[tentativeCTX];
END;
BadTentativeCTX: ERROR[Symbols.CTXIndex] = CODE;
CTXKeyEqualProc:
PROC[key1, key2: RefTab.Key]
RETURNS [
BOOL] =
BEGIN
ctx1Ref: REF Symbols.CTXIndex ← NARROW[key1];
ctx2Ref: REF Symbols.CTXIndex ← NARROW[key2];
RETURN[ctx1Ref^ = ctx2Ref^];
END;
CTXKeyHashProc:
PROC[key: RefTab.Key]
RETURNS [
CARDINAL] =
BEGIN
ctxRef: REF Symbols.CTXIndex ← NARROW[key];
asLongNumber: Basics.LongNumber ← LOOPHOLE[ctxRef^];
RETURN[Basics.BITXOR[asLongNumber.lo, asLongNumber.hi]];
END;
FetchCTXR:
PUBLIC
PROC[ctxh:
CTXH]
RETURNS[
CTXR] =
BEGIN
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
seH: SEH ~ MakeSEH[ctxh.mob, sym.ctxb[ctxh.ctx].seList];
lvl: Symbols.ContextLevel ~ sym.ctxb[ctxh.ctx].level;
varUpdated: BOOL ~ sym.ctxb[ctxh.ctx].varUpdated;
WITH x: sym.ctxb[ctxh.ctx]
SELECT
FROM
simple => ctxh.ctxr ←
NEW[CTXRBody←[seList: seH, level: lvl,
varUpdated: varUpdated,
extension: simple[copied: x.copied] ]];
included => ctxh.ctxr ←
NEW[CTXRBody←[seList: seH, level: lvl,
varUpdated: varUpdated,
extension: included[
chain: MakeCTXH[ctxh.mob, x.chain],
module: MakeMDH[ctxh.mob, x.module],
map: x.map, copied: x.copied,
reset: x.reset, closed: x.closed,
complete: x.complete, restricted: x.restricted
] ]];
imported => ctxh.ctxr ←
NEW[CTXRBody←[seList: seH, level: lvl,
varUpdated: varUpdated,
extension: imported[MakeCTXH[ctxh.mob, x.includeLink]] ]];
nil => ctxh.ctxr ←
NEW[CTXRBody←[seList: seH, level: lvl,
varUpdated: varUpdated,
extension: nil[] ]];
ENDCASE => ERROR MobError["invalid CTXRecord ctxType"];
END;
IF ctxh = NIL THEN RETURN[NIL];
IF ctxh.ctxr = NIL THEN ExamineMob[ctxh.mob, Inner];
RETURN[ctxh.ctxr];
END;
GetMobForCTXH:
PUBLIC
PROC[ctxh:
CTXH]
RETURNS[MobCookie] =
{RETURN[ctxh.mob]};
GetCtxForCTXH:
PUBLIC
PROC[ctxh:
CTXH]
RETURNS[Symbols.CTXIndex] =
{RETURN[ctxh.ctx]};
typical use in the D-machine debugger
IO.PutRope[&H.tsOutStream, MobAccessImpl.CTXToRope[ctx, 0]]
CTXToRope:
PROC[ctxh:
CTXH, depth:
INT]
RETURNS[Rope.
ROPE] =
BEGIN
IF ctxh = NIL THEN RETURN[NIL]
ELSE
BEGIN
ctxr: CTXR ← FetchCTXR[ctxh];
st: IO.STREAM ← IO.ROS[];
seh: SEH ← ctxr.seList;
putDepth: PROC[d: INT𡤀] = {FOR I: INT IN [0..depth+d) DO IO.PutF[st, " "] ENDLOOP};
putDepth[]; IO.PutF[st, "ctx = %g, level = %g\N", IO.rope[Convert.RopeFromCard[LOOPHOLE[ctxh.ctx]]], IO.card[ctxr.level]];
WHILE seh #
NIL
DO
ser: SER ← FetchSER[seh];
IO.PutF[st, "%g", IO.rope[SEHToRope[seh, depth+2]]];
WITH ser.body
SELECT
FROM
id: REF id BodySE => seh ← id.ctxLink;
ENDCASE => seh ← NIL;
ENDLOOP;
RETURN[IO.RopeFromROS[st]];
END
END;
Semantic Entries code (my own invention)
SER: TYPE = MobAccess.SER;
SERBody: TYPE = MobAccess.SERBody;
BodySE: TYPE = MobAccess.BodySE;
TypeInfoConsSE: TYPE = MobAccess.TypeInfoConsSE;
SEH: TYPE = REF SEHBody;
SEHBody:
PUBLIC
TYPE =
RECORD[
mob: MobCookie,
sei: Symbols.SEIndex,
ser: SER];
MakeSEH:
PUBLIC
PROC[mob: MobCookie, sei: Symbols.SEIndex]
RETURNS[
SEH] =
BEGIN
IF sei = Symbols.SENull THEN RETURN[NIL]
ELSE
BEGIN
seiAsKey: REF Symbols.SEIndex ← NEW[Symbols.SEIndex ← sei];
seh: SEH ← NARROW[RefTab.Fetch[mob.seiTable, seiAsKey].val];
IF seh #
NIL
THEN
RETURN[seh];
seh ← NEW[SEHBody←[mob, sei, NIL]];
IF NOT RefTab.Insert[mob.seiTable, seiAsKey, seh] THEN ERROR MobError["MobAccessImpl confused (by concurrent access?)"];
RETURN[seh]
END;
END;
This code assumes that a codedSei is obtained from an octal encoding of the offset of an se entry from the start of the se table (i.e., sei-seNull). Further, we assume that this subtraction followed by subsequent conversion into octal was performed in the same addressing units as on the machine on which the Mob was written.
MakeSEHFromCodedSei:
PUBLIC
PROC[mob: MobCookie, codedSei:
CARD, name: Rope.
ROPE]
RETURNS[
SEH] =
TRUSTED
BEGIN
tentativeResult: SEH ← NIL;
Most of this computation must be inside Inner so that mob.leftShift and mob.rightShift will be valid
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
seNull:
CARD ~
LOOPHOLE[Symbols.SENull];
the following shifts are suggested by code in MobMapperImpl
further: the are are able to perform this correction here, in MobAccessImpl, rather than in DotOAccess because we assume that the C file (from which the DotO file was ultimately obtained) was written at the same time (and hence on the same machine) as this Mob file. Hence, the corrections for addressing unit size will be the same for both files. In addition, we must determine the correction from the Mob file, because there is no correction information recorded in the DotO file.
shiftedSei:
CARD ←
SELECT
TRUE
FROM
mob.leftShift # 0 => PBasics.BITLSHIFT[codedSei, mob.leftShift],
mob.rightShift # 0 => PBasics.BITRSHIFT[codedSei, mob.rightShift],
ENDCASE => codedSei;
tentativeSei: Symbols.SEIndex ←
LOOPHOLE[shiftedSei+seNull];
The above computation is adapted from SymbolOpsExtrasImpl.SEIndexFromOffset, as suggested by MSImplE.C2CParse
tentativeSeiAsKey: REF Symbols.SEIndex ← NEW[Symbols.SEIndex ← tentativeSei];
tentativeResult ← NARROW[RefTab.Fetch[mob.seiTable, tentativeSeiAsKey].val];
IF tentativeResult =
NIL
THEN
-- we have to build it from scratch
BEGIN
CheckTentativeISei[tentativeSei, sym
! BadTentativeSei, BadTentativeCTX => {GOTO failure}];
tentativeResult ← MakeSEH[mob, tentativeSei];
EXITS
failure => NULL; -- thus, leaving result = NIL
END;
END;
ExamineMob[mob, Inner];
IF tentativeResult # NIL THEN RETURN[CheckSEHName[tentativeResult, name]] ELSE RETURN[NIL];
END;
GetCodedSeiFromSEH:
PUBLIC
PROC[seh:
SEH]
RETURNS[
CARD] =
BEGIN
result: CARD ← 0;
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
Note: we shift in the opposite direction from that which we would shift during decoding
nominal: CARD ← LOOPHOLE[seh.sei, CARD] - LOOPHOLE[Symbols.SENull, CARD];
result ←
SELECT
TRUE
FROM
seh.mob.leftShift # 0 => PBasics.BITRSHIFT[nominal, seh.mob.leftShift],
seh.mob.rightShift # 0 => PBasics.BITLSHIFT[nominal, seh.mob.rightShift],
ENDCASE => nominal;
END;
ExamineMob[seh.mob, Inner];
RETURN[result]
END;
returns NIL if doesn't check
CheckSEHName:
PROC[seh:
SEH, name: Rope.
ROPE]
RETURNS[
SEH] =
BEGIN
ser: SER ← FetchSER[seh];
body: REF BodySE ← ser.body;
IF name = NIL THEN RETURN[seh]; -- client didn't really want to check
WITH body
SELECT
FROM
id:
REF id BodySE =>
IF Rope.Equal[name, id.hash] THEN RETURN[seh]
ELSE RETURN[NIL]; -- name didn't check
ENDCASE => RETURN[NIL]; -- was expecting an id seh
END;
checks to see if tentativeSei leads to a (supposed) context that includes self. This is not a perfect check, but it is pretty good. Requires a lot of accidents to succeed if the tentativeSei is actually incorrect. Further, we don't have to depend on the client supplying a name.
CheckTentativeISei:
PROC[tentativeISei: Symbols.SEIndex, sym: SymbolTableBase] =
TRUSTED
BEGIN
[] ← BoundsCheckTentativeSei[tentativeISei, sym];
WITH body: sym.seb[tentativeISei]
SELECT
FROM
id =>
-- at least its got the right tag
BEGIN
tentativeCtx: Symbols.CTXIndex ← BoundsCheckTentativeCTX[body.idCtx, sym];
firstSei: Symbols.SEIndex ← BoundsCheckTentativeSei[sym.ctxb[tentativeCtx].seList, sym];
nextSei: Symbols.SEIndex ← firstSei;
we scan through the context looking for ourselves
DO
sei: Symbols.SEIndex ← BoundsCheckTentativeSei[nextSei, sym];
IF sei = tentativeISei
THEN
RETURN;
we found ourselves
WITH body: sym.seb[sei]
SELECT
FROM
id =>
-- at least its got the right tag
BEGIN
WITH lnk: body
SELECT
FROM
terminal => BadTentativeSei[tentativeISei];
sequential =>
{nextSei ← sei + Symbols.SERecord.id.sequential.SIZE; LOOP};
linked =>
{nextSei ← lnk.link; LOOP};
embedded => BadTentativeSei[tentativeISei]; -- I am not sure if this is what I should do here
ENDCASE => BadTentativeSei[tentativeISei];
END
ENDCASE => BadTentativeSei[tentativeISei];
ENDLOOP;
END;
ENDCASE => BadTentativeSei[tentativeISei];
END;
This check is only a bounds check, and is not precise at that. So, the contents of the record should not be trusted.
BoundsCheckTentativeSei:
PROC[tentativeSei: Symbols.SEIndex, sym: SymbolTableBase]
RETURNS[Symbols.SEIndex] =
TRUSTED
BEGIN -- this must act like a relative pointer that lands within the se table
addr:
CARD ←
LOOPHOLE[sym.seb,
CARD] +
LOOPHOLE[tentativeSei,
CARD];
this is what mob.sym.seb[card] would deliver
tableBase: CARD ← LOOPHOLE[sym.stHandle, CARD] + LOOPHOLE[sym.stHandle.seBlock.offset, CARD];
IF addr < tableBase
THEN
addr too low
BadTentativeSei[tentativeSei];
IF addr >= tableBase+sym.stHandle.seBlock.size
THEN
addr far too high (this check will not prevent a bad pointer that is too close to the end of the table)
BadTentativeSei[tentativeSei];
RETURN[tentativeSei];
END;
BadTentativeSei: ERROR[tentativeSei: Symbols.SEIndex] = CODE;
This check is complete, the name can be trusted if no signal generated. Hence the string pointer can be followed without checking.
CheckTentativeName:
PROC[name: Symbols.Name, sym: SymbolTableBase] =
TRUSTED
BEGIN
addr: CARD ← LOOPHOLE[sym.htb, CARD] + LOOPHOLE[name, CARD];
tableBase: CARD ← LOOPHOLE[sym.stHandle, CARD] + LOOPHOLE[sym.stHandle.htBlock.offset, CARD];
IF addr < tableBase
THEN
addr too low
BadTentativeName[name];
IF addr >= tableBase+sym.stHandle.htBlock.size
THEN
addr far too high (this check will not prevent a bad pointer that is too close to the end of the table)
BadTentativeName[name];
IF (addr-tableBase)
MOD
SIZE[Symbols.HTRecord] # 0
THEN
addr not exactly on an entry
BadTentativeName[name];
END;
BadTentativeName: SIGNAL[Symbols.Name] ~ CODE;
SEIKeyEqualProc:
PROC[key1, key2: RefTab.Key]
RETURNS [
BOOL] =
BEGIN
sei1Ref: REF Symbols.SEIndex ← NARROW[key1];
sei2Ref: REF Symbols.SEIndex ← NARROW[key2];
RETURN[sei1Ref^ = sei2Ref^];
END;
SEIKeyHashProc:
PROC[key: RefTab.Key]
RETURNS [
CARDINAL] =
BEGIN
seiRef: REF Symbols.SEIndex ← NARROW[key];
asLongNumber: Basics.LongNumber ← LOOPHOLE[seiRef^];
RETURN[Basics.BITXOR[asLongNumber.lo, asLongNumber.hi]];
END;
Note: I don't think that I can remove the two loopholes in this procedure, since [Mimosa]<Mimosa>Mimosa>SymbolOpsImpl.UnderType contains a LOOPHOLE under similar circumstances.
FetchSER:
PUBLIC
PROC[seh:
SEH]
RETURNS[
SER] =
BEGIN
Inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
TRUSTED
BEGIN
IF seh.sei-Symbols.SEFirst<0 OR CARD[seh.sei-Symbols.SEFirst]>=sym.stHandle.seBlock.size THEN ERROR MobError[IO.PutFR["bogus SEI %x into %g", [cardinal[LOOPHOLE[seh.sei]]], [rope[PFS.RopeFromPath[SystemInterface.GetNameOfFile[seh.mob.file]]]] ]];
seh.ser ←
NEW[SERBody ← [
body:
WITH body: sym.seb[seh.sei]
SELECT
FROM
id =>
NEW[BodySE ← [id[
extended: body.extended, public: body.public,
immutable: body.immutable, constant: body.constant,
linkSpace: body.linkSpace, idDecl: body.idDecl,
idCtx: MakeCTXH[seh.mob, body.idCtx],
idType: MakeSEH[seh.mob, body.idType],
idInfoAndValue: NIL, -- filled in below
hash: RopeForName[seh.mob, mobHandle, mobBase, sym, body.hash],
flags: body.flags,
special: body.special,
ctxLink:
WITH lnk: body
SELECT
FROM
terminal => NIL,
sequential => MakeSEH[seh.mob, seh.sei + Symbols.SERecord.id.sequential.SIZE],
linked => MakeSEH[seh.mob, lnk.link],
embedded => ERROR MobError["MobAccessImpl can't handle embedded ISEIs"],
ENDCASE => ERROR MobError["invalid id SERecord linkTag"] ]]],
cons =>
NEW[BodySE ← [cons[
align: body.align,
typeInfo:
WITH cons: body
SELECT
FROM
mode => NEW[TypeInfoConsSE ← [mode[]]],
basic =>
NEW[TypeInfoConsSE ← [basic[
ordered: cons.ordered,
code: cons.code,
length: cons.length]]],
signed => NEW[TypeInfoConsSE ← [signed[cons.length]]],
unsigned => NEW[TypeInfoConsSE ← [unsigned[cons.length]]],
real => NEW[TypeInfoConsSE ← [real[cons.length]]],
enumerated =>
NEW[TypeInfoConsSE ← [enumerated[
range: cons.range,
valueCtx: MakeCTXH[seh.mob, cons.valueCtx],
empty: cons.empty,
sparse: cons.sparse,
painted: cons.painted,
ordered: cons.ordered,
machineDep: cons.machineDep]]],
record =>
NEW[TypeInfoConsSE ← [record[
length: cons.length,
fieldCtx: MakeCTXH[seh.mob, cons.fieldCtx],
bitOrder: MyifyBitOrder[cons.bitOrder],
grain: cons.grain,
hints: [
comparable: cons.hints.comparable,
assignable: cons.hints.assignable,
unifield: cons.hints.unifield,
variant: cons.hints.variant,
privateFields: cons.hints.privateFields,
refField: cons.hints.refField,
default: cons.hints.default,
voidable: cons.hints.voidable],
packed: cons.packed,
list: cons.list,
argument: cons.argument,
monitored: cons.monitored,
machineDep: cons.machineDep,
painted: cons.painted,
linkPart:
WITH link: cons
SELECT
FROM
notLinked => NIL,
linked => MakeSEH[seh.mob, link.linkType],
ENDCASE => ERROR MobError["invalid record cons SERecord linkTag"] ]]],
ref =>
NEW[TypeInfoConsSE ← [ref[
refType: MakeSEH[seh.mob, cons.refType],
counted: cons.counted,
ordered: cons.ordered,
readOnly: cons.readOnly,
list: cons.list,
var: cons.var,
basing: cons.basing,
length: cons.length
]]],
array =>
NEW[TypeInfoConsSE ← [array[
componentType: MakeSEH[seh.mob, cons.componentType],
indexType: MakeSEH[seh.mob, cons.indexType],
packed: cons.packed,
bitOrder: MyifyBitOrder[cons.bitOrder]]]],
arraydesc =>
NEW[TypeInfoConsSE ← [arraydesc[
describedType: MakeSEH[seh.mob, cons.describedType],
var: cons.var, readOnly: cons.readOnly,
bitOrder: MyifyBitOrder[cons.bitOrder],
length: cons.length]]],
transfer =>
NEW[TypeInfoConsSE ← [transfer[
safe: cons.safe,
mode:
SELECT cons.mode
FROM
proc => proc,
port => port,
signal => signal,
error => error,
process => process,
program => program,
none => none,
ENDCASE => ERROR MobError["invalid transfer cons SERecord mode"],
typeIn: MakeSEH[seh.mob, cons.typeIn],
typeOut: MakeSEH[seh.mob, cons.typeOut],
length: cons.length]]],
definition =>
NEW[TypeInfoConsSE ← [definition[
defCtx: MakeCTXH[seh.mob, cons.defCtx],
slots: cons.slots,
named: cons.named]]],
union =>
NEW[TypeInfoConsSE ← [union[
caseCtx: MakeCTXH[seh.mob, cons.caseCtx],
tagSei: MakeSEH[seh.mob, cons.tagSei],
hints: [
equalLengths: cons.hints.equalLengths,
refField: cons.hints.refField,
default: cons.hints.default,
voidable: cons.hints.voidable],
overlaid: cons.overlaid, controlled: cons.controlled,
machineDep: cons.machineDep,
bitOrder: MyifyBitOrder[cons.bitOrder],
grain: cons.grain]]],
sequence =>
NEW[TypeInfoConsSE ← [sequence[
parentType: MakeSEH[seh.mob, cons.parentType],
tagSei: MakeSEH[seh.mob, cons.tagSei],
componentType: MakeSEH[seh.mob, cons.componentType],
packed: cons.packed,
controlled: cons.controlled,
machineDep: cons.machineDep,
bitOrder: MyifyBitOrder[cons.bitOrder],
grain: cons.grain]]],
relative =>
NEW[TypeInfoConsSE ← [relative[
baseType: MakeSEH[seh.mob, cons.baseType],
offsetType: MakeSEH[seh.mob, cons.offsetType],
resultType: MakeSEH[seh.mob, cons.resultType]]]],
subrange =>
NEW[TypeInfoConsSE ← [subrange[
empty: cons.empty,
biased: cons.biased,
filled: cons.filled,
rangeType: MakeSEH[seh.mob, cons.rangeType],
origin: cons.origin,
range: cons.range]]],
opaque =>
NEW[TypeInfoConsSE ← [opaque[
id: MakeSEH[seh.mob, cons.id],
length: cons.length,
lengthKnown: cons.lengthKnown]]],
zone =>
NEW[TypeInfoConsSE ← [zone[
counted: cons.counted,
mds: cons.mds,
length: cons.length]]],
any => NEW[TypeInfoConsSE ← [any[]]],
nil => NEW[TypeInfoConsSE ← [nil[]]],
ENDCASE => ERROR MobError["invalid cons SERecord typeTag"]
]]],
ENDCASE => ERROR MobError["invalid SERecord seTag"] ]];
delayed fill in of the idInfoAndValue field
(We delay because we must compute the idType SEH first, and we can't guarantee the order in which the fields are computed)
(Further, as this is a complicated computation, it must be done offline, either in a procedure, or a separate statement.)
WITH bodySource: sym.seb[seh.sei]
SELECT
FROM
id =>
WITH bodyTarget: seh.ser.body
SELECT
FROM
id =>
BEGIN
if we get here, then there is an idInfoAndValue field to be filled in
first we check to see if we are dealing with a TYPE name
IF bodySource.idType = Symbols.typeTYPE
THEN
BEGIN
idInfoSEI: Symbols.SEIndex ← LOOPHOLE[bodySource.idInfo];
idInfoSEH: SEH ← MakeSEH[seh.mob, idInfoSEI];
bodyTarget.idInfoAndValue ← NEW[MobAccess.TypeDesc ← [idInfoSEH, LOOPHOLE[bodySource.idValue]]];
GOTO installed;
END;
lets check to see if it is a bti
This computation is adapted from [PCedar2.0]<Mimosa>MobMapperImpl.AlterSERecord2
BEGIN
entrySEI: Symbols.SEIndex ← UnderType[bodySource.idType, sym];
isBTI: BOOLEAN ← FALSE;
WITH entryTypeBody: sym.seb[entrySEI]
SELECT
FROM
cons =>
SELECT
TRUE
FROM
bodySource.constant
AND
NOT bodySource.extended =>
WITH etb: entryTypeBody
SELECT
FROM
transfer =>
SELECT etb.mode
FROM
proc, signal, error, program => isBTI ← TRUE;
ENDCASE => NULL;
ENDCASE => NULL;
bodySource.constant
AND bodySource.extended =>
WITH etb: entryTypeBody
SELECT
FROM
transfer =>
SELECT etb.mode
FROM
proc => isBTI ← TRUE;
ENDCASE => NULL;
ENDCASE => NULL
ENDCASE => NULL;
ENDCASE => ERROR MobError["Can't happen: UnderType returned a non-cons sei"];
IF isBTI
THEN
BEGIN
bodyTarget.idInfoAndValue ← NEW [MobAccess.BlockDesc ← [MakeBTH[seh.mob, LOOPHOLE[bodySource.idInfo]], LOOPHOLE[bodySource.idValue]]];
GOTO installed;
END;
END;
not a type or bti
IF bodySource.constant
THEN
-- we are dealing with a constant
if the constant occupies one word or less, then the following will be meaningful. Otherwise we should examine the extension table?
BEGIN
bodyTarget.idInfoAndValue ← NEW[MobAccess.ConstVal ← [LOOPHOLE[bodySource.idValue]]];
GOTO installed;
END;
I think that we are dealing with a field offset, either a variable with an offset from the frame pointer (valid only if in the frame extension, then it is really an offset within the frame extension) or a field in a record (in which case it is an offset from the start of the record).
BEGIN
bitSize: CARD ← LOOPHOLE[bodySource.idInfo, CARD]; -- size of field (in bits)
bitOffset: INT ← LOOPHOLE[bodySource.idValue, INT]; -- offset of field from the appropriate base (in bits)
bodyTarget.idInfoAndValue ← NEW[MobAccess.FieldDesc ← [bitSize, bitOffset]];
GOTO installed;
END;
I may still be missing some cases. One might be able to sort them out by looking at [PCedar2.0]<Mimosa>MobMapperImpl.AlterSERecord2, although that code is really trying to decide whether to do some alterations or not, and stops its analysis as soon as it knows.
END;
ENDCASE => ERROR MobError["can't happen: we just made it the other way"];
ENDCASE => NULL;
END;
IF seh = NIL THEN RETURN[NIL];
IF seh.ser = NIL THEN ExamineMob[seh.mob, Inner];
RETURN[seh.ser]
END;
MyifyBitOrder: ARRAY Symbols.BitOrder OF MobAccess.BitOrder = [msBit: msBit, lsBit: lsBit];
GetMobForSEH:
PUBLIC
PROC[seh:
SEH]
RETURNS[MobCookie] =
{RETURN[seh.mob]};
GetSeiForSEH:
PUBLIC
PROC[seh:
SEH]
RETURNS[sei: Symbols.SEIndex] =
{RETURN[seh.sei]};
UnderType:
PROC[sei: Symbols.SEIndex, sym: SymbolTableBase]
RETURNS [Symbols.SEIndex] =
TRUSTED
BEGIN
DO
WITH se: sym.seb[sei]
SELECT
FROM
id => IF se.idType = Symbols.typeTYPE THEN {sei ← LOOPHOLE[se.idInfo]; LOOP};
cons => RETURN [sei];
ENDCASE;
ERROR MobError["BadMobContents"];
ENDLOOP;
END;
SEHToRope:
PROC[seh:
SEH, depth:
INT]
RETURNS[Rope.
ROPE] =
BEGIN
ser: SER ← FetchSER[seh];
st: IO.STREAM ← IO.ROS[];
putDepth: PROC[d: INT𡤀] = {FOR I: INT IN [0..depth+d) DO IO.PutF[st, " "] ENDLOOP};
rope:
PROC[r: Rope.
ROPE, d:
INT ← 0] =
{putDepth[d]; IO.PutRope[st, r]};
put:
PROC[d:
INT, f: Rope.
ROPE, v1, v2, v3, v4, v5:
IO.Value ← [null[]]] =
BEGIN
putDepth[d];
IO.PutF[st, f, v1, v2, v3, v4, v5];
END;
put[0, "sei = %g\N", IO.rope[Convert.RopeFromCard[LOOPHOLE[seh.sei]]]];
WITH ser.body
SELECT
FROM
id:
REF id BodySE =>
BEGIN
put[0, "id = %g\N", [rope[id.hash]]];
put[0, "ctx = %g\N", [rope[IF id.idCtx = NIL THEN "NIL" ELSE Convert.RopeFromCard[LOOPHOLE[id.idCtx.ctx]]]]];
SELECT id.special
FROM
normal => rope[" normal\N"];
globalLink => rope[" globalLink\N"];
staticLink => rope[" staticLink\N"];
frameExtension => rope[" frameExtension\N"];
memoryLink => rope[" memoryLink\N"];
returnLink => rope[" returnLink\N"];
ENDCASE => rope[" unknown special\N"];
IF id.flags.valid
THEN
BEGIN
IF id.flags.used THEN rope[" used"] ELSE rope[" not-used"];
IF id.flags.addressed THEN rope[" addressed"] ELSE rope[" not-addressed"];
IF id.flags.assigned THEN rope[" assigned"] ELSE rope[" not-assigned"];
IF id.flags.upLevel THEN rope[" upLevel\N"] ELSE rope[" not-upLevel\N"];
END;
END;
cons: REF cons BodySE => rope["cons\N"];
ENDCASE => rope["unknown ser\N"];
RETURN[IO.RopeFromROS[st]];
END;
Names
Experimental for the moment
RopeHolder: TYPE = REF RopeHolderBody;
RopeHolderBody: TYPE = RECORD[rope: Rope.ROPE];
We assume that this will be called during the fetching of some symbol table record.
WE MUST reduce the number of params to what we actually need
RopeForName:
PROC[cookie: MobCookie, mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase, name: Symbols.Name]
RETURNS[Rope.
ROPE] =
BEGIN
IF name = Symbols.nullName
THEN
RETURN[
NIL]
ELSE
TRUSTED
BEGIN
ropeHolder: RopeHolder ← NARROW[CardTab.Fetch[cookie.stringTable, sym.htb[name].ssIndex].val];
IF ropeHolder # NIL THEN RETURN[ropeHolder.rope]
ELSE
BEGIN
offset: CARDINAL ← sym.htb[name-Symbols.HTRecord.SIZE].ssIndex+1;
ss: ConvertUnsafe.SubString ← [
base: sym.ssb,
offset: offset,
length: sym.htb[name].ssIndex - offset];
rope: Rope.ROPE;
IF ss.offset > ss.base.length OR ss.length > CARD[ss.base.length-ss.offset] THEN ERROR MobError["bogus name"];
rope ← ConvertUnsafe.SubStringToRope[ss]; -- The current DMachine Cedar version of this routine allocates new storage for the rope. I.E., we do not end up pointing back into the symbol table. This is good, because if we ended up pointing back into the symbol table, we would be in deep trouble if the symbol table left the symbol table cache.
ropeHolder ← NEW[RopeHolderBody←[rope]];
IF NOT CardTab.Insert[cookie.stringTable, sym.htb[name].ssIndex, ropeHolder] THEN ERROR MobError["MobAccessImpl confused (by concurrent access?)"];
RETURN[rope];
END;
END;
END;
for debugging
ViewMob: Commander.CommandProc =
BEGIN
args: CommandTool.ArgumentVector ← CommandTool.Parse[cmd];
path: PFSNames.PATH ← PFS.PathFromRope[args[1]];
fileSet: SystemInterface.FileSet ← SystemInterface.CreateFileSet[];
BEGIN ENABLE UNWIND => SystemInterface.CloseFileSet[fileSet];
file: SystemInterface.CirioFile ← SystemInterface.GetCirioFile[fileSet, path];
mob: MobCookie ← CreateMobCookie[file];
inner:
PROC[mobHandle: MobUtilDefs.MobHandle, mobBase: MobDefs.MobBase, sym: SymbolTableBase] =
BEGIN
ERROR; -- so that we can look around with the debugger
END;
ExamineMob[mob, inner];
END;
SystemInterface.CloseFileSet[fileSet];
END;
main code
Commander.Register["ViewMob", ViewMob];
example for using ViewMob
ViewMob Test.mob
END..