Press enter to see results or esc to cancel.

Loading Used Units

Each used unit is checked if it is already loaded. If it is not it needs to be found and checked if recompilation is needed

Function LoadUsedUnits: Boolean;
Var SavedUnitPathAndName: PChar;
    LoadedUnitPublicIdentifiersChecksum: Word;
    UsedUnitPublicIdentifiersChecksum: Word;
    LoadedUnitSegment: Word;
    CurrentUsedUnitIdentifier: Word;
    LoadedUnit, BaseUnit: PUnitHeader;
    UnitIdentifierData: PUnitIdentifierData;
    UnitNameLen: Byte;

  Function FindUsedUnit: PUnitHeader;
  Var FoundUnit, UnitToCheck: PUnitHeader;
      PreviousUnitSegment: Word;

    Function CurrentUsedUnitName: PString;
    begin
      CurrentUsedUnitName :=
       @PIdentifier (Ptr (BaseUnitSegment, PUnitHeader (Ptr (BaseUnitSegment, 0))^.CurrentUsedUnitIdentifierOffset))^.Name.Str;
    end;

    Procedure CopyUnitPathAndNameToFileStrucBuffer;
    begin
      UnitPathAndName := StartOfSourceFileStructures;
      If (Ofs (UnitPathAndName^) + 80) > Ofs (CurrentSourceFile^) then Error (TooManyFiles);
      StartOfSourceFileStructures := StrECopy (UnitPathAndName, @Identifier) + 1;
    end;

    Function LoadUsedUnit: PUnitHeader;
    Var DirInfo: SearchRec;

      Function RemoveSymbolTables_Code_To_TypedConstantsReferencesOrAllIfAlreadyLoaded: PUnitHeader;
      Var UnitPtr: PUnitHeader;
      begin
        RemoveSymbolTables_Code_To_TypedConstantsReferences (Ofs (UnitPathAndName^));
        If IsLastUnitAlreadyLoaded (UnitPtr) then RemoveLastLoadedUnit
          else begin
                UnitPtr := Ptr (LastLoadedUsedUnit, 0);
                PUnitHeader (Ptr (LastLoadedUsedUnit, 0))^.CodeSegment := 0;
                PUnitHeader (Ptr (LastLoadedUsedUnit, 0))^.OverlayedUnitCodeSize := 0;
              end;
        RemoveSymbolTables_Code_To_TypedConstantsReferencesOrAllIfAlreadyLoaded := UnitPtr;
        CheckStack;
      end;

      Function FindCurrentUsedUnitFile (Options: Word): Byte;
      begin
        StrPCopy (@Identifier, CurrentUsedUnitName^);
        StrUpper (@Identifier);
        FindCurrentUsedUnitFile := FindFilePath (@Identifier, Options);
      end;

      Function IncludeOrObjectFilesChanged: Boolean;
      Var FilesBlockPtr: PUsedFilesBlockRecord;
          FilesBlockPtrRec: PtrRec absolute FilesBlockPtr;
          UnitHeader: PUnitHeader;
      begin
        IncludeOrObjectFilesChanged := True;
        UnitHeader := Ptr (LastLoadedUsedUnit, 0);
        FilesBlockPtr := Ptr (LastLoadedUsedUnit, UnitHeader^.BlockOffset [stUsedFiles]);
        Inc (FilesBlockPtrRec.Ofs, FilesBlockPtr^.Name.Len + 8);
        While FilesBlockPtrRec.Ofs <> UnitHeader^.BlockOffset [Succ (stUsedFiles)] do
          begin
            FindUsedFilePath (FilesBlockPtr, @Identifier);
            FindFirst (StrPas (@Identifier), AnyFile, DirInfo);
            If UnitDateTime < (DirInfo.Time + 1) then Exit;
            Inc (FilesBlockPtrRec.Ofs, FilesBlockPtr^.Name.Len + 8);
          end;
        IncludeOrObjectFilesChanged := False;
      end;

      Function CheckUnitName: PString;
      Var UsedUnitName: PString;
      begin
        UsedUnitName := CurrentUsedUnitName;
        If not IdentifiersEqual (@PIdentifier (Ptr (LastLoadedUsedUnit,
                                PUnitHeader (Ptr (LastLoadedUsedUnit, 0))^.UnitNameIdentifierOffset))^.Name, UsedUnitName) then
          begin
            AdditionalErrorStr := @Identifier;
            StrPCopy (@Identifier, UsedUnitName^);
            Error (UnitNameMismatch);
          end;
        CheckUnitName := UsedUnitName;
      end;

    begin
      If PushedUnitLevel <> UsedUnitLevel then
        begin
          PushUnit;
          CompilerModeOptions := CompilerModeOptions + [cmoCreateExeFile, cmoCompileToDisk, cmoCompileOnlyInterfaceUsedUnits];
          Saved_PushedUnitLevel := PushedUnitLevel;
          PushedUnitLevel := UsedUnitLevel;
        end;
      ClearCurrentModuleVarsAndCreateUnit;
      If cmoSearchForUnitSource in CompilerModeOptions then
        begin                                                               { Check for UNIT source }
          FindCurrentUsedUnitFile (Dir_Unit or Ext_PAS);
          FindFirst (StrPas (@Identifier), AnyFile, DirInfo);
          UnitSourceDateTime := DirInfo.Time + 1;
          If DOSError = 0 then                                              { UNIT source found }
            begin
              FindFilePath (@Identifier, Dir_Forced or Dir_EXE_TPU or Ext_Forced or Ext_TPU);
              CopyUnitPathAndNameToFileStrucBuffer;
              If cmoMakeModifiedUnitsOnly in CompilerModeOptions then
                Repeat
                  FindFirst (StrPas (@Identifier), AnyFile, DirInfo);
                  If DOSError <> 0 then Break;                              { no TPU file }
                  UnitDateTime := DirInfo.Time + 1;
                  If UnitDateTime < UnitSourceDateTime then Break;
                  If ReadUnit (UnitPathAndName) then                        { TPU not older than PAS }
                    Repeat
                      CheckUnitName;
                      If IncludeOrObjectFilesChanged then Break;
                      If not LoadUsedUnits then Break;
                      LoadUsedUnit := RemoveSymbolTables_Code_To_TypedConstantsReferencesOrAllIfAlreadyLoaded;
                      Exit;
                    until True;
                  ClearUnitHeader;
                until True;
                CurrentSourceFilePathLength := FindCurrentUsedUnitFile (Dir_Unit or Ext_PAS);
                CurrentFileName := @Identifier;
                CompileModule;
                CheckUnitName;
                LoadUsedUnit := RemoveSymbolTables_Code_To_TypedConstantsReferencesOrAllIfAlreadyLoaded;
                Exit;
            end;
        end;
      FindCurrentUsedUnitFile (Dir_Forced or Dir_EXE_TPU or Ext_Forced or Ext_TPU);
      FindFirst (StrPas (@Identifier), AnyFile, DirInfo);
      If DOSError <> 0 then FindCurrentUsedUnitFile (Dir_Unit or Ext_Forced or Ext_TPU);
      CopyUnitPathAndNameToFileStrucBuffer;
      If not ReadUnit (UnitPathAndName) then Error (UnitVersionMismatch);
      CheckUnitName;
      If not LoadUsedUnits then Error (UnitVersionMismatch);
      LoadUsedUnit := RemoveSymbolTables_Code_To_TypedConstantsReferencesOrAllIfAlreadyLoaded;
    end;

    Function IsCurrentUsedUnitUsedInInterface (UnitPtr: PUnitHeader): Boolean;
    Var UsedUnitId: PIdentifier;
        UsedUnitIdData: PUnitIdentifierData;
    begin
      UsedUnitId := Ptr (Seg (UnitPtr^), UnitPtr^.CurrentUsedUnitIdentifierOffset);
      UsedUnitIdData := Ptr (Seg (UnitPtr^), Ofs (UsedUnitId^) + UsedUnitId^.Name.Len + 4);
      IsCurrentUsedUnitUsedInInterface := UsedInInterface in UsedUnitIdData^.UnitIdentifierFlags
    end;

    Procedure UnitError (Err: TCompilerError);
    Var UnitName: PString;
    begin
      UnitName := Ptr (BaseUnitSegment, PUnitHeader (Ptr (BaseUnitSegment, 0))^.CurrentUsedUnitIdentifierOffset + 3);
      AdditionalErrorStr := @Identifier;
      StrPCopy (@Identifier, UnitName^);
      Error (Err);
    end;

  begin
    CurrentIdentifier := CurrentUsedUnitName^;
    If FindUnitWithName (LastLoadedUsedUnit, 4, CurrentIdentifier, FoundUnit) then
      begin
        CheckStack;
        FindUsedUnit := FoundUnit;
        Exit;
      end;
    If not FindUnitWithName (BaseUnitSegment, 4, CurrentIdentifier, FoundUnit) then
      begin
        If not FindUnitWithName (LastLibraryUnitSegment, 6, CurrentIdentifier, FoundUnit) then
          begin
            FindUsedUnit := LoadUsedUnit;
            Exit;
          end;
        FoundUnit^.PreviousUnitSegment := LastLoadedUsedUnit;
        LastLoadedUsedUnit := Seg (FoundUnit^);
        If not LoadUsedUnits then UnitError (UnitVersionMismatch);
        PUnitHeader (Ptr (LastLoadedUsedUnit, 0))^.CodeSegment := 0;
        PUnitHeader (Ptr (LastLoadedUsedUnit, 0))^.OverlayedUnitCodeSize := 0;
        FindUsedUnit := FoundUnit;
        CheckStack;
        Exit;
      end;
    If not IsCurrentUsedUnitUsedInInterface (FoundUnit) then
      begin
        CheckStack;
        FindUsedUnit := FoundUnit;
        Exit;
      end;
    UnitToCheck := Ptr (BaseUnitSegment, 0);
    Repeat
      If not IsCurrentUsedUnitUsedInInterface (UnitToCheck) then
        begin
          FindUsedUnit := LoadUsedUnit;
          Exit;
        end;
      PreviousUnitSegment := UnitToCheck^.PreviousUnitSegment;
      UnitToCheck := Ptr (PreviousUnitSegment, 0);
    until PreviousUnitSegment = Seg (FoundUnit^);
    UnitError (CircularUnitReference);
  end;

{$IFDEF DebugUsedUnits}
  Var LevelCounter: Word;
{$ENDIF}

begin
  SavedUnitPathAndName := UnitPathAndName;
  Inc (UsedUnitLevel);
  BaseUnit := Ptr (LastLoadedUsedUnit, 0);
  LastLoadedUsedUnit := BaseUnit^.PreviousUnitSegment;
  BaseUnit^.PreviousUnitSegment := BaseUnitSegment;
  BaseUnitSegment := Seg (BaseUnit^);
  UnitNameLen := PIdentifier (Ptr (Seg (BaseUnit^), BaseUnit^.UnitNameIdentifierOffset))^.Name.Len;
  UnitIdentifierData := Ptr (Seg (BaseUnit^), BaseUnit^.UnitNameIdentifierOffset + UnitNameLen + 4); { Base unit data }
  LoadedUnitSegment := Seg (BaseUnit^);

{$IFDEF DebugUsedUnits}
  If UsedUnitLevel = 1 then Writeln;
  For LevelCounter := 2 to UsedUnitLevel do Write ('  ');
  If UsedUnitLevel = 1 then Write ('#') else Write (' ');
  Writeln ('LOAD USED UNITS BY: ',
            PIdentifier (Ptr (Seg (BaseUnit^), BaseUnit^.UnitNameIdentifierOffset))^.Name.Str);
{$ENDIF}

  Repeat
    UnitIdentifierData^.UnitSegment := LoadedUnitSegment;
    CurrentUsedUnitIdentifier := UnitIdentifierData^.NextUnitIdentifier;          { Next used unit }
    BaseUnit^.CurrentUsedUnitIdentifierOffset := CurrentUsedUnitIdentifier;
    If CurrentUsedUnitIdentifier = 0 then Break;                                  { No more used units }

{$IFDEF DebugUsedUnits}
    For LevelCounter := 1 to UsedUnitLevel do Write ('  ');
    Writeln (' USES: ', PIdentifier (Ptr (Seg (BaseUnit^), CurrentUsedUnitIdentifier))^.Name.Str);
{$ENDIF}

    LoadedUnit := FindUsedUnit;
    UnitNameLen := PIdentifier (Ptr (Seg (LoadedUnit^), LoadedUnit^.UnitNameIdentifierOffset))^.Name.Len;
    UnitIdentifierData := Ptr (Seg (LoadedUnit^), LoadedUnit^.UnitNameIdentifierOffset + UnitNameLen + 4);

{$IFDEF DebugUsedUnits}
    For LevelCounter := 1 to UsedUnitLevel do Write ('  ');
    Writeln (' LOADED: ', PIdentifier (Ptr (Seg (LoadedUnit^), LoadedUnit^.UnitNameIdentifierOffset))^.Name.Str);
{$ENDIF}

    LoadedUnitPublicIdentifiersChecksum := UnitIdentifierData^.PublicIdentifiersChecksum;
    LoadedUnitSegment := Seg (LoadedUnit^);
    BaseUnit := Ptr (BaseUnitSegment, 0);                                   { BaseUnitSegment might change during unit PUSH}
    UnitNameLen := PIdentifier (Ptr (Seg (BaseUnit^), CurrentUsedUnitIdentifier))^.Name.Len;
    UnitIdentifierData := Ptr (Seg (BaseUnit^), CurrentUsedUnitIdentifier + UnitNameLen + 4);
    UsedUnitPublicIdentifiersChecksum := UnitIdentifierData^.PublicIdentifiersChecksum;
    If UsedUnitPublicIdentifiersChecksum <> 0 then
      begin
        If UsedUnitPublicIdentifiersChecksum <> LoadedUnitPublicIdentifiersChecksum then
          begin
            Inc (CurrentUsedUnitIdentifier, 3);    { name of unit which has different PublicIdentifiersChecksum }
            Break;
          end;
      end else UnitIdentifierData^.PublicIdentifiersChecksum := LoadedUnitPublicIdentifiersChecksum;
  until False;                                                                    { Load next used unit }

  BaseUnitSegment := BaseUnit^.PreviousUnitSegment;
  BaseUnit^.PreviousUnitSegment := LastLoadedUsedUnit;
  LastLoadedUsedUnit := Seg (BaseUnit^);
  Dec (UsedUnitLevel);
  If UsedUnitLevel = Saved_PushedUnitLevel then PopUnit;
  UnitPathAndName := SavedUnitPathAndName;
  LoadUsedUnits := CurrentUsedUnitIdentifier = 0;                                    { All used units loaded }

{$IFDEF DebugUsedUnits}
  For LevelCounter := 1 to UsedUnitLevel do Write ('  ');
  If UsedUnitLevel = 0 then Write ('#') else Write (' ');
  Writeln ('LOAD USED UNITS END ',
             PIdentifier (Ptr (Seg (BaseUnit^), BaseUnit^.UnitNameIdentifierOffset))^.Name.Str);
  If UsedUnitLevel = 0 then Writeln;
{$ENDIF}

end;

PushUnit and PopUnit save and restore current module when compiler is needed to compile used unit.

Procedure PushUnit;
Var ReferencedUnitRecord: PReferencedModulesBlockRecord;
    Segment: Word;
    Block: TSymbolTable;

  Procedure AdjustUnitSegments (Var Segment: Word);
  Var UnitPtr: PUnitHeader;
      UnitIdentifier: PIdentifier;
      UnitIdentifierData: PUnitIdentifierData;
  begin
    If Segment = HeapPtrRec.Seg then Segment := HeapEndRec.Seg;
    UnitPtr := Ptr (Segment, 0);
    While Seg (UnitPtr^) <> 0 do
      begin
        UnitIdentifier := Ptr (Seg (UnitPtr^), UnitPtr^.UnitNameIdentifierOffset);
        Repeat
          UnitIdentifierData := PUnitIdentifierData (PChar (UnitIdentifier) + UnitIdentifier^.Name.Len + 4);
          If UnitIdentifierData^.UnitSegment = HeapPtrRec.Seg then UnitIdentifierData^.UnitSegment := HeapEndRec.Seg;
          UnitIdentifier := Ptr (Seg (UnitPtr^), UnitIdentifierData^.NextUnitIdentifier);
        until Ofs (UnitIdentifier^) = 0;
        If UnitPtr^.PreviousUnitSegment = HeapPtrRec.Seg then UnitPtr^.PreviousUnitSegment := HeapEndRec.Seg;
        UnitPtr := Ptr (UnitPtr^.PreviousUnitSegment, 0);
      end;
  end;

begin
  HeapPtrRec.Seg := SymbolTable [stMain].Segment;                                     { PUSH Memory Blocks }
  Segment := HeapEndRec.Seg;
  For Block := stIntermediateCode downto stMain do
    begin
      SymbolTable [Block].Size := (SymbolTable [Block].UsedSize + $000F) and $FFF0;
      Dec (Segment, SymbolTable [Block].Size shr 4);
      Move (Ptr (SymbolTable [Block].Segment, 0)^, Ptr (Segment, 0)^, SymbolTable [Block].UsedSize);
      SymbolTable [Block].Segment := Segment;
    end;
  HeapEndRec.Seg := Segment;

  AdjustUnitSegments (LastLoadedUsedUnit);
  AdjustUnitSegments (BaseUnitSegment);

  ReferencedUnitRecord := Ptr (SymbolTable [stReferencedModules].Segment, 0);
  While ReferencedUnitRecord <> SymbolTable [stReferencedModules].Ptr do
    begin
      If ReferencedUnitRecord^.ModuleSegment = HeapPtrRec.Seg then ReferencedUnitRecord^.ModuleSegment := HeapEndRec.Seg;
      Inc (PChar (ReferencedUnitRecord), ReferencedUnitRecord^.UnitName.Len + 5);
    end;

  Dec (HeapEndRec.Seg, (Ofs (CurrentLineBuff) - Ofs (CompilerModeOptions) +                   { PUSH Variables }
                        Ofs (ConditionalDefinesEnd^) - Ofs (ConditionalDefines)+ $000F) shr 4);
  Move (CompilerModeOptions, HeapEnd^, Ofs (CurrentLineBuff) - Ofs (CompilerModeOptions));
  Move (ConditionalDefines, Ptr (HeapEndRec.Seg, Ofs (CurrentLineBuff) - Ofs (CompilerModeOptions))^,
                             Ofs (ConditionalDefinesEnd^) - Ofs (ConditionalDefines));        { PUSH Conditional Defines }
end;

Procedure PopUnit;
Var TempHeapPtr, TempHeapEnd: Word;
    ReferencedUnitRecord: PReferencedModulesBlockRecord;
    Segment: Word;
    Block: TSymbolTable;

  Procedure AdjustUnitSegments (Var Segment: Word);
  Var UnitPtr: PUnitHeader;
      UnitIdentifier: PIdentifier;
      UnitIdentifierData: PUnitIdentifierData;
  begin
    If Segment = TempHeapEnd then Segment := TempHeapPtr;
    UnitPtr := Ptr (Segment, 0);
    While Seg (UnitPtr^) <> 0 do
      begin
        UnitIdentifier := Ptr (Seg (UnitPtr^), UnitPtr^.UnitNameIdentifierOffset);
        Repeat
          UnitIdentifierData := PUnitIdentifierData (PChar (UnitIdentifier) + UnitIdentifier^.Name.Len + 4);
          If UnitIdentifierData^.UnitSegment = TempHeapEnd then UnitIdentifierData^.UnitSegment := TempHeapPtr;
          UnitIdentifier := Ptr (Seg (UnitPtr^), UnitIdentifierData^.NextUnitIdentifier);
        until Ofs (UnitIdentifier^) = 0;
        If UnitPtr^.PreviousUnitSegment = TempHeapEnd then UnitPtr^.PreviousUnitSegment := TempHeapPtr;
        UnitPtr := Ptr (UnitPtr^.PreviousUnitSegment, 0);
      end;
  end;

begin
  Move (HeapEnd^, CompilerModeOptions, Ofs (CurrentLineBuff) - Ofs (CompilerModeOptions));    { POP Variables }
  Move (Ptr (HeapEndRec.Seg, Ofs (CurrentLineBuff) - Ofs (CompilerModeOptions))^, ConditionalDefines,
          Ofs (ConditionalDefinesEnd^) - Ofs (ConditionalDefines));                           { POP Conditional Defines }
  TempHeapEnd := HeapEndRec.Seg + (Ofs (CurrentLineBuff) - Ofs (CompilerModeOptions) +
                   Ofs (ConditionalDefinesEnd^) - Ofs (ConditionalDefines) + $000F) shr 4;

  TempHeapPtr := HeapPtrRec.Seg;

  HeapEndRec.Seg := SymbolTable [stIntermediateCode].Segment + SymbolTable [stIntermediateCode].Size shr 4;
  Segment := HeapPtrRec.Seg;
  For Block := stMain to stIntermediateCode do
    begin
      Move (Ptr (SymbolTable [Block].Segment, 0)^, Ptr (Segment, 0)^, SymbolTable [Block].UsedSize);
      SymbolTable [Block].Segment := Segment;
      Inc (Segment, (SymbolTable [Block].UsedSize + $000F) shr 4);
    end;
  HeapPtrRec.Seg := Segment;

  AdjustUnitSegments (LastLoadedUsedUnit);
  AdjustUnitSegments (BaseUnitSegment);

  ReferencedUnitRecord := Ptr (SymbolTable [stReferencedModules].Segment, 0);
  While ReferencedUnitRecord <> SymbolTable [stReferencedModules].Ptr do
    begin
      If ReferencedUnitRecord^.ModuleSegment = TempHeapEnd then ReferencedUnitRecord^.ModuleSegment := TempHeapPtr;
      Inc (PChar (ReferencedUnitRecord), ReferencedUnitRecord^.UnitName.Len + 5);
    end;
end;