-- LookupPack.mesa  last edit, Johnsson  August 8, 1980  6:42 PM
--				Bruce   October 9, 1980  3:33 PM

DIRECTORY
  Actions USING [],
  Ascii USING [SP],
  ComData,
  Copier USING [Outer],
  DContext USING [Enumerate, GetModule],
  DebugFormat USING [BBHandle, BitAddress, EXOI, Foo],
  DebugOps USING [BitAddress, Display, Foo, Lengthen, ShortREAD, UserAborted],
  DHeap USING [AllocFob],
  DI USING [
    CSEIndex, CTXIndex, HTIndex, ISEIndex, Normalize, SearchCtxForVal, SearchCtxList,
    SEIndex, TypeForSe],
  DOutput USING [Char, Text],
  DSyms USING [FindFrame, GFHandle, GFrameMdi, HTIndex, Item, MDIndex, ModuleMdi],
  Frames USING [Invalid],
  Gf USING [CheckStarted, DisplayInMsg, FrameGfi, FrameHti, GFI, Validate],
  Init USING [CheckSymTabLength],
  Lf USING [GF, NoAccessLink, NoPrevious, PC, Previous, Validate],
  Lookup USING [Complete, HTIndex, InOut, MakeFoo, Mode, NotRelocated, StringToHti],
  MachineDefs USING [FHandle, GFHandle, GFTIndex, MaxParmsInStack, SVPointer],
  Pc USING [ContextList, CtxLink, Entry, Exit, FreeContextList],
  PrincOps USING [localbase, SignalDesc],
  State USING [Get, GetGS, GSHandle, Handle],
  Strings USING [SubStringDescriptor],
  SymbolOps USING [FirstCtxSe, NextSe, SubStringForHash],
  Symbols USING [
    ContextLevel, CSEIndex, CTXIndex, CTXNull, ctxType, HTIndex, HTNull,
    IncludedCTXIndex, IncludedCTXNull, ISEIndex, ISENull, lG, MDIndex, MDNull,
    mdType, RecordSEIndex, RecordSENull, SEIndex, SENull, seType, TransferMode],
  SymbolTable USING [Base, Missing],
  Table USING [AddNotify, Base, DropNotify, Notifier],
  UserInput USING [userAbort];

LookupPack: PROGRAM
  IMPORTS com: ComData, DContext, Copier, DebugOps, DHeap, DI, DOutput,
    DSyms, Frames, Gf, Init, Lf, Lookup, Pc, State, SymbolOps,
    SymbolTable, Table, UserInput
  EXPORTS Actions, Lookup =
  BEGIN OPEN Lookup, Symbols;

  Foo: TYPE = DebugOps.Foo;
  GFHandle: TYPE = MachineDefs.GFHandle;
  FHandle: TYPE = MachineDefs.FHandle;
  HTIndex: TYPE = Symbols.HTIndex;

  Fail: PUBLIC SIGNAL [s: STRING] = CODE;
  NotAnXfer: ERROR = CODE;
  SearchingWrongContext: ERROR = CODE;

  data: State.GSHandle ← State.GetGS[];

  seb: Table.Base;
  ctxb: Table.Base;
  mdb: Table.Base;
  notifyCnt: CARDINAL ← 0;

  Notify: Table.Notifier =
    BEGIN OPEN Symbols;
    seb ← base[seType]; ctxb ← base[ctxType]; mdb ← base[mdType];
    END;

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

  Drop: PROCEDURE =
    BEGIN
    IF notifyCnt = 1 THEN Table.DropNotify[Notify];
    notifyCnt ← notifyCnt - 1;
    END;

  InCtx: PUBLIC PROCEDURE [id: HTIndex, ctx: CTXIndex] RETURNS [Foo] =
    BEGIN
    isei: Symbols.ISEIndex ← DI.SearchCtxList[id,ctx];
    RETURN [IF isei = ISENull THEN NIL ELSE MakeFoo[isei]];
    END;

  InGF: PUBLIC PROCEDURE [
    id: HTIndex, gf: GFHandle ← NIL, check: BOOLEAN ← TRUE]
    RETURNS [Foo] =
    BEGIN
    mdi: Symbols.MDIndex;
    p: POINTER;
    IF gf = NIL THEN
      BEGIN
      p ← State.Get[].h.interpretContext;
      gf ← IF ~Gf.Validate[p] THEN Lf.GF[p] ELSE p;
      END;
    mdi ← DSyms.GFrameMdi[gf ! SymbolTable.Missing => GOTO bailout];
    RETURN[GFFoo[id, gf, mdi, check]];
    EXITS
      bailout => RETURN[NIL]
    END;

  InMod: PUBLIC PROC [id: HTIndex, mod: HTIndex ← Symbols.HTNull] RETURNS [Foo] =
    BEGIN
    gf: GFHandle ← NIL;
    mdi: Symbols.MDIndex;
    IF mod = Symbols.HTNull THEN mod ← StringToHti[DContext.GetModule[]];
    gf ← Gf.FrameHti[mod ! Fail => CONTINUE];
    mdi ← DSyms.ModuleMdi[mod ! SymbolTable.Missing => GOTO bailout];
    RETURN[GFFoo[id, gf, mdi, TRUE ! Lookup.NotRelocated => {
      DOutput.Text[" Use Interface.importedVariable, not Interface$importedVariable"L];
      GOTO bailout}]];
    EXITS
      bailout => RETURN[NIL]
    END;

  InLF: PUBLIC PROCEDURE [
    id: HTIndex, lf: FHandle ← NIL, check: BOOLEAN ← TRUE]
    RETURNS [f: Foo] =
    BEGIN
    gf: GFHandle ← NIL;
    ex: DebugFormat.EXOI;
    list: Pc.CtxLink;
    IF lf = NIL THEN lf ← State.Get[].h.interpretContext;
    IF Gf.Validate[LOOPHOLE[lf]] THEN RETURN[InGF[id,LOOPHOLE[lf],check]];
    SELECT TRUE FROM
      Pc.Entry[lf ! SymbolTable.Missing => GOTO bailout] => ex ← entry;
      Pc.Exit[lf] => ex ← exit;
      ENDCASE => ex ← in;
    gf ← Lf.GF[lf ! Frames.Invalid, Lf.NoAccessLink => CONTINUE];
    IF (f ← TryCtx[id,[DebugOps.Lengthen[lf],0],LOOPHOLE[2]]) # NIL THEN RETURN;
    list ← Pc.ContextList[Lf.PC[lf],gf,search,ex];
    IF list = NIL THEN RETURN[InGF[id,gf,check]];
    Add[];
    f ← SearchList[id,list,lf,gf ! UNWIND => {Pc.FreeContextList[list]; Drop[]}];
    Pc.FreeContextList[list];
    Drop[];
    IF f = NIL THEN RETURN[InGF[id,gf,check]];
    EXITS
      bailout => RETURN[NIL]
    END;

  SearchList: PROC [id: HTIndex, list: Pc.CtxLink, lf: FHandle, gf: GFHandle]
    RETURNS [f: Foo] =
    BEGIN
    ba: DebugOps.BitAddress ← [DebugOps.Lengthen[lf],0];
    level: ContextLevel;
    f ← NIL;
    list ← FixupList[list];
    level ← GetLevel[list];
    IF level = lG THEN ba.base ← DebugOps.Lengthen[gf];
    FOR i: Pc.CtxLink ← list, i.link UNTIL i = NIL DO
      ba.useStack ← i.onStack;
      ba.local ← level # lG;
      SELECT GetLevel[i] FROM
	= level => NULL;
	< level => {
	  level ← GetLevel[i];
	  IF level = lG THEN ba.base ← DebugOps.Lengthen[gf]
	  ELSE {
	    lf ← DebugOps.ShortREAD[@lf.local[0]] - PrincOps.localbase;
	    ba.base ← DebugOps.Lengthen[lf]}};
	ENDCASE => ERROR SearchingWrongContext;
      IF (f ← TryProcCtx[id,ba,i]) # NIL THEN EXIT;
      ENDLOOP;
    END;

  FixupList: PROC [list: Pc.CtxLink] RETURNS [Pc.CtxLink] =
    BEGIN
    twoBefore, last: Pc.CtxLink;
    outOfOrder: BOOLEAN ← TRUE;
    WHILE outOfOrder DO
      twoBefore ← last ← NIL;
      outOfOrder ← FALSE;
      FOR i: Pc.CtxLink ← list, i.link UNTIL i = NIL DO
	IF last # NIL AND GetLevel[i] > GetLevel[last] THEN 
	  BEGIN
	  outOfOrder ← TRUE;
	  last.link ← i.link; i.link ← last;
	  IF twoBefore = NIL THEN list ← i ELSE twoBefore.link ← i;
	  EXIT;
	  END;
	twoBefore ← last;
	last ← i;
	ENDLOOP;
      ENDLOOP;
    RETURN[list];
    END;

  GetLevel: PROC [i: Pc.CtxLink] RETURNS [ContextLevel] = {
    RETURN[IF i.null THEN i.level ELSE ctxb[i.ictx].level]};

  OnStack: PUBLIC PROC [
    id: HTIndex, lf: FHandle ← NIL, check: BOOLEAN ← TRUE]
    RETURNS [f: Foo] =
    BEGIN
    h: State.Handle = State.Get[];
    IF h.howSet = break THEN RETURN[InBreakBlock[id,h.interpretContext]];
    IF lf = NIL THEN lf ← h.interpretContext;
    IF data.search THEN RETURN[InLF[id,lf,check]];
    IF Gf.Validate[LOOPHOLE[lf]] THEN RETURN[InGF[id,LOOPHOLE[lf],check]];
    f ← NIL;
    DO
      IF lf = NIL THEN EXIT;
      IF (f←InLF[id,lf,check !SymbolTable.Missing => CONTINUE])#NIL THEN EXIT;
      lf ← Lf.Previous[lf ! Lf.NoPrevious => EXIT];
      ENDLOOP;
    END;

  LocalFrame: PROC [cl: PrincOps.SignalDesc, tm: Symbols.TransferMode]
    RETURNS [isei: Symbols.ISEIndex]=
    BEGIN
    lf: FHandle ← State.Get[].h.interpretContext;
    gf: GFHandle ← NIL;
    list: Pc.CtxLink;
    isei ← Symbols.ISENull;
    IF lf = NIL THEN lf ← DebugOps.ShortREAD[@data.StatePtr.dest];
    IF ~Lf.Validate[lf] THEN RETURN;
    gf ← Lf.GF[lf ! Frames.Invalid, Lf.NoAccessLink => CONTINUE];
    list ← Pc.ContextList[Lf.PC[lf],gf,search,in
      ! SymbolTable.Missing => GOTO bailout];
    IF list = NIL THEN RETURN;
    isei ← SearchListForValue[cl,tm,list,lf
	! UNWIND => Pc.FreeContextList[list]];
    Pc.FreeContextList[list];
    EXITS
      bailout => RETURN[Symbols.ISENull]
    END;

  SearchListForValue: PROC [
    cl: PrincOps.SignalDesc, tm: Symbols.TransferMode, list: Pc.CtxLink,
    lf: FHandle]
    RETURNS [isei: Symbols.ISEIndex] =
    BEGIN
    i: Pc.CtxLink;
    isei ← Symbols.ISENull;
    FOR i ← list, i.link UNTIL i = NIL DO
      IF i.null OR i.ictx = Symbols.IncludedCTXNull THEN LOOP;
      IF (isei ← DI.SearchCtxForVal[cl,i.ictx,tm]) # Symbols.ISENull THEN EXIT;
      ENDLOOP;
    END;

  InBreakBlock: PUBLIC PROC [id: HTIndex, bb: DebugFormat.BBHandle] RETURNS [f: Foo] =
    BEGIN
    list: Pc.CtxLink;
    list ← Pc.ContextList[bb.pc,bb.gf,search,bb.bt.ex];
    IF list # NIL THEN {
      Add[];
      f ← SearchList[id,list,NIL,bb.gf ! 
	Lookup.NotRelocated => RESUME[NIL];
	UNWIND => {Pc.FreeContextList[list]; Drop[]}];
      Pc.FreeContextList[list];
      Drop[];
      IF f # NIL THEN RETURN};
    RETURN[InGF[id,bb.gf,FALSE]];
    END;

  Signal: PUBLIC PROC [cl: PrincOps.SignalDesc]
      RETURNS [isei: Symbols.ISEIndex] =
    BEGIN RETURN[Xfer[cl,signal]]  END;

  Proc: PUBLIC PROC [cl: PrincOps.SignalDesc]
      RETURNS [isei: Symbols.ISEIndex] =
    BEGIN RETURN[Xfer[cl,proc]]  END;

  Xfer: PROC [cl: PrincOps.SignalDesc, tm: Symbols.TransferMode]
      RETURNS [isei: Symbols.ISEIndex] =
    BEGIN OPEN Symbols;
    gf: GFHandle = Gf.FrameGfi[cl.gfi];
    gfi: MachineDefs.GFTIndex;
    syms: DSyms.Item;
    IF gf = NIL THEN RETURN[ISENull];
    gfi ← Gf.GFI[gf] - 1;
    syms ← DSyms.FindFrame[gf];
    IF syms = NIL THEN {
      [] ← DSyms.GFrameMdi[gf ! SymbolTable.Missing => GOTO bailout];
      syms ← DSyms.FindFrame[gf]};
    cl.gfi ← cl.gfi - gfi;
    SELECT TRUE FROM
      (isei ← DI.SearchCtxForVal[cl,syms.outerCtx,tm]) # ISENull => NULL;
      (isei ← DI.SearchCtxForVal[cl,syms.importCtx,tm]) # ISENull => NULL;
      (isei ← DI.SearchCtxForVal[cl,syms.directoryCtx,tm]) # ISENull => NULL;
      (isei ← LocalFrame[cl,tm]) # ISENull => NULL;
      ENDCASE => RETURN[Symbols.ISENull];
    EXITS
      bailout => RETURN[Symbols.ISENull]
    END;

  XferCtx: PUBLIC PROC [sei: SEIndex, base: LONG POINTER, io: InOut]
      RETURNS [f: Foo] =
    BEGIN
    IF sei = SENull THEN RETURN[NIL];
    f ← DHeap.AllocFob[]; --  initialized to all zero
    f.xfer ← TRUE;
    f.there ← TRUE;
    f.addr.base ← base;
    Add[];
    WITH seb[DI.TypeForSe[sei]] SELECT FROM
      transfer =>
	BEGIN
	rsei: Symbols.RecordSEIndex ← IF io = in THEN inRecord ELSE outRecord;
	IF rsei = Symbols.SENull THEN {f.addr.base ← NIL; f.tsei ← Symbols.RecordSENull}
	ELSE
	  BEGIN
	  f.tsei ← rsei;
	  [f.words, f.bits] ← DI.Normalize[seb[rsei].length];
	  END;
	END;
      ENDCASE => {Drop[]; ERROR NotAnXfer};
    Drop[];
    END;

  StateCtx: PUBLIC PROCEDURE [
    sei: SEIndex, sv: MachineDefs.SVPointer, io: InOut,
    max: CARDINAL ← MachineDefs.MaxParmsInStack]
      RETURNS [f: Foo] =
    BEGIN
    locals: POINTER;
    f ← XferCtx[sei,NIL,io];
    IF f = NIL OR f.tsei = Symbols.RecordSENull THEN RETURN;
    locals ← 
      IF f.words > max THEN DebugOps.ShortREAD[@sv.stk[0]] ELSE @sv.stk[0];
    f.addr.base ← DebugOps.Lengthen[locals];
    f.addr.useStack ← TRUE;
    END;

  GFFoo: PROC [hti: HTIndex, gf: GFHandle, mdi: MDIndex, check: BOOLEAN]
    RETURNS [f: Foo] =
    BEGIN OPEN SymbolOps;
    ba: DebugOps.BitAddress;
    syms: DSyms.Item = DSyms.FindFrame[gf];
    IF gf # NIL AND check THEN [] ← Gf.CheckStarted[gf];
    ba ← [base: DebugOps.Lengthen[gf], offset: 0, local: FALSE];
    IF (f ← TryCtx[hti,ba,LOOPHOLE[2]]) # NIL THEN RETURN;
    IF (f ← TryCtx[hti,ba,syms.outerCtx]) # NIL THEN RETURN;
    IF (f ← SearchImportCtx[hti,ba,syms.importCtx]) # NIL THEN RETURN;
    f ← SearchDirectoryCtx[hti,ba,syms.directoryCtx,mdi];
    END;

  SearchDirectoryCtx: PROC [
      hti: HTIndex, ba: DebugOps.BitAddress, ictx: Symbols.CTXIndex,
      mdi: MDIndex]
    RETURNS [f: Foo] =
    BEGIN 
    mdRoot: MDIndex;
    target: CTXIndex;
    sei, isei: Symbols.ISEIndex;
    ss: Strings.SubStringDescriptor;
    found: BOOLEAN;

    Search: PROC [base: SymbolTable.Base] =
      BEGIN
      newMdi: Symbols.MDIndex = base.FindMdi[mdb[mdRoot].stamp];
      ihti: Symbols.HTIndex ← base.FindString[@ss];
      found ← ihti # Symbols.HTNull AND newMdi # Symbols.MDNull;
      IF ~found THEN RETURN;
      FOR i: IncludedCTXIndex ← base.mdb[newMdi].ctx,
      base.ctxb[i].chain UNTIL i = Symbols.CTXNull DO
	IF base.ctxb[i].map # target THEN LOOP;
	isei ← base.SearchContext[ihti,i];
	EXIT;
	REPEAT
	  FINISHED => isei ← Symbols.ISENull;
	ENDLOOP;
      found ← isei # Symbols.ISENull;
      END;

    IF ictx = Symbols.CTXNull THEN RETURN[NIL];
    IF (f ← TryCtx[hti,ba,ictx]) # NIL THEN RETURN;
    Complete[ictx];
    SymbolOps.SubStringForHash[@ss,hti];
    Add[];
    FOR sei ← SymbolOps.FirstCtxSe[ictx], SymbolOps.NextSe[sei]
    UNTIL sei = Symbols.ISENull DO
      ictx ← GetCtx[sei];
      WITH ctxb[ictx] SELECT FROM
	included => {mdRoot ← module; target ← map};
	ENDCASE => LOOP;
      Copier.Outer[mdi,Search];
      IF ~found THEN LOOP;
--      isei ← Copier.TokenSymbol[ictx,LOOPHOLE[isei]];
      isei ← DI.SearchCtxList[hti,ictx];
      IF isei = Symbols.ISENull THEN EXIT;
      SELECT Mode[isei] FROM
	refVal, refProc => NULL;
	ENDCASE => f ← MakeFoo[isei,ba];
      EXIT;
      ENDLOOP;
    Drop[];
    END;

  SearchImportCtx: PROC [
      hti: HTIndex, ba: DebugOps.BitAddress, ictx: Symbols.CTXIndex]
    RETURNS [f: Foo] =
    BEGIN OPEN SymbolOps;
    isei: Symbols.ISEIndex;
    IF ictx = Symbols.CTXNull THEN RETURN[NIL];
    IF (f ← TryCtx[hti,ba,ictx]) # NIL THEN RETURN;
    Complete[ictx];
    Add[];
    FOR isei ← FirstCtxSe[ictx], NextSe[isei] UNTIL isei = Symbols.ISENull DO
      IF (f ← TryCtx[hti,ba,GetCtx[isei]]) # NIL THEN EXIT;
      ENDLOOP;
    Drop[];
    END;

  GetCtx: PROC [isei: Symbols.ISEIndex] RETURNS [ctx: Symbols.CTXIndex] =
    BEGIN 
    csei: Symbols.CSEIndex ← DI.TypeForSe[isei];
    DO
      WITH seb[csei] SELECT FROM
	definition => RETURN[defCtx];
	record => RETURN[fieldCtx];
	ref => csei ← DI.TypeForSe[refType];
	long => csei ← DI.TypeForSe[rangeType];
	ENDCASE => RETURN[Symbols.CTXNull];
      ENDLOOP;
    END;

  TryCtx: PROC [hti: HTIndex, ba: DebugOps.BitAddress, ctx: CTXIndex]
    RETURNS [f: Foo] =
    BEGIN
    isei: Symbols.ISEIndex = DI.SearchCtxList[hti,ctx];
    SELECT TRUE FROM
      UserInput.userAbort => ERROR DebugOps.UserAborted;
      isei = ISENull => RETURN[NIL];
      ba.useStack => ERROR SearchingWrongContext;
      ENDCASE;
    RETURN[MakeFoo[isei, ba]];
    END;

  TryProcCtx: PROC [hti: HTIndex, ba: DebugOps.BitAddress, list: Pc.CtxLink]
    RETURNS [f: Foo] =
    BEGIN
    isei: Symbols.ISEIndex = DI.SearchCtxList[hti,list.ictx];
    stack: POINTER;
    SELECT TRUE FROM
      UserInput.userAbort => ERROR DebugOps.UserAborted;
      isei = ISENull => RETURN[NIL];
      ba.useStack AND ba.base # NIL AND Lookup.Mode[isei] = val => {
	stack ← IF list.indirect THEN DebugOps.ShortREAD[@data.StatePtr.stk[0]]
	ELSE @data.StatePtr.stk[0];
	ba.base ← DebugOps.Lengthen[stack]};
      ENDCASE;
    RETURN[MakeFoo[isei, ba]];
    END;

  FindVar: PUBLIC PROC [var: STRING] = 
    BEGIN
    hti: HTIndex = StringToHti[var];
    f: Foo;
    gf: GFHandle;
    SearchOne: PROC [g: GFHandle] RETURNS [BOOLEAN] =
      BEGIN
      IF (f←InGF[hti,g]) # NIL THEN {gf ← g; RETURN[TRUE]};
      Init.CheckSymTabLength[];
      RETURN[FALSE];
      END;
    DContext.Enumerate[SearchOne];
    IF f = NIL THEN SIGNAL Fail[var];
    f.hti ← Symbols.HTNull; DOutput.Char[Ascii.SP]; DebugOps.Display[f,TRUE];
    Gf.DisplayInMsg[gf, NIL];
    END;

  END.