home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
gnat-2.06-src.tgz
/
tar.out
/
fsf
/
gnat
/
ada
/
lib-load.adb
< prev
next >
Wrap
Text File
|
1996-09-28
|
15KB
|
414 lines
------------------------------------------------------------------------------
-- --
-- GNAT COMPILER COMPONENTS --
-- --
-- L I B . L O A D --
-- --
-- B o d y --
-- --
-- $Revision: 1.43 $ --
-- --
-- Copyright (c) 1992,1993,1994,1995 NYU, All Rights Reserved --
-- --
-- GNAT is free software; you can redistribute it and/or modify it under --
-- terms of the GNU General Public License as published by the Free Soft- --
-- ware Foundation; either version 2, or (at your option) any later ver- --
-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
-- for more details. You should have received a copy of the GNU General --
-- Public License distributed with GNAT; see file COPYING. If not, write --
-- to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. --
-- --
------------------------------------------------------------------------------
with Atree; use Atree;
with Debug; use Debug;
with Errout; use Errout;
with Fname; use Fname;
with Osint; use Osint;
with Output; use Output;
with Par;
with Scn; use Scn;
with Sinfo; use Sinfo;
with Sinput; use Sinput;
with Sinput.L; use Sinput.L;
with Uname; use Uname;
package body Lib.Load is
-----------------------
-- Local Subprograms --
-----------------------
procedure Write_Dependency_Chain;
-- This procedure is used to generate error message info lines that
-- trace the current dependency chain when a load error occurs.
function Version_Init (U : Unit_Number_Type) return Version_Id;
-- Calculate initial value of version from time stamp value
----------------
-- Initialize --
----------------
procedure Initialize is
Fname : File_Name_Type;
begin
Units.Init;
Load_Stack.Init;
Load_Stack.Increment_Last;
Load_Stack.Table (Load_Stack.Last) := Main_Unit;
-- Initialize unit table entry for Main_Unit. Note that we don't know
-- the unit name yet, that gets filled in when the parser parses the
-- main unit, at which time a check is made that it matches the main
-- file name, and then the Unit_Name field is set. The Cunit and
-- Cunit_Entity fields also get filled in later by the parser.
Units.Increment_Last;
Fname := Next_Main_Source;
Units.Table (Main_Unit).Unit_File_Name := Fname;
if Fname /= No_File then
Units.Table (Main_Unit).Unit_Name := No_Name;
Units.Table (Main_Unit).Expected_Unit := No_Name;
Units.Table (Main_Unit).Source_Index := Load_Source_File (Fname);
Units.Table (Main_Unit).Loading := True;
Units.Table (Main_Unit).Cunit := Empty;
Units.Table (Main_Unit).Cunit_Entity := Empty;
Units.Table (Main_Unit).Fatal_Error := False;
Units.Table (Main_Unit).Generate_Code := False;
Units.Table (Main_Unit).Main_Priority := Default_Main_Priority;
Units.Table (Main_Unit).Version := Version_Init (Main_Unit);
Units.Table (Main_Unit).Serial_Number := 0;
end if;
end Initialize;
---------------
-- Load_Unit --
---------------
function Load_Unit
(Uname : Unit_Name_Type;
Required : Boolean;
Enode : Node_Id)
return Unit_Number_Type
is
Calling_Unit : Unit_Number_Type;
Unum : Unit_Number_Type;
Fname : File_Name_Type := Get_File_Name (Uname);
Src_Ind : Source_File_Index;
begin
if Debug_Flag_L then
Write_Str ("*** Load request for unit: ");
Write_Unit_Name (Uname);
if Required then
Write_Str (" (Required = True)");
else
Write_Str (" (Required = False)");
end if;
Write_Eol;
end if;
-- Capture error location if it is for the main unit. The idea is to
-- post errors on the main unit location, not the most recent unit.
if Present (Enode)
and then Get_Sloc_Unit_Number (Sloc (Enode)) = Main_Unit
then
Load_Msg_Sloc := Sloc (Enode);
end if;
-- If we are generating error messages, then capture calling unit
if Present (Enode) then
Calling_Unit := Get_Sloc_Unit_Number (Sloc (Enode));
end if;
-- See if we already have an entry for this unit
Unum := Main_Unit;
while Unum <= Units.Last loop
exit when Uname = Units.Table (Unum).Unit_Name;
Unum := Unum + 1;
end loop;
-- Whether or not the entry was found, Unum is now the right value,
-- since it is one more than Units.Last (i.e. the index of the new
-- entry we will create) in the not found case.
-- A special check is necessary in the unit not found case. If the unit
-- is not found, but the file in which it lives has already been loaded,
-- then we have the problem that the file does not contain the unit that
-- is needed. We simply treat this as a file not found condition.
if Unum > Units.Last then
for J in Units.First .. Units.Last loop
if Fname = Units.Table (J).Unit_File_Name then
if Debug_Flag_L then
Write_Str ("*** File does not contain unit, Unit_Number = ");
Write_Int (Int (Unum));
Write_Eol;
end if;
if Present (Enode) then
Error_Msg_Unit_1 := Uname;
if Is_Language_Defined_Unit (Fname) then
Error_Msg
("$ is not a language defined unit", Load_Msg_Sloc);
else
Error_Msg_Name_1 := Fname;
Error_Msg
("File{ does not contain unit$", Load_Msg_Sloc);
end if;
Write_Dependency_Chain;
raise Unrecoverable_Error;
else
return No_Unit;
end if;
end if;
end loop;
end if;
-- If we are proceeding with load, then make load stack entry
Load_Stack.Increment_Last;
Load_Stack.Table (Load_Stack.Last) := Unum;
-- Case of entry already in table
if Unum <= Units.Last then
-- Here is where we check for a circular dependency, which is
-- an attempt to load a unit which is currently in the process
-- of being loaded. We do *not* care about a circular chain that
-- leads back to a body, because this kind of circular dependence
-- legitimately occurs (e.g. two package bodies that contain
-- inlined subprogram referenced by the other).
if Loading (Unum)
and then (Is_Spec_Name (Units.Table (Unum).Unit_Name)
or else Acts_As_Spec (Units.Table (Unum).Cunit))
then
if Debug_Flag_L then
Write_Str ("*** Circular dependency encountered");
Write_Eol;
end if;
if Present (Enode) then
Error_Msg ("Circular unit dependency", Load_Msg_Sloc);
Write_Dependency_Chain;
raise Unrecoverable_Error;
else
Load_Stack.Decrement_Last;
return No_Unit;
end if;
end if;
if Debug_Flag_L then
Write_Str ("*** Unit already in file table, Unit_Number = ");
Write_Int (Int (Unum));
Write_Eol;
end if;
Load_Stack.Decrement_Last;
return Unum;
-- File is not already in table, so try to open it
else
Src_Ind := Load_Source_File (Fname);
-- Make a partial entry in the file table, used even in the file not
-- found case to print the dependency chain including the last entry
Units.Increment_Last;
Units.Table (Unum).Unit_Name := Uname;
-- File was found
if Src_Ind /= No_Source_File then
if Debug_Flag_L then
Write_Str ("*** Building new unit table entry, Unit_Number = ");
Write_Int (Int (Unum));
Write_Eol;
end if;
Units.Table (Unum).Expected_Unit := Uname;
Units.Table (Unum).Unit_File_Name := Fname;
Units.Table (Unum).Source_Index := Src_Ind;
Units.Table (Unum).Cunit := Empty;
Units.Table (Unum).Cunit_Entity := Empty;
Units.Table (Unum).Fatal_Error := False;
Units.Table (Unum).Generate_Code := False;
Units.Table (Unum).Main_Priority := Default_Main_Priority;
Units.Table (Unum).Serial_Number := 0;
Units.Table (Unum).Version := Version_Init (Unum);
-- Parse the new unit
Set_Loading (Unum, True);
Initialize_Scanner (Unum);
Par;
Set_Loading (Unum, False);
if Debug_Flag_L then
Write_Str ("*** Load completed successfully, Unit_Number = ");
Write_Int (Int (Unum));
Write_Eol;
end if;
-- If loaded unit had a fatal error, then caller inherits it!
if Units.Table (Unum).Fatal_Error
and then Present (Enode)
then
Units.Table (Calling_Unit).Fatal_Error := True;
end if;
-- Remove load stack entry and return the entry in the file table
Load_Stack.Decrement_Last;
return Unum;
-- Case of file not found
else
if Debug_Flag_L then
Write_Str ("*** File was not found, Unit_Number = ");
Write_Int (Int (Unum));
Write_Eol;
end if;
-- Generate message if unit required
if Required and then Present (Enode) then
if Is_Language_Defined_Unit (Fname) then
Error_Msg_Unit_1 := Uname;
Error_Msg
("$ is not a language defined unit", Load_Msg_Sloc);
else
Error_Msg_Name_1 := Fname;
Error_Msg ("file{ not found", Load_Msg_Sloc);
end if;
Write_Dependency_Chain;
raise Unrecoverable_Error;
-- If unit not required, remove load stack entry and the junk
-- file table entry, and return No_Unit to indicate not found,
else
Load_Stack.Decrement_Last;
Units.Decrement_Last;
return No_Unit;
end if;
end if;
end if;
end Load_Unit;
------------------------
-- Make_Instance_Unit --
------------------------
-- If the unit is an instance, it appears as a package declaration, but
-- contains both declaration and body of the instance. The body becomes
-- the main unit of the compilation, and the declaration is inserted
-- at the end of the unit table. The main unit now has the name of a
-- body, which is constructed from the name of the original spec,
-- and is attached to the compilation node of the original unit.
-- The declaration has been attached to a new compilation unit node, and
-- code will have to be generated for it.
procedure Make_Instance_Unit (N : Node_Id) is
begin
Units.Increment_Last;
Units.Table (Units.Last) := Units.Table (Main_Unit);
Units.Table (Units.Last).Cunit := Library_Unit (N);
Units.Table (Units.Last).Generate_Code := True;
Units.Table (Main_Unit).Cunit := N;
Units.Table (Main_Unit).Unit_Name
:= Get_Body_Name (Get_Unit_Name (Unit (Library_Unit (N))));
Units.Table (Main_Unit).Version := Version_Init (Main_Unit);
end Make_Instance_Unit;
------------------
-- Version_Init --
------------------
function Version_Init (U : Unit_Number_Type) return Version_Id is
TS : constant Time_Stamp_Type := Time_Stamp (Source_Index (U));
V : Version_Id := 0;
begin
for J in TS'Range loop
V := V * 8;
V := V + (Character'Pos (TS (J)) - Character'Pos ('0'));
end loop;
return V;
end Version_Init;
--------------------
-- Version_Update --
--------------------
procedure Version_Update (U : Node_Id; From : Node_Id) is
Unum : constant Unit_Number_Type := Get_Cunit_Unit_Number (U);
Fnum : constant Unit_Number_Type := Get_Cunit_Unit_Number (From);
UV : Version_Id;
FV : Version_Id;
Carry : Boolean;
begin
-- The hash code is evolved by doing a 1's complement addition, i.e.
-- an add with an end around carry (we don't want bits wandering away
-- at the high order end.
-- Note that an xor would be inappropriate, because the same unit can
-- get included in the hash sum many times (for example if this unit
-- with's two units that are themselves both semantically dependent
-- on a third unit), so if we did an xor, and there was an even number
-- of such duplications we would lose the contribution from that unit.
UV := Units.Table (Unum).Version;
FV := Units.Table (Fnum).Version;
Carry := (UV >= (2 ** 31)) and then (FV >= (2 ** 31));
Units.Table (Unum).Version := UV + FV + Boolean'Pos (Carry);
end Version_Update;
----------------------------
-- Write_Dependency_Chain --
----------------------------
procedure Write_Dependency_Chain is
begin
-- The dependency chain is only written if it is at least two entries
-- deep, otherwise it is trivial (the main unit depending on a unit
-- that it obviously directly depends on).
if Load_Stack.Last - 1 > Load_Stack.First then
for U in Load_Stack.First .. Load_Stack.Last - 1 loop
Error_Msg_Unit_1 := Unit_Name (Load_Stack.Table (U));
Error_Msg_Unit_2 := Unit_Name (Load_Stack.Table (U + 1));
Error_Msg ("$ depends on $!", Load_Msg_Sloc);
end loop;
end if;
end Write_Dependency_Chain;
end Lib.Load;