
    <<SymbolCopier.mesa>>
        <<Copyright  1985 by Xerox Corporation.  All rights reserved.>>
        <<Satterthwaite, April 14, 1986 10:31:13 am PST>>
        <<Maxwell, August 11, 1983 8:34 am>>
        <<Russ Atkinson (RRA) March 6, 1985 10:23:03 pm PST>>

    DIRECTORY
        Alloc: TYPE USING [Handle, Notifier, AddNotify, DropNotify, Top, Words],
        Basics: TYPE USING [LongDivMod, LongMult],
        ConvertUnsafe: TYPE USING [SubString],
        Copier: TYPE USING [FindMdEntry, FreeSymbolTable, GetSymbolTable],
        Literals: TYPE USING [LitIndex, STNull],
        LiteralOps: TYPE USING [CopyLiteral],
        OSMiscOps: TYPE USING [FreeWords, Words],
        SourceMap: TYPE USING [nullLoc, Down],
        SymbolTable: TYPE USING [Base],
        Symbols: TYPE,
        SymbolOps: TYPE USING [EnterExtension, EnterString, FirstCtxSe, LinkBti, MakeCtxSe, MakeNonCtxSe, MakeSeChain, NewCtx, NextSe, 
        ParentBti, RCType, ResetCtxList, SearchContext, SetSeLink, SubStringForName, UnderType],
        SymbolPack: TYPE,
        Tree: TYPE USING [Base, Index, Link, Scan, MapFromX, Null, NullIndex, treeType],
        TreeOps: TYPE USING [FreeNode, GetNode, OpName, PopTree, PushNode, PushTree, ScanList, SetAttr, SetInfo],
        XSymbolSegment: TYPE SymbolSegment USING [ExtIndex, ExtRecord],
        XTree: TYPE Tree USING [Index, Link, Null],
        XTreeOps: TYPE USING [CopyTreeFromX];

    SymbolCopier: PROGRAM
        IMPORTS Alloc, Basics, Copier, LiteralOps, OSMiscOps, TreeOps, ownSymbols: SymbolPack, SourceMap, SymbolOps, XTreeOps
        EXPORTS Copier = { 
        OPEN SymbolOps, Symbols;

    <<tables defining the current symbol table>>

        table: Alloc.Handle;

        seb: Symbols.Base;        -- se table
        ctxb: Symbols.Base;        -- context table
        mdb: Symbols.Base;        -- module directory base
        bb: Symbols.Base;        -- body table
        tb: Tree.Base;        -- tree table

        CopierNotify: Alloc.Notifier = {
            <<called whenever the main symbol table is repacked>>
            seb _ base[seType]; ctxb _ base[ctxType];
            mdb _ base[mdType];  bb _ base[bodyType];
            tb _ base[Tree.treeType];
            IF iBase # NIL AND iBase = ownSymbols THEN INotify[]};


    <<table bases for the current include module>>

        iBase: SymbolTable.Base;

        iHt: LONG DESCRIPTOR FOR ARRAY HTIndex OF HTRecord;
        iSeb: Symbols.Base;
        iCtxb: Symbols.Base;

        INotify: PROC = {
            <<called whenever iBase switches or tables moved>>
            iHt _ iBase.ht;  iSeb _ iBase.seb;  iCtxb _ iBase.ctxb};


        MemoCacheSize: CARDINAL = 509;  -- prime < 512
        SearchCache: TYPE = ARRAY [0..MemoCacheSize) OF RECORD[
            hti: HTIndex,
            ctx: CTXIndex];

        memoCache: LONG POINTER TO SearchCache;


    <<initialization/finalization>>

        CopierInit: PUBLIC PROC[
            ownTable: Alloc.Handle,
            symbolCachePages: CARDINAL, useMemo: BOOL] = {
            iBase _ NIL;
            table _ ownTable;
            table.AddNotify[CopierNotify];
            IF useMemo THEN {
                memoCache _ OSMiscOps.Words[SearchCache.SIZE];
                memoCache^ _ ALL[ [hti:HTNull, ctx:CTXNull] ];
                typeCache _ OSMiscOps.Words[TypeCache.SIZE];
                typeCache^ _ ALL[ [mdi:MDNull, iSei:SENull, sei: SENull] ]}
            ELSE {memoCache _ NIL; typeCache _ NIL};
            currentBody _ BTNull};

        ResetCaches: PROC = INLINE {    -- see ResetIncludeContexts
            IF typeCache # NIL THEN OSMiscOps.FreeWords[typeCache];
            IF memoCache # NIL THEN OSMiscOps.FreeWords[memoCache]};

        CopierReset: PUBLIC PROC = {
            ResetIncludeContexts[];
            table.DropNotify[CopierNotify]; table _ NIL};


    <<manipulation of symbol tokens (without copying)>>

        SEToken: PUBLIC TYPE = RECORD[ISEIndex];
        nullSEToken: PUBLIC SEToken _ [ISENull];

        CtxValue: PUBLIC PROC[ctx: CTXIndex, value: CARDINAL] RETURNS[t: SEToken] = {
            mdi: MDIndex;
            iCtx: CTXIndex;
            [mdi, iCtx] _ InverseMapCtx[ctx];
            IF OpenIncludedTable[mdi] THEN {
                t _ [iBase.SeiForValue[value, iCtx]]; CloseIncludedTable[]}
            ELSE t _ nullSEToken;
            RETURN};

        CtxFirst: PUBLIC PROC[ctx: CTXIndex] RETURNS[t: SEToken] = {
            mdi: MDIndex;
            iCtx: CTXIndex;
            [mdi, iCtx] _ InverseMapCtx[ctx];
            IF OpenIncludedTable[mdi] THEN {
                t _ [iBase.FirstCtxSe[iCtx]]; CloseIncludedTable[]}
            ELSE t _ nullSEToken;
            RETURN};

        CtxNext: PUBLIC PROC[ctx: CTXIndex, t: SEToken] RETURNS[next: SEToken] = {
            mdi: MDIndex;
            iCtx: CTXIndex;
            [mdi, iCtx] _ InverseMapCtx[ctx];
            IF t # nullSEToken AND OpenIncludedTable[mdi] THEN {
                next _ [iBase.NextSe[t]]; CloseIncludedTable[]}
            ELSE next _ nullSEToken;
            RETURN};

        TokenHash: PUBLIC PROC[ctx: CTXIndex, t: SEToken] RETURNS[hti: HTIndex] = {
            mdi: MDIndex = InverseMapCtx[ctx].mdi;
            IF t # nullSEToken AND OpenIncludedTable[mdi] THEN {
                hti _ MapHti[iBase.seb[t].hash];  CloseIncludedTable[]}
            ELSE hti _ HTNull;
            RETURN};

        TokenValue: PUBLIC PROC[ctx: CTXIndex, t: SEToken] RETURNS[v: WORD] = {
            mdi: MDIndex = InverseMapCtx[ctx].mdi;
            IF t # nullSEToken AND OpenIncludedTable[mdi] THEN {
                v _ iBase.seb[t].idValue;  CloseIncludedTable[]}
            ELSE v _ 0;
            RETURN};

        TokenSymbol: PUBLIC PROC[ctx: CTXIndex, t: SEToken] RETURNS[sei: ISEIndex] = {
            mdi: MDIndex = InverseMapCtx[ctx].mdi;
            SELECT TRUE FROM
                (mdi = OwnMdi) => sei _ t;
                OpenIncludedTable[mdi] => {
                    sei _ LOOPHOLE[CopyIncludedSymbol[t, mdi]];  CloseIncludedTable[]};
                ENDCASE => sei _ ISENull;
            RETURN};


    <<copying across table boundaries>>

        SubString: TYPE = ConvertUnsafe.SubString;

        SearchFileCtx: PUBLIC PROC[hti: HTIndex, ctx: IncludedCTXIndex]
            RETURNS[found: BOOL, sei: ISEIndex] = {
            s: SubString;
            hash: [0..MemoCacheSize);
            iHti: HTIndex;
            iSei: ISEIndex;
            mdi: MDIndex = ctxb[ctx].module;
            s _ SubStringForName[hti];
            hash _ Basics.LongDivMod[
                Basics.LongMult[LOOPHOLE[hti], LOOPHOLE[ctx]], MemoCacheSize].remainder;
            IF memoCache # NIL AND memoCache[hash].hti = hti AND memoCache[hash].ctx = ctx THEN
                RETURN[FALSE, ISENull];
            IF OpenIncludedTable[mdi] THEN {
                iHti _ iBase.FindString[s];
                IF iHti # HTNull AND (iHt[iHti].anyPublic OR iHt[iHti].anyInternal) THEN {
                    iSei _ iBase.SearchContext[iHti, ctxb[ctx].map];
                    IF (found _ iSei # SENull) THEN sei _ CopyCtxSe[iSei, hti, ctx, mdi]}
                ELSE {found _ FALSE; sei _ ISENull};
                CloseIncludedTable[]}
            ELSE {found _ FALSE; sei _ ISENull};
            IF ~found AND memoCache # NIL THEN memoCache[hash] _ [hti:hti, ctx:ctx];
            RETURN};


        Delink: PUBLIC PROC[sei: ISEIndex] = {
            prev, next: ISEIndex;
            ctx: CTXIndex = seb[sei].idCtx;    -- assumed not reset
            prev _ ctxb[ctx].seList;
            DO
                next _ NextSe[prev];
                SELECT next FROM
                    sei => EXIT;
                    ctxb[ctx].seList, ISENull => ERROR;
                    ENDCASE => prev _ next;
                ENDLOOP;
            IF NextSe[sei] = sei THEN ctxb[ctx].seList _ ISENull
            ELSE {
                IF sei = ctxb[ctx].seList THEN ctxb[ctx].seList _ prev;
                SetSeLink[prev, NextSe[sei]]};
            SetSeLink[sei, ISENull]};

        CopySymbol: PUBLIC PROC[mdi: MDIndex, iSei: SEIndex, depth: Closure] RETURNS[sei: SEIndex] = {
            SELECT TRUE FROM
                (mdi = OwnMdi) => sei _ iSei;
                OpenIncludedTable[mdi] => {
                    sei _ CopyIncludedSymbol[iSei, mdi];
                    WITH s: seb[sei] SELECT FROM
                        id => {
                            CompleteType[s.idType, mdi, depth];
                            IF s.idType = typeTYPE THEN CompleteType[s.idInfo, mdi, depth]}
                        ENDCASE => CompleteType[sei, mdi, depth];
                        CloseIncludedTable[]};
                ENDCASE => sei _ SENull;
            RETURN};


        <<context completion>>

        CompleteContext: PUBLIC PROC[ctx: IncludedCTXIndex, depth: Closure_unit] = {
            IF ctxb[ctx].copied < depth AND OpenIncludedTable[ctxb[ctx].module] THEN {
                FillContext[ctx, depth]; CloseIncludedTable[]}
            };

        AugmentContext: PUBLIC PROC[ctx: IncludedCTXIndex, mdi: MDIndex] = {
            mdRoot: MDIndex;
            target: CTXIndex;
            [mdRoot, target] _ InverseMapCtx[ctx];
            IF ~ctxb[ctx].reset AND OpenIncludedTable[mdi] THEN {
                newMdi: MDIndex = iBase.FindMdi[mdb[mdRoot].stamp];
                IF newMdi # MDNull THEN
                    FOR iCtx: IncludedCTXIndex _ iBase.mdb[newMdi].ctx,
                        iBase.ctxb[iCtx].chain UNTIL iCtx = CTXNull DO
                            IF iBase.ctxb[iCtx].map = target THEN {
                                CopyCtxEntries[ctx, iCtx, mdi, unit];
                                IF ~iBase.ctxb[iCtx].complete THEN ctxb[ctx].complete _ FALSE;
                                IF ctxb[ctx].complete THEN ResetCtx[ctx];
                                EXIT};
                            ENDLOOP;
                CloseIncludedTable[]}
            };


        FillContext: PROC[ctx: IncludedCTXIndex, depth: Closure] = {
            mdi: MDIndex = ctxb[ctx].module;
            CopyCtxEntries[ctx, ctxb[ctx].map, mdi, depth];
            ResetCtx[ctx]};

        CopyContext: PROC[ctx, iCtx: CTXIndex, mdi: MDIndex, depth: Closure] = {
            WITH ctxb[ctx] SELECT FROM
                included => {
                    tCtx: IncludedCTXIndex = LOOPHOLE[ctx];
                    IF ctxb[tCtx].copied < depth AND (~ctxb[tCtx].closed OR depth > unit) THEN {
                        ctxb[tCtx].closed _ TRUE;
                        CopyCtxEntries[tCtx, iCtx, mdi, depth]; ResetCtx[tCtx]}};
                ENDCASE => NULL};

        CopyCtxEntries: PROC[ctx: IncludedCTXIndex, iCtx: CTXIndex, mdi: MDIndex, depth: Closure] = {
            IF ctxb[ctx].copied < depth THEN {
                pSei: ISEIndex _ ISENull;
                ctxb[ctx].copied _ depth;
                FOR iSei: ISEIndex _ iBase.FirstCtxSe[iCtx], iBase.NextSe[iSei] UNTIL iSei = SENull DO
                    hti: HTIndex = MapHti[iSeb[iSei].hash];
                    sei: ISEIndex _ IF hti = HTNull AND ctxb[ctx].reset
                        THEN FirstCtxSe[ctx]
                        ELSE SearchContext[hti, ctx];
                    IF ~ctxb[ctx].reset THEN {
                        IF sei = SENull THEN sei _ CopyCtxSe[iSei, hti, ctx, mdi];
                        IF pSei # SENull AND NextSe[pSei] # sei THEN {
                            Delink[sei]; SetSeLink[sei, NextSe[pSei]]; SetSeLink[pSei, sei]};
                        ctxb[ctx].seList _ pSei _ sei};
                    IF depth > unit AND sei # SENull THEN {
                        subType: CSEIndex =
                            UnderType[IF seb[sei].idType = typeTYPE THEN sei ELSE seb[sei].idType];
                        IF (depth = rc AND RCType[subType] # none) OR depth > rc THEN
                            IF seb[sei].idType = typeTYPE THEN CompleteVariant[sei, mdi, depth]
                            ELSE CompleteType[subType, mdi, depth]};
                    ENDLOOP;
                ctxb[ctx].complete _ TRUE}
            };


        <<recursive type completion>>

        CompleteType: PROC[sei: SEIndex, mdi: MDIndex, depth: Closure] = {
            <<N.B.  still incomplete if depth > rc>>
            type: CSEIndex = UnderType[sei];
            WITH t: seb[type] SELECT FROM
                enumerated => IF depth > rc THEN CompleteEnumeration[type, mdi, depth];
                record => CompleteRecord[type, mdi, TRUE, depth];
                ref => IF depth > rc THEN CompleteType[t.refType, mdi, depth];
                array => {
                    CompleteType[t.indexType, mdi, depth];
                    CompleteType[t.componentType, mdi, depth]};
                arraydesc => IF depth > rc THEN CompleteType[t.describedType, mdi, depth];
                transfer, definition => NULL;        -- *** temporary ***
                union => CompleteUnion[type, mdi, depth];
                sequence => {
                    CompleteType[seb[t.tagSei].idType, mdi, depth];
                    CompleteType[t.componentType, mdi, depth]};
                relative => {
                    CompleteType[t.baseType, mdi, depth];
                    CompleteType[t.offsetType, mdi, depth]};
                subrange => CompleteType[t.rangeType, mdi, depth];
                long, real => CompleteType[t.rangeType, mdi, depth];
                ENDCASE => NULL};


        CompleteEnumeration: PROC[sei: CSEIndex, mdi: MDIndex, depth: Closure] = {
            WITH type: seb[sei] SELECT FROM
                enumerated => {
                    WITH c: ctxb[type.valueCtx] SELECT FROM
                        included =>
                            IF c.copied < depth THEN {
                                IF c.module = mdi THEN FillContext[LOOPHOLE[type.valueCtx], depth]
                                ELSE {
                                    CloseIncludedTable[];
                                    CompleteContext[LOOPHOLE[type.valueCtx], depth];
                                    [] _ OpenIncludedTable[mdi]}};
                        ENDCASE => NULL};
                ENDCASE => NULL};

        CompleteRecord: PROC[sei: CSEIndex, mdi: MDIndex, doLink: BOOL, depth: Closure _ unit] = {
            WITH type: seb[sei] SELECT FROM
                record => {
                    WITH type SELECT FROM
                        linked => IF doLink THEN CompleteRecord[UnderType[linkType], mdi, TRUE, depth];
                        ENDCASE => NULL;
                    WITH c: ctxb[type.fieldCtx] SELECT FROM
                        included =>
                            IF c.copied < depth THEN {
                                IF c.module = mdi THEN FillContext[LOOPHOLE[type.fieldCtx], depth]
                                ELSE {
                                    CloseIncludedTable[];
                                    CompleteContext[LOOPHOLE[type.fieldCtx], depth];
                                    [] _ OpenIncludedTable[mdi]}};
                        ENDCASE => NULL};
                ENDCASE => NULL};

        CompleteUnion: PROC[sei: CSEIndex, mdi: MDIndex, depth: Closure] = {
            WITH type: seb[sei] SELECT FROM
                union => {
                    CompleteType[seb[type.tagSei].idType, mdi, depth];
                    WITH c: ctxb[type.caseCtx] SELECT FROM
                        included =>
                            IF c.copied < depth THEN {
                                IF c.module = mdi THEN FillContext[LOOPHOLE[type.caseCtx], depth]
                                ELSE {
                                    CloseIncludedTable[];
                                    CompleteContext[LOOPHOLE[type.caseCtx], depth];
                                    [] _ OpenIncludedTable[mdi]}};
                        ENDCASE => NULL};
                ENDCASE => NULL};

        CompleteVariant: PROC[sei: ISEIndex, mdi: MDIndex, depth: Closure] = {
            type: CSEIndex = UnderType[sei];
            WITH seb[type] SELECT FROM
                record => CompleteRecord[type, mdi, FALSE, depth];
                ENDCASE => NULL};


        <<variant copying>>

        CopyUnion: PUBLIC PROC[ctx: CTXIndex, depth: Closure _ none] = {
            WITH c: ctxb[ctx] SELECT FROM
                included =>
                    IF ~c.reset AND OpenIncludedTable[c.module] THEN {
                        FillUnionPart[LOOPHOLE[ctx], depth]; CloseIncludedTable[]};
                ENDCASE};


        FillUnionPart: PROC[ctx: IncludedCTXIndex, depth: Closure _ none] = {
            iRoot: ISEIndex = iCtxb[ctxb[ctx].map].seList;
            iSei: ISEIndex _ iRoot;
            DO
                IF iSei = SENull THEN EXIT;
                SELECT iBase.TypeForm[iSeb[iSei].idType] FROM
                    union, sequence => {
                        IF iSeb[iSei].hash # HTNull THEN
                            [] _ CopyIncludedSymbol[iSei, ctxb[ctx].module]
                        ELSE FillContext[LOOPHOLE[ctx], MAX[unit, depth]];
                        EXIT};
                    ENDCASE;
                IF (iSei _ iBase.NextSe[iSei]) = iRoot THEN EXIT;
                ENDLOOP};

        FillUnion: PROC[sei: CSEIndex, mdi: MDIndex] = {
            WITH type: seb[sei] SELECT FROM
                record => {
                    WITH c: ctxb[type.fieldCtx] SELECT FROM
                        included =>
                            IF ~c.reset THEN {
                                IF c.module = mdi THEN FillUnionPart[LOOPHOLE[type.fieldCtx]]
                                ELSE {
                                    CloseIncludedTable[];
                                    CopyUnion[type.fieldCtx];
                                    [] _ OpenIncludedTable[mdi]}};
                        ENDCASE => NULL};
                ENDCASE => NULL};


    <<mappings>>

        MapHti: PROC[iHti: HTIndex] RETURNS[hti: HTIndex] = {
            s: SubString;
            IF iHti = HTNull THEN hti _ HTNull
            ELSE {
                s _ iBase.SubStringForName[iHti];
                hti _ EnterString[s ! TableRelocated => {s.base _ iBase.ssb}]};
            RETURN};

        MissingHti: ERROR = CODE;

        InverseMapHti: PROC[hti: HTIndex] RETURNS[iHti: HTIndex] = {
            s: SubString;
            IF hti = HTNull THEN iHti _ HTNull
            ELSE {
                s _ SubStringForName[hti];
                iHti _ iBase.FindString[s];
                IF iHti = HTNull THEN ERROR MissingHti};
            RETURN};


        FindExternalCtx: PUBLIC PROC[mdi: MDIndex, iCtx: CTXIndex]
            RETURNS[ctx: IncludedCTXIndex] = {
            IF mdi # MDNull AND OpenIncludedTable[mdi] THEN {
                ctx _ MapCtx[mdi, iCtx]; CloseIncludedTable[]}
            ELSE ctx _ IncludedCTXNull;
            RETURN};

        MapCtx: PROC[mdi: MDIndex, iCtx: CTXIndex] RETURNS[IncludedCTXIndex] = {
            ctx, last: IncludedCTXIndex;
            target: CTXIndex;
            mdRoot: MDIndex;
            IF iCtx = CTXNull THEN {mdRoot _ mdi; target _ CTXNull}
            ELSE {
                WITH iCtxb[iCtx] SELECT FROM
                    included => [mdRoot, target] _ IncludedTargets[LOOPHOLE[iCtx]];
                    imported => {
                        IF iBase.mdb[iCtxb[includeLink].module].defaultImport # iCtx THEN
                            ERROR;    -- need a signal to raise
                        [mdRoot, target] _ IncludedTargets[includeLink]};
                    ENDCASE => {mdRoot _ mdi; target _ iCtx}};
            last _ IncludedCTXNull;
            FOR ctx _ mdb[mdRoot].ctx, ctxb[ctx].chain UNTIL ctx = CTXNull DO
                IF ctxb[ctx].map = target AND target # CTXNull THEN RETURN[ctx];
                last _ ctx;
                ENDLOOP;
            ctx _ table.Words[ctxType, CTXRecord.included.SIZE];
            ctxb[ctx] _ CTXRecord[
                levelOrigin: IF iCtx = CTXNull THEN 0 ELSE iCtxb[iCtx].levelOrigin,
                levelOffset: IF iCtx = CTXNull THEN lZ ELSE iCtxb[iCtx].levelOffset,
                seList: ISENull,  varUpdated: FALSE,
                extension: included[
                    chain:  IncludedCTXNull,
                    module: mdRoot,
                    map:  target,
                    restricted: FALSE,  complete: FALSE,
                    closed: FALSE,  reset: FALSE]];
            IF last = IncludedCTXNull THEN mdb[mdRoot].ctx _ ctx ELSE ctxb[last].chain _ ctx;
            RETURN[ctx]};

        InverseMapCtx: PROC[ctx: CTXIndex] RETURNS[mdi: MDIndex, iCtx: CTXIndex] = {
            WITH ctxb[ctx] SELECT FROM
                included => {mdi _ module; iCtx _ map};
                imported => [mdi, iCtx] _ InverseMapCtx[includeLink];
                ENDCASE => {mdi _ OwnMdi; iCtx _ ctx};
            RETURN};


        IncludedTargets: PROC[iCtx: IncludedCTXIndex]
            RETURNS[mdi: MDIndex, ctx: CTXIndex] = {
            oldMdi: MDIndex = iCtxb[iCtx].module;
            s: SubString;
            s _ iBase.SubStringForName[iBase.mdb[oldMdi].fileId];
            mdi _ Copier.FindMdEntry[
                id: MapHti[iBase.mdb[oldMdi].moduleId],
                version: iBase.mdb[oldMdi].stamp,
                file: MapHti[iBase.mdb[oldMdi].fileId]];
            ctx _ iCtxb[iCtx].map;
            RETURN};


        UnknownModule: PUBLIC SIGNAL[HTIndex] = CODE;

        FillModule: PUBLIC PROC[sei: ISEIndex, typeId: HTIndex, mdi: MDIndex] = {
            iHti: HTIndex;
            iSei: ISEIndex;
            IF mdi = MDNull OR ~OpenIncludedTable[mdi] THEN DummyCtxSe[sei]
            ELSE {
                <<allow failure exit>>
                    BEGIN
                    iHti _ InverseMapHti[typeId ! MissingHti => {GO TO failed}];
                    iSei _ iBase.SearchContext[iHti, iBase.stHandle.directoryCtx];
                    IF iSei = ISENull OR ~iSeb[iSei].public THEN GO TO failed;
                    CopyCtxSeInfo[sei, iSei, mdi];  seb[sei].public _ FALSE;
                    EXITS
                        failed => {SIGNAL UnknownModule[seb[sei].hash]; DummyCtxSe[sei]};
                    END;
                CloseIncludedTable[]}};

        DummyCtxSe: PROC[sei: ISEIndex] = {
            seb[sei].idType _ typeANY;  seb[sei].idInfo _ seb[sei].idValue _ 0;
            seb[sei].extended _ seb[sei].public _ seb[sei].linkSpace _ FALSE;
            seb[sei].immutable _ seb[sei].constant _ TRUE;
            seb[sei].mark3 _ seb[sei].mark4 _ TRUE};


    <<caching of (cons) types>>

        TypeCacheSize: CARDINAL = 83;        -- prime < 256/3
        TypeCacheIndex: TYPE = [0..TypeCacheSize);
        TypeCache: TYPE = ARRAY TypeCacheIndex OF RECORD[
            mdi: MDIndex,  iSei: SEIndex,    -- the search keys
            sei: SEIndex];            -- the result

        typeCache: LONG POINTER TO TypeCache;

        TypeHash: PROC[mdi: MDIndex, iSei: SEIndex] RETURNS[TypeCacheIndex] =  INLINE {
            RETURN[(LOOPHOLE[mdi,CARDINAL]*LOOPHOLE[iSei,CARDINAL]) MOD TypeCacheSize]};

        CacheType: PROC[mdi: MDIndex, iSei, sei: SEIndex] = {
            IF typeCache # NIL THEN typeCache[TypeHash[mdi, iSei]] _ [mdi:mdi, iSei:iSei, sei:sei]};

    <<copying symbols>>

        CopyIncludedSymbol: PROC[iSei: SEIndex, mdi: MDIndex] RETURNS[sei: SEIndex] = {
            IF iSei = SENull THEN RETURN[SENull];
            WITH iSe: iSeb[iSei] SELECT FROM
                id => {
                    hti: HTIndex = MapHti[iSe.hash];
                    IF iSe.idCtx IN StandardContext THEN {
                        sei _ SearchContext[hti, iSe.idCtx];
                        IF sei = SENull THEN ERROR}
                    ELSE {
                        ctx: IncludedCTXIndex = MapCtx[mdi, iSe.idCtx];
                        tSei: ISEIndex = SearchContext[hti, ctx];
                        sei _ tSei;
                        IF sei # SENull THEN seb[tSei].idCtx _ ctx
                        ELSE {
                            iMdi: MDIndex _ ctxb[ctx].module;
                            IF iMdi = mdi
                                OR (
                                    iBase.stHandle.extended AND
                                    (~iSe.extended OR iBase.stHandle.definitionsFile) AND
                                    ~mdb[iMdi].shared) THEN
                                    sei _ CopyCtxSe[LOOPHOLE[iSei, ISEIndex], hti, ctx, mdi]
                            ELSE {
                                CloseIncludedTable[];
                                IF OpenIncludedTable[iMdi] THEN
                                    iSei _ iBase.SearchContext[InverseMapHti[hti], ctxb[ctx].map]
                                ELSE [] _ OpenIncludedTable[iMdi_mdi];
                                sei _ CopyCtxSe[LOOPHOLE[iSei, ISEIndex], hti, ctx, iMdi];
                                CloseIncludedTable[];
                                [] _ OpenIncludedTable[mdi]}}}};
                cons =>
                    WITH iType: iSe SELECT FROM
                        mode => sei _ typeTYPE;
                        basic => sei _ MapBasicType[iType.code];
                        ENDCASE => {
                            i: TypeCacheIndex = TypeHash[mdi, iSei];
                            IF typeCache # NIL AND typeCache[i].iSei = iSei AND typeCache[i].mdi = mdi THEN
                                sei _ typeCache[i].sei
                            ELSE sei _ CopyNonCtxSe[LOOPHOLE[iSei, CSEIndex], mdi]};
                ENDCASE;
            RETURN};


        CopyCtxSe: PROC[iSei: ISEIndex, hti: HTIndex, ctx: CTXIndex, mdi: MDIndex]
            RETURNS[sei: ISEIndex] = {
            sei _ MakeCtxSe[hti, ctx]; CopyCtxSeInfo[sei, iSei, mdi]; RETURN};

        CopyCtxSeInfo: PROC[sei, iSei: ISEIndex, mdi: MDIndex] = {
            OPEN id: seb[sei];
            IF iSeb[iSei].idCtx = CTXNull THEN id.idCtx _ CTXNull;
            id.extended _ iSeb[iSei].extended;
            id.public _ iSeb[iSei].public;
            id.immutable _ iSeb[iSei].immutable;
            id.constant _ iSeb[iSei].constant;
            id.linkSpace _ iSeb[iSei].linkSpace;
            id.idType _ CopyIncludedSymbol[iSeb[iSei].idType, mdi];
            IF iSeb[iSei].idType = typeTYPE THEN
                id.idInfo _ CopyIncludedSymbol[iSeb[iSei].idInfo, mdi]
            ELSE IF iSeb[iSei].constant AND
                (SELECT iBase.XferMode[iSeb[iSei].idType] FROM
                    proc, program=> TRUE,
                    ENDCASE=> FALSE) THEN
                id.idInfo _ CopyIncludedBody[iSeb[iSei].idInfo, sei, mdi]
            ELSE id.idInfo _ iSeb[iSei].idInfo;
            IF iSeb[iSei].idType = typeTYPE AND iBase.CtxLevel[iSeb[iSei].idCtx] # lZ 
                AND ~iBase.stHandle.extended THEN
                    id.idValue _ iSei - ISEIndex.FIRST
            ELSE id.idValue _ iSeb[iSei].idValue;
            id.mark3 _ id.mark4 _ TRUE;
            IF id.extended THEN CopyExtension[sei, iSei, mdi]
            ELSE IF id.linkSpace THEN id.idInfo _ 0};


        currentBody: BTIndex;

    FindXExtension: PROC[base: SymbolTable.Base, sei: ISEIndex]
    RETURNS [type: ExtensionType, tree: XTree.Link] = {
    OPEN Xs: XSymbolSegment;
    extLimit: Xs.ExtIndex = LOOPHOLE[base.extLimit];    -- type may lie
    FOR exti: Xs.ExtIndex _ Xs.ExtIndex.FIRST, exti + Xs.ExtRecord.SIZE UNTIL exti = extLimit DO
    IF base.extb[exti].sei = sei THEN RETURN[base.extb[exti].type, base.extb[exti].tree];
    ENDLOOP;
    RETURN[$none, XTree.Null]};

        CopyExtension: PROC[sei, iSei: ISEIndex, mdi: MDIndex] = {
            iType: ExtensionType;
            iTree: XTree.Link;
            saveCurrentBody: BTIndex = currentBody;
            currentBody _ BTNull;
            [iType, iTree] _ FindXExtension[iBase, iSei];
            WITH iTree SELECT FROM
                subtree => IF iBase.tb[index].name = body THEN currentBody _ seb[sei].idInfo;
                ENDCASE;
            EnterExtension[sei, iType, InputExtension[iTree, mdi]];
            currentBody _ saveCurrentBody};

        InputExtension: PROC[t: XTree.Link, mdi: MDIndex] RETURNS[Tree.Link] = {

            InputTree: Tree.MapFromX = {
                WITH link: t SELECT FROM
                    hash => v _ [hash[index: MapHti[link.index]]];
                    symbol => v _ [symbol[index: LOOPHOLE[CopyIncludedSymbol[link.index, mdi]]]];
                    literal => v _ InputLiteral[link];
                    subtree => {
                        iNode: XTree.Index = link.index;
                        v _ SELECT iBase.tb[iNode].name FROM
                            block, ditem => InputBlock[iNode],
                            IN [forseq .. downthru] => InputBlock[iNode],
                            catch => InputCatch[iNode],
                            openx =>
                                XTreeOps.CopyTreeFromX[[baseP:@iBase.tb, link:iBase.tb[iNode].son[1]], InputTree],
                            ENDCASE => XTreeOps.CopyTreeFromX[[baseP:@iBase.tb, link:link], InputTree];
                        WITH v SELECT FROM
                            subtree => {
                                node: Tree.Index = index;
                                SELECT tb[node].name FROM
                                    body => tb[node].info _ currentBody;
                                    block, ditem => ExitBlock[node];
                                    safen => {        -- needed for transition only (pass 4 now places safens)
                                        v _ tb[node].son[1];
                                        tb[node].son[1] _ Tree.Null;  TreeOps.FreeNode[node]};
                                    IN [basicTC..discrimTC], cdot,
                                    IN [apply..typecode], exlist => {
                                        tb[node].info _ CopyIncludedSymbol[iBase.tb[iNode].info, mdi];
                                        SELECT tb[node].name FROM
                                            construct, exlist =>
                                                CompleteRecord[UnderType[tb[node].info], mdi, TRUE];
                                            dollar => UpdateDollar[node];
                                            union =>
                                                WITH tb[node].son[1] SELECT FROM
                                                    symbol => CompleteRecord[UnderType[index], mdi, FALSE];
                                                    ENDCASE => ERROR;
                                            apply => FillUnion[UnderType[tb[node].info], mdi];
                                            bindx => FillBinding[node, mdi];
                                            ENDCASE};
                                    IN [forseq..downthru] => NULL;
                                    do => {
                                        IF TreeOps.OpName[tb[node].son[1]] IN [forseq..downthru] THEN
                                            ExitBlock[TreeOps.GetNode[tb[node].son[1]]];
                                        tb[node].info _ SourceMap.nullLoc};
                                    bind => FillBinding[node, mdi];
                                    catch => {
                                        TreeOps.ScanList[tb[node].son[1], UpdateType];
                                        ExitBlock[node]};
                                    IN [assign..join], decl, typedecl => tb[node].info _ SourceMap.nullLoc;
                                    ENDCASE => NULL};
                            ENDCASE => NULL};
                    ENDCASE => ERROR;
                RETURN};

                UpdateDollar: PROC [node: Tree.Index] = INLINE {
                    WITH tb[node].son[1] SELECT FROM
                        subtree => {
                            sei: CSEIndex = UnderType[tb[index].info];
                            WITH type: seb[sei] SELECT FROM
                                record =>
                                    IF type.argument THEN
                                        WITH tb[node].son[2] SELECT FROM
                                            symbol => index _ SearchContext[seb[index].hash, type.fieldCtx];
                                            ENDCASE => ERROR;
                                ENDCASE};
                        ENDCASE};

                UpdateType: Tree.Scan = {
                    WITH t SELECT FROM
                        subtree => tb[index].info _ CopyIncludedSymbol[tb[index].info, mdi];
                        ENDCASE};

                FillBinding: PROC [node: Tree.Index, mdi: MDIndex] = {
                    WITH tb[node].son[1] SELECT FROM
                        subtree => {
                            subNode: Tree.Index = index;
                            rType: CSEIndex = WITH tb[subNode].son[2] SELECT FROM
                                symbol => UnderType[seb[index].idType],
                                subtree => UnderType[tb[index].info],
                                ENDCASE => ERROR;
                            CompleteRecord[rType, mdi, FALSE]};
                        ENDCASE => ERROR};


            InputLiteral: PROC [t: XTree.Link.literal] RETURNS [Tree.Link] = {
                v: Literals.LitIndex;
                WITH lit: t.index SELECT FROM
                    word => v _ LiteralOps.CopyLiteral[[baseP:@iBase.ltb, index:lit]];
                    string => {
                        IF lit.sti # Literals.STNull THEN ERROR;    -- temporary
                        v _ [string[Literals.STNull]]};
                    ENDCASE => ERROR;
                RETURN[[literal[v]]]};

            InputBlock: PROC [iNode: XTree.Index] RETURNS [v: Tree.Link] = {
                OPEN TreeOps;
                iBti: BTIndex = iBase.tb[iNode].info;
                n: CARDINAL = iBase.tb[iNode].nSons;
                bti: BTIndex;
                IF iBti = BTNull THEN bti _ BTNull
                ELSE {
                    ctx: IncludedCTXIndex = MapCtx[mdi, iBase.bb[iBti].localCtx];
                    bti _ table.Words[bodyType, BodyRecord.Other.SIZE];
                    bb[bti] _ BodyRecord[
                        link: ,
                        firstSon: BTNull,
                        type: LOOPHOLE[CopyIncludedSymbol[iBase.bb[iBti].type, mdi]],
                        localCtx: ctx,  level: iBase.bb[iBti].level,
                        sourceIndex: SourceMap.nullLoc.Down[],  info: ,
                        extension: Other[relOffset: ]];
                    LinkBti[bti: bti, parent: currentBody];  currentBody _ bti};
                FOR i: CARDINAL IN [1 .. n] DO PushTree[InputTree[iBase.tb[iNode].son[i]]] ENDLOOP;
                PushNode[iBase.tb[iNode].name, n];
                SetAttr[1, iBase.tb[iNode].attr1]; SetAttr[2, iBase.tb[iNode].attr2];
                SetAttr[3, iBase.tb[iNode].attr3];  SetInfo[bti];  v _ PopTree[];
                IF bti # BTNull THEN
                    bb[bti].info _ [Internal[bodyTree: GetNode[v], thread: Tree.NullIndex, frameSize: ]];
                RETURN};

            InputCatch: PROC [iNode: XTree.Index] RETURNS [v: Tree.Link] = {
                OPEN TreeOps;
                iBti: CBTIndex = iBase.tb[iNode].info;
                bti: CBTIndex;
                IF iBti = CBTNull THEN bti _ CBTNull
                ELSE {
                    bti _ CopyIncludedBody[
                    iBti, LOOPHOLE[CopyIncludedSymbol[iBase.bb[iBti].id, mdi]], mdi];
                    LinkBti[bti: bti, parent: currentBody];
                    currentBody _ bti};
                PushTree[InputTree[iBase.tb[iNode].son[1]]];
                PushTree[InputTree[iBase.tb[iNode].son[2]]];
                PushNode[catch, 2];
                SetAttr[1, iBase.tb[iNode].attr1]; SetAttr[2, iBase.tb[iNode].attr2];
                SetAttr[3, iBase.tb[iNode].attr3];  SetInfo[bti];  v _ PopTree[];
                IF bti # BTNull THEN
                    bb[bti].info _ [Internal[bodyTree: GetNode[v], thread: Tree.NullIndex, frameSize: ]];
                RETURN};

            ExitBlock: PROC [node: Tree.Index] = INLINE {
                IF tb[node].info # BTNull THEN currentBody _ ParentBti[tb[node].info]};

            RETURN[InputTree[t]]};


        CopyExternalBody: PUBLIC PROC[mdi: MDIndex, iBti: CBTIndex]
            RETURNS [bti: CBTIndex] = {
            IF iBti # CBTNull AND mdi # MDNull AND OpenIncludedTable[mdi] THEN {
                sei: ISEIndex;
                iSei: ISEIndex = iBase.bb[iBti].id;
                IF iSei # ISENull THEN {
                    sei _ LOOPHOLE[CopyIncludedSymbol[iSei, mdi]]; bti _ seb[sei].idInfo}
                ELSE bti _ CopyIncludedBody[iBti, ISENull, mdi];
                CloseIncludedTable[]}
            ELSE bti _ CBTNull;
            RETURN};

        CopyIncludedBody: PROC[iBti: CBTIndex, sei: ISEIndex, mdi: MDIndex]
            RETURNS [bti: CBTIndex] = {
            iCtx: CTXIndex;
            IF iBti = BTNull THEN bti _ CBTNull
            ELSE {
                iCtx _ iBase.bb[iBti].localCtx;
                WITH body: iBase.bb[iBti] SELECT FROM
                    Outer => {
                        bti _ table.Words[bodyType, BodyRecord.Callable.Outer.SIZE];
                        bb[LOOPHOLE[bti, OCBTIndex]] _ body};
                    Inner => {
                        bti _ table.Words[bodyType, BodyRecord.Callable.Inner.SIZE];
                        bb[LOOPHOLE[bti, ICBTIndex]] _ body};
                    ENDCASE => ERROR;
                bb[bti].link _ [parent, BTNull];  bb[bti].firstSon _ BTNull;
                bb[bti].id _ sei;
                IF iCtx = CTXNull THEN {bb[bti].localCtx _ CTXNull; bb[bti].type _ RecordSENull}
                ELSE {
                    bb[bti].localCtx _ MapCtx[mdi, iCtx];
                    bb[bti].type _ LOOPHOLE[CopyIncludedSymbol[iBase.bb[iBti].type, mdi]]};
                SELECT TRUE FROM
                    (iBase.bb[iBti].nesting = Catch) => bb[bti].ioType _ CSENull;
                    iBase.bb[iBti].inline => {
                        bb[bti].ioType _ CopyBodyType[iBase.bb[iBti].ioType, mdi];
                        WITH body: bb[bti].info SELECT FROM
                            Internal => body.thread _ body.bodyTree _ Tree.NullIndex;
                            ENDCASE}
                    ENDCASE =>
                        bb[bti].ioType _ IF sei = ISENull OR seb[seb[sei].idType].seTag = id
                            THEN CopyBodyType[iBase.bb[iBti].ioType, mdi]
                            ELSE UnderType[seb[sei].idType]};
            RETURN};


        MapBasicType: PROC[code: CARDINAL] RETURNS[CSEIndex] = {
            FOR sei: ISEIndex _ FirstCtxSe[StandardContext.FIRST], NextSe[sei]
                UNTIL sei = ISENull DO
                    IF seb[sei].idType = typeTYPE THEN {
                        tSei: CSEIndex = UnderType[sei];
                        WITH t: seb[tSei] SELECT FROM
                            basic => IF t.code = code THEN RETURN[tSei];
                            ENDCASE};
                    ENDLOOP;
            ERROR};


        CopyNonCtxSe: PROC[iSei: CSEIndex, mdi: MDIndex] RETURNS[sei: CSEIndex] = {
            tSei1, tSei2: SEIndex;
            WITH  iType: iSeb[iSei] SELECT FROM
                enumerated => {
                    tCtx: CTXIndex;
                    sei _ MakeNonCtxSe[SERecord.cons.enumerated.SIZE];
                    tCtx _ IF iType.valueCtx IN StandardContext
                        THEN iType.valueCtx
                        ELSE CopyIncludedValues[iType.unpainted, iType.valueCtx, mdi, sei];
                    seb[sei].typeInfo _ enumerated[
                        ordered: iType.ordered,
                        machineDep: iType.machineDep,
                        unpainted: iType.unpainted,
                        sparse: iType.sparse,
                        valueCtx: tCtx,
                        empty: iType.empty,
                        nValues: iType.nValues];
                    CacheType[mdi, iSei, sei]};
                record => {
                    tCtx: CTXIndex = IF iType.fieldCtx IN StandardContext
                        THEN iType.fieldCtx
                        ELSE MapCtx[mdi, iType.fieldCtx];
                    WITH iType SELECT FROM
                        notLinked => {
                            sei _ MakeNonCtxSe[SERecord.cons.record.notLinked.SIZE];
                            CacheType[mdi, iSei, sei];
                            seb[sei].typeInfo _ record[
                                machineDep: iType.machineDep,
                                painted: iType.painted,
                                argument: iType.argument,
                                hints: iType.hints,
                                fieldCtx: tCtx,
                                length: iType.length,
                                monitored: iType.monitored,
                                linkPart: notLinked[]]};
                        linked => {
                            sei _ MakeNonCtxSe[SERecord.cons.record.linked.SIZE];
                            CacheType[mdi, iSei, sei];
                            tSei1 _ CopyIncludedSymbol[linkType, mdi];
                            seb[sei].typeInfo _ record[
                                machineDep: iType.machineDep,
                                painted: iType.painted,
                                argument: iType.argument,
                                hints: iType.hints,
                                fieldCtx: tCtx,
                                length: iType.length,
                                monitored: iType.monitored,
                                linkPart: linked[linkType: tSei1]]};
                        ENDCASE;
                    IF ~iType.painted OR (iType.hints.refField AND iType.hints.unifield) THEN
                        CopyContext[tCtx, iType.fieldCtx, mdi, unit]};
                ref => {
                    sei _ MakeNonCtxSe[SERecord.cons.ref.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.refType, mdi];
                    seb[sei].typeInfo _ ref[
                        refType: tSei1,
                        counted: iType.counted,
                        var: iType.var,
                        readOnly: iType.readOnly,
                        ordered: iType.ordered,
                        list: iType.list,
                        basing: iType.basing]};
                array => {
                    sei _ MakeNonCtxSe[SERecord.cons.array.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.indexType, mdi];
                    tSei2 _ CopyIncludedSymbol[iType.componentType, mdi];
                    seb[sei].typeInfo _ array[
                        packed: iType.packed,
                        indexType: tSei1,
                        componentType: tSei2]};
                arraydesc => {
                    sei _ MakeNonCtxSe[SERecord.cons.arraydesc.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.describedType, mdi];
                    seb[sei].typeInfo _ arraydesc[
                        readOnly: iType.readOnly, var: iType.var,
                        describedType: tSei1]};
                transfer => {    -- do not use cache (in case of importing)
                    argSei1, argSei2: CSEIndex;
                    sei _ MakeNonCtxSe[SERecord.cons.transfer.SIZE];
                    argSei1 _ CopyArgs[iType.typeIn, mdi, FALSE];
                    argSei2 _ CopyArgs[iType.typeOut, mdi, FALSE];
                    seb[sei].typeInfo _ transfer[
                        mode: iType.mode, safe: iType.safe,
                        typeIn: argSei1, typeOut: argSei2]};
                definition => {
                    sei _ MakeNonCtxSe[SERecord.cons.definition.SIZE];
                    seb[sei].typeInfo _ definition[
                        nDummyGfi: iType.nDummyGfi,
                        named: iType.named,
                        defCtx: MapCtx[mdi, iType.defCtx]]};
                union => {
                    tag: ISEIndex;
                    tCtx: CTXIndex;
                    sei _ MakeNonCtxSe[SERecord.cons.union.SIZE];
                    CacheType[mdi, iSei, sei];
                    tCtx _ MapCtx[mdi, iType.caseCtx];
                    tag _ CopyCtxSe[iType.tagSei, MapHti[iSeb[iType.tagSei].hash], CTXNull, mdi];
                    seb[sei].typeInfo _ union[
                        caseCtx: tCtx,
                        machineDep: iType.machineDep,
                        overlaid: iType.overlaid,
                        controlled: iType.controlled,
                        tagSei: tag,
                        hints: iType.hints]};
                sequence => {
                    tag: ISEIndex;
                    sei _ MakeNonCtxSe[SERecord.cons.sequence.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.componentType, mdi];
                    tag _ CopyCtxSe[iType.tagSei, MapHti[iSeb[iType.tagSei].hash], CTXNull, mdi];
                    seb[sei].typeInfo _ sequence[
                        packed: iType.packed,
                        controlled: iType.controlled,
                        machineDep: iType.machineDep,
                        tagSei: tag,
                        componentType: tSei1]};
                relative => {
                    tSei3: SEIndex;
                    sei _ MakeNonCtxSe[SERecord.cons.relative.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.baseType, mdi];
                    tSei2 _ CopyIncludedSymbol[iType.offsetType, mdi];
                    tSei3 _ IF iType.resultType = iType.offsetType
                        THEN tSei2
                        ELSE CopyIncludedSymbol[iType.resultType, mdi];
                    seb[sei].typeInfo _ relative[
                        baseType: tSei1,
                        offsetType: tSei2,
                        resultType: tSei3]};
                opaque => {
                    sei _ MakeNonCtxSe[SERecord.cons.opaque.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.id, mdi];
                    seb[sei].typeInfo _ opaque[
                        lengthKnown: iType.lengthKnown,
                        length: iType.length,
                        id: LOOPHOLE[tSei1]]};
                zone => {
                    sei _ MakeNonCtxSe[SERecord.cons.zone.SIZE];
                    seb[sei].typeInfo _ zone[mds: iType.mds, counted: iType.counted];
                    CacheType[mdi, iSei, sei]};
                subrange => {
                    sei _ MakeNonCtxSe[SERecord.cons.subrange.SIZE];
                    tSei1 _ CopyIncludedSymbol[iType.rangeType, mdi];
                    seb[sei].typeInfo _ subrange[
                        filled: iType.filled,
                        empty: iType.empty,
                        rangeType: tSei1,
                        origin: iType.origin,
                        range: iType.range];
                    CacheType[mdi, iSei, sei]};
                long => {
                    sei _ MakeNonCtxSe[SERecord.cons.long.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.rangeType, mdi];
                    seb[sei].typeInfo _ long[rangeType: tSei1]};
                real => {
                    sei _ MakeNonCtxSe[SERecord.cons.real.SIZE];
                    CacheType[mdi, iSei, sei];
                    tSei1 _ CopyIncludedSymbol[iType.rangeType, mdi];
                    seb[sei].typeInfo _ real[rangeType: tSei1]};
                any => {
                    sei _ MakeNonCtxSe[SERecord.cons.any.SIZE];  seb[sei].typeInfo _ any[];
                    CacheType[mdi, iSei, sei]};
                ENDCASE => ERROR;
            seb[sei].mark3 _ seb[sei].mark4 _ TRUE;  RETURN};


        CopyBodyType: PROC[iSei: CSEIndex, mdi: MDIndex] RETURNS[sei: CSEIndex] = {
            argSei1, argSei2: CSEIndex;
            WITH iType: iSeb[iSei] SELECT FROM
                transfer => {
                    sei _ MakeNonCtxSe[SERecord.cons.transfer.SIZE];
                    argSei1 _ CopyArgs[iType.typeIn, mdi, TRUE];
                    argSei2 _ CopyArgs[iType.typeOut, mdi, TRUE];
                    seb[sei].typeInfo _ transfer[
                        mode: iType.mode, safe: iType.safe,
                        typeIn: argSei1, typeOut: argSei2]};
                ENDCASE => ERROR;
            seb[sei].mark3 _ seb[sei].mark4 _ TRUE;  RETURN};

        CopyArgs: PROC[iargSei: CSEIndex, mdi: MDIndex, mapped: BOOL]
            RETURNS [argSei: CSEIndex] = {
            IF iargSei = CSENull THEN argSei _ CSENull
            ELSE 
                WITH t: iSeb[iargSei] SELECT FROM
                    record => {
                        iCtx: CTXIndex = t.fieldCtx;
                        ctx: CTXIndex;
                        argSei _ MakeNonCtxSe[SERecord.cons.record.notLinked.SIZE];
                        IF ~mapped THEN ctx _ NewCtx[iBase.CtxLevel[iCtx]]
                        ELSE {
                            tCtx: IncludedCTXIndex = MapCtx[mdi, iCtx];
                            ctxb[tCtx].complete _ TRUE; ResetCtx[tCtx]; ctx _ tCtx};
                        IF ctxb[ctx].seList = ISENull THEN {
                            seChain: ISEIndex _ MakeSeChain[ctx, iBase.CtxEntries[iCtx], FALSE];
                            sei, iSei: ISEIndex;
                            ctxb[ctx].seList _ seChain;
                            FOR iSei _ iCtxb[iCtx].seList, iBase.NextSe[iSei] UNTIL iSei = ISENull DO
                                sei _ seChain;  seChain _ NextSe[seChain];
                                seb[sei].hash _ MapHti[iSeb[iSei].hash]; CopyCtxSeInfo[sei, iSei, mdi];
                                ENDLOOP};
                        seb[argSei] _ SERecord[
                            mark3: TRUE, mark4: TRUE,
                            body: cons[
                                record[
                                    machineDep: FALSE,
                                    painted: FALSE, argument: TRUE,
                                    hints: t.hints,
                                    fieldCtx: ctx,
                                    length: t.length,
                                    monitored: FALSE,
                                    linkPart: notLinked[]]]];
                        IF typeCache # NIL THEN {
                            i: TypeCacheIndex = TypeHash[mdi, iargSei];
                            typeCache[i] _ [mdi:mdi, iSei:iargSei, sei:argSei]}};
                    ENDCASE => argSei _ CopyNonCtxSe[iargSei, mdi];
            RETURN};


        CopyIncludedValues: PROC[full: BOOL, iCtx: CTXIndex, mdi: MDIndex, type: SEIndex]
            RETURNS [ctx: IncludedCTXIndex] = {
            iSei, sei, seChain: ISEIndex;
            ctx _ MapCtx[mdi, iCtx];
            iSei _ iCtxb[iCtx].seList;
            IF full OR (iSei # SENull AND iSeb[iSeb[iSei].idType].seTag # id) THEN {
                seChain _ MakeSeChain[ctx, iBase.CtxEntries[iCtx], FALSE];
                ctxb[ctx].seList _ seChain;
                ctxb[ctx].closed _ ctxb[ctx].reset _ TRUE;
                UNTIL iSei = SENull DO
                    sei _ seChain;  seChain _ NextSe[seChain];
                    seb[sei].hash _ MapHti[iSeb[iSei].hash];
                    seb[sei].extended _ seb[sei].linkSpace _ FALSE;
                    seb[sei].immutable _ seb[sei].constant _ TRUE;
                    seb[sei].public _ iSeb[iSei].public;
                    seb[sei].idType _ type;  seb[sei].idInfo _ 0;
                    seb[sei].idValue _ iSeb[iSei].idValue;
                    seb[sei].mark3 _ seb[sei].mark4 _ TRUE;
                    iSei _ iBase.NextSe[iSei];
                    ENDLOOP;
                ctxb[ctx].copied _ full; ctxb[ctx].complete _ TRUE};
            RETURN};


    <<included module accounting>>

        ResetCtx: PROC[ctx: IncludedCTXIndex] = {
            IF ~ctxb[ctx].reset THEN {ResetCtxList[ctx]; ctxb[ctx].closed _ ctxb[ctx].reset _ TRUE}};

        ResetIncludeContexts: PROC = {
            mdi: MDIndex;
            limit: MDIndex = table.Top[mdType];
            ctx: IncludedCTXIndex;
            FOR mdi _ MDIndex.FIRST, mdi + MDRecord.SIZE UNTIL mdi = limit DO
                FOR ctx _ mdb[mdi].ctx, ctxb[ctx].chain UNTIL ctx = CTXNull DO
                    ctxb[ctx].copied _ none;    -- clear bits (**** until bootstrap ****)
                    ResetCtx[ctx] ENDLOOP;
                ENDLOOP;
            ResetCaches[]};


        Outer: PUBLIC PROC[mdi: MDIndex, inner: PROC [SymbolTable.Base]] = {
            IF mdi # MDNull AND OpenIncludedTable[mdi] THEN {
                inner[iBase ! UNWIND => {CloseIncludedTable[]}]; CloseIncludedTable[]}};


        TableRelocated: PUBLIC SIGNAL = CODE;

        OpenIncludedTable: PROC[mdi: MDIndex] RETURNS [success: BOOL] = {
            base: SymbolTable.Base = IF mdi = OwnMdi THEN ownSymbols ELSE Copier.GetSymbolTable[mdi];
            IF success _ (base # NIL) THEN {
                iBase _ base; IF mdi # OwnMdi THEN iBase.notifier _ IRelocNotify; INotify[]};
            RETURN};

        IRelocNotify: PROC[base: SymbolTable.Base] = {
            IF base = iBase THEN {INotify[]; SIGNAL TableRelocated}};

        CloseIncludedTable: PROC = {
            IF iBase # ownSymbols THEN {
                iBase.notifier _ iBase.NullNotifier; Copier.FreeSymbolTable[iBase]};
            iBase _ NIL};

        }.

