konpeito 0.1.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.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +75 -0
- data/CONTRIBUTING.md +123 -0
- data/LICENSE +21 -0
- data/README.md +257 -0
- data/Rakefile +11 -0
- data/bin/konpeito +6 -0
- data/konpeito.gemspec +43 -0
- data/lib/konpeito/ast/typed_ast.rb +620 -0
- data/lib/konpeito/ast/visitor.rb +78 -0
- data/lib/konpeito/cache/cache_manager.rb +230 -0
- data/lib/konpeito/cache/dependency_graph.rb +192 -0
- data/lib/konpeito/cache.rb +8 -0
- data/lib/konpeito/cli/base_command.rb +187 -0
- data/lib/konpeito/cli/build_command.rb +220 -0
- data/lib/konpeito/cli/check_command.rb +104 -0
- data/lib/konpeito/cli/config.rb +231 -0
- data/lib/konpeito/cli/deps_command.rb +128 -0
- data/lib/konpeito/cli/doctor_command.rb +340 -0
- data/lib/konpeito/cli/fmt_command.rb +199 -0
- data/lib/konpeito/cli/init_command.rb +312 -0
- data/lib/konpeito/cli/lsp_command.rb +40 -0
- data/lib/konpeito/cli/run_command.rb +150 -0
- data/lib/konpeito/cli/test_command.rb +248 -0
- data/lib/konpeito/cli/watch_command.rb +212 -0
- data/lib/konpeito/cli.rb +301 -0
- data/lib/konpeito/codegen/builtin_methods.rb +229 -0
- data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
- data/lib/konpeito/codegen/debug_info.rb +352 -0
- data/lib/konpeito/codegen/inliner.rb +486 -0
- data/lib/konpeito/codegen/jvm_backend.rb +197 -0
- data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
- data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
- data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
- data/lib/konpeito/codegen/monomorphizer.rb +359 -0
- data/lib/konpeito/codegen/profile_runtime.c +341 -0
- data/lib/konpeito/codegen/profiler.rb +99 -0
- data/lib/konpeito/compiler.rb +592 -0
- data/lib/konpeito/dependency_resolver.rb +296 -0
- data/lib/konpeito/diagnostics/collector.rb +127 -0
- data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
- data/lib/konpeito/diagnostics/renderer.rb +144 -0
- data/lib/konpeito/formatter/formatter.rb +1214 -0
- data/lib/konpeito/hir/builder.rb +7167 -0
- data/lib/konpeito/hir/nodes.rb +2465 -0
- data/lib/konpeito/lsp/document_manager.rb +820 -0
- data/lib/konpeito/lsp/server.rb +183 -0
- data/lib/konpeito/lsp/transport.rb +38 -0
- data/lib/konpeito/parser/prism_adapter.rb +65 -0
- data/lib/konpeito/platform.rb +103 -0
- data/lib/konpeito/profile/report.rb +136 -0
- data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
- data/lib/konpeito/stdlib/compression/compression.rb +72 -0
- data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
- data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
- data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
- data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
- data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
- data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
- data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
- data/lib/konpeito/stdlib/http/extconf.rb +19 -0
- data/lib/konpeito/stdlib/http/http.rb +125 -0
- data/lib/konpeito/stdlib/http/http.rbs +57 -0
- data/lib/konpeito/stdlib/http/http_native.c +440 -0
- data/lib/konpeito/stdlib/json/extconf.rb +17 -0
- data/lib/konpeito/stdlib/json/json.rb +44 -0
- data/lib/konpeito/stdlib/json/json.rbs +33 -0
- data/lib/konpeito/stdlib/json/json_native.c +286 -0
- data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
- data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
- data/lib/konpeito/stdlib/ui/ui.rb +318 -0
- data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
- data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
- data/lib/konpeito/type_checker/inferrer.rb +565 -0
- data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
- data/lib/konpeito/type_checker/type_resolver.rb +276 -0
- data/lib/konpeito/type_checker/types.rb +1434 -0
- data/lib/konpeito/type_checker/unification.rb +323 -0
- data/lib/konpeito/ui/animation/animated_state.rb +80 -0
- data/lib/konpeito/ui/animation/easing.rb +59 -0
- data/lib/konpeito/ui/animation/value_tween.rb +66 -0
- data/lib/konpeito/ui/app.rb +379 -0
- data/lib/konpeito/ui/box.rb +38 -0
- data/lib/konpeito/ui/castella.rb +70 -0
- data/lib/konpeito/ui/castella_native.rb +76 -0
- data/lib/konpeito/ui/chart/area_chart.rb +305 -0
- data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
- data/lib/konpeito/ui/chart/base_chart.rb +210 -0
- data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
- data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
- data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
- data/lib/konpeito/ui/chart/line_chart.rb +289 -0
- data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
- data/lib/konpeito/ui/chart/scales.rb +77 -0
- data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
- data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
- data/lib/konpeito/ui/column.rb +271 -0
- data/lib/konpeito/ui/core.rb +2199 -0
- data/lib/konpeito/ui/dsl.rb +443 -0
- data/lib/konpeito/ui/frame.rb +171 -0
- data/lib/konpeito/ui/frame_native.rb +494 -0
- data/lib/konpeito/ui/markdown/ast.rb +124 -0
- data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
- data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
- data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
- data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
- data/lib/konpeito/ui/markdown/parser.rb +805 -0
- data/lib/konpeito/ui/markdown/renderer.rb +639 -0
- data/lib/konpeito/ui/markdown/theme.rb +165 -0
- data/lib/konpeito/ui/render_node.rb +260 -0
- data/lib/konpeito/ui/row.rb +207 -0
- data/lib/konpeito/ui/spacer.rb +18 -0
- data/lib/konpeito/ui/style.rb +799 -0
- data/lib/konpeito/ui/theme.rb +563 -0
- data/lib/konpeito/ui/themes/material.rb +35 -0
- data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
- data/lib/konpeito/ui/widgets/button.rb +103 -0
- data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
- data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
- data/lib/konpeito/ui/widgets/container.rb +91 -0
- data/lib/konpeito/ui/widgets/data_table.rb +667 -0
- data/lib/konpeito/ui/widgets/divider.rb +29 -0
- data/lib/konpeito/ui/widgets/image.rb +105 -0
- data/lib/konpeito/ui/widgets/input.rb +485 -0
- data/lib/konpeito/ui/widgets/markdown.rb +57 -0
- data/lib/konpeito/ui/widgets/modal.rb +163 -0
- data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
- data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
- data/lib/konpeito/ui/widgets/net_image.rb +100 -0
- data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
- data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
- data/lib/konpeito/ui/widgets/slider.rb +133 -0
- data/lib/konpeito/ui/widgets/switch.rb +84 -0
- data/lib/konpeito/ui/widgets/tabs.rb +157 -0
- data/lib/konpeito/ui/widgets/text.rb +110 -0
- data/lib/konpeito/ui/widgets/tree.rb +426 -0
- data/lib/konpeito/version.rb +5 -0
- data/lib/konpeito.rb +109 -0
- data/test_native_array.rb +172 -0
- data/test_native_array_class.rb +197 -0
- data/test_native_class.rb +151 -0
- data/tools/konpeito-asm/build.sh +65 -0
- data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
- metadata +267 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Konpeito
|
|
4
|
+
module Codegen
|
|
5
|
+
# Inlines small functions at call sites to eliminate call overhead
|
|
6
|
+
# and enable further optimizations.
|
|
7
|
+
#
|
|
8
|
+
# Inlining criteria:
|
|
9
|
+
# - Function body has <= MAX_INLINE_INSTRUCTIONS instructions
|
|
10
|
+
# - No recursion (direct or indirect)
|
|
11
|
+
# - Not a class method (for simplicity)
|
|
12
|
+
#
|
|
13
|
+
class Inliner
|
|
14
|
+
MAX_INLINE_INSTRUCTIONS = 10
|
|
15
|
+
MAX_INLINE_DEPTH = 3
|
|
16
|
+
|
|
17
|
+
attr_reader :inlined_count
|
|
18
|
+
|
|
19
|
+
def initialize(hir_program)
|
|
20
|
+
@hir_program = hir_program
|
|
21
|
+
@functions = {} # name -> HIR::Function
|
|
22
|
+
@inline_candidates = {} # name -> true/false
|
|
23
|
+
@call_graph = {} # name -> Set of called function names
|
|
24
|
+
@inlined_count = 0
|
|
25
|
+
@current_depth = 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Analyze and transform the HIR program
|
|
29
|
+
def optimize
|
|
30
|
+
build_function_map
|
|
31
|
+
build_call_graph
|
|
32
|
+
identify_candidates
|
|
33
|
+
inline_functions
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def build_function_map
|
|
39
|
+
@hir_program.functions.each do |func|
|
|
40
|
+
@functions[func.name.to_s] = func
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def build_call_graph
|
|
45
|
+
@hir_program.functions.each do |func|
|
|
46
|
+
calls = Set.new
|
|
47
|
+
func.body.each do |block|
|
|
48
|
+
block.instructions.each do |inst|
|
|
49
|
+
if inst.is_a?(HIR::Call) && self_call?(inst)
|
|
50
|
+
calls << inst.method_name.to_s
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
@call_graph[func.name.to_s] = calls
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self_call?(inst)
|
|
59
|
+
inst.receiver.is_a?(HIR::SelfRef) ||
|
|
60
|
+
(inst.receiver.is_a?(HIR::Instruction) &&
|
|
61
|
+
inst.receiver.type.is_a?(TypeChecker::Types::Untyped))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def identify_candidates
|
|
65
|
+
@functions.each do |name, func|
|
|
66
|
+
@inline_candidates[name] = should_inline?(func)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def should_inline?(func)
|
|
71
|
+
# Skip main function
|
|
72
|
+
return false if func.name.to_s == "__main__"
|
|
73
|
+
|
|
74
|
+
# Skip class methods for now
|
|
75
|
+
return false if func.owner_class
|
|
76
|
+
|
|
77
|
+
# Skip functions with rest params (*args) or keyword_rest (**kwargs)
|
|
78
|
+
# The inliner doesn't support rest param array aggregation
|
|
79
|
+
return false if func.params.any? { |p| p.rest || p.keyword_rest }
|
|
80
|
+
|
|
81
|
+
# Skip functions with multiple blocks (contains if/while/etc.)
|
|
82
|
+
# These have complex control flow that can't be simply inlined
|
|
83
|
+
return false if func.body.size > 1
|
|
84
|
+
|
|
85
|
+
# Skip functions with Branch terminators (if statements)
|
|
86
|
+
# The inliner doesn't support control flow structures
|
|
87
|
+
func.body.each do |block|
|
|
88
|
+
return false if block.terminator.is_a?(HIR::Branch)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Skip functions containing block calls (captures need complex transformation)
|
|
92
|
+
# Skip functions containing yield (need KBlock parameter handling)
|
|
93
|
+
# Skip functions containing BeginRescue (exception handling has complex sub-block structure)
|
|
94
|
+
func.body.each do |block|
|
|
95
|
+
block.instructions.each do |inst|
|
|
96
|
+
return false if inst.is_a?(HIR::Call) && inst.block
|
|
97
|
+
return false if inst.is_a?(HIR::Yield)
|
|
98
|
+
return false if inst.is_a?(HIR::BeginRescue)
|
|
99
|
+
return false if inst.is_a?(HIR::CaseStatement)
|
|
100
|
+
return false if inst.is_a?(HIR::CaseMatchStatement)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Count instructions
|
|
105
|
+
instruction_count = func.body.sum { |block| block.instructions.size }
|
|
106
|
+
return false if instruction_count > MAX_INLINE_INSTRUCTIONS
|
|
107
|
+
|
|
108
|
+
# Check for recursion
|
|
109
|
+
return false if recursive?(func.name.to_s, Set.new)
|
|
110
|
+
|
|
111
|
+
true
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def recursive?(func_name, visited)
|
|
115
|
+
return true if visited.include?(func_name)
|
|
116
|
+
visited = visited + [func_name]
|
|
117
|
+
|
|
118
|
+
calls = @call_graph[func_name] || Set.new
|
|
119
|
+
calls.any? { |callee| recursive?(callee, visited) }
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def inline_functions
|
|
123
|
+
# Process each function
|
|
124
|
+
@hir_program.functions.each do |func|
|
|
125
|
+
@current_depth = 0
|
|
126
|
+
inline_in_function(func)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def inline_in_function(func)
|
|
131
|
+
return if @current_depth >= MAX_INLINE_DEPTH
|
|
132
|
+
|
|
133
|
+
changed = true
|
|
134
|
+
while changed
|
|
135
|
+
changed = false
|
|
136
|
+
|
|
137
|
+
func.body.each do |block|
|
|
138
|
+
new_instructions = []
|
|
139
|
+
|
|
140
|
+
block.instructions.each do |inst|
|
|
141
|
+
if inst.is_a?(HIR::Call) && can_inline_call?(inst)
|
|
142
|
+
# Inline this call
|
|
143
|
+
inlined = inline_call(inst, func)
|
|
144
|
+
new_instructions.concat(inlined)
|
|
145
|
+
changed = true
|
|
146
|
+
@inlined_count += 1
|
|
147
|
+
else
|
|
148
|
+
new_instructions << inst
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Replace instructions in block
|
|
153
|
+
block.instance_variable_set(:@instructions, new_instructions)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def can_inline_call?(inst)
|
|
159
|
+
return false unless self_call?(inst)
|
|
160
|
+
|
|
161
|
+
callee_name = inst.method_name.to_s
|
|
162
|
+
@inline_candidates[callee_name] == true
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def inline_call(call_inst, caller_func)
|
|
166
|
+
callee = @functions[call_inst.method_name.to_s]
|
|
167
|
+
return [call_inst] unless callee
|
|
168
|
+
|
|
169
|
+
@current_depth += 1
|
|
170
|
+
|
|
171
|
+
# Create a unique prefix for inlined variables
|
|
172
|
+
prefix = "inline_#{@inlined_count}_"
|
|
173
|
+
|
|
174
|
+
# Map callee parameters to call arguments
|
|
175
|
+
param_map = {}
|
|
176
|
+
callee.params.each_with_index do |param, i|
|
|
177
|
+
if call_inst.args[i]
|
|
178
|
+
param_map[param.name] = call_inst.args[i]
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Clone and transform instructions from callee
|
|
183
|
+
result_instructions = []
|
|
184
|
+
final_value = nil
|
|
185
|
+
|
|
186
|
+
callee.body.each do |block|
|
|
187
|
+
block.instructions.each do |inst|
|
|
188
|
+
cloned = clone_and_rename(inst, prefix, param_map)
|
|
189
|
+
result_instructions << cloned if cloned
|
|
190
|
+
final_value = cloned
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Handle return in terminator
|
|
194
|
+
if block.terminator.is_a?(HIR::Return)
|
|
195
|
+
ret_value = block.terminator.value
|
|
196
|
+
if ret_value
|
|
197
|
+
final_value = transform_value(ret_value, prefix, param_map)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Store result if needed
|
|
203
|
+
if call_inst.result_var && final_value
|
|
204
|
+
store = HIR::StoreLocal.new(
|
|
205
|
+
var: HIR::LocalVar.new(name: call_inst.result_var),
|
|
206
|
+
value: final_value,
|
|
207
|
+
type: call_inst.type
|
|
208
|
+
)
|
|
209
|
+
result_instructions << store
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
@current_depth -= 1
|
|
213
|
+
result_instructions
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def clone_and_rename(inst, prefix, param_map)
|
|
217
|
+
case inst
|
|
218
|
+
when HIR::LoadLocal
|
|
219
|
+
# Check if this is a parameter reference
|
|
220
|
+
if param_map.key?(inst.var.name)
|
|
221
|
+
# Return the argument value directly
|
|
222
|
+
return nil if inst.result_var.nil?
|
|
223
|
+
|
|
224
|
+
# Store the argument value in the result variable
|
|
225
|
+
return HIR::StoreLocal.new(
|
|
226
|
+
var: HIR::LocalVar.new(name: prefix + inst.result_var),
|
|
227
|
+
value: param_map[inst.var.name],
|
|
228
|
+
type: inst.type
|
|
229
|
+
)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Regular local variable
|
|
233
|
+
new_var = HIR::LocalVar.new(
|
|
234
|
+
name: prefix + inst.var.name,
|
|
235
|
+
type: inst.var.type
|
|
236
|
+
)
|
|
237
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
238
|
+
HIR::LoadLocal.new(var: new_var, type: inst.type, result_var: new_result)
|
|
239
|
+
|
|
240
|
+
when HIR::StoreLocal
|
|
241
|
+
new_var = HIR::LocalVar.new(
|
|
242
|
+
name: prefix + inst.var.name,
|
|
243
|
+
type: inst.var.type
|
|
244
|
+
)
|
|
245
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
246
|
+
HIR::StoreLocal.new(var: new_var, value: new_value, type: inst.type)
|
|
247
|
+
|
|
248
|
+
when HIR::Call
|
|
249
|
+
new_receiver = transform_value(inst.receiver, prefix, param_map)
|
|
250
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
251
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
252
|
+
|
|
253
|
+
HIR::Call.new(
|
|
254
|
+
receiver: new_receiver,
|
|
255
|
+
method_name: inst.method_name,
|
|
256
|
+
args: new_args,
|
|
257
|
+
block: inst.block,
|
|
258
|
+
type: inst.type,
|
|
259
|
+
result_var: new_result
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
when HIR::IntegerLit, HIR::FloatLit, HIR::StringLit,
|
|
263
|
+
HIR::SymbolLit, HIR::BoolLit
|
|
264
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
265
|
+
inst.class.new(value: inst.value, result_var: new_result)
|
|
266
|
+
|
|
267
|
+
when HIR::NilLit
|
|
268
|
+
# NilLit doesn't have a value parameter
|
|
269
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
270
|
+
HIR::NilLit.new(result_var: new_result)
|
|
271
|
+
|
|
272
|
+
when HIR::SelfRef
|
|
273
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
274
|
+
HIR::SelfRef.new(type: inst.type, result_var: new_result)
|
|
275
|
+
|
|
276
|
+
when HIR::StringConcat
|
|
277
|
+
# Transform all parts to handle parameter substitution
|
|
278
|
+
new_parts = inst.parts.map { |part| transform_value(part, prefix, param_map) }
|
|
279
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
280
|
+
HIR::StringConcat.new(parts: new_parts, result_var: new_result)
|
|
281
|
+
|
|
282
|
+
when HIR::RangeLit
|
|
283
|
+
new_left = transform_value(inst.left, prefix, param_map)
|
|
284
|
+
new_right = transform_value(inst.right, prefix, param_map)
|
|
285
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
286
|
+
HIR::RangeLit.new(
|
|
287
|
+
left: new_left, right: new_right,
|
|
288
|
+
exclusive: inst.exclusive, type: inst.type, result_var: new_result
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
when HIR::ArrayLit
|
|
292
|
+
new_elements = inst.elements.map { |e| transform_value(e, prefix, param_map) }
|
|
293
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
294
|
+
HIR::ArrayLit.new(
|
|
295
|
+
elements: new_elements, result_var: new_result
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
when HIR::HashLit
|
|
299
|
+
new_pairs = inst.pairs.map { |k, v| [transform_value(k, prefix, param_map), transform_value(v, prefix, param_map)] }
|
|
300
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
301
|
+
HIR::HashLit.new(
|
|
302
|
+
pairs: new_pairs, result_var: new_result
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
when HIR::LoadGlobalVar
|
|
306
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
307
|
+
HIR::LoadGlobalVar.new(name: inst.name, type: inst.type, result_var: new_result)
|
|
308
|
+
|
|
309
|
+
when HIR::StoreGlobalVar
|
|
310
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
311
|
+
HIR::StoreGlobalVar.new(name: inst.name, value: new_value, type: inst.type)
|
|
312
|
+
|
|
313
|
+
when HIR::LoadClassVar
|
|
314
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
315
|
+
HIR::LoadClassVar.new(name: inst.name, type: inst.type, result_var: new_result)
|
|
316
|
+
|
|
317
|
+
when HIR::StoreClassVar
|
|
318
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
319
|
+
HIR::StoreClassVar.new(name: inst.name, value: new_value, type: inst.type)
|
|
320
|
+
|
|
321
|
+
when HIR::RegexpLit
|
|
322
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
323
|
+
HIR::RegexpLit.new(pattern: inst.pattern, options: inst.options, result_var: new_result)
|
|
324
|
+
|
|
325
|
+
when HIR::NativeArrayAlloc
|
|
326
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
327
|
+
new_size = transform_value(inst.size, prefix, param_map)
|
|
328
|
+
HIR::NativeArrayAlloc.new(
|
|
329
|
+
size: new_size,
|
|
330
|
+
element_type: inst.element_type,
|
|
331
|
+
result_var: new_result
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
when HIR::StaticArrayAlloc
|
|
335
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
336
|
+
new_initial = inst.initial_value ? transform_value(inst.initial_value, prefix, param_map) : nil
|
|
337
|
+
HIR::StaticArrayAlloc.new(
|
|
338
|
+
element_type: inst.element_type,
|
|
339
|
+
size: inst.size,
|
|
340
|
+
initial_value: new_initial,
|
|
341
|
+
result_var: new_result
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
when HIR::NativeArrayGet
|
|
345
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
346
|
+
new_array = transform_value(inst.array, prefix, param_map)
|
|
347
|
+
new_index = transform_value(inst.index, prefix, param_map)
|
|
348
|
+
HIR::NativeArrayGet.new(
|
|
349
|
+
array: new_array, index: new_index,
|
|
350
|
+
element_type: inst.element_type, result_var: new_result
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
when HIR::NativeArraySet
|
|
354
|
+
new_array = transform_value(inst.array, prefix, param_map)
|
|
355
|
+
new_index = transform_value(inst.index, prefix, param_map)
|
|
356
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
357
|
+
HIR::NativeArraySet.new(
|
|
358
|
+
array: new_array, index: new_index, value: new_value,
|
|
359
|
+
element_type: inst.element_type
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
when HIR::NativeArrayLength
|
|
363
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
364
|
+
new_array = transform_value(inst.array, prefix, param_map)
|
|
365
|
+
HIR::NativeArrayLength.new(array: new_array, result_var: new_result)
|
|
366
|
+
|
|
367
|
+
when HIR::StaticArrayGet
|
|
368
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
369
|
+
new_array = transform_value(inst.array, prefix, param_map)
|
|
370
|
+
new_index = transform_value(inst.index, prefix, param_map)
|
|
371
|
+
HIR::StaticArrayGet.new(
|
|
372
|
+
array: new_array, index: new_index,
|
|
373
|
+
element_type: inst.element_type, size: inst.size, result_var: new_result
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
when HIR::StaticArraySet
|
|
377
|
+
new_array = transform_value(inst.array, prefix, param_map)
|
|
378
|
+
new_index = transform_value(inst.index, prefix, param_map)
|
|
379
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
380
|
+
HIR::StaticArraySet.new(
|
|
381
|
+
array: new_array, index: new_index, value: new_value,
|
|
382
|
+
element_type: inst.element_type, size: inst.size
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
when HIR::StaticArraySize
|
|
386
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
387
|
+
new_array = transform_value(inst.array, prefix, param_map)
|
|
388
|
+
HIR::StaticArraySize.new(
|
|
389
|
+
array: new_array, size: inst.size, result_var: new_result
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
when HIR::ConstantLookup
|
|
393
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
394
|
+
HIR::ConstantLookup.new(name: inst.name, scope: inst.scope, type: inst.type, result_var: new_result)
|
|
395
|
+
|
|
396
|
+
when HIR::NativeNew
|
|
397
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
398
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
399
|
+
HIR::NativeNew.new(class_type: inst.class_type, result_var: new_result, args: new_args)
|
|
400
|
+
|
|
401
|
+
when HIR::NativeFieldGet
|
|
402
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
403
|
+
new_object = transform_value(inst.object, prefix, param_map)
|
|
404
|
+
HIR::NativeFieldGet.new(object: new_object, field_name: inst.field_name,
|
|
405
|
+
class_type: inst.class_type, result_var: new_result)
|
|
406
|
+
|
|
407
|
+
when HIR::NativeFieldSet
|
|
408
|
+
new_object = transform_value(inst.object, prefix, param_map)
|
|
409
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
410
|
+
HIR::NativeFieldSet.new(object: new_object, field_name: inst.field_name,
|
|
411
|
+
value: new_value, class_type: inst.class_type)
|
|
412
|
+
|
|
413
|
+
when HIR::NativeMethodCall
|
|
414
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
415
|
+
new_receiver = transform_value(inst.receiver, prefix, param_map)
|
|
416
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
417
|
+
HIR::NativeMethodCall.new(
|
|
418
|
+
receiver: new_receiver, method_name: inst.method_name,
|
|
419
|
+
args: new_args, class_type: inst.class_type,
|
|
420
|
+
method_sig: inst.method_sig, owner_class: inst.owner_class,
|
|
421
|
+
result_var: new_result
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
when HIR::LoadInstanceVar
|
|
425
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
426
|
+
HIR::LoadInstanceVar.new(name: inst.name, type: inst.type, result_var: new_result)
|
|
427
|
+
|
|
428
|
+
when HIR::StoreInstanceVar
|
|
429
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
430
|
+
HIR::StoreInstanceVar.new(name: inst.name, value: new_value, type: inst.type)
|
|
431
|
+
|
|
432
|
+
when HIR::StoreConstant
|
|
433
|
+
new_value = transform_value(inst.value, prefix, param_map)
|
|
434
|
+
HIR::StoreConstant.new(name: inst.name, value: new_value, scope: inst.scope, type: inst.type)
|
|
435
|
+
|
|
436
|
+
when HIR::SuperCall
|
|
437
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
438
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
439
|
+
HIR::SuperCall.new(args: new_args, type: inst.type, result_var: new_result)
|
|
440
|
+
|
|
441
|
+
else
|
|
442
|
+
# For other instructions, just return as-is with renamed result
|
|
443
|
+
inst
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def transform_value(value, prefix, param_map)
|
|
448
|
+
case value
|
|
449
|
+
when HIR::LoadLocal
|
|
450
|
+
if param_map.key?(value.var.name)
|
|
451
|
+
param_map[value.var.name]
|
|
452
|
+
else
|
|
453
|
+
new_var = HIR::LocalVar.new(
|
|
454
|
+
name: prefix + value.var.name,
|
|
455
|
+
type: value.var.type
|
|
456
|
+
)
|
|
457
|
+
HIR::LoadLocal.new(var: new_var, type: value.type, result_var: nil)
|
|
458
|
+
end
|
|
459
|
+
when HIR::Instruction
|
|
460
|
+
if value.result_var
|
|
461
|
+
# Create a LoadLocal to reference the renamed variable
|
|
462
|
+
new_var = HIR::LocalVar.new(
|
|
463
|
+
name: prefix + value.result_var,
|
|
464
|
+
type: value.type
|
|
465
|
+
)
|
|
466
|
+
HIR::LoadLocal.new(var: new_var, type: value.type, result_var: nil)
|
|
467
|
+
else
|
|
468
|
+
value
|
|
469
|
+
end
|
|
470
|
+
when String
|
|
471
|
+
# Variable name reference - create a LoadLocal
|
|
472
|
+
var_name = if param_map.key?(value)
|
|
473
|
+
# If it's a parameter, use the argument directly
|
|
474
|
+
return param_map[value]
|
|
475
|
+
else
|
|
476
|
+
prefix + value
|
|
477
|
+
end
|
|
478
|
+
new_var = HIR::LocalVar.new(name: var_name, type: TypeChecker::Types::UNTYPED)
|
|
479
|
+
HIR::LoadLocal.new(var: new_var, type: TypeChecker::Types::UNTYPED, result_var: nil)
|
|
480
|
+
else
|
|
481
|
+
value
|
|
482
|
+
end
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
|
|
7
|
+
module Konpeito
|
|
8
|
+
module Codegen
|
|
9
|
+
# JVM Backend: Orchestrates the compilation pipeline from HIR to .jar
|
|
10
|
+
#
|
|
11
|
+
# Pipeline:
|
|
12
|
+
# HIR → JVMGenerator → JSON IR → ASM tool (subprocess) → .class files → jar → .jar
|
|
13
|
+
class JVMBackend
|
|
14
|
+
ASM_TOOL_DIR = File.expand_path("../../../tools/konpeito-asm", __dir__)
|
|
15
|
+
ASM_TOOL_JAR = File.join(ASM_TOOL_DIR, "konpeito-asm.jar")
|
|
16
|
+
JAVA_HOME = ENV["JAVA_HOME"] || Platform.default_java_home
|
|
17
|
+
|
|
18
|
+
attr_reader :output_file
|
|
19
|
+
|
|
20
|
+
def initialize(jvm_generator, output_file:, module_name:, run_after: false, emit_ir: false, classpath: nil, library: false)
|
|
21
|
+
@jvm_generator = jvm_generator
|
|
22
|
+
@output_file = output_file
|
|
23
|
+
@module_name = module_name
|
|
24
|
+
@run_after = run_after
|
|
25
|
+
@emit_ir = emit_ir
|
|
26
|
+
@classpath = classpath
|
|
27
|
+
@library = library
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate
|
|
31
|
+
ensure_asm_tool!
|
|
32
|
+
|
|
33
|
+
Dir.mktmpdir("konpeito-jvm") do |tmpdir|
|
|
34
|
+
classes_dir = File.join(tmpdir, "classes")
|
|
35
|
+
FileUtils.mkdir_p(classes_dir)
|
|
36
|
+
|
|
37
|
+
# Step 1: Generate JSON IR
|
|
38
|
+
json_ir = @jvm_generator.to_json
|
|
39
|
+
|
|
40
|
+
# Optionally save JSON IR for debugging
|
|
41
|
+
if @emit_ir
|
|
42
|
+
ir_path = @output_file.sub(/\.jar$/, ".json")
|
|
43
|
+
FileUtils.mkdir_p(File.dirname(File.expand_path(ir_path)))
|
|
44
|
+
File.write(ir_path, JSON.pretty_generate(@jvm_generator.to_json_ir))
|
|
45
|
+
puts "JSON IR written to: #{ir_path}"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Step 2: Run ASM tool to generate .class files
|
|
49
|
+
run_asm_tool(json_ir, classes_dir)
|
|
50
|
+
|
|
51
|
+
# Step 2.5: Copy runtime classes (KArray, KHash, etc.)
|
|
52
|
+
copy_runtime_classes(classes_dir)
|
|
53
|
+
|
|
54
|
+
if @library
|
|
55
|
+
# Library mode: no Main-Class manifest
|
|
56
|
+
create_jar_without_manifest(classes_dir)
|
|
57
|
+
else
|
|
58
|
+
# Step 3: Create manifest
|
|
59
|
+
manifest_path = File.join(tmpdir, "MANIFEST.MF")
|
|
60
|
+
main_class = @jvm_generator.send(:main_class_name).gsub("/", ".")
|
|
61
|
+
File.write(manifest_path, "Main-Class: #{main_class}\n")
|
|
62
|
+
|
|
63
|
+
# Step 4: Package into .jar
|
|
64
|
+
create_jar(classes_dir, manifest_path)
|
|
65
|
+
|
|
66
|
+
# Step 5: Optionally run
|
|
67
|
+
run_jar if @run_after
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def ensure_asm_tool!
|
|
75
|
+
unless File.exist?(ASM_TOOL_JAR)
|
|
76
|
+
puts "Building ASM tool (first-time setup)..."
|
|
77
|
+
build_script = File.join(ASM_TOOL_DIR, "build.sh")
|
|
78
|
+
unless File.exist?(build_script)
|
|
79
|
+
raise CodegenError, "ASM tool build script not found: #{build_script}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
output = `bash #{build_script} 2>&1`
|
|
83
|
+
unless $?.success?
|
|
84
|
+
$stderr.puts output
|
|
85
|
+
raise CodegenError, "Failed to build ASM tool"
|
|
86
|
+
end
|
|
87
|
+
puts "ASM tool ready."
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def run_asm_tool(json_ir, output_dir)
|
|
92
|
+
java_cmd = find_java
|
|
93
|
+
|
|
94
|
+
# Run the ASM tool, passing JSON IR via stdin
|
|
95
|
+
cmd = [java_cmd, "-jar", ASM_TOOL_JAR, output_dir]
|
|
96
|
+
|
|
97
|
+
IO.popen(cmd, "r+", err: [:child, :out]) do |io|
|
|
98
|
+
io.write(json_ir)
|
|
99
|
+
io.close_write
|
|
100
|
+
output = io.read
|
|
101
|
+
unless output.strip.empty?
|
|
102
|
+
# Check for errors
|
|
103
|
+
if output.include?("Error") || output.include?("Exception")
|
|
104
|
+
raise CodegenError, "ASM tool error:\n#{output}"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
unless $?.success?
|
|
110
|
+
raise CodegenError, "ASM tool failed with exit code #{$?.exitstatus}"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def create_jar_without_manifest(classes_dir)
|
|
115
|
+
jar_cmd = find_jar
|
|
116
|
+
|
|
117
|
+
FileUtils.mkdir_p(File.dirname(File.expand_path(@output_file)))
|
|
118
|
+
|
|
119
|
+
output_path = File.expand_path(@output_file)
|
|
120
|
+
cmd = [jar_cmd, "cf", output_path, "-C", classes_dir, "."]
|
|
121
|
+
|
|
122
|
+
result = system(*cmd)
|
|
123
|
+
unless result
|
|
124
|
+
raise CodegenError, "jar command failed"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def create_jar(classes_dir, manifest_path)
|
|
129
|
+
jar_cmd = find_jar
|
|
130
|
+
|
|
131
|
+
# Ensure output directory exists
|
|
132
|
+
FileUtils.mkdir_p(File.dirname(File.expand_path(@output_file)))
|
|
133
|
+
|
|
134
|
+
# Create JAR with manifest
|
|
135
|
+
output_path = File.expand_path(@output_file)
|
|
136
|
+
cmd = [jar_cmd, "cfm", output_path, manifest_path, "-C", classes_dir, "."]
|
|
137
|
+
|
|
138
|
+
result = system(*cmd)
|
|
139
|
+
unless result
|
|
140
|
+
raise CodegenError, "jar command failed"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def run_jar
|
|
145
|
+
java_cmd = find_java
|
|
146
|
+
if @classpath
|
|
147
|
+
# Use -cp mode so external JARs are on the classpath
|
|
148
|
+
cp = "#{File.expand_path(@output_file)}#{Platform.classpath_separator}#{@classpath}"
|
|
149
|
+
main_class = @jvm_generator.send(:main_class_name).gsub("/", ".")
|
|
150
|
+
cmd = [java_cmd]
|
|
151
|
+
cmd << "-XstartOnFirstThread" if RUBY_PLATFORM.include?("darwin")
|
|
152
|
+
cmd += ["-cp", cp, main_class]
|
|
153
|
+
puts "Running: #{cmd.join(' ')}"
|
|
154
|
+
system(*cmd)
|
|
155
|
+
else
|
|
156
|
+
puts "Running: java -jar #{@output_file}"
|
|
157
|
+
system(java_cmd, "-jar", @output_file)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def find_java
|
|
162
|
+
# Try JAVA_HOME first, then PATH
|
|
163
|
+
java_path = File.join(JAVA_HOME, "bin", "java")
|
|
164
|
+
return java_path if File.exist?(java_path)
|
|
165
|
+
|
|
166
|
+
# Try PATH
|
|
167
|
+
java_in_path = Platform.find_executable("java")
|
|
168
|
+
return java_in_path if java_in_path
|
|
169
|
+
|
|
170
|
+
raise CodegenError, "Java not found. Install Java 21+: #{Platform.java_install_hint}"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def copy_runtime_classes(classes_dir)
|
|
174
|
+
runtime_dir = File.join(ASM_TOOL_DIR, "runtime-classes")
|
|
175
|
+
return unless File.directory?(runtime_dir)
|
|
176
|
+
|
|
177
|
+
# Copy runtime .class files preserving directory structure
|
|
178
|
+
Dir.glob(File.join(runtime_dir, "**", "*.class")).each do |class_file|
|
|
179
|
+
relative_path = class_file.sub("#{runtime_dir}/", "")
|
|
180
|
+
dest = File.join(classes_dir, relative_path)
|
|
181
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
|
182
|
+
FileUtils.cp(class_file, dest)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def find_jar
|
|
187
|
+
jar_path = File.join(JAVA_HOME, "bin", "jar")
|
|
188
|
+
return jar_path if File.exist?(jar_path)
|
|
189
|
+
|
|
190
|
+
jar_in_path = Platform.find_executable("jar")
|
|
191
|
+
return jar_in_path if jar_in_path
|
|
192
|
+
|
|
193
|
+
raise CodegenError, "jar command not found. Install Java 21+: #{Platform.java_install_hint}"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|