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,1621 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rbs"
4
+ require "tmpdir"
5
+ require_relative "annotation_parser"
6
+
7
+ module Konpeito
8
+ module TypeChecker
9
+ # Loads and manages RBS type definitions
10
+ class RBSLoader
11
+ attr_reader :environment, :native_classes, :native_modules, :boxed_classes, :cfunc_methods, :ffi_libraries,
12
+ :extern_classes, :simd_classes, :jvm_classes
13
+
14
+ def initialize
15
+ @environment = nil
16
+ @loaded = false
17
+ @native_classes = {} # class_name -> NativeClassType
18
+ @native_modules = {} # module_name -> NativeModuleType
19
+ @boxed_classes = {} # class_name -> true (explicitly boxed classes)
20
+ @cfunc_methods = {} # "ClassName.method_name" -> CFuncType
21
+ @ffi_libraries = {} # class/module_name -> library_name (e.g., :LibM -> "libm")
22
+ @extern_classes = {} # class_name -> ExternClassType (external C struct wrappers)
23
+ @simd_classes = {} # class_name -> SIMDClassType (SIMD vector classes)
24
+ @jvm_classes = {} # "Java::Util::ArrayList" -> { jvm_internal_name:, methods:, static_methods: }
25
+ @rbs_file_contents = {} # path -> content
26
+ @temp_dirs = [] # Temp directories for single RBS files
27
+ @definition_builder = nil # Cached DefinitionBuilder
28
+ @definition_cache = {} # [type_name, singleton?] -> Definition
29
+ end
30
+
31
+ # Cleanup temp directories created for single RBS file loading
32
+ def cleanup
33
+ @temp_dirs.each do |dir|
34
+ FileUtils.remove_entry(dir) if File.exist?(dir)
35
+ rescue StandardError
36
+ # Ignore cleanup errors
37
+ end
38
+ @temp_dirs.clear
39
+ end
40
+
41
+ # Load RBS definitions from standard library and optional paths
42
+ # @param rbs_paths [Array<String>] User-specified RBS file paths
43
+ # @param stdlib_libraries [Array<String>] Stdlib library names to load (e.g., ["json", "fileutils"])
44
+ # @param inline_rbs_content [String, nil] RBS content generated from inline annotations
45
+ def load(rbs_paths: [], stdlib_libraries: [], inline_rbs_content: nil)
46
+ cleanup # Clean up any temp dirs from previous load
47
+ loader = RBS::EnvironmentLoader.new
48
+
49
+ # Load stdlib libraries first (only if RBS definitions exist)
50
+ stdlib_libraries.each do |lib_name|
51
+ if stdlib_rbs_available?(lib_name)
52
+ loader.add(library: lib_name)
53
+ else
54
+ warn "Warning: No RBS types available for stdlib '#{lib_name}'"
55
+ end
56
+ end
57
+
58
+ # Add user-specified paths and store file contents for annotation parsing
59
+ rbs_paths.each do |path|
60
+ if File.directory?(path)
61
+ loader.add(path: Pathname(path))
62
+ Dir.glob(File.join(path, "**/*.rbs")).each do |file|
63
+ @rbs_file_contents[file] = File.read(file)
64
+ end
65
+ elsif File.file?(path)
66
+ # For single files, create a unique temp directory to avoid loading
67
+ # other RBS files from the same directory (which could cause
68
+ # DuplicatedMethodDefinitionError)
69
+ content = File.read(path)
70
+ @rbs_file_contents[path] = content
71
+
72
+ temp_dir = Dir.mktmpdir("konpeito_rbs_")
73
+ @temp_dirs << temp_dir
74
+ temp_file = File.join(temp_dir, File.basename(path))
75
+ File.write(temp_file, content)
76
+ loader.add(path: Pathname(temp_dir))
77
+ end
78
+ end
79
+
80
+ # Add inline RBS content if provided
81
+ if inline_rbs_content && !inline_rbs_content.strip.empty?
82
+ @rbs_file_contents["(inline)"] = inline_rbs_content
83
+
84
+ temp_dir = Dir.mktmpdir("konpeito_inline_rbs_")
85
+ @temp_dirs << temp_dir
86
+ temp_file = File.join(temp_dir, "inline.rbs")
87
+ File.write(temp_file, inline_rbs_content)
88
+ loader.add(path: Pathname(temp_dir))
89
+ end
90
+
91
+ begin
92
+ @environment = RBS::Environment.from_loader(loader).resolve_type_names
93
+ rescue RBS::EnvironmentLoader::UnknownLibraryError => e
94
+ # If stdlib loading fails, retry without it and emit warning
95
+ warn "Warning: #{e.message}"
96
+ # Create a fresh loader without the problematic stdlib libraries
97
+ loader = RBS::EnvironmentLoader.new
98
+ rbs_paths.each do |path|
99
+ if File.directory?(path)
100
+ loader.add(path: Pathname(path))
101
+ elsif File.file?(path)
102
+ # Re-add temp directory
103
+ temp_dir = @temp_dirs.find { |d| File.exist?(File.join(d, File.basename(path))) }
104
+ loader.add(path: Pathname(temp_dir)) if temp_dir
105
+ end
106
+ end
107
+ @environment = RBS::Environment.from_loader(loader).resolve_type_names
108
+ end
109
+ invalidate_definition_cache!
110
+ @loaded = true
111
+
112
+ # Parse declarations from RBS AST (unified parsing)
113
+ parse_declarations
114
+
115
+ self
116
+ end
117
+
118
+ def loaded?
119
+ @loaded
120
+ end
121
+
122
+ # Check if a class is a NativeClass
123
+ def native_class?(class_name)
124
+ @native_classes.key?(class_name.to_sym)
125
+ end
126
+
127
+ # Get NativeClassType for a class (returns nil if not native)
128
+ def native_class_type(class_name)
129
+ @native_classes[class_name.to_sym]
130
+ end
131
+
132
+ # Check if a module is a NativeModule
133
+ def native_module?(module_name)
134
+ @native_modules.key?(module_name.to_sym)
135
+ end
136
+
137
+ # Get NativeModuleType for a module (returns nil if not native)
138
+ def native_module_type(module_name)
139
+ @native_modules[module_name.to_sym]
140
+ end
141
+
142
+ # Check if a class is explicitly boxed (VALUE-based, for CRuby interop)
143
+ def boxed_class?(class_name)
144
+ @boxed_classes.key?(class_name.to_sym)
145
+ end
146
+
147
+ # Get CFuncType for a method (returns nil if not a cfunc)
148
+ # @param class_name [Symbol, String] The class/module name
149
+ # @param method_name [Symbol, String] The method name
150
+ # @param singleton [Boolean] Whether this is a singleton (class) method
151
+ # @return [Types::CFuncType, nil] The cfunc type if found
152
+ def cfunc_method(class_name, method_name, singleton: false)
153
+ key = singleton ? :"#{class_name}.#{method_name}" : :"#{class_name}##{method_name}"
154
+ @cfunc_methods[key]
155
+ end
156
+
157
+ # Check if a method is a cfunc
158
+ def cfunc_method?(class_name, method_name, singleton: false)
159
+ !cfunc_method(class_name, method_name, singleton: singleton).nil?
160
+ end
161
+
162
+ # Get the FFI library name for a class/module
163
+ # @param name [Symbol, String] The class or module name
164
+ # @return [String, nil] The library name (e.g., "libm") or nil
165
+ def ffi_library(name)
166
+ @ffi_libraries[name.to_sym]
167
+ end
168
+
169
+ # Check if a class/module has an %a{ffi} annotation
170
+ def ffi_library?(name)
171
+ !ffi_library(name).nil?
172
+ end
173
+
174
+ # Get all FFI libraries used (for linking)
175
+ # @return [Array<String>] Unique list of library names
176
+ def all_ffi_libraries
177
+ # Combine FFI libraries from cfunc modules and extern classes
178
+ libs = @ffi_libraries.values.dup
179
+ @extern_classes.each_value { |ext| libs << ext.ffi_library }
180
+ libs.uniq
181
+ end
182
+
183
+ # Check if a class is an extern class (wrapping external C struct)
184
+ def extern_class?(class_name)
185
+ @extern_classes.key?(class_name.to_sym)
186
+ end
187
+
188
+ # Get ExternClassType for a class (returns nil if not extern)
189
+ def extern_class_type(class_name)
190
+ @extern_classes[class_name.to_sym]
191
+ end
192
+
193
+ # Check if a class is a SIMD class (vector operations)
194
+ def simd_class?(class_name)
195
+ @simd_classes.key?(class_name.to_sym)
196
+ end
197
+
198
+ # Get SIMDClassType for a class (returns nil if not simd)
199
+ def simd_class_type(class_name)
200
+ @simd_classes[class_name.to_sym]
201
+ end
202
+
203
+ # Get class definition by name
204
+ def class_definition(name)
205
+ ensure_loaded!
206
+ type_name = parse_type_name(name)
207
+ environment.class_decls[type_name]
208
+ end
209
+
210
+ # Check if a type exists in the environment
211
+ def type_exists?(name)
212
+ ensure_loaded!
213
+ type_name = parse_type_name(name)
214
+ environment.class_decls.key?(type_name)
215
+ end
216
+
217
+ # Parse a type string into an RBS type
218
+ def parse_type(type_string)
219
+ ensure_loaded!
220
+ RBS::Parser.parse_type(type_string)
221
+ end
222
+
223
+ # Get instance methods for a class
224
+ def instance_methods(class_name)
225
+ ensure_loaded!
226
+
227
+ type_name = parse_type_name(class_name)
228
+ decl = environment.class_decls[type_name]
229
+ return [] unless decl
230
+
231
+ definition = build_definition(type_name, singleton: false)
232
+ return [] unless definition
233
+
234
+ definition.methods.keys
235
+ end
236
+
237
+ # Get method type for a class method
238
+ def method_type(class_name, method_name, singleton: false)
239
+ ensure_loaded!
240
+
241
+ type_name = parse_type_name(class_name)
242
+ decl = environment.class_decls[type_name]
243
+ return nil unless decl
244
+
245
+ definition = build_definition(type_name, singleton: singleton)
246
+ return nil unless definition
247
+
248
+ method = definition.methods[method_name.to_sym]
249
+ return nil unless method
250
+
251
+ method.defs.map(&:type)
252
+ end
253
+
254
+ # Directly look up method type from module/class declaration without building full definition.
255
+ # This avoids expensive DefinitionBuilder and is faster for simple lookups.
256
+ # Falls back to the full method_type if not found (to handle inheritance, mix-ins, etc.)
257
+ def method_type_direct(class_name, method_name, singleton: false)
258
+ ensure_loaded!
259
+
260
+ type_name = parse_type_name(class_name)
261
+ decl_entry = environment.class_decls[type_name]
262
+ return method_type(class_name, method_name, singleton: singleton) unless decl_entry
263
+
264
+ # Search in all declarations (handles reopened classes)
265
+ decl_entry.decls.each do |d|
266
+ decl = d.decl
267
+ next unless decl.respond_to?(:members)
268
+
269
+ decl.members.each do |member|
270
+ case member
271
+ when RBS::AST::Members::MethodDefinition
272
+ # Check if this is the method we're looking for
273
+ is_singleton = member.kind == :singleton
274
+ next unless is_singleton == singleton && member.name == method_name.to_sym
275
+
276
+ # Return the method types
277
+ return member.overloads.map(&:method_type)
278
+ end
279
+ end
280
+ end
281
+
282
+ # Fall back to full definition builder (handles inheritance)
283
+ method_type(class_name, method_name, singleton: singleton)
284
+ end
285
+
286
+ # Alias for backward compatibility
287
+ alias direct_method_type method_type_direct
288
+
289
+ # Look up a constant's type
290
+ def constant_type(constant_name)
291
+ ensure_loaded!
292
+
293
+ type_name = parse_type_name(constant_name)
294
+ environment.constant_decls[type_name]&.decl&.type
295
+ end
296
+
297
+ # Look up a global variable's type
298
+ def global_variable_type(name)
299
+ ensure_loaded!
300
+
301
+ environment.global_decls[name.to_sym]&.decl&.type
302
+ end
303
+
304
+ # Get superclass of a class
305
+ def superclass(class_name)
306
+ ensure_loaded!
307
+
308
+ type_name = parse_type_name(class_name)
309
+ decl_entry = environment.class_decls[type_name]
310
+ return nil unless decl_entry
311
+
312
+ # Get the first declaration that has a superclass
313
+ decl_entry.decls.each do |d|
314
+ decl = d.decl
315
+ return decl.super_class.name.name.to_s if decl.respond_to?(:super_class) && decl.super_class
316
+ end
317
+
318
+ nil
319
+ end
320
+
321
+ # Get all instance variables (with types) for a class
322
+ def instance_variables(class_name)
323
+ ensure_loaded!
324
+
325
+ type_name = parse_type_name(class_name)
326
+ decl_entry = environment.class_decls[type_name]
327
+ return {} unless decl_entry
328
+
329
+ ivars = {}
330
+
331
+ decl_entry.decls.each do |d|
332
+ decl = d.decl
333
+ next unless decl.respond_to?(:members)
334
+
335
+ decl.members.each do |member|
336
+ case member
337
+ when RBS::AST::Members::InstanceVariable
338
+ ivars[member.name] = member.type
339
+ end
340
+ end
341
+ end
342
+
343
+ ivars
344
+ end
345
+
346
+ # Get all methods (with types) for a class or module
347
+ def all_methods(class_name, singleton: false)
348
+ ensure_loaded!
349
+
350
+ type_name = parse_type_name(class_name)
351
+ decl = environment.class_decls[type_name]
352
+ return {} unless decl
353
+
354
+ builder = RBS::DefinitionBuilder.new(env: environment)
355
+
356
+ definition = if singleton
357
+ builder.build_singleton(type_name)
358
+ else
359
+ builder.build_instance(type_name)
360
+ end
361
+
362
+ definition.methods.transform_values do |method|
363
+ method.defs.map(&:type)
364
+ end
365
+ end
366
+
367
+ # Get methods defined directly on a class (not inherited)
368
+ def own_methods(class_name, singleton: false)
369
+ ensure_loaded!
370
+
371
+ type_name = parse_type_name(class_name)
372
+ decl_entry = environment.class_decls[type_name]
373
+ return {} unless decl_entry
374
+
375
+ methods = {}
376
+
377
+ decl_entry.decls.each do |d|
378
+ decl = d.decl
379
+ next unless decl.respond_to?(:members)
380
+
381
+ decl.members.each do |member|
382
+ case member
383
+ when RBS::AST::Members::MethodDefinition
384
+ is_singleton = member.kind == :singleton
385
+ next unless is_singleton == singleton
386
+
387
+ methods[member.name] = member.overloads.map(&:method_type)
388
+ end
389
+ end
390
+ end
391
+
392
+ methods
393
+ end
394
+
395
+ # Convert a type to its string representation
396
+ def type_to_string(type)
397
+ type.to_s
398
+ end
399
+
400
+ # Check if one RBS type is a subtype of another
401
+ # @param sub_type [RBS::Types::t] The potential subtype
402
+ # @param super_type [RBS::Types::t] The potential supertype
403
+ # @return [Boolean] true if sub_type is a subtype of super_type
404
+ def subtype?(sub_type, super_type)
405
+ ensure_loaded!
406
+
407
+ # Any (untyped) accepts all types
408
+ return true if super_type.is_a?(RBS::Types::Bases::Any)
409
+
410
+ # Same type
411
+ return true if sub_type == super_type
412
+
413
+ # ClassInstance comparison
414
+ if sub_type.is_a?(RBS::Types::ClassInstance) && super_type.is_a?(RBS::Types::ClassInstance)
415
+ return check_class_subtype(sub_type.name, super_type.name)
416
+ end
417
+
418
+ false
419
+ end
420
+
421
+ private
422
+
423
+ # Check if one class is a subtype of another (based on inheritance)
424
+ def check_class_subtype(sub_name, super_name)
425
+ return true if sub_name == super_name
426
+
427
+ # Get the class declaration
428
+ decl_entry = environment.class_decls[sub_name]
429
+ return false unless decl_entry
430
+
431
+ # Check superclass chain
432
+ decl_entry.decls.each do |d|
433
+ decl = d.decl
434
+ if decl.respond_to?(:super_class) && decl.super_class
435
+ parent_name = decl.super_class.name
436
+ return true if parent_name == super_name
437
+ return true if check_class_subtype(parent_name, super_name)
438
+ end
439
+ end
440
+
441
+ false
442
+ end
443
+
444
+ public
445
+
446
+ # Check if stdlib RBS types are available for a library
447
+ def stdlib_rbs_available?(lib_name)
448
+ # Try to find the library in RBS's built-in paths
449
+ begin
450
+ test_loader = RBS::EnvironmentLoader.new
451
+ test_loader.add(library: lib_name)
452
+ true
453
+ rescue RBS::EnvironmentLoader::UnknownLibraryError
454
+ false
455
+ end
456
+ end
457
+
458
+ def ensure_loaded!
459
+ raise Error, "RBS environment not loaded. Call #load first." unless @loaded
460
+ end
461
+
462
+ # Cached DefinitionBuilder (one per environment)
463
+ def definition_builder
464
+ @definition_builder ||= RBS::DefinitionBuilder.new(env: environment)
465
+ end
466
+
467
+ # Build and cache a class/module definition
468
+ def build_definition(type_name, singleton: false)
469
+ cache_key = [type_name, singleton]
470
+ return @definition_cache[cache_key] if @definition_cache.key?(cache_key)
471
+
472
+ definition = begin
473
+ if singleton
474
+ definition_builder.build_singleton(type_name)
475
+ else
476
+ definition_builder.build_instance(type_name)
477
+ end
478
+ rescue RBS::NoTypeFoundError
479
+ nil
480
+ end
481
+
482
+ @definition_cache[cache_key] = definition
483
+ definition
484
+ end
485
+
486
+ # Invalidate definition caches (call when environment changes)
487
+ def invalidate_definition_cache!
488
+ @definition_builder = nil
489
+ @definition_cache = {}
490
+ end
491
+
492
+ def parse_type_name(name)
493
+ name_str = name.to_s
494
+ if name_str.include?("::")
495
+ # Nested class: "Ractor::Port" → name: :Port, namespace: ::Ractor::
496
+ parts = name_str.split("::")
497
+ short_name = parts.pop.to_sym
498
+ namespace = RBS::Namespace.new(path: parts.map(&:to_sym), absolute: true)
499
+ RBS::TypeName.new(namespace: namespace, name: short_name)
500
+ else
501
+ RBS::TypeName.new(namespace: RBS::Namespace.root, name: name.to_sym)
502
+ end
503
+ end
504
+
505
+ # ============================================================
506
+ # Unified Declaration Parsing (using RBS AST with %a{} annotations)
507
+ # ============================================================
508
+
509
+ # Parse all declarations from RBS file contents
510
+ def parse_declarations
511
+ @rbs_file_contents.each_value do |content|
512
+ parse_declarations_from_content(content)
513
+ end
514
+
515
+ # Validate inheritance (superclasses must exist)
516
+ validate_native_inheritance
517
+ end
518
+
519
+ # Parse declarations from RBS content using RBS AST
520
+ # Recognizes %a{} annotations for:
521
+ # - %a{native} - Native struct class
522
+ # - %a{native: vtable} - Native with dynamic dispatch
523
+ # - %a{extern} - External C struct wrapper (requires %a{ffi})
524
+ # - %a{boxed} - VALUE-based class
525
+ # - %a{struct} - Value type (pass-by-value)
526
+ # - %a{simd} - SIMD vector class
527
+ # - %a{ffi: "lib"} - FFI library for class/module
528
+ # - %a{cfunc: "name"} - C function binding for method
529
+ # - %a{cfunc} - C function (use method name)
530
+ def parse_declarations_from_content(content)
531
+ buffer = RBS::Buffer.new(name: "(inline)", content: content)
532
+ _, _, declarations = RBS::Parser.parse_signature(buffer)
533
+
534
+ declarations.each do |decl|
535
+ case decl
536
+ when RBS::AST::Declarations::Class
537
+ parse_class_declaration(decl)
538
+ when RBS::AST::Declarations::Module
539
+ parse_module_declaration(decl)
540
+ end
541
+ end
542
+ rescue RBS::ParsingError => e
543
+ warn "Warning: RBS parsing error: #{e.message}"
544
+ end
545
+
546
+ # Parse a class declaration
547
+ def parse_class_declaration(decl)
548
+ class_name = decl.name.name
549
+
550
+ # Check for Java:: namespace prefix (JVM interop class)
551
+ full_name = decl.name.to_s.delete_prefix("::")
552
+ if full_name.start_with?("Java::")
553
+ parse_jvm_class(decl, full_name)
554
+ return
555
+ end
556
+
557
+ annotations = AnnotationParser.parse_all(decl.annotations)
558
+
559
+ # Skip built-in types
560
+ if builtin_type?(class_name)
561
+ return
562
+ end
563
+
564
+ # Check for %a{ffi: "lib"} at class level
565
+ ffi_ann = AnnotationParser.find(annotations, :ffi)
566
+ @ffi_libraries[class_name] = ffi_ann[:library] if ffi_ann
567
+
568
+ # Check for %a{boxed}
569
+ if AnnotationParser.has?(annotations, :boxed)
570
+ @boxed_classes[class_name] = true
571
+ return
572
+ end
573
+
574
+ # Check for %a{extern} (requires ffi)
575
+ if AnnotationParser.has?(annotations, :extern)
576
+ parse_extern_class(decl, ffi_ann)
577
+ return
578
+ end
579
+
580
+ # Check for %a{simd}
581
+ if AnnotationParser.has?(annotations, :simd)
582
+ parse_simd_class(decl)
583
+ return
584
+ end
585
+
586
+ # Check for %a{struct} (value type)
587
+ is_struct = AnnotationParser.has?(annotations, :struct)
588
+
589
+ # Check for %a{native} with options
590
+ native_ann = AnnotationParser.find(annotations, :native)
591
+ use_vtable = native_ann&.dig(:vtable) || false
592
+
593
+ # Parse fields and methods
594
+ fields = {}
595
+ methods = {}
596
+ superclass_name = decl.super_class&.name&.name
597
+
598
+ decl.members.each do |member|
599
+ case member
600
+ when RBS::AST::Members::InstanceVariable
601
+ # Strip @ prefix from instance variable name (e.g., :@x -> :x)
602
+ field_name = member.name.to_s.delete_prefix("@").to_sym
603
+ native_type = convert_rbs_type_to_native_field(member.type)
604
+ fields[field_name] = native_type if native_type
605
+ when RBS::AST::Members::MethodDefinition
606
+ # Parse method annotations for cfunc
607
+ method_annotations = AnnotationParser.parse_all(member.annotations)
608
+ cfunc_ann = AnnotationParser.find(method_annotations, :cfunc)
609
+
610
+ if cfunc_ann
611
+ parse_cfunc_method(class_name, member, cfunc_ann)
612
+ else
613
+ # Skip field accessors
614
+ field_base = member.name.to_s.chomp("=").to_sym
615
+ is_accessor = fields.key?(field_base) ||
616
+ (superclass_name && will_inherit_field?(superclass_name, field_base))
617
+ next if is_accessor
618
+ next if member.kind == :singleton # Skip constructors
619
+
620
+ method_type = parse_native_method(member, class_name)
621
+ methods[member.name] = method_type if method_type
622
+ end
623
+ end
624
+ end
625
+
626
+ # Create NativeClassType if has fields or methods
627
+ return if fields.empty? && methods.empty?
628
+
629
+ native_type = Types::NativeClassType.new(
630
+ class_name,
631
+ fields,
632
+ methods,
633
+ superclass: superclass_name,
634
+ vtable: use_vtable,
635
+ is_value_type: is_struct
636
+ )
637
+
638
+ # Validate value type constraints
639
+ if is_struct
640
+ valid, error_msg = native_type.valid_value_type?
641
+ unless valid
642
+ warn "Warning: %a{struct} #{class_name} is invalid: #{error_msg}"
643
+ native_type.is_value_type = false
644
+ end
645
+ end
646
+
647
+ @native_classes[class_name] = native_type
648
+ end
649
+
650
+ # Parse a module declaration
651
+ def parse_module_declaration(decl)
652
+ module_name = decl.name.name
653
+ annotations = AnnotationParser.parse_all(decl.annotations)
654
+
655
+ # Check for %a{ffi: "lib"}
656
+ ffi_ann = AnnotationParser.find(annotations, :ffi)
657
+ @ffi_libraries[module_name] = ffi_ann[:library] if ffi_ann
658
+
659
+ # Check for %a{jvm_static: "..."} module (maps to external Java class)
660
+ jvm_static_ann = AnnotationParser.find(annotations, :jvm_static)
661
+ if jvm_static_ann
662
+ parse_jvm_static_module(decl, jvm_static_ann[:java_class])
663
+ return
664
+ end
665
+
666
+ # Check for %a{native} module
667
+ if AnnotationParser.has?(annotations, :native)
668
+ parse_native_module(decl)
669
+ return
670
+ end
671
+
672
+ # Parse methods for cfunc annotations
673
+ decl.members.each do |member|
674
+ next unless member.is_a?(RBS::AST::Members::MethodDefinition)
675
+
676
+ method_annotations = AnnotationParser.parse_all(member.annotations)
677
+ cfunc_ann = AnnotationParser.find(method_annotations, :cfunc)
678
+ parse_cfunc_method(module_name, member, cfunc_ann) if cfunc_ann
679
+ end
680
+ end
681
+
682
+ # Parse a native module (with %a{native})
683
+ def parse_native_module(decl)
684
+ module_name = decl.name.name
685
+ methods = {}
686
+
687
+ decl.members.each do |member|
688
+ next unless member.is_a?(RBS::AST::Members::MethodDefinition)
689
+
690
+ method_type = parse_native_method(member, module_name)
691
+ methods[member.name] = method_type if method_type
692
+ end
693
+
694
+ return if methods.empty?
695
+
696
+ @native_modules[module_name] = Types::NativeModuleType.new(module_name, methods)
697
+ end
698
+
699
+ # Parse an extern class (with %a{extern})
700
+ def parse_extern_class(decl, ffi_ann)
701
+ class_name = decl.name.name
702
+
703
+ unless ffi_ann
704
+ warn "Warning: %a{extern} class #{class_name} requires %a{ffi} annotation, skipping"
705
+ return
706
+ end
707
+
708
+ extern_methods = {}
709
+
710
+ decl.members.each do |member|
711
+ next unless member.is_a?(RBS::AST::Members::MethodDefinition)
712
+
713
+ overload = member.overloads.first
714
+ next unless overload
715
+
716
+ func_type = overload.method_type.type
717
+ return_type_str = rbs_type_to_string(func_type.return_type)
718
+ param_types = parse_extern_params(func_type)
719
+ return_type = parse_extern_return_type(return_type_str, class_name)
720
+
721
+ is_singleton = member.kind == :singleton
722
+ is_constructor = is_singleton && (return_type == :ptr || return_type_str == class_name.to_s)
723
+
724
+ # Instance methods receive opaque pointer as first param
725
+ unless is_singleton
726
+ param_types = [:ptr] + param_types
727
+ end
728
+
729
+ extern_methods[member.name] = Types::ExternMethodType.new(
730
+ member.name.to_s,
731
+ param_types,
732
+ is_constructor ? :ptr : return_type,
733
+ is_constructor: is_constructor
734
+ )
735
+ end
736
+
737
+ @extern_classes[class_name] = Types::ExternClassType.new(
738
+ class_name,
739
+ ffi_ann[:library],
740
+ extern_methods
741
+ )
742
+ end
743
+
744
+ # Parse a SIMD class (with %a{simd})
745
+ def parse_simd_class(decl)
746
+ class_name = decl.name.name
747
+ field_names = []
748
+ simd_methods = {}
749
+
750
+ decl.members.each do |member|
751
+ case member
752
+ when RBS::AST::Members::InstanceVariable
753
+ # Strip @ prefix from instance variable name
754
+ field_name = member.name.to_s.delete_prefix("@").to_sym
755
+ type_str = rbs_type_to_string(member.type)
756
+ unless type_str == "Float"
757
+ warn "Warning: %a{simd} class #{class_name} field '#{field_name}' must be Float, got #{type_str}"
758
+ next
759
+ end
760
+ field_names << field_name
761
+ when RBS::AST::Members::MethodDefinition
762
+ # Skip field accessors
763
+ field_base = member.name.to_s.chomp("=").to_sym
764
+ next if field_names.include?(field_base)
765
+ next if member.kind == :singleton
766
+
767
+ method_type = parse_simd_method(member, class_name)
768
+ simd_methods[member.name] = method_type if method_type
769
+ end
770
+ end
771
+
772
+ # Validate SIMD field count
773
+ unless Types::SIMDClassType::ALLOWED_WIDTHS.include?(field_names.size)
774
+ warn "Warning: %a{simd} class #{class_name} must have #{Types::SIMDClassType::ALLOWED_WIDTHS.join('/')} Float fields, got #{field_names.size}"
775
+ return
776
+ end
777
+
778
+ @simd_classes[class_name] = Types::SIMDClassType.new(class_name, field_names, simd_methods)
779
+ end
780
+
781
+ # Parse a JVM interop class (Java:: namespace prefix)
782
+ # e.g., class Java::Util::ArrayList → java/util/ArrayList
783
+ def parse_jvm_class(decl, full_name)
784
+ # Convert Java::Util::ArrayList → java/util/ArrayList
785
+ # All segments form the Java class path; package segments are lowercased
786
+ segments = full_name.split("::")
787
+ jvm_segments = segments.each_with_index.map do |seg, i|
788
+ i < segments.size - 1 ? seg[0].downcase + seg[1..] : seg
789
+ end
790
+ jvm_internal_name = jvm_segments.join("/")
791
+
792
+ methods = {}
793
+ static_methods = {}
794
+ constructor_params = nil
795
+
796
+ decl.members.each do |member|
797
+ next unless member.is_a?(RBS::AST::Members::MethodDefinition)
798
+
799
+ overload = member.overloads.first
800
+ next unless overload
801
+
802
+ func_type = overload.method_type.type
803
+ param_types = func_type.required_positionals.map { |p| jvm_rbs_type_to_tag(p.type) }
804
+ return_type = jvm_rbs_type_to_tag(func_type.return_type)
805
+
806
+ method_name = member.name.to_s
807
+
808
+ if member.kind == :singleton
809
+ if method_name == "new"
810
+ # Constructor: store param types for <init> descriptor
811
+ constructor_params = param_types
812
+ # Return type of constructor is the class itself
813
+ return_type = :value
814
+ else
815
+ static_methods[method_name] = { params: param_types, return: return_type }
816
+ end
817
+ else
818
+ # Check if return type is the same class (for method chaining)
819
+ # Use full qualified name from RBS type for Java:: classes
820
+ ret_type_obj = func_type.return_type
821
+ if ret_type_obj.is_a?(RBS::Types::ClassInstance)
822
+ ret_full_name = ret_type_obj.name.to_s
823
+ else
824
+ ret_full_name = rbs_type_to_string(ret_type_obj)
825
+ end
826
+ return_class = (ret_full_name == full_name) ? full_name : nil
827
+
828
+ methods[method_name] = {
829
+ params: param_types,
830
+ return: return_type,
831
+ return_class: return_class
832
+ }
833
+ end
834
+ end
835
+
836
+ @jvm_classes[full_name] = {
837
+ jvm_internal_name: jvm_internal_name,
838
+ methods: methods,
839
+ static_methods: static_methods,
840
+ constructor_params: constructor_params
841
+ }
842
+ end
843
+
844
+ # Parse a module with %a{jvm_static: "java/class/Name"} annotation.
845
+ # Maps a Ruby module to an external Java class's static methods.
846
+ def parse_jvm_static_module(decl, java_class)
847
+ module_name = decl.name.to_s.delete_prefix("::")
848
+ static_methods = {}
849
+
850
+ decl.members.each do |member|
851
+ next unless member.is_a?(RBS::AST::Members::MethodDefinition)
852
+ next unless member.kind == :singleton # def self.xxx only
853
+
854
+ overload = member.overloads.first
855
+ next unless overload
856
+
857
+ func_type = overload.method_type.type
858
+ param_types = func_type.required_positionals.map { |p| jvm_rbs_type_to_tag(p.type) }
859
+ return_type = jvm_rbs_type_to_tag(func_type.return_type)
860
+
861
+ method_name = member.name.to_s
862
+ java_method_name = snake_to_camel(method_name)
863
+
864
+ method_info = { params: param_types, return: return_type, java_name: java_method_name }
865
+
866
+ # Check for %a{callback: "..." descriptor: "..."} annotation on method
867
+ method_annotations = AnnotationParser.parse_all(member.annotations)
868
+ callback_ann = AnnotationParser.find(method_annotations, :callback)
869
+ if callback_ann
870
+ cb_info = { interface: callback_ann[:interface] }
871
+ if callback_ann[:descriptor]
872
+ cb_info[:descriptor] = callback_ann[:descriptor]
873
+ cb_info[:param_types] = parse_callback_descriptor_params(callback_ann[:descriptor])
874
+ cb_info[:return_type] = parse_callback_descriptor_return(callback_ann[:descriptor])
875
+ end
876
+ method_info[:block_callback] = cb_info
877
+ end
878
+
879
+ static_methods[method_name] = method_info
880
+ end
881
+
882
+ @jvm_classes[module_name] = {
883
+ jvm_internal_name: java_class,
884
+ methods: {},
885
+ static_methods: static_methods,
886
+ constructor_params: nil,
887
+ jvm_static_module: true
888
+ }
889
+ end
890
+
891
+ # Convert snake_case to camelCase (e.g., "set_background" -> "setBackground")
892
+ def snake_to_camel(name)
893
+ parts = name.split("_")
894
+ parts[0] + parts[1..].map(&:capitalize).join
895
+ end
896
+
897
+ # Convert camelCase to snake_case (e.g., "setBackground" -> "set_background")
898
+ def camel_to_snake(name)
899
+ name.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
900
+ end
901
+
902
+ # Register Java:: references found by AST pre-scan.
903
+ # Introspects the classpath for the referenced classes and auto-registers them
904
+ # in @jvm_classes so that load_classpath_types can supplement their methods.
905
+ def register_java_references(java_refs, classpath)
906
+ refs = java_refs[:refs]
907
+ aliases = java_refs[:aliases]
908
+
909
+ # Skip classes already registered via RBS
910
+ new_refs = refs.reject { |ruby_name, _| @jvm_classes.key?(ruby_name) }
911
+ return if new_refs.empty?
912
+
913
+ # Introspect the classpath for referenced classes
914
+ target_classes = new_refs.values
915
+ introspect_json = run_introspector(classpath, target_classes)
916
+ return unless introspect_json
917
+
918
+ # Build SAM interface map from all introspected classes
919
+ sam_interfaces = build_sam_interface_map(introspect_json)
920
+
921
+ # Register each class in @jvm_classes
922
+ new_refs.each do |ruby_name, jvm_internal|
923
+ class_data = introspect_json.dig("classes", jvm_internal)
924
+ next unless class_data
925
+
926
+ register_introspected_class(ruby_name, jvm_internal, class_data, sam_interfaces)
927
+ end
928
+
929
+ # Register aliases: KCanvas → same data as Java::Konpeito::Canvas::Canvas
930
+ aliases.each do |alias_name, java_path|
931
+ next if @jvm_classes.key?(alias_name)
932
+
933
+ source = @jvm_classes[java_path]
934
+ @jvm_classes[alias_name] = source.dup if source
935
+ end
936
+ end
937
+
938
+ # Load type information from classpath JAR/class files.
939
+ # Supplements @jvm_classes entries that were registered from RBS but may lack
940
+ # method signatures. RBS-defined methods take priority; missing ones are filled
941
+ # from classpath introspection.
942
+ def load_classpath_types(classpath)
943
+ return unless classpath
944
+
945
+ target_classes = @jvm_classes.values.map { |info| info[:jvm_internal_name] }.compact
946
+ return if target_classes.empty?
947
+
948
+ introspect_json = run_introspector(classpath, target_classes)
949
+ return unless introspect_json
950
+
951
+ merge_introspected_types(introspect_json)
952
+ end
953
+
954
+ # Parse param types from a JVM method descriptor (e.g., "(DD)V" -> [:double, :double])
955
+ def parse_callback_descriptor_params(descriptor)
956
+ return [] unless descriptor =~ /\A\(([^)]*)\)/
957
+
958
+ params_str = ::Regexp.last_match(1)
959
+ types = []
960
+ i = 0
961
+ while i < params_str.length
962
+ case params_str[i]
963
+ when "I" then types << :i64; i += 1
964
+ when "J" then types << :i64; i += 1
965
+ when "D" then types << :double; i += 1
966
+ when "F" then types << :double; i += 1
967
+ when "Z" then types << :i8; i += 1
968
+ when "L"
969
+ # Object type: skip to ;
970
+ semi = params_str.index(";", i)
971
+ class_name = params_str[(i + 1)...semi]
972
+ types << if class_name == "java/lang/String"
973
+ :string
974
+ else
975
+ :value
976
+ end
977
+ i = semi + 1
978
+ else
979
+ types << :value; i += 1
980
+ end
981
+ end
982
+ types
983
+ end
984
+
985
+ # Parse return type from a JVM method descriptor (e.g., "(DD)V" -> :void)
986
+ def parse_callback_descriptor_return(descriptor)
987
+ return :void unless descriptor =~ /\)(.+)\z/
988
+
989
+ ret = ::Regexp.last_match(1)
990
+ case ret
991
+ when "V" then :void
992
+ when "I", "J" then :i64
993
+ when "D", "F" then :double
994
+ when "Z" then :i8
995
+ when "Ljava/lang/String;" then :string
996
+ else :value
997
+ end
998
+ end
999
+
1000
+ # Convert RBS type to JVM interop type tag
1001
+ # Returns a symbol tag or a string for Java class references (e.g., "java/util/ArrayList")
1002
+ def jvm_rbs_type_to_tag(type)
1003
+ # For ClassInstance types, check full qualified name for Java:: prefix
1004
+ if type.is_a?(RBS::Types::ClassInstance)
1005
+ full_name = type.name.to_s
1006
+ if full_name.start_with?("Java::")
1007
+ # Return the JVM internal name as a string tag (e.g., "java/lang/StringBuilder")
1008
+ segments = full_name.split("::")
1009
+ jvm_segs = segments.each_with_index.map { |s, i| i < segments.size - 1 ? s[0].downcase + s[1..] : s }
1010
+ return jvm_segs.join("/")
1011
+ end
1012
+ end
1013
+
1014
+ type_str = rbs_type_to_string(type)
1015
+ case type_str
1016
+ when "Integer" then :i64
1017
+ when "Float" then :double
1018
+ when "String" then :string
1019
+ when "Bool", "bool" then :i8
1020
+ when "void" then :void
1021
+ else :value # Object, untyped, etc.
1022
+ end
1023
+ end
1024
+
1025
+ # Check if a class name is a JVM interop class
1026
+ def jvm_class?(name)
1027
+ @jvm_classes.key?(name.to_s)
1028
+ end
1029
+
1030
+ # Run the KonpeitoAssembler --introspect subprocess to extract type info from classpath.
1031
+ # Returns parsed JSON hash or nil on failure.
1032
+ def run_introspector(classpath, target_classes)
1033
+ asm_jar = File.expand_path("../../../tools/konpeito-asm/konpeito-asm.jar", __dir__)
1034
+ return nil unless File.exist?(asm_jar)
1035
+
1036
+ java_home = ENV["JAVA_HOME"] || Platform.default_java_home
1037
+ java_cmd = File.join(java_home, "bin", "java")
1038
+ unless File.exist?(java_cmd)
1039
+ java_cmd = Platform.find_executable("java") || "java"
1040
+ end
1041
+
1042
+ request = { "classpath" => classpath, "classes" => target_classes }
1043
+ request_json = JSON.generate(request)
1044
+
1045
+ cmd = [java_cmd, "-jar", asm_jar, "--introspect"]
1046
+ output = nil
1047
+ IO.popen(cmd, "r+", err: File::NULL) do |io|
1048
+ io.write(request_json)
1049
+ io.close_write
1050
+ output = io.read
1051
+ end
1052
+
1053
+ return nil unless $?.success? && output && !output.strip.empty?
1054
+
1055
+ JSON.parse(output)
1056
+ rescue StandardError => e
1057
+ $stderr.puts "[DEBUG] run_introspector error: #{e.class}: #{e.message}" if ENV["KONPEITO_DEBUG"]
1058
+ nil
1059
+ end
1060
+
1061
+ # Merge introspected class info into @jvm_classes.
1062
+ # RBS-defined methods take priority; classpath fills in missing ones.
1063
+ def merge_introspected_types(introspect_data)
1064
+ classes_data = introspect_data["classes"]
1065
+ return unless classes_data
1066
+
1067
+ # Build SAM interface map from inner classes:
1068
+ # { "konpeito/canvas/KCanvas$MouseCallback" => { method: "call", descriptor: "(DD)V" } }
1069
+ sam_interfaces = {}
1070
+ classes_data.each_value do |class_info|
1071
+ inner_classes = class_info["inner_classes"] || {}
1072
+ inner_classes.each do |inner_name, inner_info|
1073
+ next unless inner_info["is_interface"]
1074
+
1075
+ abstract_methods = inner_info["abstract_methods"] || {}
1076
+ if abstract_methods.size == 1
1077
+ method_name, method_info = abstract_methods.first
1078
+ sam_interfaces[inner_name] = {
1079
+ method: method_name,
1080
+ descriptor: method_info["descriptor"]
1081
+ }
1082
+ end
1083
+ end
1084
+ end
1085
+
1086
+ # Merge into each @jvm_classes entry
1087
+ @jvm_classes.each do |ruby_name, jvm_info|
1088
+ jvm_internal = jvm_info[:jvm_internal_name]
1089
+ class_data = classes_data[jvm_internal]
1090
+ next unless class_data
1091
+
1092
+ existing_static = jvm_info[:static_methods] || {}
1093
+ introspected_static = class_data["static_methods"] || {}
1094
+
1095
+ introspected_static.each do |java_method_name, method_data|
1096
+ ruby_method_name = camel_to_snake(java_method_name)
1097
+
1098
+ # Skip if already defined in RBS
1099
+ next if existing_static.key?(ruby_method_name)
1100
+
1101
+ descriptor = method_data["descriptor"]
1102
+ param_types = parse_callback_descriptor_params(descriptor)
1103
+ return_type = parse_callback_descriptor_return(descriptor)
1104
+
1105
+ method_info = {
1106
+ params: param_types,
1107
+ return: return_type,
1108
+ java_name: java_method_name
1109
+ }
1110
+
1111
+ # Auto-detect SAM callback parameters:
1112
+ # If a parameter type is a SAM interface, convert to block_callback
1113
+ # and remove the SAM parameter from the param list
1114
+ raw_param_types = parse_descriptor_raw_params(descriptor)
1115
+ sam_param_idx = nil
1116
+ raw_param_types.each_with_index do |raw_type, idx|
1117
+ if raw_type.is_a?(String) && sam_interfaces.key?(raw_type)
1118
+ sam_param_idx = idx
1119
+ break
1120
+ end
1121
+ end
1122
+
1123
+ if sam_param_idx
1124
+ sam_info = sam_interfaces[raw_param_types[sam_param_idx]]
1125
+ method_info[:block_callback] = {
1126
+ interface: raw_param_types[sam_param_idx],
1127
+ descriptor: sam_info[:descriptor],
1128
+ param_types: parse_callback_descriptor_params(sam_info[:descriptor]),
1129
+ return_type: parse_callback_descriptor_return(sam_info[:descriptor])
1130
+ }
1131
+ # Remove the SAM parameter from the param list
1132
+ method_info[:params].delete_at(sam_param_idx)
1133
+ end
1134
+
1135
+ existing_static[ruby_method_name] = method_info
1136
+ end
1137
+
1138
+ jvm_info[:static_methods] = existing_static
1139
+
1140
+ # Also merge instance methods
1141
+ existing_methods = jvm_info[:methods] || {}
1142
+ introspected_methods = class_data["methods"] || {}
1143
+
1144
+ introspected_methods.each do |java_method_name, method_data|
1145
+ ruby_method_name = camel_to_snake(java_method_name)
1146
+ next if existing_methods.key?(ruby_method_name)
1147
+
1148
+ descriptor = method_data["descriptor"]
1149
+ param_types = parse_callback_descriptor_params(descriptor)
1150
+ return_type = parse_callback_descriptor_return(descriptor)
1151
+
1152
+ existing_methods[ruby_method_name] = {
1153
+ params: param_types,
1154
+ return: return_type
1155
+ }
1156
+ end
1157
+
1158
+ jvm_info[:methods] = existing_methods
1159
+
1160
+ # Merge constructor if not already defined
1161
+ if jvm_info[:constructor_params].nil? && class_data["constructor"]
1162
+ ctor_desc = class_data["constructor"]["descriptor"]
1163
+ jvm_info[:constructor_params] = parse_callback_descriptor_params(ctor_desc) if ctor_desc
1164
+ end
1165
+ end
1166
+ end
1167
+
1168
+ # Build SAM (Single Abstract Method) interface map from introspection data.
1169
+ # Returns { "pkg/Class$Interface" => { method: "call", descriptor: "(DD)V" } }
1170
+ def build_sam_interface_map(introspect_data)
1171
+ sam_interfaces = {}
1172
+ classes_data = introspect_data["classes"] || {}
1173
+ classes_data.each_value do |class_info|
1174
+ inner_classes = class_info["inner_classes"] || {}
1175
+ inner_classes.each do |inner_name, inner_info|
1176
+ next unless inner_info["is_interface"]
1177
+
1178
+ abstract_methods = inner_info["abstract_methods"] || {}
1179
+ if abstract_methods.size == 1
1180
+ method_name, method_info = abstract_methods.first
1181
+ sam_interfaces[inner_name] = {
1182
+ method: method_name,
1183
+ descriptor: method_info["descriptor"]
1184
+ }
1185
+ end
1186
+ end
1187
+ end
1188
+ sam_interfaces
1189
+ end
1190
+
1191
+ # Register one introspected class in @jvm_classes (auto-registered without RBS).
1192
+ def register_introspected_class(ruby_name, jvm_internal, class_data, sam_interfaces)
1193
+ static_methods = {}
1194
+ (class_data["static_methods"] || {}).each do |java_name, info|
1195
+ ruby_method = camel_to_snake(java_name)
1196
+ descriptor = info["descriptor"]
1197
+ method_info = {
1198
+ params: parse_callback_descriptor_params(descriptor),
1199
+ return: parse_callback_descriptor_return(descriptor),
1200
+ java_name: java_name
1201
+ }
1202
+ # Auto-detect SAM callback parameters
1203
+ detect_sam_callback!(method_info, descriptor, sam_interfaces)
1204
+ static_methods[ruby_method] = method_info
1205
+ end
1206
+
1207
+ methods = {}
1208
+ (class_data["methods"] || {}).each do |java_name, info|
1209
+ ruby_method = camel_to_snake(java_name)
1210
+ descriptor = info["descriptor"]
1211
+ method_info = {
1212
+ params: parse_callback_descriptor_params(descriptor),
1213
+ return: parse_callback_descriptor_return(descriptor),
1214
+ java_name: java_name
1215
+ }
1216
+ # Auto-detect SAM callback on instance methods too
1217
+ detect_sam_callback!(method_info, descriptor, sam_interfaces)
1218
+ methods[ruby_method] = method_info
1219
+ end
1220
+
1221
+ constructor_params = nil
1222
+ if class_data["constructor"]
1223
+ ctor_desc = class_data["constructor"]["descriptor"]
1224
+ constructor_params = parse_callback_descriptor_params(ctor_desc) if ctor_desc
1225
+ end
1226
+
1227
+ @jvm_classes[ruby_name] = {
1228
+ jvm_internal_name: jvm_internal,
1229
+ methods: methods,
1230
+ static_methods: static_methods,
1231
+ constructor_params: constructor_params,
1232
+ auto_registered: true
1233
+ }
1234
+ end
1235
+
1236
+ # Detect SAM callback parameters in a method and update method_info in-place.
1237
+ def detect_sam_callback!(method_info, descriptor, sam_interfaces)
1238
+ raw_param_types = parse_descriptor_raw_params(descriptor)
1239
+ sam_param_idx = nil
1240
+ raw_param_types.each_with_index do |raw_type, idx|
1241
+ if raw_type.is_a?(String) && sam_interfaces.key?(raw_type)
1242
+ sam_param_idx = idx
1243
+ break
1244
+ end
1245
+ end
1246
+
1247
+ return unless sam_param_idx
1248
+
1249
+ sam_info = sam_interfaces[raw_param_types[sam_param_idx]]
1250
+ method_info[:block_callback] = {
1251
+ interface: raw_param_types[sam_param_idx],
1252
+ descriptor: sam_info[:descriptor],
1253
+ param_types: parse_callback_descriptor_params(sam_info[:descriptor]),
1254
+ return_type: parse_callback_descriptor_return(sam_info[:descriptor])
1255
+ }
1256
+ # Remove the SAM parameter from the param list
1257
+ method_info[:params].delete_at(sam_param_idx)
1258
+ end
1259
+
1260
+ # Parse raw parameter types from descriptor, returning class names as strings
1261
+ # for L...; types (needed for SAM interface detection).
1262
+ # e.g., "(Lkonpeito/canvas/KCanvas$MouseCallback;)V" -> ["konpeito/canvas/KCanvas$MouseCallback"]
1263
+ def parse_descriptor_raw_params(descriptor)
1264
+ return [] unless descriptor =~ /\A\(([^)]*)\)/
1265
+
1266
+ params_str = ::Regexp.last_match(1)
1267
+ types = []
1268
+ i = 0
1269
+ while i < params_str.length
1270
+ case params_str[i]
1271
+ when "I", "J", "D", "F", "Z", "B", "C", "S"
1272
+ types << params_str[i]
1273
+ i += 1
1274
+ when "L"
1275
+ semi = params_str.index(";", i)
1276
+ class_name = params_str[(i + 1)...semi]
1277
+ types << class_name
1278
+ i = semi + 1
1279
+ when "["
1280
+ # Array type: skip dimension prefix
1281
+ types << :array
1282
+ i += 1
1283
+ # Skip element type
1284
+ if params_str[i] == "L"
1285
+ semi = params_str.index(";", i)
1286
+ i = semi + 1
1287
+ else
1288
+ i += 1
1289
+ end
1290
+ else
1291
+ types << :unknown
1292
+ i += 1
1293
+ end
1294
+ end
1295
+ types
1296
+ end
1297
+
1298
+ # Parse a cfunc method (with %a{cfunc} or %a{cfunc: "name"})
1299
+ def parse_cfunc_method(context_name, member, cfunc_ann)
1300
+ overload = member.overloads.first
1301
+ return unless overload
1302
+
1303
+ func_type = overload.method_type.type
1304
+ c_func_name = cfunc_ann[:c_name] || member.name.to_s
1305
+
1306
+ param_types = func_type.required_positionals.map do |param|
1307
+ parse_cfunc_type(rbs_type_to_string(param.type))
1308
+ end
1309
+
1310
+ return_type = parse_cfunc_type(rbs_type_to_string(func_type.return_type))
1311
+
1312
+ cfunc_type = Types::CFuncType.new(c_func_name, param_types, return_type)
1313
+ singleton = member.kind == :singleton
1314
+ key = singleton ? :"#{context_name}.#{member.name}" : :"#{context_name}##{member.name}"
1315
+ @cfunc_methods[key] = cfunc_type
1316
+ end
1317
+
1318
+ # Parse a native method from RBS AST
1319
+ def parse_native_method(member, class_name)
1320
+ overload = member.overloads.first
1321
+ return nil unless overload
1322
+
1323
+ func_type = overload.method_type.type
1324
+ param_types = []
1325
+ param_names = []
1326
+
1327
+ func_type.required_positionals.each do |param|
1328
+ param_types << convert_to_native_return_type(rbs_type_to_string(param.type), class_name)
1329
+ param_names << (param.name || :"arg#{param_names.size}")
1330
+ end
1331
+
1332
+ return_type = convert_to_native_return_type(rbs_type_to_string(func_type.return_type), class_name)
1333
+
1334
+ Types::NativeMethodType.new(param_types, return_type, param_names: param_names)
1335
+ end
1336
+
1337
+ # Parse a SIMD method from RBS AST
1338
+ def parse_simd_method(member, class_name)
1339
+ overload = member.overloads.first
1340
+ return nil unless overload
1341
+
1342
+ func_type = overload.method_type.type
1343
+ param_types = []
1344
+ param_names = []
1345
+
1346
+ func_type.required_positionals.each do |param|
1347
+ type_str = rbs_type_to_string(param.type)
1348
+ param_types << parse_simd_type(type_str, class_name)
1349
+ param_names << (param.name || :"arg#{param_names.size}")
1350
+ end
1351
+
1352
+ return_type = parse_simd_type(rbs_type_to_string(func_type.return_type), class_name)
1353
+
1354
+ Types::NativeMethodType.new(param_types, return_type, param_names: param_names)
1355
+ end
1356
+
1357
+ # Parse extern method parameters
1358
+ def parse_extern_params(func_type)
1359
+ func_type.required_positionals.map do |param|
1360
+ parse_extern_type(rbs_type_to_string(param.type))
1361
+ end
1362
+ end
1363
+
1364
+ # ============================================================
1365
+ # Type Conversion Helpers
1366
+ # ============================================================
1367
+
1368
+ # Convert RBS type to string representation
1369
+ def rbs_type_to_string(type)
1370
+ case type
1371
+ when RBS::Types::ClassInstance
1372
+ base_name = type.name.name.to_s
1373
+ if type.args.empty?
1374
+ base_name
1375
+ else
1376
+ # Handle generic types: NativeHash[String, Integer] -> "NativeHash[String, Integer]"
1377
+ args_str = type.args.map { |a| rbs_type_to_string(a) }.join(", ")
1378
+ "#{base_name}[#{args_str}]"
1379
+ end
1380
+ when RBS::Types::Bases::Bool
1381
+ "Bool"
1382
+ when RBS::Types::Bases::Void
1383
+ "void"
1384
+ when RBS::Types::Bases::Nil
1385
+ "nil"
1386
+ when RBS::Types::Optional
1387
+ rbs_type_to_string(type.type) + "?"
1388
+ when RBS::Types::Literal
1389
+ # Handle literal types (e.g., 4 in StaticArray[Float, 4])
1390
+ type.literal.to_s
1391
+ else
1392
+ type.to_s
1393
+ end
1394
+ end
1395
+
1396
+ # Convert RBS type to native field type
1397
+ def convert_rbs_type_to_native_field(type)
1398
+ type_str = rbs_type_to_string(type)
1399
+ is_optional = type.is_a?(RBS::Types::Optional)
1400
+ base_type_str = type_str.chomp("?")
1401
+
1402
+ convert_to_native_field_type(base_type_str, is_optional)
1403
+ end
1404
+
1405
+ # Convert RBS type string to native field type
1406
+ def convert_to_native_field_type(type_str, is_optional = false)
1407
+ case type_str
1408
+ when "Float", "Float64" then :Float64
1409
+ when "Integer", "Int64" then :Int64
1410
+ when "Bool", "bool" then :Bool
1411
+ when "String" then :String
1412
+ when "Array" then :Array
1413
+ when "Hash" then :Hash
1414
+ when "Object", "untyped" then :Object
1415
+ else
1416
+ # Could be a NativeClass type
1417
+ if is_optional
1418
+ # Optional NativeClass = reference (stored as VALUE), can be nil
1419
+ { ref: type_str.to_sym }
1420
+ else
1421
+ # Non-optional = embedded (stored as struct)
1422
+ type_str.to_sym
1423
+ end
1424
+ end
1425
+ end
1426
+
1427
+ # Convert RBS type string to native method return/param type
1428
+ def convert_to_native_return_type(type_str, class_name)
1429
+ case type_str
1430
+ when "Float", "Float64" then :Float64
1431
+ when "Integer", "Int64" then :Int64
1432
+ when "void", "Void", "nil" then :Void
1433
+ when class_name.to_s then :Self
1434
+ else
1435
+ type_str.to_sym
1436
+ end
1437
+ end
1438
+
1439
+ # Convert type string to cfunc type symbol
1440
+ def parse_cfunc_type(type_str)
1441
+ case type_str.strip
1442
+ when "Float" then :Float
1443
+ when "Integer" then :Integer
1444
+ when "String" then :String
1445
+ when "Bool", "bool" then :Bool
1446
+ when "void", "nil" then :void
1447
+ else type_str.to_sym
1448
+ end
1449
+ end
1450
+
1451
+ # Convert type string to extern type symbol
1452
+ def parse_extern_type(type_str)
1453
+ case type_str.strip
1454
+ when "Float" then :Float
1455
+ when "Integer" then :Integer
1456
+ when "String" then :String
1457
+ when "Bool", "bool" then :Bool
1458
+ when "Array" then :Array
1459
+ when "Hash" then :Hash
1460
+ when "void", "nil" then :void
1461
+ else :ptr # Unknown types are treated as opaque pointers
1462
+ end
1463
+ end
1464
+
1465
+ # Parse extern class return type
1466
+ def parse_extern_return_type(type_str, class_name)
1467
+ case type_str
1468
+ when class_name.to_s then :ptr # Return Self = return opaque pointer
1469
+ when "void", "nil" then :void
1470
+ else parse_extern_type(type_str)
1471
+ end
1472
+ end
1473
+
1474
+ # Convert type string to SIMD type symbol
1475
+ def parse_simd_type(type_str, class_name)
1476
+ case type_str
1477
+ when class_name.to_s then :Self
1478
+ when "Float", "Float64" then :Float64
1479
+ when "void", "nil" then :Void
1480
+ else type_str.to_sym
1481
+ end
1482
+ end
1483
+
1484
+ # Check if class is a built-in type that should be skipped
1485
+ def builtin_type?(class_name)
1486
+ name_str = class_name.to_s
1487
+ # Extract base name (before '[' if generic)
1488
+ base_name = name_str.split("[").first
1489
+
1490
+ # Check for generic built-in types
1491
+ %w[NativeArray NativeHash StaticArray Slice ByteBuffer ByteSlice StringBuffer NativeString].include?(base_name) ||
1492
+ # Legacy class name encoding patterns (backward compatibility)
1493
+ name_str.match?(/\AStaticArray\d+(Float|Int)\z/) ||
1494
+ name_str.match?(/\ASlice(Int64|Float64)\z/) ||
1495
+ name_str.match?(/\ANativeHash(String|Symbol|Integer)(Integer|Float|Bool|String|Object|Array|Hash|\w+)\z/)
1496
+ end
1497
+
1498
+ # Parse generic native type from RBS ClassInstance
1499
+ # Returns hash with type info, or nil if not a generic native type
1500
+ def parse_generic_native_type(type)
1501
+ return nil unless type.is_a?(RBS::Types::ClassInstance)
1502
+ return nil if type.args.empty?
1503
+
1504
+ base_name = type.name.name.to_s
1505
+ args = type.args
1506
+
1507
+ case base_name
1508
+ when "NativeHash"
1509
+ return nil unless args.size == 2
1510
+ {
1511
+ type: :native_hash,
1512
+ key_type: resolve_generic_type_arg(args[0]),
1513
+ value_type: resolve_generic_type_arg(args[1])
1514
+ }
1515
+ when "NativeArray"
1516
+ return nil unless args.size == 1
1517
+ {
1518
+ type: :native_array,
1519
+ element_type: resolve_generic_type_arg(args[0])
1520
+ }
1521
+ when "StaticArray"
1522
+ return nil unless args.size == 2
1523
+ {
1524
+ type: :static_array,
1525
+ element_type: resolve_generic_type_arg(args[0]),
1526
+ size: resolve_literal_arg(args[1])
1527
+ }
1528
+ when "Slice"
1529
+ return nil unless args.size == 1
1530
+ {
1531
+ type: :slice,
1532
+ element_type: resolve_generic_type_arg(args[0])
1533
+ }
1534
+ else
1535
+ nil
1536
+ end
1537
+ end
1538
+
1539
+ # Resolve a generic type argument to internal type symbol
1540
+ def resolve_generic_type_arg(arg)
1541
+ case arg
1542
+ when RBS::Types::ClassInstance
1543
+ name = arg.name.name.to_s
1544
+ case name
1545
+ when "Integer", "Int64" then :Integer
1546
+ when "Float", "Float64" then :Float
1547
+ when "Bool", "bool" then :Bool
1548
+ when "String" then :String
1549
+ when "Symbol" then :Symbol
1550
+ when "Array" then :Array
1551
+ when "Hash" then :Hash
1552
+ when "Object" then :Object
1553
+ else
1554
+ # Could be a NativeClass type
1555
+ name.to_sym
1556
+ end
1557
+ when RBS::Types::Bases::Bool
1558
+ :Bool
1559
+ else
1560
+ arg.to_s.to_sym
1561
+ end
1562
+ end
1563
+
1564
+ # Resolve a literal argument (for StaticArray size)
1565
+ def resolve_literal_arg(arg)
1566
+ case arg
1567
+ when RBS::Types::Literal
1568
+ arg.literal
1569
+ when RBS::Types::ClassInstance
1570
+ # Might be a constant reference, try to parse as integer
1571
+ arg.name.name.to_s.to_i
1572
+ else
1573
+ arg.to_s.to_i
1574
+ end
1575
+ end
1576
+
1577
+ # Check if a superclass has a field (for accessor detection)
1578
+ def will_inherit_field?(superclass_name, field_name)
1579
+ parent = @native_classes[superclass_name]
1580
+ return false unless parent
1581
+ return true if parent.fields.key?(field_name)
1582
+ return false unless parent.superclass
1583
+
1584
+ will_inherit_field?(parent.superclass, field_name)
1585
+ end
1586
+
1587
+ # Validate that all superclasses and embedded types exist
1588
+ def validate_native_inheritance
1589
+ @native_classes.each do |class_name, class_type|
1590
+ # Set registry for embedded type resolution
1591
+ class_type.native_class_registry = @native_classes
1592
+
1593
+ # Validate superclass
1594
+ if class_type.superclass && !@native_classes.key?(class_type.superclass)
1595
+ raise Error, "NativeClass #{class_name} inherits from unknown class #{class_type.superclass}"
1596
+ end
1597
+
1598
+ # Validate embedded and reference NativeClass field types
1599
+ class_type.fields.each do |field_name, field_type|
1600
+ next if Types::NativeClassType::ALLOWED_PRIMITIVE_TYPES.include?(field_type)
1601
+ next if Types::NativeClassType::RUBY_OBJECT_TYPES.include?(field_type)
1602
+
1603
+ # Check for reference types (Hash with :ref key)
1604
+ if field_type.is_a?(Hash) && field_type[:ref]
1605
+ ref_class = field_type[:ref]
1606
+ unless @native_classes.key?(ref_class)
1607
+ raise Error, "NativeClass #{class_name} has field '#{field_name}' referencing unknown type #{ref_class}"
1608
+ end
1609
+ next
1610
+ end
1611
+
1612
+ # Embedded NativeClass
1613
+ unless @native_classes.key?(field_type)
1614
+ raise Error, "NativeClass #{class_name} has field '#{field_name}' with unknown type #{field_type}"
1615
+ end
1616
+ end
1617
+ end
1618
+ end
1619
+ end
1620
+ end
1621
+ end