DIRECTORY
Alloc: TYPE USING [AddNotify, Bounds, DropNotify, Handle, Notifier, Top],
BcdBindDefs: TYPE USING [RelocHandle],
BcdComData: TYPE USING [currentName, table, textIndex],
BcdControlDefs: TYPE USING [],
BcdDefs:
TYPE
USING [
CTIndex, cttype, cxtype, EXPIndex, EXPNull, exptype,
FTIndex, FTNull, fttype, IMPIndex, IMPNull, IMPRecord, imptype,
LFIndex, LFNull, lftype, Link, MTIndex, MTRecord, mttype,
NameRecord, NameString, NullLink, NullName, sstype, sttype, treetype, UnboundLink],
BcdErrorDefs:
TYPE
USING [
ErrorHti, ErrorInterface, ErrorSti, Error2Files, GetModule, GetSti],
BcdUtilDefs:
TYPE
USING [
EqVersions, GetDummyGfi, GetGfi, InstanceName, NameForSti],
ConvertUnsafe: TYPE USING [SubString],
HashOps: TYPE USING [FindString],
PrincOps: TYPE USING [EPRange, GFTIndex],
Symbols: TYPE USING [CXIndex, cxNull, HTIndex, htNull, STIndex, stNull, STRecord],
Table: TYPE USING [Base],
Tree: TYPE USING [Scan, null],
TreeOps: TYPE USING [ScanList];
BcdBind:
PROGRAM
IMPORTS
Alloc, BcdErrorDefs, BcdUtilDefs, HashOps, TreeOps,
data: BcdComData
EXPORTS BcdControlDefs = {
OPEN BcdDefs, Symbols;
BindError: PUBLIC ERROR ~ CODE;
table: Alloc.Handle;
tb, stb, ctb, cxb, mtb, lfb, etb, itb, ftb: Table.Base;
ssb: BcdDefs.NameString;
Notifier: Alloc.Notifier ~ {
tb ← base[treetype]; stb ← base[sttype]; cxb ← base[cxtype];
ctb ← base[cttype]; mtb ← base[mttype]; lfb ← base[lftype];
etb ← base[exptype]; itb ← base[imptype];
ftb ← base[fttype];
ssb ← base[sstype]};
Error: PROC ~ {ERROR BindError};
ItiToIndex:
PROC [impi: IMPIndex]
RETURNS [
CARDINAL] ~
INLINE {
RETURN [LOOPHOLE[impi, CARDINAL]/IMPRecord.SIZE]};
relocationHead: BcdBindDefs.RelocHandle;
rel: BcdBindDefs.RelocHandle;
BindRoot:
PUBLIC
PROC [relocationRoot: BcdBindDefs.RelocHandle] ~ {
table ← data.table; table.AddNotify[Notifier];
relocationHead ← relocationRoot;
SetupGFMap[];
AssignImports[
! BcdErrorDefs.GetSti => {
IF rel #
NIL
THEN
RESUME [StiForContext[
IF rel.type = $inner THEN rel.parentcx ELSE rel.context]]}];
BindModules[];
ReleaseGFMap[];
table.DropNotify[Notifier]; table ← NIL};
LinkType:
TYPE ~
RECORD [
SELECT tag:*
FROM
gfi => [gfi: PrincOps.GFTIndex],
import => [impi: IMPIndex],
ENDCASE];
GFMapItem:
TYPE ~
RECORD [
linkItem: LinkType,
expi: EXPIndex,
offset: [0..4)];
GFMap: TYPE ~ RECORD [SEQUENCE length: CARDINAL OF GFMapItem];
RelMap: TYPE ~ RECORD [SEQUENCE length: CARDINAL OF CARDINAL];
finalFirstDummy: PrincOps.GFTIndex;
gfMap: REF GFMap ← NIL;
relMap: REF RelMap ← NIL;
SetupGFMap:
PROC ~ {
nDummies: CARDINAL ← BcdUtilDefs.GetDummyGfi[0]-1;
nImports: CARDINAL ~ table.Bounds[imptype].size/IMPRecord.SIZE;
finalFirstDummy ← BcdUtilDefs.GetGfi[0];
IF nDummies # 0 THEN nDummies ← nDummies + 1;
gfMap ← NEW[GFMap[nDummies]];
FOR i: CARDINAL IN [0..nDummies) DO gfMap[i] ← [[gfi[0]], EXPNull, 0] ENDLOOP;
relMap ← NEW[RelMap[nImports]];
FOR rel: BcdBindDefs.RelocHandle ← relocationHead, rel.link
UNTIL rel =
NIL
DO
FOR iti: IMPIndex ← IMPIndex.
FIRST+rel.import, iti+IMPRecord.
SIZE
UNTIL iti = rel.importLimit
DO
relMap[ItiToIndex[iti]] ← itb[iti].gfi + rel.dummygfi-rel.originalfirstdummy;
ENDLOOP;
ENDLOOP};
RelocatedGfi:
PROC [iti: IMPIndex]
RETURNS [
CARDINAL] ~ {
RETURN [IF iti = IMPNull THEN 0 ELSE relMap[ItiToIndex[iti]]]};
ReleaseGFMap:
PROC ~ {
gfMap ← NIL;
relMap ← NIL};
NameToHti:
PROC [name: NameRecord]
RETURNS [hti: HTIndex] ~ {
ss: ConvertUnsafe.SubString ← [base~@ssb.string, offset~name, length~ssb.size[name]];
hti ← HashOps.FindString[ss];
IF hti = htNull THEN Error[]};
ExpiForSti:
PROC [sti: STIndex]
RETURNS [EXPIndex] ~ {
RETURN [
IF sti = stNull
THEN EXPNull
ELSE
WITH s~~stb[sti]
SELECT
FROM
external => WITH m~~s.map SELECT FROM interface => m.expi, ENDCASE => EXPNull,
ENDCASE => EXPNull]};
AssignImports:
PROC ~ {
saveIndex: CARDINAL ~ data.textIndex;
saveName: NameRecord ~ data.currentName;
FOR rel ← relocationHead, rel.link
UNTIL rel =
NIL
DO
data.textIndex ← rel.textIndex;
data.currentName ← BcdUtilDefs.NameForSti[StiForContext[rel.context]];
SELECT
TRUE
FROM
(rel.type = $outer) => AssignOuter[rel];
(rel.parameters # Tree.null) => AssignByPosition[rel];
ENDCASE => AssignByName[rel];
ENDLOOP;
data.textIndex ← saveIndex; data.currentName ← saveName};
AssignOuter:
PROC [rel: BcdBindDefs.RelocHandle] ~ {
FOR iti: IMPIndex ← IMPIndex.
FIRST+rel.import, iti+IMPRecord.
SIZE
UNTIL iti = rel.importLimit
DO
sti: STIndex ~ LookupInstance[iti, rel.context];
IF sti = stNull THEN LOOP;
IF stb[sti].impi # IMPNull
THEN {
OPEN imp~~itb[stb[sti].impi];
stb[sti].impgfi ← imp.gfi ← BcdUtilDefs.GetGfi[imp.ngfi]}
ELSE BcdErrorDefs.ErrorSti[$error, "is not imported by any module"L, sti];
ENDLOOP};
AssignByName:
PROC [rel: BcdBindDefs.RelocHandle] ~ {
iti, import: IMPIndex;
export: EXPIndex;
defgfi: CARDINAL;
sti, parentSti: STIndex;
FOR iti ← IMPIndex.
FIRST+rel.import, iti+IMPRecord.
SIZE
UNTIL iti = rel.importLimit
DO
sti ←
IF rel.type = $inner
THEN LookupInstance[iti, rel.context]
ELSE LookupInterface[iti, rel.context];
IF sti = stNull THEN LOOP;
defgfi ← stb[sti].impgfi;
IF stb[sti].impi # IMPNull
THEN
SELECT rel.type
FROM
$inner => {
IF (parentSti ← LookupInterface[iti, rel.parentcx]) = stNull
THEN LOOP;
import ← stb[parentSti].impi; export ← ExpiForSti[parentSti];
defgfi ← stb[parentSti].impgfi;
sti ← parentSti};
ENDCASE => {import ← stb[sti].impi; export ← ExpiForSti[sti]}
ELSE {import ← IMPNull; export ← ExpiForSti[sti]};
WITH s~~stb[sti]
SELECT
FROM
external =>
WITH m~~s.map
SELECT
FROM
module => AssignModule[defgfi, m.mti, iti];
interface => AssignInterface[defgfi, import, export, iti];
unknown => AssignImport[defgfi, import, iti];
ENDCASE => Error[];
unknown => AssignImport[defgfi, import, iti];
ENDCASE => Error[];
ENDLOOP};
LookupInstance:
PROC [iti: IMPIndex, cxi: CXIndex]
RETURNS [STIndex] ~ {
RETURN [
IF cxi = cxNull
THEN stNull
ELSE Lookup[
hti~NameToHti[
IF itb[iti].namedInstance
THEN BcdUtilDefs.InstanceName[[import[iti]]]
ELSE itb[iti].name],
cxi~rel.context]]};
LookupInterface:
PROC [iti: IMPIndex, cxi: CXIndex]
RETURNS [STIndex] ~ {
RETURN [IF cxi = cxNull THEN stNull ELSE Lookup[NameToHti[itb[iti].name], cxi]]};
AssignByPosition:
PROC [rel: BcdBindDefs.RelocHandle] ~ {
iti: IMPIndex;
TooManyParameters: ERROR ~ CODE;
AssignPosition: Tree.Scan ~ {
sti: STIndex ~ WITH t SELECT FROM symbol => index, ENDCASE => ERROR;
import: IMPIndex ~ stb[sti].impi;
export: EXPIndex ~ ExpiForSti[sti];
defgfi: CARDINAL ~ stb[sti].impgfi;
IF iti = rel.importLimit THEN ERROR TooManyParameters;
WITH s~~stb[sti]
SELECT
FROM
external =>
WITH m~~s.map
SELECT
FROM
module => AssignModule[defgfi, m.mti, iti];
interface => AssignInterface[defgfi, import, export, iti];
unknown => AssignImport[defgfi, import, iti];
ENDCASE => Error[];
ENDCASE => BcdErrorDefs.ErrorSti[$error, "is undeclared"L, sti];
iti ← iti + BcdDefs.IMPRecord.SIZE};
iti ← IMPIndex.FIRST + rel.import;
TreeOps.ScanList[rel.parameters, AssignPosition
! TooManyParameters => {GOTO tooMany}];
IF iti # rel.importLimit THEN GOTO tooFew;
EXITS
tooMany => BcdErrorDefs.ErrorHti[$error,
"has too many parameters"L, HtiForRelocation[rel]];
tooFew => BcdErrorDefs.ErrorHti[$error,
"has too few parameters"L, HtiForRelocation[rel]]};
MakeLink:
PROC [defgfi:
CARDINAL, import: IMPIndex, offset:
CARDINAL]
RETURNS [LinkType] ~ {
RETURN [
SELECT
TRUE
FROM
(defgfi # 0) => [gfi[defgfi+offset]],
(import = IMPNull) => [gfi[0]],
ENDCASE => [import[import]]]};
AssignModule:
PROC [defgfi: PrincOps.GFTIndex, mti: MTIndex, iti: IMPIndex] ~ {
OPEN imp~~itb[iti];
gfi: CARDINAL ~ RelocatedGfi[iti];
IF imp.port # $module
OR ~BcdUtilDefs.EqVersions[imp.file, mtb[mti].file]
THEN
BcdErrorDefs.Error2Files[
class~$error,
s~"is required for import, but available version is"L,
ft1~imp.file, ft2~mtb[mti].file];
gfMap[gfi] ← [
linkItem~[gfi[IF defgfi # 0 THEN defgfi ELSE mtb[mti].gfi]],
expi~EXPNull, offset~0]};
AssignInterface:
PROC [defgfi: PrincOps.GFTIndex, import: IMPIndex, expi: EXPIndex, iti: IMPIndex] ~ {
OPEN exp~~etb[expi], imp~~itb[iti];
gfi: CARDINAL ~ RelocatedGfi[iti];
IF expi # EXPNull
AND
(imp.port # exp.port
OR ~BcdUtilDefs.EqVersions[imp.file, exp.file])
THEN
BcdErrorDefs.Error2Files[
class~$error,
s~"is required for import, but available version is"L,
ft1~imp.file, ft2~exp.file];
IF imp.port = $module
THEN
gfMap[gfi] ← [
linkItem~[gfi[etb[expi].links[0].gfi]],
expi~EXPNull, offset~0]
ELSE
FOR i: [0..4)
IN [0..imp.ngfi)
DO
gfMap[gfi+i] ← [
linkItem~MakeLink[defgfi, import, i],
expi~expi, offset~i];
ENDLOOP};
AssignImport:
PROC [defgfi: PrincOps.GFTIndex, import: IMPIndex, iti: IMPIndex] ~ {
OPEN imp~~itb[iti];
gfi: CARDINAL ~ RelocatedGfi[iti];
IF import # IMPNull
AND
(imp.port # itb[import].port
OR
~BcdUtilDefs.EqVersions[imp.file, itb[import].file])
THEN
BcdErrorDefs.Error2Files[
class~$error,
s~"is required for import, but available version is"L,
ft1~imp.file, ft2~itb[import].file];
FOR i: [0..4)
IN [0..imp.ngfi)
DO
gfMap[gfi+i] ← [
linkItem~MakeLink[defgfi, import, i],
expi~EXPNull, offset~i];
ENDLOOP};
Lookup:
PROC [hti: HTIndex, cxi: CXIndex]
RETURNS [sti: STIndex] ~ {
FOR sti ← cxb[cxi].link, stb[sti].link
UNTIL sti = stNull
DO
IF stb[sti].hti = hti THEN RETURN ENDLOOP;
RETURN [stNull]};
StiForContext:
PROC [cxi: CXIndex]
RETURNS [sti: STIndex] ~ {
stLimit: STIndex ~ table.Top[sttype];
FOR sti ← STIndex.
FIRST, sti+STRecord.
SIZE
UNTIL sti = stLimit
DO
WITH s~~stb[sti]
SELECT
FROM
local => IF s.context = cxi THEN RETURN;
ENDCASE;
ENDLOOP;
RETURN [stNull]};
HtiForRelocation:
PROC [rel: BcdBindDefs.RelocHandle]
RETURNS [HTIndex] ~ {
sti: STIndex;
mti: MTIndex;
cti: CTIndex;
IF rel.type # $file
THEN {
sti ← StiForContext[rel.context]; RETURN [stb[sti].hti]};
mti ← MTIndex.FIRST + rel.module; cti ← CTIndex.FIRST + rel.config;
RETURN [NameToHti[IF mtb[mti].config = cti THEN ctb[cti].name ELSE mtb[mti].name]]};
BindModules:
PROC ~ {
saveIndex: CARDINAL ~ data.textIndex;
saveName: NameRecord ~ data.currentName;
mtLimit: MTIndex ~ table.Top[mttype];
rel ← relocationHead;
FOR mti: MTIndex ← MTIndex.
FIRST,
mti + (
WITH m~~mtb[mti]
SELECT
FROM
direct => MTRecord.direct.SIZE + m.length*Link.SIZE,
indirect => MTRecord.indirect.SIZE,
multiple => MTRecord.multiple.SIZE,
ENDCASE => ERROR)
UNTIL mti = mtLimit DO
SetRelocationForModule[mti];
WITH m~~mtb[mti]
SELECT
FROM
direct =>
FOR i:
CARDINAL
IN [0 .. m.length)
DO
m.frag[i] ← RelocateLink[m.frag[i]
! BcdErrorDefs.GetModule => {RESUME [mti, i]}];
ENDLOOP;
indirect => BindFragment[mti, m.links];
multiple => BindFragment[mti, m.links];
ENDCASE => ERROR;
ENDLOOP;
data.textIndex ← saveIndex; data.currentName ← saveName};
SetRelocationForModule:
PROC [mti: MTIndex] ~ {
gfi: PrincOps.GFTIndex ~ mtb[mti].gfi;
FOR rel ← rel, rel.link
UNTIL rel =
NIL
DO
IF gfi IN [rel.firstgfi..rel.lastgfi] THEN GOTO found ENDLOOP;
FOR rel ← relocationHead, rel.link
UNTIL rel =
NIL
DO
IF gfi IN [rel.firstgfi..rel.lastgfi] THEN GOTO found ENDLOOP;
Error[];
EXITS found => {
data.textIndex ← rel.textIndex;
data.currentName ← BcdUtilDefs.NameForSti[StiForContext[rel.context]]}};
BindFragment:
PROC [mti: MTIndex, lfi: LFIndex] ~ {
IF lfi # LFNull
THEN
FOR i:
CARDINAL
IN [0 .. lfb[lfi].length)
DO
lfb[lfi].frag[i] ← RelocateLink[lfb[lfi].frag[i]
! BcdErrorDefs.GetModule => {RESUME [mti, i]}];
ENDLOOP};
RelocateLink:
PROC [cl: BcdDefs.Link]
RETURNS [BcdDefs.Link] ~ {
SELECT
TRUE
FROM
(cl.vtag = $type) => ERROR;
(cl.gfi = 0) => NULL;
(cl.gfi < rel.originalfirstdummy) => cl.gfi ← cl.gfi + rel.firstgfi-1;
ENDCASE => {
gfi: CARDINAL;
expi: EXPIndex;
map: LONG POINTER TO GFMapItem;
gfi ← cl.gfi + rel.dummygfi-rel.originalfirstdummy;
DO
map ← @gfMap[gfi];
IF (expi←map.expi) # EXPNull
THEN {
newCl: BcdDefs.Link ~ etb[expi].links[cl.ep + map.offset*PrincOps.EPRange];
IF newCl # NullLink THEN RETURN [newCl]};
WITH map.linkItem
SELECT
FROM
m: LinkType.gfi => {
IF (gfi←m.gfi) = 0 THEN GOTO unbindable;
IF gfi < finalFirstDummy
AND cl.ep = 0
THEN
cl ← [variable[vgfi~0, var~0, vtag~$var]];
EXIT};
m: LinkType.import => gfi ← RelocatedGfi[m.impi]+map.offset;
ENDCASE;
REPEAT
unbindable => {
importName: NameRecord;
offset: CARDINAL;
importFti: FTIndex;
[importName, offset, importFti] ← LookupImport[cl.gfi];
BcdErrorDefs.ErrorInterface[
class~$warning, s~"is unbindable"L,
import~[name~importName, fti~importFti], ep~(cl.ep + offset)];
RETURN [IF cl.vtag = $var THEN NullLink ELSE UnboundLink]};
ENDLOOP;
cl.gfi ← gfi};
RETURN [cl]};
LookupImport:
PROC [gfi: PrincOps.GFTIndex]
RETURNS [importName: NameRecord, offset: CARDINAL, importFti: FTIndex] ~ {
FOR iti: IMPIndex ← (IMPIndex.
FIRST + rel.import),
(iti + IMPRecord.
SIZE)
UNTIL iti = rel.importLimit
DO
OPEN imp~~itb[iti];
IF gfi
IN [imp.gfi..imp.gfi+imp.ngfi)
THEN
RETURN[
importName~imp.name, offset~(gfi-imp.gfi)*PrincOps.EPRange,
importFti~imp.file];
ENDLOOP;
RETURN [importName~NullName, offset~0, importFti~FTNull]};
}.