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,659 @@
1
+ package com.konpeito.asm;
2
+
3
+ import org.objectweb.asm.*;
4
+ import java.io.*;
5
+ import java.lang.invoke.*;
6
+ import java.nio.file.*;
7
+ import java.util.*;
8
+
9
+ /**
10
+ * KonpeitoAssembler: Reads JSON IR from stdin, generates .class files using ASM.
11
+ *
12
+ * Usage: java -cp konpeito-asm.jar com.konpeito.asm.KonpeitoAssembler <output_dir>
13
+ *
14
+ * JSON IR is read from stdin. Each class definition produces a .class file in output_dir.
15
+ */
16
+ public class KonpeitoAssembler {
17
+
18
+ public static void main(String[] args) throws Exception {
19
+ if (args.length >= 1 && args[0].equals("--introspect")) {
20
+ ClassIntrospector.run();
21
+ return;
22
+ }
23
+
24
+ if (args.length < 1) {
25
+ System.err.println("Usage: KonpeitoAssembler <output_dir>");
26
+ System.err.println(" KonpeitoAssembler --introspect");
27
+ System.exit(1);
28
+ }
29
+
30
+ String outputDir = args[0];
31
+ Files.createDirectories(Path.of(outputDir));
32
+
33
+ // Read JSON from stdin
34
+ String json;
35
+ try (var reader = new BufferedReader(new InputStreamReader(System.in))) {
36
+ var sb = new StringBuilder();
37
+ String line;
38
+ while ((line = reader.readLine()) != null) {
39
+ sb.append(line).append('\n');
40
+ }
41
+ json = sb.toString();
42
+ }
43
+
44
+ // Parse and generate
45
+ var assembler = new KonpeitoAssembler();
46
+ assembler.processJson(json, outputDir);
47
+ }
48
+
49
+ // Class hierarchy map: className -> superClassName (for COMPUTE_FRAMES resolution)
50
+ private final Map<String, String> classHierarchy = new HashMap<>();
51
+ // Interface set: classNames that are interfaces
52
+ private final Set<String> interfaceSet = new HashSet<>();
53
+
54
+ public void processJson(String json, String outputDir) throws Exception {
55
+ var parser = new JsonParser(json);
56
+ var root = parser.parseObject();
57
+
58
+ @SuppressWarnings("unchecked")
59
+ var classes = (List<Map<String, Object>>) root.get("classes");
60
+ if (classes == null) {
61
+ System.err.println("Error: no 'classes' key in JSON IR");
62
+ System.exit(1);
63
+ }
64
+
65
+ // Build class hierarchy map before generating any classes
66
+ for (var classDef : classes) {
67
+ String name = (String) classDef.get("name");
68
+ String superName = (String) classDef.getOrDefault("superName", "java/lang/Object");
69
+ classHierarchy.put(name, superName);
70
+ var access = (List<?>) classDef.get("access");
71
+ if (access != null && access.contains("interface")) {
72
+ interfaceSet.add(name);
73
+ }
74
+ }
75
+
76
+ for (var classDef : classes) {
77
+ generateClass(classDef, outputDir);
78
+ }
79
+ }
80
+
81
+ private void generateClass(Map<String, Object> classDef, String outputDir) throws Exception {
82
+ String className = (String) classDef.get("name");
83
+ String superName = (String) classDef.getOrDefault("superName", "java/lang/Object");
84
+ int accessFlags = parseAccessFlags((List<?>) classDef.get("access"));
85
+
86
+ @SuppressWarnings("unchecked")
87
+ var interfaces = (List<String>) classDef.getOrDefault("interfaces", List.of());
88
+
89
+ // Use custom ClassWriter that knows about our generated class hierarchy
90
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
91
+ @Override
92
+ protected String getCommonSuperClass(String type1, String type2) {
93
+ // If both are in our hierarchy, walk up to find common ancestor
94
+ if (classHierarchy.containsKey(type1) || classHierarchy.containsKey(type2)) {
95
+ // Collect all ancestors of type1
96
+ Set<String> ancestors1 = new HashSet<>();
97
+ String t = type1;
98
+ while (t != null && !t.equals("java/lang/Object")) {
99
+ ancestors1.add(t);
100
+ t = classHierarchy.getOrDefault(t, "java/lang/Object");
101
+ if (ancestors1.contains(t)) break; // prevent infinite loop
102
+ }
103
+ ancestors1.add("java/lang/Object");
104
+
105
+ // Walk up type2's chain until we find a common ancestor
106
+ t = type2;
107
+ while (t != null) {
108
+ if (ancestors1.contains(t)) return t;
109
+ if (t.equals("java/lang/Object")) break;
110
+ t = classHierarchy.getOrDefault(t, "java/lang/Object");
111
+ }
112
+ return "java/lang/Object";
113
+ }
114
+ // Fall back to default for standard library classes
115
+ try {
116
+ return super.getCommonSuperClass(type1, type2);
117
+ } catch (Exception e) {
118
+ return "java/lang/Object";
119
+ }
120
+ }
121
+ };
122
+ cw.visit(Opcodes.V21, accessFlags, className, null, superName,
123
+ interfaces.isEmpty() ? null : interfaces.toArray(new String[0]));
124
+
125
+ // Generate record components (for Java Records)
126
+ @SuppressWarnings("unchecked")
127
+ var recordComponents = (List<Map<String, Object>>) classDef.getOrDefault("recordComponents", List.of());
128
+ for (var comp : recordComponents) {
129
+ String compName = (String) comp.get("name");
130
+ String compDescriptor = (String) comp.get("descriptor");
131
+ cw.visitRecordComponent(compName, compDescriptor, null).visitEnd();
132
+ }
133
+
134
+ // Generate fields
135
+ @SuppressWarnings("unchecked")
136
+ var fields = (List<Map<String, Object>>) classDef.getOrDefault("fields", List.of());
137
+ for (var field : fields) {
138
+ generateField(cw, field);
139
+ }
140
+
141
+ // Generate methods
142
+ @SuppressWarnings("unchecked")
143
+ var methods = (List<Map<String, Object>>) classDef.getOrDefault("methods", List.of());
144
+ for (var method : methods) {
145
+ try {
146
+ generateMethod(cw, method);
147
+ } catch (Exception e) {
148
+ String mName = (String) method.get("name");
149
+ String mDesc = (String) method.get("descriptor");
150
+ System.err.println("ASM error in class=" + className + " method=" + mName + " descriptor=" + mDesc);
151
+ throw e;
152
+ }
153
+ }
154
+
155
+ cw.visitEnd();
156
+
157
+ // Write .class file
158
+ byte[] bytecode = cw.toByteArray();
159
+ String filePath = outputDir + "/" + className + ".class";
160
+ Path path = Path.of(filePath);
161
+ Files.createDirectories(path.getParent());
162
+ Files.write(path, bytecode);
163
+ }
164
+
165
+ private void generateField(ClassWriter cw, Map<String, Object> field) {
166
+ String name = (String) field.get("name");
167
+ String descriptor = (String) field.get("descriptor");
168
+ int access = parseAccessFlags((List<?>) field.get("access"));
169
+ Object value = field.get("value"); // for static final constants
170
+ cw.visitField(access, name, descriptor, null, value);
171
+ }
172
+
173
+ private void generateMethod(ClassWriter cw, Map<String, Object> method) {
174
+ String name = (String) method.get("name");
175
+ String descriptor = (String) method.get("descriptor");
176
+ int access = parseAccessFlags((List<?>) method.get("access"));
177
+
178
+ MethodVisitor mv = cw.visitMethod(access, name, descriptor, null, null);
179
+
180
+ // Abstract methods have no body
181
+ if ((access & Opcodes.ACC_ABSTRACT) != 0) {
182
+ mv.visitEnd();
183
+ return;
184
+ }
185
+
186
+ mv.visitCode();
187
+
188
+ // Label registry for jumps
189
+ Map<String, Label> labels = new HashMap<>();
190
+
191
+ // Process exception table BEFORE instructions (ASM requires this)
192
+ @SuppressWarnings("unchecked")
193
+ var exceptionTable = (List<Map<String, Object>>)
194
+ method.getOrDefault("exceptionTable", List.of());
195
+ for (var entry : exceptionTable) {
196
+ mv.visitTryCatchBlock(
197
+ getLabel(labels, (String) entry.get("start")),
198
+ getLabel(labels, (String) entry.get("end")),
199
+ getLabel(labels, (String) entry.get("handler")),
200
+ (String) entry.get("type")); // null for finally/catch-all
201
+ }
202
+
203
+ @SuppressWarnings("unchecked")
204
+ var instructions = (List<Map<String, Object>>) method.getOrDefault("instructions", List.of());
205
+ for (var inst : instructions) {
206
+ emitInstruction(mv, inst, labels);
207
+ }
208
+
209
+ mv.visitMaxs(0, 0); // ASM COMPUTE_MAXS handles this
210
+ mv.visitEnd();
211
+ }
212
+
213
+ private void emitInstruction(MethodVisitor mv, Map<String, Object> inst, Map<String, Label> labels) {
214
+ String op = (String) inst.get("op");
215
+
216
+ switch (op) {
217
+ // --- Labels ---
218
+ case "label" -> {
219
+ String labelName = (String) inst.get("name");
220
+ mv.visitLabel(getLabel(labels, labelName));
221
+ }
222
+
223
+ // --- Load/Store (long takes 2 slots) ---
224
+ case "lload" -> mv.visitVarInsn(Opcodes.LLOAD, getInt(inst, "var"));
225
+ case "lstore" -> mv.visitVarInsn(Opcodes.LSTORE, getInt(inst, "var"));
226
+ case "dload" -> mv.visitVarInsn(Opcodes.DLOAD, getInt(inst, "var"));
227
+ case "dstore" -> mv.visitVarInsn(Opcodes.DSTORE, getInt(inst, "var"));
228
+ case "iload" -> mv.visitVarInsn(Opcodes.ILOAD, getInt(inst, "var"));
229
+ case "istore" -> mv.visitVarInsn(Opcodes.ISTORE, getInt(inst, "var"));
230
+ case "aload" -> mv.visitVarInsn(Opcodes.ALOAD, getInt(inst, "var"));
231
+ case "astore" -> mv.visitVarInsn(Opcodes.ASTORE, getInt(inst, "var"));
232
+
233
+ // --- Constants ---
234
+ case "ldc" -> {
235
+ Object value = inst.get("value");
236
+ if (value instanceof String s) {
237
+ mv.visitLdcInsn(s);
238
+ } else if (value instanceof Number n) {
239
+ // For int/float constants
240
+ if (n instanceof Double || (n instanceof Number && n.toString().contains("."))) {
241
+ mv.visitLdcInsn(n.doubleValue());
242
+ } else {
243
+ mv.visitLdcInsn(n.intValue());
244
+ }
245
+ }
246
+ }
247
+ case "ldc2_w" -> {
248
+ Object value = inst.get("value");
249
+ String type = (String) inst.getOrDefault("type", "long");
250
+ if ("double".equals(type)) {
251
+ mv.visitLdcInsn(((Number) value).doubleValue());
252
+ } else {
253
+ mv.visitLdcInsn(((Number) value).longValue());
254
+ }
255
+ }
256
+ case "iconst" -> {
257
+ int val = getInt(inst, "value");
258
+ switch (val) {
259
+ case -1 -> mv.visitInsn(Opcodes.ICONST_M1);
260
+ case 0 -> mv.visitInsn(Opcodes.ICONST_0);
261
+ case 1 -> mv.visitInsn(Opcodes.ICONST_1);
262
+ case 2 -> mv.visitInsn(Opcodes.ICONST_2);
263
+ case 3 -> mv.visitInsn(Opcodes.ICONST_3);
264
+ case 4 -> mv.visitInsn(Opcodes.ICONST_4);
265
+ case 5 -> mv.visitInsn(Opcodes.ICONST_5);
266
+ default -> mv.visitIntInsn(Opcodes.BIPUSH, val);
267
+ }
268
+ }
269
+ case "lconst_0" -> mv.visitInsn(Opcodes.LCONST_0);
270
+ case "lconst_1" -> mv.visitInsn(Opcodes.LCONST_1);
271
+ case "dconst_0" -> mv.visitInsn(Opcodes.DCONST_0);
272
+ case "dconst_1" -> mv.visitInsn(Opcodes.DCONST_1);
273
+ case "aconst_null" -> mv.visitInsn(Opcodes.ACONST_NULL);
274
+
275
+ // --- Arithmetic (int) ---
276
+ case "iadd" -> mv.visitInsn(Opcodes.IADD);
277
+ case "isub" -> mv.visitInsn(Opcodes.ISUB);
278
+
279
+ // --- Arithmetic (long) ---
280
+ case "ladd" -> mv.visitInsn(Opcodes.LADD);
281
+ case "lsub" -> mv.visitInsn(Opcodes.LSUB);
282
+ case "lmul" -> mv.visitInsn(Opcodes.LMUL);
283
+ case "ldiv" -> mv.visitInsn(Opcodes.LDIV);
284
+ case "lrem" -> mv.visitInsn(Opcodes.LREM);
285
+ case "lneg" -> mv.visitInsn(Opcodes.LNEG);
286
+ case "land" -> mv.visitInsn(Opcodes.LAND);
287
+ case "lor" -> mv.visitInsn(Opcodes.LOR);
288
+ case "lxor" -> mv.visitInsn(Opcodes.LXOR);
289
+ case "lshl" -> mv.visitInsn(Opcodes.LSHL);
290
+ case "lshr" -> mv.visitInsn(Opcodes.LSHR);
291
+ case "lushr" -> mv.visitInsn(Opcodes.LUSHR);
292
+
293
+ // --- Arithmetic (double) ---
294
+ case "dadd" -> mv.visitInsn(Opcodes.DADD);
295
+ case "dsub" -> mv.visitInsn(Opcodes.DSUB);
296
+ case "dmul" -> mv.visitInsn(Opcodes.DMUL);
297
+ case "ddiv" -> mv.visitInsn(Opcodes.DDIV);
298
+ case "drem" -> mv.visitInsn(Opcodes.DREM);
299
+ case "dneg" -> mv.visitInsn(Opcodes.DNEG);
300
+
301
+ // --- Type conversions ---
302
+ case "l2d" -> mv.visitInsn(Opcodes.L2D);
303
+ case "d2l" -> mv.visitInsn(Opcodes.D2L);
304
+ case "i2l" -> mv.visitInsn(Opcodes.I2L);
305
+ case "l2i" -> mv.visitInsn(Opcodes.L2I);
306
+ case "i2d" -> mv.visitInsn(Opcodes.I2D);
307
+ case "d2i" -> mv.visitInsn(Opcodes.D2I);
308
+
309
+ // --- Comparisons ---
310
+ case "lcmp" -> mv.visitInsn(Opcodes.LCMP);
311
+ case "dcmpl" -> mv.visitInsn(Opcodes.DCMPL);
312
+ case "dcmpg" -> mv.visitInsn(Opcodes.DCMPG);
313
+
314
+ // --- Branch instructions ---
315
+ case "ifeq" -> mv.visitJumpInsn(Opcodes.IFEQ, getLabel(labels, (String) inst.get("target")));
316
+ case "ifne" -> mv.visitJumpInsn(Opcodes.IFNE, getLabel(labels, (String) inst.get("target")));
317
+ case "iflt" -> mv.visitJumpInsn(Opcodes.IFLT, getLabel(labels, (String) inst.get("target")));
318
+ case "ifge" -> mv.visitJumpInsn(Opcodes.IFGE, getLabel(labels, (String) inst.get("target")));
319
+ case "ifgt" -> mv.visitJumpInsn(Opcodes.IFGT, getLabel(labels, (String) inst.get("target")));
320
+ case "ifle" -> mv.visitJumpInsn(Opcodes.IFLE, getLabel(labels, (String) inst.get("target")));
321
+ case "if_icmpeq" -> mv.visitJumpInsn(Opcodes.IF_ICMPEQ, getLabel(labels, (String) inst.get("target")));
322
+ case "if_icmpne" -> mv.visitJumpInsn(Opcodes.IF_ICMPNE, getLabel(labels, (String) inst.get("target")));
323
+ case "if_icmplt" -> mv.visitJumpInsn(Opcodes.IF_ICMPLT, getLabel(labels, (String) inst.get("target")));
324
+ case "if_icmpge" -> mv.visitJumpInsn(Opcodes.IF_ICMPGE, getLabel(labels, (String) inst.get("target")));
325
+ case "if_icmpgt" -> mv.visitJumpInsn(Opcodes.IF_ICMPGT, getLabel(labels, (String) inst.get("target")));
326
+ case "if_icmple" -> mv.visitJumpInsn(Opcodes.IF_ICMPLE, getLabel(labels, (String) inst.get("target")));
327
+ case "ifnull" -> mv.visitJumpInsn(Opcodes.IFNULL, getLabel(labels, (String) inst.get("target")));
328
+ case "ifnonnull" -> mv.visitJumpInsn(Opcodes.IFNONNULL, getLabel(labels, (String) inst.get("target")));
329
+ case "goto" -> mv.visitJumpInsn(Opcodes.GOTO, getLabel(labels, (String) inst.get("target")));
330
+
331
+ // --- Return ---
332
+ case "lreturn" -> mv.visitInsn(Opcodes.LRETURN);
333
+ case "dreturn" -> mv.visitInsn(Opcodes.DRETURN);
334
+ case "ireturn" -> mv.visitInsn(Opcodes.IRETURN);
335
+ case "areturn" -> mv.visitInsn(Opcodes.ARETURN);
336
+ case "return" -> mv.visitInsn(Opcodes.RETURN);
337
+
338
+ // --- Method calls ---
339
+ case "invokestatic" -> {
340
+ boolean isIface = inst.containsKey("isInterface") && (boolean) inst.get("isInterface");
341
+ mv.visitMethodInsn(Opcodes.INVOKESTATIC,
342
+ (String) inst.get("owner"), (String) inst.get("name"),
343
+ (String) inst.get("descriptor"), isIface);
344
+ }
345
+ case "invokevirtual" -> mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
346
+ (String) inst.get("owner"), (String) inst.get("name"),
347
+ (String) inst.get("descriptor"), false);
348
+ case "invokespecial" -> mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
349
+ (String) inst.get("owner"), (String) inst.get("name"),
350
+ (String) inst.get("descriptor"), false);
351
+ case "invokeinterface" -> mv.visitMethodInsn(Opcodes.INVOKEINTERFACE,
352
+ (String) inst.get("owner"), (String) inst.get("name"),
353
+ (String) inst.get("descriptor"), true);
354
+
355
+ // --- invokedynamic (for blocks/lambdas via LambdaMetafactory) ---
356
+ case "invokedynamic" -> {
357
+ String indyName = (String) inst.get("name");
358
+ String indyDescriptor = (String) inst.get("descriptor");
359
+
360
+ // Bootstrap method handle
361
+ Handle bootstrapHandle = new Handle(
362
+ Opcodes.H_INVOKESTATIC,
363
+ (String) inst.get("bootstrapOwner"),
364
+ (String) inst.get("bootstrapName"),
365
+ (String) inst.get("bootstrapDescriptor"),
366
+ false);
367
+
368
+ // Bootstrap arguments
369
+ @SuppressWarnings("unchecked")
370
+ var bsmArgs = (List<Map<String, Object>>) inst.getOrDefault("bootstrapArgs", List.of());
371
+ Object[] args = new Object[bsmArgs.size()];
372
+ for (int i = 0; i < bsmArgs.size(); i++) {
373
+ Map<String, Object> arg = bsmArgs.get(i);
374
+ String argType = (String) arg.get("type");
375
+ switch (argType) {
376
+ case "methodType" -> args[i] = Type.getMethodType((String) arg.get("descriptor"));
377
+ case "handle" -> {
378
+ int tag = parseHandleTag((String) arg.get("tag"));
379
+ boolean isInterface = arg.containsKey("itf") && Boolean.TRUE.equals(arg.get("itf"));
380
+ args[i] = new Handle(tag,
381
+ (String) arg.get("owner"),
382
+ (String) arg.get("name"),
383
+ (String) arg.get("descriptor"),
384
+ isInterface);
385
+ }
386
+ default -> throw new RuntimeException("Unknown bootstrap arg type: " + argType);
387
+ }
388
+ }
389
+
390
+ mv.visitInvokeDynamicInsn(indyName, indyDescriptor, bootstrapHandle, args);
391
+ }
392
+
393
+ // --- Field access ---
394
+ case "getfield" -> mv.visitFieldInsn(Opcodes.GETFIELD,
395
+ (String) inst.get("owner"), (String) inst.get("name"),
396
+ (String) inst.get("descriptor"));
397
+ case "putfield" -> mv.visitFieldInsn(Opcodes.PUTFIELD,
398
+ (String) inst.get("owner"), (String) inst.get("name"),
399
+ (String) inst.get("descriptor"));
400
+ case "getstatic" -> mv.visitFieldInsn(Opcodes.GETSTATIC,
401
+ (String) inst.get("owner"), (String) inst.get("name"),
402
+ (String) inst.get("descriptor"));
403
+ case "putstatic" -> mv.visitFieldInsn(Opcodes.PUTSTATIC,
404
+ (String) inst.get("owner"), (String) inst.get("name"),
405
+ (String) inst.get("descriptor"));
406
+
407
+ // --- Object creation ---
408
+ case "new" -> mv.visitTypeInsn(Opcodes.NEW, (String) inst.get("type"));
409
+ case "dup" -> mv.visitInsn(Opcodes.DUP);
410
+ case "dup2" -> mv.visitInsn(Opcodes.DUP2);
411
+ case "dup_x1" -> mv.visitInsn(Opcodes.DUP_X1);
412
+ case "dup_x2" -> mv.visitInsn(Opcodes.DUP_X2);
413
+ case "dup2_x1" -> mv.visitInsn(Opcodes.DUP2_X1);
414
+ case "dup2_x2" -> mv.visitInsn(Opcodes.DUP2_X2);
415
+ case "pop" -> mv.visitInsn(Opcodes.POP);
416
+ case "pop2" -> mv.visitInsn(Opcodes.POP2);
417
+ case "swap" -> mv.visitInsn(Opcodes.SWAP);
418
+
419
+ // --- Type checks ---
420
+ case "instanceof" -> mv.visitTypeInsn(Opcodes.INSTANCEOF, (String) inst.get("type"));
421
+ case "checkcast" -> mv.visitTypeInsn(Opcodes.CHECKCAST, (String) inst.get("type"));
422
+
423
+ // --- Array operations ---
424
+ case "newarray" -> {
425
+ String atype = (String) inst.get("type");
426
+ int typeCode = switch (atype) {
427
+ case "long" -> Opcodes.T_LONG;
428
+ case "double" -> Opcodes.T_DOUBLE;
429
+ case "int" -> Opcodes.T_INT;
430
+ case "byte" -> Opcodes.T_BYTE;
431
+ case "boolean" -> Opcodes.T_BOOLEAN;
432
+ default -> Opcodes.T_LONG;
433
+ };
434
+ mv.visitIntInsn(Opcodes.NEWARRAY, typeCode);
435
+ }
436
+ case "anewarray" -> mv.visitTypeInsn(Opcodes.ANEWARRAY, (String) inst.get("type"));
437
+ case "arraylength" -> mv.visitInsn(Opcodes.ARRAYLENGTH);
438
+ case "laload" -> mv.visitInsn(Opcodes.LALOAD);
439
+ case "lastore" -> mv.visitInsn(Opcodes.LASTORE);
440
+ case "daload" -> mv.visitInsn(Opcodes.DALOAD);
441
+ case "dastore" -> mv.visitInsn(Opcodes.DASTORE);
442
+ case "aaload" -> mv.visitInsn(Opcodes.AALOAD);
443
+ case "aastore" -> mv.visitInsn(Opcodes.AASTORE);
444
+
445
+ // --- Exception handling ---
446
+ case "athrow" -> mv.visitInsn(Opcodes.ATHROW);
447
+
448
+ // --- Line number (for debugging) ---
449
+ case "linenumber" -> {
450
+ int line = getInt(inst, "line");
451
+ String labelName = (String) inst.get("label");
452
+ if (labelName != null) {
453
+ mv.visitLineNumber(line, getLabel(labels, labelName));
454
+ }
455
+ }
456
+
457
+ default -> System.err.println("Warning: unknown instruction: " + op);
458
+ }
459
+ }
460
+
461
+ // --- Helpers ---
462
+
463
+ private Label getLabel(Map<String, Label> labels, String name) {
464
+ return labels.computeIfAbsent(name, k -> new Label());
465
+ }
466
+
467
+ private int getInt(Map<String, Object> inst, String key) {
468
+ Object val = inst.get(key);
469
+ if (val instanceof Number n) return n.intValue();
470
+ throw new RuntimeException("Expected int for key '" + key + "', got: " + val);
471
+ }
472
+
473
+ private int parseHandleTag(String tag) {
474
+ return switch (tag) {
475
+ case "H_GETFIELD" -> Opcodes.H_GETFIELD;
476
+ case "H_GETSTATIC" -> Opcodes.H_GETSTATIC;
477
+ case "H_PUTFIELD" -> Opcodes.H_PUTFIELD;
478
+ case "H_PUTSTATIC" -> Opcodes.H_PUTSTATIC;
479
+ case "H_INVOKEVIRTUAL" -> Opcodes.H_INVOKEVIRTUAL;
480
+ case "H_INVOKESTATIC" -> Opcodes.H_INVOKESTATIC;
481
+ case "H_INVOKESPECIAL" -> Opcodes.H_INVOKESPECIAL;
482
+ case "H_NEWINVOKESPECIAL" -> Opcodes.H_NEWINVOKESPECIAL;
483
+ case "H_INVOKEINTERFACE" -> Opcodes.H_INVOKEINTERFACE;
484
+ default -> throw new RuntimeException("Unknown handle tag: " + tag);
485
+ };
486
+ }
487
+
488
+ private int parseAccessFlags(List<?> flags) {
489
+ if (flags == null) return 0;
490
+ int result = 0;
491
+ for (Object flag : flags) {
492
+ result |= switch ((String) flag) {
493
+ case "public" -> Opcodes.ACC_PUBLIC;
494
+ case "private" -> Opcodes.ACC_PRIVATE;
495
+ case "protected" -> Opcodes.ACC_PROTECTED;
496
+ case "static" -> Opcodes.ACC_STATIC;
497
+ case "final" -> Opcodes.ACC_FINAL;
498
+ case "abstract" -> Opcodes.ACC_ABSTRACT;
499
+ case "interface" -> Opcodes.ACC_INTERFACE;
500
+ case "super" -> Opcodes.ACC_SUPER;
501
+ case "synthetic" -> Opcodes.ACC_SYNTHETIC;
502
+ case "record" -> Opcodes.ACC_RECORD;
503
+ default -> 0;
504
+ };
505
+ }
506
+ return result;
507
+ }
508
+
509
+ // ========================================================================
510
+ // Minimal JSON Parser (no external dependencies)
511
+ // ========================================================================
512
+ static class JsonParser {
513
+ private final String input;
514
+ private int pos;
515
+
516
+ JsonParser(String input) {
517
+ this.input = input;
518
+ this.pos = 0;
519
+ }
520
+
521
+ Map<String, Object> parseObject() {
522
+ skipWhitespace();
523
+ expect('{');
524
+ var map = new LinkedHashMap<String, Object>();
525
+ skipWhitespace();
526
+ if (peek() != '}') {
527
+ do {
528
+ skipWhitespace();
529
+ String key = parseString();
530
+ skipWhitespace();
531
+ expect(':');
532
+ skipWhitespace();
533
+ Object value = parseValue();
534
+ map.put(key, value);
535
+ skipWhitespace();
536
+ } while (tryConsume(','));
537
+ }
538
+ expect('}');
539
+ return map;
540
+ }
541
+
542
+ List<Object> parseArray() {
543
+ expect('[');
544
+ var list = new ArrayList<>();
545
+ skipWhitespace();
546
+ if (peek() != ']') {
547
+ do {
548
+ skipWhitespace();
549
+ list.add(parseValue());
550
+ skipWhitespace();
551
+ } while (tryConsume(','));
552
+ }
553
+ expect(']');
554
+ return list;
555
+ }
556
+
557
+ Object parseValue() {
558
+ skipWhitespace();
559
+ char c = peek();
560
+ return switch (c) {
561
+ case '"' -> parseString();
562
+ case '{' -> parseObject();
563
+ case '[' -> parseArray();
564
+ case 't' -> { consume("true"); yield Boolean.TRUE; }
565
+ case 'f' -> { consume("false"); yield Boolean.FALSE; }
566
+ case 'n' -> { consume("null"); yield null; }
567
+ default -> parseNumber();
568
+ };
569
+ }
570
+
571
+ String parseString() {
572
+ expect('"');
573
+ var sb = new StringBuilder();
574
+ while (pos < input.length()) {
575
+ char c = input.charAt(pos++);
576
+ if (c == '"') return sb.toString();
577
+ if (c == '\\') {
578
+ char esc = input.charAt(pos++);
579
+ switch (esc) {
580
+ case '"' -> sb.append('"');
581
+ case '\\' -> sb.append('\\');
582
+ case '/' -> sb.append('/');
583
+ case 'n' -> sb.append('\n');
584
+ case 'r' -> sb.append('\r');
585
+ case 't' -> sb.append('\t');
586
+ case 'u' -> {
587
+ String hex = input.substring(pos, pos + 4);
588
+ sb.append((char) Integer.parseInt(hex, 16));
589
+ pos += 4;
590
+ }
591
+ default -> sb.append(esc);
592
+ }
593
+ } else {
594
+ sb.append(c);
595
+ }
596
+ }
597
+ throw new RuntimeException("Unterminated string");
598
+ }
599
+
600
+ Number parseNumber() {
601
+ int start = pos;
602
+ if (peek() == '-') pos++;
603
+ while (pos < input.length() && Character.isDigit(input.charAt(pos))) pos++;
604
+ boolean isFloat = false;
605
+ if (pos < input.length() && input.charAt(pos) == '.') {
606
+ isFloat = true;
607
+ pos++;
608
+ while (pos < input.length() && Character.isDigit(input.charAt(pos))) pos++;
609
+ }
610
+ if (pos < input.length() && (input.charAt(pos) == 'e' || input.charAt(pos) == 'E')) {
611
+ isFloat = true;
612
+ pos++;
613
+ if (pos < input.length() && (input.charAt(pos) == '+' || input.charAt(pos) == '-')) pos++;
614
+ while (pos < input.length() && Character.isDigit(input.charAt(pos))) pos++;
615
+ }
616
+ String num = input.substring(start, pos);
617
+ if (isFloat) return Double.parseDouble(num);
618
+ long val = Long.parseLong(num);
619
+ if (val >= Integer.MIN_VALUE && val <= Integer.MAX_VALUE) return (int) val;
620
+ return val;
621
+ }
622
+
623
+ char peek() {
624
+ if (pos >= input.length()) throw new RuntimeException("Unexpected end of input");
625
+ return input.charAt(pos);
626
+ }
627
+
628
+ void expect(char c) {
629
+ skipWhitespace();
630
+ if (pos >= input.length() || input.charAt(pos) != c) {
631
+ throw new RuntimeException("Expected '" + c + "' at pos " + pos +
632
+ ", got: " + (pos < input.length() ? "'" + input.charAt(pos) + "'" : "EOF"));
633
+ }
634
+ pos++;
635
+ }
636
+
637
+ boolean tryConsume(char c) {
638
+ skipWhitespace();
639
+ if (pos < input.length() && input.charAt(pos) == c) {
640
+ pos++;
641
+ return true;
642
+ }
643
+ return false;
644
+ }
645
+
646
+ void consume(String s) {
647
+ for (char c : s.toCharArray()) {
648
+ if (pos >= input.length() || input.charAt(pos) != c) {
649
+ throw new RuntimeException("Expected '" + s + "' at pos " + pos);
650
+ }
651
+ pos++;
652
+ }
653
+ }
654
+
655
+ void skipWhitespace() {
656
+ while (pos < input.length() && Character.isWhitespace(input.charAt(pos))) pos++;
657
+ }
658
+ }
659
+ }