konpeito 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -955,14 +955,24 @@ module Konpeito
955
955
  }
956
956
  elsif has_rescue
957
957
  outer_br.rescue_clauses.each_with_index do |clause, i|
958
- clause.exception_classes.each do |exc_class|
959
- jvm_exc = ruby_exception_to_jvm(exc_class)
958
+ if clause.exception_classes.empty?
959
+ # Bare rescue → catch StandardError (Java Exception)
960
960
  @pending_exception_table << {
961
961
  "start" => try_start,
962
962
  "end" => try_end,
963
963
  "handler" => handler_labels[i],
964
- "type" => jvm_exc
964
+ "type" => "java/lang/Exception"
965
965
  }
966
+ else
967
+ clause.exception_classes.each do |exc_class|
968
+ jvm_exc = ruby_exception_to_jvm(exc_class)
969
+ @pending_exception_table << {
970
+ "start" => try_start,
971
+ "end" => try_end,
972
+ "handler" => handler_labels[i],
973
+ "type" => jvm_exc
974
+ }
975
+ end
966
976
  end
967
977
  end
968
978
  end
@@ -1313,6 +1323,8 @@ module Konpeito
1313
1323
  generate_begin_rescue(inst)
1314
1324
  when HIR::CaseStatement
1315
1325
  generate_case_statement(inst)
1326
+ when HIR::CaseEqualityCheck
1327
+ generate_case_equality_check(inst)
1316
1328
  when HIR::CaseMatchStatement
1317
1329
  generate_case_match_statement(inst)
1318
1330
  when HIR::LoadGlobalVar
@@ -2844,9 +2856,22 @@ module Konpeito
2844
2856
  end
2845
2857
  end
2846
2858
 
2847
- # Use inlined try body if provided (from block's pre-BeginRescue instructions),
2848
- # otherwise fall back to BeginRescue's stored try_blocks
2849
- try_body = inlined_try_body || inst.try_blocks || []
2859
+ # Determine try body. If BeginRescue stores its own try_blocks, use those.
2860
+ # Pre-BeginRescue instructions (inlined_try_body) are only the try body when
2861
+ # the BeginRescue has no stored try_blocks (legacy HIR builder behavior).
2862
+ has_own_try = (inst.try_blocks && !inst.try_blocks.empty?) || (inst.try_hir_blocks && !inst.try_hir_blocks.empty?)
2863
+ pre_try_instructions = []
2864
+ if has_own_try
2865
+ # BeginRescue has its own try body. Pre-BeginRescue instructions that are NOT
2866
+ # already in try_blocks go BEFORE the try scope (e.g. variable initializations
2867
+ # before a begin/rescue block).
2868
+ try_body = inst.try_blocks || []
2869
+ try_block_ids = Set.new(try_body.map(&:object_id))
2870
+ pre_try_instructions = (inlined_try_body || []).reject { |i| try_block_ids.include?(i.object_id) }
2871
+ else
2872
+ # Legacy: pre-BeginRescue instructions ARE the try body
2873
+ try_body = (inlined_try_body && !inlined_try_body.empty?) ? inlined_try_body : []
2874
+ end
2850
2875
 
2851
2876
  # If the begin/rescue has a result variable, pre-initialize it with null
2852
2877
  result_var = inst.result_var
@@ -2874,6 +2899,11 @@ module Konpeito
2874
2899
  # Label for exception ensure path (catch-all)
2875
2900
  finally_handler_label = new_label("finally_handler") if has_ensure
2876
2901
 
2902
+ # 0. Emit pre-try instructions (code before begin/rescue that is NOT in the try scope)
2903
+ pre_try_instructions.each do |pre_inst|
2904
+ instructions.concat(generate_instruction(pre_inst))
2905
+ end
2906
+
2877
2907
  # 1. Try block (generate BEFORE registering outer exception handlers,
2878
2908
  # so that inner/nested BeginRescue handlers appear first in the table.
2879
2909
  # JVM checks exception table entries in order; first match wins.)
@@ -2907,14 +2937,24 @@ module Konpeito
2907
2937
  }
2908
2938
  elsif has_rescue
2909
2939
  inst.rescue_clauses.each_with_index do |clause, i|
2910
- clause.exception_classes.each do |exc_class|
2911
- jvm_exc = ruby_exception_to_jvm(exc_class)
2940
+ if clause.exception_classes.empty?
2941
+ # Bare rescue (no exception class) → catch StandardError (Java Exception)
2912
2942
  @pending_exception_table << {
2913
2943
  "start" => try_start,
2914
2944
  "end" => try_end,
2915
2945
  "handler" => handler_labels[i],
2916
- "type" => jvm_exc
2946
+ "type" => "java/lang/Exception"
2917
2947
  }
2948
+ else
2949
+ clause.exception_classes.each do |exc_class|
2950
+ jvm_exc = ruby_exception_to_jvm(exc_class)
2951
+ @pending_exception_table << {
2952
+ "start" => try_start,
2953
+ "end" => try_end,
2954
+ "handler" => handler_labels[i],
2955
+ "type" => jvm_exc
2956
+ }
2957
+ end
2918
2958
  end
2919
2959
  end
2920
2960
  end
@@ -3132,6 +3172,48 @@ module Konpeito
3132
3172
  instructions
3133
3173
  end
3134
3174
 
3175
+ # ========================================================================
3176
+ # CaseEqualityCheck (HIR node used by the block-based case/when)
3177
+ # ========================================================================
3178
+
3179
+ # Generate condition === predicate for case/when dispatch.
3180
+ # The condition is the when-value (e.g. 10, "hello") and the predicate is
3181
+ # the case expression (e.g. x). Result is stored as :i8 (boolean).
3182
+ # Uses load_value to either load from already-generated slot or generate inline.
3183
+ def generate_case_equality_check(inst)
3184
+ instructions = []
3185
+ result_var = inst.result_var
3186
+
3187
+ if inst.predicate.nil?
3188
+ # No predicate (bare `case; when cond then ...`): evaluate condition as truthy
3189
+ instructions.concat(load_value(inst.condition, :value))
3190
+ instructions << { "op" => "invokestatic", "owner" => "konpeito/runtime/RubyDispatch",
3191
+ "name" => "isTruthy", "descriptor" => "(Ljava/lang/Object;)Z" }
3192
+ if result_var
3193
+ ensure_slot(result_var, :i8)
3194
+ instructions << store_instruction(result_var, :i8)
3195
+ @variable_types[result_var.to_s] = :i8
3196
+ end
3197
+ else
3198
+ # condition === predicate: condition is the when value, predicate is the case expr.
3199
+ # Load condition (when value) — box to Object
3200
+ instructions.concat(load_value(inst.condition, :value))
3201
+ # Load predicate (case expression) — box to Object
3202
+ instructions.concat(load_value(inst.predicate, :value))
3203
+ # Call RubyDispatch.caseEqual(condition, predicate) → Z
3204
+ instructions << { "op" => "invokestatic", "owner" => "konpeito/runtime/RubyDispatch",
3205
+ "name" => "caseEqual",
3206
+ "descriptor" => "(Ljava/lang/Object;Ljava/lang/Object;)Z" }
3207
+ if result_var
3208
+ ensure_slot(result_var, :i8)
3209
+ instructions << store_instruction(result_var, :i8)
3210
+ @variable_types[result_var.to_s] = :i8
3211
+ end
3212
+ end
3213
+
3214
+ instructions
3215
+ end
3216
+
3135
3217
  # ========================================================================
3136
3218
  # case/when Statement
3137
3219
  # ========================================================================
@@ -15243,6 +15325,15 @@ module Konpeito
15243
15325
  incoming_types = phi.incoming.values.map do |val|
15244
15326
  var = extract_var_name(val)
15245
15327
  type = var ? @variable_types[var] : nil
15328
+ # For LoadInstanceVar, check the CLASS-LEVEL field type instead of HM annotation.
15329
+ # HM inference can produce incorrect types for ivars due to TypeVar pollution
15330
+ # (e.g., @font_family_val typed as Float instead of String).
15331
+ # The class field declaration is the source of truth.
15332
+ if type.nil? && val.is_a?(HIR::LoadInstanceVar) && @current_class_name
15333
+ field_name = val.name.to_s.sub(/^@/, "")
15334
+ ivar_info = resolve_ivar_info(field_name)
15335
+ type = ivar_info[:type] if ivar_info
15336
+ end
15246
15337
  type || infer_type_from_hir(val) || literal_type_tag(val) || :value
15247
15338
  end
15248
15339