| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- E X P _ C G -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2010-2014, Free Software Foundation, Inc. -- |
| -- -- |
| -- 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 3, 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 COPYING3. If not, go to -- |
| -- http://www.gnu.org/licenses for a complete copy of the license. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Atree; use Atree; |
| with Einfo; use Einfo; |
| with Elists; use Elists; |
| with Exp_Disp; use Exp_Disp; |
| with Exp_Dbug; use Exp_Dbug; |
| with Exp_Tss; use Exp_Tss; |
| with Lib; use Lib; |
| with Namet; use Namet; |
| with Opt; use Opt; |
| with Output; use Output; |
| with Sem_Aux; use Sem_Aux; |
| with Sem_Disp; use Sem_Disp; |
| with Sem_Type; use Sem_Type; |
| with Sem_Util; use Sem_Util; |
| with Sinfo; use Sinfo; |
| with Sinput; use Sinput; |
| with Snames; use Snames; |
| with System; use System; |
| with Table; |
| with Uintp; use Uintp; |
| |
| package body Exp_CG is |
| |
| -- We duplicate here some declarations from packages Interfaces.C and |
| -- Interfaces.C_Streams because adding their dependence to the frontend |
| -- causes bootstrapping problems with old versions of the compiler. |
| |
| subtype FILEs is System.Address; |
| -- Corresponds to the C type FILE* |
| |
| subtype C_chars is System.Address; |
| -- Pointer to null-terminated array of characters |
| |
| function fputs (Strng : C_chars; Stream : FILEs) return Integer; |
| pragma Import (C, fputs, "fputs"); |
| |
| -- Import the file stream associated with the "ci" output file. Done to |
| -- generate the output in the file created and left opened by routine |
| -- toplev.c before calling gnat1drv. |
| |
| Callgraph_Info_File : FILEs; |
| pragma Import (C, Callgraph_Info_File); |
| |
| package Call_Graph_Nodes is new Table.Table ( |
| Table_Component_Type => Node_Id, |
| Table_Index_Type => Natural, |
| Table_Low_Bound => 1, |
| Table_Initial => 50, |
| Table_Increment => 100, |
| Table_Name => "Call_Graph_Nodes"); |
| -- This table records nodes associated with dispatching calls and tagged |
| -- type declarations found in the main compilation unit. Used as an |
| -- auxiliary storage because the call-graph output requires fully qualified |
| -- names and they are not available until the backend is called. |
| |
| function Is_Predefined_Dispatching_Operation (E : Entity_Id) return Boolean; |
| -- Determines if E is a predefined primitive operation. |
| -- Note: This routine should replace the routine with the same name that is |
| -- currently available in exp_disp because it extends its functionality to |
| -- handle fully qualified names ??? |
| |
| function Slot_Number (Prim : Entity_Id) return Uint; |
| -- Returns the slot number associated with Prim. For predefined primitives |
| -- the slot is returned as a negative number. |
| |
| procedure Write_Output (Str : String); |
| -- Used to print a line in the output file (this is used as the |
| -- argument for a call to Set_Special_Output in package Output). |
| |
| procedure Write_Call_Info (Call : Node_Id); |
| -- Subsidiary of Generate_CG_Output that generates the output associated |
| -- with a dispatching call. |
| |
| procedure Write_Type_Info (Typ : Entity_Id); |
| -- Subsidiary of Generate_CG_Output that generates the output associated |
| -- with a tagged type declaration. |
| |
| ------------------------ |
| -- Generate_CG_Output -- |
| ------------------------ |
| |
| procedure Generate_CG_Output is |
| N : Node_Id; |
| |
| begin |
| -- No output if the "ci" output file has not been previously opened |
| -- by toplev.c |
| |
| if Callgraph_Info_File = Null_Address then |
| return; |
| end if; |
| |
| -- Setup write routine, create the output file and generate the output |
| |
| Set_Special_Output (Write_Output'Access); |
| |
| for J in Call_Graph_Nodes.First .. Call_Graph_Nodes.Last loop |
| N := Call_Graph_Nodes.Table (J); |
| |
| if Nkind (N) in N_Subprogram_Call then |
| Write_Call_Info (N); |
| |
| else pragma Assert (Nkind (N) = N_Defining_Identifier); |
| |
| -- The type may be a private untagged type whose completion is |
| -- tagged, in which case we must use the full tagged view. |
| |
| if not Is_Tagged_Type (N) and then Is_Private_Type (N) then |
| N := Full_View (N); |
| end if; |
| |
| pragma Assert (Is_Tagged_Type (N)); |
| |
| Write_Type_Info (N); |
| end if; |
| end loop; |
| |
| Set_Special_Output (null); |
| end Generate_CG_Output; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize is |
| begin |
| Call_Graph_Nodes.Init; |
| end Initialize; |
| |
| ----------------------------------------- |
| -- Is_Predefined_Dispatching_Operation -- |
| ----------------------------------------- |
| |
| function Is_Predefined_Dispatching_Operation |
| (E : Entity_Id) return Boolean |
| is |
| function Homonym_Suffix_Length (E : Entity_Id) return Natural; |
| -- Returns the length of the homonym suffix corresponding to E. |
| -- Note: This routine relies on the functionality provided by routines |
| -- of Exp_Dbug. Further work needed here to decide if it should be |
| -- located in that package??? |
| |
| --------------------------- |
| -- Homonym_Suffix_Length -- |
| --------------------------- |
| |
| function Homonym_Suffix_Length (E : Entity_Id) return Natural is |
| Prefix_Length : constant := 2; |
| -- Length of prefix "__" |
| |
| H : Entity_Id; |
| Nr : Nat := 1; |
| |
| begin |
| if not Has_Homonym (E) then |
| return 0; |
| |
| else |
| H := Homonym (E); |
| while Present (H) loop |
| if Scope (H) = Scope (E) then |
| Nr := Nr + 1; |
| end if; |
| |
| H := Homonym (H); |
| end loop; |
| |
| if Nr = 1 then |
| return 0; |
| |
| -- Prefix "__" followed by number |
| |
| else |
| declare |
| Result : Natural := Prefix_Length + 1; |
| |
| begin |
| while Nr >= 10 loop |
| Result := Result + 1; |
| Nr := Nr / 10; |
| end loop; |
| |
| return Result; |
| end; |
| end if; |
| end if; |
| end Homonym_Suffix_Length; |
| |
| -- Local variables |
| |
| Full_Name : constant String := Get_Name_String (Chars (E)); |
| Suffix_Length : Natural; |
| TSS_Name : TSS_Name_Type; |
| |
| -- Start of processing for Is_Predefined_Dispatching_Operation |
| |
| begin |
| if not Is_Dispatching_Operation (E) then |
| return False; |
| end if; |
| |
| -- Search for and strip suffix for body-nested package entities |
| |
| Suffix_Length := Homonym_Suffix_Length (E); |
| for J in reverse Full_Name'First + 2 .. Full_Name'Last loop |
| if Full_Name (J) = 'X' then |
| |
| -- Include the "X", "Xb", "Xn", ... in the part of the |
| -- suffix to be removed. |
| |
| Suffix_Length := Suffix_Length + Full_Name'Last - J + 1; |
| exit; |
| end if; |
| |
| exit when Full_Name (J) /= 'b' and then Full_Name (J) /= 'n'; |
| end loop; |
| |
| -- Most predefined primitives have internally generated names. Equality |
| -- must be treated differently; the predefined operation is recognized |
| -- as a homogeneous binary operator that returns Boolean. |
| |
| if Full_Name'Length > TSS_Name_Type'Length then |
| TSS_Name := |
| TSS_Name_Type |
| (Full_Name |
| (Full_Name'Last - TSS_Name'Length - Suffix_Length + 1 |
| .. Full_Name'Last - Suffix_Length)); |
| |
| if TSS_Name = TSS_Stream_Read |
| or else TSS_Name = TSS_Stream_Write |
| or else TSS_Name = TSS_Stream_Input |
| or else TSS_Name = TSS_Stream_Output |
| or else TSS_Name = TSS_Deep_Adjust |
| or else TSS_Name = TSS_Deep_Finalize |
| then |
| return True; |
| |
| elsif not Has_Fully_Qualified_Name (E) then |
| if Nam_In (Chars (E), Name_uSize, Name_uAlignment, Name_uAssign) |
| or else |
| (Chars (E) = Name_Op_Eq |
| and then Etype (First_Formal (E)) = Etype (Last_Formal (E))) |
| or else Is_Predefined_Interface_Primitive (E) |
| then |
| return True; |
| end if; |
| |
| -- Handle fully qualified names |
| |
| else |
| declare |
| type Names_Table is array (Positive range <>) of Name_Id; |
| |
| Predef_Names_95 : constant Names_Table := |
| (Name_uSize, |
| Name_uAlignment, |
| Name_Op_Eq, |
| Name_uAssign); |
| |
| Predef_Names_05 : constant Names_Table := |
| (Name_uDisp_Asynchronous_Select, |
| Name_uDisp_Conditional_Select, |
| Name_uDisp_Get_Prim_Op_Kind, |
| Name_uDisp_Get_Task_Id, |
| Name_uDisp_Requeue, |
| Name_uDisp_Timed_Select); |
| |
| begin |
| for J in Predef_Names_95'Range loop |
| Get_Name_String (Predef_Names_95 (J)); |
| |
| -- The predefined primitive operations are identified by the |
| -- names "_size", "_alignment", etc. If we try a pattern |
| -- matching against this string, we can wrongly match other |
| -- primitive operations like "get_size". To avoid this, we |
| -- add the "__" scope separator, which can only prepend |
| -- predefined primitive operations because other primitive |
| -- operations can neither start with an underline nor |
| -- contain two consecutive underlines in its name. |
| |
| if Full_Name'Last - Suffix_Length > Name_Len + 2 |
| and then |
| Full_Name |
| (Full_Name'Last - Name_Len - 2 - Suffix_Length + 1 |
| .. Full_Name'Last - Suffix_Length) = |
| "__" & Name_Buffer (1 .. Name_Len) |
| then |
| -- For the equality operator the type of the two operands |
| -- must also match. |
| |
| return Predef_Names_95 (J) /= Name_Op_Eq |
| or else |
| Etype (First_Formal (E)) = Etype (Last_Formal (E)); |
| end if; |
| end loop; |
| |
| if Ada_Version >= Ada_2005 then |
| for J in Predef_Names_05'Range loop |
| Get_Name_String (Predef_Names_05 (J)); |
| |
| if Full_Name'Last - Suffix_Length > Name_Len + 2 |
| and then |
| Full_Name |
| (Full_Name'Last - Name_Len - 2 - Suffix_Length + 1 |
| .. Full_Name'Last - Suffix_Length) = |
| "__" & Name_Buffer (1 .. Name_Len) |
| then |
| return True; |
| end if; |
| end loop; |
| end if; |
| end; |
| end if; |
| end if; |
| |
| return False; |
| end Is_Predefined_Dispatching_Operation; |
| |
| ---------------------- |
| -- Register_CG_Node -- |
| ---------------------- |
| |
| procedure Register_CG_Node (N : Node_Id) is |
| begin |
| if Nkind (N) in N_Subprogram_Call then |
| if Current_Scope = Main_Unit_Entity |
| or else Entity_Is_In_Main_Unit (Current_Scope) |
| then |
| -- Register a copy of the dispatching call node. Needed since the |
| -- node containing a dispatching call is rewritten by the |
| -- expander. |
| |
| declare |
| Copy : constant Node_Id := New_Copy (N); |
| Par : Node_Id; |
| |
| begin |
| -- Determine the enclosing scope to use when generating the |
| -- call graph. This must be done now to avoid problems with |
| -- control structures that may be rewritten during expansion. |
| |
| Par := Parent (N); |
| while Nkind (Par) /= N_Subprogram_Body |
| and then Nkind (Parent (Par)) /= N_Compilation_Unit |
| loop |
| Par := Parent (Par); |
| pragma Assert (Present (Par)); |
| end loop; |
| |
| Set_Parent (Copy, Par); |
| Call_Graph_Nodes.Append (Copy); |
| end; |
| end if; |
| |
| else pragma Assert (Nkind (N) = N_Defining_Identifier); |
| if Entity_Is_In_Main_Unit (N) then |
| Call_Graph_Nodes.Append (N); |
| end if; |
| end if; |
| end Register_CG_Node; |
| |
| ----------------- |
| -- Slot_Number -- |
| ----------------- |
| |
| function Slot_Number (Prim : Entity_Id) return Uint is |
| E : constant Entity_Id := Ultimate_Alias (Prim); |
| begin |
| if Is_Predefined_Dispatching_Operation (E) then |
| return -DT_Position (E); |
| else |
| return DT_Position (E); |
| end if; |
| end Slot_Number; |
| |
| ------------------ |
| -- Write_Output -- |
| ------------------ |
| |
| procedure Write_Output (Str : String) is |
| Nul : constant Character := Character'First; |
| Line : String (Str'First .. Str'Last + 1); |
| Errno : Integer; |
| |
| begin |
| -- Add the null character to the string as required by fputs |
| |
| Line := Str & Nul; |
| Errno := fputs (Line'Address, Callgraph_Info_File); |
| pragma Assert (Errno >= 0); |
| end Write_Output; |
| |
| --------------------- |
| -- Write_Call_Info -- |
| --------------------- |
| |
| procedure Write_Call_Info (Call : Node_Id) is |
| Ctrl_Arg : constant Node_Id := Controlling_Argument (Call); |
| Ctrl_Typ : constant Entity_Id := Base_Type (Etype (Ctrl_Arg)); |
| Prim : constant Entity_Id := Entity (Sinfo.Name (Call)); |
| P : constant Node_Id := Parent (Call); |
| |
| begin |
| Write_Str ("edge: { sourcename: "); |
| Write_Char ('"'); |
| |
| -- The parent node is the construct that contains the call: subprogram |
| -- body or library-level package. Display the qualified name of the |
| -- entity of the construct. For a subprogram, it is the entity of the |
| -- spec, which carries a homonym counter when it is overloaded. |
| |
| if Nkind (P) = N_Subprogram_Body |
| and then not Acts_As_Spec (P) |
| then |
| Get_External_Name (Corresponding_Spec (P)); |
| |
| else |
| Get_External_Name (Defining_Entity (P)); |
| end if; |
| |
| Write_Str (Name_Buffer (1 .. Name_Len)); |
| |
| if Nkind (P) = N_Package_Declaration then |
| Write_Str ("___elabs"); |
| |
| elsif Nkind (P) = N_Package_Body then |
| Write_Str ("___elabb"); |
| end if; |
| |
| Write_Char ('"'); |
| Write_Eol; |
| |
| -- The targetname is a triple: |
| -- N: the index in a vtable used for dispatch |
| -- V: the type who's vtable is used |
| -- S: the static type of the expression |
| |
| Write_Str (" targetname: "); |
| Write_Char ('"'); |
| |
| pragma Assert (No (Interface_Alias (Prim))); |
| |
| -- The check on Is_Ancestor is done here to avoid problems with |
| -- renamings of primitives. For example: |
| |
| -- type Root is tagged ... |
| -- procedure Base (Obj : Root); |
| -- procedure Base2 (Obj : Root) renames Base; |
| |
| if Present (Alias (Prim)) |
| and then |
| Is_Ancestor |
| (Find_Dispatching_Type (Ultimate_Alias (Prim)), |
| Root_Type (Ctrl_Typ), |
| Use_Full_View => True) |
| then |
| -- This is a special case in which we generate in the ci file the |
| -- slot number of the renaming primitive (i.e. Base2) but instead of |
| -- generating the name of this renaming entity we reference directly |
| -- the renamed entity (i.e. Base). |
| |
| Write_Int (UI_To_Int (Slot_Number (Prim))); |
| Write_Char (':'); |
| Write_Name |
| (Chars (Find_Dispatching_Type (Ultimate_Alias (Prim)))); |
| else |
| Write_Int (UI_To_Int (Slot_Number (Prim))); |
| Write_Char (':'); |
| Write_Name (Chars (Root_Type (Ctrl_Typ))); |
| end if; |
| |
| Write_Char (','); |
| Write_Name (Chars (Root_Type (Ctrl_Typ))); |
| |
| Write_Char ('"'); |
| Write_Eol; |
| |
| Write_Str (" label: "); |
| Write_Char ('"'); |
| Write_Location (Sloc (Call)); |
| Write_Char ('"'); |
| Write_Eol; |
| |
| Write_Char ('}'); |
| Write_Eol; |
| end Write_Call_Info; |
| |
| --------------------- |
| -- Write_Type_Info -- |
| --------------------- |
| |
| procedure Write_Type_Info (Typ : Entity_Id) is |
| Elmt : Elmt_Id; |
| Prim : Node_Id; |
| |
| Parent_Typ : Entity_Id; |
| Separator_Needed : Boolean := False; |
| |
| begin |
| -- Initialize Parent_Typ handling private types |
| |
| Parent_Typ := Etype (Typ); |
| |
| if Present (Full_View (Parent_Typ)) then |
| Parent_Typ := Full_View (Parent_Typ); |
| end if; |
| |
| Write_Str ("class {"); |
| Write_Eol; |
| |
| Write_Str (" classname: "); |
| Write_Char ('"'); |
| Write_Name (Chars (Typ)); |
| Write_Char ('"'); |
| Write_Eol; |
| |
| Write_Str (" label: "); |
| Write_Char ('"'); |
| Write_Name (Chars (Typ)); |
| Write_Char ('\'); |
| Write_Location (Sloc (Typ)); |
| Write_Char ('"'); |
| Write_Eol; |
| |
| if Parent_Typ /= Typ then |
| Write_Str (" parent: "); |
| Write_Char ('"'); |
| Write_Name (Chars (Parent_Typ)); |
| |
| -- Note: Einfo prefix not needed if this routine is moved to |
| -- exp_disp??? |
| |
| if Present (Einfo.Interfaces (Typ)) |
| and then not Is_Empty_Elmt_List (Einfo.Interfaces (Typ)) |
| then |
| Elmt := First_Elmt (Einfo.Interfaces (Typ)); |
| while Present (Elmt) loop |
| Write_Str (", "); |
| Write_Name (Chars (Node (Elmt))); |
| Next_Elmt (Elmt); |
| end loop; |
| end if; |
| |
| Write_Char ('"'); |
| Write_Eol; |
| end if; |
| |
| Write_Str (" virtuals: "); |
| Write_Char ('"'); |
| |
| Elmt := First_Elmt (Primitive_Operations (Typ)); |
| while Present (Elmt) loop |
| Prim := Node (Elmt); |
| |
| -- Skip internal entities associated with overridden interface |
| -- primitives, and also inherited primitives. |
| |
| if Present (Interface_Alias (Prim)) |
| or else |
| (Present (Alias (Prim)) |
| and then Find_Dispatching_Type (Prim) /= |
| Find_Dispatching_Type (Alias (Prim))) |
| then |
| goto Continue; |
| end if; |
| |
| -- Do not generate separator for output of first primitive |
| |
| if Separator_Needed then |
| Write_Str ("\n"); |
| Write_Eol; |
| Write_Str (" "); |
| else |
| Separator_Needed := True; |
| end if; |
| |
| Write_Int (UI_To_Int (Slot_Number (Prim))); |
| Write_Char (':'); |
| |
| -- Handle renamed primitives |
| |
| if Present (Alias (Prim)) then |
| Write_Name (Chars (Ultimate_Alias (Prim))); |
| else |
| Write_Name (Chars (Prim)); |
| end if; |
| |
| -- Display overriding of parent primitives |
| |
| if Present (Overridden_Operation (Prim)) |
| and then |
| Is_Ancestor |
| (Find_Dispatching_Type (Overridden_Operation (Prim)), Typ, |
| Use_Full_View => True) |
| then |
| Write_Char (','); |
| Write_Int |
| (UI_To_Int (Slot_Number (Overridden_Operation (Prim)))); |
| Write_Char (':'); |
| Write_Name |
| (Chars (Find_Dispatching_Type (Overridden_Operation (Prim)))); |
| end if; |
| |
| -- Display overriding of interface primitives |
| |
| if Has_Interfaces (Typ) then |
| declare |
| Prim_Elmt : Elmt_Id; |
| Prim_Op : Node_Id; |
| Int_Alias : Entity_Id; |
| |
| begin |
| Prim_Elmt := First_Elmt (Primitive_Operations (Typ)); |
| while Present (Prim_Elmt) loop |
| Prim_Op := Node (Prim_Elmt); |
| Int_Alias := Interface_Alias (Prim_Op); |
| |
| if Present (Int_Alias) |
| and then |
| not Is_Ancestor (Find_Dispatching_Type (Int_Alias), Typ, |
| Use_Full_View => True) |
| and then (Alias (Prim_Op)) = Prim |
| then |
| Write_Char (','); |
| Write_Int (UI_To_Int (Slot_Number (Int_Alias))); |
| Write_Char (':'); |
| Write_Name (Chars (Find_Dispatching_Type (Int_Alias))); |
| end if; |
| |
| Next_Elmt (Prim_Elmt); |
| end loop; |
| end; |
| end if; |
| |
| <<Continue>> |
| Next_Elmt (Elmt); |
| end loop; |
| |
| Write_Char ('"'); |
| Write_Eol; |
| |
| Write_Char ('}'); |
| Write_Eol; |
| end Write_Type_Info; |
| |
| end Exp_CG; |