--          This file is part of SmartEiffel The GNU Eiffel Compiler.
--       Copyright (C) 1994-2002 LORIA - INRIA - U.H.P. Nancy 1 - FRANCE
--          Dominique COLNET and Suzanne COLLIN - SmartEiffel@loria.fr
--                       http://SmartEiffel.loria.fr
-- SmartEiffel is  free  software;  you can  redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by the Free
-- Software  Foundation;  either  version  2, or (at your option)  any  later
-- version. SmartEiffel is distributed in the hope that it will be useful,but
-- WITHOUT 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  along  with  SmartEiffel;  see the file COPYING.  If not,
-- write to the  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-- Boston, MA 02111-1307, USA.
--
class ONCE_ROUTINE_POOL
   --
   -- Singleton object in charge of runnable once routines.
   -- This singleton is shared via the GLOBALS.`once_routine_pool' once function.
   --

inherit GLOBALS

feature {RUN_FEATURE} -- For `compile_to_c' as well as `compile_to_jvm':

   register_procedure(rf: RUN_FEATURE) is
      require
         rf.is_once_procedure
      do
         check
            not procedure_list.has(rf)
         end
         procedure_list.add_last(rf)
      end

   register_function(rf: RUN_FEATURE) is
      require
         rf.is_once_function
      local
	 rc: RUN_CLASS
      do
         check not function_list.has(rf) end
         function_list.add_last(rf)
	 if rf.name.to_string = as_std_output then
	    if std_output_flush_atexit = Void then
	       if rf.base_feature.base_class.name.to_string = as_general then
		  rc := rf.result_type.run_class
		  std_output_flush_atexit := rc.get_feature_with(as_flush)
	       end
	    end
	 end
      end

feature {GC_HANDLER}

   gc_mark_in(c_code: STRING) is
	 -- To mark results of once functions because they are part
	 -- of the root.
      local
         i: INTEGER
         rf: RUN_FEATURE
         of: ONCE_FUNCTION
         mem: FIXED_ARRAY[ONCE_FUNCTION]
         rt: E_TYPE
         oresult: STRING
      do
         if function_list.count > 0 then
            from
               i := function_list.upper
               !!mem.with_capacity(i // 2)
            until
               i < 0
            loop
               rf := function_list.item(i)
               of ?= rf.base_feature
	       check
		  of /= Void
	       end
               if not mem.fast_has(of) then
                  if rf.current_type.run_class.at_run_time then
                     mem.add_last(of)
                     rt := rf.result_type
                     if rt.need_gc_mark_function then
                        oresult := o_result(of)
			gc_handler.mark_for(c_code,oresult,rt.run_class)
                     end
                  end
               end
               i := i - 1
            end
         end
      end

feature {JVM}

   fields_count: INTEGER

   jvm_define_fields is
      local
         byte_idx, idx_flag, i: INTEGER; rf: RUN_FEATURE
         bf: E_FEATURE; name_list: FIXED_ARRAY[INTEGER]
      do
         !!name_list.with_capacity(fields_count)
         if function_list.count > 0 then
            from
               i := function_list.upper
               byte_idx := constant_pool.idx_utf8(fz_41)
            until
               i < 0
            loop
               rf := function_list.item(i)
               bf := rf.base_feature
               idx_flag := idx_name_for_flag(bf)
               if name_list.fast_has(idx_flag) then
               else
                  name_list.add_last(idx_flag)
                  -- ---------- Static field for flag:
                  field_info.add(9,idx_flag,byte_idx)
                  -- ---------- Static field for result:
                  field_info.add(9,
                                 idx_name_for_result(bf),
                                 idx_descriptor(rf.result_type.run_type))
               end
               i := i - 1
            end
         end
         if procedure_list.count > 0 then
            from
               i := procedure_list.upper
               byte_idx := constant_pool.idx_utf8(fz_41)
            until
               i < 0
            loop
               rf := procedure_list.item(i)
               bf := rf.base_feature
               idx_flag := idx_name_for_flag(bf)
               if name_list.fast_has(idx_flag) then
               else
                  name_list.add_last(idx_flag)
                  -- ---------- Static field for flag:
                  field_info.add(9,idx_flag,byte_idx)
               end
               i := i - 1
            end
         end
      end

   jvm_initialize_fields is
      local
         i: INTEGER
      do
         if jvm_flag_list.count > 0 then
            from
               i := jvm_flag_list.upper
            until
               i < 0
            loop
               -- Set once flag :
               code_attribute.opcode_iconst_0
               code_attribute.opcode_putstatic(jvm_flag_list.item(i),-1)
               i := i - 1
            end
         end
      end

feature {RUN_FEATURE} -- For `compile_to_jvm' only:

   idx_fieldref_for_flag(rf: RUN_FEATURE): INTEGER is
      require
         rf.is_once_routine
      local
	 cp: CONSTANT_POOL
      do
	 cp := constant_pool
         Result := cp.idx_fieldref3(jvm_root_class,
				    o_flag(rf.base_feature),
				    fz_41)
      end

feature {NONE} -- For `compile_to_c' as well as `compile_to_jvm':

   idx_fieldref_for_result(rf: RUN_FEATURE): INTEGER is
      require
         rf.is_once_function
      local
	 cp: CONSTANT_POOL
	 rt: E_TYPE
      do
	 cp := constant_pool
         rt := rf.result_type.run_type
         Result := cp.idx_fieldref3(jvm_root_class,
				    o_result(rf.base_feature),
				    jvm_descriptor(rt))
      end

   procedure_list: FIXED_ARRAY[RUN_FEATURE] is
	 -- Live set of once procedures.
      once
         !!Result.with_capacity(32)
      end

   function_list: FIXED_ARRAY[RUN_FEATURE] is
	 -- Live set of once functions.
      once
         !!Result.with_capacity(32)
      end

   o_flag(bf: E_FEATURE): STRING is
	 -- Compute the only one corresponding `flag': fBCxxKey used
	 -- to check that execution is done.
      require
	 bf /= Void
      do
	 Result := o_flag_buffer
         Result.clear
         Result.append(once "fBC")
         bf.base_class.id.append_in(Result)
         Result.append(bf.first_name.to_key)
	 Result := string_aliaser.item(Result)
      ensure
	 Result = string_aliaser.item(Result)
      end

   buffer: STRING is
      once
	 !!Result.make(64)
      end

   c_define_o_result_buffer: STRING is
      once
	 !!Result.make(64)
      end

   o_flag_buffer: STRING is
      once
	 !!Result.make(32)
      end

   o_result_buffer: STRING is
      once
	 !!Result.make(32)
      end

   jvm_descriptor_buffer: STRING is
      once
	 !!Result.make(32)
      end

feature {ABSTRACT_RESULT,RUN_FEATURE}

   o_result(bf: E_FEATURE): STRING is
      require
	 bf /= Void
      do
	 Result := o_result_buffer
         Result.clear
         Result.append(once "oBC")
         bf.base_class.id.append_in(Result)
         Result.append(bf.first_name.to_key)
	 Result := string_aliaser.item(Result)
      ensure
	 Result = string_aliaser.item(Result)
      end

   c_put_o_result(rf: RUN_FEATURE) is
      require
	 rf.is_once_function
      do
	 cpp.put_string(o_result(rf.base_feature))
      end

   jvm_result_load(rf: RUN_FEATURE) is
      require
	 rf.is_once_function
      local
         result_space, idx_result: INTEGER
      do
         result_space := rf.result_type.jvm_stack_space
         idx_result := idx_fieldref_for_result(rf)
         code_attribute.opcode_getstatic(idx_result,result_space)
      end

   jvm_result_store(rf: RUN_FEATURE) is
      require
	 rf.is_once_function
      local
         result_space, idx_result: INTEGER
      do
         result_space := rf.result_type.jvm_stack_space
         idx_result := idx_fieldref_for_result(rf)
         code_attribute.opcode_putstatic(idx_result,- result_space)
      end

feature {C_PRETTY_PRINTER} -- For `compile_to_c':

   c_pre_compute is
	 -- Generate C code to pre_compute some functions.
      local
	 i: INTEGER; memory: FIXED_ARRAY[E_FEATURE]
	 rf: RUN_FEATURE; bf: E_FEATURE
      do
	 echo.put_string(fz_04)
	 echo.put_string(fz_05)
	 cpp.put_string(fz_pco_comment)
	 from
	    i := function_list.upper
	    !!memory.with_capacity(64)
	 until
	    i < 0
	 loop
	    rf := function_list.item(i)
	    if rf.run_class.at_run_time then
	       if rf.is_pre_computable then
		  bf := rf.base_feature
		  if not memory.fast_has(bf) then
		     memory.add_last(bf)
		     echo.put_character('%T')
		     echo.put_string(bf.base_class.name.to_string)
		     echo.put_character('.')
		     echo.put_string(rf.name.to_string)
		     echo.put_character('%N')
		     c_pre_compute_of(rf,bf)
		     cpp.put_string(fz_pco_comment)
		  end
	       end
	    end
	    i := i - 1
	 end
	 echo.print_count(fz_04,memory.count)
      end

   std_output_flush_atexit: RUN_FEATURE
	 -- To be called by the atexit() function if any.
   
feature {RUN_FEATURE} -- For `compile_to_c':

   c_define_o_flag(rf: RUN_FEATURE) is
	 -- Add the definition/initialization of the corresponding
	 -- `o_flag' if not yet done.
      require
	 rf.is_once_routine
      local
	 bf: E_FEATURE
	 bcbf: BASE_CLASS
	 flag: STRING
      do
         if not smart_eiffel.scoop or else rf.result_type.is_separate then
            bf := rf.base_feature
            bcbf := bf.base_class
            flag := o_flag(bf)
            if not bcbf.once_flag(flag) then
               buffer.copy(once "int ")
               buffer.append(flag)
               cpp.put_extern2(buffer,'0')
            end
         end
      end

   c_define_o_result(rf: RUN_FEATURE) is
      require
	 rf.is_once_function
      local
	 bf: E_FEATURE; bcbf: BASE_CLASS; oresult: STRING; rt: E_TYPE
      do
         if not smart_eiffel.scoop or else rf.result_type.is_separate then
            bf := rf.base_feature
            bcbf := bf.base_class
            oresult := o_result(bf)
            if not bcbf.once_flag(oresult) then
               buffer.clear
               rt := rf.result_type
               if rt.is_separate then
                  buffer.extend('s')
               end
               buffer.extend('T')
               if rt.is_expanded then
                  rt.id.append_in(buffer)
                  buffer.extend(' ')
               else
                  buffer.extend('0')
                  buffer.extend('*')
               end
               buffer.append(oresult)
               c_define_o_result_buffer.clear
               rt.c_initialize_in(c_define_o_result_buffer)
               cpp.put_extern5(buffer,c_define_o_result_buffer)
            end
         end
     end

   c_test_o_flag(rf: RUN_FEATURE) is
      require
	 rf.is_once_routine
      local
	 flag: STRING; rt: E_TYPE
      do
         if not smart_eiffel.scoop or else rf.result_type.is_separate then
            flag := o_flag(rf.base_feature)
            cpp.put_string(once "if(")
            cpp.put_string(flag)
            cpp.put_string(once "==0){")
            cpp.put_string(flag)
            cpp.put_string(once "=1;{%N")
         else
            rt := rf.result_type
            buffer.clear
            buffer.extend('T')
            if rt.is_expanded then
               rt.id.append_in(buffer)
               buffer.extend(' ')
            else
               buffer.extend('0')
               buffer.extend('*')
            end
            flag := o_result(rf.base_feature)
            cpp.put_string(buffer)
            cpp.put_string(flag)
            cpp.put_string(fz_00)
            cpp.put_string(once "[
                se_subsystem_t* self=se_current_subsystem_thread();
                if(!self->vft.is_set_once(self,"
                                ]")
            cpp.put_string(flag)
            cpp.put_string(once "%")){%N")
            cpp.put_string(buffer)
            cpp.put_string(once "* ptr_")
            cpp.put_string(flag)
            cpp.put_string(once "=(")
            cpp.put_string(buffer)
            cpp.put_string(once "*)malloc(sizeof(")
            cpp.put_string(buffer)
            cpp.put_string(once "));%N")
         end
      end

   c_return_o_result(rf: RUN_FEATURE) is
      require
	 rf.is_once_function
      local
         oresult: STRING; rt: E_TYPE
      do
         oresult := o_result(rf.base_feature)
         if smart_eiffel.scoop and then not rf.result_type.is_separate then
            rt := rf.result_type
            buffer.clear
            buffer.extend('T')
            if rt.is_expanded then
               rt.id.append_in(buffer)
               buffer.extend(' ')
            else
               buffer.extend('0')
               buffer.extend('*')
            end
            cpp.put_string(once "*ptr_")
            cpp.put_string(oresult)
            cpp.put_character('=')
            cpp.put_string(oresult)
            cpp.put_string(once ";%Nself->vft.set_once(self,%"")
            cpp.put_string(oresult)
            cpp.put_string(once "%", ptr_")
            cpp.put_string(oresult)
            cpp.put_string(once ");}%Nelse {")
            cpp.put_string(oresult)
            cpp.put_string(once "=*((")
            cpp.put_string(buffer)
            cpp.put_string(once "*)(self->vft.get_once(self,%"")
            cpp.put_string(oresult)
            cpp.put_string(once "%")));}%N")
         else
            cpp.put_string(once "}}%N")
         end
         cpp.put_string(once "return ")
         cpp.put_string(oresult)
         cpp.put_string(once ";%N}%N")
      end

feature {NONE} -- For `compile_to_jvm':

   jvm_flag_list: FIXED_ARRAY[INTEGER] is
      once
         !!Result.with_capacity(32)
      end

   idx_descriptor(rt: E_TYPE): INTEGER is
      require
         rt /= Void
      do
         Result := constant_pool.idx_utf8(jvm_descriptor(rt))
      end

   idx_name_for_result(bf: E_FEATURE): INTEGER is
      require
         bf /= Void
      do
         Result := constant_pool.idx_utf8(o_result(bf))
      end

   idx_name_for_flag(bf: E_FEATURE): INTEGER is
      require
         bf /= Void
      do
         Result := constant_pool.idx_utf8(o_flag(bf))
      end

   jvm_descriptor(rt: E_TYPE): STRING is
      do
	 Result := jvm_descriptor_buffer
         Result.clear
         if rt.is_reference then
            Result.append(jvm_root_descriptor)
         else
            rt.jvm_descriptor_in(Result)
         end
      end

feature {RUN_FEATURE_6,RUN_FEATURE_11}

   is_pre_computable(rf: RUN_FEATURE): BOOLEAN is
      require
	 rf.is_once_function
      do
         if not smart_eiffel.scoop then
	    if frozen_general.fast_has(rf.name.to_string) then
	       Result := true
	    elseif rf.arguments = Void and then not rf.use_current then
	       if rf.routine_body = Void then
		  Result := true
	       elseif not ace.invariant_check then
		  Result := rf.routine_body.is_pre_computable
	       end
	    end
	    if Result then
	       if rf.require_assertion /= Void then
		  rf.require_assertion.clear_run_feature
	       end
	       if rf.ensure_assertion /= Void then
		  rf.ensure_assertion.clear_run_feature
	       end
	    end
	 end
      end

feature {NONE}

   c_pre_compute_of(rf: RUN_FEATURE; bf: E_FEATURE) is
      require
	 rf.is_once_function
         rf.is_pre_computable
         cpp.on_c
      local
	 result_type: E_TYPE; local_vars: LOCAL_VAR_LIST; rf3: RUN_FEATURE_3
      do
	 if rf.require_assertion /= Void then
	    rf.require_assertion.compile_to_c
	 end
	 result_type := rf.result_type
	 if result_type.is_user_expanded then
	    rf3 := result_type.run_class.a_default_create
	    if rf3 /= Void then
	       cpp.put_proc_call_0(rf3,Void,o_result(rf.base_feature))
	    end
	 end
	 --
	 local_vars := rf.local_vars
         if local_vars /= Void then
            cpp.put_character('{')
            local_vars.c_declare
         end
	 --
         if rf.routine_body /= Void then
            rf.routine_body.compile_to_c
         end
	 --
	 if rf.ensure_assertion /= Void then
	    rf.ensure_assertion.compile_to_c
         end
	 --
         if local_vars /= Void then
            cpp.put_character('}')
         end
      end

   frozen_general: ARRAY[STRING] is
      once
         Result := <<as_std_error, as_std_input, as_io, as_std_output>>
      end

   fz_pco_comment: STRING is "/*PCO*/%N"

   singleton_memory: ONCE_ROUTINE_POOL is
      once
	 Result := Current
      end

invariant

   is_real_singleton: Current = singleton_memory

end -- ONCE_ROUTINE_POOL