--          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_MANIFEST_STRING_POOL
   --
   -- Unique global object in charge of once manifest string used (i.e. once
   -- manifest string in live code).
   --

inherit GLOBALS

feature {MANIFEST_STRING}

   register(ms: MANIFEST_STRING): STRING is
         -- To register a runnable one.
         -- Choose the appropriate mangling.
      require
         not smart_eiffel.is_ready
         ms.is_once
      local
         x: INTEGER
      do
         header.copy(once "ms")
         x := ms.start_position.base_class.id
         x.append_in(header)
         header.extend('_')
         x := ms.to_string.hash_code
         x.append_in(header)
         from
         until
            not mangling_dictionary.has(header)
         loop
            header.extend('a')
         end
         Result := header.twin
         mangling_dictionary.add(ms,Result)
	 -- To look for identical definition in order to optimize
	 -- a delivery:
	 -- multiple_definitions_warning(ms)
      end

   se_ms_c_call_in(buffer: STRING; ms: MANIFEST_STRING) is
      do
	 buffer.append(once "se_ms(")
	 ms.count.append_in(buffer)
	 buffer.extend(',')
	 string_to_c_code(ms.to_string,buffer)
	 buffer.extend(')')
      end

   se_ms_trace_c_call_in(buffer: STRING; ms: MANIFEST_STRING) is
      local
	 p: POSITION
      do
	 buffer.append(once "se_ms_trace(")
	 ms.count.append_in(buffer)
	 buffer.extend(',')
	 string_to_c_code(ms.to_string,buffer)
	 p := ms.start_position
	 buffer.extend(',')
	 p.line.append_in(buffer)
	 buffer.extend(',')
	 p.column.append_in(buffer)
	 buffer.extend(',')
	 string_to_c_code(p.path,buffer)
	 buffer.extend(')')
      end

feature {SMART_EIFFEL}

   c_define1(string_at_run_time: BOOLEAN) is
      do
         if not string_at_run_time then
            cpp.put_string(
               once "typedef struct S7 T7;%N%
               %struct S7{T9 _storage;T2 _count;T2 _capacity;};%N")
         end
      end

   c_define2(string_at_run_time: BOOLEAN) is
      require
         cpp.on_c
      local
         i, j, function_count, mdc: INTEGER; ms: MANIFEST_STRING
      do
	 if not smart_eiffel.scoop then
	    mdc := mangling_dictionary.count
	    echo.print_count(once "Manifest String",mdc)
	    if mdc > 0 then
	       from -- For the *.h file:
		  i := 1
	       until
		  i > mdc
	       loop
		  ms := mangling_dictionary.item(i)
		  header.copy(fz_t7_star)
		  header.append(ms.mangling)
		  cpp.put_extern1(header)
		  i := i + 1
	       end
	    end
	 end
	 --
         define_se_ms(string_at_run_time)
         --
	 if smart_eiffel.scoop then
	    cpp.put_c_function(once "void se_msi1()",
			       once "")
	 elseif mdc > 0 or else  ace.manifest_string_trace then
	    cpp.sys_runtime_h_and_c(fz_manifest_string_trace)
	    from -- For the *.c file:
	       i := 1
	       function_count := 1
	    until
	       function_count > 1 and then i > mdc
	    loop
	       header.copy(fz_void)
	       header.extend(' ')
	       header.append(fz_se_msi)
	       function_count.append_in(header)
	       header.append(fz_c_void_args)
	       from
		  body.clear
		  j := nb_ms_per_function
	       until
		  j = 0 or else i > mdc
	       loop
		  ms := mangling_dictionary.item(i)
		  body.append(ms.mangling)
		  body.extend('=')
		  se_ms_c_call_in(body,ms)
		  body.extend(';')
		  body.extend('%N')
		  j := j - 1
		  i := i + 1
	       end
	       function_count := function_count + 1
	       if i <= mdc then
		  body.append(fz_se_msi)
		  function_count.append_in(body)
		  body.append(fz_c_no_args_procedure)
	       end
	       cpp.next_bunch_size(3)
	       cpp.put_c_function(header,body)
	    end
   	 end
      ensure
         cpp.on_c
      end

feature {C_PRETTY_PRINTER, MANIFEST_STRING}

   string_to_c_code(s: STRING; c_code: STRING) is
      local
         break, s_count, i: INTEGER

      do
         c_code.extend('%"')
         from
            i := 1
	    s_count := s.count
         until
            i > s_count
         loop
            character_to_c_code(s.item(i),c_code)
            i := i + 1
	    break := break + 1
	    if break > 1024 then
	       -- Because of a limitation of the Visual C/C++
	       -- compiler which do not like too long lines:
	       c_code.append(once "%"%N%"")
	       break := 0
	    end
         end
         c_code.extend('%"')
      end

feature {C_PRETTY_PRINTER}

   character_to_c_code(c: CHARACTER; c_code: STRING) is
      do
         if c = '%N' then
            c_code.extend('\')
            c_code.extend('n')
         elseif c = '\' then
            c_code.extend('\')
            c_code.extend('\')
         elseif c = '%"' then
            c_code.extend('\')
            c_code.extend('%"')
         elseif c = '%'' then
            c_code.extend('\')
            c_code.extend('%'')
         elseif c.code < 32 or else 122 < c.code then
            c_code.extend('\')
            c.code.to_octal.append_in(c_code)
            c_code.append(once "%"%"")
         else
            c_code.extend(c)
         end
      end

   c_call_initialize is
      require
         cpp.on_c
      do
         if mangling_dictionary.count > 0 then
            cpp.put_string(fz_se_msi)
            cpp.put_character('1')
            cpp.put_string(fz_c_no_args_procedure)
         end
      ensure
         cpp.on_c
      end

feature {GC_HANDLER}

   define_manifest_string_mark is
      local
         i, mdc, ms_count, function_count: INTEGER
         ms: MANIFEST_STRING
      do
         mdc := mangling_dictionary.count
         function_count := 1
         define_manifest_string_mark_header(function_count)
         from
            i := 1
         until
            i > mdc
         loop
            if ms_count > 300 then
               ms_count := 0
               function_count := function_count + 1
               cpp.put_string(fz_manifest_string_mark)
               cpp.put_integer(function_count)
               cpp.put_string(fz_c_no_args_procedure)
               cpp.put_string(fz_12)
               define_manifest_string_mark_header(function_count)
            end
            ms := mangling_dictionary.item(i)
	    cpp.put_string(once "gc_mark7(")
	    cpp.put_string(ms.mangling)
	    cpp.put_string(fz_14)
	    ms_count := ms_count + 1
            i := i + 1
         end
         cpp.put_string(fz_12)
      end

feature {JVM}

   jvm_define_fields is
      local
         cp: like constant_pool
         ms: MANIFEST_STRING
         name_idx, string_idx, i, mdc: INTEGER
      do
         mdc := mangling_dictionary.count
         if mdc > 0 then
            cp := constant_pool
            string_idx := cp.idx_eiffel_string_descriptor
            from
               i := 1
            until
               i > mdc
            loop
               ms := mangling_dictionary.item(i)
               name_idx := cp.idx_utf8(ms.mangling)
               field_info.add(9,name_idx,string_idx)
               i := i + 1
            end
         end
      end

   jvm_initialize_fields is
      local
         cp: like constant_pool; ca: like code_attribute
         ms: MANIFEST_STRING; i, mdc: INTEGER
      do
         mdc := mangling_dictionary.count
         if mdc > 0 then
            cp := constant_pool
            ca := code_attribute
            from
               i := 1
            until
               i > mdc
            loop
               ms := mangling_dictionary.item(i)
               ca.opcode_push_manifest_string(ms.to_string)
               ca.opcode_putstatic(ms.fieldref_idx,-1)
               i := i + 1
            end
         end
      end

feature {NONE}

   mangling_dictionary: DICTIONARY[MANIFEST_STRING,STRING] is
      once
         !!Result.with_capacity(4096)
      end

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

   body: STRING is
      once
         !!Result.make(2048)
      end

   define_se_ms(string_at_run_time: BOOLEAN) is
      do
         header.copy(once "T7*se_ms(int c,char*e)")
         body.copy(once "/* Allocate a Manifest STRING.*/%NT7*")
	 common_body_for_se_string_and_se_ms(string_at_run_time)
         --
         header.copy(once "T7*se_string(char*e)")
         body.copy(
            once "/* Allocate an Eiffel STRING by copying C char*e */%N%
		 %int c=strlen(e);%N%
		 %T7*")
	 common_body_for_se_string_and_se_ms(string_at_run_time)
	 --
	 if smart_eiffel.scoop then
	    cpp.put_c_function(once "T7* scoop_once_manifest_string(char* key, int count, char* value)",
			       once "{
    se_subsystem_t* self = se_current_subsystem_thread()
    T7* result = NULL
    if (self->vft.is_set_once(self, key)) {
	result = (T7*)self->vft.get_once(self, key)
    }
    else {
	result = se_ms(count, value)
	self->vft.set_once(self, key, result)
    }
    return result
				     }")
         end
      end

   common_body_for_se_string_and_se_ms(string_at_run_time: BOOLEAN) is
      do
         gc_handler.new_manifest_string_in(body,string_at_run_time)
         body.append(once "s->_count=c;%N%
		     %s->_capacity=c+1;%N%
		     %s->_storage=((T9)")
         gc_handler.new_native9_in(body,string_at_run_time)
         body.append(once "(c+1));%N%
		     %memcpy(s->_storage,e,c+1);%N%
		     %return s;")
         cpp.put_c_function(header,body)
      end

   define_manifest_string_mark_header(number: INTEGER) is
      do
         header.copy(fz_void)
         header.extend(' ')
         header.append(fz_manifest_string_mark)
         number.append_in(header)
         header.append(fz_c_void_args)
         cpp.put_c_heading(header)
      end

   fz_manifest_string_mark: STRING is "manifest_string_mark"

   fz_se_msi: STRING is "se_msi"

   nb_ms_per_function: INTEGER is 50

   memory: DICTIONARY[MANIFEST_STRING,STRING]

   multiple_definitions_warning(ms: MANIFEST_STRING) is
      local
	 ms2: MANIFEST_STRING
      do
	 if memory = Void then
	    create memory.make
	    error_handler.append("Multiple definition of manifest string enabled.")
	    error_handler.print_as_warning
	 end
	 ms2 := memory.reference_at(ms.to_string)
	 if ms2 /= Void and then
	    not ms2.to_string.is_empty and then
	    ms2.to_string.first /= '.' 
	  then
	    error_handler.add_position(ms.start_position)
	    error_handler.add_position(ms2.start_position)
	    error_handler.append("Multiple definition of the same once manifest string.")
	    error_handler.print_as_warning
	 else
	    memory.add(ms,ms.to_string)
	 end
      end

end -- ONCE_MANIFEST_STRING_POOL