--          This file is part of SmallEiffel The GNU Eiffel Compiler.
--          Copyright (C) 1994-98 LORIA - UHP - CRIN - INRIA - FRANCE
--            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr 
--                       http://www.loria.fr/SmallEiffel
-- SmallEiffel 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. SmallEiffel 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  SmallEiffel;  see the file COPYING.  If not,
-- write to the  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-- Boston, MA 02111-1307, USA.
--
expanded class SWITCH
   --
   -- Set of tools (no attributes) to handle one switching site.
   --

inherit GLOBALS;

feature {NONE}

   running: ARRAY[RUN_CLASS] is
	 -- The global one.
      once
	 !!Result.with_capacity(256,1);
      end;
   
feature 
   
   c_define(up_rf: RUN_FEATURE) is
	 -- Define the switching C function for `up_rf'.
      require
	 cpp.on_c
      local
	 boost: BOOLEAN;
	 arguments:  FORMAL_ARG_LIST;
	 result_type, arg_type: TYPE;
	 i: INTEGER;
      do
	 boost := run_control.boost;
	 arguments := up_rf.arguments;
	 result_type := up_rf.result_type;
	 c_code.clear;
	 if result_type = Void then
	    c_code.append(fz_void);
	 else
	    result_type := result_type.run_type;
	    result_type.c_type_for_result_in(c_code);
	 end;
	 c_code.extend(' ');
	 c_code.extend('X');
	 up_rf.current_type.id.append_in(c_code);
	 up_rf.name.mapping_c_in(c_code);
	 if boost then
	    c_code.append("(void *C");
	 else
	    c_code.append("(se_dump_stack*caller,int l,int c,int f, void *C");
	 end;
	 if arguments /= Void then
	    from  
	       i := 1;
	    until
	       i > arguments.count
	    loop
	       c_code.extend(',');
	       arg_type := arguments.type(i).run_type;
	       arg_type.c_type_for_argument_in(c_code);
	       c_code.append(" a");
	       i.append_in(c_code);
	       i := i + 1;
	    end;
	 end;
	 c_code.extend(')');
	 cpp.put_c_heading(c_code);
	 cpp.swap_on_c;
	 if result_type /= Void then
	    c_code.clear;
	    result_type.c_type_for_result_in(c_code);
	    c_code.extend(' ');
	    c_code.extend('R');
	    if not boost then
	       c_code.extend('=');
	       result_type.c_initialize_in(c_code);
	    end;
	    c_code.append(fz_00);
	    cpp.put_string(c_code);
	 end;
	 if not boost then
	    cpp.put_string("se_dump_stack ds=*caller;%N");
	 end;
	 cpp.put_string("{int id=");
	 running.copy(up_rf.current_type.run_class.running);
	 sort_running(running);
	 if boost then
	    cpp.put_string("((T0*)C)->id;%N");
	 else
	    cpp.put_string("vc(C,l,c,f)->id;%N");
	 end;
	 if run_control.all_check then
	    c_switch(up_rf);
	 else
	    c_dicho(up_rf,1,running.upper);
	 end;
	 cpp.put_string(fz_12);
	 if not boost then
	    cpp.put_string("se_dst=caller;%N");
	 end;
	 if result_type /= Void then
	    cpp.put_string(fz_15);
	 else
	    cpp.put_string(fz_12);
	 end;
      ensure	 
	 cpp.on_c
      end;

feature {C_PRETTY_PRINTER}

   put_arguments(up_rf: RUN_FEATURE; fal: FORMAL_ARG_LIST) is
	 -- Produce C code for arguments of `fal' used
	 -- inside the switching C function.
      require
	 cpp.on_c;
	 fal.count = up_rf.arguments.count
      local
	 i, up: INTEGER;
      do
	 from  
	    i := 1;
	    up := fal.count;
	 until
	    i > up
	 loop
	    if i > 1 then
	       cpp.put_character(',');
	    end;
	    put_ith_argument(up_rf,fal,i);
	    i := i + 1;
	 end;
      ensure
	 cpp.on_c
      end;
   
   put_ith_argument(up_rf: RUN_FEATURE; fal: FORMAL_ARG_LIST; index: INTEGER) is
	 -- Produce C code for argument `index' of `fal' used
	 -- inside the switching C function.
      require
	 cpp.on_c;
	 fal.count = up_rf.arguments.count;
	 1 <= index;
	 index <= fal.count
      local
	 eal: like fal;
	 at, ft: TYPE;
      do
	 eal := up_rf.arguments;
	 at := eal.type(index).run_type;
	 ft := fal.type(index).run_type;
	 if at.is_reference and then ft.is_basic_eiffel_expanded then
	    cpp.put_character('(');
	    ft.cast_to_ref;
	    cpp.put_character('a');
	    cpp.put_integer(index);
	    cpp.put_string(")->_item");
	 else
	    cpp.put_character('a');
	    cpp.put_integer(index);
	 end;
      ensure
	 cpp.on_c
      end;
   
feature {NONE}
   
   c_dicho(up_rf: RUN_FEATURE; bi, bs: INTEGER) is
	 -- Produce dichotomic inspection code for Current id.
      require
	 bi <= bs
      local
	 m: INTEGER;
	 dyn_rc: RUN_CLASS;
	 dyn_rf: RUN_FEATURE;
      do
	 if bi = bs then
	    dyn_rc := running.item(bi);
	    dyn_rf := dyn_rc.dynamic(up_rf);
	    tail_opening(up_rf.result_type,dyn_rf.result_type);
	    cpp.push_switch(dyn_rf,up_rf);
	    dyn_rf.mapping_c;
	    cpp.pop;
	    tail_closing(up_rf.result_type,dyn_rf.result_type);
	 else	    
	    m := (bi + bs) // 2;
	    dyn_rc := running.item(m);
	    cpp.put_string("if(id<=");
	    cpp.put_integer(dyn_rc.id);
	    cpp.put_string("){%N");
	    c_dicho(up_rf,bi,m);
	    cpp.put_string("}%Nelse{%N");
	    c_dicho(up_rf,m + 1,bs);
	    cpp.put_character('}');
	 end;
      end;

   c_switch(up_rf: RUN_FEATURE) is
	 -- Produce C switch inspection code for Current id.
      local
	 i: INTEGER;
	 dyn_rc: RUN_CLASS;
	 dyn_rf: RUN_FEATURE;
      do
	 cpp.put_string("switch(id){%N"); 
	 from
	    i := 1;
	 until
	    i > running.upper
	 loop
	    dyn_rc := running.item(i);
	    dyn_rf := dyn_rc.dynamic(up_rf);
	    cpp.put_string("case "); 
	    cpp.put_integer(dyn_rc.id);
	    cpp.put_character(':');
	    tail_opening(up_rf.result_type,dyn_rf.result_type);
	    cpp.push_switch(dyn_rf,up_rf);
	    dyn_rf.mapping_c;
	    cpp.pop;
	    tail_closing(up_rf.result_type,dyn_rf.result_type);
	    cpp.put_string("%Nbreak;%N"); 
	    i := i + 1;
	 end;
	 if run_control.no_check then
	    cpp.put_string("default: error2(C,l,c,f);%N");
	 end;
	 cpp.put_string(fz_12);
      end;

feature {C_PRETTY_PRINTER,SWITCH}

   name(up_rf: RUN_FEATURE): STRING is
      do
	 c_code.clear;
	 c_code.extend('X');
	 up_rf.current_type.id.append_in(c_code);
	 c_code.append(up_rf.name.to_key);
	 Result := c_code;
      end;

feature

   jvm_descriptor(up_rf: RUN_FEATURE): STRING is
      local
	 arguments:  FORMAL_ARG_LIST;
	 rt: TYPE;
      do
	 arguments := up_rf.arguments;
	 tmp_jvmd.clear;
	 tmp_jvmd.extend('(');
	 tmp_jvmd.append(jvm_root_descriptor);
	 if arguments /= Void then
	    arguments.jvm_descriptor_in(tmp_jvmd);
	 end;
	 rt := up_rf.result_type;
	 if rt = Void then
	    tmp_jvmd.append(fz_19);
	 else
	    rt := rt.run_type;
	    tmp_jvmd.extend(')');
	    if rt.is_reference then
	       tmp_jvmd.append(jvm_root_descriptor);
	    else
	       rt.jvm_descriptor_in(tmp_jvmd);
	    end;
	 end;
	 Result := tmp_jvmd;
      end;

feature {NONE}

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

feature 

   idx_methodref(up_rf: RUN_FEATURE): INTEGER is
      require
	 up_rf /= Void
      do
	 Result := constant_pool.idx_methodref3(jvm_root_class,
						name(up_rf),
						jvm_descriptor(up_rf));
      end;
   
feature {JVM}

   jvm_mapping(cpc: CALL_PROC_CALL) is
      require
	 cpc /= Void
      local
	 idx, stack_level: INTEGER;
	 up_rf: RUN_FEATURE;
	 target: EXPRESSION;
	 eal: EFFECTIVE_ARG_LIST;
	 fal: FORMAL_ARG_LIST;
	 switch: SWITCH;
      do
	 target := cpc.target;
	 up_rf := cpc.run_feature;
	 eal := cpc.arguments;
	 target.compile_to_jvm;
	 stack_level := 1;
	 if eal /= Void then
	    fal := up_rf.arguments;
	    stack_level := stack_level + eal.compile_to_jvm(fal);
	 end;
	 if up_rf.result_type /= Void then
	    stack_level := stack_level - up_rf.result_type.jvm_stack_space;
	 end;
	 idx := switch.idx_methodref(up_rf);
	 code_attribute.opcode_invokestatic(idx,-stack_level);
      end;

feature {SWITCH_COLLECTION}

   jvm_define(up_rf: RUN_FEATURE) is
      local
	 rt: TYPE;
      do
	 -- Define the Java switching static method for `up_rf'.
	 method_info.start(9,name(up_rf),jvm_descriptor(up_rf));
	 running.copy(up_rf.current_type.run_class.running);
	 rt := up_rf.result_type;
	 if rt /= Void then
	    rt := rt.run_type;
	 end;
	 jvm_switch(up_rf,rt);
	 method_info.finish;
      end;

feature {NONE}

   c_code: STRING is
      once
	 !!Result.make(256);
      end;

   tail_opening(x_type, r_type: TYPE) is
      do
	 if x_type /= Void then
	    c_code.copy("R=((");
	    x_type.c_type_for_result_in(c_code);
	    c_code.append(")(");
	    cpp.put_string(c_code);
	    if r_type.is_expanded and then x_type.is_reference then
	       r_type.to_reference;
	       cpp.put_character('(');
	    end;
	 end;
      end;

   tail_closing(x_type, r_type: TYPE) is
      do
	 if x_type /= Void then
	    if r_type.is_expanded and then x_type.is_reference then
	       cpp.put_character(')');
	    end;
	    cpp.put_string(fz_16);
	 end;
      end;

feature {NONE}

   jvm_switch(up_rf: RUN_FEATURE; rt: TYPE) is
	 -- Produce Java sequential switch code.
      require
	 rt /= Void implies rt.run_type = rt
      local
	 space, point, i: INTEGER;
	 dyn_rc: RUN_CLASS;
	 dyn_rf: RUN_FEATURE;
	 boost: BOOLEAN;
	 ca: like code_attribute;
	 static_na, dynamic_na: TYPE_NATIVE_ARRAY;
      do
	 ca := code_attribute;
	 from
	    boost := run_control.boost;
	    i := running.upper;
	 until
	    i = 0
	 loop
	    dyn_rc := running.item(i);
	    dyn_rf := dyn_rc.dynamic(up_rf);
	    if i = 1 and then boost then
	    else
	       ca.opcode_aload_0;
	       dyn_rf.run_class.opcode_instanceof;
	       point := ca.opcode_ifeq;
	    end;
	    jvm.push_switch(dyn_rf,up_rf);
	    dyn_rf.mapping_jvm;
	    jvm.pop;
	    if rt = Void then 
	       ca.opcode_return;
	    elseif rt.is_native_array then
	       static_na ?= rt.run_type;
	       dynamic_na ?= dyn_rf.result_type.run_type;
	       if static_na.run_time_mark = dynamic_na.run_time_mark then
		  static_na.jvm_return_code;
	       elseif static_na.of_references and then dynamic_na.of_references then
		  static_na.jvm_return_code;
	       else
		  if run_control.no_check then
		     ca.runtime_error(up_rf.start_position,up_rf.current_type,
                     "System-level Validity error detected inside virtual %
		     %switching code.%N%
		     %Bad NATIVE_ARRAY type return type.");
		  end;
		  ca.opcode_pop;
		  space := rt.jvm_push_default;
		  rt.jvm_return_code;
	       end;
	    else
	       space := dyn_rf.result_type.jvm_convert_to(rt);
	       rt.jvm_return_code;
	    end;
	    if i = 1 and then boost then
	    else
	       ca.resolve_u2_branch(point);
	    end;
	    i := i - 1;
	 end;
	 if not boost then
	    ca.opcode_aload_0;
	    ca.runtime_error_bad_target(up_rf.start_position,up_rf.current_type,
               "System-level Validity error detected inside virtual %
	       %switching code.%N%
               %Bad target type for dynamic dispatch.");
	    if rt = Void then
	       ca.opcode_return;
	    else
	       space := rt.jvm_push_default;
	       rt.jvm_return_code;
	    end;
	 end;
      end;

end
