DIRECTORY
AMBridge
USING
[GetWorld, IsRemote, nilRemoteFrameHandle, nilRemoteGlobalFrameHandle, RemoteFHFromTV, RemoteFrameHandle, RemoteGFHFromTV, RemoteGlobalFrameHandle, RemotePD, RemoteSED, TVForProc, TVForRemoteFrame, TVForRemoteGFHReferent, TVForRemoteProc, TVForRemoteSignal, TVForSignal, TVToCardinal, TVToRemoteProc,TVToRemoteSignal, WordSequence, WordSequenceRecord],
AMProcessBasic USING[ReturnLink],
AMTypes
USING
[Domain, Error, Index, IndexToTV, Range, Size, TV, TVStatus, TVType, TypeClass, TypedVariable, UnderType],
BrandXSymbolDefs
USING
[SymbolTableBase, rootBodyIndex, BodyIndex, nullBodyIndex, outerContextLevel],
BrandYSymbolDefs
USING
[SymbolTableBase, rootBodyIndex, BodyIndex, nullBodyIndex, outerContextLevel],
DebuggerSwap USING[CallDebugger],
LoadState USING[GlobalFrameToType, local, Acquire, Release],
PrincOps
USING
[BytePC, ControlLink, CSegPrefix, EPRange, Frame, FrameHandle, GlobalFrame, GlobalFrameHandle, localbase, MaxNGfi, NullLink, ProcDesc, SignalDesc, StateVector, UnboundLink, SD, sSignal],
PrincOpsUtils USING[GlobalFrame, Codebase],
RemotePrincOpsUtils USING[RemoteGlobalFrame],
Rope USING[ROPE],
RTSymbolDefs
USING
[BlockContextLevel, BodyIndex, CallableBodyIndex, nullBodyIndex, SymbolConstructorIndex, SymbolIdIndex, SymbolIndex, SymbolTableBase],
RTSymbolOps
USING
[AcquireType, BodyLevel, BodyType, CallableBodyEntryIndex, CallableBTI, ISEConstant, ISEImmutable, ISEType, IsRootBTI, IsTypeSEI, NullBTI, NullSEI, ParentBody, RootBodyType, SEUnderType],
RTSymbols USING[AcquireSTBFromGFH, ReleaseSTB],
RTTypesPrivate
USING
[BuildRecordFieldDescriptor, FieldDescriptor, GetIdConstantValue, GFHToName, RecordComponentISEI, TypedVariableRec, UnwindIndirectProcDesc],
RTTypesRemotePrivate
USING
[AcquireBTIFromRemoteFH, AcquireSTBFromRemoteGFH, GetRemoteEp, GetRemoteFrameHeader, GetRemoteGFHeader, IsRemoteCatchFrame, RemoteGFHToName, RemoteSignalValues],
SafeStorage USING[Type, nullType, fhType, gfhType],
WorldVM USING[CurrentIncarnation, Long, ShortAddress, World, LocalWorld];
raises typeFault
TVToSignal:
PUBLIC
PROC[tv: TypedVariable]
RETURNS[
ERROR
ANY
RETURNS
ANY] = {
SELECT TypeClass[UnderType[TVType[tv]]]
FROM
signal, error => NULL;
nil => RETURN[LOOPHOLE[PrincOps.UnboundLink, ERROR ANY RETURNS ANY]];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
RETURN[LOOPHOLE[TVToCardinal[tv], ERROR ANY RETURNS ANY]];
};
TVForFrame:
PUBLIC
PROC[
fh: PrincOps.FrameHandle,
evalStack: POINTER TO PrincOps.StateVector ← NIL,
return: BOOL ← FALSE,
contextPC: BOOL ← FALSE]
RETURNS[ans:
TV ←
NIL] = {
bti: BodyIndex;
isCatchFrame: BOOLEAN ← FALSE;
IF fh = NIL THEN RETURN[NIL];
[isCatchFrame, bti] ← AcquireBTIFromFH[fh, contextPC
! Error => IF reason = noSymbols THEN {bti ← nullBodyIndex; CONTINUE}];
RETURN[
NEW[TypedVariableRec
← [referentType: [fhType],
head:
[fh[fh: fh, evalStack: evalStack,
bti: bti, isCatchFrame: isCatchFrame, return: return, contextPC: contextPC]],
status: mutable,
field: entire[]
]]];
};
all such tvs have the same (distinguished) type: fhType
raises typeFault
FHFromTV:
PUBLIC
PROC[tv: TypedVariable]
RETURNS[PrincOps.FrameHandle] = {
IF tv = NIL THEN RETURN[NIL];
WITH tv
SELECT
FROM
tr:
REF TypedVariableRec =>
WITH tfh: tr.head
SELECT
FROM
fh => RETURN[tfh.fh];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
};
TVForGFHReferent:
PUBLIC
PROC[gfh: PrincOps.GlobalFrameHandle]
RETURNS[TypedVariable] = {
IF gfh = NIL THEN RETURN[NIL];
RETURN[
NEW[TypedVariableRec
← [referentType: [gfhType], head: [gfh[gfh: gfh]], status: mutable, field: entire[]]]];
};
raises typeFault
GFHFromTV:
PUBLIC
PROC[tv: TypedVariable]
RETURNS[PrincOps.GlobalFrameHandle] = {
IF tv = NIL THEN RETURN[NIL];
WITH tv
SELECT
FROM
tr:
REF TypedVariableRec =>
WITH tgfh: tr.head
SELECT
FROM
gfh => RETURN[tgfh.gfh];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
ENDCASE => ERROR;
};
raises typeFault
GlobalParent:
PUBLIC
SAFE
PROC[tv: TypedVariable
--transfer or local frame--]
RETURNS[TypedVariable--globalFrame--] = TRUSTED {
IF IsRemote[tv]
THEN {
type: Type = TVType[tv];
gfh: RemoteGlobalFrameHandle ← nilRemoteGlobalFrameHandle;
SELECT TypeClass[UnderType[type]]
FROM
localFrame => {
fh: RemoteFrameHandle = RemoteFHFromTV[tv];
IF fh.fh = 0 THEN RETURN[NIL];
gfh ←
[world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh: LOOPHOLE[GetRemoteFrameHeader[fh].accesslink, WorldVM.ShortAddress]];
};
program, procedure => {
pd: RemotePD = TVToRemoteProc[tv];
IF pd.pd = PrincOps.NullLink THEN RETURN[NIL];
gfh ←
[world: pd.world,
worldIncarnation: CurrentIncarnation[pd.world],
gfh: LOOPHOLE[RemoteGlobalFrame[pd.world, pd.pd], WorldVM.ShortAddress]];
};
signal, error => {
sed: RemoteSED = TVToRemoteSignal[tv];
IF sed.sed = PrincOps.NullLink
OR LOOPHOLE[sed.sed, CARDINAL] = 177777B
OR LOOPHOLE[sed.sed, CARDINAL] = LOOPHOLE[UNWIND, CARDINAL]
OR LOOPHOLE[sed.sed, CARDINAL] = LOOPHOLE[ABORTED, CARDINAL]
THEN RETURN[NIL];
gfh ←
[world: sed.world,
worldIncarnation: CurrentIncarnation[sed.world],
gfh: LOOPHOLE[RemoteGlobalFrame[sed.world, sed.sed], WorldVM.ShortAddress]];
};
nil => RETURN[NIL];
ENDCASE => ERROR Error[reason: typeFault, type: type];
RETURN[TVForRemoteGFHReferent[gfh]];
}
ELSE {
-- local case
type: Type = TVType[tv];
gfh: PrincOps.GlobalFrameHandle;
SELECT TypeClass[UnderType[type]]
FROM
localFrame => {
fh: PrincOps.FrameHandle = FHFromTV[tv];
IF fh = NIL THEN RETURN[NIL];
gfh ← fh.accesslink;
};
signal, error => {
sed: PrincOps.ProcDesc = LOOPHOLE[TVToSignal[tv], PrincOps.ProcDesc];
IF sed = PrincOps.NullLink
OR LOOPHOLE[sed, CARDINAL] = 177777B
OR LOOPHOLE[sed, CARDINAL] = LOOPHOLE[UNWIND, CARDINAL]
OR LOOPHOLE[sed, CARDINAL] = LOOPHOLE[ABORTED, CARDINAL]
THEN RETURN[NIL];
gfh ← LOOPHOLE[PrincOpsUtils.GlobalFrame[sed], PrincOps.GlobalFrameHandle];
};
program, procedure => {
pd: PrincOps.ProcDesc
= UnwindIndirectProcDesc[LOOPHOLE[TVToCardinal[tv], PrincOps.ControlLink]];
IF pd = PrincOps.NullLink THEN RETURN[NIL];
gfh ← LOOPHOLE[PrincOpsUtils.GlobalFrame[pd], PrincOps.GlobalFrameHandle];
};
nil => RETURN[NIL];
ENDCASE => ERROR Error[reason: typeFault, type: type];
RETURN[TVForGFHReferent[gfh]];
};
}; -- end GlobalParent
ContextPC: PUBLIC SAFE PROC[tv: TypedVariable--localFrame--]
RETURNS[ans: PrincOps.BytePC] =
TRUSTED {
WITH th:
NARROW[tv,
REF TypedVariableRec].head
SELECT
FROM
remoteFH =>
ans ← [GetRemoteFrameHeader[RemoteFHFromTV[tv]].pc
- (IF th.contextPC THEN 0 ELSE 1)];
fh => ans ← [th.fh.pc - (IF th.contextPC THEN 0 ELSE 1)];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
};
IsStarted:
PUBLIC
SAFE
PROC[tv: TypedVariable
--globalFrame--]
RETURNS[
BOOL] =
TRUSTED {
rtr: REF TypedVariableRec ← NARROW[tv];
IF IsRemote[tv]
THEN {
rgfh: REF PrincOps.GlobalFrame = GetRemoteGFHeader[RemoteGFHFromTV[tv]];
RETURN[(NOT rgfh.code.out) OR rgfh.started];
}
ELSE
WITH th: rtr.head
SELECT
FROM
gfh => RETURN[(NOT th.gfh.code.out) OR th.gfh.started];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
};
IsCopied:
PUBLIC
SAFE
PROC[tv: TypedVariable
--globalFrame--]
RETURNS[
BOOL] =
TRUSTED {
rtr: REF TypedVariableRec ← NARROW[tv];
IF IsRemote[tv]
THEN {
rgfh: REF PrincOps.GlobalFrame = GetRemoteGFHeader[RemoteGFHFromTV[tv]];
RETURN[rgfh.copied];
}
ELSE
WITH th: rtr.head SELECT FROM
gfh => RETURN[th.gfh.copied];
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
};
Procedure:
PUBLIC
SAFE
PROC[tv: TypedVariable
--localFrame--]
RETURNS [TypedVariable--procedure--] = TRUSTED {
IF IsRemote[tv]
THEN {
stb: SymbolTableBase;
rtr: REF TypedVariableRec;
bti: BodyIndex;
fh: RemoteFrameHandle ← nilRemoteFrameHandle;
IF tv = NIL THEN RETURN[NIL];
rtr ← NARROW[tv];
WITH th: rtr.head
SELECT
FROM
remoteFH =>
IF th.isCatchFrame
THEN bti ← nullBodyIndex
ELSE {
bti ← th.bti;
IF NullBTI[bti] -- last time there were no symbols for this turkey
THEN {
name: ROPE;
fh ← RemoteFHFromTV[tv];
bti ← th.bti ← AcquireBTIFromRemoteFH[fh, th.contextPC];
returns BTNull if still no symbols
IF
NOT NullBTI[th.bti]
THEN {
[th.isCatchFrame, th.bti] ← IsRemoteCatchFrame[fh, bti];
bti ← th.bti;
IF th.isCatchFrame THEN bti ← nullBodyIndex;
}
ELSE {
name ← RemoteGFHToName
[[world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh: LOOPHOLE[GetRemoteFrameHeader[fh].accesslink, WorldVM.ShortAddress]]];
ERROR AMTypes.Error[reason: noSymbols, msg: name];
};
};
};
ENDCASE => bti ← nullBodyIndex;
IF NullBTI[bti] THEN ERROR Error[reason: typeFault, type: TVType[tv]];
fh ← RemoteFHFromTV[tv];
IF fh.fh = 0 THEN RETURN[NIL];
stb ← AcquireSTBFromRemoteGFH
[[world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh: LOOPHOLE[GetRemoteFrameHeader[fh].accesslink, WorldVM.ShortAddress]]];
FOR bti ← bti, ParentBody[stb, bti]
UNTIL NullBTI[bti]
DO
IF CallableBTI[stb, bti]
THEN {
sppd: PrincOps.ProcDesc ← LOOPHOLE[PrincOps.UnboundLink, PrincOps.ProcDesc];
entryIndex: [0..PrincOps.EPRange*PrincOps.MaxNGfi)
← CallableBodyEntryIndex[stb, LOOPHOLE[bti, CallableBodyIndex]];
IF entryIndex = 0 THEN ERROR Error[reason: typeFault, type: TVType[tv]];
start proc frame
sppd.gfi
← GetRemoteGFHeader
[[world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh:
LOOPHOLE[GetRemoteFrameHeader[fh].accesslink,
WorldVM.ShortAddress]
]].gfi + entryIndex/PrincOps.EPRange;
sppd.ep ← entryIndex MOD PrincOps.EPRange;
ReleaseSTB[stb];
RETURN[TVForRemoteProc[[fh.world, CurrentIncarnation[fh.world], sppd]]];
};
ENDLOOP;
ReleaseSTB[stb];
RETURN[NIL];
}
ELSE {
-- non-remote localFrame TV arg to Procedure
stb: SymbolTableBase;
rtr: REF TypedVariableRec;
bti: BodyIndex;
fh: PrincOps.FrameHandle;
IF tv = NIL THEN RETURN[NIL];
rtr ← NARROW[tv];
WITH th: rtr.head
SELECT
FROM
fh =>
IF th.isCatchFrame
THEN ERROR Error[reason: typeFault, type: TVType[tv]]
ELSE {
bti ← th.bti;
XXX be careful about access via assign to RC variables that appear to be in a local frame (startproc)
but really live in the GF. Also remote case
IF NullBTI[bti] -- no symbols for this localFrame TV
THEN {
fh ← FHFromTV[tv];
bti ← th.bti ← AcquireBTIFromFH[fh, th.contextPC ! ANY => CONTINUE].bti;
returns BTNull if still no symbols
IF NOT NullBTI[th.bti]
THEN {
[th.isCatchFrame, th.bti] ← IsCatchFrame[fh, bti];
bti ← th.bti;
IF th.isCatchFrame
THEN ERROR Error[reason: typeFault, type: TVType[tv]];
}
ELSE
ERROR AMTypes.Error[reason: noSymbols,
msg: GFHToName[th.fh.accesslink]];
};
};
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
here with bti # nullBodyIndex
fh ← FHFromTV[tv];
IF fh = NIL THEN RETURN[NIL];
stb ← AcquireSTBFromGFH[fh.accesslink];
search up thru enclosing blocks till a callable one
FOR bti ← bti, ParentBody[stb, bti]
UNTIL NullBTI[bti]
DO
IF CallableBTI[stb, bti]
THEN {
sppd: PrincOps.ProcDesc ← LOOPHOLE[PrincOps.UnboundLink, PrincOps.ProcDesc];
entryIndex: [0..PrincOps.EPRange*PrincOps.MaxNGfi)
← CallableBodyEntryIndex[stb, LOOPHOLE[bti, CallableBodyIndex]];
IF entryIndex = 0 THEN ERROR Error[reason: typeFault, type: TVType[tv]];
going up from a block or a body inside a startproc.
sppd.gfi ← fh.accesslink.gfi + entryIndex/PrincOps.EPRange;
sppd.ep ← entryIndex MOD PrincOps.EPRange;
ReleaseSTB[stb];
RETURN[TVForProc[LOOPHOLE[sppd, PROC ANY RETURNS ANY]]];
};
ENDLOOP;
ReleaseSTB[stb];
here if the local frame has no enclosing callable body.
This can't happen.
ERROR;
};
}; -- end Procedure
Signal:
PUBLIC
SAFE
PROC[tv: TypedVariable
--localFrame--]
RETURNS[ans: TypedVariable--signal descriptor--] = TRUSTED {
IF IsRemote[tv]
THEN {
world: World;
sed: PrincOps.SignalDesc;
[world: world, signal: sed] ← RemoteSignalValues[tv];
ans ← TVForRemoteSignal
[[world: world, worldIncarnation: CurrentIncarnation[world], sed: sed]]
}
ELSE ans ← TVForSignal[
LOOPHOLE[RemoteSignalValues[tv].signal,
ERROR ANY RETURNS ANY]]};
Argument:
PUBLIC
SAFE
PROC[tv: TypedVariable
--local or catch Frame--, index: Index]
RETURNS[TypedVariable] = TRUSTED {RETURN[ArgOrResult[tv, index, Domain]]};
Result:
PUBLIC
SAFE
PROC[tv: TypedVariable
--local or catch Frame--, index: Index]
RETURNS[TypedVariable] = TRUSTED {RETURN[ArgOrResult[tv, index, Range]]};
ArgOrResult:
PROC[
tv: TypedVariable--local or catch Frame--,
index: Index,
domainOrRange: PROC[Type] RETURNS [Type]
]
RETURNS[TypedVariable] = {
type: Type;
catch: BOOL;
message: UNSPECIFIED;
world: World;
argsTV: REF TypedVariableRec;
tvr: REF TypedVariableRec;
offset: INTEGER ← 0;
returns a signal type if tv is for a catch frame,
a procedure type if tv is for a local frame,
raises TypeFault otherwise.
GetSignalOrProcType:
PROC[tv: TypedVariable]
RETURNS [type: Type ← nullType, catch: BOOLEAN ← FALSE] = {
rtr: REF TypedVariableRec ← NARROW[tv];
WITH th: rtr.head
SELECT
FROM
fh => {
catch ← th.isCatchFrame;
type ← TVType[IF catch THEN Signal[tv] ELSE Procedure[tv]];
};
remoteFH => {
catch ← th.isCatchFrame;
type ← TVType[IF catch THEN Signal[tv] ELSE Procedure[tv]];
};
ENDCASE => ERROR Error[reason: typeFault, type: TVType[tv]];
};
IF tv = NIL THEN ERROR Error[reason: typeFault, type: nullType];
tvr ← NARROW[tv];
[type, catch] ← GetSignalOrProcType[tv];
IF type = nullType THEN ERROR Error[reason: badIndex];
type ← domainOrRange[type];
IF type = nullType THEN ERROR Error[reason: badIndex];
WITH tvh: tvr.head
SELECT
FROM
fh, remoteFH => NULL;
ENDCASE => ERROR Error[reason: typeFault, type: type];
IF ~catch OR domainOrRange = Range
THEN {
-- find the specified element in the frame, create a TV for it
BuildEmbeddedTV:
PROC[stb: SymbolTableBase, isei: SymbolIdIndex] = {
sei: SymbolIndex = ISEType[stb, isei];
csei: SymbolConstructorIndex ← SEUnderType[stb, sei];
bitsForType:
LONG
CARDINAL ←
(
WITH stb
SELECT
FROM
t: SymbolTableBase.x => t.e.BitsForType[NARROW[csei, SymbolConstructorIndex.x].e],
t: SymbolTableBase.y => t.e.BitsForType[NARROW[csei, SymbolConstructorIndex.x].e],
ENDCASE => ERROR); -- bits for the value in the field
fieldBits:
CARDINAL ←
(
WITH stb
SELECT
FROM
t: SymbolTableBase.x => t.e.seb[NARROW[isei, SymbolIdIndex.x].e].idInfo,
t: SymbolTableBase.y => t.e.seb[NARROW[isei, SymbolIdIndex.x].e].idInfo,
ENDCASE => ERROR); -- bits for the field
fieldBitOffset:
CARDINAL ←
(
WITH stb
SELECT
FROM
t: SymbolTableBase.x => t.e.seb[NARROW[isei, SymbolIdIndex.x].e].idValue,
t: SymbolTableBase.y => t.e.seb[NARROW[isei, SymbolIdIndex.x].e].idValue,
ENDCASE => ERROR);
bit offset of the field within the local frame
cType: Type ← AcquireType[stb, sei];
IF ISEConstant[stb, isei]
THEN {
IF IsRemote[tv] AND IsTypeSEI[ISEType[stb, isei]]
THEN argsTV ←
NEW[TypedVariableRec ←
[referentType: [cType],
head: [constant[]], -- type code is valid in local world
status: const,
field: constant[value: GetIdConstantValue[tv, stb, isei]]]]
ELSE
-- either a constant component of a local tv or not a type constant
argsTV ← NEW[TypedVariableRec ←
[referentType: [cType],
head:
(
WITH tv
SELECT
FROM
tr: REF TypedVariableRec => tr.head,
ENDCASE => ERROR),
status: const,
field: constant[value: GetIdConstantValue[tv, stb, isei]]]];
}
ELSE
argsTV ←
NEW[TypedVariableRec ←
[referentType: [cType],
head:
(
WITH tv
SELECT
FROM
tr:REF TypedVariableRec => tr.head,
ENDCASE => ERROR),
status: (IF ISEImmutable[stb, isei] THEN readOnly ELSE TVStatus[tv]),
field:
embedded[
fd: BuildRecordFieldDescriptor[tv, fieldBitOffset, fieldBits, bitsForType]]]];
}; -- END BuildEmbeddedTV
Begin Here
RTTypesPrivate.RecordComponentISEI[UnderType[type], index, BuildEmbeddedTV];
RETURN[argsTV];
}; -- END ~catch OR domainOrRange = Range
gotta do something different if this is an arg to a catch phrase
[world: world, message: message] ← RemoteSignalValues[tv];
message is either a pointer to a long arg record OR a single word value
IF Size[type] = 1
THEN {
ws: WordSequence = NEW[WordSequenceRecord[1]];
ws[0] ← LOOPHOLE[message, CARDINAL];
argsTV ← NEW[TypedVariableRec
← [referentType: [type],
head: tvr.head,
status: const,
field: constant[value: ws]]]
}
ELSE
IF IsRemote[tv]
THEN
argsTV ←
NEW[TypedVariableRec
← [referentType: [type],
head:
[remotePointer
[remotePointer:
[world: world,
worldIncarnation: CurrentIncarnation[world],
ptr: Long[world: world, addr: message]]]],
status: readOnly,
field: entire[]]]
ELSE argsTV ←
NEW[TypedVariableRec
← [referentType: [type],
head: [pointer[ptr: LONG[LOOPHOLE[message, POINTER]]]],
status: readOnly,
field: entire[]]];
RETURN[IndexToTV[argsTV, index]];
}; -- end ArgOrResult
EnclosingBody:
PUBLIC
SAFE
PROC[tv: TypedVariable
--localFrame--]
RETURNS[TypedVariable--localFrame--] = TRUSTED { -- maybe NIL
NOTE: if tv represents a block within a catch phrase, EnclosingBody may return the wrong context; i.e., the one enclosing the catch phrase. This won't come up if there is no block in the catch phrase at all; TVForFrame will detect it and do the right thing. It's harder to do the right thing here. The right solution is to put catch phrases in the body table!!!
IF IsRemote[tv]
THEN {
stb: SymbolTableBase = AcquireSTBFromRemoteGFH
[[world: GetWorld[tv],
worldIncarnation: CurrentIncarnation[GetWorld[tv]],
gfh:
LOOPHOLE[GetRemoteFrameHeader[RemoteFHFromTV[tv]].accesslink,
WorldVM.ShortAddress]]];
rtr: REF TypedVariableRec ← NARROW[tv ! UNWIND => ReleaseSTB[stb]];
bti: BodyIndex;
evalStack: WordSequence;
isCatchFrame: BOOL;
remoteFH: RemoteFrameHandle ← nilRemoteFrameHandle;
contextPC: BOOL;
level: BlockContextLevel;
return:
BOOL;
static nesting level, associated with a CTX (optimization: blocks, instead of counting up, get level of enclosing proc).
0 (lZ)=> off in the heap (not part of a frame...lifetime determined by some sort of runtime storage management, e.g. records, ctx for names of guys in an emum);
1 (lG) => global frame, outermost proc
2 (lL) and >2 => nested procedure.
catch phrase in main prog would have a level of 2 (tho these are not recorded, because there are not symbol table entries for catch phrases.
WITH th: rtr.head
SELECT
FROM
remoteFH => {
bti ← th.bti;
evalStack ← th.evalStack;
isCatchFrame ← th.isCatchFrame;
remoteFH ← th.remoteFrameHandle;
contextPC ← th.contextPC;
return ← th.return;
};
ENDCASE => {
ReleaseSTB[stb];
ERROR Error[reason: typeFault, type: TVType[tv]];
};
a fh tv will have NullBTI[bti] if there are no symbols for its referent or if it represents a frame for a startproc or catch phrase at a point outside the scope of locals
IF NullBTI[bti]
THEN {
IF isCatchFrame
THEN {
get the bti of the smallest body that encloses the PC (if it's rootBodyIndex then RETURN[NIL]). Verify that this body is a body contained (not necessarily properly) within a frame somewhere up the static link. Return a tv for such a frame.
rfh: RemoteFrameHandle = RemoteFHFromTV[tv];
rgfh: WorldVM.ShortAddress ← LOOPHOLE[GetRemoteFrameHeader[rfh].accesslink];
bti ← SmallestBTIFromRemoteFH[rfh, stb, contextPC ! UNWIND => ReleaseSTB[stb]];
IF IsRootBTI[bti]
THEN {ReleaseSTB[stb];
RETURN[
NIL]};
-- not in the scope of any locals
starting with the frame of the most recent instance of the smallest enclosing static parent of the catch frame, search up the static chain to find the first frame that encloses the catch frame
IF GetRemoteFrameHeader[remoteFH].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
tv ← TVForRemoteFrame
[[world: remoteFH.world,
worldIncarnation: CurrentIncarnation[remoteFH.world],
fh:
LOOPHOLE[GetRemoteFrameHeader[remoteFH].staticlink
- PrincOps.localbase, WorldVM.ShortAddress]]];
UNTIL IsBodyEncloser[er: tv, ee: bti, stb: stb]
look up the static chain to find an encloser of bti
DO
rmtFH: RemoteFrameHandle ← RemoteFHFromTV[tv];
IF
LOOPHOLE[GetRemoteFrameHeader[rmtFH].accesslink, WorldVM.ShortAddress] # rgfh
THEN {ReleaseSTB[stb]; ERROR};
(bti # rootBodyIndex) => one better exist
IF GetRemoteFrameHeader[rmtFH].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
tv ← TVForRemoteFrame
[[world: rmtFH.world,
worldIncarnation: CurrentIncarnation[rmtFH.world],
fh:
LOOPHOLE[GetRemoteFrameHeader[rmtFH].staticlink
- PrincOps.localbase, WorldVM.ShortAddress]]];
ENDLOOP;
rtr ← NARROW[tv, REF TypedVariableRec];
WITH th: rtr.head
SELECT
FROM
remoteFH => {
Easy if we already have the right bti, otherwise get the new value of isCatchFrame
IF bti = th.bti THEN RETURN[tv];
evalStack ← NIL; -- NOTE
isCatchFrame ← th.isCatchFrame;
return ← th.return;
contextPC ← th.contextPC;
};
ENDCASE => ERROR;
ReleaseSTB[stb];
RETURN[
NEW[TypedVariableRec ←
[referentType: [fhType],
head:
[remoteFH[remoteFrameHandle: RemoteFHFromTV[tv],
evalStack: evalStack,
bti: bti,
isCatchFrame: isCatchFrame,
return: return,
contextPC: contextPC]],
status: mutable,
field: entire[]]]];
}
ELSE {ReleaseSTB[stb]; RETURN[NIL]} -- either no symbols or no locals
}; -- end IF NullBTI[bti]
IF CallableBTI[stb, bti]
THEN
No more enclosing blocks: this one is callable and not a catch frame
IF (
WITH BodyLevel[stb, bti]
SELECT
FROM
-- TRUE => not a nested procedure
t: BlockContextLevel.x => t.e <= bx.outerContextLevel,
t: BlockContextLevel.y => t.e <= by.outerContextLevel,
ENDCASE => ERROR)
THEN {ReleaseSTB[stb]; RETURN[NIL]}
ELSE {
-- this is a frame for a nested procedure
ReleaseSTB[stb];
IF GetRemoteFrameHeader[remoteFH].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
RETURN
[TVForRemoteFrame
[[world: remoteFH.world,
worldIncarnation: CurrentIncarnation[remoteFH.world],
fh:
LOOPHOLE[GetRemoteFrameHeader[remoteFH].staticlink
- PrincOps.localbase, WorldVM.ShortAddress]]]];
};
level ← BodyLevel[stb, bti];
bti ← ParentBody[stb, bti]; -- A Callable bt entry also describes the outer block locals
IF IsRootBTI[bti] THEN bti ← nullBodyIndex;
means either that tv represents a frame for a startproc or catch phrase at a place outside the scope of locals.
IF isCatchFrame
AND (
NOT NullBTI[bti])
AND level # BodyLevel[stb, bti]
THEN
{
When the level changes inside of a catch phrase then we have to go through the static link (local 0) to get to the frame that corresponds to this bti. The static link points at local 0, not the frame base (=> subtract localbase).
ReleaseSTB[stb]; -- we no longer need the stb
IF GetRemoteFrameHeader[remoteFH].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
tv ← TVForRemoteFrame
[[world: remoteFH.world,
worldIncarnation: CurrentIncarnation[remoteFH.world],
fh:
LOOPHOLE[GetRemoteFrameHeader[remoteFH].staticlink- PrincOps.localbase,
WorldVM.ShortAddress]]];
rtr ← NARROW[tv, REF TypedVariableRec];
WITH th: rtr.head
SELECT
FROM
remoteFH => {
Easy if we already have the right bti, otherwise get the new value of isCatchFrame
IF bti = th.bti THEN RETURN[tv];
evalStack ← NIL; -- NOTE
isCatchFrame ← th.isCatchFrame;
return ← th.return;
contextPC ← th.contextPC;
};
ENDCASE => ERROR
}
ELSE ReleaseSTB[stb];
RETURN[NEW[TypedVariableRec ←
[referentType: [fhType],
head:
[remoteFH[remoteFrameHandle: RemoteFHFromTV[tv],
evalStack: evalStack,
bti: bti,
isCatchFrame: isCatchFrame,
return: return,
contextPC: contextPC]],
status: mutable,
field: entire[]]]];
}
ELSE {
-- local case of EnclosingBody
stb: SymbolTableBase = AcquireSTBFromGFH[FHFromTV[tv].accesslink];
rtr: REF TypedVariableRec ← NARROW[tv ! UNWIND => ReleaseSTB[stb]];
bti: BodyIndex;
evalStack: POINTER TO PrincOps.StateVector;
isCatchFrame: BOOL;
contextPC: BOOL;
level: BlockContextLevel;
return:
BOOL;
static nesting level, associated with a CTX (optimization: blocks, instead of counting up, get level of enclosing proc).
0 (lZ)=> off in the heap (not part of a frame...lifetime determined by some sort of runtime storage management, e.g. records, ctx for names of guys in an emum);
1 (lG) => global frame, outermost proc
2 (lL) and >2 => nested procedure.
catch phrase in main prog would have a level of 2 (tho these are not recorded, because there are not symbol table entries for catch phrases.
WITH th: rtr.head
SELECT
FROM
fh => {
bti ← th.bti;
evalStack ← th.evalStack;
isCatchFrame ← th.isCatchFrame;
contextPC ← th.contextPC;
return ← th.return;
};
ENDCASE => {
ReleaseSTB[stb];
ERROR Error[reason: typeFault, type: TVType[tv]];
};
a fh tv will have NullBTI[bti] if there are no symbols for its referent or if it represents a frame for a startproc or catch phrase at a point outside the scope of locals
IF NullBTI[bti]
THEN {
IF isCatchFrame
THEN {
get the bti of the smallest body that encloses the PC (if it's rootBodyIndex then RETURN[NIL]). Verify that this body is a body contained (not necessarily properly) within a frame somewhere up the static link. Return a tv for such a frame.
fh: PrincOps.FrameHandle = FHFromTV[tv];
gfh: PrincOps.GlobalFrameHandle ← fh.accesslink;
bti ← SmallestBTIFromFH[fh, stb, contextPC ! UNWIND => ReleaseSTB[stb]];
IF IsRootBTI[bti] THEN {ReleaseSTB[stb]; RETURN[NIL]}; -- not in the scope of any locals
starting with the frame of the most recent instance of the smallest enclosing static parent of the catch frame, search up the static chain to find the first frame that encloses the catch frame
IF fh.staticlink = NIL THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
tv ← TVForFrame[LOOPHOLE[fh.staticlink - PrincOps.localbase, PrincOps.FrameHandle]];
UNTIL IsBodyEncloser[er: tv, ee: bti, stb: stb]
look up the static chain to find an encloser of bti
DO
IF FHFromTV[tv].accesslink # gfh THEN {ReleaseSTB[stb]; ERROR};
(bti # rootBodyIndex) => one better exist
IF FHFromTV[tv].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
tv ← TVForFrame[
LOOPHOLE[FHFromTV[tv].staticlink - PrincOps.localbase,
PrincOps.FrameHandle]];
ENDLOOP;
rtr ← NARROW[tv, REF TypedVariableRec];
WITH th: rtr.head
SELECT
FROM
fh => {
Easy if we already have the right bti, otherwise get the new value of isCatchFrame
IF bti = th.bti THEN RETURN[tv];
evalStack ← NIL; -- NOTE
isCatchFrame ← th.isCatchFrame;
return ← th.return;
contextPC ← th.contextPC;
};
ENDCASE => ERROR;
ReleaseSTB[stb];
RETURN[NEW[TypedVariableRec ←
[referentType: [fhType],
head:
[fh[fh: FHFromTV[tv],
evalStack: evalStack,
bti: bti,
isCatchFrame: isCatchFrame,
return: return,
contextPC: contextPC]],
status: mutable,
field: entire[]]]];
}
ELSE {ReleaseSTB[stb]; RETURN[NIL]} -- either no symbols or no locals
}; -- end IF NullBTI[bti]
IF CallableBTI[stb, bti]
THEN
No more enclosing blocks: this one is callable and not a catch frame
IF (
WITH BodyLevel[stb, bti]
SELECT
FROM
-- TRUE => not a nested procedure
t: BlockContextLevel.x => t.e <= bx.outerContextLevel,
t: BlockContextLevel.y => t.e <= by.outerContextLevel,
ENDCASE => ERROR)
THEN {ReleaseSTB[stb]; RETURN[NIL]}
ELSE {
-- this is a frame for a nested procedure
ReleaseSTB[stb];
IF FHFromTV[tv].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
RETURN[TVForFrame[
LOOPHOLE[FHFromTV[tv].staticlink - PrincOps.localbase,
PrincOps.FrameHandle]]];
};
level ← BodyLevel[stb, bti];
bti ← ParentBody[stb, bti]; -- A Callable bt entry also describes the outer block locals
IF IsRootBTI[bti] THEN bti ← nullBodyIndex;
means either that tv represents a frame for a startproc or catch phrase at a place outside the scope of locals.
IF isCatchFrame AND (NOT NullBTI[bti]) AND level # BodyLevel[stb, bti]
THEN {
When the level changes inside of a catch phrase then we have to go through the static link (local 0) to get to the frame that corresponds to this bti. The static link points at local 0, not the frame base (=> subtract localbase).
ReleaseSTB[stb]; -- we no longer need the stb
IF FHFromTV[tv].staticlink =
NIL
THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
tv ← TVForFrame[
LOOPHOLE[FHFromTV[tv].staticlink - PrincOps.localbase,
PrincOps.FrameHandle]];
rtr ← NARROW[tv, REF TypedVariableRec];
WITH th: rtr.head
SELECT
FROM
fh => {
Easy if we already have the right bti, otherwise get the new value of isCatchFrame
IF bti = th.bti THEN RETURN[tv];
evalStack ← NIL; -- NOTE
isCatchFrame ← th.isCatchFrame;
return ← th.return;
contextPC ← th.contextPC;
};
ENDCASE => ERROR
}
ELSE ReleaseSTB[stb];
RETURN[
NEW[TypedVariableRec ←
[referentType: [fhType],
head: [fh[fh: FHFromTV[tv],
evalStack: evalStack,
bti: bti,
isCatchFrame: isCatchFrame,
return: return,
contextPC: contextPC]],
status: mutable,
field: entire[]]]];
}; -- end local case of EnclosingBody
}; -- end EnclosingBody
Locals:
PUBLIC
SAFE
PROC[tv: TypedVariable
--localFrame--]
RETURNS[TypedVariable--record--] = TRUSTED { -- maybe NIL
IF IsRemote[tv]
THEN {
stb: SymbolTableBase;
rtr: REF TypedVariableRec;
bti: BodyIndex;
fh: RemoteFrameHandle = RemoteFHFromTV[tv];
type: Type;
IF fh = nilRemoteFrameHandle THEN RETURN[NIL];
stb ← AcquireSTBFromRemoteGFH
[[world: fh.world,
worldIncarnation: CurrentIncarnation[fh.world],
gfh: LOOPHOLE[GetRemoteFrameHeader[fh].accesslink, WorldVM.ShortAddress]]];
rtr ← NARROW[tv ! UNWIND => ReleaseSTB[stb]];
bti ← (WITH th: rtr.head SELECT FROM remoteFH => th.bti, ENDCASE => nullBodyIndex);
IF NullBTI[bti]
OR IsRootBTI[bti]
OR NullSEI[BodyType[stb, bti]]
THEN {ReleaseSTB[stb]; RETURN[NIL]};
type ← AcquireType[stb, BodyType[stb, bti] ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
RETURN[
NEW[TypedVariableRec ←
[referentType: [type], head: rtr.head, status: mutable, field: entire[]]]];
}
ELSE {
stb: SymbolTableBase;
rtr: REF TypedVariableRec;
bti: BodyIndex;
fh: PrincOps.FrameHandle = FHFromTV[tv];
type: Type;
IF fh = NIL THEN RETURN[NIL];
stb ← AcquireSTBFromGFH[fh.accesslink];
rtr ← NARROW[tv ! UNWIND => ReleaseSTB[stb]];
bti ← (WITH th: rtr.head SELECT FROM fh => th.bti, ENDCASE => nullBodyIndex);
IF NullBTI[bti]
OR IsRootBTI[bti]
OR NullSEI[BodyType[stb, bti]]
THEN {ReleaseSTB[stb]; RETURN[NIL]};
type ← AcquireType[stb, BodyType[stb, bti] ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
RETURN[
NEW[TypedVariableRec ←
[referentType: [type], head: rtr.head, status: mutable, field: entire[]]]];
};
};
DynamicParent:
PUBLIC
SAFE
PROC[tv: TypedVariable
--localFrame--]
RETURNS[TypedVariable--localFrame--] = TRUSTED {
IF IsRemote[tv]
THEN {
remoteFH: RemoteFrameHandle = RemoteFHFromTV[tv];
cl: PrincOps.ControlLink;
IF remoteFH.fh = 0 THEN RETURN[NIL];
cl ← AMProcessBasic.ReturnLink
[remoteFH.world, LOOPHOLE[remoteFH.fh, PrincOps.FrameHandle]];
IF cl.proc OR cl.indirect
THEN RETURN[NIL]
ELSE
RETURN[TVForRemoteFrame
[[world: remoteFH.world,
worldIncarnation: CurrentIncarnation[remoteFH.world],
fh: LOOPHOLE[cl.frame, WorldVM.ShortAddress]]]];
}
ELSE {
fh: PrincOps.FrameHandle ← FHFromTV[tv];
cl: PrincOps.ControlLink;
IF fh = NIL THEN RETURN[NIL];
cl ← AMProcessBasic.ReturnLink[WorldVM.LocalWorld[], fh];
IF cl.proc OR cl.indirect THEN RETURN[NIL] ELSE RETURN[TVForFrame[cl.frame]];
};
};
Globals:
PUBLIC
SAFE
PROC[tv: TypedVariable
--globalFrame--]
RETURNS[TypedVariable--record--] = TRUSTED {
IF IsRemote[tv]
THEN {
tvr: REF TypedVariableRec;
gfh: RemoteGlobalFrameHandle ← nilRemoteGlobalFrameHandle;
type: Type;
IF tv = NIL THEN RETURN[NIL];
tvr ← NARROW[tv];
gfh ← RemoteGFHFromTV[tv];
IF gfh = nilRemoteGlobalFrameHandle THEN RETURN[NIL];
{ stb: SymbolTableBase = AcquireSTBFromRemoteGFH[gfh];
type ← AcquireType[stb, RootBodyType[stb] ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
};
RETURN[
NEW[TypedVariableRec ←
[referentType: [type], head: tvr.head, status: mutable, field: entire[]]]];
}
ELSE {
tvr: REF TypedVariableRec;
gfh: PrincOps.GlobalFrameHandle;
type: Type;
IF tv = NIL THEN RETURN[NIL];
tvr ← NARROW[tv];
gfh ← GFHFromTV[tv];
IF gfh = NIL THEN RETURN[NIL];
LoadState.local.Acquire[];
type ← LoadState.local.GlobalFrameToType[gfh ! UNWIND => LoadState.local.Release[]];
LoadState.local.Release[];
IF type = nullType
THEN {
stb: SymbolTableBase = AcquireSTBFromGFH[gfh];
type ← AcquireType[stb, RootBodyType[stb] ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
};
RETURN[
NEW[TypedVariableRec ←
[referentType: [type], head: tvr.head, status: mutable, field: entire[]]]];
};
};
IsBodyEncloser:
PROC[er: TypedVariable, ee: BodyIndex, stb: SymbolTableBase]
RETURNS[ans: BOOL ← FALSE] = {
rtr: REF TypedVariableRec ← NARROW[er];
WITH th: rtr.head
SELECT
FROM
fh => {
-- does th.bti enclose ee?
WITH stb
SELECT
FROM
t: SymbolTableBase.x =>
FOR bti: bx.BodyIndex ←
NARROW[th.bti, BodyIndex.x].e, t.e.SonBti[bti]
UNTIL bti = bx.nullBodyIndex
DO IF bti = NARROW[ee, BodyIndex.x].e THEN RETURN[TRUE];
ENDLOOP;
t: SymbolTableBase.y =>
FOR bti: by.BodyIndex ←
NARROW[th.bti, BodyIndex.y].e, t.e.SonBti[bti]
UNTIL bti = by.nullBodyIndex
DO IF bti = NARROW[ee, BodyIndex.y].e THEN RETURN[TRUE];
ENDLOOP;
ENDCASE => ERROR;
};
remoteFH => {
-- does th.bti enclose ee?
WITH stb
SELECT
FROM
t: SymbolTableBase.x =>
FOR bti: bx.BodyIndex ←
NARROW[th.bti, BodyIndex.x].e, t.e.SonBti[bti]
UNTIL bti = bx.nullBodyIndex
DO IF bti = NARROW[ee, BodyIndex.x].e THEN RETURN[TRUE];
ENDLOOP;
t: SymbolTableBase.y =>
FOR bti: by.BodyIndex ←
NARROW[th.bti, BodyIndex.y].e, t.e.SonBti[bti]
UNTIL bti = by.nullBodyIndex
DO IF bti = NARROW[ee, BodyIndex.y].e THEN RETURN[TRUE];
ENDLOOP;
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
Cases: normal, startproc, block in startproc, catch frame, block in catch frame, catch frame in startproc. Possibly no symbols. all cases below can raise Error[noSymbols] normal: returns a vanilla-flavored (maybe callable) bti catch frame: returns a non-callable bti or nullBodyIndex (if outside scope of locals) startproc: returns a vanilla-flavored bti, maybe rootBodyIndex
AcquireBTIFromFH:
PROC[fh: PrincOps.FrameHandle, contextPC:
BOOL]
RETURNS[isCatchFrame: BOOL, bti: BodyIndex] = {
stb: SymbolTableBase = AcquireSTBFromGFH[fh.accesslink];
bti ← SmallestBTIFromFH[fh, stb, contextPC ! UNWIND => ReleaseSTB[stb]];
if fh is for a catch phrase, bti might be for a proc that contains it.
[isCatchFrame, bti] ← IsCatchFrame[fh, bti ! UNWIND => ReleaseSTB[stb]];
ReleaseSTB[stb];
RETURN[isCatchFrame, bti];
};
SmallestBTIFromFH:
PROC[fh: PrincOps.FrameHandle, stb: SymbolTableBase, contextPC:
BOOL]
RETURNS[bti: BodyIndex] = {
start: PrincOps.BytePC;
epn: CARDINAL;
framePC: PrincOps.BytePC = [(fh.pc - (IF contextPC THEN 0 ELSE 1))];
[epn, start] ← GetEp[framePC, fh.accesslink, stb];
if epn = 0 then this is a frame for the startproc or for a catchframe in the startproc
bti ← ConvertCbti[
base: stb,
pc: framePC,
start: start,
lastBti: LOOPHOLE[GetCBTI[stb, epn], BodyIndex]
];
};
SmallestBTIFromRemoteFH:
PROC[rfh: RemoteFrameHandle, stb: SymbolTableBase, contextPC: BOOL]
RETURNS[bti: BodyIndex] = {
start: PrincOps.BytePC;
epn: CARDINAL;
remoteFrame: REF PrincOps.Frame = GetRemoteFrameHeader[rfh];
framePC: PrincOps.BytePC = [(remoteFrame.pc - (IF contextPC THEN 0 ELSE 1))];
[epn, start] ← GetRemoteEp[
pc: framePC,
gf: [world: rfh.world,
worldIncarnation: CurrentIncarnation[rfh.world],
gfh: LOOPHOLE[remoteFrame.accesslink, ShortAddress]],
stb: stb];
if epn = 0 then this is a frame for the startproc or for a catchframe in the startproc
bti ← ConvertCbti[
base: stb,
pc: framePC,
start: start,
lastBti: LOOPHOLE[GetCBTI[stb, epn], BodyIndex]];
};
finds the cbti for the specified entrypoint
GetCBTI:
PUBLIC
PROC[stb: SymbolTableBase, epn:
CARDINAL]
RETURNS[cbti: CallableBodyIndex] = {
IsThisItX:
PROC[bti: bx.BodyIndex]
RETURNS[stop:
BOOL] = {
WITH
NARROW[stb, SymbolTableBase.x].e.bb[bti]
SELECT
FROM
Callable => RETURN[(NOT inline) AND (epn = entryIndex)];
ENDCASE => RETURN[FALSE];
};
IsThisItY:
PROC[bti: by.BodyIndex]
RETURNS[stop:
BOOL] = {
WITH
NARROW[stb, SymbolTableBase.y].e.bb[bti]
SELECT
FROM
Callable => RETURN[(NOT inline) AND (epn = entryIndex)];
ENDCASE => RETURN[FALSE];
};
bi: BodyIndex =
WITH stb
SELECT
FROM
t: SymbolTableBase.x => [x[t.e.EnumerateBodies[bx.rootBodyIndex, IsThisItX]]],
t: SymbolTableBase.y => [y[t.e.EnumerateBodies[by.rootBodyIndex, IsThisItY]]],
ENDCASE => ERROR;
RETURN[LOOPHOLE[bi, CallableBodyIndex]];
};
finds the bti for the smallest enclosing block
ConvertCbti:
PUBLIC PROC[lastBti: BodyIndex, pc, start: PrincOps.BytePC, base: SymbolTableBase]
RETURNS[bti: BodyIndex] = {
WITH base
SELECT
FROM
t: SymbolTableBase.x =>
RETURN[[x[ConvertCbtiX[NARROW[lastBti, BodyIndex.x].e, pc, start, t.e]]]];
t: SymbolTableBase.y =>
RETURN[[y[ConvertCbtiY[NARROW[lastBti, BodyIndex.y].e, pc, start, t.e]]]];
ENDCASE => ERROR;
};
ConvertCbtiX:
PROC[lastBti: bx.BodyIndex, pc, start: PrincOps.BytePC, base: bx.SymbolTableBase]
RETURNS[bti: bx.BodyIndex] = {
bodyStart: PrincOps.BytePC;
bti ← lastBti;
DO
FOR lastBti ← base.SonBti[bti], base.SiblingBti[lastBti]
UNTIL lastBti = bx.nullBodyIndex
DO
WITH body: base.bb[lastBti]
SELECT
FROM
Callable => LOOP;
Other => {
bodyStart ← [start + body.relOffset];
WITH body.info
SELECT
FROM
External => IF pc IN [bodyStart..bodyStart+bytes) THEN {bti ← lastBti; EXIT};
ENDCASE;
};
ENDCASE;
REPEAT FINISHED => RETURN
ENDLOOP;
ENDLOOP;
};
ConvertCbtiY:
PROC[lastBti: by.BodyIndex, pc, start: PrincOps.BytePC, base: by.SymbolTableBase]
RETURNS[bti: by.BodyIndex] = {
bodyStart: PrincOps.BytePC;
bti ← lastBti;
DO
FOR lastBti ← base.SonBti[bti], base.SiblingBti[lastBti]
UNTIL lastBti = by.nullBodyIndex DO
WITH body: base.bb[lastBti]
SELECT
FROM
Callable => LOOP;
Other => {
bodyStart ← [start + body.relOffset];
WITH body.info
SELECT
FROM
External => IF pc IN [bodyStart..bodyStart+bytes) THEN {bti ← lastBti; EXIT};
ENDCASE;
};
ENDCASE;
REPEAT FINISHED => RETURN
ENDLOOP;
ENDLOOP;
};
finds the entrypoint index and its start pc for the proc containing the specified pc. BEWARE that if the pc is in a catch phrase, this returns the epn for the statically enclosing procedure of the catch phrase.
GetEp:
PUBLIC PROC[pc: PrincOps.BytePC, gf: PrincOps.GlobalFrameHandle, stb: SymbolTableBase]
RETURNS [ep: EVRange, start: PrincOps.BytePC] = {
FindMaxEI:
PROC
RETURNS [max: EVRange] = {
GetMaxX:
PROC [bti: bx.BodyIndex]
RETURNS [stop:
BOOL] = {
WITH
NARROW[stb, SymbolTableBase.x].e.bb[bti]
SELECT
FROM
Callable => IF ~inline THEN max ← MAX[max, entryIndex];
ENDCASE;
RETURN[FALSE];
};
GetMaxY:
PROC [bti: by.BodyIndex]
RETURNS [stop:
BOOL] = {
WITH
NARROW[stb, SymbolTableBase.y].e.bb[bti]
SELECT
FROM
Callable => IF ~inline THEN max ← MAX[max, entryIndex];
ENDCASE;
RETURN[FALSE];
};
max ← 0;
WITH stb
SELECT
FROM
t: SymbolTableBase.x => [] ← t.e.EnumerateBodies[bx.rootBodyIndex, GetMaxX];
t: SymbolTableBase.y => [] ← t.e.EnumerateBodies[by.rootBodyIndex, GetMaxY];
ENDCASE => ERROR;
};
body of GetEp begins here
diff: CARDINAL ← LAST[CARDINAL];
anyProcedure: BOOLEAN ← FALSE;
FOR i: EVRange
IN [0..FindMaxEI[]]
DO
Card:
PROC[pc: PrincOps.BytePC]
RETURNS[
CARDINAL] =
INLINE {RETURN[LOOPHOLE[pc, CARDINAL]]};
last: PrincOps.BytePC ← GetPc[gf, i];
IF Card[last] > Card[pc] THEN LOOP;
IF Card[pc] - Card[last] > diff THEN LOOP;
diff ← Card[pc] - Card[last];
ep ← i; start ← last;
anyProcedure ← TRUE;
ENDLOOP;
IF ~anyProcedure THEN ERROR; -- SIGNAL NotInAnyProcedure;
};
finds the start (byte) pc for the specified entrypoint index and GF
GetPc:
PUBLIC
PROC[gf: PrincOps.GlobalFrameHandle, i: EVRange]
RETURNS [PrincOps.BytePC] = {
codeBase: LONG POINTER TO PrincOps.CSegPrefix = PrincOpsUtils.Codebase[gf];
wpc: CARDINAL = codeBase.entry[i].initialpc; -- GROAN
RETURN[LOOPHOLE[wpc*2, PrincOps.BytePC]];
};
IsCatchFrame:
PROC[frame: PrincOps.FrameHandle, bti: BodyIndex]
RETURNS[BOOL, BodyIndex] = {
L0Frame: PrincOps.FrameHandle; -- will be the frame that encloses the catch frame
nextFrame: PrincOps.FrameHandle;
tr: REF TypedVariableRec;
IF frame = NIL THEN RETURN[FALSE, bti];
nextFrame ← AMProcessBasic.ReturnLink[WorldVM.LocalWorld[], frame].frame;
IF nextFrame = NIL THEN RETURN[FALSE, bti];
IF nextFrame.accesslink # sigGF OR ~nextFrame.mark THEN RETURN[FALSE, bti];
return FALSE if caller is not the signaller
IF frame.staticlink = NIL THEN DebuggerSwap.CallDebugger["Please call Paul Rovner"];
L0Frame ← LOOPHOLE[frame.staticlink - PrincOps.localbase, PrincOps.FrameHandle];
IF frame.accesslink # L0Frame.accesslink THEN RETURN[FALSE, bti];
detect situation where catch frame has no locals, thus no body table entry.
tr ← NARROW[TVForFrame[L0Frame], REF TypedVariableRec];
WITH hd: tr.head
SELECT
FROM
fh =>
IF bti = hd.bti
THEN
bti ← IF bti.brand = x THEN [x[bx.nullBodyIndex]] ELSE [y[by.nullBodyIndex]];
return nullBodyIndex if isCatchFrame and it has no locals
ENDCASE => ERROR;
RETURN[TRUE, bti];
}; -- end IsCatchFrame