-- file Pass3M.mesa -- last modified by Satterthwaite, September 7, 1982 1:41 pm -- last modified by Donahue, 9-Dec-81 14:45:35 DIRECTORY Alloc: TYPE USING [Notifier, Top], ComData: TYPE USING [ bodyIndex, idUNWIND, ownSymbols, seAnon, stopping, table, textIndex, typeCONDITION, typeListANY, typeLOCK], Log: TYPE USING [Error, ErrorSei, ErrorTree], Pass3: TYPE USING [lockNode], P3: TYPE USING [ Attr, FullAttr, NPUse, Safety, TextForm, pathNP, phraseNP, BoundNP, MergeNP, SequenceNP, SetNP, And, Apply, BindTree, BumpArgRefs, Bundling, CanonicalType, CheckLocals, CheckScope, ClearRefStack, CopyTree, DefaultInit, EnterType, EnterComposite, Exp, FirstId, InitialExp, LongPath, MakeLongType, MakeRefType, MatchFields, NewableType, OperandInternal, OperandLhs, OperandType, PopCtx, PushCtx, RAttr, Rhs, RPop, RPush, RType, SafetyAttr, SetSafety, SealRefStack, SearchCtxList, Stmt, TypeAppl, TypeForTree, Unbundle, UnsealRefStack, UpdateTreeAttr, Voidable, VoidExp], P3S: TYPE USING [BodyData, continued, currentBody, markCatch, safety], Strings: TYPE USING [String, SubStringDescriptor], Symbols: TYPE USING [ Base, HTIndex, SERecord, SEIndex, ISEIndex, CSEIndex, RecordSEIndex, RefSEIndex, CTXIndex, BodyRecord, BTIndex, CBTIndex, HTNull, SENull, ISENull, CSENull, RecordSENull, CTXNull, CBTNull, lG, lZ, RootBti, typeANY, seType, ctxType, mdType, bodyType], SymbolOps: TYPE USING [ ArgCtx, ArgRecord, EnterString, FindString, FirstCtxSe, MakeNonCtxSe, NextSe, NormalType, ReferentType, TransferTypes, TypeRoot, UnderType], Tree: TYPE USING [Base, Index, Link, Map, NodeName, Null, NullIndex, Scan, treeType], TreeOps: TYPE USING [ FreeNode, FreeTree, GetNode, ListLength, MakeList, MakeNode, NthSon, PopTree, PushList, PushTree, PushSe, PushNode, OpName, ReverseUpdateList, ScanList, SetAttr, SetInfo, UpdateList], Types: TYPE USING [Assignable, Equivalent]; Pass3M: PROGRAM IMPORTS Alloc, Log, P3, P3S, SymbolOps, TreeOps, Types, dataPtr: ComData, passPtr: Pass3 EXPORTS P3 = { OPEN SymbolOps, Symbols, P3, TreeOps; InsertCatchLabel: PUBLIC SIGNAL [catchSeen, exit: BOOLEAN] = CODE; tb: Tree.Base; -- tree base address (local copy) seb: Symbols.Base; -- se table base address (local copy) ctxb: Symbols.Base; -- context table base (local copy) mdb: Symbols.Base; -- module table base (local copy) bb: Symbols.Base; -- body table base (local copy) MiscNotify: PUBLIC Alloc.Notifier = { -- called by allocator whenever table area is repacked tb ← base[Tree.treeType]; seb ← base[seType]; ctxb ← base[ctxType]; mdb ← base[mdType]; bb ← base[bodyType]}; current: POINTER TO P3S.BodyData = @P3S.currentBody; -- statements MiscStmt: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = { val ← [subtree[index: node]]; -- the default SELECT tb[node].name FROM signal, error, start, join, wait => { PushTree[SELECT tb[node].name FROM start => Start[node], join => Join[node], wait => Wait[node], ENDCASE => Signal[node]]; SELECT RType[] FROM CSENull, typeANY => NULL; ENDCASE => Log.Error[nonVoidStmt]; SetInfo[dataPtr.textIndex]; val ← PopTree[]; RPop[]; pathNP ← SequenceNP[pathNP][phraseNP]; IF OpName[val] = error THEN current.reachable ← FALSE}; xerror => { subNode: Tree.Index; IF current.catchDepth # 0 THEN Log.Error[misplacedReturn]; tb[node].name ← error; val ← MiscStmt[node]; subNode ← GetNode[val]; IF tb[subNode].attr1 THEN Log.ErrorTree[typeClash, val]; SELECT tb[subNode].name FROM error, errorx => tb[subNode].name ← xerror; ENDCASE => NULL; tb[subNode].attr1 ← current.entry; tb[subNode].attr3 ← FALSE; IF current.entry THEN tb[subNode].attr2 ← CheckLocals[tb[subNode].son[2]]; IF tb[subNode].nSons > 2 THEN Log.Error[misplacedCatch]; current.reachable ← FALSE}; resume => Resume[node]; reject => { IF current.catchDepth = 0 THEN Log.Error[misplacedResume]; current.reachable ← FALSE}; continue, retry => { SIGNAL InsertCatchLabel[catchSeen:FALSE, exit:tb[node].name=continue]; current.reachable ← FALSE}; restart => {val ← Restart[node]; pathNP ← SequenceNP[pathNP][phraseNP]}; stop => { IF dataPtr.bodyIndex # RootBti OR current.catchDepth # 0 OR current.returnRecord # SENull THEN Log.Error[misplacedStop]; dataPtr.stopping ← TRUE; pathNP ← SetNP[pathNP]}; notify, broadcast => { OPEN tb[node]; type: CSEIndex; IF ~current.lockHeld THEN Log.Error[misplacedMonitorRef]; son[1] ← Exp[son[1], typeANY]; IF OperandLhs[son[1]] = none THEN Log.ErrorTree[nonLHS, son[1]]; type ← RType[]; RPop[]; pathNP ← SequenceNP[pathNP][phraseNP]; IF type # dataPtr.typeCONDITION THEN Log.ErrorTree[typeClash, son[1]]}; free => { OPEN tb[node]; type, subType: CSEIndex; long, counted: BOOLEAN; [v:son[1], long:long, counted:counted] ← EvalZone[son[1]]; RPop[]; son[2] ← Exp[son[2], typeANY]; type ← RType[]; RPop[]; subType ← NormalType[type]; WITH s: seb[subType] SELECT FROM ref => { IF s.readOnly THEN Log.ErrorTree[nonLHS, son[2]]; type ← UnderType[s.refType]; IF long # (seb[type].typeTag = long) THEN GO TO fail; type ← NormalType[type]; WITH t: seb[type] SELECT FROM ref => IF t.counted # counted THEN GO TO fail; ENDCASE => GO TO fail; IF P3S.safety = checked AND ~counted THEN Log.ErrorTree[unsafeOperation, [subtree[node]]]; EXITS fail => Log.ErrorTree[typeClash, son[2]]}; ENDCASE => Log.ErrorTree[typeClash, son[2]]; IF nSons > 3 THEN { saveNP: NPUse = phraseNP; [] ← CatchPhrase[son[4]]; phraseNP ← MergeNP[saveNP][phraseNP]}; attr2 ← long; attr3 ← counted; SELECT TRUE FROM ~counted => attr1 ← FALSE; (OpName[son[2]] # addr) => {Log.ErrorTree[other, son[2]]; attr1 ← FALSE}; ENDCASE => attr1 ← OperandLhs[NthSon[son[2], 1]] = counted}; dst, lst, lstf => { OPEN tb[node]; v: Tree.Link; v ← son[1] ← Exp[son[1], typeANY]; RPop[]; SELECT name FROM dst => IF OperandLhs[son[1]] = none THEN GO TO fail; lst => current.noXfers ← FALSE; lstf => current.reachable ← FALSE; ENDCASE; IF name # dst THEN phraseNP ← SetNP[phraseNP]; pathNP ← SequenceNP[pathNP][phraseNP]; -- check for simple addressability DO WITH v SELECT FROM symbol => IF seb[index].constant THEN GO TO fail ELSE EXIT; subtree => { IF tb[index].name # dollar THEN GO TO fail; v ← tb[index].son[1]}; ENDCASE => GO TO fail; ENDLOOP; IF P3S.safety = checked THEN Log.ErrorTree[unsafeOperation, [subtree[node]]]; EXITS fail => Log.ErrorTree[nonLHS, tb[node].son[1]]}; enable => { OPEN tb[node]; saveEnabled: BOOLEAN = current.unwindEnabled; IF CatchPhrase[son[1]].unwindCaught THEN current.unwindEnabled ← TRUE; IF phraseNP # none THEN pathNP ← unsafe; son[2] ← UpdateList[son[2], Stmt]; attr3 ← FALSE; current.unwindEnabled ← saveEnabled}; ENDCASE => Log.Error[unimplemented]; RETURN}; -- dynamic storage allocation New: PROC [node: Tree.Index, target: CSEIndex] RETURNS [Tree.Link] = { OPEN tb[node]; pType: RefSEIndex; subType, rootType: SEIndex; attr: Attr; saveNP: NPUse; long, counted: BOOLEAN; [v:son[1], long:long, counted:counted] ← EvalZone[son[1]]; attr ← RAttr[]; RPop[]; saveNP ← phraseNP; current.noXfers ← attr.noXfer ← FALSE; attr.const ← FALSE; son[2] ← TypeAppl[son[2]]; attr ← And[attr, RAttr[]]; saveNP ← MergeNP[saveNP][phraseNP]; RPop[]; subType ← TypeForTree[son[2]]; rootType ← TypeRoot[subType]; IF ~NewableType[subType] THEN Log.ErrorTree[typeLength, son[2]]; IF counted THEN EnterType[rootType, FALSE]; IF son[3] = Tree.Null THEN son[3] ← DefaultInit[subType] ELSE { StringInit: PROC [t: Tree.Link] RETURNS [BOOLEAN] = INLINE { RETURN [SELECT OpName[t] FROM stringinit => TRUE, lengthen => (OpName[NthSon[t, 1]] = stringinit), ENDCASE => FALSE]}; extFlag: BOOLEAN; [son[3], extFlag] ← InitialExp[son[3], subType]; SELECT TRUE FROM extFlag => Log.ErrorTree[misusedInline, son[3]]; StringInit[son[3]] => Log.ErrorTree[defaultForm, son[3]]; ENDCASE}; attr ← And[attr, RAttr[]]; phraseNP ← SequenceNP[saveNP][phraseNP]; RPop[]; pType ← MakeRefType[ cType: subType, readOnly: tb[node].attr1, counted: counted, hint: NormalType[target]]; IF counted THEN { -- rType: RefSEIndex = MakeRefType[cType: rootType, counted: TRUE, hint: pType]; -- EnterType[IF long THEN MakeLongType[rType, target] ELSE rType, FALSE]; IF son[3] # Tree.Null THEN EnterComposite[UnderType[subType], son[3], TRUE]}; IF son[3] = Tree.Null AND ~Voidable[subType] THEN Log.ErrorTree[missingInit, [subtree[node]]]; IF nSons > 3 THEN { saveNP: NPUse = phraseNP; [] ← CatchPhrase[son[4]]; phraseNP ← MergeNP[saveNP][phraseNP]}; attr2 ← long; attr3 ← counted; RPush[IF long THEN MakeLongType[pType, target] ELSE pType, attr]; RETURN [[subtree[index: node]]]}; EvalZone: PROC [t: Tree.Link] RETURNS [v: Tree.Link, long, counted: BOOLEAN] = { type, nType: CSEIndex; nDerefs: CARDINAL; long ← counted ← TRUE; IF t = Tree.Null THEN {v ← Tree.Null; RPush[typeANY, FullAttr]} ELSE { v ← Exp[t, typeANY]; type ← RType[]; nDerefs ← 0; DO nType ← NormalType[type]; WITH s: seb[nType] SELECT FROM zone => {long ← ~s.mds; counted ← s.counted; GO TO success}; ref => { IF (nDerefs ← nDerefs + 1) > 63 THEN GO TO failure; PushTree[v]; PushNode[uparrow, 1]; SetAttr[2, seb[type].typeTag = long]; type ← UnderType[s.refType]; SetInfo[type]; v ← PopTree[]}; record => IF Bundling[nType] # 0 THEN type ← Unbundle[LOOPHOLE[nType, RecordSEIndex]] ELSE GO TO failure; ENDCASE => GO TO failure; REPEAT success => NULL; failure => Log.ErrorTree[typeClash, v]; ENDLOOP}; RETURN}; -- list allocation Cons: PROC [node: Tree.Index, target: CSEIndex] RETURNS [Tree.Link] = { nType: CSEIndex = NodeType[target, [subtree[node]]]; PushTree[tb[node].son[1]]; tb[node].son[1] ← Tree.Null; PushNode[implicitTC, 0]; SetInfo[nType]; PushTree[tb[node].son[2]]; tb[node].son[2] ← Tree.Null; PushTree[Tree.Null]; PushNode[apply, -2]; SetAttr[1, FALSE]; IF tb[node].nSons > 2 THEN { PushTree[tb[node].son[3]]; tb[node].son[3] ← Tree.Null; PushNode[new, 4]} ELSE PushNode[new, 3]; FreeNode[node]; RETURN [Exp[PopTree[], target]]}; ListCons: PROC [node: Tree.Index, target: CSEIndex] RETURNS [Tree.Link] = { nType: CSEIndex = NodeType[target, [subtree[node]]]; n: CARDINAL = ListLength[tb[node].son[2]]; k: CARDINAL ← n; list, zone: Tree.Link; ListItem: Tree.Map = { PushTree[IF (k←k-1) = 0 THEN zone ELSE P3.CopyTree[zone]]; PushNode[implicitTC, 0]; SetInfo[nType]; PushTree[t]; PushTree[list]; PushList[2]; PushTree[Tree.Null]; PushNode[apply, -2]; SetAttr[1, FALSE]; list ← MakeNode[new, 3]; RETURN [Tree.Null]}; PushTree[Tree.Null]; list ← MakeNode[nil, 1]; IF n = 0 THEN [v: tb[node].son[1]] ← EvalZone[tb[node].son[1]] ELSE { zone ← tb[node].son[1]; tb[node].son[1] ← Tree.Null; tb[node].son[2] ← ReverseUpdateList[tb[node].son[2], ListItem]}; FreeNode[node]; RETURN [Exp[list, target]]}; NodeType: PROC [target: CSEIndex, t: Tree.Link] RETURNS [nType: CSEIndex] = { subType: CSEIndex = NormalType[target]; WITH r: seb[subType] SELECT FROM ref => { rType: CSEIndex = UnderType[r.refType]; IF ~r.list AND (seb[rType].typeTag = any OR rType = typeANY) THEN nType ← ReferentType[dataPtr.typeListANY] ELSE { IF ~r.list THEN Log.ErrorTree[typeClash, t]; nType ← rType}}; ENDCASE => IF subType = typeANY THEN nType ← ReferentType[dataPtr.typeListANY] ELSE {Log.ErrorTree[typeClash, t]; nType ← typeANY}; RETURN}; -- control transfers MiscXfer: PUBLIC PROC [node: Tree.Index, target: CSEIndex] RETURNS [val: Tree.Link] = { SELECT tb[node].name FROM new => val ← New[node, target]; signalx, errorx => val ← Signal[node]; create => val ← Create[node, target]; startx => val ← Start[node]; fork => val ← Fork[node, target]; joinx => val ← Join[node]; cons => val ← Cons[node, target]; listcons => val ← ListCons[node, target]; ENDCASE => {Log.Error[unimplemented]; val ← [subtree[node]]}; RETURN}; MakeFrameRecord: PUBLIC PROC [t: Tree.Link] RETURNS [rSei: CSEIndex] = { bti: CBTIndex = XferBody[t]; IF bti # CBTNull THEN { argType: SEIndex = TransferTypes[bb[bti].ioType].typeIn; rSei ← IF bb[bti].type # RecordSENull AND argType = SENull THEN bb[bti].type ELSE AllocFrameRecord[bti, argType]} ELSE {Log.Error[nonTypeCons]; rSei ← typeANY}; RETURN}; AllocFrameRecord: PROC [bti: CBTIndex, link: SEIndex] RETURNS [sei: RecordSEIndex] = { sei ← LOOPHOLE[MakeNonCtxSe[SIZE[linked record cons SERecord]]]; seb[sei] ← SERecord[mark3: TRUE, mark4: FALSE, body: cons[record[ machineDep: FALSE, painted: TRUE, argument: FALSE, hints: [ unifield: FALSE, variant: FALSE, assignable: FALSE, comparable: FALSE, privateFields: TRUE, refField: TRUE, default: FALSE, voidable: FALSE], fieldCtx: bb[bti].localCtx, length: IF bb[bti].type # RecordSENull THEN seb[bb[bti].type].length ELSE 0, monitored: bb[bti].monitored, linkPart: linked[link]]]]; RETURN}; BodyForTree: PUBLIC PROC [t: Tree.Link] RETURNS [CBTIndex] = { sei: ISEIndex; node, subNode: Tree.Index; WITH t SELECT FROM symbol => { sei ← index; SELECT TRUE FROM seb[sei].mark4 => RETURN [IF seb[sei].constant THEN seb[sei].idInfo ELSE CBTNull]; seb[sei].immutable => { node ← seb[sei].idValue; WITH tb[node].son[3] SELECT FROM subtree => { subNode ← index; IF tb[subNode].name = body THEN RETURN [tb[subNode].info]}; ENDCASE}; ENDCASE}; subtree => { node ← index; SELECT tb[node].name FROM cdot, dot, dollar => RETURN [BodyForTree[tb[node].son[2]]]; ENDCASE}; ENDCASE; RETURN [CBTNull]}; XferBody: PROC [t: Tree.Link] RETURNS [bti: CBTIndex] = { sei: ISEIndex; type: CSEIndex; WITH t SELECT FROM symbol => { sei ← index; type ← UnderType[seb[sei].idType]; bti ← WITH seb[type] SELECT FROM transfer => IF ~seb[sei].immutable THEN CBTNull ELSE SELECT mode FROM program => IF seb[sei].mark4 THEN (IF seb[sei].constant THEN seb[sei].idInfo ELSE CBTNull) ELSE RootBti, proc => IF sei = bb[dataPtr.bodyIndex].id THEN dataPtr.bodyIndex ELSE CBTNull, ENDCASE => CBTNull, ENDCASE => CBTNull}; ENDCASE => bti ← CBTNull; RETURN}; XferForFrame: PUBLIC PROC [ctx: CTXIndex] RETURNS [type: CSEIndex ← CSENull] = { bti: BTIndex ← FIRST[BTIndex]; btLimit: BTIndex = (dataPtr.table).Top[bodyType]; UNTIL bti = btLimit DO WITH entry: bb[bti] SELECT FROM Callable => { IF entry.localCtx = ctx THEN RETURN [UnderType[entry.ioType]]; bti ← bti + (WITH entry SELECT FROM Inner => SIZE[Inner Callable BodyRecord], ENDCASE => SIZE[Outer Callable BodyRecord])}; ENDCASE => bti ← bti + SIZE[Other BodyRecord]; ENDLOOP; RETURN [CSENull]}; Create: PROC [node: Tree.Index, target: CSEIndex] RETURNS [val: Tree.Link] = { subNode: Tree.Index; val ← ForceApplication[tb[node].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; subNode ← GetNode[val]; BEGIN OPEN tb[subNode]; type, mType, rType: CSEIndex; attr: Attr; CreateError: PROC = {Log.ErrorTree[typeClash, son[1]]; type ← typeANY}; name ← create; attr1 ← TRUE; son[1] ← Exp[son[1], typeANY]; mType ← RType[]; attr ← RAttr[]; RPop[]; phraseNP ← SetNP[phraseNP]; WITH seb[mType] SELECT FROM transfer => IF mode = program THEN SELECT XferBody[son[1]] FROM CBTNull => type ← mType; RootBti => { type ← IF seb[target].typeTag = ref THEN MakeRefType[MakeFrameRecord[son[1]], target] ELSE mType; attr1 ← FALSE}; ENDCASE => CreateError[] ELSE CreateError[]; ref => { type ← mType; rType ← UnderType[refType]; WITH seb[rType] SELECT FROM record => SELECT TRUE FROM (ctxb[fieldCtx].level # lG) => CreateError[]; (seb[target].typeTag = transfer) => { type ← XferForFrame[fieldCtx]; IF type = CSENull THEN {Log.Error[unimplemented]; type ← typeANY}}; ENDCASE; ENDCASE => IF refType # typeANY THEN CreateError[]}; ENDCASE => IF mType = typeANY THEN type ← typeANY ELSE CreateError[]; IF son[2] # Tree.Null THEN { Log.ErrorTree[noApplication, son[1]]; son[2] ← UpdateList[son[2], VoidExp]}; IF nSons > 2 THEN [] ← CatchPhrase[son[3]]; current.noXfers ← attr.noXfer ← FALSE; attr.const ← FALSE; RPush[type, attr]; END; RETURN}; Start: PROC [node: Tree.Index] RETURNS [Tree.Link] = { subNode: Tree.Index; subNode ← Apply[GetNode[ForceApplication[tb[node].son[1]]], typeANY, TRUE]; SELECT tb[subNode].name FROM start, startx, apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN [[subtree[subNode]]]}; Restart: PROC [node: Tree.Index] RETURNS [val: Tree.Link] = { subNode: Tree.Index; type: CSEIndex; val ← ForceApplication[tb[node].son[1]]; subNode ← GetNode[val]; BEGIN OPEN tb[subNode]; name ← tb[node].name; info ← tb[node].info; son[1] ← Exp[son[1], typeANY]; type ← RType[]; RPop[]; phraseNP ← SetNP[phraseNP]; WITH seb[type] SELECT FROM ref => NULL; -- a weak check for now transfer => IF mode # program OR XferBody[son[1]] # CBTNull THEN Log.ErrorTree[typeClash, son[1]]; ENDCASE => IF type # typeANY THEN Log.ErrorTree[typeClash, son[1]]; IF son[2] # Tree.Null THEN { Log.ErrorTree[noApplication, son[1]]; son[2] ← UpdateList[son[2], VoidExp]}; IF nSons > 2 THEN [] ← CatchPhrase[son[3]]; END; current.noXfers ← FALSE; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN}; Fork: PROC [node: Tree.Index, target: CSEIndex] RETURNS [Tree.Link] = { subNode: Tree.Index; type: CSEIndex; attr: Attr; t: Tree.Link ← ForceApplication[tb[node].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; subNode ← Apply[GetNode[t], typeANY, TRUE]; attr ← RAttr[]; RPop[]; SELECT tb[subNode].name FROM call, callx => { s: Tree.Link ← tb[subNode].son[1]; subType: CSEIndex; IF OpName[s] = thread THEN { s ← NthSon[s, 1]; Log.ErrorTree[misusedInline, s]}; IF current.lockHeld AND OperandInternal[s] THEN Log.ErrorTree[internalCall, s]; subType ← OperandType[s]; WITH procType: seb[subType] SELECT FROM transfer => { type ← MakeNonCtxSe[SIZE[transfer cons SERecord]]; seb[type] ← SERecord[mark3: TRUE, mark4: TRUE, body: cons[transfer[ mode: process, safe: procType.safe, typeIn: RecordSENull, typeOut: procType.typeOut]]]; IF P3S.safety = checked THEN { CheckArg: Tree.Map = { argType: CSEIndex = OperandType[t]; subType: CSEIndex = NormalType[argType]; WITH s: seb[subType] SELECT FROM ref => {IF s.var THEN Log.ErrorTree[unsafeOperation, t]; v ← t}; transfer => v ← CheckScope[t, argType]; ENDCASE => v ← t; RETURN}; tb[subNode].son[1] ← CheckScope[s, subType]; tb[subNode].son[2] ← UpdateList[tb[subNode].son[2], CheckArg]}}; ENDCASE => ERROR; tb[subNode].name ← fork}; apply => type ← typeANY; ENDCASE => {Log.ErrorTree[typeClash, tb[node].son[1]]; type ← typeANY}; tb[subNode].info ← type; RPush[type, attr]; RETURN [[subtree[subNode]]]}; Join: PROC [node: Tree.Index] RETURNS [Tree.Link] = { subNode: Tree.Index; subNode ← Apply[GetNode[ForceApplication[tb[node].son[1]]], typeANY, TRUE]; SELECT tb[subNode].name FROM join, joinx => NULL; apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN [[subtree[subNode]]]}; Wait: PROC [node: Tree.Index] RETURNS [val: Tree.Link] = { subNode: Tree.Index; saveNP: NPUse; IF ~current.lockHeld THEN Log.Error[misplacedMonitorRef]; subNode ← Apply[GetNode[ForceApplication[tb[node].son[1]]], typeANY, TRUE]; SELECT tb[subNode].name FROM wait => NULL; apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; IF OperandLhs[tb[subNode].son[1]] = none THEN Log.ErrorTree[nonLHS, tb[subNode].son[1]]; [] ← FreeTree[tb[subNode].son[2]]; saveNP ← phraseNP; tb[subNode].son[2] ← tb[subNode].son[1]; tb[subNode].son[1] ← CopyLock[]; phraseNP ← MergeNP[saveNP][phraseNP]; RETURN [[subtree[subNode]]]}; -- monitors LockVar: PUBLIC PROC [t: Tree.Link] RETURNS [val: Tree.Link] = { type, nType: CSEIndex; desc: Strings.SubStringDescriptor; sei: ISEIndex; nDerefs: CARDINAL; long, b: BOOLEAN; Dereference: PROC [type: CSEIndex] = { PushTree[val]; PushNode[uparrow, 1]; SetInfo[type]; SetAttr[2, long]; val ← PopTree[]}; val ← Exp[t, typeANY]; long ← LongPath[val]; type ← RType[]; RPop[]; nDerefs ← 0; DO IF type = dataPtr.typeLOCK THEN {IF nDerefs # 0 THEN Dereference[type]; GO TO success}; type ← UnderType[TypeRoot[type]]; nType ← NormalType[type]; WITH seb[nType] SELECT FROM record => { IF monitored THEN { desc ← ["LOCK"L, 0, ("LOCK"L).length]; [b, sei] ← SearchCtxList[EnterString[@desc], fieldCtx]; IF ~b THEN {Log.Error[noAccess]; sei ← dataPtr.seAnon}; PushTree[val]; PushSe[sei]; PushNode[IF nDerefs = 0 THEN dollar ELSE dot, 2]; SetInfo[dataPtr.typeLOCK]; SetAttr[2, long]; val ← PopTree[]; GO TO success}; GO TO failure}; ref => { IF (nDerefs ← nDerefs + 1) > 63 THEN GO TO failure; long ← seb[type].typeTag = long; IF nDerefs > 1 THEN Dereference[type]; type ← UnderType[refType]}; ENDCASE => GO TO failure; REPEAT success => NULL; failure => Log.ErrorTree[typeClash, val]; ENDLOOP; IF OperandLhs[val] = none THEN Log.ErrorTree[nonLHS, val]; RETURN}; FindLockParams: PUBLIC PROC RETURNS [formal, actual: ISEIndex] = { node: Tree.Index = GetNode[tb[passPtr.lockNode].son[1]]; found: BOOLEAN; IF node = Tree.NullIndex THEN formal ← actual ← ISENull ELSE { formal ← FirstId[node]; IF current.inputRecord = SENull THEN found ← FALSE ELSE [found, actual] ← SearchCtxList[ seb[formal].hash, seb[current.inputRecord].fieldCtx]; IF ~found THEN actual ← ISENull}; RETURN}; LambdaApply: PROC [t: Tree.Link, formal, actual: ISEIndex] RETURNS [v: Tree.Link] = { BindFormal: PROC [sei: ISEIndex] RETURNS [Tree.Link] = { RETURN [[symbol[index: IF sei = formal THEN actual ELSE sei]]]}; v ← BindTree[t, BindFormal]; [] ← UpdateTreeAttr[v]; RETURN}; CopyLock: PUBLIC PROC RETURNS [val: Tree.Link] = { formal, actual: ISEIndex; SELECT TRUE FROM passPtr.lockNode = Tree.NullIndex => val ← Tree.Null; tb[current.bodyNode].son[4] # Tree.Null => val ← LambdaApply[tb[current.bodyNode].son[4], ISENull, ISENull]; ENDCASE => { [formal:formal, actual:actual] ← FindLockParams[]; IF formal # SENull THEN { IF actual = SENull THEN {Log.ErrorSei[missingLock, formal]; actual ← dataPtr.seAnon}; IF ~Types.Assignable[ [dataPtr.ownSymbols, UnderType[seb[formal].idType]], [dataPtr.ownSymbols, UnderType[seb[actual].idType]]] THEN Log.ErrorSei[typeClash, actual]}; val ← LambdaApply[tb[passPtr.lockNode].son[2], formal, actual]}; RETURN}; -- signals Signal: PUBLIC PROC [node: Tree.Index] RETURNS [val: Tree.Link] = { subNode: Tree.Index; nodeTag: Tree.NodeName = tb[node].name; subNode ← Apply[GetNode[ForceApplication[tb[node].son[1]]], typeANY, TRUE]; SELECT tb[subNode].name FROM signal, signalx => tb[subNode].name ← nodeTag; error, errorx => { SELECT nodeTag FROM signal, signalx => Log.ErrorTree[typeClash, tb[subNode].son[1]]; ENDCASE => NULL; tb[subNode].name ← nodeTag}; apply => NULL; ENDCASE => Log.ErrorTree[typeClash, tb[subNode].son[1]]; tb[node].son[1] ← Tree.Null; FreeNode[node]; RETURN [[subtree[subNode]]]}; ForceApplication: PROC [t: Tree.Link] RETURNS [Tree.Link] = { IF OpName[t] = apply THEN RETURN [t]; PushTree[t]; PushTree[Tree.Null]; RETURN [MakeNode[apply, 2]]}; -- catch phrases CatchPhrase: PUBLIC PROC [t: Tree.Link] RETURNS [unwindCaught: BOOLEAN] = { saveReachable: BOOLEAN = current.reachable; savePathNP: NPUse = pathNP; saveSafety: Safety = P3S.safety; enclosingSafe: BOOLEAN = (saveSafety = checked); entryNP, exitNP: NPUse; CatchItem: Tree.Scan = { node: Tree.Index = GetNode[t]; type: CSEIndex ← typeANY; mixed, safe, error: BOOLEAN ← FALSE; saveIndex: CARDINAL = dataPtr.textIndex; CatchLabel: Tree.Map = { subType: CSEIndex; v ← Exp[t, typeANY]; subType ← CanonicalType[RType[]]; RPop[]; entryNP ← SequenceNP[entryNP][phraseNP]; WITH t: seb[subType] SELECT FROM transfer => IF t.mode = signal OR t.mode = error THEN { IF type = typeANY THEN type ← subType ELSE IF ~Types.Equivalent[ [dataPtr.ownSymbols, type], [dataPtr.ownSymbols, subType]] THEN mixed ← TRUE; IF t.safe THEN safe ← TRUE; IF t.mode = error THEN error ← TRUE} ELSE Log.ErrorTree[typeClash, v]; ENDCASE => IF subType # typeANY THEN Log.ErrorTree[typeClash, v]; RETURN}; dataPtr.textIndex ← tb[node].info; tb[node].son[1] ← UpdateList[tb[node].son[1], CatchLabel]; IF mixed THEN type ← typeANY; tb[node].son[2] ← CatchBody[ tb[node].son[2], type, safe OR (error AND enclosingSafe)]; IF tb[node].son[1] = Tree.Link[symbol[index: dataPtr.idUNWIND]] THEN { unwindCaught ← TRUE; IF current.entry AND ~current.unwindEnabled AND current.catchDepth = 0 THEN { PushTree[tb[node].son[2]]; PushTree[CopyLock[]]; PushNode[unlock, 1]; SetInfo[dataPtr.textIndex]; tb[node].son[2] ← MakeList[2]}}; tb[node].info ← IF type # typeANY THEN type ELSE SENull; dataPtr.textIndex ← saveIndex; RETURN}; CatchBody: PROC [body: Tree.Link, type: CSEIndex, safe: BOOLEAN] RETURNS [val: Tree.Link] = { saveRecord: RecordSEIndex = current.resumeRecord; saveFlag: BOOLEAN = current.resumeFlag; current.catchDepth ← current.catchDepth + 1; WITH t: seb[type] SELECT FROM transfer => { current.resumeFlag ← t.mode = signal; PushArgCtx[t.typeIn]; BumpArgRefs[ArgRecord[t.typeIn], TRUE]; PushArgCtx[current.resumeRecord ← ArgRecord[t.typeOut]]; ClearRefStack[]}; ENDCASE => { current.resumeFlag ← FALSE; current.resumeRecord ← RecordSENull}; current.reachable ← TRUE; pathNP ← entryNP; SELECT OpName[body] FROM block, checked => SetSafety[SafetyAttr[GetNode[body]]]; ENDCASE; IF safe AND P3S.safety = none THEN Log.Error[unsafeBlock]; val ← UpdateList[body, Stmt ! InsertCatchLabel => {IF catchSeen THEN RESUME}]; exitNP ← BoundNP[exitNP][pathNP]; WITH t: seb[type] SELECT FROM transfer => {PopArgCtx[t.typeOut]; PopArgCtx[t.typeIn]}; ENDCASE; current.catchDepth ← current.catchDepth - 1; current.resumeRecord ← saveRecord; current.resumeFlag ← saveFlag; SetSafety[saveSafety]; RETURN}; setLabel, continued: BOOLEAN; node: Tree.Index = GetNode[t]; SealRefStack[]; setLabel ← continued ← unwindCaught ← FALSE; entryNP ← exitNP ← none; BEGIN ENABLE InsertCatchLabel => { IF ~catchSeen THEN { setLabel ← TRUE; IF exit THEN continued ← TRUE; SIGNAL InsertCatchLabel[catchSeen:TRUE, exit:exit]; RESUME}}; ScanList[tb[node].son[1], CatchItem]; IF tb[node].nSons > 1 THEN tb[node].son[2] ← CatchBody[tb[node].son[2], typeANY, enclosingSafe]; END; IF setLabel THEN {P3S.markCatch ← TRUE; P3S.continued ← continued}; UnsealRefStack[]; current.reachable ← saveReachable; phraseNP ← exitNP; pathNP ← savePathNP; RETURN}; PushArgCtx: PROC [sei: CSEIndex] = { ctx: CTXIndex = ArgCtx[sei]; IF ctx # CTXNull THEN { ctxb[ctx].level ← current.level + current.catchDepth; PushCtx[ctx]}}; PopArgCtx: PROC [sei: CSEIndex] = { ctx: CTXIndex = ArgCtx[sei]; IF ctx # CTXNull THEN {PopCtx[]; ctxb[ctx].level ← lZ}}; Resume: PROC [node: Tree.Index] = { OPEN tb[node]; rSei: RecordSEIndex = current.resumeRecord; IF ~current.resumeFlag THEN Log.Error[misplacedResume]; IF rSei # SENull AND son[1] = Tree.Null THEN { n: CARDINAL ← 0; BumpArgRefs[rSei, FALSE]; FOR sei: ISEIndex ← FirstCtxSe[seb[rSei].fieldCtx], NextSe[sei] UNTIL sei = SENull DO n ← n+1; IF n=1 AND seb[sei].hash = HTNull THEN Log.Error[illDefinedReturn]; PushSe[sei]; ENDLOOP; son[1] ← MakeList[n]} ELSE { son[1] ← IF attr1 AND rSei # SENull THEN Rhs[son[1], rSei] ELSE MatchFields[rSei, son[1]]; RPop[]; pathNP ← SequenceNP[pathNP][phraseNP]}; current.reachable ← FALSE}; -- Rope identification (temporary) CheckHash: PROC [hti: HTIndex, s: Strings.String] RETURNS [BOOLEAN] = { desc: Strings.SubStringDescriptor; desc ← [base: s, offset: 0, length: s.length]; RETURN [FindString[@desc] = hti]}; TextRep: PUBLIC PROC [rType: SEIndex] RETURNS [form: TextForm ← text] = { type: CSEIndex = UnderType[rType]; rope: STRING = "Rope"L; ropeRep: STRING = "RopeRep"L; textRep: STRING = "TextRep"L; WITH se: seb[rType] SELECT FROM id => IF CheckHash[se.hash, ropeRep] THEN form ← rope ELSE IF CheckHash[se.hash, textRep] THEN form ← ropeText; ENDCASE; WITH t: seb[type] SELECT FROM record => WITH c: ctxb[t.fieldCtx] SELECT FROM included => IF ~CheckHash[mdb[c.module].moduleId, rope] THEN form ← text; ENDCASE => form ← text; ENDCASE => form ← text; RETURN}; }.