-- SMLDriverImpl.mesa
-- last edit by Schmidt, May 27, 1983 6:31 pm
-- last edit by Satterthwaite, August 15, 1983 11:51 am
-- procedures to load and start modules in a Model
-- links:
-- IF gfi > firstdummy, then gfi is index into Import table
-- and ep is index into the export record paired with that import.
-- Binding is simply to copy control link in the export record
-- into this link
-- IF gfi < firstdummy, then gfi in this link is an index into the config's
-- moduletable. Do not alter the ep
-- spaces:
-- assume there are n modules
-- there will be
-- 1 space in MDS for all the frames (and frame links)
-- 1 space in VM for the Fake Config Bcd for the load state
-- n ReadOnly spaces for Code
-- code and code links are in same space
-- n spaces for the Bcd headers
-- (deleted on UnLoad)
DIRECTORY
Atom: TYPE USING [MakeAtom, PutProp],
BcdDefs: TYPE USING [
Base, BCD, EPLimit, EXPIndex, EXPRecord, FTIndex, FTRecord, GFTIndex,
IMPIndex, IMPRecord, Link, MTIndex, MTRecord, NameRecord, VersionStamp],
BcdOps: TYPE USING [
BcdBase, EXPHandle, FTHandle, IMPHandle, MTHandle,
ProcessExports, ProcessImports, ProcessModules],
--CedarExporterImpl: TYPE USING [ProcessPendingEntries, SaveResolvedEntries],
CedarLinkerOps: TYPE USING [
FindVariableLink, GetIR, GetPendingList, IR, PendingList, SetPendingList],
CS: TYPE USING [RopeFromStamp],
Directory: TYPE USING [DeleteFile, Error],
File: TYPE USING [Capability],
IO: TYPE USING [atom, card, PutF, rope, STREAM, string, UserAborted],
List: TYPE USING [DReverse],
Loader: TYPE USING [Error],
LoaderPrivate: TYPE USING [FindMappedSpace, GetModuleLink],
PilotLoaderOps: TYPE USING [DestroyMap, IthLink, LinkSegmentLength, ReleaseFrames],
PilotLoadStateFormat: TYPE USING [ConfigIndex, NullConfig],
PilotLoadStateOps: TYPE USING [
ConfigIndex, GetMap, InputLoadState, Map, NullConfig, ReleaseLoadState,
ReleaseMap, RemoveConfig],
PrincOps: TYPE USING [
ControlLink, GFTIndex, GFTNull, GlobalFrameHandle, NullLink, UnboundLink],
Process: TYPE USING [Detach],
Rope: TYPE USING [Text],
RTLoader: TYPE USING [AcquireTypesAndLiterals],
Runtime: TYPE USING [ValidateGlobalFrame],
RuntimeInternal: TYPE USING [Codebase],
SMFakeBcd: TYPE USING [BuildFakeBcd],
SMFI: TYPE USING [BcdFileInfo],
SMLDriver: TYPE USING [],
SMLoad: TYPE USING [
AllocateIR, BuildFramePtrInterface, BuildInterface, CloseLinkSpace,
ConvertLink, FreeLoadInfo, GfiMap, InvalidFile, IR, IRSeq, IRSeqRecord,
LoadGlobalFrames, LoadIncremental, LoadInfo, NSToRope,
OpenLinkSpace, ReadLink, ReplaceResult, WriteLink],
SMProj: TYPE USING [Proj, Available, Fill, Find],
SMTree: TYPE Tree USING [
ApplOp, BindOp, Handle, Id, Link, Name, NodeName, null, nullName],
SMTreeOps: TYPE USING [
TM, GetExt, GetName, NSons, NthSon, OpName, PutExt, Scan, ScanSons],
SMVal: TYPE USING [
Binding, BtoD, BtoG, IdName, IdType, LoadMod, LoadModRecord,
GetExtFromParse, OuterBody, PutExtInParse, Select, ValOf, ValOfNthSon, VisitNodes],
Space: TYPE USING [
Delete, GetHandle, Handle, nullHandle, PageFromLongPointer, virtualMemory],
Time: TYPE USING [Current],
TimeStamp: TYPE USING [Null, Stamp];
SMLDriverImpl: PROGRAM
IMPORTS
Atom, BcdOps, --CedarExporterImpl,-- CedarLinkerOps, CS, Directory,
IO, List, Loader, LoaderPrivate, PilotLoaderOps, PilotLoadStateOps, Process,
RTLoader, Runtime, RuntimeInternal, SMFakeBcd, SMLoad, SMProj, SMTreeOps,
SMVal, Space, Time
EXPORTS SMLDriver
SHARES --CedarExporterImpl,-- CedarLinkerOps ~ {
OPEN Tree~~SMTree, TreeOps~~SMTreeOps;
-- no MDS usage!
LS: TYPE~REF LoaderState;
LoaderState: PUBLIC TYPE~RECORD[ -- state information for the modeller's loader
z: ZONE←,
tm: TreeOps.TM←,
out: IO.STREAM←, -- for messages
fakeBcdSpace: Space.Handle←Space.nullHandle, -- the bcd space for a fake config
fakeBcdFileName: Rope.Text←NIL, -- name of backing file
configIndex: PilotLoadStateFormat.ConfigIndex←PilotLoadStateFormat.NullConfig,
frameInterfaces: LIST OF REF FrameListRecord←NIL,
importedInterfaces: SMLoad.IRSeq←NIL,
started: BOOLLSE];
FrameListRecord: TYPE~RECORD[
name: ATOM←NIL,
stamp: TimeStamp.Stamp←TimeStamp.Null,
ir: SMLoad.IR←NIL];
Create: PUBLIC SAFE PROC[z: ZONE, tm: TreeOps.TM, out: IO.STREAM]
RETURNS[LS] ~ CHECKED {
RETURN [z.NEW[LoaderState ← [z~z, tm~tm, out~out]]]};
Loaded: PUBLIC SAFE PROC[ls: LS] RETURNS[BOOL] ~ CHECKED {
RETURN[ls ~= NIL AND ls.configIndex ~= PilotLoadStateFormat.NullConfig]};
LoadAndBind: PUBLIC SAFE PROC[ls: LS, root: Tree.Link, replace: BOOL]
RETURNS[errors: BOOL ← FALSE] ~ TRUSTED {
ENABLE UNWIND => {PilotLoadStateOps.ReleaseLoadState[]};
time: LONG CARDINAL ← Time.Current[];
nBcds: NAT;
formals, body: Tree.Link;
[formals, body] ← SMVal.OuterBody[root];
-- unload any bcds that may be around from the last invocation
-- regardless of "replacement", old config in load state is unloaded
IF Loaded[ls] THEN DeleteLoadStateEntry[ls, replace];
ls.configIndex ← PilotLoadStateOps.InputLoadState[]; -- locks the load state
--*** load state locked ***
-- this will acquire all the (explicit) imports from the load state
IF ~replace THEN InputActuals[ls, formals];
-- this will build all interface records
nBcds ← LoadBcds[ls, body ! SMLoad.InvalidFile => {GOTO fail}];
[ls.fakeBcdFileName, ls.fakeBcdSpace] ← -- releases the load state
SMFakeBcd.BuildFakeBcd[ls.configIndex, body, ls.fakeBcdFileName, ls.fakeBcdSpace, ls.out];
--*** load state released ***
ProcessPlusAndThen[ls, body];
-- PutExportsInLoadState[g];
-- now fill in all the frame links
ResolveImports[ls, body]; -- may lock the load state while finding hidden imports
-- now call Cedar related procedures to finish the loading
ProcessCedarBcds[ls, body];
time ← Time.Current[] - time;
IF nBcds = 0 THEN ls.out.PutF["Nothing was loaded.\n\n"]
ELSE {
ls.out.PutF["%d modules loaded\n", IO.card[nBcds]];
ls.out.PutF["Total time to load: %d seconds.\n\n", IO.card[time]]};
EXITS
fail => NULL;
};
-- always frees the bcdbases stored in our structures
Unload: PUBLIC SAFE PROC[
ls: LS, root: Tree.Link, unloadTheBcd: BOOL] ~ TRUSTED {
nunl: NAT ← 0;
-- traverses the value tree
ForEachApply: SAFE PROC[node, parent: Tree.Link] ~ TRUSTED {
IF TreeOps.OpName[node] IN Tree.ApplOp THEN
WITH SMVal.GetExtFromParse[node] SELECT FROM
loadMod: SMVal.LoadMod =>
IF loadMod.loadInfo ~= NIL THEN {
nunl ← nunl + 1;
IF unloadTheBcd THEN {
IF nunl = 1 THEN ls.out.PutF["Unloading modules.\n"];
-- remember that a binder bcd loaded by
-- the modeller will have only ONE mapped
-- space for all its code
FOR i: NAT IN [0 .. loadMod.loadInfo.size) DO
prog: PROGRAM ← LOOPHOLE[loadMod.loadInfo[i].frame];
space: Space.Handle;
IF RuntimeInternal.Codebase[prog] ~= NIL THEN {
space ← Space.GetHandle[
Space.PageFromLongPointer[RuntimeInternal.Codebase[prog]]];
IF space ~= Space.nullHandle AND space ~= Space.virtualMemory THEN
Space.Delete[LoaderPrivate.FindMappedSpace[space]];
};
ENDLOOP;
-- frees the global frames
PilotLoaderOps.ReleaseFrames[
loadMod.loadInfo.bcdBase, loadMod.loadInfo.frameList,
loadMod.loadInfo.map];
PilotLoaderOps.DestroyMap[loadMod.loadInfo.map]};
-- frees the bcdBase space
loadMod.loadInfo ← SMLoad.FreeLoadInfo[loadMod.loadInfo]};
ENDCASE => NULL;
};
SMVal.VisitNodes[ls.tm, SMVal.OuterBody[root].body, ForEachApply];
IF unloadTheBcd THEN {
DeleteLoadStateEntry[ls, FALSE];
IF nunl > 0 THEN {
ls.out.PutF["%d modules unloaded.\n", IO.card[nunl]];
ls.out.PutF["All code spaces and frames have been freed.\nDO NOT TRY TO USE THEM.\n\n"];
};
}
ELSE { -- detach the fake config from the modeller
ls.configIndex ← PilotLoadStateOps.NullConfig;
ls.fakeBcdSpace ← Space.nullHandle; ls.fakeBcdFileName ← NIL};
ls.started ← FALSE; ls.importedInterfaces ← NIL};
-- internal procedures
DeleteLoadStateEntry: PROC[ls: LS, replace: BOOL] ~ {
index: PilotLoadStateOps.ConfigIndex ← ls.configIndex;
IF index ~= PilotLoadStateOps.NullConfig THEN {
ENABLE UNWIND => {PilotLoadStateOps.ReleaseLoadState[]};
map: PilotLoadStateOps.Map;
ls.configIndex ← PilotLoadStateOps.NullConfig;
[] ← PilotLoadStateOps.InputLoadState[];
map ← PilotLoadStateOps.GetMap[index];
PilotLoadStateOps.RemoveConfig[map, index];
PilotLoadStateOps.ReleaseMap[map];
PilotLoadStateOps.ReleaseLoadState[]};
IF ~replace AND ls.fakeBcdSpace ~= Space.nullHandle THEN {
Space.Delete[ls.fakeBcdSpace];
ls.fakeBcdSpace ← Space.nullHandle;
Directory.DeleteFile[LOOPHOLE[ls.fakeBcdFileName] ! Directory.Error => {CONTINUE}];
ls.fakeBcdFileName ← NIL};
};
-- the load state is locked for the entire time
LoadBcds: PROC[ls: LS, root: Tree.Link] RETURNS [nBcds: NAT ← 0] ~ {
ENABLE UNWIND => {PilotLoadStateOps.ReleaseLoadState[]};
configGfi: PrincOps.GFTIndex ← 1;
ForEachApply: SAFE PROC[node, parent: Tree.Link] ~ TRUSTED {
IF TreeOps.OpName[node] IN Tree.ApplOp THEN {
-- this is interesting if either an Apply of a source file whose bcd is valid
-- or an fiBcd for a .Bcd mentioned directly in the model
bcd: SMProj.Proj ← NIL;
WITH SMVal.ValOfNthSon[node, 1] SELECT FROM
subNode: Tree.Handle =>
IF TreeOps.OpName[subNode] IN Tree.ApplOp THEN
bcd ← NARROW[TreeOps.GetExt[subNode]];
fiBcd: SMFI.BcdFileInfo => { -- temporary (inefficient)
bcd ← SMProj.Find[fiBcd.stamp];
IF ~bcd.Available THEN bcd.Fill[fiBcd.localName, FALSE]}
ENDCASE;
IF bcd # NIL AND ~bcd.interface THEN {
loadMod: SMVal.LoadMod ← NARROW[SMVal.GetExtFromParse[node]];
IF loadMod = NIL THEN loadMod ← (ls.z).NEW[SMVal.LoadModRecord ← []];
loadMod.proj ← bcd;
IF ~bcd.Available THEN
ls.out.PutF["Error - can't load %s (not on the disk)\n", IO.rope[bcd.localName]]
ELSE IF loadMod.loadInfo = NIL THEN {
nBcds ← nBcds + 1;
[loadMod.loadInfo, configGfi] ←
SMLoad.LoadGlobalFrames[bcd.capability, ls.configIndex, configGfi, ls.out
! SMLoad.InvalidFile => {
ls.out.PutF["Error - invalid bcd %s\n", IO.rope[bcd.localName]];
REJECT} -- not caught at this level
];
SetUpExports[ls, loadMod]}
ELSE IF loadMod.mustReplace THEN {
replaceResult: SMLoad.ReplaceResult ~
SMLoad.LoadIncremental[bcd.capability, loadMod.loadInfo, ls.out];
IF replaceResult = $ok THEN {
loadMod.mustReplace ← FALSE; nBcds ← nBcds + 1;
SetUpExports[ls, loadMod]} -- gets any newly exported procs
ELSE ls.out.PutF[
SELECT replaceResult FROM
$configNotReplaceable => "Load of %s failed, is a config.\n",
$frameTooBig => "Load of %s failed, frame too big.\n",
$ngfiTooBig => "Load of %s failed, # gfis too big.\n",
$checkForMRFailed => "Load of %s failed, outstanding local frames(?).\n",
ENDCASE => ERROR,
IO.rope[bcd.localName]];
};
SMVal.PutExtInParse[node, loadMod]};
};
};
SMVal.VisitNodes[ls.tm, root, ForEachApply]};
-- fill in exported interface records from the bcd
SetUpExports: PROC[ls: LS, loadMod: SMVal.LoadMod] ~ {
loadInfo: SMLoad.LoadInfo ~ loadMod.loadInfo;
n: NAT ← 1;
ForEachExport: PROC[eth: BcdOps.EXPHandle, eti: BcdDefs.EXPIndex]
RETURNS[stop: BOOLLSE] ~ {
ir: SMLoad.IR ~ SMLoad.BuildInterface[loadInfo, eth];
loadInfo.exports[n] ← ir;
n ← n+1};
loadInfo.exports ← (ls.z).NEW[SMLoad.IRSeqRecord[loadInfo.bcdBase.nExports+1]];
IF loadInfo.bcdBase.nModules = 1 THEN
-- build interface record for a compiler-produced module
loadInfo.exports[0] ← SMLoad.BuildFramePtrInterface[loadInfo.bcdBase, loadInfo[0].frame];
[] ← BcdOps.ProcessExports[loadInfo.bcdBase, ForEachExport]};
-- load state should not be locked
ProcessPlusAndThen: PROC[ls: LS, root: Tree.Link] ~ {
BinaryOp: PROC[left, right: SMLoad.IR, mode: Tree.NodeName]
RETURNS[result: SMLoad.IR] ~ {
IF left = NIL THEN RETURN[right];
IF right = NIL THEN RETURN[left];
IF left.size ~= right.size OR left.stamp ~= right.stamp THEN {
-- a TYPE CHECK
ls.out.PutF["Interface mismatch between %s and %s.\n",
IO.atom[left.name], IO.atom[right.name]];
RETURN[left]};
result ← SMLoad.AllocateIR[left.name, left.size];
result.stamp ← left.stamp;
result.resolved ← TRUE;
FOR i: NAT IN [0 .. left.size) DO
SELECT mode FROM
$then =>
result[i] ← IF EmptyLink[left[i].link] THEN right[i] ELSE left[i];
$union => { -- +
IF ~EmptyLink[left[i].link] AND ~EmptyLink[right[i].link] THEN
ls.out.PutF["Multiple exports of item %d in interface %s.\n",
IO.card[i], IO.atom[left.name]];
result[i] ← IF EmptyLink[left[i].link] THEN right[i] ELSE left[i]};
ENDCASE => ERROR; -- other operators not yet implemented
IF EmptyLink[result[i].link] THEN result.resolved ← FALSE;
ENDLOOP;
};
ForEachNode: SAFE PROC[node, parent: Tree.Link] ~ TRUSTED {
SELECT TreeOps.OpName[node] FROM
$union, $then => {
left: SMLoad.IR ~ PossibleCoercion[SMVal.ValOfNthSon[node, 1]];
right: SMLoad.IR ~ PossibleCoercion[SMVal.ValOfNthSon[node, 2]];
TreeOps.PutExt[node, BinaryOp[left, right, TreeOps.OpName[node]]]};
$subscript => {
gb: Tree.Link ~ TreeOps.NthSon[node, 1];
left: Tree.Link ~ SMVal.ValOf[gb];
selector: Tree.Name ~ TreeOps.GetName[TreeOps.NthSon[node, 2]];
SELECT TreeOps.OpName[left] FROM
IN Tree.ApplOp => {
typeName: Tree.Name ~ IndexToType[gb, selector];
desiredName: Tree.Name ~
(IF typeName # Tree.nullName THEN typeName ELSE selector);
loadMod: SMVal.LoadMod ~ NARROW[SMVal.GetExtFromParse[left]];
exports: SMLoad.IRSeq ~ loadMod.loadInfo.exports;
FOR i: NAT IN [0 .. exports.size) DO
IF exports[i].name = desiredName THEN {
TreeOps.PutExt[node, exports[i]];
EXIT};
REPEAT
FINISHED =>
IF typeName # $CONTROL THEN
ls.out.PutF["Error - %s is not exported by %s.\n",
IO.atom[selector], IO.rope[loadMod.proj.localName]];
ENDLOOP;
};
IN Tree.BindOp => {
v: Tree.Link ~ SMVal.Select[left, selector];
IF v # Tree.null THEN TreeOps.PutExt[node, PossibleCoercion[v]]
ELSE ls.out.PutF["Error - %s is not a valid selector.\n", IO.atom[selector]]};
ENDCASE;
};
ENDCASE => NULL;
};
SMVal.VisitNodes[ls.tm, root, ForEachNode]};
IndexToType: PROC[gb: Tree.Link, index: Tree.Name]
RETURNS[typeName: Tree.Name ← Tree.nullName] ~ {
WITH gb SELECT FROM
id: Tree.Id => {
d: Tree.Link ~ SMVal.IdType[id];
FindIndexType: TreeOps.Scan ~ CHECKED {
elemName: Tree.Name ~ TreeOps.GetName[TreeOps.NthSon[t, 1]];
IF elemName = index THEN {
type: Tree.Link ~ TreeOps.NthSon[t, 2];
WITH type SELECT FROM
typeId: Tree.Id => typeName ← SMVal.IdName[typeId];
ENDCASE => -- temporary
IF TreeOps.OpName[type] = $control THEN typeName ← $CONTROL
ELSE NULL; -- for now
};
};
IF TreeOps.OpName[d] # $decl THEN ERROR;
TreeOps.ScanSons[d, FindIndexType]};
ENDCASE => NULL; -- for now
};
EmptyLink: PROC[link: PrincOps.ControlLink] RETURNS[BOOL] ~ {
RETURN[link = PrincOps.UnboundLink OR link = PrincOps.NullLink]};
-- fill in links
InputActuals: PROC[ls: LS, formals: Tree.Link] ~ {
-- called with the load state locked
n: NAT ~ TreeOps.NSons[formals];
IF n = 0 THEN ls.importedInterfaces ← NIL
ELSE {
ls.importedInterfaces ← (ls.z).NEW[SMLoad.IRSeqRecord[n]];
FOR i: NAT IN [1 .. n] DO
id: Tree.Id ~ NARROW[TreeOps.GetExt[TreeOps.NthSon[formals, i]]];
type: Tree.Link ~ SMVal.ValOf[SMVal.IdType[id]];
stamp: TimeStamp.Stamp ← TimeStamp.Null;
-- examine first son, it is either an apply for a mesa or an fiBcd for a bcd in the model
WITH SMVal.ValOfNthSon[type, 1] SELECT FROM
node: Tree.Handle =>
IF TreeOps.OpName[node] IN Tree.ApplOp THEN
stamp ← NARROW[TreeOps.GetExt[node], SMProj.Proj].stamp;
fiBcd: SMFI.BcdFileInfo => stamp ← fiBcd.stamp;
ENDCASE;
IF stamp # TimeStamp.Null THEN
ls.importedInterfaces[i-1] ← GetInterface[ls, stamp];
ENDLOOP;
};
};
GetInterface: PROC[ls: LS, bcdVers: TimeStamp.Stamp] RETURNS[ir: SMLoad.IR] ~ {
-- called with loadstate locked
linkerIR: CedarLinkerOps.IR;
name: ATOM;
{
[interface~linkerIR, name~name] ← CedarLinkerOps.GetIR[version~bcdVers
! Loader.Error => {
IF type = $versionMismatch THEN {
ls.out.PutF["Error - version mismatch on %s\n",
IO.string[LOOPHOLE[message]]];
PilotLoadStateOps.ReleaseLoadState[];
GOTO inputForNextIR};
PilotLoadStateOps.ReleaseLoadState[]; REJECT} -- reject it
];
IF linkerIR = NIL THEN {
-- this is one of
-- 1) an imported module from the loadstate (done on demand; see LookupFrame)
-- 2) an imported interface that is all-INLINES
-- 3) or an imported interface that no one exports (error)
ir ← NIL}
ELSE {
ir ← SMLoad.AllocateIR[name, linkerIR.size];
ir.stamp ← bcdVers;
FOR i: NAT IN [0 .. ir.size) DO ir[i] ← [link~linkerIR[i]] ENDLOOP};
EXITS
inputForNextIR => [] ← PilotLoadStateOps.InputLoadState[];
};
RETURN};
ResolveImports: PROC[ls: LS, root: Tree.Link] ~ {
-- traverses the value tree
ForEachApply: SAFE PROC[node, parent: Tree.Link] ~ TRUSTED {
IF TreeOps.OpName[node] IN Tree.ApplOp THEN
WITH SMVal.GetExtFromParse[node] SELECT FROM
loadMod: SMVal.LoadMod =>
IF ~loadMod.loadInfo.linksResolved THEN {
rand: Tree.Link ~ SMVal.ValOfNthSon[node, 2];
args: Tree.Link ~ (IF SMVal.Binding[rand] THEN SMVal.BtoG[rand] ELSE rand);
FillInImports[loadMod, args]}
ENDCASE => NULL;
};
FillInImports: PROC[loadMod: SMVal.LoadMod, args: Tree.Link] ~ {
loadInfo: SMLoad.LoadInfo ~ loadMod.loadInfo;
bcdBase: BcdOps.BcdBase ~ loadInfo.bcdBase;
gfiMap: SMLoad.GfiMap ~ loadInfo.gfiMap;
imports: SMLoad.IRSeq ~ loadInfo.imports;
mod, imp: NAT ← 0;
ForEachImport: PROC[ith: BcdOps.IMPHandle, iti: BcdDefs.IMPIndex]
RETURNS[stop: BOOLLSE] ~ {
fth: BcdOps.FTHandle ~ @LOOPHOLE[bcdBase + bcdBase.ftOffset, BcdDefs.Base][ith.file];
FOR i: NAT IN [0 .. ith.ngfi) DO
gfiMap[ith.gfi + i] ← [index~imp, whichOne~i];
ENDLOOP;
-- handle funny cases where two instances of the same interface are imported ???
IF ith.gfi = gfiMap.size THEN
gfiMap.size ← gfiMap.size + ith.ngfi;
imports[imp] ← LookUpInterface[ls, fth.version, imp, args];
-- imports[imp] may be NIL
IF FALSE AND imports[imp] = NIL THEN {
-- generates spurious warnings about Inline, etc.
sym: Rope.Text ~ SMLoad.NSToRope[bcdBase, ith.name];
ls.out.PutF["Warning- cannot find exporter of %s anywhere.\n", IO.rope[sym]]};
imp ← imp + 1};
ForEachModule: PROC[mth: BcdOps.MTHandle, mti: BcdDefs.MTIndex]
RETURNS[stop: BOOLLSE] ~ {
resolved: BOOL ← TRUE;
[] ← SMLoad.OpenLinkSpace[loadInfo[mod].frame, mth, bcdBase];
FOR i: CARDINAL IN [0 .. PilotLoaderOps.LinkSegmentLength[mth, bcdBase]) DO
bound: BOOL;
clink: PrincOps.ControlLink ← SMLoad.ReadLink[i];
[clink, bound] ← NewLink[
ls~ls, blink~PilotLoaderOps.IthLink[mth, i, bcdBase],
oldclink~clink, loadMod~loadMod, frame~loadInfo[mod].frame,
mth~mth, bcdBase~bcdBase, linkinx~i];
IF bound THEN SMLoad.WriteLink[offset~i, link~clink]
ELSE resolved ← FALSE;
ENDLOOP;
SMLoad.CloseLinkSpace[loadInfo[mod].frame];
IF ~resolved THEN loadInfo.linksResolved ← FALSE;
mod ← mod + 1};
IF bcdBase.nImports = 0 THEN RETURN; -- no imports
-- the first part of gfiMap, the map between config gfi's and real gfi's,
-- has already been computed
-- set up map between dummygfi's and the import table
-- IF gfiMap.size > bcdBase.firstdummy THEN ERROR;
gfiMap.size ← bcdBase.firstdummy; -- adjust for dummies to come
[] ← BcdOps.ProcessImports[bcdBase, ForEachImport];
loadInfo.linksResolved ← TRUE;
-- now run thru the frame links looking for imports to fill in
[] ← BcdOps.ProcessModules[bcdBase, ForEachModule]};
SMVal.VisitNodes[ls.tm, root, ForEachApply]};
PossibleCoercion: SAFE PROC[t: Tree.Link] RETURNS[SMLoad.IR] ~ CHECKED {
SELECT TreeOps.OpName[t] FROM
IN Tree.ApplOp => {
-- this is a coercion, simply use the first export
loadMod: SMVal.LoadMod ~ NARROW[SMVal.GetExtFromParse[t]];
RETURN[loadMod.loadInfo.exports[1]]}; -- check size before allowing coercion?
$subscript, $then, $union =>
RETURN[NARROW[TreeOps.GetExt[t]]];
$nil => RETURN[NIL];
ENDCASE => RETURN[NIL] -- ERROR?
};
LookUpInterface: PROC[
ls: LS, bcdVers: TimeStamp.Stamp, imp: NAT, args: Tree.Link]
RETURNS[SMLoad.IR] ~ {
LookupOutside: PROC[id: Tree.Id] RETURNS[ir: SMLoad.IR] ~ {
ir ← ls.importedInterfaces[id.p-1]; -- id must be a formal
IF ir = NIL THEN
ls.out.PutF["Can't import %s from load state\n", IO.atom[SMVal.IdName[id]]];
RETURN};
IF TreeOps.OpName[args] # $group THEN ERROR;
IF imp+1 > TreeOps.NSons[args] THEN
RETURN[HiddenImport[ls, args, bcdVers]];
WITH SMVal.ValOfNthSon[args, imp+1] SELECT FROM
node: Tree.Handle => RETURN[PossibleCoercion[node]];
id: Tree.Id => RETURN[LookupOutside[id]];
ENDCASE => ERROR;
};
HiddenImport: PROC[ls: LS, args: Tree.Link, bcdVers: TimeStamp.Stamp]
RETURNS[ir: SMLoad.IR ← NIL] ~ {
CheckArg: TreeOps.Scan ~ CHECKED {
WITH t SELECT FROM
id: Tree.Id => {
type: Tree.Link ~ SMVal.ValOf[SMVal.IdType[id]];
IF TreeOps.OpName[type] IN Tree.ApplOp THEN {
typeArgs: Tree.Link ~ SMVal.ValOfNthSon[type, 2];
CheckTypeArg: TreeOps.Scan ~ CHECKED {
WITH SMVal.ValOf[t] SELECT FROM
node: Tree.Handle => {
argIr: SMLoad.IR ~ PossibleCoercion[node];
IF argIr ~= NIL AND argIr.stamp = bcdVers THEN {
IF ir ~= NIL AND ir ~= argIr THEN
ls.out.PutF["Ambiguous implicit import of %s\n", IO.atom[ir.name]];
ir ← argIr};
};
ENDCASE;
CheckArg[t]}; -- multi-level hidden imports
TreeOps.ScanSons[typeArgs, CheckTypeArg]};
};
node: Tree.Handle =>
SELECT TreeOps.OpName[node] FROM
$union, $then => {
CheckArg[TreeOps.NthSon[node, 1]]; CheckArg[TreeOps.NthSon[node, 2]]};
ENDCASE => NULL; -- for now
ENDCASE;
};
TreeOps.ScanSons[args, CheckArg];
-- ir will be NIL here only if implicitly importing instance came from loadstate
IF ir = NIL THEN {
[] ← PilotLoadStateOps.InputLoadState[];
ir ← GetInterface[ls, bcdVers ! UNWIND => {PilotLoadStateOps.ReleaseLoadState[]}];
PilotLoadStateOps.ReleaseLoadState[]};
RETURN};
NewLink: PROC[
ls: LS, blink: BcdDefs.Link, oldclink: PrincOps.ControlLink,
loadMod: SMVal.LoadMod,
frame: PrincOps.GlobalFrameHandle, mth: BcdOps.MTHandle, bcdBase: BcdOps.BcdBase,
linkinx: CARDINAL]
RETURNS[newclink: PrincOps.ControlLink, resolved: BOOL] ~ {
loadInfo: SMLoad.LoadInfo ~ loadMod.loadInfo;
FindLink: PROC[blink: BcdDefs.Link] RETURNS[PrincOps.ControlLink, BOOL] ~ {
IF blink.gfi < loadInfo.bcdBase.firstdummy THEN {
SELECT blink.vtag FROM
$proc0, $proc1 => {
rgfi: PrincOps.GFTIndex ~ loadInfo.gfiMap[blink.gfi].index;
newclink ← SMLoad.ConvertLink[blink];
newclink.gfi ← rgfi + loadInfo.gfiMap[blink.gfi].whichOne;
resolved ← (rgfi ~= PrincOps.GFTNull)};
$var => {
[link~newclink] ← CedarLinkerOps.FindVariableLink[
bcd~loadInfo.bcdBase, mthLink~blink,
rgfi~loadInfo.gfiMap[blink.vgfi].index];
resolved ← ~EmptyLink[newclink]};
ENDCASE => NULL;
}
ELSE {
intNo: CARDINAL ~ loadInfo.gfiMap[blink.gfi].index;
trueEP: CARDINAL ~
blink.ep + (loadInfo.gfiMap[blink.gfi].whichOne * BcdDefs.EPLimit);
ir: SMLoad.IR ← loadInfo.imports[intNo];
IF ir = NIL THEN -- try the module frame table
loadInfo.imports[intNo] ← ir ← LookupFrame[ls, loadInfo.bcdBase, intNo];
-- import not satisfied?
IF ir = NIL OR EmptyLink[ir[trueEP].link] THEN {
ith: BcdOps.IMPHandle ~ GetImpHandle[loadInfo.bcdBase, intNo];
fth: BcdOps.FTHandle ~
@LOOPHOLE[loadInfo.bcdBase + loadInfo.bcdBase.ftOffset, BcdDefs.Base][ith.file];
sym: Rope.Text ~ SMLoad.NSToRope[loadInfo.bcdBase, ith.name];
ls.out.PutF["Warning - Unable to resolve import of item #%d from interface %s\n\tof %s ",
IO.card[trueEP], IO.rope[sym], IO.rope[CS.RopeFromStamp[fth.version]]];
ls.out.PutF["(the %dth import of %s).\n",
IO.card[intNo], IO.rope[loadMod.proj.localName]];
UpdatePendingList[ls, sym, frame, mth, bcdBase, linkinx, trueEP];
RETURN[oldclink, FALSE]};
-- at this point module and variable links are set to their absolute addresses
newclink ← ir[trueEP].link; resolved ← TRUE};
RETURN[newclink, resolved]};
newclink ← oldclink; resolved ← FALSE;
SELECT blink.vtag FROM
$proc0, $proc1,
$var =>
IF EmptyLink[oldclink] THEN [newclink, resolved] ← FindLink[blink];
ENDCASE => newclink ← LOOPHOLE[blink.typeID];
};
UpdatePendingList: PROC[
ls: LS,
name: Rope.Text, frame: PrincOps.GlobalFrameHandle,
mth: BcdOps.MTHandle, bcdBase: BcdOps.BcdBase, linkinx, trueEP: CARDINAL] ~ {
atom: ATOM ~ Atom.MakeAtom[name];
pending: CedarLinkerOps.PendingList ← CedarLinkerOps.GetPendingList[atom];
-- pendingCount ← pendingCount + 1;
pending ← (ls.z).CONS[[frame, mth, bcdBase, linkinx, LOOPHOLE[trueEP]], pending];
CedarLinkerOps.SetPendingList[atom, pending]};
-- intNo starts at 0
GetImpHandle: PROC[bcdBase: BcdOps.BcdBase, intNo: CARDINAL]
RETURNS[ith: BcdOps.IMPHandle] ~ {
RETURN[@LOOPHOLE[bcdBase + bcdBase.impOffset, BcdDefs.Base]
[LOOPHOLE[intNo*SIZE[BcdDefs.IMPRecord], BcdDefs.IMPIndex]]];
};
LookupFrame: PROC[ls: LS, bcdBase: BcdOps.BcdBase, intNo: CARDINAL]
RETURNS[SMLoad.IR] ~ {
ith: BcdOps.IMPHandle ~ GetImpHandle[bcdBase, intNo];
fth: BcdOps.FTHandle ~ @LOOPHOLE[bcdBase + bcdBase.ftOffset, BcdDefs.Base][ith.file];
name: ATOM ~ Atom.MakeAtom[SMLoad.NSToRope[bcdBase, ith.name]];
version: TimeStamp.Stamp ~ fth.version;
ir: SMLoad.IR;
clink: PrincOps.ControlLink;
old: REF BcdDefs.VersionStamp;
FOR l: LIST OF REF FrameListRecord ← ls.frameInterfaces, l.rest UNTIL l = NIL DO
IF l.first.stamp = version AND l.first.ir.name = name THEN
RETURN[l.first.ir];
ENDLOOP;
old ← (ls.z).NEW[BcdDefs.VersionStamp ← version];
Atom.PutProp[name, $version, old];
-- try for imported module, this is very expensive
clink ← LoaderPrivate.GetModuleLink[atom~name];
IF ~EmptyLink[clink] THEN { -- found
ir ← SMLoad.AllocateIR[name, 1];
ir[0] ← [link~clink];
ir.stamp ← version}
ELSE ir ← NIL;
-- caches result so GetModuleLink is only called once per name
ls.frameInterfaces ← (ls.z).CONS[(ls.z).NEW[FrameListRecord ←
[stamp~version, ir~ir]], ls.frameInterfaces];
RETURN[ir]};
-- call Paul Rovner's procedure to fixup the Cedar Atoms and Ropes section
ProcessCedarBcds: PROC[ls: LS, root: Tree.Link] ~ {
-- traverses the value tree
ForEachApply: SAFE PROC[node, parent: Tree.Link] ~ TRUSTED {
IF TreeOps.OpName[node] IN Tree.ApplOp THEN
WITH SMVal.GetExtFromParse[node] SELECT FROM
loadMod: SMVal.LoadMod => {
loadInfo: SMLoad.LoadInfo ~ loadMod.loadInfo;
bcdBase: BcdOps.BcdBase;
IF loadInfo = NIL OR loadInfo.rtStarted THEN RETURN;
bcdBase ← loadInfo.bcdBase;
IF ~bcdBase.extended THEN RETURN;
RTLoader.AcquireTypesAndLiterals[bcd~bcdBase, map~loadInfo.map];
loadInfo.rtStarted ← TRUE};
ENDCASE => NULL;
};
SMVal.VisitNodes[ls.tm, root, ForEachApply]};
StartElemRecord: TYPE ~ RECORD [
prog: PROGRAM,
frame: PrincOps.GlobalFrameHandle];
Started: PUBLIC SAFE PROC[ls: LS] RETURNS[BOOL] ~ CHECKED {
RETURN [Loaded[ls] AND ls.started]};
StartAll: PUBLIC SAFE PROC[ls: LS, root: Tree.Link] ~ TRUSTED {
starr: LIST OF REF StartElemRecord;
prog: PROGRAM;
dontFork: BOOL ← TRUE;
-- traverses the value tree
ForEachNode: SAFE PROC[node, parent: Tree.Link] ~ CHECKED {
IF SMVal.Binding[node] THEN {
d: Tree.Link ~ SMVal.BtoD[node];
g: Tree.Link ~ SMVal.BtoG[node];
p: NAT ← 0;
CheckElem: TreeOps.Scan ~ TRUSTED {
p ← p+1;
IF TreeOps.OpName[SMVal.ValOfNthSon[t, 2]] = $control THEN
StartModule[SMVal.ValOfNthSon[g, p]]
};
TreeOps.ScanSons[d, CheckElem]};
};
StartModule: PROC[t: Tree.Link] ~ {
WITH t SELECT FROM
node: Tree.Handle => {
SELECT TreeOps.OpName[node] FROM
IN Tree.ApplOp =>
WITH SMVal.GetExtFromParse[node] SELECT FROM
loadMod: SMVal.LoadMod => {
FOR i: NAT IN [0 .. loadMod.loadInfo.size) DO
Runtime.ValidateGlobalFrame[loadMod.loadInfo[i].frame];
IF loadMod.loadInfo[i].frame.started THEN {
ls.out.PutF["Error - %s has already been started.\n",
IO.rope[loadMod.proj.localName]];
RETURN};
ENDLOOP;
ls.out.PutF["Will start %s\n", IO.rope[loadMod.proj.localName]];
prog ← LOOPHOLE[loadMod.loadInfo.cm];
starr ← (ls.z).CONS[(ls.z).NEW[StartElemRecord ← [
prog~prog, frame~loadMod.loadInfo[0].frame]], starr];
};
ENDCASE;
$subscript => StartModule[SMVal.ValOfNthSon[node, 1]];
ENDCASE;
};
ENDCASE => NULL;
};
SMVal.VisitNodes[ls.tm, SMVal.OuterBody[root].body, ForEachNode];
starr ← LOOPHOLE[List.DReverse[LOOPHOLE[starr]]];
IF dontFork THEN StartProcedure[ls, starr]
ELSE Process.Detach[FORK StartProcedure[ls, starr]];
};
-- this procedure may be forked
StartProcedure: PROC[ls: LS, starr: LIST OF REF StartElemRecord] ~ {
i: CARDINAL ← 0;
ls.started ← TRUE;
{
ENABLE ABORTED, IO.UserAborted => {GOTO aborted};
FOR l: LIST OF REF StartElemRecord ← starr, l.rest UNTIL l = NIL DO
i ← i + 1;
IF l.first.frame.started THEN
ls.out.PutF["Error - element %d of start list has already been started.\n", IO.card[i]]
ELSE START l.first.prog;
ENDLOOP;
EXITS
aborted => NULL;
};
IF i = 0 THEN ls.out.PutF["Nothing was started.\n\n"]
ELSE ls.out.PutF["All %d modules have been started.\n\n", IO.card[i]]};
}.
-- code not yet ready (needs additions to SMOps.MS or Global)
PutExportsInLoadState: PROC[g: SM.Global] ~ {
mi: SM.MI;
FOR l: SM.ModuleList ← g.moduleList, l.rest UNTIL l = NIL DO
mi ← l.first;
IF mi.exportedInterface THEN {
FOR l: LIST OF SMLoad.IR ← g.moduleExports, l.rest UNTIL l = NIL DO
IF l.first.bcdVers = mi.bcdVers THEN {
AddInterfaceToLoadState[g, l.first];
-- g.ttyout.PutF["%s exported into load state.\n", IO.rope[l.first.name]];
EXIT};
REPEAT
FINISHED =>
g.ttyout.PutF["Error - cannot find %s exported by this program.\n",
IO.rope[mi.fileName]];
ENDLOOP;
};
ENDLOOP;
-- process any pendings we have found
IF g.toBeProcessed ~= NIL THEN
CedarExporterImpl.ProcessPendingEntries[g.toBeProcessed];
};
AddInterfaceToLoadState: PROC[g: SM.Global, ir: SMLoad.IR] ~ {
atom: ATOM ~ Atom.MakeAtom[ir.name];
pending: CedarLinkerOps.PendingList;
linkerIR: CedarLinkerOps.IR ~ CedarLinkerOps.GetIR[atom, ir.stamp, ir.size].interface;
FOR i: NAT IN [0 .. ir.size) DO
IF ~EmptyLink[ir[i].link] THEN linkerIR[i] ← ir[i].link;
ENDLOOP;
-- fill in any importers
pending ← CedarLinkerOps.GetPendingList[atom];
IF pending ~= NIL THEN {
[g.toBeProcessed, pending] ←
CedarExporterImpl.SaveResolvedEntries[g.toBeProcessed, pending, linkerIR];
CedarLinkerOps.SetPendingList[atom, pending]};
};