--          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://SmallEiffel.loria.fr
-- 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.
--
class JVM
   --
   -- Handling of Java Virtual Machine byte code generation.
   --

inherit CODE_PRINTER;

creation make

feature {NONE}

   make is
      do
      end;

feature

   current_frame: RUN_FEATURE;
         -- Current method or current field.

   argument_offset_of(an: ARGUMENT_NAME): INTEGER is
      require
         an /= Void
      do
         Result := current_frame.jvm_argument_offset(an);
      ensure
         Result >= 0
      end;

   local_offset_of(ln: LOCAL_NAME): INTEGER is
      require
         ln /= Void
      do
         Result := current_frame.jvm_local_variable_offset(ln);
      ensure
         Result >= 0
      end;

   std_is_equal(rc: RUN_CLASS; wa: ARRAY[RUN_FEATURE_2]) is
         -- Produce byte code to compare two operands at top of stack.
         -- Result `1'  (true) or `0' (false) is left at top of stack.
      require
         not rc.current_type.is_basic_eiffel_expanded
      local
         ca: like code_attribute;
         rf2: RUN_FEATURE_2;
         point1, point2, space, i: INTEGER;
      do
         ca := code_attribute;
         if wa = Void then
            if rc.current_type.is_expanded then
               ca.opcode_pop2;
               ca.opcode_iconst_1;
            else
               ca.opcode_swap;
               ca.opcode_pop;
               rc.opcode_instanceof;
            end;
         else
            ca.branches.clear; -- pop2 then false
            if rc.current_type.is_reference then
               ca.opcode_dup;
               rc.opcode_instanceof;
               ca.branches.add_last(ca.opcode_ifeq);
            end;
            from
               i := wa.upper;
            until
               i = 0
            loop
               rf2 := wa.item(i);
               space := rf2.result_type.jvm_stack_space - 1;
               if i > 1 then
                  ca.opcode_dup2;
               end;
               space := rc.opcode_getfield(rf2);
               if space = 0 then
                  ca.opcode_swap;
               else
                  ca.opcode_dup2_x1;
                  ca.opcode_pop2;
               end;
               space := rc.opcode_getfield(rf2);
               if i > 1 then
                  ca.branches.add_last(rf2.result_type.jvm_if_x_ne);
               else
                  point1 := rf2.result_type.jvm_if_x_ne;
               end;
               i := i - 1;
            end;
            ca.opcode_iconst_1;
            point2 := ca.opcode_goto;
            ca.resolve_branches;
            ca.opcode_pop2;
            ca.resolve_u2_branch(point1);
            ca.opcode_iconst_0;
            ca.resolve_u2_branch(point2);
         end;
      end;

   fields_by_fields_copy(wa: ARRAY[RUN_FEATURE_2]) is
         -- Stack : ..., destination, model => ..., destination
         -- Assume the checkcast is already done.
      local
         ca: like code_attribute;
         rf2: RUN_FEATURE_2;
         idx, space, i: INTEGER;
      do
         ca := code_attribute;
         if wa /= Void then
            from
               i := wa.upper;
            until
               i = 0
            loop
               ca.opcode_dup2;
               rf2 := wa.item(i);
               idx := constant_pool.idx_fieldref(rf2);
               space := rf2.result_type.jvm_stack_space;
               ca.opcode_getfield(idx,space - 1);
               ca.opcode_putfield(idx,-(space + 1));
               i := i - 1;
            end;
         end;
         ca.opcode_pop;
      end;

feature {RUN_FEATURE}

   add_field(rf: RUN_FEATURE) is
      require
         rf /= Void
      do
         check
            not fields.fast_has(rf)
         end;
         fields.add_last(rf);
      ensure
         fields.fast_has(rf)
      end;

feature {SMALL_EIFFEL}

   prepare_output_directory is
         -- A new output directory is created or the content of the old 
         -- existing one is cleared.
      require
         small_eiffel.is_ready
      local
         output_name: STRING;
         basic_directory: BASIC_DIRECTORY;
      do
         output_name := run_control.output_name;
         if output_name = Void then
            output_name := run_control.root_class.twin;
            output_name.to_lower;
         elseif output_name.has_suffix(class_suffix) then
            output_name.remove_suffix(class_suffix);
         end;
         echo.put_string("Trying to prepare directory %"");
         echo.put_string(output_name);
         echo.put_string("%" to store Java byte code.%N");
         basic_directory.connect_to(output_name);
         if basic_directory.is_connected then
            basic_directory.disconnect;
            basic_directory.remove_files_of(output_name);
	    -- *******
            -- Because a bug of BASIC_DIRECTORY.remove_files_of under
	    -- WINDOWS, we may need to recreate the directory itself :
            basic_directory.connect_to(output_name);
            if not basic_directory.is_connected then
               if basic_directory.create_new_directory(output_name) then
               end
            end
	    -- *******
         elseif basic_directory.create_new_directory(output_name) then
         end;
      ensure
         run_control.output_name /= Void
      end;

   write_jvm_root_class is
      local
         idx: INTEGER;
         cp: like constant_pool;
         ca: like code_attribute;
         output_name: STRING;
      do
         cp := constant_pool;
         ca := code_attribute
         output_name := run_control.output_name;
         system_tools.class_file_path(out_file_path,output_name,fz_jvm_root);
         bfw_connect(out_file,out_file_path);
         start_basic;
         this_class_idx := cp.idx_class2(jvm_root_class);
         super_class_idx := cp.idx_java_lang_object;
         -- Fields :
         args_field;
         manifest_string_pool.jvm_define_fields;
         once_routine_pool.jvm_define_fields;
         if run_control.no_check then
            field_info.add(9,cp.idx_utf8(fz_58),cp.idx_utf8(fz_41));
         end;
         -- Methods :
         method_info.add_init(fz_java_lang_object);
         -- The _initialize_eiffel_runtime static method :
         current_frame := Void;
         method_info.start(9,fz_28,fz_23);
         -- Set `args' field to store command line arguments.
         ca.opcode_aload_0;
         ca.opcode_putstatic(args_field_idx,-1);
         manifest_string_pool.jvm_initialize_fields;
         once_routine_pool.jvm_initialize_fields;
         if run_control.no_check then
            idx := cp.idx_fieldref3(jvm_root_class,fz_58,fz_41);
            ca.opcode_iconst_0;
            ca.opcode_putstatic(idx,-1);
         end;
         ca.opcode_return;
         method_info.finish;
         --    For switches :
         switch_collection.jvm_define;
         finish_class;
      end;

   write_main_class(rf3: RUN_FEATURE_3) is
         -- Write Java Byte Code for main class to call `rf3'.
      require
         rf3 /= Void;
         small_eiffel.is_ready
      local
         idx: INTEGER;
         cp: like constant_pool;
         ca: like code_attribute;
      do
         cp := constant_pool;
         ca := code_attribute;
         out_file_path.copy(run_control.output_name);
         out_file_path.append(class_suffix);
         bfw_connect(out_file,out_file_path);
         start_basic;
         this_class_idx := cp.idx_class2(run_control.output_name);
         super_class_idx := cp.idx_java_lang_object;
         -- Methods :
         --    The main method :
         current_frame := Void;
         method_info.add_init(fz_java_lang_object);
         method_info.start(9,fz_main,fz_23);
         ca.opcode_aload_0;
         idx := cp.idx_methodref3(jvm_root_class,fz_28,fz_23);
         ca.opcode_invokestatic(idx,0);
         rf3.run_class.jvm_basic_new;
         idx := cp.idx_methodref(rf3);
         ca.opcode_invokevirtual(idx,-1);
         ca.opcode_return;
         method_info.finish;
         finish_class;
      end;

feature {CODE_ATTRIBUTE}

   max_locals: INTEGER is
      do
         if current_frame /= Void then
            Result := current_frame.jvm_max_locals;
         else
            Result := 4;
         end;
      end;

feature {RUN_CLASS,SWITCH_COLLECTION}

   set_current_frame(cf: like current_frame) is
      do
         current_frame := cf;
      end;

feature {CP_INFO}

   b_put_u1(byte: CHARACTER) is
      require
         byte.code <= 255
      do
         out_file.put_byte(byte);
      end;

feature {CONSTANT_POOL,FIELD_INFO,METHOD_INFO}

   b_put_u2(u2: INTEGER) is
      do
         b_put_u1((u2 // 256).to_character);
         b_put_u1((u2 \\ 256).to_character);
      end;

feature {CP_INFO,FIELD_INFO,METHOD_INFO}

   b_put_byte_string(str: STRING) is
      require
         str /= Void
      local
         i: INTEGER;
      do
         from
            i := 1;
         until
            i > str.count
         loop
            b_put_u1(str.item(i));
            i := i + 1;
         end;
      end;

feature {NATIVE_SMALL_EIFFEL}

   push_se_argc is
      local
         ca: like code_attribute;
      do
         ca := code_attribute;
         ca.opcode_getstatic(args_field_idx,1);
         ca.opcode_arraylength;
         ca.opcode_iconst_1;
         ca.opcode_iadd;
      end;

   push_se_argv is
      local
         point1, point2, i: INTEGER;
         ca: like code_attribute;
      do
         ca := code_attribute;
         ca.opcode_getstatic(args_field_idx,1);
         i := push_ith_argument(1)
         ca.opcode_dup;
         point1 := ca.opcode_ifeq;
         ca.opcode_iconst_1;
         ca.opcode_isub;
         ca.opcode_aaload;
         point2 := ca.opcode_goto;
         ca.resolve_u2_branch(point1);
         ca.opcode_pop2;
         i := constant_pool.idx_string(run_control.output_name);
         ca.opcode_ldc(i);
         ca.resolve_u2_branch(point2);
         ca.opcode_java_string2eiffel_string;
      end;

feature {CALL_PROC_CALL}

   call_proc_call_mapping(cpc: CALL_PROC_CALL) is
      local
         target: EXPRESSION;
         target_type: TYPE;
         rc: RUN_CLASS;
         running: ARRAY[RUN_CLASS];
         switch: SWITCH;
         rf: RUN_FEATURE;
         result_type: TYPE;
      do
         target := cpc.target;
         target_type := target.result_type.run_type;
         rf := cpc.run_feature;
         if target.is_current then
            push_direct(cpc);
            rf.mapping_jvm;
            pop
         elseif target.is_manifest_string then
            push_direct(cpc);
            rf.mapping_jvm;
            pop
         elseif target_type.is_expanded then
            push_direct(cpc);
            rf.mapping_jvm;
            pop
         else
            running := target_type.run_class.running;
            check
               running /= Void
            end;
            if running.count = 1 then
               push_check(cpc);
               rf := running.first.dynamic(rf);
               rf.mapping_jvm;
               pop
            else
               switch_count := switch_count + 1;
               switch.jvm_mapping(cpc);
            end;
         end;
         result_type := rf.result_type;
         -- Optional checkcast for result :
         if result_type /= Void then
            if not result_type.is_none then
               rc := result_type.run_class;
               if rc /= Void then
                  running := rc.running;
                  if running /= Void and then running.count = 1 then
                     running.first.opcode_checkcast;
                  end;
               end;
            end;
         end;
      end;

feature {RUN_CLASS}

   prepare_fields is
      local
         i: INTEGER;
         rf: RUN_FEATURE;
      do
         from
            i := fields.upper;
         until
            i < 0
         loop
            rf := fields.item(i);
            rf.jvm_define;
            i := i - 1;
         end;
      end;

   prepare_methods is
      local
         i: INTEGER;
      do
         from
            i := methods.upper;
         until
            i < 0
         loop
            set_current_frame(methods.item(i));
            current_frame.jvm_define;
            i := i - 1;
         end;
      end;

   start_new_class(rc: RUN_CLASS) is
      require
         run_control.output_name /= Void
      local
         name_idx, type_idx: INTEGER;
         cp: like constant_pool;
         output_name: STRING;
      do
         cp := constant_pool;
         start_basic;
         tmp_string.copy(rc.unqualified_name);
         output_name := run_control.output_name;
         system_tools.class_file_path(out_file_path,output_name,tmp_string);
         bfw_connect(out_file,out_file_path);
         this_class_idx := rc.jvm_constant_pool_index;
         super_class_idx := cp.idx_jvm_root_class;
         method_info.add_init(jvm_root_class);
         -- <clinit> :
         method_info.start(9,fz_76,fz_29);
         if run_control.generating_type_used then
            name_idx := cp.idx_utf8(as_generating_type);
            type_idx := cp.idx_eiffel_string_descriptor;
            field_info.add(9,name_idx,type_idx);
            name_idx := cp.idx_fieldref5(this_class_idx,name_idx,type_idx);
            code_attribute.opcode_aconst_null;
            code_attribute.opcode_putstatic(name_idx,-1);
         end;
         if run_control.generating_type_used then
            name_idx := cp.idx_utf8(as_generator);
            field_info.add(9,name_idx,type_idx);
            name_idx := cp.idx_fieldref5(this_class_idx,name_idx,type_idx);
            code_attribute.opcode_aconst_null;
            code_attribute.opcode_putstatic(name_idx,-1);
         end;
         code_attribute.opcode_return;
         method_info.finish;
         rc.jvm_define_class_invariant;
      end;

   finish_class is
         -- Really print the class file.
      do
         put_magic;
         put_minor_version;
         put_major_version;
         constant_pool.write_bytes;
         b_put_u2(access_flags);
         b_put_u2(this_class_idx);
         b_put_u2(super_class_idx);
         -- interfaces_count :
         b_put_u2(0);
         field_info.write_bytes;
         method_info.write_bytes;
         -- attributes_count :
         b_put_u2(0);
         out_file.disconnect;
      end;

   push_expanded_initialize(rf: RUN_FEATURE) is
      do
         stack_push(C_expanded_initialize);
         stack_rf.put(rf,top);
      end;

   define_class_invariant_method(ci: CLASS_INVARIANT) is
      require
         run_control.invariant_check;
         ci /= Void
      local
         ca: like code_attribute;
         cp: like constant_pool;
      do
         ca := code_attribute;
         cp := constant_pool;
         method_info.start(1,fz_invariant,fz_29);
         stack_push(C_switch);
         ci.compile_to_jvm(true);
         pop;
         ca.opcode_return;
         method_info.finish;
      end;

feature {NATIVE}

   inside_twin(cpy: RUN_FEATURE) is
      do
         stack_push(C_inside_twin);
         stack_rf.put(cpy,top);
         cpy.mapping_jvm;
         pop;
      end;

feature {CREATION_CALL}

   inside_new(rf: RUN_FEATURE; cpc: CALL_PROC_CALL) is
      require
         rf /= Void;
         cpc /= Void
      do
         stack_push(C_inside_new);
         stack_rf.put(rf,top);
         stack_cpc.put(cpc,top);
         rf.mapping_jvm;
         pop;
      end;

feature {RUN_FEATURE,NATIVE}

   add_method(rf: RUN_FEATURE) is
      require
         rf /= Void
      do
         check
            not methods.fast_has(rf)
         end;
         methods.add_last(rf);
      ensure
         methods.fast_has(rf)
      end;

   target_position: POSITION is
      local
         code: INTEGER;
      do
         code := stack_code.item(top);
         inspect
            code
         when C_direct_call, C_check_id then
            Result := stack_cpc.item(top).start_position;
         when C_inside_twin then
         when C_switch then
         when C_expanded_initialize then
         when C_inside_new then
            Result := stack_cpc.item(top).start_position;
         else
         end;
      end;

   push_target is
         -- Produce java byte code in order to push target on the
         -- jvm stack.
      local
         code: INTEGER;
      do
         code := stack_code.item(top);
         inspect
            code
         when C_direct_call then
            stack_cpc.item(top).target.compile_to_jvm;
         when C_check_id then
            opcode_check_id_for(stack_cpc.item(top).target);
         when C_switch then
            code_attribute.opcode_aload_0;
         when C_expanded_initialize then
            code_attribute.opcode_dup;
         when C_inside_new then
            code_attribute.opcode_dup;
         when C_inside_twin then
            code_attribute.opcode_aload_1;
         end;
      end;

   push_target_as_target is
         -- Same as `push_target' but with class invariant check
         -- and the checkcast opcode.
      local
         target: EXPRESSION;
         rc: RUN_CLASS;
         code: INTEGER;
      do
         code := stack_code.item(top);
         inspect
            code
         when C_direct_call then
            target := stack_cpc.item(top).target;
            target.compile_target_to_jvm;
         when C_check_id then
            target := stack_cpc.item(top).target;
            opcode_check_id_for(target);
         when C_switch then
            code_attribute.opcode_aload_0;
            rc := stack_rf.item(top).run_class;
            rc.opcode_checkcast;
         when C_expanded_initialize then
            code_attribute.opcode_dup;
         when C_inside_new then
            code_attribute.opcode_dup;
         when C_inside_twin then
            code_attribute.opcode_aload_1;
         when C_precursor then
            stack_rf.item(top).current_type.jvm_push_local(0);
         end;
      end;

   drop_target is
      local
         code: INTEGER;
         cpc: CALL_PROC_CALL;
         e: EXPRESSION;
      do
         code := stack_code.item(top);
         inspect
            code
         when C_direct_call, C_check_id then
            cpc := stack_cpc.item(top);
            e ?= cpc;
            if e = Void or else not e.can_be_dropped then
               cpc.target.compile_to_jvm;
               if cpc.run_feature.current_type.jvm_stack_space = 1 then
                  code_attribute.opcode_pop;
               else
                  code_attribute.opcode_pop2;
               end;
            end;
         when C_switch then
         when C_expanded_initialize then
         when C_inside_new then
         when C_inside_twin then
         end;
      end;

   drop_ith_argument(i: INTEGER) is
      local
         space: INTEGER;
      do
         from
            space := push_ith_argument(i);
         until
            space = 0
         loop
            code_attribute.opcode_pop;
            space := space - 1;
         end;
      end;

   push_arguments: INTEGER is
      local
         code: INTEGER;
         cpc: CALL_PROC_CALL;
         eal: EFFECTIVE_ARG_LIST;
         rf, dyn_rf: RUN_FEATURE;
         fal: FORMAL_ARG_LIST;
      do
         code := stack_code.item(top);
         inspect
            code
         when C_direct_call, C_check_id then
            cpc := stack_cpc.item(top);
            eal := cpc.arguments;
            if eal /= Void then
               rf := cpc.run_feature;
               fal := rf.arguments;
               Result := eal.compile_to_jvm(fal);
            end;
         when C_switch then
            rf := stack_static_rf.item(top);
            dyn_rf := stack_rf.item(top);
            fal := rf.arguments;
            if fal /= Void then
               Result := fal.jvm_switch_push(dyn_rf.arguments);
            end;
         when C_expanded_initialize then
            check
               stack_rf.item(top).arguments = Void
            end;
         when C_inside_new then
            cpc := stack_cpc.item(top);
            eal := cpc.arguments;
            if eal /= Void then
               rf := stack_rf.item(top);
               fal := rf.arguments;
               Result := eal.compile_to_jvm(fal);
            end;
         when C_inside_twin then
            Result := push_ith_argument(1);
         when C_precursor then
            eal := stack_args.item(top);
            if eal /= Void then
               rf := stack_rf.item(top);
               fal := rf.arguments;
               Result := eal.compile_to_jvm(fal);
            end;
         end;
      end;

   push_ith_argument(i: INTEGER): INTEGER is
      local
         code: INTEGER;
         cpc: CALL_PROC_CALL;
         eal: EFFECTIVE_ARG_LIST;
         rf, dyn_rf: RUN_FEATURE;
         fal: FORMAL_ARG_LIST;
      do
         code := stack_code.item(top);
         inspect
            code
         when C_direct_call, C_check_id then
            cpc := stack_cpc.item(top);
            eal := cpc.arguments;
            rf := cpc.run_feature;
            fal := rf.arguments;
            Result := eal.compile_to_jvm_ith(fal,i);
         when C_switch then
            rf := stack_static_rf.item(top);
            dyn_rf := stack_rf.item(top);
            fal := rf.arguments;
            Result := fal.jvm_switch_push_ith(dyn_rf.arguments,i);
         when C_expanded_initialize then
            check
               false
            end;
         when C_inside_new then
            cpc := stack_cpc.item(top);
            eal := cpc.arguments;
            rf := stack_rf.item(top);
            fal := rf.arguments;
            Result := eal.compile_to_jvm_ith(fal,i);
         when C_inside_twin then
            code_attribute.opcode_aload_0;
            Result := 1;
         end;
      end;

feature {SWITCH}

   push_switch(rf, static_rf: RUN_FEATURE) is
      require
         rf /= Void;
         static_rf /= Void;
         rf.run_class.dynamic(static_rf) = rf
      do
         stack_push(C_switch);
         stack_rf.put(rf,top);
         stack_static_rf.put(static_rf,top);
      end;

feature {NONE}

   fields: FIXED_ARRAY[RUN_FEATURE] is
      once
         !!Result.with_capacity(4);
      end;

   methods: FIXED_ARRAY[RUN_FEATURE] is
      once
         !!Result.with_capacity(64);
      end;

   access_flags: INTEGER is 33;
         -- To allow any external access.

   this_class_idx: INTEGER;

   super_class_idx: INTEGER;

   start_basic is
      do
         fields.clear;
         methods.clear;
         constant_pool.clear;
         field_info.clear;
         method_info.clear;
      end;

   put_magic is
         -- CAFEBABE ;-)
      do
         b_put_byte_string("%/202/%/254/%/186/%/190/");
      end;

   put_minor_version is
      do
         b_put_u2(3);
      end;

   put_major_version is
      do
         b_put_byte_string("%/0/%/45/");
      end;

   args_field is
         -- Define `args' field to store command line arguments.
      local
         args_idx, jaos_idx: INTEGER;
         cp: like constant_pool;
      do
         cp := constant_pool;
         args_idx := cp.idx_utf8(fz_74);
         jaos_idx := cp.idx_utf8(fz_75);
         field_info.add(9,args_idx,jaos_idx);
      end;

   args_field_idx: INTEGER is
      local
         cp: like constant_pool;
      do
         cp := constant_pool;
         Result := cp.idx_fieldref3(jvm_root_class,fz_74,fz_75);
      end;

   out_file: BINARY_FILE_WRITE is
         -- Current output class file.
      once
         !!Result.make;
      end;

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

   tmp_string: STRING is
      once
         !!Result.make(16);
      end;

   push_direct(cpc: CALL_PROC_CALL) is
      require
         cpc /= Void
      do
         stack_push(C_direct_call);
         stack_cpc.put(cpc,top);
         direct_call_count := direct_call_count + 1;
      end;

   push_check(cpc: CALL_PROC_CALL) is
      require
         cpc /= Void
      do
         stack_push(C_check_id);
         stack_cpc.put(cpc,top);
      end;

   opcode_check_id_for(e: EXPRESSION) is
         -- Produce byte-code for `e' with non-void check and id
         -- check.
         -- Finally, also add a `checkcast' opcode.
      require
         e.result_type.run_class.running.count = 1
      local
         point1, point2: INTEGER;
         rc: RUN_CLASS;
         ct: TYPE;
         ca: like code_attribute;
      do
         e.compile_to_jvm;
         rc := e.result_type.run_class.running.first;
         ct := rc.current_type;
         if run_control.no_check then
            ca := code_attribute;
            ca.opcode_dup;
            point1 := ca.opcode_ifnull;
            ca.opcode_dup;
            rc.opcode_instanceof;
            point2 := ca.opcode_ifne;
            ca.resolve_u2_branch(point1);
            ca.opcode_dup;
            ca.runtime_error_bad_target(e.start_position,ct,Void);
            ca.resolve_u2_branch(point2);
            rc.jvm_check_class_invariant;
         end;
         rc.opcode_checkcast;
      end;

   bfw_connect(bfw: BINARY_FILE_WRITE; path: STRING) is
      require
         not bfw.is_connected;
         path /= Void
      do
         bfw.connect_to(path);
         if bfw.is_connected then
            echo.put_string("Writing %"");
            echo.put_string(path);
            echo.put_string("%" file.%N");
         else
            echo.w_put_string("Cannot write file %"");
            echo.w_put_string(path);
            echo.w_put_string(fz_b0);
            die_with_code(exit_failure_code);
         end;
      ensure
         bfw.is_connected
      end;

end -- JVM

