<<>> <> <> <> <> <> <> <> <> <> DIRECTORY Alloc USING [AddNotify, Bounds, DropNotify, Handle, Notifier, Top], MobBindDefs USING [RelocHandle], MobComData USING [data], MobControlDefs USING [], MobDefs USING [CTIndex, cttype, cxtype, EXPIndex, EXPNull, exptype, FTIndex, FTNull, fttype, IMPIndex, IMPNull, IMPRecord, imptype, LFIndex, LFNull, lftype, Link, ModuleIndex, MTIndex, MTRecord, mttype, NameRecord, nullLink, NullName, sstype, sttype, treetype, unboundLink], MobErrorDefs USING [ErrorHti, ErrorInterface, ErrorSti, Error2Files, GetModule, GetSti], MobUtilDefs USING [EqVersions, GetDummyGfi, GetGfi, InstanceName, NameForSti], ConvertUnsafe USING [SubString], MobHashOps USING [FindString], MobSymbols USING [CXIndex, cxNull, HTIndex, HTNull, STIndex, stNull, STRecord], Table USING [Base], MobTree USING [Scan, null, Link], MobTreeOps USING [ScanList]; MobBind: PROGRAM IMPORTS Alloc, MobErrorDefs, MobUtilDefs, MobHashOps, MobTreeOps, MobComData EXPORTS MobControlDefs = { OPEN MobDefs, MobSymbols; BindError: PUBLIC ERROR ~ CODE; table: Alloc.Handle; tb, stb, ctb, cxb, mtb, lfb, etb, itb, ftb: Table.Base; ssb: LONG STRING; 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[CARD] ~ INLINE { RETURN[LOOPHOLE[impi, CARD]/IMPRecord.SIZE]}; relocationHead: MobBindDefs.RelocHandle; rel: MobBindDefs.RelocHandle; BindRoot: PUBLIC PROC[relocationRoot: MobBindDefs.RelocHandle] ~ { table ¬ MobComData.data.table; table.AddNotify[Notifier]; relocationHead ¬ relocationRoot; SetupGFMap[]; AssignImports[ ! MobErrorDefs.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: ModuleIndex], import => [impi: IMPIndex], ENDCASE]; GFOffset: TYPE = INT; GFMapItem: TYPE ~ RECORD[ linkItem: LinkType, expi: EXPIndex, offset: GFOffset]; GFMap: TYPE ~ RECORD[SEQUENCE length: CARDINAL OF GFMapItem]; RelMap: TYPE ~ RECORD[SEQUENCE length: CARDINAL OF CARDINAL]; finalFirstDummy: ModuleIndex; gfMap: REF GFMap ¬ NIL; relMap: REF RelMap ¬ NIL; SetupGFMap: PROC ~ { nDummies: CARDINAL ¬ MobUtilDefs.GetDummyGfi[0]-1; nImports: CARDINAL ~ table.Bounds[imptype].size/IMPRecord.SIZE; finalFirstDummy ¬ MobUtilDefs.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: MobBindDefs.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].modIndex + 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, offset~name+1, length~ssb.text[name].ORD]; hti ¬ MobHashOps.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 ~ MobComData.data.textIndex; saveName: NameRecord ~ MobComData.data.currentName; FOR rel ¬ relocationHead, rel.link UNTIL rel = NIL DO MobComData.data.textIndex ¬ rel.textIndex; MobComData.data.currentName ¬ MobUtilDefs.NameForSti[StiForContext[rel.context]]; SELECT TRUE FROM (rel.type = $outer) => AssignOuter[rel]; (rel.parameters # MobTree.null) => AssignByPosition[rel]; ENDCASE => AssignByName[rel]; ENDLOOP; MobComData.data.textIndex ¬ saveIndex; MobComData.data.currentName ¬ saveName}; AssignOuter: PROC[rel: MobBindDefs.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 { stb[sti].impgfi ¬ itb[stb[sti].impi].modIndex ¬ MobUtilDefs.GetGfi[1]} ELSE MobErrorDefs.ErrorSti[$error, "is not imported by any module"L, sti]; ENDLOOP }; AssignByName: PROC[rel: MobBindDefs.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 MobUtilDefs.InstanceName[[0,0,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: MobBindDefs.RelocHandle] ~ { iti: IMPIndex; TooManyParameters: ERROR ~ CODE; AssignPosition: MobTree.Scan ~ { sti: STIndex ~ NARROW[t, MobTree.Link.symbol].index; 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 => MobErrorDefs.ErrorSti[$error, "is undeclared"L, sti]; iti ¬ iti + MobDefs.IMPRecord.SIZE}; iti ¬ IMPIndex.FIRST + rel.import; MobTreeOps.ScanList[rel.parameters, AssignPosition ! TooManyParameters => {GOTO tooMany}]; IF iti # rel.importLimit THEN GOTO tooFew; EXITS tooMany => MobErrorDefs.ErrorHti[$error, "has too many parameters"L, HtiForRelocation[rel]]; tooFew => MobErrorDefs.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: ModuleIndex, mti: MTIndex, iti: IMPIndex] ~ { gfi: CARDINAL ~ RelocatedGfi[iti]; IF itb[iti].port # $module OR ~MobUtilDefs.EqVersions[itb[iti].file, mtb[mti].file] THEN MobErrorDefs.Error2Files[ class~$error, s~"is required for import, but available version is"L, ft1~itb[iti].file, ft2~mtb[mti].file]; gfMap[gfi] ¬ [ linkItem~[gfi[IF defgfi # 0 THEN defgfi ELSE mtb[mti].modIndex]], expi~EXPNull, offset~0]}; AssignInterface: PROC[defgfi: ModuleIndex, import: IMPIndex, expi: EXPIndex, iti: IMPIndex] ~ { gfi: CARDINAL ~ RelocatedGfi[iti]; IF expi # EXPNull AND (itb[iti].port # etb[expi].port OR ~MobUtilDefs.EqVersions[itb[iti].file, etb[expi].file]) THEN MobErrorDefs.Error2Files[ class~$error, s~"is required for import, but available version is"L, ft1~itb[iti].file, ft2~etb[expi].file]; IF itb[iti].port = $module THEN gfMap[gfi] ¬ [ linkItem~[gfi[etb[expi].links[0].from.modIndex]], expi~EXPNull, offset~0] ELSE FOR i: GFOffset IN [0..1) DO gfMap[gfi+i] ¬ [ linkItem~MakeLink[defgfi, import, i], expi~expi, offset~i]; ENDLOOP}; AssignImport: PROC[defgfi: ModuleIndex, import: IMPIndex, iti: IMPIndex] ~ { gfi: CARDINAL ~ RelocatedGfi[iti]; IF import # IMPNull AND (itb[iti].port # itb[import].port OR ~MobUtilDefs.EqVersions[itb[iti].file, itb[import].file]) THEN MobErrorDefs.Error2Files[ class~$error, s~"is required for import, but available version is"L, ft1~itb[iti].file, ft2~itb[import].file]; FOR i: GFOffset IN [0..1) 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 ~ LOOPHOLE[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: MobBindDefs.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 ~ MobComData.data.textIndex; saveName: NameRecord ~ MobComData.data.currentName; mtLimit: MTIndex ~ LOOPHOLE[table.Top[mttype]]; rel ¬ relocationHead; FOR mti: MTIndex ¬ MTIndex.FIRST, (mti + MTRecord.SIZE) UNTIL mti = mtLimit DO SetRelocationForModule[mti]; FOR i: CARDINAL IN [0 .. lfb[mtb[mti].links].length) DO saveLink: MobDefs.Link ¬ lfb[mtb[mti].links].frag[i]; lfb[mtb[mti].links].frag[i] ¬ RelocateLink[saveLink ! MobErrorDefs.GetModule => {RESUME [mti, i, saveLink]}]; ENDLOOP; ENDLOOP; MobComData.data.textIndex ¬ saveIndex; MobComData.data.currentName ¬ saveName}; SetRelocationForModule: PROC[mti: MTIndex] ~ { gfi: ModuleIndex ~ mtb[mti].modIndex; 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 => { MobComData.data.textIndex ¬ rel.textIndex; MobComData.data.currentName ¬ MobUtilDefs.NameForSti[StiForContext[rel.context]]}}; BindFragment: PROC[mti: MTIndex, lfi: LFIndex] ~ { IF lfi # LFNull THEN FOR i: CARDINAL IN [0 .. lfb[lfi].length) DO saveLink: MobDefs.Link ¬ lfb[lfi].frag[i]; lfb[lfi].frag[i] ¬ RelocateLink[saveLink ! MobErrorDefs.GetModule => RESUME [mti, i, saveLink]]; ENDLOOP }; RelocateLink: PROC[cl: MobDefs.Link] RETURNS[MobDefs.Link] ~ { SELECT TRUE FROM (cl.tag = $type) => ERROR; (cl.modIndex = 0) => NULL; (cl.modIndex < rel.originalfirstdummy) => cl.modIndex ¬ cl.modIndex + rel.firstgfi-1; ENDCASE => { gfi: CARDINAL; expi: EXPIndex; map: LONG POINTER TO GFMapItem; gfi ¬ cl.modIndex + rel.dummygfi-rel.originalfirstdummy; DO map ¬ @gfMap[gfi]; IF (expi¬map.expi) # EXPNull THEN { newCl: MobDefs.Link ~ etb[expi].links[cl.offset + map.offset*0].from; 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.offset = 0 THEN cl ¬ [tag: $var, modIndex: 0, offset: 0]; EXIT}; m: LinkType.import => gfi ¬ RelocatedGfi[m.impi]+map.offset; ENDCASE; REPEAT unbindable => { importName: NameRecord; offset: CARDINAL; importFti: FTIndex; [importName, offset, importFti] ¬ LookupImport[cl.modIndex]; MobErrorDefs.ErrorInterface[ class~$warning, s~"is unbindable"L, import~[name~importName, fti~importFti], ep~(cl.offset + offset)]; RETURN [IF cl.tag = $var THEN nullLink ELSE unboundLink]}; ENDLOOP; cl.modIndex ¬ gfi}; RETURN [cl]}; LookupImport: PROC[gfi: ModuleIndex] RETURNS[importName: NameRecord, offset: CARDINAL, importFti: FTIndex] ~ { FOR iti: IMPIndex ¬ (IMPIndex.FIRST + rel.import), (iti + IMPRecord.SIZE) UNTIL iti = rel.importLimit DO IF gfi IN [itb[iti].modIndex..itb[iti].modIndex+1) THEN RETURN[ importName~itb[iti].name, offset~(gfi-itb[iti].modIndex)*0 <>, importFti~itb[iti].file]; ENDLOOP; RETURN[importName~NullName, offset~0, importFti~FTNull]}; }.