-- SMProjImpl.mesa 
-- last edit by Satterthwaite, August 4, 1983 1:37 pm

DIRECTORY
  BcdDefs: TYPE USING [Base, MTIndex],
  BcdOps: TYPE USING [BcdBase],
  CompilerOps: TYPE USING [Transaction],
  CS: TYPE USING [EndsIn, EquivalentRS, RootName, StringToRope, z],
  Directory: TYPE USING [Error, ignore, Lookup, Rename],
  File: TYPE USING [Capability, nullCapability],
  FileSegment: TYPE USING [],
  OrderedSymbolTable: TYPE OrderedSymbolTable USING [
    Table, CreateTable, DestroyTable, EnumerateIncreasing, Initialize, Insert, Lookup],
  Rope: TYPE USING [Cat, Flatten, Text],
  SMProj: TYPE USING [Proj, ProjInfo],
  Space: TYPE USING [
    Create, CreateUniformSwapUnits, Delete, Handle, LongPointer, MakeReadOnly,
    Map, virtualMemory],
  TimeStamp: TYPE USING [Stamp, Null];
	
SMProjImpl: CEDAR MONITOR 
    IMPORTS CS, Directory, OrderedSymbolTable, Rope, Space
    EXPORTS SMProj ~ {

  Proj: TYPE ~ SMProj.Proj;	-- can't be opaque with current RedBlackTree impl
  
 -- all code in this module updates the projection table
 -- therefore all PUBLIC procedures acquire the monitor lock

 -- MDS usage (protected by the monitor lock)
  table: OrderedSymbolTable.Table;
 -- endof MDS

 -- code to manipulate the projection (bcd) cache

  Flush: PUBLIC ENTRY PROC ~ {
    ENABLE UNWIND => {NULL};
    table.DestroyTable};
	
  Reset: PUBLIC ENTRY PROC ~ {
    ENABLE UNWIND => {NULL};
    
    ResetProj: PROC[proj: Proj] RETURNS[stop: BOOL←FALSE] ~ {
      IF proj.state > $analyzed THEN {
        proj.capability ← File.nullCapability;  proj.state ← $analyzed};
      };

    table.EnumerateIncreasing[ResetProj]};
    
  Erase: PUBLIC ENTRY PROC[proj: Proj] ~ {
    ENABLE UNWIND => {NULL};
    oldCap: File.Capability ~ proj.capability;
    IF oldCap ~= File.nullCapability THEN  {

      EraseProj: PROC[proj: Proj] RETURNS[stop: BOOL←FALSE] ~ {
	IF oldCap = proj.capability THEN {
	  proj.state ← MAX[proj.state, $analyzed];
	  proj.capability ← File.nullCapability;
	  proj.localName ← NIL}
	};

      table.EnumerateIncreasing[EraseProj]};
    };


  Analyzed: PUBLIC ENTRY PROC[proj: Proj] RETURNS[BOOL] ~ {
    ENABLE UNWIND => {NULL};
    RETURN[proj.state >= $analyzed]};
    
  Available: PUBLIC ENTRY PROC[proj: Proj] RETURNS[BOOL] ~ {
    ENABLE UNWIND => {NULL};
    RETURN[proj.state = $opened]};
    

  Fill: PUBLIC ENTRY PROC[proj: Proj, localName: Rope.Text, new: BOOL] ~ {
    ENABLE UNWIND => {NULL};
    IF proj.state < $opened THEN {
      proj.localName ← (IF CS.EndsIn[localName, ".bcd"]
	THEN localName ELSE CS.RootName[localName].Cat[".bcd"].Flatten[]);
      IF ~new THEN OpenProj[proj]};
    };
    

  Find: PUBLIC ENTRY PROC[stamp: TimeStamp.Stamp] RETURNS[proj: Proj] ~ {
    ENABLE UNWIND => {NULL};
    proj ← table.Lookup[stamp];
    IF proj = NIL THEN {
      proj ← (CS.z).NEW[SMProj.ProjInfo ← [stamp~stamp, state~$empty]];
      table.Insert[proj, stamp]};
    RETURN};


  Rename: PUBLIC ENTRY PROC[proj: Proj, newName: Rope.Text] ~ TRUSTED {
    ENABLE UNWIND => {NULL};
    Directory.Rename[
      newName~LOOPHOLE[newName], oldName~LOOPHOLE[proj.localName]];
    proj.localName ← newName};


  FindFile: PROC[localName: Rope.Text] RETURNS [cap: File.Capability] ~ TRUSTED {
    cap ← Directory.Lookup[
	fileName~LOOPHOLE[localName],
	permissions~Directory.ignore
	    ! Directory.Error => {cap ← File.nullCapability; CONTINUE}];
    };

  RetrieveRemoteFile: PROC[proj: Proj] ~ {};	-- place holder

  OpenProj: PROC[proj: Proj] ~ TRUSTED {
    cap: File.Capability ~ FindFile[proj.localName];
    IF cap = File.nullCapability THEN NULL
    ELSE IF proj.state = $analyzed THEN {
      proj.capability ← cap; proj.state ← $opened}	-- version stamp not verified here
    ELSE {
      bcdBase: BcdOps.BcdBase;
      space: Space.Handle;
      [space, bcdBase] ← LoadUpBcd[cap];
      IF bcdBase.version = proj.stamp THEN {
	firstMti: BcdDefs.MTIndex ~ BcdDefs.MTIndex.FIRST;
	mtb: BcdDefs.Base ~ LOOPHOLE[bcdBase + bcdBase.mtOffset];
	sgb: BcdDefs.Base ~ LOOPHOLE[bcdBase + bcdBase.sgOffset];
	proj.interface ← bcdBase.definitions;
	proj.symbolPages ← [
	    base~sgb[mtb[firstMti].sseg].base,
	    pages~sgb[mtb[firstMti].sseg].pages];
	proj.capability ← cap;
	proj.state ← $opened};
      Space.Delete[space]};
    };


  LoadUpBcd: PROC[cap: File.Capability]
      RETURNS[space: Space.Handle, bcdBase: BcdOps.BcdBase] ~ TRUSTED {
    nPages: CARDINAL ← 10;
    IF cap = File.nullCapability THEN ERROR;
    DO
      space ← Space.Create[size~nPages, parent~Space.virtualMemory];
      space.Map[window~[file~cap, base~1]];
      bcdBase ← space.LongPointer;
      IF bcdBase.nPages <= nPages THEN EXIT;
      nPages ← bcdBase.nPages;
      Space.Delete[space];
      ENDLOOP;
    Space.CreateUniformSwapUnits[parent~space, size~8];
    space.MakeReadOnly};


  -- for proj info provided by the compiler
  
  Update: PUBLIC ENTRY PROC[
   	proj: Proj, parms: POINTER TO READONLY CompilerOps.Transaction] ~ TRUSTED {
    ENABLE UNWIND => {NULL};
    IF proj.stamp # parms.objectVersion THEN ERROR;
    proj.interface ← parms.interface;
    IF ~CS.EquivalentRS[proj.localName, parms.objectName] THEN
      proj.localName ← StringToText[parms.objectName];
    proj.capability ← parms.objectFile;
    proj.symbolPages ← parms.symbolPages;
    proj.state ← $opened};
    
  StringToText: PROC[string: LONG STRING] RETURNS[Rope.Text] ~ TRUSTED INLINE {
    RETURN [CS.StringToRope[string].Flatten]};

  -- start code
  EmptyProj: PROC RETURNS [Proj] ~ {
    RETURN [(CS.z).NEW[SMProj.ProjInfo←[stamp~TimeStamp.Null]]]};
    
  OrderedSymbolTable.Initialize[EmptyProj[], EmptyProj[]];
  table ← OrderedSymbolTable.CreateTable[EmptyProj[]];
  
  }.