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.
Files changed (180) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/CHANGELOG.md +75 -0
  4. data/CONTRIBUTING.md +123 -0
  5. data/LICENSE +21 -0
  6. data/README.md +257 -0
  7. data/Rakefile +11 -0
  8. data/bin/konpeito +6 -0
  9. data/konpeito.gemspec +43 -0
  10. data/lib/konpeito/ast/typed_ast.rb +620 -0
  11. data/lib/konpeito/ast/visitor.rb +78 -0
  12. data/lib/konpeito/cache/cache_manager.rb +230 -0
  13. data/lib/konpeito/cache/dependency_graph.rb +192 -0
  14. data/lib/konpeito/cache.rb +8 -0
  15. data/lib/konpeito/cli/base_command.rb +187 -0
  16. data/lib/konpeito/cli/build_command.rb +220 -0
  17. data/lib/konpeito/cli/check_command.rb +104 -0
  18. data/lib/konpeito/cli/config.rb +231 -0
  19. data/lib/konpeito/cli/deps_command.rb +128 -0
  20. data/lib/konpeito/cli/doctor_command.rb +340 -0
  21. data/lib/konpeito/cli/fmt_command.rb +199 -0
  22. data/lib/konpeito/cli/init_command.rb +312 -0
  23. data/lib/konpeito/cli/lsp_command.rb +40 -0
  24. data/lib/konpeito/cli/run_command.rb +150 -0
  25. data/lib/konpeito/cli/test_command.rb +248 -0
  26. data/lib/konpeito/cli/watch_command.rb +212 -0
  27. data/lib/konpeito/cli.rb +301 -0
  28. data/lib/konpeito/codegen/builtin_methods.rb +229 -0
  29. data/lib/konpeito/codegen/cruby_backend.rb +1090 -0
  30. data/lib/konpeito/codegen/debug_info.rb +352 -0
  31. data/lib/konpeito/codegen/inliner.rb +486 -0
  32. data/lib/konpeito/codegen/jvm_backend.rb +197 -0
  33. data/lib/konpeito/codegen/jvm_generator.rb +13412 -0
  34. data/lib/konpeito/codegen/llvm_generator.rb +13191 -0
  35. data/lib/konpeito/codegen/loop_optimizer.rb +363 -0
  36. data/lib/konpeito/codegen/monomorphizer.rb +359 -0
  37. data/lib/konpeito/codegen/profile_runtime.c +341 -0
  38. data/lib/konpeito/codegen/profiler.rb +99 -0
  39. data/lib/konpeito/compiler.rb +592 -0
  40. data/lib/konpeito/dependency_resolver.rb +296 -0
  41. data/lib/konpeito/diagnostics/collector.rb +127 -0
  42. data/lib/konpeito/diagnostics/diagnostic.rb +237 -0
  43. data/lib/konpeito/diagnostics/renderer.rb +144 -0
  44. data/lib/konpeito/formatter/formatter.rb +1214 -0
  45. data/lib/konpeito/hir/builder.rb +7167 -0
  46. data/lib/konpeito/hir/nodes.rb +2465 -0
  47. data/lib/konpeito/lsp/document_manager.rb +820 -0
  48. data/lib/konpeito/lsp/server.rb +183 -0
  49. data/lib/konpeito/lsp/transport.rb +38 -0
  50. data/lib/konpeito/parser/prism_adapter.rb +65 -0
  51. data/lib/konpeito/platform.rb +103 -0
  52. data/lib/konpeito/profile/report.rb +136 -0
  53. data/lib/konpeito/rbs_inline/preprocessor.rb +199 -0
  54. data/lib/konpeito/stdlib/compression/compression.rb +72 -0
  55. data/lib/konpeito/stdlib/compression/compression.rbs +60 -0
  56. data/lib/konpeito/stdlib/compression/compression_native.c +415 -0
  57. data/lib/konpeito/stdlib/compression/extconf.rb +19 -0
  58. data/lib/konpeito/stdlib/crypto/crypto.rb +85 -0
  59. data/lib/konpeito/stdlib/crypto/crypto.rbs +74 -0
  60. data/lib/konpeito/stdlib/crypto/crypto_native.c +312 -0
  61. data/lib/konpeito/stdlib/crypto/extconf.rb +40 -0
  62. data/lib/konpeito/stdlib/http/extconf.rb +19 -0
  63. data/lib/konpeito/stdlib/http/http.rb +125 -0
  64. data/lib/konpeito/stdlib/http/http.rbs +57 -0
  65. data/lib/konpeito/stdlib/http/http_native.c +440 -0
  66. data/lib/konpeito/stdlib/json/extconf.rb +17 -0
  67. data/lib/konpeito/stdlib/json/json.rb +44 -0
  68. data/lib/konpeito/stdlib/json/json.rbs +33 -0
  69. data/lib/konpeito/stdlib/json/json_native.c +286 -0
  70. data/lib/konpeito/stdlib/ui/extconf.rb +216 -0
  71. data/lib/konpeito/stdlib/ui/konpeito_ui_native.cpp +1625 -0
  72. data/lib/konpeito/stdlib/ui/konpeito_ui_native.h +162 -0
  73. data/lib/konpeito/stdlib/ui/ui.rb +318 -0
  74. data/lib/konpeito/stdlib/ui/ui.rbs +247 -0
  75. data/lib/konpeito/type_checker/annotation_parser.rb +67 -0
  76. data/lib/konpeito/type_checker/hm_inferrer.rb +2565 -0
  77. data/lib/konpeito/type_checker/inferrer.rb +565 -0
  78. data/lib/konpeito/type_checker/rbs_loader.rb +1621 -0
  79. data/lib/konpeito/type_checker/type_resolver.rb +276 -0
  80. data/lib/konpeito/type_checker/types.rb +1434 -0
  81. data/lib/konpeito/type_checker/unification.rb +323 -0
  82. data/lib/konpeito/ui/animation/animated_state.rb +80 -0
  83. data/lib/konpeito/ui/animation/easing.rb +59 -0
  84. data/lib/konpeito/ui/animation/value_tween.rb +66 -0
  85. data/lib/konpeito/ui/app.rb +379 -0
  86. data/lib/konpeito/ui/box.rb +38 -0
  87. data/lib/konpeito/ui/castella.rb +70 -0
  88. data/lib/konpeito/ui/castella_native.rb +76 -0
  89. data/lib/konpeito/ui/chart/area_chart.rb +305 -0
  90. data/lib/konpeito/ui/chart/bar_chart.rb +288 -0
  91. data/lib/konpeito/ui/chart/base_chart.rb +210 -0
  92. data/lib/konpeito/ui/chart/chart_helpers.rb +79 -0
  93. data/lib/konpeito/ui/chart/gauge_chart.rb +171 -0
  94. data/lib/konpeito/ui/chart/heatmap_chart.rb +222 -0
  95. data/lib/konpeito/ui/chart/line_chart.rb +289 -0
  96. data/lib/konpeito/ui/chart/pie_chart.rb +219 -0
  97. data/lib/konpeito/ui/chart/scales.rb +77 -0
  98. data/lib/konpeito/ui/chart/scatter_chart.rb +303 -0
  99. data/lib/konpeito/ui/chart/stacked_bar_chart.rb +276 -0
  100. data/lib/konpeito/ui/column.rb +271 -0
  101. data/lib/konpeito/ui/core.rb +2199 -0
  102. data/lib/konpeito/ui/dsl.rb +443 -0
  103. data/lib/konpeito/ui/frame.rb +171 -0
  104. data/lib/konpeito/ui/frame_native.rb +494 -0
  105. data/lib/konpeito/ui/markdown/ast.rb +124 -0
  106. data/lib/konpeito/ui/markdown/mermaid/layout.rb +387 -0
  107. data/lib/konpeito/ui/markdown/mermaid/models.rb +232 -0
  108. data/lib/konpeito/ui/markdown/mermaid/parser.rb +519 -0
  109. data/lib/konpeito/ui/markdown/mermaid/renderer.rb +336 -0
  110. data/lib/konpeito/ui/markdown/parser.rb +805 -0
  111. data/lib/konpeito/ui/markdown/renderer.rb +639 -0
  112. data/lib/konpeito/ui/markdown/theme.rb +165 -0
  113. data/lib/konpeito/ui/render_node.rb +260 -0
  114. data/lib/konpeito/ui/row.rb +207 -0
  115. data/lib/konpeito/ui/spacer.rb +18 -0
  116. data/lib/konpeito/ui/style.rb +799 -0
  117. data/lib/konpeito/ui/theme.rb +563 -0
  118. data/lib/konpeito/ui/themes/material.rb +35 -0
  119. data/lib/konpeito/ui/themes/tokyo_night.rb +6 -0
  120. data/lib/konpeito/ui/widgets/button.rb +103 -0
  121. data/lib/konpeito/ui/widgets/calendar.rb +1034 -0
  122. data/lib/konpeito/ui/widgets/checkbox.rb +119 -0
  123. data/lib/konpeito/ui/widgets/container.rb +91 -0
  124. data/lib/konpeito/ui/widgets/data_table.rb +667 -0
  125. data/lib/konpeito/ui/widgets/divider.rb +29 -0
  126. data/lib/konpeito/ui/widgets/image.rb +105 -0
  127. data/lib/konpeito/ui/widgets/input.rb +485 -0
  128. data/lib/konpeito/ui/widgets/markdown.rb +57 -0
  129. data/lib/konpeito/ui/widgets/modal.rb +163 -0
  130. data/lib/konpeito/ui/widgets/multiline_input.rb +968 -0
  131. data/lib/konpeito/ui/widgets/multiline_text.rb +180 -0
  132. data/lib/konpeito/ui/widgets/net_image.rb +100 -0
  133. data/lib/konpeito/ui/widgets/progress_bar.rb +70 -0
  134. data/lib/konpeito/ui/widgets/radio_buttons.rb +93 -0
  135. data/lib/konpeito/ui/widgets/slider.rb +133 -0
  136. data/lib/konpeito/ui/widgets/switch.rb +84 -0
  137. data/lib/konpeito/ui/widgets/tabs.rb +157 -0
  138. data/lib/konpeito/ui/widgets/text.rb +110 -0
  139. data/lib/konpeito/ui/widgets/tree.rb +426 -0
  140. data/lib/konpeito/version.rb +5 -0
  141. data/lib/konpeito.rb +109 -0
  142. data/test_native_array.rb +172 -0
  143. data/test_native_array_class.rb +197 -0
  144. data/test_native_class.rb +151 -0
  145. data/tools/konpeito-asm/build.sh +65 -0
  146. data/tools/konpeito-asm/lib/asm-9.7.1.jar +0 -0
  147. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
  148. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class +0 -0
  149. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
  150. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class +0 -0
  151. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class +0 -0
  152. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class +0 -0
  153. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
  154. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class +0 -0
  155. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class +0 -0
  156. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class +0 -0
  157. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class +0 -0
  158. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class +0 -0
  159. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
  160. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class +0 -0
  161. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class +0 -0
  162. data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
  163. data/tools/konpeito-asm/src/ClassIntrospector.java +312 -0
  164. data/tools/konpeito-asm/src/KonpeitoAssembler.java +659 -0
  165. data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +390 -0
  166. data/tools/konpeito-asm/src/konpeito/runtime/KCompression.java +168 -0
  167. data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +48 -0
  168. data/tools/konpeito-asm/src/konpeito/runtime/KCrypto.java +151 -0
  169. data/tools/konpeito-asm/src/konpeito/runtime/KFile.java +100 -0
  170. data/tools/konpeito-asm/src/konpeito/runtime/KHTTP.java +113 -0
  171. data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +228 -0
  172. data/tools/konpeito-asm/src/konpeito/runtime/KJSON.java +405 -0
  173. data/tools/konpeito-asm/src/konpeito/runtime/KMath.java +54 -0
  174. data/tools/konpeito-asm/src/konpeito/runtime/KRactor.java +244 -0
  175. data/tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java +53 -0
  176. data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +49 -0
  177. data/tools/konpeito-asm/src/konpeito/runtime/KThread.java +49 -0
  178. data/tools/konpeito-asm/src/konpeito/runtime/KTime.java +53 -0
  179. data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +416 -0
  180. metadata +267 -0
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Konpeito
4
+ module Codegen
5
+ # Profiling instrumentation for LLVM IR code generation.
6
+ # Inserts function entry/exit probes that call into C runtime
7
+ # for collecting call counts and execution time.
8
+ class Profiler
9
+ attr_reader :function_ids
10
+
11
+ def initialize(llvm_module, builder)
12
+ @mod = llvm_module
13
+ @builder = builder
14
+ @function_ids = {} # function_name => unique_id
15
+ @next_id = 0
16
+
17
+ declare_runtime_functions
18
+ end
19
+
20
+ # Declare external C runtime functions for profiling
21
+ def declare_runtime_functions
22
+ ptr_type = LLVM::Pointer(LLVM::Int8)
23
+
24
+ # void konpeito_profile_enter(int func_id, const char* func_name)
25
+ @profile_enter = @mod.functions.add(
26
+ "konpeito_profile_enter",
27
+ [LLVM::Int32, ptr_type],
28
+ LLVM.Void
29
+ )
30
+
31
+ # void konpeito_profile_exit(int func_id)
32
+ @profile_exit = @mod.functions.add(
33
+ "konpeito_profile_exit",
34
+ [LLVM::Int32],
35
+ LLVM.Void
36
+ )
37
+
38
+ # void konpeito_profile_init(int num_functions, const char* output_path)
39
+ @profile_init = @mod.functions.add(
40
+ "konpeito_profile_init",
41
+ [LLVM::Int32, ptr_type],
42
+ LLVM.Void
43
+ )
44
+
45
+ # void konpeito_profile_finalize(void)
46
+ @profile_finalize = @mod.functions.add(
47
+ "konpeito_profile_finalize",
48
+ [],
49
+ LLVM.Void
50
+ )
51
+ end
52
+
53
+ # Register a function for profiling and return its ID
54
+ def register_function(name)
55
+ return @function_ids[name] if @function_ids.key?(name)
56
+
57
+ id = @next_id
58
+ @next_id += 1
59
+ @function_ids[name] = id
60
+ id
61
+ end
62
+
63
+ # Insert entry probe at function start
64
+ # Call this right after positioning builder at entry block
65
+ def insert_entry_probe(function_name)
66
+ func_id = register_function(function_name)
67
+
68
+ # Use global_string_pointer to create a string constant and get its pointer
69
+ func_name_ptr = @builder.global_string_pointer(function_name)
70
+
71
+ @builder.call(@profile_enter, LLVM::Int32.from_i(func_id), func_name_ptr)
72
+ end
73
+
74
+ # Insert exit probe before return instruction
75
+ # Call this before generating return instruction
76
+ def insert_exit_probe(function_name)
77
+ func_id = @function_ids[function_name]
78
+ return unless func_id
79
+
80
+ @builder.call(@profile_exit, LLVM::Int32.from_i(func_id))
81
+ end
82
+
83
+ # Generate initialization call (called once at module init)
84
+ def generate_init_call(builder, output_path)
85
+ num_funcs = LLVM::Int32.from_i(@function_ids.size)
86
+
87
+ # Use global_string_pointer to create a string constant and get its pointer
88
+ path_ptr = builder.global_string_pointer(output_path)
89
+
90
+ builder.call(@profile_init, num_funcs, path_ptr)
91
+ end
92
+
93
+ # Get total number of registered functions
94
+ def num_functions
95
+ @function_ids.size
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,592 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbconfig"
4
+
5
+ module Konpeito
6
+ # Compilation statistics collected during a build
7
+ CompileStats = Data.define(
8
+ :resolved_files, # Integer - number of source files resolved
9
+ :rbs_count, # Integer - number of RBS definitions loaded
10
+ :functions, # Integer - number of functions compiled
11
+ :classes, # Integer - number of classes compiled
12
+ :modules, # Integer - number of modules compiled
13
+ :specializations, # Integer - monomorphization specializations generated
14
+ :inlined, # Integer - call sites inlined
15
+ :hoisted, # Integer - loop-invariant instructions hoisted
16
+ :duration_s # Float - total compile time in seconds
17
+ )
18
+
19
+ class Compiler
20
+ attr_reader :source_file, :output_file, :format, :verbose, :rbs_paths, :require_paths, :diagnostics, :debug, :profile, :incremental, :compile_stats
21
+
22
+ def initialize(source_file:, output_file:, format: :cruby_ext, verbose: false, rbs_paths: [], optimize: true, require_paths: [], debug: false, profile: false, incremental: false, clean_cache: false, inline_rbs: false, target: :native, run_after: false, emit_ir: false, classpath: nil, library: false)
23
+ @source_file = source_file
24
+ @format = format
25
+ @verbose = verbose
26
+ @rbs_paths = rbs_paths
27
+ @require_paths = require_paths
28
+ @rbs_loader = nil
29
+ @hm_inferrer = nil
30
+ @optimize = optimize
31
+ @stdlib_requires = []
32
+ @diagnostics = []
33
+ @debug = debug
34
+ @profile = profile
35
+ @incremental = incremental
36
+ @clean_cache = clean_cache
37
+ @cache_manager = nil
38
+ @inline_rbs = inline_rbs
39
+ @target = target
40
+ @run_after = run_after
41
+ @emit_ir = emit_ir
42
+ @classpath = classpath
43
+ @library = library
44
+ @output_file = output_file || default_output_file(target: target)
45
+ @compile_stats = nil
46
+ @_resolved_file_count = 0
47
+ @_specialization_count = 0
48
+ @_inlined_count = 0
49
+ @_hoisted_count = 0
50
+ end
51
+
52
+ def compile
53
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
54
+
55
+ setup_cache if @incremental
56
+
57
+ @parsed_ast = parse
58
+ typed_ast = type_check_ast(@parsed_ast)
59
+ hir = generate_hir(typed_ast)
60
+ hir = optimize_hir(hir) if @optimize
61
+ resolve_types(hir) if @target == :jvm
62
+ generate_code(hir)
63
+
64
+ save_cache if @incremental
65
+
66
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
67
+
68
+ # Collect stats
69
+ rbs_count = @rbs_paths.size
70
+ func_count = hir.functions.size
71
+ class_count = hir.classes.size
72
+ mod_count = hir.respond_to?(:modules) ? hir.modules.size : 0
73
+
74
+ @compile_stats = CompileStats.new(
75
+ resolved_files: @_resolved_file_count,
76
+ rbs_count: rbs_count,
77
+ functions: func_count,
78
+ classes: class_count,
79
+ modules: mod_count,
80
+ specializations: @_specialization_count,
81
+ inlined: @_inlined_count,
82
+ hoisted: @_hoisted_count,
83
+ duration_s: duration
84
+ )
85
+
86
+ output_file
87
+ end
88
+
89
+ def type_check
90
+ t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
91
+
92
+ @parsed_ast = parse
93
+ type_check_ast(@parsed_ast)
94
+
95
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - t0
96
+ @compile_stats = CompileStats.new(
97
+ resolved_files: @_resolved_file_count,
98
+ rbs_count: @rbs_paths.size,
99
+ functions: 0,
100
+ classes: 0,
101
+ modules: 0,
102
+ specializations: 0,
103
+ inlined: 0,
104
+ hoisted: 0,
105
+ duration_s: duration
106
+ )
107
+
108
+ true
109
+ end
110
+
111
+ private
112
+
113
+ def parse
114
+ log "Resolving dependencies for #{source_file}..."
115
+ resolver = DependencyResolver.new(
116
+ base_paths: @require_paths,
117
+ verbose: verbose,
118
+ cache_manager: @cache_manager
119
+ )
120
+ merged_ast, auto_rbs_paths, stdlib_requires, runtime_native_exts = resolver.resolve(source_file)
121
+
122
+ # Merge auto-detected RBS paths (normalize to absolute for dedup)
123
+ @rbs_paths = (@rbs_paths.map { |p| File.expand_path(p) } + auto_rbs_paths).uniq
124
+
125
+ # Store stdlib requires for runtime loading
126
+ @stdlib_requires = stdlib_requires
127
+
128
+ # Store runtime native extensions (excluded from linker flags)
129
+ @runtime_native_extensions = runtime_native_exts || []
130
+
131
+ @_resolved_file_count = resolver.resolved_files.size
132
+ @_resolved_file_paths = resolver.resolved_files.keys
133
+
134
+ if verbose && resolver.resolved_files.size > 1
135
+ log "Resolved #{resolver.resolved_files.size} files:"
136
+ resolver.resolved_files.keys.each { |f| log " - #{f}" }
137
+ end
138
+
139
+ if verbose && !@stdlib_requires.empty?
140
+ log "Stdlib requires (will be loaded at runtime):"
141
+ @stdlib_requires.each { |lib| log " - #{lib}" }
142
+ end
143
+
144
+ merged_ast
145
+ end
146
+
147
+ def rbs_loader
148
+ @rbs_loader ||= begin
149
+ loader = TypeChecker::RBSLoader.new
150
+
151
+ # Process inline RBS from all resolved source files
152
+ # Auto-detect files with "# rbs_inline: enabled" even without --inline flag
153
+ inline_rbs_content = nil
154
+ files_to_scan = @_resolved_file_paths || (File.exist?(source_file) ? [source_file] : [])
155
+ preprocessor = RBSInline::Preprocessor.new
156
+ inline_parts = []
157
+ files_to_scan.each do |path|
158
+ next unless File.exist?(path)
159
+ content = File.read(path)
160
+ # Process if --inline flag is set OR file has rbs_inline marker
161
+ if @inline_rbs || RBSInline::Preprocessor.has_inline_rbs?(content)
162
+ rbs_output = preprocessor.process(content, filename: path)
163
+ inline_parts << rbs_output unless rbs_output.strip.empty?
164
+ end
165
+ end
166
+ unless inline_parts.empty?
167
+ inline_rbs_content = inline_parts.join("\n")
168
+ log "Generated RBS from inline annotations (#{inline_parts.size} files)" if verbose
169
+ end
170
+
171
+ loader.load(
172
+ rbs_paths: rbs_paths,
173
+ stdlib_libraries: @stdlib_requires,
174
+ inline_rbs_content: inline_rbs_content
175
+ )
176
+
177
+ # Auto-register Java:: references from AST before classpath introspection
178
+ if @target == :jvm && @classpath && !@classpath.empty? && @parsed_ast
179
+ # ClassIntrospector requires absolute paths in classpath
180
+ abs_classpath = @classpath.split(Platform.classpath_separator).map { |p| File.expand_path(p) }.join(Platform.classpath_separator)
181
+ java_refs = scan_java_references(@parsed_ast)
182
+ loader.register_java_references(java_refs, abs_classpath) unless java_refs[:refs].empty?
183
+ end
184
+
185
+ if @target == :jvm
186
+ abs_cp = @classpath ? @classpath.split(Platform.classpath_separator).map { |p| File.expand_path(p) }.join(Platform.classpath_separator) : @classpath
187
+ loader.load_classpath_types(abs_cp)
188
+ end
189
+ loader
190
+ end
191
+ end
192
+
193
+ def type_check_ast(ast)
194
+ log "Type checking..."
195
+
196
+ # Read source content for diagnostic messages
197
+ source_content = File.exist?(source_file) ? File.read(source_file) : nil
198
+
199
+ # Build typed AST with HM inference
200
+ builder = AST::TypedASTBuilder.new(
201
+ rbs_loader,
202
+ use_hm: true,
203
+ file_path: source_file,
204
+ source: source_content
205
+ )
206
+ typed_ast = builder.build(ast)
207
+
208
+ # Save HM inferrer for monomorphization
209
+ @hm_inferrer = builder.instance_variable_get(:@hm_inferrer)
210
+
211
+ # Store diagnostics for later display
212
+ @diagnostics = builder.diagnostics
213
+
214
+ # Show inferred types in verbose mode
215
+ if verbose && @hm_inferrer
216
+ show_inferred_types(@hm_inferrer)
217
+ end
218
+
219
+ # Log unresolved type warnings (Kotlin-style validation)
220
+ if verbose && @hm_inferrer && @hm_inferrer.respond_to?(:unresolved_type_warnings)
221
+ warnings = @hm_inferrer.unresolved_type_warnings
222
+ unless warnings.empty?
223
+ log "Unresolved type warnings (#{warnings.size}):"
224
+ warnings.each do |w|
225
+ case w[:kind]
226
+ when :param
227
+ log " #{w[:function]} param[#{w[:index]}]: #{w[:typevar]}"
228
+ when :return
229
+ log " #{w[:function]} return: #{w[:typevar]}"
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ typed_ast
236
+ end
237
+
238
+ def show_inferred_types(hm_inferrer)
239
+ func_types = hm_inferrer.instance_variable_get(:@function_types)
240
+ env = hm_inferrer.instance_variable_get(:@env).first
241
+
242
+ has_output = false
243
+
244
+ unless func_types.empty?
245
+ log "Inferred function types:"
246
+ func_types.each do |name, func_type|
247
+ final_type = hm_inferrer.finalize(func_type)
248
+ log " #{name}: #{final_type}"
249
+ end
250
+ has_output = true
251
+ end
252
+
253
+ # Show inferred variable types (excluding functions)
254
+ var_types = env.reject do |name, scheme|
255
+ hm_inferrer.finalize(scheme.type).is_a?(TypeChecker::FunctionType)
256
+ end
257
+
258
+ unless var_types.empty?
259
+ log "Inferred variable types:" if has_output
260
+ var_types.each do |name, scheme|
261
+ final_type = hm_inferrer.finalize(scheme.type)
262
+ log " #{name}: #{final_type}"
263
+ end
264
+ end
265
+ end
266
+
267
+ def generate_hir(typed_ast)
268
+ log "Generating HIR..."
269
+
270
+ builder = HIR::Builder.new(rbs_loader: @rbs_loader)
271
+ builder.build(typed_ast)
272
+ end
273
+
274
+ def optimize_hir(hir)
275
+ log "Optimizing HIR..."
276
+
277
+ # Apply monomorphization
278
+ if @hm_inferrer
279
+ log " - Monomorphization"
280
+ @monomorphizer = Codegen::Monomorphizer.new(hir, @hm_inferrer)
281
+ @monomorphizer.analyze
282
+ @monomorphizer.transform
283
+
284
+ if verbose && !@monomorphizer.specializations.empty?
285
+ log " Generated specializations:"
286
+ @monomorphizer.specializations.each do |(func, types), name|
287
+ log " #{func}(#{types.join(', ')}) -> #{name}"
288
+ end
289
+ end
290
+
291
+ if verbose && !@monomorphizer.union_dispatches.empty?
292
+ log " Union type dispatches:"
293
+ @monomorphizer.union_dispatches.each do |(_target, _types), info|
294
+ log " #{info[:target]}(#{info[:original_types].map(&:to_s).join(', ')}):"
295
+ info[:specializations].each do |concrete_types, specialized_name|
296
+ log " -> #{specialized_name} for (#{concrete_types.join(', ')})"
297
+ end
298
+ end
299
+ end
300
+ end
301
+
302
+ @_specialization_count = @monomorphizer ? @monomorphizer.specializations.size : 0
303
+
304
+ # Apply inlining
305
+ log " - Inlining"
306
+ inliner = Codegen::Inliner.new(hir)
307
+ inliner.optimize
308
+ @_inlined_count = inliner.inlined_count
309
+
310
+ if verbose && inliner.inlined_count > 0
311
+ log " Inlined #{inliner.inlined_count} call site(s)"
312
+ end
313
+
314
+ # Apply loop optimizations (LICM)
315
+ log " - Loop optimization"
316
+ loop_optimizer = Codegen::LoopOptimizer.new(hir)
317
+ loop_optimizer.optimize
318
+ @_hoisted_count = loop_optimizer.hoisted_count
319
+
320
+ if verbose && loop_optimizer.hoisted_count > 0
321
+ log " Hoisted #{loop_optimizer.hoisted_count} loop-invariant instruction(s)"
322
+ end
323
+
324
+ hir
325
+ end
326
+
327
+ def resolve_types(hir)
328
+ require_relative "type_checker/type_resolver"
329
+
330
+ log "Resolving types..."
331
+
332
+ # Collect JVM interop class info from rbs_loader if available
333
+ jvm_interop_classes = {}
334
+ if @rbs_loader && @rbs_loader.respond_to?(:jvm_classes)
335
+ jvm_interop_classes = @rbs_loader.jvm_classes || {}
336
+ end
337
+
338
+ resolver = TypeChecker::TypeResolver.new(
339
+ hir,
340
+ hm_inferrer: @hm_inferrer,
341
+ rbs_loader: @rbs_loader,
342
+ jvm_interop_classes: jvm_interop_classes,
343
+ monomorphizer: @monomorphizer
344
+ )
345
+
346
+ unless resolver.resolve!
347
+ @type_resolution_errors = resolver.errors
348
+ if @verbose
349
+ resolver.errors.each { |e| puts " #{e}" }
350
+ puts ""
351
+ puts " Type inference could not determine the receiver type."
352
+ puts " These methods may not work correctly if called at runtime."
353
+ puts ""
354
+ end
355
+ end
356
+ end
357
+
358
+ def generate_code(hir)
359
+ if @target == :jvm
360
+ generate_jvm(hir)
361
+ else
362
+ log "Generating #{format} code..."
363
+ case format
364
+ when :cruby_ext
365
+ generate_cruby_extension(hir)
366
+ when :standalone
367
+ generate_standalone(hir)
368
+ else
369
+ raise CodegenError, "Unknown format: #{format}"
370
+ end
371
+ end
372
+ end
373
+
374
+ def generate_cruby_extension(hir)
375
+ log "Generating LLVM IR..."
376
+
377
+ # Detect if HIR uses JSON parse_as
378
+ uses_json_parse_as = hir_uses_json_parse_as?(hir)
379
+ log " - JSON parse_as detected" if uses_json_parse_as && verbose
380
+
381
+ # Generate LLVM IR with monomorphization support
382
+ llvm_gen = Codegen::LLVMGenerator.new(
383
+ module_name: module_name,
384
+ monomorphizer: @monomorphizer,
385
+ rbs_loader: @rbs_loader,
386
+ debug: @debug,
387
+ profile: @profile,
388
+ source_file: source_file
389
+ )
390
+ llvm_gen.generate(hir)
391
+
392
+ log "Compiling to native code..."
393
+
394
+ # Compile to .so/.bundle
395
+ backend = Codegen::CRubyBackend.new(
396
+ llvm_gen,
397
+ output_file: output_file,
398
+ module_name: module_name,
399
+ rbs_loader: @rbs_loader,
400
+ stdlib_requires: @stdlib_requires,
401
+ runtime_native_extensions: @runtime_native_extensions || [],
402
+ debug: @debug,
403
+ profile: @profile,
404
+ uses_json_parse_as: uses_json_parse_as
405
+ )
406
+ backend.generate
407
+
408
+ log "Generated: #{output_file}"
409
+ end
410
+
411
+ def generate_standalone(hir)
412
+ raise CodegenError, "Standalone generation not yet implemented"
413
+ end
414
+
415
+ def generate_jvm(hir)
416
+ require_relative "codegen/jvm_generator"
417
+ require_relative "codegen/jvm_backend"
418
+
419
+ @jvm_gen = Codegen::JVMGenerator.new(
420
+ module_name: module_name,
421
+ monomorphizer: @monomorphizer,
422
+ rbs_loader: @rbs_loader,
423
+ verbose: @verbose
424
+ )
425
+ @jvm_gen.generate(hir)
426
+
427
+ # TypeVar fallbacks in codegen — unresolved types use invokedynamic for
428
+ # runtime method resolution. Report as warning, not error.
429
+ if @jvm_gen.typevar_fallback_count > 0
430
+ warn_msg = "#{@jvm_gen.typevar_fallback_count} unresolved type(s) — using invokedynamic fallback."
431
+ log warn_msg
432
+ end
433
+
434
+ if @jvm_gen.rbs_fallback_count > 0
435
+ log "Type resolution: #{@jvm_gen.rbs_fallback_count} RBS fallbacks (HM inference gaps)"
436
+ end
437
+
438
+ jvm_gen = @jvm_gen
439
+
440
+ backend = Codegen::JVMBackend.new(
441
+ jvm_gen,
442
+ output_file: output_file,
443
+ module_name: module_name,
444
+ run_after: @run_after,
445
+ emit_ir: @emit_ir,
446
+ classpath: @classpath,
447
+ library: @library
448
+ )
449
+ backend.generate
450
+
451
+ log "Generated: #{output_file}"
452
+ end
453
+
454
+ def hir_uses_json_parse_as?(hir)
455
+ hir.functions.any? do |func|
456
+ func.body.any? do |bb|
457
+ bb.instructions.any? do |instr|
458
+ instr.is_a?(HIR::JSONParseAs) || instr.is_a?(HIR::JSONParseArrayAs)
459
+ end
460
+ end
461
+ end
462
+ end
463
+
464
+ def module_name
465
+ @module_name ||= File.basename(source_file, ".rb").gsub(/[^a-zA-Z0-9_]/, "_")
466
+ end
467
+
468
+ def default_output_file(target: :native)
469
+ base = File.basename(source_file, ".rb")
470
+ if target == :jvm
471
+ "#{base}.jar"
472
+ else
473
+ case format
474
+ when :cruby_ext
475
+ "#{base}#{Platform.shared_lib_extension}"
476
+ when :standalone
477
+ base
478
+ else
479
+ base
480
+ end
481
+ end
482
+ end
483
+
484
+ def log(message)
485
+ puts message if verbose
486
+ end
487
+
488
+ # Scan AST for Java:: constant references and constant aliases.
489
+ # Returns { refs: { "Java::X::Y" => "x/Y" }, aliases: { "KCanvas" => "Java::X::Y" } }
490
+ def scan_java_references(ast)
491
+ refs = {}
492
+ aliases = {}
493
+ scan_node_for_java_refs(ast, refs, aliases)
494
+ { refs: refs, aliases: aliases }
495
+ end
496
+
497
+ def scan_node_for_java_refs(node, refs, aliases)
498
+ return unless node
499
+
500
+ case node
501
+ when Prism::ConstantPathNode
502
+ full_name = extract_constant_path_name(node)
503
+ if full_name&.start_with?("Java::")
504
+ refs[full_name] = ruby_path_to_jvm_internal(full_name) unless refs.key?(full_name)
505
+ end
506
+ when Prism::ConstantWriteNode
507
+ # KCanvas = Java::Konpeito::Canvas::Canvas
508
+ value_node = node.value
509
+ if value_node.is_a?(Prism::ConstantPathNode)
510
+ full_name = extract_constant_path_name(value_node)
511
+ if full_name&.start_with?("Java::")
512
+ refs[full_name] = ruby_path_to_jvm_internal(full_name) unless refs.key?(full_name)
513
+ aliases[node.name.to_s] = full_name
514
+ end
515
+ end
516
+ end
517
+
518
+ # Recurse into child nodes
519
+ children = if node.respond_to?(:child_nodes)
520
+ node.child_nodes
521
+ elsif node.respond_to?(:compact_child_nodes)
522
+ node.compact_child_nodes
523
+ else
524
+ []
525
+ end
526
+ children.each { |child| scan_node_for_java_refs(child, refs, aliases) }
527
+ end
528
+
529
+ def extract_constant_path_name(node)
530
+ parts = []
531
+ current = node
532
+ while current.is_a?(Prism::ConstantPathNode)
533
+ parts.unshift(current.name.to_s)
534
+ current = current.parent
535
+ end
536
+ parts.unshift(current.name.to_s) if current.respond_to?(:name)
537
+ parts.join("::")
538
+ end
539
+
540
+ # Convert "Java::Konpeito::Canvas::Canvas" → "konpeito/canvas/Canvas"
541
+ def ruby_path_to_jvm_internal(ruby_path)
542
+ segments = ruby_path.delete_prefix("Java::").split("::")
543
+ segments.each_with_index.map { |s, i|
544
+ i < segments.size - 1 ? s[0].downcase + s[1..] : s
545
+ }.join("/")
546
+ end
547
+
548
+ # Incremental compilation support
549
+
550
+ def setup_cache
551
+ require_relative "cache"
552
+
553
+ @cache_manager = Cache::CacheManager.new
554
+
555
+ if @clean_cache
556
+ log "Clearing compilation cache..."
557
+ @cache_manager.clean!
558
+ end
559
+ end
560
+
561
+ def save_cache
562
+ return unless @cache_manager
563
+
564
+ @cache_manager.save_manifest
565
+ log "Cache saved." if verbose
566
+ end
567
+
568
+ def cache_ast(path, ast)
569
+ return unless @cache_manager
570
+
571
+ @cache_manager.put_ast(path, ast)
572
+ end
573
+
574
+ def get_cached_ast(path)
575
+ return nil unless @cache_manager
576
+
577
+ @cache_manager.get_ast(path)
578
+ end
579
+
580
+ def cache_needs_recompile?(path)
581
+ return true unless @cache_manager
582
+
583
+ @cache_manager.needs_recompile?(path)
584
+ end
585
+
586
+ def register_dependency(from, to)
587
+ return unless @cache_manager
588
+
589
+ @cache_manager.add_dependency(from, to)
590
+ end
591
+ end
592
+ end