-- DSymTab.Mesa  Edited by:
  -- Bruce June 10, 1980  6:58 PM
  -- Sandman May 5, 1980  6:02 PM

DIRECTORY
  BcdDefs USING [
    Base, BCD, FTNull, FTSelf, MTIndex, NullVersion, SGIndex, SGNull,
    VersionID, VersionStamp],
  BcdOps USING [
    BcdBase, FTHandle, MTHandle, NameString, ProcessModules, SGHandle],
  ComData,
  CompilerUtil USING [PassIndex, PrefillSymbols],
  Copier,
  DebugOps,
  DI USING [ResetLongs],
  DOutput USING [Blanks, Char, Text],
  DSyms,
  Frames USING [Invalid],
  Gf USING [Check, File, Frame, GFI, Name, Original],
  LiteralOps USING [Initialize],
  LoadStateOps USING [
    AcquireBcd, ConfigIndex, InputLoadState, LoadStateInvalid,
    MapRealToConfig, NullConfig, ReleaseBcd, ReleaseLoadState],
  Lookup,
  MachineDefs,
  Pc,
  SegmentDefs USING [
    DefaultAccess, DeleteFileSegment, FileError, FileNameError,
    FileHandle, FileSegmentHandle, FileSegmentAddress,
    MoveFileSegment, NewFile, NewFileSegment, OldFileOnly, PageNumber, Read,
    SwapIn, SwapOut, Unlock, VMtoFileSegment],
  Storage: FROM "storage",
  String USING [
    AppendString, AppendSubString, SubString, SubStringDescriptor],
  SymbolOps USING [EnterString, Initialize, Reset],
  SymbolPack,
  Symbols USING [
    BTIndex, HTIndex, HTNull, IncludedCTXNull, 
    MDIndex, MDNull, mdType, MDRecord, OwnMdi, RootBti],
  SymbolSegment: FROM "symbolsegment",
  SymbolTable: FROM "symboltable" USING [Base, Missing],
  Table: FROM "table",
  TreeOps: FROM "TreeOps" USING [Initialize];

DSymTab: PROGRAM
  IMPORTS BcdOps, data: ComData, CompilerUtil, Copier,
    DebugOps, DI, DOutput, 
    Frames, Gf, LiteralOps, LoadStateOps, Lookup, SegmentDefs, Storage,
    String, SymbolOps, SymbolPack, SymbolTable, Table, TreeOps
  EXPORTS CompilerUtil, DSyms =
  BEGIN OPEN DSyms, MachineDefs;

  Head: Item ← NIL;
  s: STRING ← NIL;
  NoFGT: PUBLIC ERROR = CODE;
  CantAttach: ERROR = CODE;
  SymbolTableProblem: ERROR = CODE;

  MakeSwappable: PUBLIC PROC[module:PROGRAM, pass:CompilerUtil.PassIndex]={};

  FreeItems: PUBLIC PROCEDURE =
    BEGIN OPEN SegmentDefs, SymbolTable;
    syms, next: Item;
    FOR syms ← Head, next UNTIL syms = NIL DO
      next ← syms.link;
      Storage.Free[syms];
      ENDLOOP;
    Copier.CopierReset[];
    Copier.FileReset[];
    Copier.FileInit["XDebug"L, BcdDefs.NullVersion];
    Copier.CopierInit[FALSE];
    Copier.CreateFileTable[16];
    Head ← NIL;
    END;
  
  CleanItems: PUBLIC PROCEDURE =
    BEGIN
    syms, next, prev: Item;
    prev ← NIL;
    FOR syms ← Head, next UNTIL syms = NIL DO
      next ← syms.link;
      IF syms.table = NIL THEN
	BEGIN
	IF prev = NIL THEN Head ← next ELSE prev.link ← next;
	Storage.Free[syms];
	END
      ELSE prev ← syms;
      ENDLOOP;
    END;

  Enumerate: PUBLIC PROC [proc: PROCEDURE [Item] RETURNS [BOOLEAN]]
      RETURNS [syms: Item] =
    BEGIN
    last: Item;
    FOR syms ← Head, syms.link UNTIL syms = NIL DO
      IF proc[syms] THEN
	BEGIN
	IF syms = Head THEN RETURN;
	last.link ← syms.link; syms.link ← Head; Head ← syms;
	RETURN
	END;
      last ← syms;
      ENDLOOP;
    END;
  
  FindMod: PUBLIC PROC [name: HTIndex] RETURNS [Item] =
    BEGIN OPEN Symbols;
    mdb: Table.Base;
    length: CARDINAL;
    mdi: MDIndex;
    [mdb,length] ← Table.Bounds[Symbols.mdType];
    FOR mdi ← OwnMdi, mdi+SIZE[MDRecord] UNTIL
    LOOPHOLE[mdi,CARDINAL] = length DO
      IF mdb[mdi].moduleId = name THEN RETURN[FindMdi[mdi]];
      ENDLOOP;
    RETURN[NIL];
    END;

  FindMdi: PUBLIC PROC [mdi: MDIndex] RETURNS [Item] =
    BEGIN
    Find: PROC [d: Item] RETURNS[BOOLEAN] = 
      BEGIN
      WITH sym: d SELECT FROM
	hash => RETURN[FALSE];
	mod => RETURN[mdi = sym.mdi];
	ENDCASE => ERROR;
      END;
    RETURN[Enumerate[Find]];
    END;

  FindFrame: PUBLIC PROC [gf: MachineDefs.GFHandle] RETURNS [i: Item] =
    BEGIN
    Find: PROC [d: Item] RETURNS[BOOLEAN] = {RETURN[gf = d.gf]};
    RETURN[Enumerate[Find]];
    END;

  ModuleMdi: PUBLIC PROC [hti: HTIndex, fgt: BOOLEAN ← FALSE]
    RETURNS [MDIndex] =
    BEGIN
    syms: Item;
    gf: GFHandle ← NIL;
    mod: STRING ← [40];
    Lookup.HtiToString[hti,mod];
    gf ← Gf.Frame[mod ! Lookup.Fail => CONTINUE];
    IF (syms ← FindMod[hti]) = NIL THEN
      BEGIN
      IF gf = NIL THEN {AttachSyms[gf,mod]; syms ← FindMod[hti]}
      ELSE syms ← AddItem[hti,gf];
      END;
    SetSegment[syms,fgt];
    WITH syms SELECT FROM
      mod => RETURN[mdi];
      ENDCASE => ERROR SymbolTable.Missing[NIL];
    END;

  GFrameMdi: PUBLIC PROC [gf: GFHandle, fgt: BOOLEAN ← FALSE]
    RETURNS [MDIndex] =
    BEGIN
    syms: Item;
    Gf.Check[gf];
    IF (syms ← FindFrame[gf]) = NIL THEN syms ← AddItem[Symbols.HTNull,gf];
    SetSegment[syms,fgt];
    WITH syms SELECT FROM
      mod => RETURN[mdi];
      ENDCASE => ERROR SymbolTable.Missing[NIL];
    END;

  Stopping: PUBLIC PROC [gf: GFHandle, fgt: BOOLEAN ← FALSE]
    RETURNS [BOOLEAN] =
    BEGIN
    syms: Item;
    Gf.Check[gf];
    IF (syms ← FindFrame[gf]) = NIL THEN syms ← AddItem[Symbols.HTNull,gf];
    SetSegment[syms,fgt];
    RETURN[syms.stopping];
    END;

  CrossJumped: PUBLIC PROC [gf: GFHandle, fgt: BOOLEAN ← FALSE]
    RETURNS [BOOLEAN] =
    BEGIN
    syms: Item;
    Gf.Check[gf];
    IF (syms ← FindFrame[gf]) = NIL THEN syms ← AddItem[Symbols.HTNull,gf];
    SetSegment[syms,fgt];
    RETURN[syms.crossJumped];
    END;

  GFrameHti: PUBLIC PROC [gf: GFHandle, fgt: BOOLEAN ← FALSE]
    RETURNS [HTIndex] =
    BEGIN
    mdi: MDIndex ← Symbols.MDNull;
    i: Item;
    mdi ← GFrameMdi[gf,fgt !SymbolTable.Missing => CONTINUE];
    IF mdi # Symbols.MDNull THEN
      RETURN[Table.Bounds[Symbols.mdType].base[mdi].moduleId];
    i ← FindFrame[gf];
    WITH i SELECT FROM
      hash => RETURN[hti];
      ENDCASE => ERROR SymbolTableProblem;
    END;

  ModuleToHti: PUBLIC PROC [s: STRING] RETURNS [hti: Symbols.HTIndex] =
    BEGIN 
    mod: STRING ← [40];
    desc: String.SubStringDescriptor;
    String.AppendString[mod,s]; StripExtension[mod];
    desc ← [base: mod, offset: 0, length: mod.length];
    RETURN[SymbolOps.EnterString[@desc]];
    END;

  HtiFromLoadState: PROC [gf: GFHandle] RETURNS [Symbols.HTIndex] =
    BEGIN
    name: STRING ← [40];
    Gf.Name[name,gf];
    RETURN[ModuleToHti[name]];
    END;

  mdb: Table.Base;

  Notify: Table.Notifier = {mdb ← base[Symbols.mdType]};

  AddItem: PROC [hti: HTIndex, gf: GFHandle] RETURNS [syms: Item] = 
    BEGIN
    version: BcdDefs.VersionStamp;
    seg: FSHandle;
    jumped, fgt: BOOLEAN;
    [version: version, seg: seg, fgt: fgt, jumped:jumped] ←
      SymbolSegForFrame[gf];
    syms ← CacheItem[
      hti:hti, gf:gf, version:version, seg:seg, fgt:fgt, jumped:jumped];
    END;

  CacheItem: PROC [
        hti: HTIndex, gf: GFHandle, version: BcdDefs.VersionStamp,
        seg: FSHandle, fgt, jumped: BOOLEAN]
      RETURNS [syms: Item] = 
    BEGIN
    mdi: Symbols.MDIndex;
    syms ← Storage.Node[SIZE[ItemObject]];
    IF seg = NIL THEN
      BEGIN
      syms↑ ← [link: Head, gf: gf, table: seg, fgtAvailable: fgt,
      symbol: hash[HtiFromLoadState[gf]], outerCtx: Symbols.IncludedCTXNull,
      directoryCtx: Symbols.IncludedCTXNull,
      importCtx: Symbols.IncludedCTXNull, crossJumped:jumped, stopping:];
      Head ← syms;
      ERROR SymbolTable.Missing[NIL];
      END
    ELSE
      BEGIN
      mdi ← Copier.EnterFileSegment[hti,version,seg];
      syms↑ ← [link: Head, gf: gf, table:seg, fgtAvailable:fgt,
      symbol: mod[mdi], outerCtx: Symbols.IncludedCTXNull,
      directoryCtx: Symbols.IncludedCTXNull,
      importCtx: Symbols.IncludedCTXNull, crossJumped:jumped, stopping:];
      Head ← syms;
      END;
    Copier.Outer[mdi,EnterContexts];
    Table.AddNotify[Notify];
    mdb[mdi].shared ← TRUE;
    IF mdb[mdi].moduleId = Symbols.HTNull THEN
      BEGIN
      ssd: String.SubStringDescriptor; 
      IF s = NIL THEN ERROR SymbolTableProblem
      ELSE ssd ← [base: s, length: s.length, offset: 0];
      mdb[mdi].moduleId ← SymbolOps.EnterString[@ssd];
      s ← Storage.FreeStringNil[s];
      END;
    [] ← Copier.EnterFileSegment[mdb[mdi].moduleId,version,seg];
    Table.DropNotify[Notify];
    syms.outerCtx ← Copier.FindExternalCtx[mdi,syms.outerCtx];
    syms.directoryCtx ← Copier.FindExternalCtx[mdi,syms.directoryCtx];
    syms.importCtx ← Copier.FindExternalCtx[mdi,syms.importCtx];
    RETURN
    END;
  
  SetSegment: PROC [syms: Item, fgt: BOOLEAN] =
    BEGIN
    IF ~fgt OR syms.fgtAvailable THEN RETURN;
    WITH syms SELECT FROM
      hash => ERROR SymbolTable.Missing[NIL];
      mod => ERROR NoFGT;
      ENDCASE;
    END;

  EnterContexts: PROC [base: SymbolTable.Base] =
    BEGIN
    mdb: Table.Base ← Table.Bounds[Symbols.mdType].base;
    ssd: String.SubStringDescriptor;
    ss: String.SubString = @ssd;
    Head.outerCtx ← base.mainCtx;
    Head.directoryCtx ← base.stHandle.directoryCtx;
    Head.importCtx ← base.stHandle.importCtx;
    Head.stopping ← base.bb[Symbols.RootBti].stopping;
    WITH Head SELECT FROM
      mod => IF mdb[mdi].moduleId # Symbols.HTNull THEN RETURN;
      ENDCASE => ERROR SymbolTableProblem;
    base.SubStringForHash[ss,base.mdb[Symbols.OwnMdi].moduleId];
    IF s # NIL THEN ERROR SymbolTableProblem;
    s ← Storage.String[ss.length];
    String.AppendSubString[s,ss];
    END;

  AttachSyms: PUBLIC PROC [gf: GFHandle, file: STRING] =
    BEGIN
    syms: Item;
    mod: STRING ← [40];
    version: BcdDefs.VersionStamp;
    fgt, jumped: BOOLEAN;
    seg: MachineDefs.FSHandle;
    fh: FileHandle;
    String.AppendString[mod,file]; StripExtension[mod];
    String.AppendString[mod,".bcd"L];
    fh ← SegmentDefs.NewFile[mod,SegmentDefs.Read,SegmentDefs.OldFileOnly !
      SegmentDefs.FileNameError => {SIGNAL Lookup.Fail[mod]; CONTINUE}];
    IF gf # NIL AND (syms ← FindFrame[gf]) # NIL THEN
      {syms.table ← NIL; CleanItems[]};
    [version:version, seg:seg, jumped:jumped, fgt:fgt] ← CompilerSeg[gf,fh !
      CantAttach =>
	BEGIN OPEN DOutput;
	Blanks[1]; Text[mod]; Text[" is a definitions file!"L];
	GOTO bad
	END];
    [] ← CacheItem[
      hti:Symbols.HTNull, gf:gf, version:version, seg:seg, fgt:fgt,
      jumped:jumped
        ! SymbolTable.Missing =>
	  BEGIN OPEN DOutput;
	  Blanks[1]; Text[mod]; Text[" does not have a valid symbol table!"L];
	  CONTINUE
	  END];
    DebugOps.InvalidateFileCache[];
    EXITS bad => NULL
    END;
  
  CompilerSeg: PROC [gf: GFHandle, file: FileHandle] RETURNS [
    version: BcdDefs.VersionStamp, seg: FSHandle, fgt,jumped: BOOLEAN] =
    BEGIN OPEN SegmentDefs;
    sgh: BcdOps.SGHandle;
    mth: BcdOps.MTHandle;
    sSeg: BcdDefs.SGIndex;
    bcdPages: CARDINAL ← 1;
    bcd: BcdOps.BcdBase;
    headerSeg: FSHandle ← NewFileSegment[file, 1, bcdPages, Read];
    DO
      SwapIn[headerSeg];  bcd ← FileSegmentAddress[headerSeg];
      IF bcd.versionIdent # BcdDefs.VersionID THEN 
	{CleanupSeg[headerSeg]; RETURN[BcdDefs.NullVersion,NIL,FALSE,FALSE]};
      IF bcdPages = bcd.nPages THEN EXIT;
      bcdPages ← bcd.nPages;
      Unlock[headerSeg];  SwapOut[headerSeg];
      MoveFileSegment[headerSeg, 1, bcdPages];
      ENDLOOP;
    IF bcd.nConfigs # 0 THEN 
      {CleanupSeg[headerSeg]; RETURN[BcdDefs.NullVersion,NIL,FALSE,FALSE]};
    IF bcd.definitions AND gf # NIL THEN 
      {CleanupSeg[headerSeg]; ERROR CantAttach};
    mth ← @LOOPHOLE[bcd+bcd.mtOffset,Table.Base][FIRST[BcdDefs.MTIndex]];
    sSeg ← mth.sseg; jumped ← mth.crossJumped;
    sgh ← @LOOPHOLE[bcd+bcd.sgOffset,Table.Base][sSeg];
    IF sSeg=BcdDefs.SGNull OR sgh.pages = 0 OR sgh.file # BcdDefs.FTSelf THEN
      {CleanupSeg[headerSeg]; RETURN[BcdDefs.NullVersion,NIL,FALSE,FALSE]};
    version ← bcd.version;
    seg ← NewFileSegment[file, sgh.base, sgh.pages+sgh.extraPages, Read];
    fgt ← sgh.extraPages # 0;
    CleanupSeg[headerSeg];
    END;

  SymbolSegForFrame: PROCEDURE [f: GFHandle] RETURNS [
    version: BcdDefs.VersionStamp, seg: FSHandle, fgt,jumped: BOOLEAN] =
    BEGIN OPEN LoadStateOps, SegmentDefs, BcdDefs, BcdOps;
    cgfi: GFTIndex;
    config: LoadStateOps.ConfigIndex;
    bcd: BcdBase;
    cdesc, segdesc: SGIndex;
    FindModule: PROCEDURE [mth: MTHandle, mti: MTIndex] RETURNS [BOOLEAN] =
      BEGIN
      IF cgfi IN [mth.gfi..mth.gfi+mth.ngfi) THEN
	BEGIN
	cdesc ← mth.code.sgi;
	segdesc ← mth.sseg;
	jumped ← mth.crossJumped;
	RETURN[TRUE];
	END;
      RETURN[FALSE];
      END;
    BEGIN
    f ← Gf.Original[f];
    [] ← InputLoadState[ ! LoadStateInvalid => GOTO nil];
    [cgfi,config] ← MapRealToConfig[Gf.GFI[f]];
    IF config = NullConfig THEN ERROR Frames.Invalid[f !
      UNWIND => ReleaseLoadState[]];
    bcd ← AcquireBcd[config];
    [] ← BcdOps.ProcessModules[bcd, FindModule];
    [version: version, seg:seg, fgt: fgt] ←
      FindSeg[f, bcd, segdesc, cdesc !
	FileNameError, FileError => {Cleanup[bcd]; GOTO nil};
	UNWIND => Cleanup[bcd]];
    Cleanup[bcd];
    EXITS
      nil => RETURN[BcdDefs.NullVersion,NIL,FALSE,FALSE];
    END;
    RETURN
    END;
  
  FindSeg: PROCEDURE [
      frame: GFHandle, bcd: BcdOps.BcdBase, syms, code: BcdDefs.SGIndex]
    RETURNS [version: BcdDefs.VersionStamp, seg: FSHandle, fgt: BOOLEAN] =
    BEGIN OPEN SegmentDefs, BcdDefs, BcdOps;
    ss: String.SubStringDescriptor;
    tempssb: NameString;
    name: STRING ← [40];
    file: FileHandle;
    symsbcd: BcdOps.BcdBase;
    bcdseg: FSHandle ← VMtoFileSegment[bcd];
    sgb: Base ← LOOPHOLE[bcd+bcd.sgOffset];
    sgh: SGHandle ← @sgb[syms];
    f: FTHandle = @LOOPHOLE[bcd+bcd.ftOffset, Base][sgh.file];
    BEGIN
    SELECT sgh.file FROM
      FTNull => RETURN[BcdDefs.NullVersion,NIL,FALSE];
      FTSelf => 
	BEGIN
	version ← f.version;
	seg ← NewFileSegment[bcdseg.file, sgh.base, sgh.pages+sgh.extraPages, Read];
	fgt ← sgh.extraPages # 0;
	RETURN;
	END;
      sgb[code].file =>
	IF (file ← Gf.File[frame]) # NIL THEN GOTO found;
      ENDCASE;
    tempssb ← LOOPHOLE[bcd+bcd.ssOffset]; 
    ss ← [@tempssb.string, f.name, tempssb.size[f.name]];
    String.AppendSubString[name, @ss];
    CheckExtension[name];
    file ← DebugOps.CacheNewFile[name, DefaultAccess];
    EXITS
      found => NULL;
    END;
    seg ← NewFileSegment[file, 1, 1, Read];
    SwapIn[seg];
    symsbcd ← FileSegmentAddress[seg];
    IF symsbcd.version # f.version THEN
      BEGIN
      IF name.length = 0 THEN DebugOps.FileName[name, file];
      Unlock[seg];
      DeleteFileSegment[seg];
      IncorrectVersion[name];
      RETURN[BcdDefs.NullVersion,NIL,FALSE];
      END;
    Unlock[seg];
    MoveFileSegment[seg, sgh.base, sgh.pages+sgh.extraPages];
    version ← f.version;
    fgt ← sgh.extraPages # 0;
    RETURN;
    END;

  IncorrectVersion: PROC [name: STRING] =
    BEGIN OPEN DOutput;
    Char[' ]; Text[name]; Text[" referenced in different versions! "L];
    END;

  StripExtension: PUBLIC PROCEDURE [name: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i DECREASING IN [1..name.length) DO
      IF name[i] = '. THEN {name.length ← i; RETURN};
      ENDLOOP;
    END;
  
  CheckExtension: PROCEDURE [s: STRING] =
    BEGIN
    i: CARDINAL;
    FOR i DECREASING IN [1..s.length) DO
      IF s[i] = '. THEN RETURN;
      ENDLOOP;
    String.AppendString[s,".bcd"L];
    END;
  
  CleanupSeg: PROCEDURE [seg: FSHandle] =
    BEGIN
    SegmentDefs.Unlock[seg];
    SegmentDefs.DeleteFileSegment[seg];
    END;
  
  Cleanup: PROCEDURE [bcd: BcdOps.BcdBase] =
    BEGIN
    LoadStateOps.ReleaseBcd[bcd];
    LoadStateOps.ReleaseLoadState[];
    RETURN
    END;

  SymbolTablePages: CARDINAL = 20;
  SymbolTableSize: CARDINAL = SymbolTablePages*MachineDefs.PageSize;

  bounds: ARRAY SymbolSegment.Tables OF CARDINAL;

  Initialize: PUBLIC PROCEDURE =
    BEGIN
    i: Table.Selector;
    weights: ARRAY SymbolSegment.Tables OF CARDINAL ← ALL[1];
    Table.Create[[origin: LOOPHOLE[
      Storage.Pages[SymbolTablePages],CARDINAL], size: SymbolTableSize],
      DESCRIPTOR[weights]];
    START SymbolPack;
    data.bodyRoot ← FIRST[Symbols.BTIndex];
    data.definitionsOnly ← FALSE;
    SymbolOps.Initialize[];
    LiteralOps.Initialize[];
    TreeOps.Initialize[];
    CompilerUtil.PrefillSymbols[];
    Copier.FileInit["XDebug"L, BcdDefs.NullVersion];
    Copier.CopierInit[FALSE];
    Copier.CreateFileTable[16];
    FOR i IN SymbolSegment.Tables DO
      bounds[i] ← Table.Bounds[i].size;
      ENDLOOP;
    END;

  Prune: PUBLIC PROCEDURE =
    BEGIN
    i: Table.Selector;
    DI.ResetLongs[];
    FOR i IN SymbolSegment.Tables DO
      Table.Trim[i, bounds[i]];
      ENDLOOP;
    SymbolOps.Reset[];
    END;
  
  END...