--          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 AGENT_ARGS

inherit GLOBALS; HASHABLE redefine is_equal end

creation {AGENT_POOL} make

feature

   hash_code: INTEGER is
      do
	 Result := signature.hash_code
      end

   is_equal(other: like Current): BOOLEAN is
      do
	 Result := signature.is_equal(other.signature)
      end

   signature: STRING
	 -- Computed using `arguments' and `result_type'.

feature {AGENT_POOL}

   result_type: E_TYPE

   c_define_agent_launcher is
      require
	 cpp.on_c
      local
	 boost: BOOLEAN
      do
	 boost := c_define_agent_launcher_heading(once "(live)")
	 buffer.clear
         if not boost then
            buffer.append(
            once "[
                se_frame_descriptor fd={"<agent-call-wrapper-1>",0,0,"",1};
                se_dump_stack ds;
                ds.fd=&fd;
                ds.p=0;
                ds.caller=caller;

              ]")
         end
	 buffer.append(once "switch (((se_agent0*)a)->agent_id){%N")
	 agent_pool.c_switch_in(buffer, type_of_agent, result_type)
	 if not boost then
	    buffer.append(once "[
               default:
               error0("Internal error in agent launcher.",NULL);
                                ]")
	 end
	 buffer.append(once "}%N}%N")
	 cpp.put_string(buffer)
      ensure
	 cpp.on_c
      end

   c_define_missing_agent_launcher is
      local
	 boost: BOOLEAN
      do
	 boost := c_define_agent_launcher_heading(once "(missing)")
	 buffer.clear
	 if not boost then
	    buffer.append("error0(%"Missing agent.%",NULL);%N")
	 end
	 buffer.append("}%N")
	 cpp.put_string(buffer)
      end
   
   jvm_define(actual: BOOLEAN) is
	 -- Define both the deferred wrapper or the `actual' definition.
      local
	 flags, i: INTEGER; descripteur: STRING
	 ca: CODE_ATTRIBUTE; cp: CONSTANT_POOL
      do
	 ca := code_attribute
	 cp := constant_pool
	 flags := 9
	 descripteur := once "...................."
	 descripteur.clear
	 descripteur.extend('(')
	 if types /= Void then
	    from 
	       i := 1
	    until
	       i > types.count
	    loop
	       types.item(i).jvm_descriptor_in(descripteur)
	       i := i + 1
	    end
	 end
	 descripteur.append(")V")
	 method_info.start(flags,signature,descripteur)
	 if actual then
	    ca.opcode_return
	 else
	    ca.opcode_return
	 end
	 method_info.finish
      end

feature {AGENT_INSTRUCTION,AGENT_EXPRESSION}

   c_agent_definition_call(agent_target: EXPRESSION) is
      require
	 agent_target /= Void
      local
	 boost: BOOLEAN; position: POSITION
      do
	 position := agent_target.start_position
	 agent_pool.check_for_callee(Current,position)
	 if result_type = Void then
	    if ace.no_check then
	       cpp.put_trace_or_sedb_instruction(position)
	    end
	 end
	 cpp.put_string(signature)
	 cpp.put_character('(')
	 boost := ace.boost
	 if not boost then
	    cpp.put_string(once "&ds,vc(")
	 end
	 agent_target.compile_to_c
	 if not boost then
	    cpp.put_character(',')
	    cpp.put_position(agent_target.start_position)
	    cpp.put_character(')')
	 end
	 if face_tuple.count > 0 then
	    cpp.put_character(',')
	    face_tuple.compile_to_c
	 end
	 cpp.put_character(')')
      end

   jvm_agent_definition_call(agent_target: EXPRESSION) is
      require
	 agent_target /= Void
      local
	i, count, idx:INTEGER; cp: CONSTANT_POOL; ca: CODE_ATTRIBUTE
	class_name, descripteur: STRING; place:INTEGER
      do
	ca:=code_attribute
	cp:=constant_pool
	place:=0
	agent_target.compile_to_jvm
	face_tuple.compile_to_jvm
	descripteur:="("
	if types /= Void then
	  count := types.count
	  from i := 1 until i > count
	  loop
	    types.item(i).jvm_descriptor_in(descripteur)
            descripteur.remove_last(1)
	    place:=place+types.item(i).jvm_stack_space
	    i := i + 1
	  end
	descripteur:=descripteur+")"
	end
	if (result_type /= Void) then
	  result_type.jvm_descriptor_in(descripteur)
	else
          descripteur:=descripteur+"V"
	end
	class_name:="_agent"
	idx:=cp.idx_methodref3(class_name,signature,descripteur)
	ca.opcode_invokevirtual(idx,place)
      end

feature {NONE}

   type_of_agent: TYPE_OF_AGENT
   
   types: ARRAY[E_TYPE]

   face_tuple: FACE_TUPLE

   debug_info: STRING

   make(toa: like type_of_agent;
	args: EFFECTIVE_ARG_LIST; rt: like result_type) is
	 -- Also check type conformance of `args' with `open'.
      require
	 toa /= Void
	 args.count = 1
      local
	 i, count: INTEGER; e: EXPRESSION; t, ft: E_TYPE; open: TYPE_TUPLE
      do
	 type_of_agent := toa
	 open := toa.open
	 result_type := rt
	 create signature.make(32)
	 signature.append(as_call)
	 signature.extend('_')
	 debug
	    debug_info := ("agent_args:").twin
	 end
	 face_tuple ?= args.expression(1)
	 if face_tuple = Void then
	    error_handler.add_position(args.expression(1).start_position)
	    error_handler.append(
               "Only the explicit manifest TUPLE notation (i.e. the square %
	       %bracketed notation) is allowed here. If you cannot work %
	       %around, please contact us on our mailing list %
	       %(SmartEiffel@loria.fr).")
	    error_handler.print_as_fatal_error
	 else
	    from
	       count := face_tuple.count
	       if count > 0 then
		  create types.make(1,count)
	       end
	       if count /= open.count then
		  error_handler.add_position(open.start_position)
		  error_handler.add_position(args.start_position)
		  error_handler.append(
		     "Bad number of open arguments for agent call.")
		  error_handler.print_as_fatal_error
	       end
	       i := 1
	    until
	       i > count
	    loop
	       e := face_tuple.expression(i)
	       t := e.result_type.run_type
	       extend_signature(t)
	       types.put(t,i)
	       ft := open.type(i)
	       if not t.is_a(ft) then
		  error_handler.add_position(ft.start_position)
		  error_handler.add_position(e.start_position)
		  error_handler.append(
		  "Incompatible open arguments for agent call.")
		  error_handler.print_as_fatal_error
	       end
	       i := i + 1
	    end
	 end
	 if rt = Void then
	    signature.extend('v')
	 else
	    extend_signature(rt)
	 end
      ensure
	 type_of_agent = toa
	 result_type = rt
      end

   c_define_agent_launcher_heading(tag: STRING): BOOLEAN is
      local
	 boost: BOOLEAN; i, count: INTEGER
      do
	 echo.put_string(once "Defining ")
	 echo.put_string(tag)
	 echo.put_string(once " agent wrapper: ")
	 echo.put_string(signature)
	 echo.put_string(once "%N")
	 boost := ace.boost
	 buffer.clear
	 if result_type = Void then
	    buffer.append(fz_void)
	 else
	    result_type.c_type_for_result_in(buffer)
	 end
	 buffer.extend(' ')
	 buffer.append(signature)
	 buffer.extend('(')
	 if not boost then 
	    buffer.append(once "se_dump_stack*caller,")
	 end
	 buffer.append(once "/*agent*/T0*a")
	 if types /= Void then
	    count := types.count
	    from i := 1 until i > count
	    loop
	       buffer.extend(',')
	       types.item(i).c_type_for_argument_in(buffer)
	       buffer.extend(' ')
	       buffer.extend('a')
	       i.append_in(buffer)
	       i := i + 1
	    end
	 end
	 buffer.extend(')')
	 cpp.put_c_heading(buffer)
	 Result := boost
      end
   
   extend_signature(t: E_TYPE) is
      do
	 debug
	    debug_info.append(t.run_time_mark)
	    debug_info.extend(' ')
	 end
	 if t.is_reference then
	    signature.extend('r')
	    signature.extend('0')
	 else
	    signature.extend('e')
	    t.id.append_in(signature)
	 end
      end

   buffer: STRING is
      once
	 create Result.make(256)
      end

invariant

   not signature.is_empty

   face_tuple /= Void

end -- AGENT_ARGS
