------------------------------------------------------------------------------
--                                                                          --
--                         GNAT COMPILER COMPONENTS                         --
--                                                                          --
--    S Y S T E M . F I N A L I Z A T I O N _ I M P L E M E N T A T I O N   --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--                            $Revision: 1.23 $                             --
--                                                                          --
--     Copyright (C) 1992,1993,1994,1995 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 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,  59 Temple Place - Suite 330,  Boston, --
-- MA 02111-1307, USA.                                                      --
--                                                                          --
-- As a special exception,  if other files  instantiate  generics from this --
-- unit, or you link  this unit with other files  to produce an executable, --
-- this  unit  does not  by itself cause  the resulting  executable  to  be --
-- covered  by the  GNU  General  Public  License.  This exception does not --
-- however invalidate  any other reasons why  the executable file  might be --
-- covered by the  GNU Public License.                                      --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- It is now maintained by Ada Core Technologies Inc (http://www.gnat.com). --
--                                                                          --
------------------------------------------------------------------------------

with Ada.Exceptions;
with Ada.Finalization;
with Ada.Finalization.List_Controller;
with Ada.Unchecked_Conversion;
with System.Storage_Elements;

package body System.Finalization_Implementation is

   use Ada.Finalization;
   use Ada.Finalization.List_Controller;
   use Ada.Exceptions;
   use System.Finalization_Root;
   use System.Storage_Elements;

   --------------------------
   -- Attach_To_Final_List --
   --------------------------

   procedure Attach_To_Final_List
     (L   : in out Finalizable_Ptr;
      Obj : in out Finalizable)
   is
      Obj_Ref : constant Finalizable_Ptr := Obj'Unchecked_Access;
   begin
      if L = null then
         Obj.Next := null;
         Obj.Prev := null;
         L        := Obj_Ref;

      --  First attachment on a list of dynamically allocated objects. The
      --  access to the list_controller is kept in the PREV of the first
      --  element.

      elsif Is_Empty_List_Controller (L)  then
         Obj.Next := null;
         Obj.Prev := L;
         L        := Obj_Ref;

      else
         Obj.Next := L;
         Obj.Prev := L.Prev;
         L.Prev   := Obj_Ref;
         L        := Obj_Ref;
      end if;
   end Attach_To_Final_List;

   ----------------------------
   -- Detach_From_Final_List --
   ----------------------------

   procedure Detach_From_Final_List (Obj : in out Finalizable) is
   begin

      if Obj.Prev = null then

         null;  -- should not happen

      --  when it is the first of a chain of dynamically-allocated object,
      --  L may not be the right pointer since the allocation and the
      --  deallocation can be done with 2 different compatible general
      --  access types

      elsif Is_List_Controller (Obj.Prev) then
         Update_List_Controller (Obj.Prev, Obj.Next);

      else
         Obj.Prev.Next := Obj.Next;
      end if;

      if Obj.Next /= null then
         Obj.Next.Prev := Obj.Prev;
         Obj.Next := null;
      end if;

      Obj.Prev := null;
   end Detach_From_Final_List;

   -------------------
   -- Finalize_List --
   -------------------

   procedure Finalize_List (L : Finalizable_Ptr) is
      P     : Finalizable_Ptr := L;
      Q     : Finalizable_Ptr;

   begin
      while P /= null loop
         Q := P.Next;
         Finalize (P.all);
         P := Q;
      end loop;

   exception
      when E_Occ : others =>
         Finalize_List (Q);

         Raise_Exception (
           E       => Program_Error'Identity,
           Message => "exception "
             & Exception_Name (E_Occ) & " raised during finalization");
   end Finalize_List;

   --------------------------
   -- Finalize_Global_List --
   --------------------------

   procedure Finalize_Global_List is
   begin
      Finalize_List (Global_Final_List);
   end Finalize_Global_List;

   ------------------
   -- Finalize_One --
   ------------------

   procedure Finalize_One (Obj : in out  Finalizable) is
   begin
      Detach_From_Final_List (Obj);
      Finalize (Root_Controlled'Class (Obj));

   exception
      when E_Occ : others =>
         Raise_Exception (
           E       => Program_Error'Identity,
           Message => "exception "
             & Exception_Name (E_Occ) & " raised during finalization");
   end Finalize_One;

   ----------------------------------
   -- Record_Controller Management --
   ----------------------------------

   ----------------
   -- Initialize --
   ----------------

   procedure Initialize (Object : in out Limited_Record_Controller) is
   begin
      null;
   end Initialize;

   procedure Initialize (Object : in out Record_Controller) is
   begin
      Object.My_Address := Object'Address;
   end Initialize;

   -------------
   --  Adjust --
   -------------

   procedure Adjust (Object : in out Record_Controller) is

      My_Offset : constant Storage_Offset
        := Object.My_Address - Object'Address;

      P : Finalizable_Ptr;

      procedure Ptr_Adjust (Ptr : in out Finalizable_Ptr);
      --  Substract the offset to the pointer

      procedure Reverse_Adjust (P : Finalizable_Ptr);
      --  Ajust the components in the reverse order in which they are stored
      --  on the finalization list. (Adjust and Finalization are not done in
      --  the same order)

      procedure Ptr_Adjust (Ptr : in out Finalizable_Ptr) is
         function To_Addr is
           new Ada.Unchecked_Conversion (Finalizable_Ptr, Address);

         function To_Ptr is
           new Ada.Unchecked_Conversion (Address, Finalizable_Ptr);

      begin
         if Ptr /= null then
            Ptr := To_Ptr (To_Addr (Ptr) - My_Offset);
         end if;
      end Ptr_Adjust;

      procedure Reverse_Adjust (P : Finalizable_Ptr) is
      begin
         if P /= null then
            Ptr_Adjust (P.Next);
            Ptr_Adjust (P.Prev);
            Reverse_Adjust (P.Next);
            Adjust (P.all);
         end if;
      end Reverse_Adjust;

   begin

      --  Adjust the components and their finalization pointers next

      Ptr_Adjust (Object.F);
      Reverse_Adjust (Object.F);

      --  then Adjust the object itself

      Object.My_Address := Object'Address;
   end Adjust;

   --------------
   -- Finalize --
   --------------

   procedure Finalize   (Object : in out Limited_Record_Controller) is
   begin
      Finalize_List (Object.F);
   end Finalize;
end System.Finalization_Implementation;
