note description: "Visitor for BYTE_NODE objects which generates the MSIL code." class IL_NODE_GENERATOR inherit BYTE_NODE_VISITOR redefine is_valid end SHARED_BYTE_CONTEXT export {NONE} all end SHARED_WORKBENCH export {NONE} all end IL_CONST export {NONE} all end REFACTORING_HELPER export {NONE} all end SHARED_TYPES export {NONE} all end SHARED_BN_STATELESS_VISITOR export {NONE} all end SHARED_IL_CONSTANTS export {NONE} all end COMPILER_EXPORTER export {NONE} all end IL_PREDEFINED_STRINGS export {NONE} all end INTERNAL_COMPILER_STRING_EXPORTER feature -- Status report is_valid: BOOLEAN -- Is Current valid for visiting? do Result := il_generator /= Void end feature -- Generation generate_il (a_code_generator: like il_generator; a_body: BYTE_CODE) -- Generate IL byte code `a_body'. local r_type: TYPE_A local_list: ARRAYED_LIST [TYPE_A] inh_assert: INHERITED_ASSERTION feat: FEATURE_I end_of_assertion: IL_LABEL end_of_routine: IL_LABEL l_saved_in_assertion, l_saved_supplier_precondition, l_saved_in_precondition: INTEGER l_saved_rescue_level: INTEGER l_nb_precond: INTEGER l_old_expr_count: INTEGER keep: BOOLEAN is_once_creation: BOOLEAN do il_generator := a_code_generator keep := context.workbench_mode or else system.keep_assertions -- Put a breakable point on feature name. il_generator.put_line_info (a_body.start_line_number) il_generator.flush_sequence_points (context.class_type) if a_body.once_manifest_string_count > 0 then il_generator.generate_once_string_allocation (a_body.once_manifest_string_count) end local_list := context.local_list local_list.wipe_out feat := Context.current_feature inh_assert := Context.inherited_assertion inh_assert.init Context.set_origin_has_precondition (True) if not Context.associated_class.is_basic and then feat.assert_id_set /= Void then --! Do not get inherited pre & post for basic types a_body.formulate_inherited_assertions (feat.assert_id_set) end -- Set up the local variables a_body.setup_local_variables (True) is_once_creation := a_body.is_once_creation -- Initialize use of local variables to IL side r_type := context.real_type (if is_once_creation then -- Record the type of the class itself. context.current_type else a_body.result_type end) if not r_type.is_void then il_generator.put_result_info (r_type) end -- Set number of meaningful local variables. i.e. we -- do not include local variables that are temporarly -- generated for metamorphose and other operations. il_generator.set_local_count (local_list.count) if not local_list.is_empty then generate_il_local_info (local_list) end if keep then -- Generate local variable to save assertions level. context.add_local (boolean_type) l_saved_supplier_precondition := context.local_list.count context.set_saved_supplier_precondition (l_saved_supplier_precondition) il_generator.put_dummy_local_info (boolean_type, l_saved_supplier_precondition) il_generator.generate_save_supplier_precondition il_generator.generate_local_assignment (l_saved_supplier_precondition) end if a_body.rescue_clause /= Void or else a_body.is_once then -- Generate local variable to save `rescue_level'. context.add_local (integer_type) l_saved_rescue_level := context.local_list.count context.set_saved_rescue_level (l_saved_rescue_level) il_generator.put_dummy_local_info (integer_type, l_saved_rescue_level) il_generator.generate_get_rescue_level il_generator.generate_local_assignment (l_saved_rescue_level) end if a_body.rescue_clause /= Void then -- Generate local variable to save assertions level. context.add_local (boolean_type) l_saved_in_assertion := context.local_list.count il_generator.put_dummy_local_info (boolean_type, l_saved_in_assertion) il_generator.generate_in_assertion_status il_generator.generate_local_assignment (l_saved_in_assertion) -- Generate local variable to save in precondition status. context.add_local (boolean_type) l_saved_in_precondition := context.local_list.count il_generator.put_dummy_local_info (boolean_type, l_saved_in_precondition) il_generator.generate_in_precondition_status il_generator.generate_local_assignment (l_saved_in_precondition) -- Generate local variable to save `last_exception' object. context.add_local (system_exception_type) saved_last_exception := context.local_list.count il_generator.put_dummy_local_info (system_exception_type, saved_last_exception) il_generator.generate_last_exception il_generator.generate_local_assignment (saved_last_exception) -- Create retry label. retry_label := il_generator.create_label il_generator.mark_label (cil_node_generator.retry_label) end -- Make IL code for preconditions if context.origin_has_precondition and (a_body.precondition /= Void or inh_assert.has_precondition) then if keep then generate_il_precondition (a_body) elseif System.is_precompile_finalized then l_nb_precond := 0 if a_body.precondition /= Void then l_nb_precond := l_nb_precond + a_body.precondition.count end if inh_assert.has_precondition then l_nb_precond := l_nb_precond + inh_assert.precondition_list_count end generate_ghost_debug_infos (a_body, l_nb_precond) end end -- Generate prologue for once routine if a_body.is_once then il_generator.generate_once_prologue (is_once_creation) end if a_body.rescue_clause /= Void then -- Start "try" block that will catch exceptions -- and invoke rescue clause il_generator.generate_start_exception_block end_of_routine := il_generator.create_label end if keep and then (a_body.old_expressions /= Void or inh_assert.has_postcondition) then context.set_assertion_type ({ASSERT_TYPE}.in_postcondition) end_of_assertion := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_ensure) il_generator.branch_on_false (end_of_assertion) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status -- Calculate how many try/catch blocks are needed for old expression evaluation. if a_body.old_expressions /= Void then l_old_expr_count := a_body.old_expressions.count end if inh_assert.has_postcondition then l_old_expr_count := l_old_expr_count + inh_assert.old_expression_count end il_generator.prepare_old_expresssion_blocks (l_old_expr_count) if a_body.old_expressions /= Void then from a_body.old_expressions.start until a_body.old_expressions.after loop generate_il_old_init (a_body.old_expressions.item) a_body.old_expressions.forth end end if inh_assert.has_postcondition then inh_assert.generate_il_old_exp_init (Current) end il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (end_of_assertion) context.set_assertion_type (0) end -- Initialize local variables (if required). if r_type.is_true_expanded and then not r_type.is_external then if attached {CL_TYPE_A} r_type as cl_type_i then il_generator.create_expanded_object (cl_type_i) end il_generator.generate_result_assignment end if not local_list.is_empty then initialize_locals (local_list) end if is_once_creation then il_generator.generate_current il_generator.generate_result_assignment end if a_body.compound /= Void then if a_body.body_index = context.copy_body_index then generate_copy else a_body.compound.process (Current) end il_generator.flush_sequence_points (context.class_type) end -- Make IL code for postcondition if keep and then (a_body.postcondition /= Void or inh_assert.has_postcondition) then end_of_assertion := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_ensure) il_generator.branch_on_false (end_of_assertion) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status context.set_assertion_type ({ASSERT_TYPE}.in_postcondition) if a_body.postcondition /= Void then a_body.postcondition.process (Current) Il_generator.flush_sequence_points (context.class_type) end if inh_assert.has_postcondition then inh_assert.generate_il_postcondition (Current) end context.set_assertion_type (0) il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (end_of_assertion) end if keep then il_generator.generate_local (l_saved_supplier_precondition) il_generator.generate_restore_supplier_precondition end if a_body.rescue_clause /= Void then -- Restore last exception at the end of normal exit of the routine. il_generator.generate_local (saved_last_exception) il_generator.generate_restore_last_exception -- Restore rescue level at the end of normal exit of the routine. il_generator.generate_local (l_saved_rescue_level) il_generator.generate_set_rescue_level il_generator.generate_start_rescue -- Restore precondition status. il_generator.generate_local (l_saved_in_precondition) il_generator.generate_set_precondition_status -- Restore assertion level. il_generator.generate_local (l_saved_in_assertion) il_generator.generate_set_assertion_status -- Generate code of rescue. a_body.rescue_clause.process (Current) -- Setup rescue level before rethrowing. il_generator.put_integer_32_constant (0) il_generator.generate_set_rescue_level il_generator.generate_end_exception_block il_generator.mark_label (end_of_routine) end inh_assert.wipe_out il_generator.put_debug_info (a_body.end_location) il_generator.flush_sequence_points (context.class_type) generate_il_return (a_body) il_generator := Void retry_label := Void end generate_il_node (a_code_generator: IL_CODE_GENERATOR; a_node: BYTE_NODE) -- Generate `a_node' using `il_generator'. require a_node_not_void: a_node /= Void a_code_generator_not_void: a_code_generator /= Void a_code_generator_valid: il_generator = Void or else il_generator = a_code_generator do if il_generator = Void then il_generator := a_code_generator a_node.process (Current) il_generator := Void else a_node.process (Current) end end frozen generate_il_assignment (a_node: ACCESS_B; source_type: TYPE_A) -- Generate source assignment IL code. require is_valid: is_valid a_node_not_void: a_node /= Void is_writable: not a_node.read_only source_type_not_void: source_type /= Void local target_type: TYPE_A do target_type := context.real_type (a_node.type) generate_il_simple_assignment (a_node, target_type, source_type) end generate_reattachment (source: EXPR_B; source_type, target_type: TYPE_A) -- Generate code that ensures semantics of reattachment -- of expression of `source_type' to entity of `target_type' -- assuming the expression value is on the stack. require source_type_not_void: source_type /= Void target_type_not_void: target_type /= Void local label: IL_LABEL label_done: IL_LABEL do if source_type.is_expanded then if not source_type.is_basic and then not source_type.is_external then -- Generate code to copy object at compile time if target_type.is_expanded then -- Box object before calling run-time feature il_generator.generate_metamorphose (target_type) end generate_twin_routine ({PREDEFINED_NAMES}.twin_name_id, target_type) end elseif target_type.is_basic then il_generator.generate_check_cast (source_type, target_type) il_generator.generate_load_address (target_type) il_generator.generate_load_from_address_as_basic (target_type) elseif source.is_dynamic_clone_required (source_type) and then (source_type.is_external and then target_type.is_external and then source_type.base_class.is_interface and then target_type.base_class.is_interface or else not source_type.is_external and then not source_type.is_generated_as_single_type and then not source_type.is_optimized_as_frozen) then -- Generate code to copy object at run-time -- depending on its dynamic type il_generator.duplicate_top il_generator.generate_is_true_instance_of (system.system_value_type_class.compiled_class.types.first.type) label := il_generator.create_label il_generator.branch_on_false (label) generate_twin_routine ({PREDEFINED_NAMES}.twin_name_id, target_type) if target_type.is_expanded then -- Ensure a value of an expanded type is on the stack instead of a reference. label_done := il_generator.create_label il_generator.branch_to (label_done) il_generator.mark_label (label) il_generator.generate_unmetamorphose (target_type) il_generator.mark_label (label_done) else il_generator.mark_label (label) end elseif target_type.is_expanded then il_generator.generate_unmetamorphose (target_type) end end generate_il_load_value (b: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]) -- Load value of the inspect expression of construct `b`. require is_valid: is_valid node_attached: b /= Void do if b.switch.is_fast_as_local then b.switch.process (Current) else il_generator.duplicate_top end end generate_il_old_init (a_node: UN_OLD_B) -- Generate old initialization of `a_node'. require is_valid: is_valid a_node_not_void: a_node /= Void do il_generator.generate_start_old_try_block (a_node.exception_position) a_node.expr.process (Current) il_generator.generate_local_assignment (a_node.position) il_generator.generate_catch_old_exception_block (a_node.exception_position) end generate_il_precondition_node (a_node: ASSERT_B; a_failure_block: IL_LABEL) -- Generate `a_node' as precondition require is_valid: is_valid a_node_not_void: a_node /= Void a_failure_block_not_void: a_failure_block /= Void do generate_il_line_info (a_node, True) a_node.expr.process (Current) il_generator.generate_precondition_check (a_node.tag, a_failure_block) end generate_il_when_part (b: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]; case_index: INTEGER; labels: ARRAY [detachable IL_LABEL]) -- Generate code for When_part idetified by `case_index' and -- adjust `labels' if required. require is_valid: is_valid node_attached: attached b case_list_attached: attached b.case_list valid_case_index: 1 <= case_index and case_index <= b.case_list.count labels_attached: attached labels valid_labels: labels.lower = -1 and labels.upper = b.case_list.count local case_label: IL_LABEL do case_label := labels [case_index] if not attached case_label then case_label := il_generator.create_label labels [case_index] := case_label end il_generator.mark_label (case_label) if attached b.case_list as cs then if attached {CASE_B} cs [case_index] as c then if attached c.compound as compound then compound.process (Current) end elseif attached {CASE_EXPRESSION_B} cs [case_index] as c then if not b.switch.is_fast_as_local then -- Remove the duplication made for case comparison. il_generator.pop end generate_expression_il_for_type (c.content, context.real_type (if attached {INSPECT_EXPRESSION_B} b as e then e.type else any_type end)) else check know_case_type: False end end else check from_precondition: False end end il_generator.branch_to (labels.item (-1)) ensure label_attached: attached labels [case_index] end feature {NONE} -- Implementation generate_il_return (a_body: BYTE_CODE) -- Generate IL final return statement require is_valid: is_valid a_body_not_voyd: a_body /= Void local has_return_value: BOOLEAN do if a_body.is_once then il_generator.generate_once_epilogue else if not context.real_type (a_body.result_type).is_void then il_generator.generate_result has_return_value := True end il_generator.generate_return (has_return_value) end end generate_il_precondition (a_body: BYTE_CODE) -- Generate IL precondition checking blocks. require is_valid: is_valid a_body_not_voyd: a_body /= Void local inh_assert: INHERITED_ASSERTION end_of_assertion, success_block, failure_block: IL_LABEL need_end_label: BOOLEAN do context.set_assertion_type ({ASSERT_TYPE}.in_precondition) inh_assert := Context.inherited_assertion end_of_assertion := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_require) il_generator.branch_on_false (end_of_assertion) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status il_generator.put_boolean_constant (True) il_generator.generate_set_precondition_status if attached a_body.precondition as ps then need_end_label := True success_block := il_generator.create_label failure_block := il_generator.create_label ⟳ p: ps ¦ generate_il_precondition_node (p, failure_block) ⟲ il_generator.branch_to (success_block) il_generator.mark_label (failure_block) Il_generator.flush_sequence_points (context.class_type) end if inh_assert.has_precondition then inh_assert.generate_il_precondition (Current) if need_end_label then il_generator.mark_label (success_block) end else if need_end_label then il_generator.generate_precondition_violation il_generator.mark_label (success_block) end end il_generator.put_boolean_constant (False) il_generator.generate_set_precondition_status il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (end_of_assertion) context.set_assertion_type (0) end generate_il_local_info (local_list: ARRAYED_LIST [TYPE_A]) -- Generate IL info for local variables in `local_list'. require is_valid: is_valid local_list_not_void: local_list /= Void local_list_not_empty: not local_list.is_empty local i: INTEGER do i := 1 if (System.line_generation or context.workbench_mode) and then attached context.current_feature.real_body as l_body and then attached {ROUTINE_AS} l_body.content as routine_as and then attached routine_as.locals as rout_locals then -- Generate debug information for local variables. -- `local_list' is in the same order as `routine_as.locals' -- so this is easy to find for each element of `local_list' -- its name in `routine_as.locals'. from rout_locals.start local_list.start until local_list.after loop if not rout_locals.after then across rout_locals.item.id_list as ids loop il_generator.put_local_info (context.real_type (local_list.item), ids.item) local_list.forth end rout_locals.forth else il_generator.put_dummy_local_info (context.real_type (local_list.item), i) i := i + 1 local_list.forth end end else -- Debug information is not needed or routine has no local variables. -- Generate local variables without debug information. across local_list as l loop il_generator.put_nameless_local_info (context.real_type (l.item), i) i := i + 1 end end end initialize_locals (local_list: ARRAYED_LIST [TYPE_A]) -- Generate code to initialize local variables of the current routine -- taking their types from `local_list'. require is_valid: is_valid local_list_not_void: local_list /= Void local_list_not_empty: not local_list.is_empty local i, nb: INTEGER do -- We only initialize expanded locals defined by user -- not the intermediate one required for the code generation. from local_list.start i := 1 nb := context.byte_code.local_count until local_list.after or i > nb loop if attached {CL_TYPE_A} context.real_type (local_list.item) as l_local_type and then l_local_type.is_true_expanded and then not l_local_type.is_external then il_generator.create_expanded_object (l_local_type) il_generator.generate_local_assignment (i) end i := i + 1 local_list.forth end end generate_copy -- Generate body of feature `copy' from ANY. local class_type, l_old_class_type: CLASS_TYPE cl_type_i: CL_TYPE_A skeleton: SKELETON feature_id: INTEGER do class_type := context.original_class_type if class_type.is_expanded then cl_type_i := class_type.type skeleton := class_type.skeleton if skeleton /= Void then l_old_class_type := context.class_type context.set_class_type (class_type) from skeleton.start until skeleton.after loop feature_id := skeleton.item.feature_id il_generator.generate_current il_generator.generate_argument (1) if not cl_type_i.is_basic then il_generator.generate_metamorphose (cl_type_i) il_generator.generate_load_address (cl_type_i) il_generator.generate_attribute (True, cl_type_i, feature_id) end il_generator.generate_attribute_assignment (True, cl_type_i, feature_id) skeleton.forth end context.set_class_type (l_old_class_type) end else il_generator.generate_current il_generator.generate_argument (1) il_generator.generate_call_to_standard_copy end end feature -- Access il_generator: IL_CODE_GENERATOR -- Actual IL code generator. is_in_creation_call: BOOLEAN -- Is current call a creation instruction is_in_external_creation_call: BOOLEAN -- Is current call a creation instruction on an external type? is_nested_call: BOOLEAN -- Is current call from a NESTED_B node? is_object_load_required: BOOLEAN -- If we manipulate an expanded and try to access the object, we need -- to load the object from its address. is_this_argument_current: BOOLEAN -- Is current argument loaded to the stack a Current? is_last_argument_current: BOOLEAN -- Is last argument loaded to the stack a Current? retry_label: IL_LABEL -- Label used for `retry' clause. feature {NONE} -- Access saved_last_exception: INTEGER -- Number of local to store `last_exception' feature {NONE} -- Routine visitor process_std_byte_code (a_node: STD_BYTE_CODE) -- Process current element. do end feature {BYTE_NODE} -- Implementation visitors process_hidden_if_b (a_node: HIDDEN_IF_B) -- Process `a_node'. do process_if_b (a_node) end process_hidden_b (a_node: HIDDEN_B) -- Process `a_node'. do context.enter_hidden_code process_byte_list (a_node) context.exit_hidden_code end process_do_rescue_b (a_node: DO_RESCUE_B) -- Process `a_node' do -- Todo to_implement ("Implement " + generator + ".process_rescue_b") a_node.process (Current) end process_try_b (a_node: TRY_B) -- Process `a_node' do -- Todo to_implement ("Implement " + generator + ".process_try_b") a_node.process (Current) end feature {NONE} -- Visitors process_access_expr_b (a_node: ACCESS_EXPR_B) -- Process `a_node'. local l_is_nested_call: like is_nested_call l_type_i: TYPE_A do l_is_nested_call := is_nested_call is_nested_call := False a_node.expr.process (Current) if l_is_nested_call then l_type_i := context.real_type (a_node.type) if l_type_i.is_true_expanded and then not l_type_i.base_class.is_enum then generate_il_metamorphose (l_type_i, l_type_i, True) end end end process_address_b (a_node: ADDRESS_B) -- Process `a_node'. local l_target_type: CL_TYPE_A l_target_feature_id: INTEGER do l_target_type := context.context_class_type.type if l_target_type.is_expanded then l_target_feature_id := l_target_type.base_class.feature_of_rout_id ( system.class_of_id (a_node.class_id).feature_of_feature_id (a_node.feature_id).rout_id_set.first ).feature_id else l_target_type := l_target_type.implemented_type (a_node.class_id) l_target_feature_id := a_node.feature_id end il_generator.generate_routine_address (l_target_type, l_target_feature_id, is_last_argument_current) end process_argument_b (a_node: ARGUMENT_B) -- Process `a_node'. do if need_access_address (a_node, is_nested_call) then il_generator.generate_argument_address (a_node.position) else il_generator.generate_argument (a_node.position) end is_nested_call := False end process_array_const_b (a_node: ARRAY_CONST_B) -- Process `a_node'. local l_target_type: TYPE_A i: INTEGER l_special_feat_tbl: FEATURE_TABLE l_special_to_array_feat, l_special_make_feat, l_put_feat: FEATURE_I l_special_info: CREATE_TYPE do if attached {GEN_TYPE_A} context.real_type (a_node.type) as l_real_ty then l_target_type := l_real_ty.generics.first -- Creation of SPECIAL l_special_info := a_node.special_info.updated_info l_special_feat_tbl := l_special_info.type.base_class.feature_table l_special_info.generate_il -- Call creation procedure `make_empty' or `make' depending on the compilation mode il_generator.duplicate_top il_generator.put_integer_32_constant (a_node.expressions.count) if system.is_using_new_special then l_special_make_feat := l_special_feat_tbl.item_id ({PREDEFINED_NAMES}.make_empty_name_id) else l_special_make_feat := l_special_feat_tbl.item_id ({PREDEFINED_NAMES}.make_name_id) end il_generator.generate_feature_access (l_special_info.type, l_special_make_feat.origin_feature_id, l_special_make_feat.argument_count, l_special_make_feat.has_return_value, True) -- Find `put' or `extend' from SPECIAL depending on the compilation mode. l_put_feat := l_special_feat_tbl.item_id (if system.is_using_new_special then {PREDEFINED_NAMES}.extend_name_id else {PREDEFINED_NAMES}.put_name_id end) -- Fill the SPECIAL. check i = 0 end across a_node.expressions as e loop if attached {EXPR_B} e.item as l_expr then -- Prepare call to `put'. il_generator.duplicate_top -- Generate expression generate_expression_il_for_type (l_expr, l_target_type) if not system.is_using_new_special then il_generator.put_integer_32_constant (i) end il_generator.generate_feature_access (l_special_info.type, l_put_feat.origin_feature_id, l_put_feat.argument_count, l_put_feat.has_return_value, True) i := i + 1 end end -- Creation of the ARRAY by calling `to_array' from SPECIAL to create the ARRAY instance. l_special_to_array_feat := l_special_feat_tbl.item_id ({PREDEFINED_NAMES}.to_array_name_id) il_generator.generate_feature_access (l_special_info.type, l_special_to_array_feat.origin_feature_id, l_special_to_array_feat.argument_count, l_special_to_array_feat.has_return_value, True) end end process_assert_b (a_node: ASSERT_B) -- Process `a_node'. do check expr_exists: a_node.expr /= Void not_in_precondition: context.assertion_type /= {ASSERT_TYPE}.in_precondition end generate_il_line_info (a_node, True) a_node.expr.process (Current) il_generator.generate_assertion_check (context.assertion_type, a_node.tag) end process_assign_b (a_node: ASSIGN_B) -- Process `a_node'. local source_type: TYPE_A target_type: TYPE_A do generate_il_line_info (a_node, True) process_pragma (a_node) -- Code that needs to be generated when performing -- assignment to an attribute. generate_il_start_assignment (a_node.target) source_type := context.real_type (a_node.source.type) target_type := context.real_type (a_node.target.type) -- Generate expression byte code. generate_expression_il_for_type (a_node.source, target_type) -- Check if the assignment instruction is used to model -- creation instruction. if not a_node.is_creation_instruction then -- Generate code for reattachment. generate_reattachment (a_node.source, source_type, target_type) end -- Generate assignment. generate_il_assignment (a_node.target, source_type) end process_attribute_b (a_node: ATTRIBUTE_B) -- Process `a_node'. local l_r_type: TYPE_A l_cl_type: CL_TYPE_A l_target_type: TYPE_A l_target_attribute_id: INTEGER l_cancel_attribute_generation: BOOLEAN l_is_nested_call: like is_nested_call l_need_address: BOOLEAN do l_is_nested_call := is_nested_call is_nested_call := False if l_is_nested_call then l_need_address := need_access_address (a_node, l_is_nested_call) end -- Type of attribute in current context l_r_type := context.real_type (a_node.type) if l_r_type.is_none then -- Accessing Void attribute from ANY il_generator.put_default_value (l_r_type) elseif attached {CL_TYPE_A} a_node.context_type as c then -- Type of class which defines current attribute. l_cl_type := c if l_cl_type.is_expanded and then not l_cl_type.is_true_external then -- Access attribute directly. if l_cl_type.base_class.is_typed_pointer then -- Use non-generic class POINTER because TYPED_POINTER is not generated. l_cl_type := system.pointer_class.compiled_class.actual_type end l_target_type := l_cl_type l_target_attribute_id := l_cl_type.base_class.feature_of_rout_id (a_node.routine_id).feature_id else l_target_type := l_cl_type.implemented_type (a_node.written_in) l_target_attribute_id := a_node.attribute_id end check valid_type: l_cl_type /= Void end -- Let's try to prepare call to `XXX.attribute.copy' in -- case of `attribute' is a basic type. if a_node.parent /= Void and l_r_type.is_basic and then attached {FEATURE_B} a_node.parent.message as l_feature_call and then attached {CL_TYPE_A} l_r_type as l_attribute_cl_type and then il_special_routines.has (l_feature_call.feature_name_id, l_attribute_cl_type) and then il_special_routines.function_type = {IL_SPECIAL_FEATURES}.set_item_type then -- Since we do not need to load attribute value onto the stack, -- we cancel the attribute generation. -- IL_SPECIAL_FEATURES.generate_set_item will know that Object -- where Current belongs is on top of the stack. l_cancel_attribute_generation := True end if not l_cancel_attribute_generation then if a_node.is_first and a_node.need_target then -- Accessing attribute written in current analyzed class. if l_need_address and not context.context_class_type.is_generated_as_single_type then -- We need current target which will be used later on in -- NESTED_B.generate_il to assign back the new value of the attribute. il_generator.generate_current end il_generator.generate_current elseif l_cl_type.is_basic then -- A metamorphose is required to perform call. generate_il_metamorphose (l_cl_type, l_target_type, need_real_metamorphose (a_node, l_cl_type)) end -- We push code to access Current attribute. if l_need_address then il_generator.generate_attribute_address (l_target_type, l_r_type, l_target_attribute_id) else if l_target_type.is_generated_as_single_type then il_generator.generate_attribute (a_node.need_target, l_target_type, l_target_attribute_id) else il_generator.generate_feature_access (l_target_type, l_target_attribute_id, 0, True, True) end -- Generate cast if we have to generate verifiable code -- since attribute might have been redefined and in this -- case its type for IL generation is the one from the -- parent not the redefined one. Doing the cast enable -- the verifier to find out that what we are doing is -- correct. if system.il_verifiable and then not l_r_type.is_expanded and then not l_r_type.is_none then il_generator.generate_check_cast (Void, l_r_type) end end end end end process_bin_and_b (a_node: BIN_AND_B) -- Process `a_node'. do process_bin_and_then_b (a_node) end process_bin_and_then_b (a_node: B_AND_THEN_B) -- Process `a_node'. local l_and_then_label, l_final_label: IL_LABEL do if a_node.is_built_in then l_and_then_label := il_generator.create_label l_final_label := il_generator.create_label a_node.left.process (Current) il_generator.branch_on_false (l_and_then_label) a_node.right.process (Current) il_generator.branch_to (l_final_label) il_generator.mark_label (l_and_then_label) il_generator.put_boolean_constant (False) il_generator.mark_label (l_final_label) else a_node.nested_b.process (Current) end end process_bin_div_b (a_node: BIN_DIV_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_div) end process_bin_eq_b (a_node: BIN_EQ_B) -- Process `a_node'. do generate_bin_equal_b (a_node, il_eq) end process_bin_free_b (a_node: BIN_FREE_B) -- Process `a_node'. do a_node.nested_b.process (Current) end process_bin_ge_b (a_node: BIN_GE_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_ge) end process_bin_gt_b (a_node: BIN_GT_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_gt) end process_bin_implies_b (a_node: B_IMPLIES_B) -- Process `a_node'. local l_final_label: IL_LABEL do if a_node.is_built_in then l_final_label := il_generator.create_label a_node.left.process (Current) il_generator.generate_unary_operator (il_not) il_generator.duplicate_top il_generator.branch_on_true (l_final_label) il_generator.pop a_node.right.process (Current) il_generator.mark_label (l_final_label) else a_node.nested_b.process (Current) end end process_bin_le_b (a_node: BIN_LE_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_le) end process_bin_lt_b (a_node: BIN_LT_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_lt) end process_bin_minus_b (a_node: BIN_MINUS_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_minus) end process_bin_mod_b (a_node: BIN_MOD_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_mod) end process_bin_ne_b (a_node: BIN_NE_B) -- Process `a_node'. do generate_bin_equal_b (a_node, il_ne) end process_bin_not_tilde_b (a_node: BIN_NOT_TILDE_B) -- Process `a_node'. do process_bin_tilde_b (a_node) il_generator.generate_unary_operator (il_not) end process_bin_or_b (a_node: BIN_OR_B) -- Process `a_node'. do process_bin_or_else_b (a_node) end process_bin_or_else_b (a_node: B_OR_ELSE_B) -- Process `a_node'. local l_final_label: IL_LABEL do if a_node.is_built_in then l_final_label := il_generator.create_label a_node.left.process (Current) il_generator.duplicate_top il_generator.branch_on_true (l_final_label) il_generator.pop a_node.right.process (Current) il_generator.mark_label (l_final_label) else a_node.nested_b.process (Current) end end process_bin_plus_b (a_node: BIN_PLUS_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_plus) end process_bin_power_b (a_node: BIN_POWER_B) -- Process `a_node'. local l_power_value: DOUBLE do if a_node.is_built_in then a_node.left.process (Current) il_generator.convert_to_real_64 if attached {REAL_CONST_B} a_node.right as l_power_nb then l_power_value := l_power_nb.value.to_double if l_power_value = 0.0 then il_generator.pop il_generator.put_real_64_constant (1.0) elseif l_power_value = 1.0 then elseif l_power_value = 2.0 then il_generator.duplicate_top il_generator.generate_binary_operator (il_star, False) elseif l_power_value = 3.0 then il_generator.duplicate_top il_generator.duplicate_top il_generator.generate_binary_operator (il_star, False) il_generator.generate_binary_operator (il_star, False) else a_node.right.process (Current) il_generator.convert_to_real_64 il_generator.generate_binary_operator (il_power, False) end else a_node.right.process (Current) il_generator.convert_to_real_64 il_generator.generate_binary_operator (il_power, False) end else a_node.nested_b.process (Current) end end process_bin_slash_b (a_node: BIN_SLASH_B) -- Process `a_node'. do if a_node.is_built_in then a_node.left.process (Current) il_generator.convert_to_real_64 a_node.right.process (Current) il_generator.convert_to_real_64 il_generator.generate_binary_operator (il_slash, False) else a_node.nested_b.process (Current) end end process_bin_star_b (a_node: BIN_STAR_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_star) end process_bin_tilde_b (a_node: BIN_TILDE_B) -- Process `a_node'. local l_lt, l_rt: TYPE_A l_continue_label, l_void_label, l_end_label, l_failure_label: IL_LABEL l_left_local: INTEGER do l_lt := context.real_type (a_node.left.type) l_rt := context.real_type (a_node.right.type) if l_lt.is_basic and then l_rt.is_basic then -- From the byte node generation, we know that they must -- be of the same type, otherwise something else than a -- BIN_TILDE_B node is generated. check same_basic_type: l_lt.same_as (l_rt) end a_node.left.process (Current) a_node.right.process (Current) if a_node.is_real_comparison then il_generator.generate_real_comparison_routine (il_eq, l_lt.is_real_32, boolean_type) else il_generator.generate_binary_operator (il_eq, l_lt.is_natural) end elseif (l_lt.is_expanded and then l_rt.is_none) or else (l_lt.is_none and then l_rt.is_expanded) then -- An expanded type is neither Void, so we simply evaluate the expressions and -- then remove them from the stack to insert the expected value. a_node.left.process (Current) il_generator.pop a_node.right.process (Current) il_generator.pop il_generator.put_boolean_constant (False) else -- Generate left operand. generate_expression_il_for_type (a_node.left, any_type) -- Store result of a left expression in a local. context.add_local (any_type) l_left_local := context.local_list.count il_generator.put_dummy_local_info (any_type, l_left_local) il_generator.generate_local_assignment (l_left_local) -- Generate right operand. generate_expression_il_for_type (a_node.right, any_type) -- Check for voidness. l_continue_label := il_generator.create_label l_void_label := il_generator.create_label l_end_label := il_generator.create_label l_failure_label := il_generator.create_label il_generator.duplicate_top il_generator.branch_on_false (l_void_label) -- Right hand side is not Void, check that left hand side is not Void too. il_generator.generate_local (l_left_local) il_generator.put_void il_generator.generate_binary_operator (il_eq, False) il_generator.branch_on_false (l_continue_label) -- Code when right hand side is not Void, but left is. -- First remove right hand side since not needed as the answer is False. il_generator.pop il_generator.put_boolean_constant (False) il_generator.branch_to (l_end_label) -- Code when both right hand side and left hand size are not Void il_generator.mark_label (l_continue_label) -- First check that they have the same type. il_generator.duplicate_top il_generator.generate_local (l_left_local) il_generator.generate_same_type_test il_generator.branch_on_false (l_failure_label) -- Now check equality: il_generator.generate_local (l_left_local) -- FIXME: This call assumes that `is_equal' from ANY always takes -- `like Current' as argument, but actually it could be different. il_generator.generate_object_equality_test il_generator.branch_to (l_end_label) -- Failure on same type. il_generator.mark_label (l_failure_label) -- Remove right hand side il_generator.pop il_generator.put_boolean_constant (False) il_generator.branch_to (l_end_label) -- Right hand side was Void, return True if left hand side was Void too. il_generator.mark_label (l_void_label) -- Remove right hand side since not needed and simply compare `left' with Void. il_generator.pop il_generator.generate_local (l_left_local) il_generator.put_void il_generator.generate_binary_operator (il_eq, False) il_generator.branch_to (l_end_label) -- Mark end of equality expression (if required). il_generator.mark_label (l_end_label) end end process_bin_xor_b (a_node: BIN_XOR_B) -- Process `a_node'. do generate_converted_binary_b (a_node, il_xor) end process_bool_const_b (a_node: BOOL_CONST_B) -- Process `a_node'. do il_generator.put_boolean_constant (a_node.value) end process_byte_list (a_node: BYTE_LIST [BYTE_NODE]) -- Process `a_node'. local l_area: SPECIAL [BYTE_NODE] i, nb: INTEGER do from l_area := a_node.area nb := a_node.count until i = nb loop l_area.item (i).process (Current) i := i + 1 end end process_case_b (a_node: CASE_B) -- Process `a_node'. do -- Nothing to be done, as it is handled in INSPECT_B end process_case_expression_b (a_node: CASE_EXPRESSION_B) -- do -- Nothing to be done, as it is handled in `process_inspect_expression_b`. end process_char_const_b (a_node: CHAR_CONST_B) -- Process `a_node'. do if a_node.is_character_32 then il_generator.put_natural_32_constant (a_node.value.natural_32_code) else il_generator.put_character_constant (a_node.value.to_character_8) end end process_char_val_b (a_node: CHAR_VAL_B) -- Process `a_node'. do end process_check_b (a_node: CHECK_B) -- Process `a_node'. local l_label: IL_LABEL do process_pragma (a_node) if a_node.check_list /= Void and then (context.workbench_mode or else context.class_type.associated_class.assertion_level.is_check) then l_label := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_check) il_generator.branch_on_false (l_label) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status context.set_assertion_type ({ASSERT_TYPE}.in_check) Il_generator.put_silent_line_info (a_node.line_number) a_node.check_list.process (Current) context.set_assertion_type (0) il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status Il_generator.mark_label (l_label) check end_location_not_void: a_node.end_location /= Void end il_generator.put_silent_debug_info (a_node.end_location) end end process_constant_b (a_node: CONSTANT_B) -- Process `a_node'. do is_nested_call := False a_node.value.generate_il end process_creation_expr_b (a_node: CREATION_EXPR_B) -- Process `a_node'. local l_creation_type: TYPE_A l_nested: NESTED_B l_is_il_external: BOOLEAN do is_nested_call := False l_creation_type := context.real_type (a_node.type) if l_creation_type.is_basic then il_generator.put_default_value (l_creation_type) elseif l_creation_type.is_external then --or else a_node.call.is_external then -- Creation call on external class. if a_node.call /= Void then create l_nested l_nested.set_target (a_node) l_nested.set_message (a_node.call) a_node.call.set_parent (l_nested) is_in_creation_call := True a_node.call.process (Current) is_in_creation_call := False a_node.call.set_parent (Void) else -- We called `default_create' on an external class. a_node.info.updated_info.generate_il end else if attached {EXTERNAL_B} a_node.call as l_ext_call then l_is_il_external := l_ext_call.extension.is_il end if not l_is_il_external then -- Standard creation call on non-il external. a_node.info.updated_info.generate_il end if a_node.call /= Void then if l_creation_type.is_expanded then -- Take an address of a boxed object. il_generator.generate_metamorphose (l_creation_type) il_generator.generate_load_address (l_creation_type) end if not l_is_il_external and then not (attached l_creation_type.base_class as b and then b.is_once) then il_generator.duplicate_top end create l_nested l_nested.set_target (a_node) l_nested.set_message (a_node.call) a_node.call.set_parent (l_nested) is_in_creation_call := True is_in_external_creation_call := l_is_il_external a_node.call.process (Current) is_in_external_creation_call := False is_in_creation_call := False a_node.call.set_parent (Void) if l_creation_type.is_expanded then -- Load expanded object. il_generator.generate_load_from_address (l_creation_type) end end end end process_current_b (a_node: CURRENT_B) -- Process `a_node'. local l_type_i: TYPE_A l_is_object_load_required: like is_object_load_required do if is_nested_call then l_is_object_load_required := is_object_load_required is_nested_call := False else l_is_object_load_required := True end is_this_argument_current := True l_type_i := context.real_type (a_node.type) if l_is_object_load_required and then l_type_i.is_basic then il_generator.generate_current_as_basic else il_generator.generate_current if l_is_object_load_required then is_object_load_required := False if l_type_i.is_expanded then is_this_argument_current := False il_generator.generate_load_from_address (l_type_i) end end end end process_custom_attribute_b (a_node: CUSTOM_ATTRIBUTE_B) -- Process `a_node'. do -- Generated by CUSTOM_ATTRIBUTE_GENERATOR. end process_debug_b (a_node: DEBUG_B) -- Process `a_node'. do process_pragma (a_node) if a_node.compound /= Void then if a_node.is_debug_clause_enabled then generate_il_line_info (a_node, True) a_node.compound.process (Current) check end_location_not_void: a_node.end_location /= Void end il_generator.put_silent_debug_info (a_node.end_location) else il_generator.put_ghost_debug_infos (a_node.line_number, a_node.compound.count) end end end process_elsif_b (a_node: ELSIF_B) -- Process `a_node'. do -- Generation done in IF_B end process_elsif_expression_b (a_node: ELSIF_EXPRESSION_B) -- do -- Generation done by `process_if_b'. end process_expr_address_b (a_node: EXPR_ADDRESS_B) -- Process `a_node'. do fixme ("Not supported in .NET") end process_external_b (a_node: EXTERNAL_B) -- Process `a_node'. local l_cl_type: CL_TYPE_A l_return_type: TYPE_A l_count: INTEGER l_real_metamorphose: BOOLEAN l_real_target: ACCESS_B l_is_in_creation: like is_in_creation_call l_is_nested_call: like is_nested_call l_is_in_external_creation_call: like is_in_external_creation_call do l_is_in_creation := is_in_creation_call l_is_nested_call := is_nested_call l_is_in_external_creation_call := is_in_external_creation_call is_in_creation_call := False is_nested_call := False is_in_external_creation_call := False is_this_argument_current := False -- Type of object on which we are performing call to Current. if attached {CL_TYPE_A} (if a_node.is_static_call then context.real_type (a_node.static_class_type) else a_node.context_type end) as c then l_cl_type := c -- Let's find out if we are performing a call on a basic type -- or on an enum type. This happens only when we are calling -- magically added feature on basic types. if il_special_routines.has (a_node.feature_name_id, l_cl_type) then if not a_node.is_static_call and then a_node.is_first then -- Because the code is generated for a basic type, -- the value has to be loaded. il_generator.generate_current_as_basic end il_special_routines.generate_il (Current, a_node, l_cl_type, a_node.parameters) elseif not attached {IL_EXTENSION_I} a_node.extension as l_il_ext then -- Generate call to C external. generate_il_c_call (a_node, not l_is_in_creation) else if l_cl_type.is_expanded then if l_cl_type.base_class.is_typed_pointer then -- Use non-generic class POINTER because TYPED_POINTER is not generated. l_cl_type := system.pointer_class.compiled_class.actual_type end -- Current type is expanded. We need to find out if -- we need to generate a box operation, meaning that -- the feature is inherited from a non-expanded class. l_real_metamorphose := need_real_metamorphose (a_node, l_cl_type) end if a_node.is_first then if need_current (l_il_ext.type) then -- First call in dot expression, we need to generate Current -- only when we do not call a static feature. il_generator.generate_current end elseif l_cl_type.is_expanded then if l_il_ext.type = operator_type then -- Operators are static functions, so a value has to be passed rather than an address. il_generator.generate_load_from_address (l_cl_type) elseif l_il_ext.type /= creator_type and a_node.parent /= Void then -- No need to do anything special in case of a call to -- a constructor. The generation of `a_node.target' of current call already -- did any special transformation to perfom call. if a_node.is_message or else a_node.parent.parent = Void then l_real_target := a_node.parent.target else l_real_target := a_node.parent.parent.target end if l_real_target.is_predefined or l_real_target.is_attribute then -- For same reason we don't do anything for a call to -- a constructor, when `l_real_target' is predefined or is -- an attribute any special transformation have already been done. if l_real_metamorphose then -- Feature is written in an inherited class of current -- expanded class. We need to box. il_generator.generate_metamorphose (l_cl_type) end elseif a_node.written_in /= l_cl_type.class_id then -- A call to parent routine. generate_il_metamorphose (l_cl_type, Void, l_real_metamorphose) end end end if a_node.parameters /= Void then -- Generate parameters if any. l_count := a_node.parameters.count a_node.parameters.process (Current) end if l_is_in_creation or else l_il_ext.type /= creator_type then -- We are not performing a creation call, neither a call -- to a constructor. if a_node.is_static_call or else a_node.precursor_type /= Void then -- A call to precursor or a static call is never polymorphic. l_il_ext.generate_call (False) elseif l_is_in_external_creation_call then l_il_ext.generate_external_creation_call (l_cl_type) elseif l_cl_type.is_expanded and then not l_cl_type.is_true_external and then not l_is_in_creation then -- The feature is generated in the current class. il_generator.generate_feature_access (l_cl_type, l_cl_type.base_class.feature_of_rout_id (a_node.routine_id).feature_id, l_count, not context.real_type (a_node.type).is_void, False) else -- Standard call to an external feature. if not l_cl_type.is_enum or else l_il_ext.type /= field_type then -- Call will be polymorphic if it target of call is a reference -- or if target has been boxed, or if type of external -- forces a static binding (eg static features). l_il_ext.generate_call (l_cl_type.is_reference or else l_real_metamorphose) end end else -- Current external is a creation, we perform a slightly different -- call to constructor, but basically it is very close to `generate_call' -- but doing a static binding. l_il_ext.generate_creation_call end end if l_is_nested_call then l_return_type := context.real_type (a_node.type) if l_return_type.is_true_expanded and then not l_return_type.base_class.is_enum then generate_il_metamorphose (l_return_type, l_return_type, True) end end end end process_feature_b (a_node: FEATURE_B) -- Process `a_node'. local l_cl_type: CL_TYPE_A l_orig_return_type, l_return_type: TYPE_A l_native_array_class_type: NATIVE_ARRAY_CLASS_TYPE l_special_array_class_type: SPECIAL_CLASS_TYPE l_invariant_checked: BOOLEAN l_class_c: CLASS_C l_real_metamorphose: BOOLEAN l_need_generation: BOOLEAN l_target_type, l_precursor_type: CL_TYPE_A l_is_call_on_any: BOOLEAN l_is_nested_call: like is_nested_call l_is_in_creation: like is_in_creation_call l_type: TYPE_A creation_expression: CREATION_EXPR_B is_once_creation: BOOLEAN do l_is_in_creation := is_in_creation_call l_is_nested_call := is_nested_call is_in_creation_call := False is_nested_call := False is_this_argument_current := False -- Get type on which call will be performed. if attached {CL_TYPE_A} a_node.context_type as ct then l_cl_type := ct -- Get the real type of `Precursor'. if attached a_node.precursor_type as p and then attached {CL_TYPE_A} context.real_type (p) as c then l_precursor_type := c end -- Let's find out if we are performing a call on a basic type -- or on an enum type. This happens only when we are calling -- magically added feature on basic types. if il_special_routines.has (a_node.feature_name_id, l_cl_type) then if a_node.is_first then -- Because the code is generated for a basic type, -- the value has to be loaded. il_generator.generate_current_as_basic end il_special_routines.generate_il (Current, a_node, l_cl_type, a_node.parameters) else l_is_call_on_any := a_node.is_any_feature and l_precursor_type = Void -- Find location of feature. if not l_is_call_on_any and l_cl_type.is_expanded and not l_cl_type.is_true_external then if l_cl_type.base_class.is_typed_pointer then -- Use non-generic class POINTER because TYPED_POINTER is not generated. l_cl_type := system.pointer_class.compiled_class.actual_type end l_target_type := l_cl_type else l_target_type := l_cl_type.implemented_type (a_node.written_in) end check target_type_not_void: l_target_type /= Void end l_class_c := l_cl_type.base_class if l_class_c.is_native_array then check not_expanded: not l_cl_type.is_expanded end l_native_array_class_type := {NATIVE_ARRAY_CLASS_TYPE} / l_cl_type.associated_class_type (context.context_class_type.type) check native_array_class_type_not_void: l_native_array_class_type /= Void end end l_invariant_checked := (context.workbench_mode or l_class_c.assertion_level.is_invariant) and then not a_node.is_first and then (l_native_array_class_type = Void) if l_cl_type.is_expanded then -- Current type is expanded. We need to find out if -- we need to generate a box operation, meaning that -- the feature is inherited from a non-expanded class. l_real_metamorphose := need_real_metamorphose (a_node, l_cl_type) end if a_node.is_first then -- First call in dot expression, we need to generate Current -- only when we do not call a static feature. if a_node.is_class_target_needed then -- A call to the class feature that needs "Current". -- Generate an empty object to be used as a target of the call. create creation_expression l_type := a_node.precursor_type creation_expression.set_info (l_type.create_info) creation_expression.set_type (l_type) process_creation_expr_b (creation_expression) if l_type.is_expanded then -- Box expanded object. il_generator.generate_metamorphose (l_type) -- Take address of expanded object. il_generator.generate_load_address (l_type) end elseif a_node.is_instance_free then -- A call to the class feature that does not need "Current". il_generator.put_void else -- A call to an object feature. il_generator.generate_current if system.il_verifiable and then not context.context_cl_type.base_class.simple_conform_to (system.class_of_id (a_node.written_in)) then -- Add a cast for non-conforming inheritance. il_generator.generate_check_cast (context.context_class_type.type, context.context_class_type.written_type (system.class_of_id (a_node.written_in)).type) end end elseif l_cl_type.is_basic then -- A metamorphose is required to perform call. generate_il_metamorphose (l_cl_type, l_target_type, True) end is_once_creation := a_node.is_once and then l_class_c.is_once and then attached l_class_c.creators as cs and then cs.has (a_node.feature_name_id) if l_invariant_checked then if not is_once_creation then generate_il_call_invariant_leading (l_cl_type, not l_is_in_creation) elseif not l_is_in_creation then -- The invariant after the call should be checked on a returned value. -- But the invariant before the call should be checked on the current value. il_generator.duplicate_top generate_il_call_invariant (l_cl_type, True) end end -- Box value type if the call is made to the predefined feature from ANY -- This has to be done before calculating feature arguments if l_is_call_on_any then if l_cl_type.is_true_expanded then if not l_cl_type.is_enum then il_generator.generate_load_from_address (l_cl_type) end il_generator.generate_metamorphose (l_cl_type) elseif l_cl_type.is_basic and then a_node.is_first then il_generator.generate_load_from_address_as_object (l_cl_type) il_generator.generate_metamorphose (l_cl_type) end elseif not a_node.is_first and then l_cl_type.is_basic then -- Address of a value is required. il_generator.generate_load_address (l_cl_type) elseif a_node.is_first and then not a_node.is_instance_free and then attached l_precursor_type and then context.context_class_type.is_expanded then check precursor_not_expanded: not l_precursor_type.is_expanded end if context.context_class_type.is_basic then il_generator.generate_load_from_address_as_object (context.context_cl_type) il_generator.generate_metamorphose (context.context_cl_type) else il_generator.generate_load_from_address (context.context_class_type.type) il_generator.generate_metamorphose (context.context_class_type.type) end il_generator.generate_check_cast (context.context_class_type.type, l_precursor_type) end if l_class_c.is_special and then attached {SPECIAL_CLASS_TYPE} l_cl_type.associated_class_type (context.context_class_type.type) as s then check not_expanded: not l_cl_type.is_expanded end inspect a_node.feature_name_id when {PREDEFINED_NAMES}.item_name_id, {PREDEFINED_NAMES}.infix_at_name_id, {PREDEFINED_NAMES}.at_name_id, {PREDEFINED_NAMES}.put_name_id then -- Generate code directly. l_special_array_class_type := s l_special_array_class_type.prepare_generate_il (a_node.feature_name_id, l_cl_type) else -- Treat the call as a regular one. end end if a_node.parameters /= Void then -- Generate parameters if any. if l_native_array_class_type /= Void and then a_node.feature_name_id = {PREDEFINED_NAMES}.put_name_id then check parameters_count_is_two: a_node.parameters.count = 2 end a_node.parameters.i_th (1).process (Current) if context.real_type (a_node.parameters.i_th (2).type).is_true_expanded then l_native_array_class_type.generate_il_put_preparation (l_cl_type, context.context_class_type.type) end a_node.parameters.i_th (2).process (Current) elseif l_is_call_on_any then -- Run-time features work on arguments of reference type only from a_node.parameters.start until a_node.parameters.after loop if attached {CL_TYPE_A} context.real_type (a_node.parameters.item.attachment_type) as l_arg_type and then l_arg_type.is_expanded then generate_expression_il_for_type (a_node.parameters.item, l_arg_type.reference_type) else a_node.parameters.item.process (Current) end a_node.parameters.forth end else a_node.parameters.process (Current) end end l_return_type := if is_once_creation then l_cl_type else context.real_type (a_node.type) end -- Special handling when precursor has a different expanded status -- than current type. We need to find out the return type of the Precursor. if l_precursor_type /= Void and then context.context_class_type.is_expanded then context.change_class_type_context ( l_precursor_type.associated_class_type (context.context_class_type.type), l_precursor_type.instantiated_in (context.context_class_type.type), l_precursor_type.associated_class_type (context.context_class_type.type), l_precursor_type.instantiated_in (context.context_class_type.type)) l_orig_return_type := context.real_type (a_node.type) context.restore_class_type_context else l_orig_return_type := l_return_type end l_need_generation := True if l_native_array_class_type /= Void then l_need_generation := False l_native_array_class_type.generate_il (a_node.feature_name_id, l_cl_type, context.context_class_type.type) if System.il_verifiable and then not l_return_type.is_expanded and then not l_return_type.is_none and then not l_return_type.is_void then il_generator.generate_check_cast (Void, l_return_type) end elseif attached l_special_array_class_type then l_special_array_class_type.generate_il (a_node.feature_name_id, l_cl_type, context.context_class_type.type) l_need_generation := False if System.il_verifiable and then not l_return_type.is_expanded and then not l_return_type.is_none and then not l_return_type.is_void then il_generator.generate_check_cast (Void, l_return_type) end end if l_need_generation then -- Perform call to feature if l_is_call_on_any then generate_il_any_call (a_node, l_target_type, l_cl_type, l_cl_type.is_reference or else l_real_metamorphose) else if l_cl_type.is_true_expanded then generate_il_normal_call (a_node, l_cl_type, False) else generate_il_normal_call (a_node, l_target_type, l_cl_type.is_reference or else l_real_metamorphose) end end end if l_invariant_checked then if is_once_creation then -- Use returned value rather than the target object to check invariant. il_generator.duplicate_top generate_il_call_invariant_trailing (l_cl_type, void_type) else generate_il_call_invariant_trailing (l_cl_type, l_return_type) end end if l_precursor_type /= Void and then l_orig_return_type /= l_return_type and then not l_orig_return_type.is_expanded and then l_return_type.is_expanded then -- Return type of precursor is not expanded, we need to make -- an expanded out of it. Because the way it works, the value -- we get from the Precursor call must be a boxed value. il_generator.generate_check_cast (l_orig_return_type, l_return_type) if l_return_type.is_basic then il_generator.generate_load_address (l_return_type) il_generator.generate_load_from_address_as_basic (l_return_type) else il_generator.generate_unmetamorphose (l_return_type) end elseif l_is_nested_call then l_return_type := context.real_type (a_node.type) if l_return_type.is_true_expanded and then not l_return_type.base_class.is_enum then -- Pointer to value type is required. generate_il_metamorphose (l_return_type, l_return_type, True) end end end else check is_valid_context_type: False end end end process_agent_call_b (a_node: AGENT_CALL_B) do process_feature_b (a_node) end process_formal_conversion_b (a_node: FORMAL_CONVERSION_B) -- Process `a_node'. local l_type: TYPE_A l_expr_type: TYPE_A do -- Generate expression a_node.expr.process (Current) -- Check if conversion expanded -> reference is needed in -- case we assign an expression whose type is a formal generic -- parameter of the current class to a reference type whose -- formal's constraint conforms to. l_type := context.real_type (a_node.type) l_expr_type := context.real_type (a_node.expr.type) if a_node.is_conversion_needed (l_expr_type, l_type) then if a_node.is_boxing or else not l_expr_type.is_basic then il_generator.generate_metamorphose (l_expr_type) else if attached {BASIC_A} l_expr_type as b then generate_il_eiffel_metamorphose (b) else check from_condition: False then end end end end end process_guard_b (a_node: GUARD_B) -- local i: INTEGER do process_pragma (a_node) i := a_node.line_number if i > 0 then Il_generator.put_silent_line_info (i) end if attached a_node.check_list as c then -- Generate assertions. context.set_assertion_type ({ASSERT_TYPE}.in_guard) c.process (Current) context.set_assertion_type (0) end if attached a_node.compound as c then -- Generated compound. c.process (Current) end if attached a_node.end_location as e then il_generator.put_silent_debug_info (e) end end process_hector_b (a_node: HECTOR_B) -- Process `a_node'. do cil_access_address_generator.generate_il_address (il_generator, a_node.expr) end process_if_b (a_node: IF_B) -- local l_else_label, l_end_label, l_elsif_label: IL_LABEL do -- Generated byte code for condition if attached {HIDDEN_IF_B} a_node then context.enter_hidden_code a_node.condition.process (Current) context.exit_hidden_code else generate_il_line_info (a_node, True) process_pragma (a_node) a_node.condition.process (Current) end -- Generated a test l_else_label := il_generator.create_label il_generator.branch_on_false (l_else_label) if attached a_node.compound as l_compound then -- Generated IL code for first compound (if any). l_compound.process (Current) end if attached a_node.else_part or attached a_node.elsif_list then l_end_label := il_generator.create_label il_generator.branch_to (l_end_label) end -- Else label il_generator.mark_label (l_else_label) if attached a_node.elsif_list as l_elsif_list then -- Generates IL code for alternatives across l_elsif_list as e loop if attached {ELSIF_B} e.item as l_elsif_clause then -- Generate byte code for expression. generate_il_line_info (l_elsif_clause, True) l_elsif_clause.expr.process (Current) -- Test if False. l_elsif_label := il_generator.create_label il_generator.branch_on_false (l_elsif_label) if attached l_elsif_clause.compound as c then -- Generate alternative compound byte code. c.process (Current) end il_generator.branch_to (l_end_label) il_generator.mark_label (l_elsif_label) end end end if a_node.else_part /= Void then -- Generates byte code for default compound. a_node.else_part.process (Current) end if attached l_end_label then -- End of `if' statement. il_generator.mark_label (l_end_label) end check end_location_not_void: a_node.end_location /= Void end il_generator.put_silent_debug_info (a_node.end_location) end process_if_expression_b (a_node: IF_EXPRESSION_B) -- Process `a_node'. local l_elsif_clause: ELSIF_EXPRESSION_B l_else_label, l_end_label, l_elsif_label: IL_LABEL t: TYPE_A do t := context.real_type (a_node.type) -- Generated IL code for condition. a_node.condition.process (Current) -- Generated a test l_else_label := il_generator.create_label il_generator.branch_on_false (l_else_label) -- Generated IL code for first expression. generate_expression_il_for_type (a_node.then_expression, t) l_end_label := il_generator.create_label il_generator.branch_to (l_end_label) -- Else label il_generator.mark_label (l_else_label) if attached a_node.elsif_list as l_elsif_list then -- Generates IL code for alternatives. across l_elsif_list as c loop l_elsif_clause := c.item -- Generate byte code for condition. generate_il_line_info (l_elsif_clause, True) l_elsif_clause.condition.process (Current) -- Test if False l_elsif_label := il_generator.create_label il_generator.branch_on_false (l_elsif_label) -- Generate alternative expression IL code. generate_expression_il_for_type (l_elsif_clause.expression, t) il_generator.branch_to (l_end_label) il_generator.mark_label (l_elsif_label) end end -- Generates byte code for default expression. generate_expression_il_for_type (a_node.else_expression, t) -- End of `if' statement. il_generator.mark_label (l_end_label) if attached a_node.end_location as l then il_generator.put_silent_debug_info (l) end end process_inspect_b (a_node: INSPECT_B) -- Process `a_node'. local l_else_label: IL_LABEL l_end_label: IL_LABEL l_intervals: SORTED_TWO_WAY_LIST [INTERVAL_B] l_spans: ARRAYED_LIST [INTERVAL_SPAN] inspect_type: TYPE_A min_value: INTERVAL_VAL_B max_value: INTERVAL_VAL_B labels: ARRAY [detachable IL_LABEL] do l_else_label := il_generator.create_label l_end_label := il_generator.create_label generate_il_line_info (a_node, True) process_pragma (a_node) if not a_node.switch.is_fast_as_local then -- Generate switch expression byte code a_node.switch.process (Current) end if a_node.case_list /= Void then -- Sort and merge intervals l_intervals := create_sorted_interval_list (a_node) merge_intervals (a_node, l_intervals) if l_intervals.count > 0 then -- Calculate minimum and maximum values inspect_type := a_node.switch.type if attached {CL_TYPE_A} inspect_type as l_cl_type_i and then l_cl_type_i.is_enum then create {INT_VAL_B} min_value.make ({INTEGER_32}.min_value) create {INT_VAL_B} max_value.make ({INTEGER_32}.max_value) else min_value := inspect_type.minimum_interval_value max_value := inspect_type.maximum_interval_value end -- Make sure there are no different objects for the same boundary if l_intervals.first.lower.is_equal (min_value) then min_value := l_intervals.first.lower end if l_intervals.last.upper.is_equal (max_value) then max_value := l_intervals.last.upper end -- Group intervals. l_spans := build_spans (a_node, l_intervals, min_value, max_value) -- Create array of labels for all cases, put `l_end_label' at position `-1' and `l_else_label' at position `0'. create labels.make_filled (Void, -1, a_node.case_list.count) labels.put (l_end_label, -1) labels.put (l_else_label, 0) generate_spans (a_node, l_spans, 1, l_spans.count, min_value, max_value, True, True, labels) end end il_generator.mark_label (l_else_label) if a_node.else_part /= Void then a_node.else_part.process (Current) else il_generator.generate_raise_exception ({EXCEP_CONST}.incorrect_inspect_value, Void) -- Throw an exception end il_generator.mark_label (l_end_label) if not a_node.switch.is_fast_as_local then -- Either there was nothing in the inspect and therefore we need to -- pop the value on which we were inspecting as it is not used. Or if -- there was some `when' parts we need to remove the duplication made -- for case comparison. il_generator.pop end check end_location_not_void: a_node.end_location /= Void end il_generator.put_silent_debug_info (a_node.end_location) end process_inspect_expression_b (b: INSPECT_EXPRESSION_B) -- local l_else_label: IL_LABEL l_end_label: IL_LABEL l_intervals: SORTED_TWO_WAY_LIST [INTERVAL_B] l_spans: ARRAYED_LIST [INTERVAL_SPAN] inspect_type: TYPE_A min_value: INTERVAL_VAL_B max_value: INTERVAL_VAL_B labels: ARRAY [detachable IL_LABEL] t: TYPE_A do t := context.real_type (b.type) l_else_label := il_generator.create_label l_end_label := il_generator.create_label generate_il_line_info (b, True) -- Generate inspect expression. if not b.switch.is_fast_as_local then -- Generate switch expression byte code. b.switch.process (Current) end -- Generate cases. if attached b.case_list as cs then -- Sort and merge intervals. l_intervals := create_sorted_interval_list (b) merge_intervals (b, l_intervals) if l_intervals.count > 0 then -- Calculate minimum and maximum values inspect_type := b.switch.type if attached {CL_TYPE_A} inspect_type as l_cl_type_i and then l_cl_type_i.is_enum then create {INT_VAL_B} min_value.make ({INTEGER_32}.min_value) create {INT_VAL_B} max_value.make ({INTEGER_32}.max_value) else min_value := inspect_type.minimum_interval_value max_value := inspect_type.maximum_interval_value end -- Make sure there are no different objects for the same boundary if l_intervals.first.lower.is_equal (min_value) then min_value := l_intervals.first.lower end if l_intervals.last.upper.is_equal (max_value) then max_value := l_intervals.last.upper end -- Group intervals. l_spans := build_spans (b, l_intervals, min_value, max_value) -- Create array of labels for all cases, put `l_end_label' at position `-1' and `l_else_label' at position `0'. create labels.make_filled (Void, -1, cs.count) labels.put (l_end_label, -1) labels.put (l_else_label, 0) generate_spans (b, l_spans, 1, l_spans.count, min_value, max_value, True, True, labels) end end -- Generate else part. il_generator.mark_label (l_else_label) if not b.switch.is_fast_as_local then -- Remove the duplication made for case comparison. il_generator.pop end if attached b.else_part as p then generate_expression_il_for_type (p, t) else -- Throw an exception. il_generator.generate_raise_exception ({EXCEP_CONST}.incorrect_inspect_value, Void) -- Make sure the stack is in consistent state. il_generator.put_default_value (t) end -- Final end. il_generator.mark_label (l_end_label) check end_location_attached: attached b.end_location end il_generator.put_silent_debug_info (b.end_location) end process_instr_call_b (a_node: INSTR_CALL_B) -- Process `a_node'. do generate_il_line_info (a_node, True) process_pragma (a_node) a_node.call.process (Current) end process_instr_list_b (a_node: INSTR_LIST_B) -- Process `a_node'. do process_pragma (a_node) a_node.compound.process (Current) end process_int64_val_b (a_node: INT64_VAL_B) -- Process `a_node'. do end process_int_val_b (a_node: INT_VAL_B) -- Process `a_node'. do end process_integer_constant (a_node: INTEGER_CONSTANT) -- Process `a_node'. do a_node.generate_il end process_inv_assert_b (a_node: INV_ASSERT_B) -- Process `a_node'. do process_assert_b (a_node) end process_invariant_b (a_node: INVARIANT_B) -- Process `a_node'. local l: ARRAYED_LIST [TYPE_A] do context.local_list.wipe_out context.add_locals (a_node.object_test_locals) context.set_assertion_type ({ASSERT_TYPE}.in_invariant) context.set_original_body_index (a_node.associated_class.invariant_feature.body_index) -- Allocate memory for once manifest strings if required. if a_node.once_manifest_string_count > 0 then il_generator.generate_once_string_allocation (a_node.once_manifest_string_count) end -- Record local variable information. l := context.local_list il_generator.set_local_count (l.count) if not l.is_empty then generate_il_local_info (l) end il_generator.generate_invariant_body (a_node.byte_list) context.set_assertion_type (0) end process_local_b (a_node: LOCAL_B) -- Process `a_node'. do if need_access_address (a_node, is_nested_call) then il_generator.generate_local_address (a_node.position) else il_generator.generate_local (a_node.position) end is_nested_call := False end process_loop_b (a_node: LOOP_B) -- Process `a_node'. local l_test_label, l_end_label, l_label: IL_LABEL l_local_list: ARRAYED_LIST [TYPE_A] l_variant_local_number: INTEGER l_check_assertion: BOOLEAN l_context: like context l_old_hidden_code_level: INTEGER do l_context := context l_old_hidden_code_level := l_context.hidden_code_level l_context.set_hidden_code_level (0) l_check_assertion := l_context.workbench_mode or l_context.class_type.associated_class.assertion_level.is_loop if attached a_node.iteration_initialization as i then -- Generate IL code for the iteration initialization. il_generator.put_line_info (i.first.line_number) l_context.enter_hidden_code i.process (Current) l_context.exit_hidden_code end if a_node.from_part /= Void then -- Generate IL code for the from part a_node.from_part.process (Current) end if l_check_assertion and then a_node.variant_part /= Void then -- Initialization of the variant control variable l_local_list := l_context.local_list l_context.add_local (integer_32_type) l_variant_local_number := l_local_list.count il_generator.put_dummy_local_info (a_node.variant_part.type, l_variant_local_number) end if l_check_assertion and then (a_node.invariant_part /= Void or else a_node.variant_part /= Void) then l_label := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_loop) il_generator.branch_on_false (l_label) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status if a_node.invariant_part /= Void then l_context.set_assertion_type ({ASSERT_TYPE}.in_loop_invariant) a_node.invariant_part.process (Current) l_context.set_assertion_type (0) end -- Variant loop byte code if a_node.variant_part /= Void then l_context.set_assertion_type ({ASSERT_TYPE}.in_loop_variant) generate_il_variant_init (a_node.variant_part, l_variant_local_number) l_context.set_assertion_type (0) end il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (l_label) end -- Loop labels l_test_label := il_generator.create_label l_end_label := il_generator.create_label -- Generate byte code for exit expression il_generator.mark_label (l_test_label) if attached a_node.iteration_exit_condition as e then -- Generate test for iteration exit condition. e.process (Current) il_generator.branch_on_true (l_end_label) end if attached a_node.stop as s then -- Generate debug information. generate_il_line_info (a_node, True) process_pragma (a_node) -- Generate test for loop exit condition. s.process (Current) il_generator.branch_on_true (l_end_label) end if a_node.compound /= Void then a_node.compound.process (Current) end if attached a_node.advance_code as a then a.process (Current) end if l_check_assertion and then (a_node.invariant_part /= Void or else a_node.variant_part /= Void) then l_label := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_loop) il_generator.branch_on_false (l_label) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status -- Invariant loop byte code if a_node.invariant_part /= Void then context.set_assertion_type ({ASSERT_TYPE}.in_loop_invariant) a_node.invariant_part.process (Current) context.set_assertion_type (0) end -- Variant loop byte code if a_node.variant_part /= Void then context.set_assertion_type ({ASSERT_TYPE}.in_loop_variant) generate_il_variant_check (a_node.variant_part, l_variant_local_number) context.set_assertion_type (0) end il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (l_label) end il_generator.branch_to (l_test_label) il_generator.mark_label (l_end_label) check end_location_not_void: a_node.end_location /= Void end il_generator.put_silent_debug_info (a_node.end_location) l_context.set_hidden_code_level (l_old_hidden_code_level) end process_loop_expr_b (a_node: LOOP_EXPR_B) -- local l_test_label, l_end_label, l_label: IL_LABEL l_local_list: ARRAYED_LIST [TYPE_A] l_result_local_number: INTEGER l_variant_local_number: INTEGER l_check_assertion: BOOLEAN i: detachable BYTE_LIST [BYTE_NODE] v: detachable VARIANT_B l_context: like context l_old_hidden_code_level: INTEGER do l_context := context l_old_hidden_code_level := l_context.hidden_code_level l_context.set_hidden_code_level (0) l_check_assertion := l_context.workbench_mode or l_context.class_type.associated_class.assertion_level.is_loop -- Generate IL code for the iteration part il_generator.put_line_info (a_node.iteration_code.line_number) l_context.enter_hidden_code a_node.iteration_code.process (Current) l_context.exit_hidden_code -- Initialize loop expression result variable. l_local_list := context.local_list context.add_local (boolean_type) l_result_local_number := l_local_list.count il_generator.put_dummy_local_info (boolean_type, l_result_local_number) il_generator.put_boolean_constant (a_node.is_all) il_generator.generate_local_assignment (l_result_local_number) if l_check_assertion then i := a_node.invariant_code v := a_node.variant_code end if v /= Void then -- Initialization of the variant control variable context.add_local (integer_32_type) l_variant_local_number := l_local_list.count il_generator.put_dummy_local_info (v.type, l_variant_local_number) end if i /= Void or else v /= Void then l_label := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_loop) il_generator.branch_on_false (l_label) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status if i /= Void then context.set_assertion_type ({ASSERT_TYPE}.in_loop_invariant) i.process (Current) context.set_assertion_type (0) end -- Variant loop byte code if v /= Void then context.set_assertion_type ({ASSERT_TYPE}.in_loop_variant) generate_il_variant_init (v, l_variant_local_number) context.set_assertion_type (0) end il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (l_label) end -- Loop labels l_test_label := il_generator.create_label l_end_label := il_generator.create_label il_generator.mark_label (l_test_label) -- Generate byte code for loop result variable check. il_generator.generate_local (l_result_local_number) if a_node.is_all then il_generator.branch_on_false (l_end_label) else il_generator.branch_on_true (l_end_label) end -- Generate byte code for iteration exit condition. a_node.iteration_exit_condition_code.process (Current) il_generator.branch_on_true (l_end_label) if attached a_node.exit_condition_code as e then -- Generate byte code for optional exit condition. e.process (Current) il_generator.branch_on_true (l_end_label) end -- Evaluate loop expression and assign its value to the loop result variable. l_context.enter_hidden_code a_node.expression_code.process (Current) l_context.exit_hidden_code il_generator.generate_local_assignment (l_result_local_number) -- Advance the loop cursor. a_node.advance_code.process (Current) if i /= Void or else v /= Void then l_label := il_generator.create_label il_generator.generate_is_assertion_checked ({ASSERTION_I}.Ck_loop) il_generator.branch_on_false (l_label) il_generator.put_boolean_constant (True) il_generator.generate_set_assertion_status -- Invariant loop byte code if i /= Void then context.set_assertion_type ({ASSERT_TYPE}.in_loop_invariant) i.process (Current) context.set_assertion_type (0) end -- Variant loop byte code if v /= Void then context.set_assertion_type ({ASSERT_TYPE}.in_loop_variant) generate_il_variant_check (v, l_variant_local_number) context.set_assertion_type (0) end il_generator.put_boolean_constant (False) il_generator.generate_set_assertion_status il_generator.mark_label (l_label) end il_generator.branch_to (l_test_label) il_generator.mark_label (l_end_label) il_generator.generate_local (l_result_local_number) l_context.set_hidden_code_level (l_old_hidden_code_level) end process_nat64_val_b (a_node: NAT64_VAL_B) -- Process `a_node'. do end process_nat_val_b (a_node: NAT_VAL_B) -- Process `a_node'. do end process_nested_b (a_node: NESTED_B) -- Process `a_node'. local l_can_discard_target: BOOLEAN l_is_target_generated: BOOLEAN l_attr: ATTRIBUTE_B l_local_number: INTEGER l_type: TYPE_A l_need_attribute_to_be_assigned_back: BOOLEAN do is_object_load_required := False l_can_discard_target := not a_node.message.need_target if l_can_discard_target then -- If we have a constant or a static external call, -- we can forget about the generation of `a_node.target' only -- if it is not a routine call. If the generation -- of `a_node.target' occurred, we need to pop from -- execution stack the value returned by `a_node.target' -- because it is not needed to perform the call to `a_node.message'. l_is_target_generated := not a_node.target.is_predefined and (a_node.parent /= Void or not a_node.target.is_attribute) else l_is_target_generated := True end if l_is_target_generated then -- We pass `True' to force a special treatment on -- generation of `a_node.target' if it is an expanded object. -- Namely if `a_node.target' is predefined we will load -- the address of `a_node.target' instead of `a_node.target' itself. -- `a_node.message' will manage the boxing operation if needed. is_nested_call := True a_node.target.process (Current) is_nested_call := False -- In case of attributes which requires their address to be loaded -- to perform the call, we check wether or not the value needs to be -- assigned back to the attribute or not, that is to say if they -- are first in the call chain. -- If it does then its address was loaded in previous call to `a_node.target', so -- we need to duplicate it. if attached {ATTRIBUTE_B} a_node.target as a then l_attr := a l_need_attribute_to_be_assigned_back := need_access_address (l_attr, True) and then l_attr.is_first and then not context.context_class_type.is_generated_as_single_type if l_need_attribute_to_be_assigned_back then il_generator.duplicate_top end end end if l_can_discard_target and l_is_target_generated then il_generator.pop end -- Generate call if attached {CALL_ACCESS_B} a_node.message as l_call_access then l_call_access.process (Current) else a_node.message.process (Current) end if l_need_attribute_to_be_assigned_back then l_type := context.real_type (a_node.message.type) if not l_type.is_void then context.add_local (l_type) l_local_number := context.local_list.count il_generator.put_dummy_local_info (l_type, l_local_number) il_generator.generate_local_assignment (l_local_number) end if attached {CL_TYPE_A} l_attr.context_type as l_cl_type then il_generator.generate_expanded_attribute_assignment ( l_cl_type.implemented_type (l_attr.written_in), context.real_type (l_attr.type), l_attr.attribute_id) end if not l_type.is_void then il_generator.generate_local (l_local_number) end end end process_null_conversion_b (a_node: NULL_CONVERSION_B) -- Process `a_node'. local l_expr_type, l_type: TYPE_A do a_node.expr.process (Current) l_expr_type := context.real_type (a_node.expr.type) l_type := context.real_type (a_node.type) if not l_type.same_as (l_expr_type) then -- Even in non-verifiable code we are required to do a conversion. -- See eweasel test#dotnet118 where passing a manifest integer `1' -- (of type INTEGER_32) to a routine expecting a NATURAL_64 would -- cause the .NET runtime to fail. il_generator.convert_to (l_type) end is_object_load_required := False end process_object_test_b (a_node: OBJECT_TEST_B) -- Process `a_node'. local l_target_type, l_source_type: TYPE_A l_target_formal_type: FORMAL_A l_target_class_type: CL_TYPE_A success_label, failure_label: IL_LABEL basic_failure_label: IL_LABEL do -- Get types l_target_type := a_node.target.type if attached {FORMAL_A} l_target_type as fa then l_target_formal_type := fa end l_target_type := context.real_type (l_target_type) l_source_type := context.real_type (a_node.expression.type) check target_type_not_void: l_target_type /= Void source_type_not_void: l_source_type /= Void end -- Generate expression byte code is_object_load_required := True a_node.expression.process (Current) is_object_load_required := False l_target_class_type ?= l_target_type if l_target_class_type /= Void and then l_target_class_type.base_class.is_typed_pointer and then not l_source_type.same_as (l_target_type) then -- Object test for TYPED_POINTER fails -- if the source is not of the same type as the target. -- Remove expression value because it is not used. il_generator.pop -- Types do not conform. il_generator.put_boolean_constant (False) elseif l_source_type.is_expanded and then l_target_type.is_expanded then -- NOOP if classes are different or normal assignment otherwise. if l_target_class_type /= Void and then attached {CL_TYPE_A} l_source_type as l_source_class_type and then l_target_class_type.class_id = l_source_class_type.class_id then -- Do normal assignment. generate_reattachment (a_node.expression, l_source_type, l_target_type) -- Generate assignment header depending of the type -- of the target (local, attribute or result). generate_il_assignment (a_node.target, l_source_type) -- Types conform. il_generator.put_boolean_constant (True) else -- Remove expression value because it is not used. il_generator.pop -- Types do not conform. il_generator.put_boolean_constant (False) end else if l_source_type.is_expanded then if attached {CL_TYPE_A} l_source_type as l_source_class_type and then l_source_class_type.base_class.is_typed_pointer then -- Use non-generic class POINTER because TYPED_POINTER is not generated. l_source_type := system.pointer_class.compiled_class.actual_type end generate_il_metamorphose (l_source_type, l_target_type, True) end -- Save the original value so that it can be used for the second chance type test. il_generator.duplicate_top -- Generate Test on type. if l_target_formal_type /= Void and then not l_target_type.is_basic then -- As the `l_target_type` is System.Object for such formal -- the "is instance of"w will wrongly always return True. -- then have special handling in `il_generator` for that formal type. il_generator.generate_is_instance_of (l_target_formal_type) else il_generator.generate_is_instance_of (l_target_type) end failure_label := il_generator.create_label success_label := il_generator.create_label il_generator.branch_on_true (success_label) -- Assignment attempt failed. l_target_class_type ?= l_target_type if l_target_class_type /= Void and then l_target_class_type.is_basic and then l_target_class_type.generics = Void then -- Check native .NET type. -- Generate Test on type il_generator.generate_is_instance_of_external (l_target_class_type) il_generator.duplicate_top basic_failure_label := il_generator.create_label il_generator.branch_on_false (basic_failure_label) -- Generate reattachment. il_generator.generate_external_unmetamorphose (l_target_class_type) -- Generate assignment header depending of the type -- of the target (local, attribute or result). generate_il_assignment (a_node.target, l_source_type) -- Types conform. il_generator.put_boolean_constant (True) -- Terminate reattachment. il_generator.branch_to (failure_label) il_generator.mark_label (basic_failure_label) end -- Types do not conform. il_generator.pop il_generator.put_boolean_constant (False) il_generator.branch_to (failure_label) il_generator.mark_label (success_label) generate_reattachment (a_node.expression, l_source_type, l_target_type) -- Generate assignment header depending of the type -- of the target (local, attribute or result). generate_il_assignment (a_node.target, l_source_type) -- Types conform. il_generator.put_boolean_constant (True) il_generator.mark_label (failure_label) end end process_object_test_local_b (a_node: OBJECT_TEST_LOCAL_B) do if need_access_address (a_node, is_nested_call) then il_generator.generate_local_address (context.object_test_local_position (a_node)) else il_generator.generate_local (context.object_test_local_position (a_node)) end is_nested_call := False end process_once_string_b (a_node: ONCE_STRING_B) -- Process `a_node'. do if a_node.is_dotnet_string then il_generator.generate_once_string (a_node.number - 1, a_node.value_32, string_type_cil) elseif a_node.is_string_32 then if a_node.is_immutable then il_generator.generate_once_string (a_node.number - 1, a_node.value_32, string_type_immutable_string_32) else il_generator.generate_once_string (a_node.number - 1, a_node.value_32, string_type_string_32) end else if a_node.is_immutable then il_generator.generate_once_string (a_node.number - 1, a_node.value_32, string_type_immutable_string_8) else il_generator.generate_once_string (a_node.number - 1, a_node.value_32, string_type_string) end end end process_operand_b (a_node: OPERAND_B) -- Process `a_node'. do is_nested_call := False end process_parameter_b (a_node: PARAMETER_B) -- Process `a_node'. local target_type: TYPE_A do is_last_argument_current := is_this_argument_current is_this_argument_current := False target_type := context.real_type (a_node.attachment_type) generate_expression_il_for_type (a_node.expression, target_type) generate_reattachment (a_node.expression, context.real_type (a_node.type), target_type) end process_paran_b (a_node: PARAN_B) -- Process `a_node'. do a_node.expr.process (Current) is_object_load_required := False end process_real_const_b (a_node: REAL_CONST_B) -- Process `a_node'. do if a_node.real_size = 64 then il_generator.put_real_64_constant (a_node.value.to_double) else il_generator.put_real_32_constant (a_node.value.to_real) end end process_require_b (a_node: REQUIRE_B) do process_assert_b (a_node) end process_result_b (a_node: RESULT_B) -- Process `a_node'. do if need_access_address (a_node, is_nested_call) then il_generator.generate_result_address else il_generator.generate_result end is_nested_call := False end process_retry_b (a_node: RETRY_B) -- Process `a_node'. do il_generator.generate_local (context.saved_rescue_level) il_generator.generate_set_rescue_level generate_il_line_info (a_node, True) process_pragma (a_node) il_generator.generate_leave_to (retry_label) end process_reverse_b (a_node: REVERSE_B) -- Process `a_node'. local l_target_type, l_source_type: TYPE_A l_target_class_type: CL_TYPE_A success_label, failure_label: IL_LABEL basic_failure_label: IL_LABEL do generate_il_line_info (a_node, True) process_pragma (a_node) -- Code that needs to be generated when performing -- assignment to an attribute. generate_il_start_assignment (a_node.target) -- Get types l_target_type := a_node.target.type l_target_type := context.real_type (l_target_type) l_source_type := context.real_type (a_node.source.type) check target_type_not_void: l_target_type /= Void source_type_not_void: l_source_type /= Void end -- Generate expression byte code is_object_load_required := True a_node.source.process (Current) is_object_load_required := False l_target_class_type ?= l_target_type if l_target_class_type /= Void and then l_target_class_type.base_class.is_typed_pointer and then not l_source_type.same_as (l_target_type) then -- Reverse reattachment to TYPED_POINTER is NOOP -- if the source is not of the same type as the target. -- Remove expression value because it is not used. il_generator.pop -- Avoid reattachment altogether. generate_il_cancel_assignment (a_node.target) elseif l_source_type.is_expanded and then l_target_type.is_expanded then -- NOOP if classes are different or normal assignment otherwise. if l_target_class_type /= Void and then attached {CL_TYPE_A} l_source_type as l_source_class_type and then l_target_class_type.class_id = l_source_class_type.class_id then -- Do normal assignment. generate_reattachment (a_node.source, l_source_type, l_target_type) -- Generate assignment header depending of the type -- of the target (local, attribute or result). generate_il_assignment (a_node.target, l_source_type) else -- Remove expression value because it is not used. il_generator.pop -- Avoid reattachment altogether. generate_il_cancel_assignment (a_node.target) end else if l_source_type.is_expanded then if attached {CL_TYPE_A} l_source_type as l_source_class_type and then l_source_class_type.base_class.is_typed_pointer then -- Use non-generic class POINTER because TYPED_POINTER is not generated. l_source_type := system.pointer_class.compiled_class.actual_type end generate_il_metamorphose (l_source_type, l_target_type, True) end if l_target_type.is_expanded then -- Conditional branch is used to avoid reattachment -- if the types do not conform. il_generator.duplicate_top end -- Generate Test on type il_generator.generate_is_instance_of (l_target_type) if l_target_type.is_expanded then failure_label := il_generator.create_label success_label := il_generator.create_label il_generator.branch_on_true (success_label) -- Assignment attempt failed. l_target_class_type ?= l_target_type if l_target_class_type /= Void and then l_target_class_type.is_basic and then l_target_class_type.generics = Void then -- Check native .NET type. -- Generate Test on type il_generator.generate_is_instance_of_external (l_target_class_type) il_generator.duplicate_top basic_failure_label := il_generator.create_label il_generator.branch_on_false (basic_failure_label) -- Generate reattachment. il_generator.generate_external_unmetamorphose (l_target_class_type) -- Generate assignment header depending of the type -- of the target (local, attribute or result). generate_il_assignment (a_node.target, l_source_type) -- Terminate reattachment. il_generator.branch_to (failure_label) il_generator.mark_label (basic_failure_label) end -- Remove duplicate obtained from call to `isinst'. il_generator.pop -- Avoid reattachment altogether. generate_il_cancel_assignment (a_node.target) il_generator.branch_to (failure_label) il_generator.mark_label (success_label) end generate_reattachment (a_node.source, l_source_type, l_target_type) -- Generate assignment header depending of the type -- of the target (local, attribute or result). generate_il_assignment (a_node.target, l_source_type) if failure_label /= Void then il_generator.mark_label (failure_label) end end end process_routine_creation_b (a_node: ROUTINE_CREATION_B) -- Process `a_node'. local l_set_rout_disp_feat: FEATURE_I l_decl_type: CL_TYPE_A do if attached {GEN_TYPE_A} context.real_type (a_node.type) as l_real_ty then (create {CREATE_TYPE}.make (l_real_ty)).generate_il il_generator.duplicate_top l_set_rout_disp_feat := l_real_ty.base_class.feature_table. item_id ({PREDEFINED_NAMES}.set_rout_disp_name_id) l_decl_type := l_real_ty.implemented_type (l_set_rout_disp_feat.origin_class_id) if attached {CL_TYPE_A} context.real_type (a_node.class_type) as l_cl_type then if a_node.is_inline_agent then il_generator.put_impl_method_token (l_cl_type.implemented_type (a_node.origin_class_id), a_node.feature_id) else il_generator.put_method_token (l_cl_type.implemented_type (a_node.origin_class_id), a_node.feature_id) end end -- Arguments if attached a_node.arguments as a then a.process (Current) else il_generator.put_void end -- Open map if attached a_node.open_positions as o then o.process (Current) else il_generator.put_void end il_generator.put_boolean_constant (a_node.is_inline_agent) il_generator.generate_feature_access (l_decl_type, l_set_rout_disp_feat.origin_feature_id, l_set_rout_disp_feat.argument_count, l_set_rout_disp_feat.has_return_value, True) end end process_separate_b (a_node: SEPARATE_INSTRUCTION_B) -- do a_node.arguments.process (Current) if attached a_node.compound as c and then not c.is_empty then if system.is_scoop then debug ("to_implement") to_implement ("Support code generation for inline separate instruction") end end c.process (Current) end end process_string_b (a_node: STRING_B) -- Process `a_node'. do if a_node.is_dotnet_string then il_generator.put_system_string_32 (a_node.value_32) else if a_node.is_string_32 then if a_node.is_immutable then il_generator.put_immutable_manifest_string_32 (a_node.value_32) else il_generator.put_manifest_string_32 (a_node.value_32) end else if a_node.is_immutable then il_generator.put_immutable_manifest_string_8 (a_node.value_32) else il_generator.put_manifest_string (a_node.value_32) end end end end process_strip_b (a_node: STRIP_B) -- Process `a_node'. do check not_implemented: False end end process_tuple_access_b (a_node: TUPLE_ACCESS_B) -- Process `a_node'. local l_feat_tbl: FEATURE_TABLE l_decl_type: CL_TYPE_A l_tuple_element_type: TYPE_A l_item_feat, l_put_feat: FEATURE_I do if attached {TUPLE_TYPE_A} context.real_type (a_node.tuple_type) as l_real_ty then l_feat_tbl := l_real_ty.base_class.feature_table if a_node.source /= Void then generate_il_line_info (a_node, True) a_node.source.process (Current) if attached {CL_TYPE_A} context.real_type (a_node.source.type) as l_actual_type and then l_actual_type.is_expanded then il_generator.generate_external_metamorphose (l_actual_type) end il_generator.put_integer_32_constant (a_node.position) -- Find `put' from TUPLE l_put_feat := l_feat_tbl.item_id ({PREDEFINED_NAMES}.put_name_id) l_decl_type := l_real_ty.implemented_type (l_put_feat.origin_class_id) -- Call `put' from TUPLE il_generator.generate_feature_access (l_decl_type, l_put_feat.origin_feature_id, l_put_feat.argument_count, l_put_feat.has_return_value, True) else il_generator.put_integer_32_constant (a_node.position) -- Find `fast_item' from TUPLE l_item_feat := l_feat_tbl.item_id ({PREDEFINED_NAMES}.fast_item_name_id) l_decl_type := l_real_ty.implemented_type (l_item_feat.origin_class_id) -- Call `fast_item' from TUPLE il_generator.generate_feature_access (l_decl_type, l_item_feat.origin_feature_id, l_item_feat.argument_count, l_item_feat.has_return_value, True) l_tuple_element_type := context.real_type (a_node.tuple_element_type) if l_tuple_element_type.is_expanded then if attached {CL_TYPE_A} l_tuple_element_type as l_actual_type then il_generator.generate_external_unmetamorphose (l_actual_type) else il_generator.generate_unmetamorphose (l_tuple_element_type) end elseif System.il_verifiable and then not l_tuple_element_type.is_none then il_generator.generate_check_cast (Void, l_tuple_element_type) end end end end process_tuple_const_b (a_node: TUPLE_CONST_B) -- Process `a_node'. local l_decl_type: CL_TYPE_A i: INTEGER l_feat_tbl: FEATURE_TABLE l_make_feat, l_put_feat: FEATURE_I l_expr: EXPR_B do if attached {GEN_TYPE_A} context.real_type (a_node.type) as l_real_ty then l_feat_tbl := l_real_ty.base_class.feature_table l_make_feat := l_feat_tbl.item_id ({PREDEFINED_NAMES}.default_create_name_id) l_decl_type := l_real_ty.implemented_type (l_make_feat.origin_class_id) -- Creation of Array (create {CREATE_TYPE}.make (l_real_ty)).generate_il -- Call creation procedure of TUPLE il_generator.duplicate_top il_generator.generate_feature_access (l_decl_type, l_make_feat.origin_feature_id, l_make_feat.argument_count, l_make_feat.has_return_value, True) -- Find `put' from TUPLE l_put_feat := l_feat_tbl.item_id ({PREDEFINED_NAMES}.put_name_id) l_decl_type := l_real_ty.implemented_type (l_put_feat.origin_class_id) i := 1 across a_node.expressions as e loop l_expr := e.item -- Prepare call to `put`. il_generator.duplicate_top -- Generate expression. l_expr.process (Current) if attached {CL_TYPE_A} context.real_type (l_expr.type) as l_actual_type and then l_actual_type.is_expanded then -- We generate a metamorphosed version of type. il_generator.generate_external_metamorphose (l_actual_type) end il_generator.put_integer_32_constant (i) il_generator.generate_feature_access (l_decl_type, l_put_feat.origin_feature_id, l_put_feat.argument_count, l_put_feat.has_return_value, True) i := i + 1 end end end process_type_expr_b (a_node: TYPE_EXPR_B) -- Process `a_node'. do if a_node.is_dotnet_type then il_generator.put_type_instance (context.real_type (a_node.type_type)) else fixme ("Instance should be unique.") fixme ("We also need to use `a_node.type_type' when it is not an instance of CL_TYPE_A.") context.real_type (a_node.type_data).create_info.generate_il end end process_typed_interval_b (a_node: TYPED_INTERVAL_B [INTERVAL_VAL_B]) -- Process `a_node'. do end process_un_free_b (a_node: UN_FREE_B) -- Process `a_node'. do a_node.nested_b.process (Current) end process_un_minus_b (a_node: UN_MINUS_B) -- Process `a_node'. do generate_unary_b (a_node, il_uminus) end process_un_not_b (a_node: UN_NOT_B) -- Process `a_node'. do generate_unary_b (a_node, il_not) end process_un_old_b (a_node: UN_OLD_B) -- Process `a_node'. do if a_node.is_exception_block_needed then il_generator.generate_raising_old_exception (a_node.exception_position) end il_generator.generate_local (a_node.position) end process_un_plus_b (a_node: UN_PLUS_B) -- Process `a_node'. do generate_unary_b (a_node, il_uplus) end process_variant_b (a_node: VARIANT_B) -- Process `a_node'. do process_assert_b (a_node) end process_void_b (a_node: VOID_B) -- Process `a_node'. do il_generator.put_void end feature {NONE} -- Access need_access_address (a_node: ACCESS_B; a_is_target_of_call: BOOLEAN): BOOLEAN -- Does `a_node' access need its address loaded in memory? require is_valid: is_valid a_node_not_void: a_node /= Void local l_call_access: CALL_ACCESS_B l_nested: NESTED_B l_constant: CONSTANT_B do if a_is_target_of_call and then attached {CL_TYPE_A} context.real_type (a_node.type) as l_cl_type then Result := l_cl_type.is_true_expanded and then not l_cl_type.is_enum if Result then l_call_access ?= a_node.parent.message if l_call_access = Void then -- Find out if it is a nested call. l_nested ?= a_node.parent.message if l_nested = Void then l_constant ?= a_node.parent.message check -- It has to be a constant access, as otherwise -- it means the original code was not Eiffel code. l_constant_not_void: l_constant /= Void end l_call_access ?= l_constant.access else l_call_access ?= l_nested.target end end -- We do not load the address if it is an optimized call of the compiler. Result := attached {FEATURE_B} l_call_access as l_feature_b implies not il_special_routines.has (l_feature_b.feature_name_id, l_cl_type) if Result and then attached {EXTERNAL_B} l_call_access as l_ext then Result := attached {IL_EXTENSION_I} l_ext.extension as l_il_ext implies l_il_ext.type /= {SHARED_IL_CONSTANTS}.Operator_type end end end end feature {NONE} -- Conversion generate_expression_il_for_type (a_node: EXPR_B; a_target_type: TYPE_A) -- Generate IL code for `expression' that is attached -- or compared to the target of type `a_target_type'. require is_valid: is_valid a_node_not_void: a_node /= Void target_type_not_void: a_target_type /= Void local l_expression_type: TYPE_A l_cl_type: CL_TYPE_A do is_object_load_required := True a_node.process (Current) is_object_load_required := False l_expression_type := context.real_type (a_node.type) if a_target_type.is_reference and then l_expression_type.is_expanded and then attached {CL_TYPE_A} l_expression_type as c then l_cl_type := c if not l_expression_type.is_basic then -- Simply box the object. il_generator.generate_metamorphose (l_expression_type) else if l_cl_type.base_class.is_typed_pointer then -- Use POINTER instead of TYPED_POINTER. l_cl_type := system.pointer_class.compiled_class.actual_type l_expression_type := l_cl_type end if not a_target_type.is_external or else l_cl_type.generics /= Void then -- Basic type is attached to Eiffel reference type, -- so basic type has to be represented by Eiffel type -- rather than by built-in IL type. if attached {BASIC_A} l_expression_type as b then generate_il_eiffel_metamorphose (b) else check from_condition: False then end end else il_generator.generate_external_metamorphose (l_expression_type) end end end end generate_il_eiffel_metamorphose (a_type: BASIC_A) -- Generate a metamorphose of `a_type' into a _REF type. require is_valid: is_valid a_type_not_void: a_type /= Void do il_generator.generate_eiffel_metamorphose (a_type) end generate_il_metamorphose (a_type, a_target_type: TYPE_A; a_real_metamorphose: BOOLEAN) -- Generate a metamorphose of target object. -- If `a_real_metamorphose' is set to True, target is an -- expanded type and feature is defined in a non-expanded class. -- If False, feature is defined in an expanded -- class we simply need to load address of target. require is_valid: is_valid local l_local_number: INTEGER do if a_target_type /= Void and then a_type.is_basic and then not a_target_type.is_true_external then -- Box object. if attached {BASIC_A} a_type as b then generate_il_eiffel_metamorphose (b) else check from_condition: False then end end if not a_real_metamorphose then -- Get address of an object. il_generator.generate_load_address (a_type) end elseif a_target_type = Void and then not a_real_metamorphose or else a_target_type /= Void and then a_type.is_expanded and a_type.same_as (a_target_type) then -- Take object address. context.add_local (a_type) l_local_number := context.local_list.count il_generator.put_dummy_local_info (a_type, l_local_number) il_generator.generate_local_assignment (l_local_number) il_generator.generate_local_address (l_local_number) else -- Box object. il_generator.generate_metamorphose (a_type) end end feature {NONE} -- Debug information generate_il_line_info (a_node: BYTE_NODE; is_breakable_for_studio_dbg: BOOLEAN) -- Generate source line information in IL code. require is_valid: is_valid a_node_not_void: a_node /= Void local l_number: INTEGER do if system.line_generation or context.workbench_mode then l_number := a_node.line_number if l_number > 0 then if is_breakable_for_studio_dbg then il_generator.put_line_info (l_number) else il_generator.put_silent_line_info (l_number) end end end end generate_ghost_debug_infos (a_node: BYTE_NODE; n: INTEGER) -- Generate `a_nb' ghost debug informations, -- this is to deal, for instance, with the not generated debug clauses require is_valid: is_valid a_node_not_void: a_node /= Void do if (System.line_generation or context.workbench_mode) and then system.is_precompile_finalized then il_generator.put_ghost_debug_infos (a_node.line_number, n) end end process_pragma (a_node: INSTR_B) -- Process pragma `a_pragma'. local l_parser: LINE_PRAGMA_PARSER l_pragma: BREAK_AS do l_pragma := a_node.line_pragma if (l_pragma /= Void) and then (system.line_generation or context.workbench_mode) then create l_parser l_parser.parse (l_pragma.literal_text (Void)) if l_parser.is_pragma then if l_parser.is_default then il_generator.set_pragma_offset (0) il_generator.set_default_document il_generator.set_stop_breakpoints_generation (False) elseif l_parser.is_hidden then il_generator.set_pragma_offset (0) il_generator.set_default_document il_generator.set_stop_breakpoints_generation (True) else il_generator.set_pragma_offset (l_parser.line_number - l_pragma.start_location.line) il_generator.set_document (l_parser.document) il_generator.set_stop_breakpoints_generation (False) end il_generator.put_silent_line_info (a_node.line_number) end end end feature {NONE} -- Implementation: binary operators generate_converted_binary_b (a_node: BINARY_B; an_opcode: INTEGER) -- Generate binary node `a_node' which cast either left hand side -- or right hand side to heaviest type before performing binary operation. require is_valid: is_valid a_node_not_void: a_node /= Void local l_left_type, l_right_type: TYPE_A l_type: TYPE_A l_same_type: BOOLEAN do if a_node.is_built_in then l_left_type := context.real_type (a_node.left.type) l_right_type := context.real_type (a_node.right.type) l_same_type := l_left_type.same_as (l_right_type) if not l_same_type then l_type := l_left_type.heaviest (l_right_type) end a_node.left.process (Current) if not l_same_type and then l_type = l_right_type then -- FIXME: Manu 1/29/2002: When evaluating inherited assertions where -- type is formal, type is not properly computed and therefore we do -- not get a basic type, but a formal one instead. -- When this bug will be fixed, we can remove the if statement for -- a basic type and replace it by a check statement. if l_type.is_basic then il_generator.convert_to (l_type) end end a_node.right.process (Current) if not l_same_type and then l_type = l_left_type then -- FIXME: See above fixme. if l_type.is_basic then il_generator.convert_to (l_type) end end if a_node.is_real_comparison then if l_same_type then il_generator.generate_real_comparison_routine (an_opcode, l_left_type.is_real_32, boolean_type) else il_generator.generate_real_comparison_routine (an_opcode, l_type.is_real_32, boolean_type) end else if l_same_type then l_type := l_left_type end il_generator.generate_binary_operator (an_opcode, -- Generate unsigned comparison for natural numbers. l_type.is_natural or else -- Generate unordered comparison for real numbers with equality because the result will be inverted, -- and IEEE 754 standard behavior is expected. ((an_opcode = il_ge or an_opcode = il_le) and then (l_type.is_real_32 or else l_type.is_real_64))) end else a_node.nested_b.process (Current) end end generate_bin_equal_b (a_node: BIN_EQUAL_B; an_opcode: INTEGER) -- Generate byte code for equality test require is_valid: is_valid a_node_not_void: a_node /= Void an_opcode_valie: an_opcode = il_ne or an_opcode = il_eq local l_left_type: TYPE_A l_right_type: TYPE_A l_comparison_type: TYPE_A l_continue_label: IL_LABEL l_end_label: IL_LABEL l_right_value: INTEGER do l_left_type := context.real_type (a_node.left.type) l_right_type := context.real_type (a_node.right.type) if (l_left_type.is_none and l_right_type.is_expanded) or (l_left_type.is_expanded and l_right_type.is_none) then -- Simple type can never be Void, so we simply evaluate -- expressions and then remove them from the stack to insert -- the expected value a_node.left.process (Current) a_node.right.process (Current) il_generator.pop il_generator.pop il_generator.put_boolean_constant (an_opcode = il_ne) else if (l_left_type.is_expanded or else l_right_type.is_expanded) and then not (l_left_type.is_basic and then l_right_type.is_basic) then -- Object (value) equality. -- Select reference type to which expanded types will be converted -- for comparison that expects arguments of type System.Object. if l_left_type.is_expanded then l_comparison_type := l_right_type if l_comparison_type.is_expanded then -- Both type are expanded. If either of them is external -- but not basic, the comparison type is System.Object. -- Otherwise it is ANY. if l_left_type.is_external and then not l_left_type.is_basic or else l_right_type.is_external and then not l_right_type.is_basic then create {CL_TYPE_A} l_comparison_type.make (system.system_object_id) else create {CL_TYPE_A} l_comparison_type.make (system.any_id) end end else l_comparison_type := l_left_type end -- Generate left operand. generate_expression_il_for_type (a_node.left, l_comparison_type) if l_left_type.is_reference then -- Check for voidness. l_continue_label := il_generator.create_label l_end_label := il_generator.create_label il_generator.duplicate_top il_generator.branch_on_true (l_continue_label) il_generator.pop il_generator.put_boolean_constant (False) il_generator.branch_to (l_end_label) il_generator.mark_label (l_continue_label) end -- Generate right operand. generate_expression_il_for_type (a_node.right, l_comparison_type) if l_right_type.is_reference then -- Check for voidness. l_continue_label := il_generator.create_label l_end_label := il_generator.create_label il_generator.duplicate_top il_generator.branch_on_true (l_continue_label) -- Remove left operand as well. il_generator.pop il_generator.pop il_generator.put_boolean_constant (False) il_generator.branch_to (l_end_label) il_generator.mark_label (l_continue_label) end -- Call "is_equal". il_generator.generate_object_equality_test -- Negate result if required. if an_opcode = il_ne then il_generator.generate_unary_operator (il_not) end -- Mark end of equality expression (if required). if l_end_label /= Void then il_generator.mark_label (l_end_label) end elseif l_left_type.is_basic or else l_right_type.is_basic or else not a_node.left.is_dynamic_clone_required (l_left_type) or else not a_node.right.is_dynamic_clone_required (l_right_type) then -- Basic type equality or pure reference type equality. generate_converted_binary_b (a_node, an_opcode) else -- Reference type equality. -- Check if one of the operands is of expanded type -- and use object comparison. Use reference comparison -- otherwise. -- Store result of a right expression in a local. a_node.right.process (Current) context.add_local (l_right_type) l_right_value := context.local_list.count il_generator.put_dummy_local_info (l_right_type, l_right_value) il_generator.generate_local_assignment (l_right_value) -- Check expandedness of a left expression object. a_node.left.process (Current) il_generator.duplicate_top il_generator.generate_is_true_instance_of (system.system_value_type_class.compiled_class.types.first.type) l_end_label := il_generator.create_label l_continue_label := il_generator.create_label il_generator.branch_on_true (l_continue_label) -- Compare references. il_generator.generate_local (l_right_value) il_generator.generate_binary_operator (an_opcode, False) il_generator.branch_to (l_end_label) il_generator.mark_label (l_continue_label) -- Compare objects. il_generator.duplicate_top il_generator.generate_local (l_right_value) -- Here we call {ISE_RUNTIME}.equal from the .NET runtime generate_equal_routine ({PREDEFINED_NAMES}.equal_name_id) -- Negate result if required for comparing objects. if an_opcode = il_ne then il_generator.generate_unary_operator (il_not) end il_generator.mark_label (l_end_label) end end end feature {NONE} -- Implementation: Inspect create_sorted_interval_list (b: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]): SORTED_TWO_WAY_LIST [INTERVAL_B] -- Create a sorted list of all intervals in the multi-branch construct `b`. require is_valid: is_valid node_attached: attached b case_list_not_empty: not b.case_list.is_empty local case_index: INTEGER case_b: ABSTRACT_CASE_B [BYTE_NODE] interval_b: INTERVAL_B is_empty_else_part: BOOLEAN do create Result.make is_empty_else_part := attached b.else_part and then not b.has_else_code across b.case_list as cs loop case_b := cs.item -- Add all intervals associated with current When_part unless they do the same as Else_part does if attached case_b.interval as case_intervals and then (not is_empty_else_part or else case_b.has_content) then case_index := cs.target_index ⟳ i: case_intervals ¦ interval_b := i interval_b.set_case_index (case_index) Result.extend (interval_b) ⟲ end end ensure result_not_void: Result /= Void end merge_intervals (a_node: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]; intervals: like create_sorted_interval_list) -- Merge adjacent intervals with the same code. require is_valid: is_valid a_node_not_void: a_node /= Void intervals_not_void: intervals /= Void local interval_b: INTERVAL_B case_index: INTEGER next_interval_b: INTERVAL_B next_case_index: INTEGER do from intervals.start until intervals.after loop next_interval_b := intervals.item next_case_index := next_interval_b.case_index if case_index = next_case_index and then interval_b.upper.is_next (next_interval_b.lower) then interval_b.set_upper (next_interval_b.upper) intervals.remove else interval_b := next_interval_b case_index := next_case_index intervals.forth end end end minimum_group_size: INTEGER = 7 -- Minimum number of items in group for which switch IL instruction is generated -- Switch instruction adds too much overhead when group consists of less than 7 items build_spans (a_node: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]; intervals: like create_sorted_interval_list; min_value, max_value: INTERVAL_VAL_B): ARRAYED_LIST [INTERVAL_SPAN] -- New sorted list of spans built from given `intervals' bounded with `min_value' and `max_value' require is_valid: is_valid a_node_not_void: a_node /= Void intervals_not_void: intervals /= Void min_value_not_void: min_value /= Void max_value_not_void: max_value /= Void local groups: TWO_WAY_LIST [INTERVAL_GROUP] interval_element: BI_LINKABLE [INTERVAL_B] group: INTERVAL_GROUP do create groups.make -- Merge intervals in groups by walking intervals backward from interval_element := intervals.last_element until interval_element = Void loop create group.make (interval_element) groups.put_front (group) from interval_element := interval_element.left until interval_element = Void or else not group.is_extended loop group.extend_with_interval (interval_element) if group.is_extended then interval_element := interval_element.left end end end -- Merge heading and trailing gaps group := groups.first if group /= Void and then group.lower /= min_value then group.extend_with_lower_gap (min_value, True) end group := groups.last if group /= Void and then group.upper /= max_value then group.extend_with_upper_gap (max_value, True) end -- Merge groups by walking them forward from create Result.make (groups.count) groups.start until groups.after loop group := groups.item group.set_is_extended (True) from groups.forth until groups.after or else not group.is_extended loop group.extend_with_group (groups.item) if group.is_extended then groups.remove end end if group.count >= minimum_group_size then -- This is a dense group with enough elements Result.extend (group) else -- Group has too little elements, replace it by elements themselves from interval_element := group.lower_interval until interval_element = group.upper_interval loop Result.extend (interval_element.item) interval_element := interval_element.right end Result.extend (interval_element.item) end end ensure result_not_void: Result /= Void end generate_spans (b: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]; spans: like build_spans; lower, upper: INTEGER; min, max: INTERVAL_VAL_B; is_min_included, is_max_included: BOOLEAN; labels: ARRAY [detachable IL_LABEL]) -- Generate selectors for `spans' with indexes `lower'..`upper' within interval `min'..`max' -- where these bounds are included according to `is_min_inclued' and `is_max_included'. Use -- `else_label' to branch to Else_part. require is_valid: is_valid node_attached: attached b local middle: INTEGER span: INTERVAL_SPAN next_label: IL_LABEL do if lower = upper then -- There is only one group span := spans.i_th (lower) generate_il_line_info (b, False) span.generate_il (Current, min, max, is_min_included, is_max_included, labels, b) else -- Divide groups in two parts and recurse middle := (lower + upper) // 2 span := spans.i_th (middle) next_label := il_generator.create_label generate_il_line_info (b, False) generate_il_branch_on_greater (span.upper, span.is_upper_included, next_label, b) generate_spans (b, spans, lower, middle, min, span.upper, is_min_included, span.is_upper_included, labels) il_generator.mark_label (next_label) generate_spans (b, spans, middle + 1, upper, span.upper, max, not span.is_upper_included, is_max_included, labels) end end generate_il_branch_on_greater (an_interval: INTERVAL_VAL_B; is_included: BOOLEAN; label: IL_LABEL; multi_branch: ABSTRACT_INSPECT_B [ABSTRACT_CASE_B [BYTE_NODE], BYTE_NODE]) -- Generate branch to `label' if value on IL stack it greater than this value. -- Assume that current value is included in lower interval if `is_included' is true. require is_valid: is_valid an_interval_not_void: an_interval /= Void label_not_void: label /= Void multi_branch_attached: attached multi_branch do generate_il_load_value (multi_branch) an_interval.il_load_value il_generator.branch_on_condition (if an_interval.is_signed then if is_included then {MD_OPCODES}.bgt else {MD_OPCODES}.bge end else if is_included then {MD_OPCODES}.bgt_un else {MD_OPCODES}.bge_un end end, label) end feature {NONE} -- Implementation: loop generate_il_variant_init (a_node: VARIANT_B; a_local: INTEGER) -- Initialize variant computation. require is_valid: is_valid a_node_not_void: a_node /= Void a_local_not_negative: a_local > 0 do check expr_exists: a_node.expr /= Void not_in_precondition: context.assertion_type /= {ASSERT_TYPE}.in_precondition end generate_il_line_info (a_node, True) -- Generate expression, duplicate top to perform checking, -- and store it in `a_local'. a_node.expr.process (Current) il_generator.duplicate_top il_generator.generate_local_assignment (a_local) il_generator.put_integer_32_constant (0) il_generator.generate_binary_operator ({IL_CONST}.il_ge, False) il_generator.generate_assertion_check (context.assertion_type, a_node.tag) end generate_il_variant_check (a_node: VARIANT_B; a_local: INTEGER) -- Compute new variant and raise an assertion violation if not satisfied. require is_valid: is_valid a_node_not_void: a_node /= Void a_local_not_negative: a_local > 0 do check expr_exists: a_node.expr /= Void not_in_precondition: context.assertion_type /= {ASSERT_TYPE}.in_precondition end generate_il_line_info (a_node, True) a_node.expr.process (Current) -- We need 3 times the new value on top: -- 1 - to compare it with old value stored in `a_local' and -- make sure it is decreasing. -- 2 - for storing new value of variant in `a_local'. -- 3 - to make sure it is a positive value. il_generator.duplicate_top il_generator.duplicate_top il_generator.generate_local (a_local) il_generator.generate_binary_operator ({IL_CONST}.Il_lt, False) il_generator.generate_assertion_check (context.assertion_type, a_node.tag) il_generator.generate_local_assignment (a_local) il_generator.put_integer_32_constant (0) il_generator.generate_binary_operator ({IL_CONST}.il_ge, False) il_generator.generate_assertion_check (context.assertion_type, a_node.tag) end feature {NONE} -- Implementation: unary operators generate_unary_b (a_node: UNARY_B; an_opcode: INTEGER) -- Generate code for `a_node'. require is_valid: is_valid a_node_not_void: a_node /= Void do if a_node.is_built_in then a_node.expr.process (Current) il_generator.generate_unary_operator (an_opcode) else a_node.nested_b.process (Current) end end feature {NONE} -- Implementation: assignments generate_il_start_assignment (a_node: ACCESS_B) -- Generate location of assignment if needed. require is_valid: is_valid a_node_not_void: a_node /= Void do -- Nothing to do in the current implementation. end generate_il_cancel_assignment (a_node: ACCESS_B) -- Remove any data from the stack put by `generate_il_start_assignment'. require is_valid: is_valid a_node_not_void: a_node /= Void do -- Nothing to do in the current implementation. end frozen generate_il_simple_assignment (a_node: ACCESS_B; target_type, source_type: TYPE_A) -- Generate simple source assignment require is_valid: is_valid a_node_not_void: a_node /= Void is_writable: not a_node.read_only target_type_not_void: target_type /= Void source_type_not_void: source_type /= Void local cl_type: CL_TYPE_A local_index: INTEGER do -- Generate cast if we have to generate verifiable code -- since access might have been redefined and in this -- case its type for IL generation is the one from the -- parent not the redefined one. Doing the cast enable -- the verifier to find out that what we are doing is -- correct. Cast is not needed for expanded type since -- they cannot be redefined. if System.il_verifiable and then not target_type.is_expanded and then not target_type.is_none then il_generator.generate_check_cast (source_type, target_type) end if attached {ATTRIBUTE_B} a_node as attr then -- Check the type in which we are generating the current attribute -- assignment. We could take `attr.context_type' but this would not -- work when we regenerate the assignment in a descendant class (case -- where routine is regenerated in an expanded type). cl_type := context.original_class_type.type -- Save expression value in a local, put Current on the stack, -- load saved expression value and perform the assignment. context.add_local (target_type) local_index := context.local_list.count il_generator.put_dummy_local_info (target_type, local_index) il_generator.generate_local_assignment (local_index) il_generator.generate_current il_generator.generate_local (local_index) if cl_type.is_expanded then -- Generate direct assignment. il_generator.generate_attribute_assignment (attr.need_target, cl_type, cl_type.base_class.feature_of_rout_id (attr.routine_id).feature_id) else -- Generate assignment to the potentially inherited attribute. il_generator.generate_attribute_assignment (attr.need_target, cl_type.implemented_type (attr.written_in), attr.attribute_id) end elseif a_node.is_local then if attached {OBJECT_TEST_LOCAL_B} a_node as o then il_generator.generate_local_assignment (context.object_test_local_position (o)) elseif attached {LOCAL_B} a_node as l then il_generator.generate_local_assignment (l.position) end elseif a_node.is_result then il_generator.generate_result_assignment end end feature {NONE} -- Implementation: Feature calls need_real_metamorphose (a_node: CALL_ACCESS_B; a_type: CL_TYPE_A): BOOLEAN -- Does call `a_node' originate from a reference type? require is_valid: is_valid a_node_not_void: a_node /= Void a_type_not_void: a_type /= Void a_type_has_associated_class: a_type.base_class /= Void do Result := system.class_of_id (a_node.written_in).is_external and then a_node.written_in /= a_type.base_class.class_id end generate_il_c_call (a_node: EXTERNAL_B; inv_checked: BOOLEAN) -- Generate IL code for feature call. -- If `inv_checked' generates invariant check before call. require is_valid: is_valid a_node_not_void: a_node /= Void local l_cl_type: CL_TYPE_A l_return_type: TYPE_A l_invariant_checked: BOOLEAN l_real_metamorphose: BOOLEAN l_count: INTEGER l_target_type: TYPE_A do -- Get type on which call will be performed. if attached {CL_TYPE_A} a_node.context_type as c then l_cl_type := c if l_cl_type.is_expanded then if l_cl_type.base_class.is_typed_pointer then -- Use POINTER instead of TYPED_POINTER. l_cl_type := system.pointer_class.compiled_class.actual_type end -- Current type is expanded. We need to find out if -- we need to generate a box operation, meaning that -- the feature is inherited from a non-expanded class. l_real_metamorphose := need_real_metamorphose (a_node, l_cl_type) end -- Let's find out if we are performing a call on a basic type -- or on an enum type. This happens only when we are calling -- magically added feature on basic types. l_invariant_checked := (context.workbench_mode or l_cl_type.base_class.assertion_level.is_invariant) and then not a_node.is_first if a_node.is_first then -- First call in dot expression, we need to generate Current -- only when we do not call a static feature. if l_cl_type.is_reference then -- Normal call, we simply push current object. if a_node.is_static_call then -- Bug fix until we generate direct static access -- to C external. -- Find location of feature. l_target_type := a_node.static_class_type; (create {CREATE_TYPE}.make (l_target_type)).generate_il else il_generator.generate_current end else il_generator.generate_current if l_real_metamorphose then -- Feature is written in an inherited class of current -- expanded class. We need to box. il_generator.generate_metamorphose (l_cl_type) end end elseif l_cl_type.is_expanded then -- A metamorphose is required to perform call. generate_il_metamorphose (l_cl_type, Void, l_real_metamorphose) end if l_invariant_checked then generate_il_call_invariant_leading (l_cl_type, inv_checked) end if a_node.parameters /= Void then -- Generate parameters if any. a_node.parameters.process (Current) l_count := a_node.parameters.count end l_return_type := context.real_type (a_node.type) -- Perform call to feature -- FIXME: performance problem here since we are retrieving the -- FEATURE_TABLE. This could be avoided if at creation of FEATURE_B -- node we add the feature_id in the parent class. if a_node.is_static_call or else a_node.precursor_type /= Void then -- In IL, if you can call Precursor, it means that parent is -- not expanded and therefore we can safely generate a static -- call to Precursor feature. -- Use implemented type instead of `a_node.static_class_type` l_target_type := l_cl_type.implemented_type (a_node.written_in) il_generator.generate_feature_access ( l_target_type, a_node.feature_id, l_count, not l_return_type.is_void, False) elseif l_cl_type.is_expanded then il_generator.generate_feature_access (l_cl_type, l_cl_type.base_class.feature_of_rout_id (a_node.routine_id).feature_id, l_count, not l_return_type.is_void, False) else il_generator.generate_feature_access ( l_cl_type.implemented_type (a_node.written_in), a_node.feature_id, l_count, not l_return_type.is_void, l_cl_type.is_reference or else l_real_metamorphose) end if System.il_verifiable and then not l_return_type.is_expanded and then not l_return_type.is_none and then not l_return_type.is_void then il_generator.generate_check_cast (Void, l_return_type) end if l_invariant_checked then generate_il_call_invariant_trailing (l_cl_type, l_return_type) end end end generate_il_call_invariant (cl_type: CL_TYPE_A; entry: BOOLEAN) -- Generate IL code for calling invariant feature on class type `cl_type'. -- Is the invariant checking `entry'? require is_valid: is_valid cl_type_not_void: cl_type /= Void do if cl_type.is_true_expanded then -- Box object before checking its class invariant if not cl_type.is_enum then il_generator.generate_load_from_address (cl_type) end il_generator.generate_metamorphose (cl_type) end il_generator.generate_invariant_checking (cl_type, entry) end generate_il_call_invariant_leading (cl_type: CL_TYPE_A; is_checked_before_call: BOOLEAN) -- Generate IL code for calling invariant feature on class type `cl_type' -- before associated feature call if `is_checked_before_call' is true. -- Object on which a class invariant has -- to be checked has to be on the evaluation stack. -- It is preserved for the subsequent invariant check after feature call. require is_valid: is_valid cl_type_not_void: cl_type /= Void do -- Need two copies of current object in stack -- to perform invariant check before and after -- feature call. il_generator.duplicate_top if is_checked_before_call then il_generator.duplicate_top generate_il_call_invariant (cl_type, True) end end generate_il_call_invariant_trailing (cl_type: CL_TYPE_A; a_return_type: TYPE_A) -- Generate IL code for calling invariant feature on class type `cl_type' -- after associated feature call with result type `a_return_type'. -- It is assumed that `generate_il_call_invariant_leading' is called -- before feature call to make necessary bookkeeping. require is_valid: is_valid cl_type_not_void: cl_type /= Void return_type_not_void: a_return_type /= Void local local_number: INTEGER do if a_return_type.is_void then generate_il_call_invariant (cl_type, False) else -- It is a function and we need to save the result onto -- a local variable. context.add_local (a_return_type) local_number := context.local_list.count il_generator.put_dummy_local_info (a_return_type, local_number) il_generator.generate_local_assignment (local_number) generate_il_call_invariant (cl_type, False) il_generator.generate_local (local_number) end end il_special_routines: IL_SPECIAL_FEATURES -- Array containing special routines. once create Result ensure il_special_routines_not_void: Result /= Void end generate_il_normal_call (a_node: FEATURE_B; target_type: CL_TYPE_A; is_virtual: BOOLEAN) -- Normal feature call. require is_valid: is_valid a_node_not_void: a_node /= Void target_type_not_void: target_type /= Void local l_count: INTEGER l_return_type: TYPE_A target_feature_id: INTEGER do if attached a_node.parameters as p then l_count := p.count end l_return_type := if a_node.is_once and then attached target_type.base_class as b and then b.is_once and then b.creators.has (a_node.feature_name_id) then target_type else context.real_type (a_node.type) end if target_type.is_expanded then -- Generate direct call. target_feature_id := target_type.base_class.feature_of_rout_id (a_node.routine_id).feature_id else target_feature_id := a_node.feature_id end if a_node.is_target_type_fixed then -- In IL, if you can call Precursor, it means that parent is -- not expanded and therefore we can safely generate a static -- call to Precursor feature. il_generator.generate_precursor_feature_access ( target_type, target_feature_id, l_count, not l_return_type.is_void) else il_generator.generate_feature_access ( target_type, target_feature_id, l_count, not l_return_type.is_void, is_virtual) end if System.il_verifiable and then not l_return_type.is_expanded and then not l_return_type.is_none and then not l_return_type.is_void then il_generator.generate_check_cast (Void, l_return_type) end end generate_il_any_call (a_node: FEATURE_B; written_type, target_type: CL_TYPE_A; is_virtual: BOOLEAN) -- Generate call to routine of ANY that works for both ANY and SYSTEM_OBJECT. -- Arguments and target of the call are already pushed on the stack. require is_valid: is_valid a_node_not_void: a_node /= Void il_generation: System.il_generation is_any_feature: a_node.is_any_feature written_type_not_void: written_type /= Void target_type_not_void: target_type /= Void not_precursor_call: a_node.precursor_type = Void local l_count: INTEGER l_return_type: TYPE_A do if a_node.parameters /= Void then l_count := a_node.parameters.count end l_return_type := context.real_type (a_node.type) -- It is safe to use the name and not the routine ID to identify the routine -- because we are in ANY and therefore no renaming took place. inspect a_node.feature_name_id when {PREDEFINED_NAMES}.conforms_to_name_id, {PREDEFINED_NAMES}.deep_equal_name_id, {PREDEFINED_NAMES}.is_deep_equal_name_id, {PREDEFINED_NAMES}.same_type_name_id, {PREDEFINED_NAMES}.standard_equal_name_id, {PREDEFINED_NAMES}.standard_is_equal_name_id then generate_frozen_boolean_routine (a_node.feature_name_id) when {PREDEFINED_NAMES}.default_name_id then generate_default (target_type, l_return_type) when {PREDEFINED_NAMES}.default_pointer_name_id then generate_default_pointer (target_type) when {PREDEFINED_NAMES}.do_nothing_name_id then generate_do_nothing (target_type) when {PREDEFINED_NAMES}.io_name_id then generate_io (a_node, written_type, target_type) when {PREDEFINED_NAMES}.operating_environment_name_id then generate_operating_environment (a_node, written_type, target_type) when {PREDEFINED_NAMES}.generator_name_id, {PREDEFINED_NAMES}.out_name_id, {PREDEFINED_NAMES}.tagged_out_name_id then generate_string_routine (a_node, written_type) when {PREDEFINED_NAMES}.generating_type_name_id then generate_generating_type (a_node, written_type, target_type) when {PREDEFINED_NAMES}.clone_name_id, {PREDEFINED_NAMES}.deep_clone_name_id, {PREDEFINED_NAMES}.standard_clone_name_id then generate_clone_routine (a_node.feature_name_id, l_return_type) when {PREDEFINED_NAMES}.copy_name_id, {PREDEFINED_NAMES}.deep_copy_name_id, {PREDEFINED_NAMES}.standard_copy_name_id then generate_copy_routine (a_node.feature_name_id) when {PREDEFINED_NAMES}.deep_twin_name_id, {PREDEFINED_NAMES}.standard_twin_name_id, {PREDEFINED_NAMES}.twin_name_id then generate_twin_routine (a_node.feature_name_id, l_return_type) when {PREDEFINED_NAMES}.equal_name_id, {PREDEFINED_NAMES}.is_equal_name_id then generate_equal_routine (a_node.feature_name_id) when {PREDEFINED_NAMES}.default_create_name_id, {PREDEFINED_NAMES}.default_rescue_name_id then generate_default_routine (a_node, written_type, target_type) when {PREDEFINED_NAMES}.to_dotnet_name_id then -- Nothing to be done, we like what we have on top of the stack -- since it is already a .NET object. else -- We come here if a routine has been added by user in ANY, or -- if we handle `print' and `internal_correct_mismatch'. if not target_type.is_external then -- Normal call to Eiffel routine. generate_il_normal_call (a_node, written_type, is_virtual) else -- New routine of ANY, or `print' or `internal_correct_mismatch' applied -- to a .NET object, we raise an exception as they should not be called -- on .NET object. -- Remove arguments and target from stack. from until l_count < 0 loop il_generator.pop l_count := l_count - 1 end il_generator.generate_raise_exception ({EXCEP_CONST}.precondition, "Feature of ANY not implemented for .NET class") end end end generate_frozen_boolean_routine (a_feature_name_id: INTEGER) -- Generate inlined call to routine of ANY that are completely frozen (that is to -- say their definition is frozen and they don't call non-frozen routine in their -- definition if ANY) and that returns a boolean value. require is_valid: is_valid valid_feature_name: a_feature_name_id = {PREDEFINED_NAMES}.conforms_to_name_id or a_feature_name_id = {PREDEFINED_NAMES}.deep_equal_name_id or a_feature_name_id = {PREDEFINED_NAMES}.is_deep_equal_name_id or a_feature_name_id = {PREDEFINED_NAMES}.same_type_name_id or a_feature_name_id = {PREDEFINED_NAMES}.standard_equal_name_id or a_feature_name_id = {PREDEFINED_NAMES}.standard_is_equal_name_id local l_extension: IL_EXTENSION_I l_id: INTEGER do -- No need to check validity of target, it is done by the runtime -- routine we are calling. -- Create representation of the routine we are calling. create l_extension l_extension.set_alias_name_id (a_feature_name_id) l_extension.set_return_type ({PREDEFINED_NAMES}.system_boolean_name_id) l_extension.set_base_class (runtime_class_name) l_extension.set_type ({SHARED_IL_CONSTANTS}.static_type) l_id := {PREDEFINED_NAMES}.system_object_name_id inspect a_feature_name_id when {PREDEFINED_NAMES}.conforms_to_name_id, {PREDEFINED_NAMES}.same_type_name_id, {PREDEFINED_NAMES}.standard_is_equal_name_id, {PREDEFINED_NAMES}.is_deep_equal_name_id then l_extension.set_argument_types (<>) when {PREDEFINED_NAMES}.deep_equal_name_id, {PREDEFINED_NAMES}.standard_equal_name_id then l_extension.set_argument_types (<>) end -- Call routine l_extension.generate_call (False) end generate_clone_routine (a_feature_name_id: INTEGER; a_result_type: TYPE_A) -- Generate inlined call to xx_clone' routines of ANY. require is_valid: is_valid a_result_type_not_void: a_result_type /= Void valid_feature_name: a_feature_name_id = {PREDEFINED_NAMES}.clone_name_id or a_feature_name_id = {PREDEFINED_NAMES}.deep_clone_name_id or a_feature_name_id = {PREDEFINED_NAMES}.standard_clone_name_id local l_extension: IL_EXTENSION_I l_id: INTEGER do -- No need to check validity of target, it is done by the runtime -- routine we are calling. -- Create representation of the routine we are calling. create l_extension l_extension.set_alias_name_id (a_feature_name_id) l_extension.set_return_type ({PREDEFINED_NAMES}.system_object_name_id) l_extension.set_base_class (runtime_class_name) l_extension.set_type ({SHARED_IL_CONSTANTS}.static_type) l_id := {PREDEFINED_NAMES}.system_object_name_id l_extension.set_argument_types (<>) -- Call routine l_extension.generate_call (False) -- Unbox result if required if context.real_type (a_result_type).is_expanded then il_generator.generate_unmetamorphose (context.real_type (a_result_type)) end -- Cast result back to proper type il_generator.generate_check_cast (Void, a_result_type) end generate_copy_routine (a_feature_name_id: INTEGER) -- Generate inlined call to xx_copy' routines of ANY. require is_valid: is_valid valid_feature_name: a_feature_name_id = {PREDEFINED_NAMES}.copy_name_id or a_feature_name_id = {PREDEFINED_NAMES}.deep_copy_name_id or a_feature_name_id = {PREDEFINED_NAMES}.standard_copy_name_id local l_extension: IL_EXTENSION_I l_id: INTEGER do -- No need to check validity of target, it is done by the runtime -- routine we are calling. -- Create representation of the routine we are calling. create l_extension l_extension.set_alias_name_id (a_feature_name_id) l_extension.set_base_class (runtime_class_name) l_extension.set_type ({SHARED_IL_CONSTANTS}.static_type) l_id := {PREDEFINED_NAMES}.system_object_name_id l_extension.set_argument_types (<>) -- Call routine l_extension.generate_call (False) end generate_default (target_type, return_type: TYPE_A) -- Generate inlined call to `default'. require is_valid: is_valid target_type_not_void: target_type /= Void do -- Check validity of target generate_call_on_void_target (target_type, False) if target_type.is_expanded then if target_type.is_basic then -- Put default value for basic type il_generator.put_default_value (target_type) elseif target_type.is_external then -- Create new instance of external expanded type. (create {CREATE_TYPE}.make (target_type)).generate_il elseif attached {CL_TYPE_A} target_type as cl_type_i then -- Create new instance of internal expanded type. il_generator.create_expanded_object (cl_type_i) else check False end end if return_type.is_reference then -- Box value type il_generator.generate_metamorphose (target_type) end else -- Reference case, we simply return Void il_generator.put_void end end generate_default_routine (a_node: FEATURE_B; written_type, target_type: CL_TYPE_A) -- Generate inlined call to `default_create' and `default_rescue'. require is_valid: is_valid a_node_not_void: a_node /= Void written_type_not_void: written_type /= Void target_type_not_void: target_type /= Void a_node.feature_name_id = {PREDEFINED_NAMES}.default_create_name_id or a_node.feature_name_id = {PREDEFINED_NAMES}.default_rescue_name_id local l_dotnet_label, l_end_label: IL_LABEL do -- Since `default_create' and `default_rescue' are virtual in ANY, -- we need to perform a dynamic dispatch when we handle an Eiffel class. l_dotnet_label := il_generator.create_label l_end_label := il_generator.create_label il_generator.generate_is_true_instance_of (any_type) il_generator.duplicate_top il_generator.branch_on_false (l_dotnet_label) generate_il_normal_call (a_node, written_type, True) il_generator.branch_to (l_end_label) il_generator.mark_label (l_dotnet_label) -- Check validity of target for .NET case generate_call_on_void_target (target_type, False) -- Nothing to be done for .NET since their content is empty. il_generator.mark_label (l_end_label) end generate_default_pointer (target_type: TYPE_A) -- Generate inlined call to `default_pointer'. require is_valid: is_valid target_type_not_void: target_type /= Void do -- Check validity of target generate_call_on_void_target (target_type, False) -- We simply return null pointer il_generator.put_integer_32_constant (0) il_generator.convert_to_native_int end generate_do_nothing (target_type: TYPE_A) -- Generate inlined call to `do_nothing'. require is_valid: is_valid target_type_not_void: target_type /= Void do -- Check validity of target generate_call_on_void_target (target_type, False) end generate_equal_routine (a_feature_name_id: INTEGER) -- Generate inlined call to `equal' and `is_equal' routines of ANY. require is_valid: is_valid valid_feature_name: a_feature_name_id = {PREDEFINED_NAMES}.equal_name_id or a_feature_name_id = {PREDEFINED_NAMES}.is_equal_name_id local l_extension: IL_EXTENSION_I l_id: INTEGER do -- No need to check validity of target, it is done by the runtime -- routine we are calling. -- Create representation of the routine we are calling. create l_extension l_extension.set_alias_name_id (a_feature_name_id) l_extension.set_return_type ({PREDEFINED_NAMES}.system_boolean_name_id) l_extension.set_base_class (runtime_class_name) l_extension.set_type ({SHARED_IL_CONSTANTS}.static_type) l_id := {PREDEFINED_NAMES}.system_object_name_id if a_feature_name_id = {PREDEFINED_NAMES}.is_equal_name_id then l_extension.set_argument_types (<>) else l_extension.set_argument_types (<>) end -- Call routine l_extension.generate_call (False) end generate_io (a_node: FEATURE_B; written_type, target_type: CL_TYPE_A) -- Generate inlined call to `io'. require is_valid: is_valid a_node_not_void: a_node /= Void written_type_not_void: written_type /= Void target_type_not_void: target_type /= Void local l_io_label: IL_LABEL do -- Check validity of target. generate_call_on_void_target (target_type, True) -- Call feature on this object if its type inherits ANY. l_io_label := il_generator.create_label il_generator.generate_is_true_instance_of (any_type) il_generator.duplicate_top il_generator.branch_on_true (l_io_label) il_generator.pop; -- Call feature from ANY because type of object does not inherit it. (create {CREATE_TYPE}.make (any_type)).generate_il il_generator.mark_label (l_io_label) generate_il_normal_call (a_node, written_type, True) end generate_operating_environment (a_node: FEATURE_B; written_type, target_type: CL_TYPE_A) -- Generate inlined call to `operating_environment'. require is_valid: is_valid a_node_not_void: a_node /= Void written_type_not_void: written_type /= Void target_type_not_void: target_type /= Void local l_oe_label: IL_LABEL do -- Check validity of target generate_call_on_void_target (target_type, True) l_oe_label := il_generator.create_label il_generator.generate_is_true_instance_of (any_type) il_generator.duplicate_top il_generator.branch_on_true (l_oe_label) il_generator.pop; (create {CREATE_TYPE}.make (any_type)).generate_il il_generator.mark_label (l_oe_label) generate_il_normal_call (a_node, written_type, True) end generate_string_routine (a_node: FEATURE_B; written_type: CL_TYPE_A) -- Generate inlined call to routines of ANY returning a STRING object: -- `generator', `generating_type', `out' and `tagged_out'. require is_valid: is_valid a_node_not_void: a_node /= Void written_type_not_void: written_type /= Void valid_feature_name: a_node.feature_name_id = {PREDEFINED_NAMES}.generating_type_name_id or a_node.feature_name_id = {PREDEFINED_NAMES}.generator_name_id or a_node.feature_name_id = {PREDEFINED_NAMES}.out_name_id or a_node.feature_name_id = {PREDEFINED_NAMES}.tagged_out_name_id local l_extension: IL_EXTENSION_I l_local: INTEGER l_out_label, l_end_label: IL_LABEL do if a_node.feature_name_id = {PREDEFINED_NAMES}.out_name_id or else a_node.feature_name_id = {PREDEFINED_NAMES}.tagged_out_name_id then -- Since `out' is virtual in ANY, we need to perform -- a dynamic dispatch when we handle an Eiffel class. l_out_label := il_generator.create_label l_end_label := il_generator.create_label il_generator.duplicate_top il_generator.generate_is_true_instance_of (any_type) il_generator.branch_on_false (l_out_label) il_generator.generate_is_true_instance_of (any_type) generate_il_normal_call (a_node, written_type, True) il_generator.branch_to (l_end_label) il_generator.mark_label (l_out_label) end -- No need to check validity of target, it is done by the runtime -- routine we are calling. -- Create representation of the routine we are calling. create l_extension l_extension.set_alias_name_id (a_node.feature_name_id) l_extension.set_return_type ({PREDEFINED_NAMES}.system_string_name_id) l_extension.set_base_class (runtime_class_name) l_extension.set_argument_types (<<{PREDEFINED_NAMES}.system_object_name_id>>) l_extension.set_type ({SHARED_IL_CONSTANTS}.static_type) -- Call routine l_extension.generate_call (False) -- Store result in temporary variable context.add_local (system_string_type) l_local := context.local_list.count il_generator.put_dummy_local_info (system_string_type, l_local) il_generator.generate_local_assignment (l_local) -- Generate Eiffel STRING object from SYSTEM_STRING object il_generator.put_manifest_string_from_system_string_local (l_local) if l_end_label /= Void then il_generator.mark_label (l_end_label) end end generate_generating_type (a_node: FEATURE_B; written_type, target_type: CL_TYPE_A) -- Generate inlined call to `generating_type'. require is_valid: is_valid a_node_not_void: a_node /= Void written_type_not_void: written_type /= Void target_type_not_void: target_type /= Void valid_feature_name: a_node.feature_name_id = {PREDEFINED_NAMES}.generating_type_name_id local l_label, l_end_label: IL_LABEL do if system.is_using_new_generating_type then -- Check validity of target. generate_call_on_void_target (target_type, True) -- Call feature on this object if its type inherits ANY. l_label := il_generator.create_label l_end_label := il_generator.create_label il_generator.duplicate_top il_generator.generate_is_true_instance_of (any_type) il_generator.branch_on_true (l_label) -- For a non-Eiffel class we will do the best we can. It works -- fine for reference type, but if the code is generated from a context -- we the type is not reference but used with an expanded type then it might -- not work. See eweasel test#dotnet0xx. il_generator.generate_generating_type_instance (context.real_type (a_node.type)) il_generator.branch_to (l_end_label) il_generator.mark_label (l_label) il_generator.generate_is_true_instance_of (any_type) generate_il_normal_call (a_node, written_type, True) il_generator.mark_label (l_end_label) else -- In compatibility mode `generating_type' return STRING. generate_string_routine (a_node, written_type) end end generate_twin_routine (a_feature_name_id: INTEGER; a_result_type: TYPE_A) -- Generate inlined call to xx_twin' routines of ANY. require is_valid: is_valid a_result_type_not_void: a_result_type /= Void valid_feature_name: a_feature_name_id = {PREDEFINED_NAMES}.twin_name_id or a_feature_name_id = {PREDEFINED_NAMES}.deep_twin_name_id or a_feature_name_id = {PREDEFINED_NAMES}.standard_twin_name_id local l_extension: IL_EXTENSION_I do -- No need to check validity of target, it is done by the runtime -- routine we are calling. -- Create representation of the routine we are calling. create l_extension l_extension.set_alias_name_id (a_feature_name_id) l_extension.set_return_type ({PREDEFINED_NAMES}.system_object_name_id) l_extension.set_base_class (runtime_class_name) l_extension.set_type ({SHARED_IL_CONSTANTS}.static_type) l_extension.set_argument_types (<<{PREDEFINED_NAMES}.system_object_name_id>>) -- Call routine l_extension.generate_call (False) -- Unbox result if required if a_result_type.is_basic then il_generator.generate_load_address (a_result_type) il_generator.generate_load_from_address_as_basic (a_result_type) elseif a_result_type.is_expanded then il_generator.generate_unmetamorphose (a_result_type) end -- Cast result back to proper type il_generator.generate_check_cast (Void, a_result_type) end feature {NONE} -- Convenience generate_call_on_void_target (target_type: TYPE_A; need_top_duplication: bOOLEAN) -- Generate check that current object is not Void. require is_valid: is_valid target_type_not_void: target_type /= Void local l_not_void_label: IL_LABEL do if need_top_duplication then il_generator.duplicate_top end if target_type.is_reference then l_not_void_label := il_generator.create_label -- Check that target is not Void. il_generator.branch_on_true (l_not_void_label) -- If target of call is Void, throw an exception. il_generator.generate_call_on_void_target_exception -- Else we proceed normally il_generator.mark_label (l_not_void_label) else -- Top element is not required so we can get rid of it. il_generator.pop end end any_type: CL_TYPE_A -- Actual type of ANY require has_any: system.any_class /= Void has_any_compiled: system.any_class.is_compiled once Result := system.any_class.compiled_class.actual_type ensure any_type_not_void: Result /= Void end system_string_type: CL_TYPE_A -- Actual type of SYSTEM_STRING require has_system_string: system.system_string_class /= Void has_system_string_compiled: system.system_string_class.is_compiled once Result := system.system_string_class.compiled_class.actual_type ensure system_string_type_not_void: Result /= Void end system_exception_type: CL_TYPE_A -- Actual type of SYSTEM_EXCEPTION require has_system_string: system.system_exception_type_class /= Void has_system_string_compiled: system.system_exception_type_class.is_compiled once Result := system.system_exception_type_class.compiled_class.actual_type ensure system_exception_type_not_void: Result /= Void end note ca_ignore: "CA033", "CA033: too large class" date: "$Date$" revision: "$Revision$" copyright: "Copyright (c) 1984-2023, Eiffel Software" license: "GPL version 2 (see http://www.eiffel.com/licensing/gpl.txt)" licensing_options: "http://www.eiffel.com/licensing" copying: "[ This file is part of Eiffel Software's Eiffel Development Environment. Eiffel Software's Eiffel Development Environment 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, version 2 of the License (available at the URL listed under "license" above). Eiffel Software's Eiffel Development Environment 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 Eiffel Software's Eiffel Development Environment; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ]" source: "[ Eiffel Software 5949 Hollister Ave., Goleta, CA 93117 USA Telephone 805-685-1006, Fax 805-685-6869 Website http://www.eiffel.com Customer support http://support.eiffel.com ]" end