-- File ModuleSymbolsImpl.Mesa 
-- Last edited by Lewis on 15-May-81 18:18:08
-- Last edited by Sweet on  2-Mar-81 15:15:02

DIRECTORY
  BcdDefs USING [
    FTIndex, FTNull, FTSelf, MTIndex, SGIndex, SGNull, VersionStamp],
  Error USING [ErrorFile, WrongSymbolsVersion],
  FileTable USING [Build, Destroy, HandleForFile, UnknownFile],
  LongStorage USING [FreePages, Pages, PagesForWords],
  ModuleSymbols USING [KnownSymbolsInfo],
  PackageSymbols USING [
    ConstRecord, InnerPackRecord, OPIndex, OPNull, OuterPackRecord],
  PackEnviron USING [Copy, SymbolSegmentBase, SymSegHandle],
  PackHeap USING [FreeSpace, GetSpace],
  Segments USING [
    Address, BaseFromSegment, DeleteSegment, FHandle, MoveSegment, NewSegment, 
    Read, SegmentAddress, SHandle, SwapIn, Unlock],
  SourceBcd USING [
    bcdBases, bcdHeader, EnumerateModules, ModuleNum, ModuleNumForMti],
  Strings,
  SymbolPack,
  Symbols USING [HTIndex, HTNull, HTRecord],
  SymbolSegment USING [VersionID],
  SymbolTable USING [Base],
  Table;

ModuleSymbolsImpl: PROGRAM
    IMPORTS Error, FileTable, LongStorage, PackEnviron, PackHeap, Segments, SourceBcd, 
      ownSymbolPack: SymbolPack
    EXPORTS ModuleSymbols =
  BEGIN  OPEN ModuleSymbols;

  InvalidSymbols: PUBLIC ERROR = CODE;
  SymbolsProblem: PUBLIC SIGNAL = CODE;

  outerPackArray: PUBLIC LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.OuterPackRecord;
  innerPackArray: PUBLIC LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.InnerPackRecord;
  constArray: PUBLIC LONG DESCRIPTOR FOR ARRAY OF PackageSymbols.ConstRecord;


  symbolSegmentOpen: BOOLEAN;
  currentFile: Segments.FHandle;
  currentSeg: Segments.SHandle;
  knownSymbols: LONG DESCRIPTOR FOR ARRAY OF ModuleSymbols.KnownSymbolsInfo;

  symbolPackStarted: BOOLEAN ← FALSE;

  Load: PUBLIC PROC [mti: BcdDefs.MTIndex] =
    BEGIN
    sgi: BcdDefs.SGIndex;
    fti: BcdDefs.FTIndex;
    loadedBefore: BOOLEAN;
    file: Segments.FHandle;
    base: CARDINAL;
    mNum: SourceBcd.ModuleNum;
    pSymPages: CARDINAL;
    codeVersion: BcdDefs.VersionStamp;
    handle: PackEnviron.SymSegHandle;

    sgi ← SourceBcd.bcdBases.mtb[mti].sseg;
    fti ← SourceBcd.bcdBases.mtb[mti].file;
    SELECT fti FROM
      BcdDefs.FTNull => {SIGNAL SymbolsProblem; RETURN};
      BcdDefs.FTSelf => codeVersion ← SourceBcd.bcdHeader.version;
      ENDCASE => codeVersion ← SourceBcd.bcdBases.ftb[fti].version; 
    IF symbolSegmentOpen OR sgi = BcdDefs.SGNull THEN {SIGNAL SymbolsProblem;  RETURN}
    ELSE
      BEGIN
      mNum ← SourceBcd.ModuleNumForMti[mti];
      [loadedBefore: loadedBefore, base: base, pages: pSymPages, file: file] ← knownSymbols[mNum];
      IF file = currentFile THEN  -- reuse (i.e. move) current file segment
        Segments.MoveSegment[
          seg: currentSeg,
          base: base,
          pages: pSymPages]
      ELSE 
        BEGIN
        IF currentSeg # NIL THEN Segments.DeleteSegment[currentSeg];
        currentSeg ← Segments.NewSegment[
          file: file,
          base: base, 
          pages: pSymPages,
          access: Segments.Read];
        END;
      symbolSegmentOpen ← TRUE;
      IF ~loadedBefore THEN
        BEGIN
        Segments.SwapIn[seg: currentSeg, base: PackEnviron.SymbolSegmentBase];
        handle ← Segments.SegmentAddress[currentSeg];
        IF handle.version # codeVersion THEN
	  BEGIN
          Error.WrongSymbolsVersion[
            class: error, module: mti, 
	    requiredVersion: codeVersion, actualVersion: handle.version];
	  Unload[];
	  ERROR InvalidSymbols;
	  END;
        pSymPages ← LongStorage.PagesForWords[handle.constBlock.offset + handle.constBlock.size];
        Segments.Unlock[currentSeg];
        Segments.MoveSegment[
          seg: currentSeg,
          base: Segments.BaseFromSegment[currentSeg],
          pages: pSymPages];
	END;
      currentFile ← file;
      Segments.SwapIn[seg: currentSeg, base: PackEnviron.SymbolSegmentBase];
      IF ~loadedBefore THEN 
        BEGIN
	knownSymbols[mNum].pages ← pSymPages;
	knownSymbols[mNum].loadedBefore ← TRUE;
	END;
      InstallTable[ownSymbolPack, currentSeg ! ANY => Unload[]];
      END;
    END;

  Unload: PUBLIC PROC =
    BEGIN
    IF symbolSegmentOpen THEN {
      Segments.Unlock[currentSeg];
      IF BASE[outerPackArray] # NIL THEN PackHeap.FreeSpace[BASE[outerPackArray]];
      outerPackArray ← DESCRIPTOR[NIL, 0];
      IF BASE[innerPackArray] # NIL THEN PackHeap.FreeSpace[BASE[innerPackArray]];  
      innerPackArray ← DESCRIPTOR[NIL, 0];
      IF BASE[constArray] # NIL THEN PackHeap.FreeSpace[BASE[constArray]];  
      constArray ← DESCRIPTOR[NIL, 0];
      symbolSegmentOpen ← FALSE};
    END;

  InstallTable: PROC [base: SymbolTable.Base, seg: Segments.SHandle] =
    BEGIN
    b: Segments.Address = Segments.SegmentAddress[seg];
    tB: Table.Base = LOOPHOLE[b];
    p: PackEnviron.SymSegHandle = b;
    IF p.versionIdent # SymbolSegment.VersionID THEN {Unload[]; ERROR InvalidSymbols};
    base.cacheInfo ← NIL;
    base.hashVec ← b + p.hvBlock.offset;
    base.ht ← DESCRIPTOR[
      b + p.htBlock.offset, 
      p.htBlock.size / SIZE[Symbols.HTRecord]];
    base.ssb ← b + p.ssBlock.offset;
    base.stHandle ← p;  
    base.sourceFile ← NIL;
    base.notifier ← NullNotifier;
    outerPackArray ← DESCRIPTOR[
      PackHeap.GetSpace[p.outerPackBlock.size],
      (p.outerPackBlock.size / SIZE[PackageSymbols.OuterPackRecord])];
    PackEnviron.Copy[
      from: (b + p.outerPackBlock.offset), to: BASE[outerPackArray], 
      nwords: p.outerPackBlock.size]; 
    innerPackArray ← DESCRIPTOR[
      PackHeap.GetSpace[p.innerPackBlock.size],
      (p.innerPackBlock.size / SIZE[PackageSymbols.InnerPackRecord])];
    PackEnviron.Copy[
      from: (b + p.innerPackBlock.offset), to: BASE[innerPackArray], 
      nwords: p.innerPackBlock.size]; 
    constArray ← DESCRIPTOR[
      PackHeap.GetSpace[p.constBlock.size],
      (p.constBlock.size / SIZE[PackageSymbols.ConstRecord])];
    PackEnviron.Copy[
      from: (b + p.constBlock.offset), to: BASE[constArray], 
      nwords: p.constBlock.size]; 
    END;

  NullNotifier: PROC [SymbolTable.Base] = {NULL};


  FindProc: PUBLIC PROC [
      ss: Strings.SubString] RETURNS [opi: PackageSymbols.OPIndex] =
    BEGIN
    hti: Symbols.HTIndex = ownSymbolPack.FindString[ss];
    l, u, i: PackageSymbols.OPIndex;

    IF hti = Symbols.HTNull THEN RETURN[PackageSymbols.OPNull];
    l ← 1;  u ← (LENGTH[outerPackArray] - 1);
    WHILE l <= u DO
      i ← CARDINAL[l+u]/2;
      SELECT outerPackArray[i].hti FROM
	> hti => u ← i-1;
	< hti => l ← i+1;
	ENDCASE => RETURN [i];
      ENDLOOP;
    RETURN [PackageSymbols.OPNull];
    END;


  Initialize: PUBLIC PROC [nModules: CARDINAL] = 
    BEGIN

    FillInKnowledge: PROC [mti: BcdDefs.MTIndex] RETURNS [stop: BOOLEAN] =
      BEGIN
      mNum: SourceBcd.ModuleNum = SourceBcd.ModuleNumForMti[mti];
      sgi: BcdDefs.SGIndex;
      file: Segments.FHandle ← NIL;
      base: CARDINAL;
      IF SourceBcd.bcdBases.mtb[mti].tableCompiled THEN  -- ignore any symbols
        knownSymbols[mNum] ← [
          loadedBefore: FALSE, file: NIL, base: 0, pages: 0]
      ELSE 
	BEGIN
        sgi ← SourceBcd.bcdBases.mtb[mti].sseg;
        base ← SourceBcd.bcdBases.sgb[sgi].base;
        file ← FileTable.HandleForFile[SourceBcd.bcdBases.sgb[sgi].file
          ! FileTable.UnknownFile => 
	      BEGIN
	      Error.ErrorFile[error, "was needed for symbols but could not be found"L, fti];
	      CONTINUE;
              END ];
        knownSymbols[mNum] ← [
          loadedBefore: FALSE, file: file, base: base, pages: 1];
        END; 
      RETURN[FALSE]
      END;

    FileTable.Build[];
    symbolSegmentOpen ← FALSE;
    currentFile ← NIL;  currentSeg ← NIL;
    knownSymbols ← DESCRIPTOR[
      LongStorage.Pages[LongStorage.PagesForWords[nModules * SIZE[KnownSymbolsInfo]]],
      nModules];
    SourceBcd.EnumerateModules[FillInKnowledge];
    -- the array in FileTableImpl could go away now
    IF ~symbolPackStarted THEN 
      {START ownSymbolPack;  symbolPackStarted ← TRUE};
    END;

  Finalize: PUBLIC PROC = 
    BEGIN
    IF symbolSegmentOpen THEN Unload[];
    IF currentSeg # NIL THEN Segments.DeleteSegment[currentSeg];
    symbolSegmentOpen ← FALSE;
    LongStorage.FreePages[BASE[knownSymbols]];
    FileTable.Destroy[];
    currentFile ← NIL;  currentSeg ← NIL;
    END;

  END.