-- file UtilsCold.Mesa
-- last modified by Bruce, October 25, 1980  9:00 PM

DIRECTORY
  ComData USING [seAnon, typeBOOL, typeCONDITION, typeLOCK, typeStringBody],
  Copier USING [
    AugmentContext, CompleteContext, CopyUnion, FindExternalCtx, Outer, SEToken,
    TokenSymbol],
  DebugOps USING [BitAddress, Foo, GFHandle, Lengthen, LongCopyWRITE, LongREAD, LongWRITE,
    ReadCodeWord, ShortREAD],
  DI USING [
    AbortWithError, Desc, Foo, GetValue, LongDesc,
    Normalize, Pad, SearchCtxList, TypeForSe],
  DIActions USING [],
  DOutput USING [EOL, Line, Octal, Text],
  DSyms USING [GFHandle, GFrameMdi],
  Dump USING [HtiVal],
  Frames USING [Invalid],
  Gf USING [AddGfi, Check, Links, NewLink, OldLink, Validate],
  DHeap USING [AllocFob],
  Inline USING [LowHalf],
  Lf USING [GF, NoAccessLink, Validate],
  Lookup USING [CopyLiteral, Flavor],
  MachineDefs USING [ControlLink, FieldDescriptor, GFHandle, ProcDesc, WordLength],
  Mopcodes USING [zWFS],
  PrincOps USING [ControlLink],
  State USING [Get],
  Storage USING [Node],
  SymbolOps USING [
    Cardinality, EnterExtension, FindExtension, FirstCtxSe, FnField,
    NextSe, TypeForm, UnderType, WordsForType, XferMode],
  SymbolPack USING [FirstCtxSe, NextSe, seb, XferMode],
  Symbols USING [
    ArraySEIndex, BitAddress, BTIndex, BTNull, CSEIndex, CTXIndex, CTXNull,
    ctxType, ExtensionType, HTIndex, IncludedCTXIndex, IncludedCTXNull, ISEIndex,
    ISENull, MDIndex, MDNull, mdType, RecordSEIndex, SEIndex, SENull, SERecord,
    seType, TransferMode, typeANY, typeTYPE],
  SymbolTable USING [Base, Missing],
  Table USING [AddNotify, Base, DropNotify, Notifier],
  Tree USING [Link, Node, Null, treeType],
  TreeOps USING [FreeTree];

UtilsCold: PROGRAM
    IMPORTS
      com: ComData, Copier, DebugOps, DI, DOutput, DSyms, Dump, Frames,
      Gf, DHeap, Inline, Lf, Lookup, State, Storage, 
      SymbolOps, myBase: SymbolPack, SymbolTable, Table, TreeOps
    EXPORTS DI, DIActions, Lookup
    SHARES Copier = 
  BEGIN OPEN DI, SymbolOps, Symbols;

  NotAProcedure: PUBLIC ERROR [cl: MachineDefs.ControlLink] = CODE;
  NotAnArray: PUBLIC ERROR = CODE;
  NotHere: PUBLIC ERROR = CODE;
  SizeMismatch: PUBLIC ERROR = CODE;
  NotRelocated: PUBLIC SIGNAL RETURNS [LONG POINTER] = CODE;

  CantAssignInDebuggerImage: ERROR = CODE;
  UnexpectedLiteral: ERROR = CODE;
  LiteralProblem: ERROR = CODE;
  OffsetVariableCrossesWordBoundary: ERROR = CODE;
  ConfusedAboutImports: ERROR = CODE;
  NoTree: ERROR = CODE;

-- tables defining the current symbol table

  seb: Table.Base;		-- se table
  mdb: Table.Base;		-- module table
  ctxb: Table.Base;		-- context table
  tb: Table.Base;		-- tree table

  Notify: Table.Notifier =
    BEGIN  -- called whenever the main symbol table is repacked
    seb ← base[seType]; ctxb ← base[ctxType]; mdb ← base[mdType];
    tb ← base[Tree.treeType];
    END;

  entryDepth: CARDINAL ← 0;

  Add: PROCEDURE =
    BEGIN
    IF entryDepth = 0 THEN Table.AddNotify[Notify];
    entryDepth ← entryDepth + 1;
    END;

  Drop: PROCEDURE =
    BEGIN
    IF (entryDepth ← entryDepth-1) = 0 THEN Table.DropNotify[Notify];
    END;

-- finding union and discriminated types

  VariantUnionType: PUBLIC PROC [type: SEIndex] RETURNS [vType: CSEIndex] =
    BEGIN
    rType: CSEIndex;
    Add[];
    rType ← TypeForSe[type];
    vType ← WITH seb[rType] SELECT FROM
      record =>
	IF hints.variant
	  THEN UnderType[TypeForSe[UnionField[LOOPHOLE[rType]]]]
	  ELSE typeANY,
      ENDCASE => typeANY;
    Drop[];
    RETURN
    END;

  SelectVariantType: PUBLIC PROCEDURE [type: SEIndex, tag: HTIndex] RETURNS [sei: ISEIndex] =
    BEGIN
    vType: CSEIndex = VariantUnionType[type];
    Add[];
    WITH seb[vType] SELECT FROM
      union => sei ← SearchCtxList[tag, caseCtx];
      ENDCASE => sei ← ISENull;
    Drop[];
    IF sei = ISENull THEN AbortWithError[unknownVariant, tag];
    RETURN
    END;


-- auxiliary procedures 

  UnionField: PROCEDURE [rSei: RecordSEIndex] RETURNS [ISEIndex] = INLINE
    BEGIN
    sei, root, next: ISEIndex;
    ctx: CTXIndex = seb[rSei].fieldCtx;
    repeated: BOOLEAN;
    IF ctxb[ctx].ctxType = simple
      THEN
	FOR sei ← ctxb[ctx].seList, next UNTIL sei = ISENull
	  DO
	  next ← NextSe[sei];
	  IF next = ISENull THEN RETURN [sei];
	  ENDLOOP
      ELSE
	BEGIN	-- defined elsewhere, UnderType is safe
	repeated ← FALSE;
	  DO
	  sei ← root ← ctxb[ctx].seList;
	    DO
	    IF sei = ISENull THEN EXIT;
	    IF TypeForm[seb[sei].idType] = union THEN RETURN [sei];
	    IF (sei ← NextSe[sei]) = root THEN EXIT;
	    ENDLOOP;
	  IF repeated THEN EXIT;
	  Copier.CopyUnion[seb[rSei].fieldCtx];  repeated ← TRUE;
	  ENDLOOP;
	END;
    RETURN [com.seAnon]
    END;

  MapCtx: PUBLIC PROC [mdi: MDIndex, ctx: CTXIndex]
      RETURNS [ictx: IncludedCTXIndex] =
    BEGIN
    IF mdi = MDNull OR ctx = CTXNull THEN RETURN[IncludedCTXNull];
    Add[];
    FOR ictx ← mdb[mdi].ctx, ctxb[ictx].chain UNTIL ictx = IncludedCTXNull DO
      IF ctxb[ictx].map = ctx THEN EXIT;
      REPEAT FINISHED => ictx ← Copier.FindExternalCtx[mdi,ctx];
      ENDLOOP;
    Drop[];
    END;

  SearchValContext: PROCEDURE [val: UNSPECIFIED, ctx: CTXIndex]
      RETURNS [ISEIndex] =
    BEGIN
    sei: ISEIndex;
    root: ISEIndex ← ctxb[ctx].seList;
    sei ← root;
    DO
      IF sei = SENull THEN EXIT;
      IF seb[sei].idValue = val THEN RETURN [sei];
      WITH id: seb[sei] SELECT FROM
        sequential =>  sei ← sei + SIZE[sequential id SERecord];
        linked =>  IF (sei ← id.link) = root THEN EXIT;
        ENDCASE => EXIT;
      ENDLOOP;
    RETURN [ISENull]
    END;

  SearchCtxForProc: PUBLIC PROC [val: UNSPECIFIED, ctx: CTXIndex]
      RETURNS [sei: ISEIndex] =
    BEGIN RETURN[SearchCtxForVal[val,ctx,proc]] END;

  SearchCtxForSignal: PUBLIC PROC [val: UNSPECIFIED, ctx: CTXIndex]
      RETURNS [sei: ISEIndex] =
    BEGIN RETURN[SearchCtxForVal[val,ctx,signal]] END;

  SearchCtxForVal: PUBLIC PROC [
    val: UNSPECIFIED, ctx: CTXIndex, tm: TransferMode]
      RETURNS [sei: ISEIndex] =
    BEGIN
    IF ctx = CTXNull THEN RETURN [ISENull];
    Add[];
    IF (sei ← SearchValContext[val,ctx]) # ISENull
      AND CheckIsei[myBase,sei,tm] THEN {Drop[]; RETURN};
    sei ← ISENull;
    WITH ctxb[ctx] SELECT FROM
      included => IF ~complete THEN 
	sei ← Copier.TokenSymbol[ctx, Generator[module,map,val,tm]];
      imported => sei ← SearchCtxForVal[val,includeLink,tm];
      simple => NULL;
      ENDCASE;
    Drop[];
    RETURN;
    END;

  Generator: PROC [
    mdi: MDIndex, ctx: CTXIndex, val: UNSPECIFIED, tm: TransferMode]
      RETURNS [token: Copier.SEToken] =
    BEGIN
    Search: PROC [iBase: SymbolTable.Base] =
      BEGIN
      isei: ISEIndex;
      FOR isei ← iBase.FirstCtxSe[ctx], iBase.NextSe[isei] UNTIL
		isei = ISENull DO
	IF (iBase.seb[isei].idValue = val) AND CheckIsei[iBase,isei,tm] THEN
	  EXIT;
	ENDLOOP;
      token ← [isei];
      END;
    Copier.Outer[mdi,Search];
    END;

  CheckIsei: PROC [base: SymbolTable.Base, isei: ISEIndex, tm: TransferMode]
    RETURNS [BOOLEAN] =
    BEGIN
    SELECT base.XferMode[base.seb[isei].idType] FROM
      tm => RETURN[base.seb[isei].constant];
      signal => IF tm = error THEN RETURN[base.seb[isei].constant];
      error => IF tm = signal THEN RETURN[base.seb[isei].constant];
      ENDCASE;
    RETURN[FALSE];
    END;

  ArraySei: PROC [sei: SEIndex] RETURNS [asei: ArraySEIndex, desc, long: BOOLEAN] =
    BEGIN
    csei: CSEIndex ← TypeForSe[sei];
    desc ← long ← FALSE;
    DO
      WITH seb[csei] SELECT FROM
	array => {asei ← LOOPHOLE[csei]; EXIT};
	arraydesc =>
	  BEGIN
	  asei ← LOOPHOLE[SymbolOps.UnderType[describedType]];
	  desc ← TRUE;
	  EXIT;
	  END;
	long => {long ← TRUE; csei ← TypeForSe[rangeType]};
	ENDCASE => NotAnArray;
      ENDLOOP;
    END;

  GetDesc: PUBLIC PROC [f: Foo] RETURNS [d: LongDesc, asei: ArraySEIndex] =
    BEGIN
    desc: BOOLEAN;
    long: BOOLEAN;
    Add[];
    [asei:asei, desc:desc, long:long] ← ArraySei[f.tsei ! UNWIND => Drop[]];
    IF long THEN {Drop[]; ERROR SizeMismatch};
    IF ~desc THEN
      BEGIN
      IF ~f.there THEN ERROR NotHere;
      d.base ← f.addr.base;
      d.length ← SymbolOps.Cardinality[seb[asei].indexType];
      END
    ELSE
      BEGIN
      sd: Desc;
      GetValue[f];
      sd ← LOOPHOLE[f.addr.base, LONG POINTER TO Desc]↑;
      d.base ← DebugOps.Lengthen[sd.base]; d.length ← sd.length;
      END;
    Drop[];
    END;

  GetLongDesc: PUBLIC PROC [f: Foo] RETURNS [ld: LongDesc, asei: ArraySEIndex] =
    BEGIN
    desc: BOOLEAN;
    long: BOOLEAN;
    lp: LONG POINTER TO LongDesc;
    Add[];
    [asei:asei, desc:desc, long:long] ← ArraySei[f.tsei ! UNWIND => Drop[]];
    IF ~long THEN {Drop[]; ERROR SizeMismatch};
    IF ~desc THEN
      BEGIN
      IF ~f.there THEN ERROR NotHere;
      ld.base ← f.addr.base;
      ld.length ← SymbolOps.Cardinality[seb[asei].indexType];
      END
    ELSE
      BEGIN
      GetValue[f];
      lp ← f.addr.base;
      ld ← lp↑;
      END;
    Drop[];
    END;

  WFS: PROC [UNSPECIFIED, POINTER, MachineDefs.FieldDescriptor] =
    MACHINE CODE BEGIN Mopcodes.zWFS END;

  PutValue: PUBLIC PROC [lhs: Foo, from: LONG POINTER] =
    BEGIN
    to: LONG POINTER = lhs.addr.base;
    IF ~lhs.there THEN ERROR CantAssignInDebuggerImage;
    IF lhs.addr.offset # 0 OR lhs.bits # 0 THEN
      BEGIN
      val: UNSPECIFIED ← DebugOps.LongREAD[to];
      fd: MachineDefs.FieldDescriptor ←
	[offset: 0, posn: lhs.addr.offset, size: lhs.bits];
      WFS[from↑,@val,fd];
      DebugOps.LongWRITE[to,val];
      END
    ELSE DebugOps.LongCopyWRITE[from: from, nwords: lhs.words, to: to];
    END;

  GetControlLink: PUBLIC PROC [f: Foo] RETURNS [PrincOps.ControlLink] =
    BEGIN
    IF f.there THEN {GetValue[f]; f.addr.base↑ ← Gf.NewLink[f.addr.base↑]};
    RETURN[f.addr.base↑];
    END;

  DerefProcDesc: PUBLIC PROC [cl: PrincOps.ControlLink]
    RETURNS [PrincOps.ControlLink] =
    BEGIN
    DO
      IF cl.gfi = 0 THEN EXIT;
      SELECT TRUE FROM
  	cl.gfi = 0 => ERROR NotAProcedure[Gf.OldLink[cl]];
	cl.proc => EXIT;
	cl.indirect => cl ← Gf.NewLink[DebugOps.ShortREAD[LOOPHOLE[cl]]];
	ENDCASE => ERROR NotAProcedure[Gf.OldLink[cl]];
      ENDLOOP;
    RETURN[cl]
    END;

  CopyMore: PUBLIC PROCEDURE [
      tsei: Symbols.SEIndex, doVariants: BOOLEAN ← FALSE] =
    BEGIN OPEN Symbols, SymbolOps, com;
    csei: CSEIndex;
    Add[];
    DO
      WITH seb[tsei] SELECT FROM
	id => tsei ← IF idType = typeTYPE THEN UnderType[tsei] ELSE idType;
	cons => BEGIN csei ← LOOPHOLE[tsei]; EXIT END;
	ENDCASE
      ENDLOOP;
    WITH seb[csei] SELECT FROM
      enumerated => IF csei # typeBOOL THEN Complete[valueCtx];
      record =>
	SELECT csei FROM
	  typeStringBody, typeLOCK, typeCONDITION => RETURN;
	  ENDCASE => Complete[fieldCtx, doVariants];
      ENDCASE;
    Drop[];
    END;

  Complete: PUBLIC PROCEDURE [
      ictx: Symbols.CTXIndex, variants: BOOLEAN ← FALSE] =
    BEGIN OPEN Symbols, SymbolOps, Copier;
    CheckUnions: PROCEDURE [isei: ISEIndex] =
      BEGIN
      tsei: SEIndex;
      IF isei = ISENull THEN RETURN;
      tsei ← seb[isei].idType;
      IF tsei = typeTYPE THEN RETURN;
      WITH se: seb[tsei] SELECT FROM
	cons => WITH se SELECT FROM
	  union =>
	    BEGIN
	    Complete[caseCtx];
	    FOR isei ← SymbolOps.FirstCtxSe[caseCtx], SymbolOps.NextSe[isei]
	    UNTIL isei = ISENull DO
	      CopyMore[isei,TRUE];
	      ENDLOOP;
	    END;
	  ENDCASE;
	ENDCASE;
      RETURN
      END;

    Add[];
    WITH ctxb[ictx] SELECT FROM
      included => IF ~complete THEN
	CompleteContext[LOOPHOLE[ictx], TRUE ! UNWIND => Drop[]];
      ENDCASE => {Drop[]; RETURN};
    WITH ctxb[ictx] SELECT FROM
      included =>
	BEGIN
	p: POINTER ← State.Get[].h.interpretContext;
	gf: MachineDefs.GFHandle ← IF Lf.Validate[p] THEN Lf.GF[p] ELSE p;
	mdi: MDIndex;
	mdi ← DSyms.GFrameMdi[gf ! SymbolTable.Missing => GOTO bailout];
	IF ~complete THEN
	  AugmentContext[LOOPHOLE[ictx], TRUE, mdi ! UNWIND => Drop[]];
--	IF ~reset AND ~complete THEN {ResetCtxList[ictx]; closed ← reset ← TRUE};
	EXITS
	  bailout => NULL;
	END;
      ENDCASE;
    IF variants THEN
      BEGIN
      isei, root: ISEIndex;
      isei ← root ← ctxb[ictx].seList;
      DO
	IF isei = SENull THEN EXIT;
	  CheckUnions[isei];
	WITH id: seb[isei] SELECT FROM
	  sequential => isei ← isei + SIZE[sequential id SERecord];
	  linked =>  IF (isei ← id.link) = root THEN EXIT;
	  ENDCASE => EXIT;
	ENDLOOP;
      END;
    Drop[];
    END;

  AddDesc: PROCEDURE [f: Foo, cl: PrincOps.ControlLink] =
    BEGIN
    p: POINTER TO PrincOps.ControlLink;
    p ← Storage.Node[1];
    p↑ ← cl;
    f.addr.base ← p;
    f.words ← 1;
    END;

  Constant: PROCEDURE [f: Foo, isei: Symbols.ISEIndex] =
    BEGIN OPEN seb[isei];
    val: POINTER;
    length: CARDINAL ← 1;
    IF ~extended THEN
      BEGIN
      val ← Storage.Node[length];
      LOOPHOLE[val, POINTER TO CARDINAL]↑ ← idValue;
      END
    ELSE
      BEGIN
      tree: Tree.Link;
      type: Symbols.ExtensionType;
      [type, tree] ← SymbolOps.FindExtension[isei];
      IF type # value THEN ERROR UnexpectedLiteral;
      IF tree = Tree.Null THEN {MakeFooError[noTree,hash]; ERROR NoTree};
      WITH tree SELECT FROM
	subtree =>
	  BEGIN
	  IF tb[index].name # mwconst THEN ERROR LiteralProblem;
	  tree ← tb[index].son[1];
	  END;
	ENDCASE => ERROR UnexpectedLiteral;
      WITH tree SELECT FROM
	literal => [val,length] ← Lookup.CopyLiteral[info];
	ENDCASE => ERROR LiteralProblem;
      END;
    f.addr.base ← val;
    f.words ← length;
    END;

  Mode: PUBLIC PROCEDURE [isei: Symbols.ISEIndex] RETURNS [Lookup.Flavor] =
    BEGIN
    tsei: Symbols.SEIndex ← seb[isei].idType;
    xfer: BOOLEAN ← SymbolOps.XferMode[tsei] # none;
    constant: BOOLEAN ← seb[isei].constant;
    linkspace: BOOLEAN ← seb[isei].linkSpace;
    SELECT TRUE FROM
      xfer AND constant => 
	BEGIN
	bti: Symbols.BTIndex ← seb[isei].idInfo;
	IF seb[isei].extended THEN RETURN[inline];
	IF bti = Symbols.BTNull THEN RETURN[controlLink]
	ELSE RETURN[unrelocatedControlLink];
	END;
      xfer AND ~constant =>
	IF linkspace THEN RETURN[refProc] ELSE RETURN[val];
      constant => RETURN[manifest];
      linkspace => RETURN[refVal];
      ENDCASE => RETURN[val];
    END;

  MakeFooError: PROCEDURE [err: {links, unbound, noTree}, u: UNSPECIFIED] =
    BEGIN OPEN DOutput;
    SELECT err FROM
      links => {Text[" !Can't find links from frame: "L]; Octal[u]; EOL[]};
      unbound => {EOL[]; Dump.HtiVal[u]; Line[" is unbound!"L]};
      noTree => {Text[" ! Tree for "L]; Dump.HtiVal[u]; Line[" not in symbol table."L]};
      ENDCASE;
    END;

  GetLink: PROC [gf: MachineDefs.GFHandle, bits: CARDINAL, proc: BOOLEAN]
    RETURNS [UNSPECIFIED] =
    BEGIN OPEN DebugOps;
    offset: INTEGER ← bits/MachineDefs.WordLength + 1;
    cl: MachineDefs.ProcDesc;
    IF ~Gf.Validate[gf] THEN gf ← Lf.GF[LOOPHOLE[gf]];
    IF Gf.Links[gf] THEN cl ← ReadCodeWord[gf, -offset]
      ELSE cl ← ShortREAD[gf-offset];
    IF proc THEN RETURN[Gf.NewLink[cl]] ELSE RETURN[cl];
    END;

  FrameAddress: PROC [
      ba: DebugOps.BitAddress, isei: Symbols.ISEIndex, sizeInc: CARDINAL] 
    RETURNS [addr: DebugOps.BitAddress, size: CARDINAL] =
    BEGIN
    delta: CARDINAL;
    addr.useStack ← FALSE;
    addr.local ← ba.local;
    [delta, addr.offset] ← Normalize[seb[isei].idValue+ba.offset];
    addr.base ← ba.base+delta;
    size ← seb[isei].idInfo+sizeInc;
    END;

  StackAddress: PROC [ba: DebugOps.BitAddress, isei: Symbols.ISEIndex]
    RETURNS [addr: DebugOps.BitAddress, size: CARDINAL] =
    BEGIN
    delta: CARDINAL;
    offset: Symbols.BitAddress;
    addr.useStack ← TRUE;
    addr.local ← ba.local;
    [offset,size] ← SymbolOps.FnField[isei];
    [delta, addr.offset] ← Normalize[offset.bd+ba.offset];
    addr.base ← ba.base+delta+offset.wd;
    END;

  CheckForHiddenPadding: PROC [f: Foo] = INLINE
    BEGIN
    csei: Symbols.CSEIndex = DI.TypeForSe[f.tsei];
    WITH seb[csei] SELECT FROM
      array => f.addr.offset ← DI.Pad[f, LOOPHOLE[csei]];
      ENDCASE;
    END;

  FixupFrame: PROC [p: POINTER] RETURNS [gf: MachineDefs.GFHandle] =
    BEGIN
    gf ← IF Lf.Validate[p] THEN Lf.GF[p] ELSE p;
    Gf.Check[gf];
    END;

  MakeFoo: PUBLIC PROCEDURE [isei: Symbols.ISEIndex,
      ba: DebugOps.BitAddress ← [NIL, 0, FALSE], sizeInc: CARDINAL ← 0]
      RETURNS [f: Foo] =
    BEGIN OPEN DebugOps, MachineDefs, Symbols, seb[isei];
    flavor: Lookup.Flavor;
    gf: GFHandle;
    IF isei = ISENull THEN RETURN[NIL];
    Add[];
    f ← DHeap.AllocFob[]; --  initialized to all zero
    f.hti ← hash;
    IF idType = Symbols.typeTYPE THEN
      BEGIN f.typeOnly ← TRUE; f.tsei ← isei; Drop[]; RETURN END;
    f.tsei ← idType;
    SELECT (flavor ← Mode[isei]) FROM
      manifest, inline, controlLink => NULL;
      ENDCASE => IF ~ba.useStack THEN
	BEGIN ENABLE UNWIND => Drop[];
	IF ba.base = NIL THEN ba.base ← SIGNAL NotRelocated;
	IF flavor # val THEN gf ← FixupFrame[Inline.LowHalf[ba.base]];
	END;
    SELECT flavor FROM
      manifest => Constant[f,isei ! NoTree => {f ← NIL; CONTINUE}];
      val =>
	BEGIN
	size: CARDINAL;
	f.there ← TRUE;
	IF ba.useStack THEN [f.addr, size] ← StackAddress[ba,isei]
	ELSE [f.addr, size] ← FrameAddress[ba, isei, sizeInc];
	[f.words, f.bits] ← Normalize[size];
	IF f.bits + f.addr.offset > MachineDefs.WordLength THEN
	  ERROR OffsetVariableCrossesWordBoundary;
	CheckForHiddenPadding[f];
	END;
      refProc =>
	BEGIN
	cl: PrincOps.ControlLink ← GetLink[gf, idValue, TRUE
	  !Frames.Invalid,Lf.NoAccessLink=>{MakeFooError[links,gf];GOTO bad}];
	AddDesc[f, cl];
	EXITS
	  bad => BEGIN Drop[]; RETURN[NIL] END;
	END;
      refVal =>
	BEGIN
	p: POINTER ← GetLink[gf, idValue, FALSE ! Frames.Invalid, Lf.NoAccessLink =>
	  BEGIN MakeFooError[links,gf]; GOTO nolinks END];
	IF p = NIL OR p = LOOPHOLE[1] THEN {MakeFooError[unbound, f.hti]; GOTO nolinks};
	WITH seb[SymbolOps.UnderType[idType]] SELECT FROM
	  ref => f.tsei ← DI.TypeForSe[refType];
	  ENDCASE => ERROR ConfusedAboutImports;
	f.there ← TRUE;
	f.addr.base ← Lengthen[p];
	WITH seb[f.tsei] SELECT FROM
	  id => [f.words, f.bits] ← Normalize[idInfo];
	  ENDCASE => f.words ← SymbolOps.WordsForType[f.tsei];
	EXITS
	  nolinks => BEGIN Drop[]; RETURN[NIL] END;
	END;
      inline =>
	BEGIN
	tree: Tree.Link;
	type: Symbols.ExtensionType;
	[type, tree] ← SymbolOps.FindExtension[isei];
	SymbolOps.EnterExtension[isei,type,TreeOps.FreeTree[tree]];
	DOutput.Line[" !Inline"L];
	Drop[];
	RETURN[NIL]
	END;
      controlLink => AddDesc[f, idValue];
      unrelocatedControlLink => AddDesc[f, Gf.AddGfi[gf, idValue]];
      ENDCASE;
    Drop[];
    END;

  END.