konpeito 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +139 -1
- data/lib/konpeito/codegen/cruby_backend.rb +5 -2
- data/lib/konpeito/codegen/inliner.rb +106 -0
- data/lib/konpeito/codegen/jvm_generator.rb +101 -19
- data/lib/konpeito/hir/builder.rb +5 -2
- data/lib/konpeito/stdlib/json/yyjson_wrapper.c +60 -0
- data/lib/konpeito/version.rb +1 -1
- data/scripts/setup_vendor.sh +33 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 85de87c8abf1bbf3ecaf6303ba4fd875f16016099a38734c55c108f123fa4086
|
|
4
|
+
data.tar.gz: 843f4b6d722d68526d031734c035c9126c520d9869a2488259dbf39ce1eade84
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eaf22b341a9135b2d81fcda350790330a78f2da9255771833676047086fbcd861919295f8cf7f4967f380cc62c8ea882499cd6b89a863b86bbd1c53e6e6f2b9e
|
|
7
|
+
data.tar.gz: ab456db3432eca45ff8b658b80c07d38cdbec33c1e4cd6e379d59dbb0cf8f3384b6f715b3bac61f7d120dfb25e479fe86b65dc4c44ad5c7e86ab18703ff6e6a9
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to Konpeito will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.2.0] - 2026-02-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Inliner: keyword argument mapping for correct inlining of functions with keyword params
|
|
12
|
+
- Inliner: ProcNew/BlockDef deep cloning with proper captured variable renaming
|
|
13
|
+
- Vendor setup script (`scripts/setup_vendor.sh`) to download yyjson source files
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- HIR builder: save/restore `@current_block` in NativeHashEach to emit into correct basic block
|
|
17
|
+
- Inliner: ProcCall handler for proc_value and args renaming during inlining
|
|
18
|
+
- JSON stdlib: add tracked `yyjson_wrapper.c` providing non-inline wrappers for LLVM-generated code (fixes SEGV in `parse_as`/`parse_array_as` tests)
|
|
19
|
+
- CRuby backend: reference yyjson wrapper from tracked source location instead of vendor directory
|
|
20
|
+
|
|
21
|
+
## [0.1.3] - 2026-02-18
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- JVM backend: resolve class method descriptor mismatch when called from instance methods (pre-register singleton method descriptors)
|
|
25
|
+
- Documentation: correct JWM and Skija GitHub URLs in README
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Documentation: Castella UI calculator demo with Style composition
|
|
29
|
+
|
|
8
30
|
## [0.1.2] - 2026-02-17
|
|
9
31
|
|
|
10
32
|
### Fixed
|
|
@@ -99,6 +121,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
99
121
|
- `%a{extern}` - external C struct wrappers
|
|
100
122
|
- `%a{simd}` - SIMD vectorization
|
|
101
123
|
|
|
124
|
+
[0.2.0]: https://github.com/i2y/konpeito/compare/v0.1.3...v0.2.0
|
|
125
|
+
[0.1.3]: https://github.com/i2y/konpeito/compare/v0.1.2...v0.1.3
|
|
102
126
|
[0.1.2]: https://github.com/i2y/konpeito/compare/v0.1.1...v0.1.2
|
|
103
127
|
[0.1.1]: https://github.com/i2y/konpeito/compare/v0.1.0...v0.1.1
|
|
104
128
|
[0.1.0]: https://github.com/i2y/konpeito/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -135,7 +135,7 @@ The JVM backend supports seamless Java interop — call Java libraries directly
|
|
|
135
135
|
|
|
136
136
|
## Castella UI
|
|
137
137
|
|
|
138
|
-
A reactive GUI framework for the JVM backend, powered by [JWM](https://github.com/
|
|
138
|
+
A reactive GUI framework for the JVM backend, powered by [JWM](https://github.com/HumbleUI/JWM) + [Skija](https://github.com/HumbleUI/Skija).
|
|
139
139
|
|
|
140
140
|
### DSL
|
|
141
141
|
|
|
@@ -230,6 +230,144 @@ column(spacing: 12.0) {
|
|
|
230
230
|
}
|
|
231
231
|
```
|
|
232
232
|
|
|
233
|
+
### Calculator Demo
|
|
234
|
+
|
|
235
|
+
A port of the original Python Castella calculator. Flex layout, button kinds (`KIND_DANGER`, `KIND_WARNING`, `KIND_SUCCESS`), reactive `state()`, and class methods called from instance method callbacks — all in ~130 lines:
|
|
236
|
+
|
|
237
|
+
<p align="center">
|
|
238
|
+
<img src="docs/screenshots/calc.png" alt="Calculator" width="320" />
|
|
239
|
+
</p>
|
|
240
|
+
|
|
241
|
+
```ruby
|
|
242
|
+
class Calc < Component
|
|
243
|
+
def initialize
|
|
244
|
+
super
|
|
245
|
+
@display = state("0")
|
|
246
|
+
@lhs = 0.0
|
|
247
|
+
@current_op = ""
|
|
248
|
+
@is_refresh = true
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# --- Calculator logic ---
|
|
252
|
+
|
|
253
|
+
def press_number(label)
|
|
254
|
+
if @display.value == "0" || @is_refresh
|
|
255
|
+
@display.set(label)
|
|
256
|
+
else
|
|
257
|
+
@display.set(@display.value + label)
|
|
258
|
+
end
|
|
259
|
+
@is_refresh = false
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def press_dot
|
|
263
|
+
if @is_refresh
|
|
264
|
+
@display.set("0.")
|
|
265
|
+
@is_refresh = false
|
|
266
|
+
return
|
|
267
|
+
end
|
|
268
|
+
if !@display.value.include?(".")
|
|
269
|
+
@display.set(@display.value + ".")
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def all_clear
|
|
274
|
+
@display.set("0")
|
|
275
|
+
@lhs = 0.0
|
|
276
|
+
@current_op = ""
|
|
277
|
+
@is_refresh = true
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def self.calc(lhs, op, rhs)
|
|
281
|
+
if op == "+"
|
|
282
|
+
lhs + rhs
|
|
283
|
+
elsif op == "-"
|
|
284
|
+
lhs - rhs
|
|
285
|
+
elsif op == "\u00D7"
|
|
286
|
+
lhs * rhs
|
|
287
|
+
elsif op == "\u00F7"
|
|
288
|
+
if rhs == 0.0
|
|
289
|
+
0.0
|
|
290
|
+
else
|
|
291
|
+
lhs / rhs
|
|
292
|
+
end
|
|
293
|
+
else
|
|
294
|
+
rhs
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def self.format_result(val)
|
|
299
|
+
if val == val.to_i.to_f
|
|
300
|
+
val.to_i.to_s
|
|
301
|
+
else
|
|
302
|
+
val.to_s
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def press_operator(new_op)
|
|
307
|
+
rhs = @display.value.to_f
|
|
308
|
+
if @current_op != ""
|
|
309
|
+
result = Calc.calc(@lhs, @current_op, rhs)
|
|
310
|
+
@display.set(Calc.format_result(result))
|
|
311
|
+
@lhs = result
|
|
312
|
+
else
|
|
313
|
+
@lhs = rhs
|
|
314
|
+
end
|
|
315
|
+
if new_op == "="
|
|
316
|
+
@current_op = ""
|
|
317
|
+
else
|
|
318
|
+
@current_op = new_op
|
|
319
|
+
end
|
|
320
|
+
@is_refresh = true
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# --- View ---
|
|
324
|
+
|
|
325
|
+
def view
|
|
326
|
+
grid = Style.new.spacing(4.0)
|
|
327
|
+
btn = Style.new.font_size(32.0)
|
|
328
|
+
op = btn + Style.new.kind(KIND_WARNING)
|
|
329
|
+
ac = btn + Style.new.kind(KIND_DANGER).flex(3)
|
|
330
|
+
eq = btn + Style.new.kind(KIND_SUCCESS)
|
|
331
|
+
wide = btn + Style.new.flex(2)
|
|
332
|
+
|
|
333
|
+
column(spacing: 4.0, padding: 4.0) {
|
|
334
|
+
text @display.value, font_size: 48.0, align: :right, kind: KIND_INFO, height: 72.0
|
|
335
|
+
row(grid) {
|
|
336
|
+
button("AC", ac) { all_clear }
|
|
337
|
+
button("\u00F7", op) { press_operator("\u00F7") }
|
|
338
|
+
}
|
|
339
|
+
row(grid) {
|
|
340
|
+
button("7", btn) { press_number("7") }
|
|
341
|
+
button("8", btn) { press_number("8") }
|
|
342
|
+
button("9", btn) { press_number("9") }
|
|
343
|
+
button("\u00D7", op) { press_operator("\u00D7") }
|
|
344
|
+
}
|
|
345
|
+
row(grid) {
|
|
346
|
+
button("4", btn) { press_number("4") }
|
|
347
|
+
button("5", btn) { press_number("5") }
|
|
348
|
+
button("6", btn) { press_number("6") }
|
|
349
|
+
button("-", op) { press_operator("-") }
|
|
350
|
+
}
|
|
351
|
+
row(grid) {
|
|
352
|
+
button("1", btn) { press_number("1") }
|
|
353
|
+
button("2", btn) { press_number("2") }
|
|
354
|
+
button("3", btn) { press_number("3") }
|
|
355
|
+
button("+", op) { press_operator("+") }
|
|
356
|
+
}
|
|
357
|
+
row(grid) {
|
|
358
|
+
button("0", wide) { press_number("0") }
|
|
359
|
+
button(".", btn) { press_dot }
|
|
360
|
+
button("=", eq) { press_operator("=") }
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
frame = JWMFrame.new("Castella Calculator", 320, 480)
|
|
367
|
+
app = App.new(frame, Calc.new)
|
|
368
|
+
app.run
|
|
369
|
+
```
|
|
370
|
+
|
|
233
371
|
## Performance
|
|
234
372
|
|
|
235
373
|
Konpeito shines in compute-heavy, typed loops where unboxed arithmetic and backend optimizations kick in. All benchmarks compare against Ruby 4.0.1 with YJIT enabled on Apple M4 Max.
|
|
@@ -893,7 +893,10 @@ module Konpeito
|
|
|
893
893
|
yyjson_dir = File.expand_path("../../../vendor/yyjson", __dir__)
|
|
894
894
|
yyjson_c = File.join(yyjson_dir, "yyjson.c")
|
|
895
895
|
yyjson_obj = File.join(yyjson_dir, "yyjson.o")
|
|
896
|
-
|
|
896
|
+
|
|
897
|
+
# Wrapper source is tracked in repo alongside JSON stdlib
|
|
898
|
+
json_stdlib_dir = File.expand_path("../stdlib/json", __dir__)
|
|
899
|
+
wrapper_c = File.join(json_stdlib_dir, "yyjson_wrapper.c")
|
|
897
900
|
wrapper_obj = File.join(yyjson_dir, "yyjson_wrapper.o")
|
|
898
901
|
|
|
899
902
|
return [] unless File.exist?(yyjson_c) && File.exist?(wrapper_c)
|
|
@@ -906,7 +909,7 @@ module Konpeito
|
|
|
906
909
|
system(*cmd) or return []
|
|
907
910
|
end
|
|
908
911
|
|
|
909
|
-
# Compile wrapper
|
|
912
|
+
# Compile wrapper (needs yyjson.h from vendor dir)
|
|
910
913
|
unless File.exist?(wrapper_obj) && File.mtime(wrapper_obj) > File.mtime(wrapper_c)
|
|
911
914
|
cmd = [cc, "-c", "-O3", "-fPIC", "-I#{yyjson_dir}", "-o", wrapper_obj, wrapper_c]
|
|
912
915
|
system(*cmd) or return []
|
|
@@ -178,6 +178,17 @@ module Konpeito
|
|
|
178
178
|
param_map[param.name] = call_inst.args[i]
|
|
179
179
|
end
|
|
180
180
|
end
|
|
181
|
+
# Also map keyword arguments by name
|
|
182
|
+
if call_inst.respond_to?(:keyword_args) && call_inst.has_keyword_args?
|
|
183
|
+
call_inst.keyword_args.each do |kw_name, kw_value|
|
|
184
|
+
# Find the callee param with matching name
|
|
185
|
+
callee.params.each do |param|
|
|
186
|
+
if param.name.to_s == kw_name.to_s
|
|
187
|
+
param_map[param.name] = kw_value
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
181
192
|
|
|
182
193
|
# Clone and transform instructions from callee
|
|
183
194
|
result_instructions = []
|
|
@@ -438,6 +449,17 @@ module Konpeito
|
|
|
438
449
|
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
439
450
|
HIR::SuperCall.new(args: new_args, type: inst.type, result_var: new_result)
|
|
440
451
|
|
|
452
|
+
when HIR::ProcNew
|
|
453
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
454
|
+
new_block_def = clone_block_def(inst.block_def, prefix, param_map)
|
|
455
|
+
HIR::ProcNew.new(block_def: new_block_def, result_var: new_result)
|
|
456
|
+
|
|
457
|
+
when HIR::ProcCall
|
|
458
|
+
new_result = inst.result_var ? prefix + inst.result_var : nil
|
|
459
|
+
new_proc = transform_value(inst.proc_value, prefix, param_map)
|
|
460
|
+
new_args = inst.args.map { |a| transform_value(a, prefix, param_map) }
|
|
461
|
+
HIR::ProcCall.new(proc_value: new_proc, args: new_args, type: inst.type, result_var: new_result)
|
|
462
|
+
|
|
441
463
|
else
|
|
442
464
|
# For other instructions, just return as-is with renamed result
|
|
443
465
|
inst
|
|
@@ -481,6 +503,90 @@ module Konpeito
|
|
|
481
503
|
value
|
|
482
504
|
end
|
|
483
505
|
end
|
|
506
|
+
|
|
507
|
+
# Deep-clone a BlockDef, renaming captured (non-block-local) variables.
|
|
508
|
+
# Block parameters are NOT renamed (they are local to the closure).
|
|
509
|
+
def clone_block_def(block_def, prefix, param_map)
|
|
510
|
+
return block_def unless block_def
|
|
511
|
+
|
|
512
|
+
block_param_names = Set.new(block_def.params.map { |p| p.name.to_s })
|
|
513
|
+
|
|
514
|
+
new_body = block_def.body.map do |bb|
|
|
515
|
+
if bb.respond_to?(:instructions)
|
|
516
|
+
new_bb = HIR::BasicBlock.new(label: bb.label)
|
|
517
|
+
bb.instructions.each do |bi|
|
|
518
|
+
new_inst = clone_block_instruction(bi, prefix, param_map, block_param_names)
|
|
519
|
+
new_bb.add_instruction(new_inst) if new_inst
|
|
520
|
+
end
|
|
521
|
+
new_bb.set_terminator(bb.terminator) if bb.terminator
|
|
522
|
+
new_bb
|
|
523
|
+
else
|
|
524
|
+
clone_block_instruction(bb, prefix, param_map, block_param_names)
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
HIR::BlockDef.new(
|
|
529
|
+
params: block_def.params,
|
|
530
|
+
body: new_body,
|
|
531
|
+
captures: block_def.captures,
|
|
532
|
+
is_lambda: block_def.is_lambda
|
|
533
|
+
)
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
# Clone a single instruction inside a block body, renaming captured vars.
|
|
537
|
+
def clone_block_instruction(inst, prefix, param_map, block_param_names)
|
|
538
|
+
case inst
|
|
539
|
+
when HIR::LoadLocal
|
|
540
|
+
var_name = inst.var.name.to_s
|
|
541
|
+
if block_param_names.include?(var_name)
|
|
542
|
+
# Block-local parameter: don't rename
|
|
543
|
+
inst
|
|
544
|
+
else
|
|
545
|
+
# Captured variable: rename with prefix
|
|
546
|
+
new_var = HIR::LocalVar.new(
|
|
547
|
+
name: prefix + var_name,
|
|
548
|
+
type: inst.var.type
|
|
549
|
+
)
|
|
550
|
+
HIR::LoadLocal.new(var: new_var, type: inst.type, result_var: inst.result_var)
|
|
551
|
+
end
|
|
552
|
+
when HIR::StoreLocal
|
|
553
|
+
var_name = inst.var.name.to_s
|
|
554
|
+
new_value = clone_block_value(inst.value, prefix, param_map, block_param_names)
|
|
555
|
+
if block_param_names.include?(var_name)
|
|
556
|
+
HIR::StoreLocal.new(var: inst.var, value: new_value, type: inst.type)
|
|
557
|
+
else
|
|
558
|
+
new_var = HIR::LocalVar.new(name: prefix + var_name, type: inst.var.type)
|
|
559
|
+
HIR::StoreLocal.new(var: new_var, value: new_value, type: inst.type)
|
|
560
|
+
end
|
|
561
|
+
when HIR::Call
|
|
562
|
+
new_receiver = clone_block_value(inst.receiver, prefix, param_map, block_param_names)
|
|
563
|
+
new_args = inst.args.map { |a| clone_block_value(a, prefix, param_map, block_param_names) }
|
|
564
|
+
HIR::Call.new(
|
|
565
|
+
receiver: new_receiver, method_name: inst.method_name,
|
|
566
|
+
args: new_args, block: inst.block,
|
|
567
|
+
type: inst.type, result_var: inst.result_var
|
|
568
|
+
)
|
|
569
|
+
else
|
|
570
|
+
# Other instructions: return as-is (literals, etc.)
|
|
571
|
+
inst
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
# Clone a value reference inside a block, renaming captured variables.
|
|
576
|
+
def clone_block_value(value, prefix, param_map, block_param_names)
|
|
577
|
+
case value
|
|
578
|
+
when HIR::LoadLocal
|
|
579
|
+
var_name = value.var.name.to_s
|
|
580
|
+
if block_param_names.include?(var_name)
|
|
581
|
+
value
|
|
582
|
+
else
|
|
583
|
+
new_var = HIR::LocalVar.new(name: prefix + var_name, type: value.var.type)
|
|
584
|
+
HIR::LoadLocal.new(var: new_var, type: value.type, result_var: nil)
|
|
585
|
+
end
|
|
586
|
+
else
|
|
587
|
+
value
|
|
588
|
+
end
|
|
589
|
+
end
|
|
484
590
|
end
|
|
485
591
|
end
|
|
486
592
|
end
|
|
@@ -3278,6 +3278,24 @@ module Konpeito
|
|
|
3278
3278
|
instructions << { "op" => "invokestatic", "owner" => "java/lang/Boolean",
|
|
3279
3279
|
"name" => "valueOf", "descriptor" => "(Z)Ljava/lang/Boolean;" }
|
|
3280
3280
|
type = :value
|
|
3281
|
+
# Unbox Object to match expected primitive return type (static methods only).
|
|
3282
|
+
# Instance method descriptors are always normalized to Object return, so
|
|
3283
|
+
# unboxing would be incorrect — the value might not be the expected wrapper type.
|
|
3284
|
+
elsif !@generating_instance_method && @current_function_return_type == :double && type == :value
|
|
3285
|
+
instructions << { "op" => "checkcast", "type" => "java/lang/Double" }
|
|
3286
|
+
instructions << { "op" => "invokevirtual", "owner" => "java/lang/Double",
|
|
3287
|
+
"name" => "doubleValue", "descriptor" => "()D" }
|
|
3288
|
+
type = :double
|
|
3289
|
+
elsif !@generating_instance_method && @current_function_return_type == :i64 && type == :value
|
|
3290
|
+
instructions << { "op" => "checkcast", "type" => "java/lang/Long" }
|
|
3291
|
+
instructions << { "op" => "invokevirtual", "owner" => "java/lang/Long",
|
|
3292
|
+
"name" => "longValue", "descriptor" => "()J" }
|
|
3293
|
+
type = :i64
|
|
3294
|
+
elsif !@generating_instance_method && @current_function_return_type == :i8 && type == :value
|
|
3295
|
+
instructions << { "op" => "checkcast", "type" => "java/lang/Boolean" }
|
|
3296
|
+
instructions << { "op" => "invokevirtual", "owner" => "java/lang/Boolean",
|
|
3297
|
+
"name" => "booleanValue", "descriptor" => "()Z" }
|
|
3298
|
+
type = :i8
|
|
3281
3299
|
end
|
|
3282
3300
|
|
|
3283
3301
|
instructions << case type
|
|
@@ -4029,6 +4047,33 @@ module Konpeito
|
|
|
4029
4047
|
|
|
4030
4048
|
@method_descriptors[key] = "(#{params_desc})#{type_to_descriptor(ret_type)}"
|
|
4031
4049
|
end
|
|
4050
|
+
|
|
4051
|
+
# Pre-register singleton (class) method descriptors
|
|
4052
|
+
(class_def.singleton_methods || []).each do |method_name|
|
|
4053
|
+
key = "#{class_name}.#{method_name}"
|
|
4054
|
+
next if @method_descriptors.key?(key)
|
|
4055
|
+
|
|
4056
|
+
func = find_class_singleton_method(class_name, method_name.to_s)
|
|
4057
|
+
next unless func
|
|
4058
|
+
|
|
4059
|
+
rbs_param_types = resolve_rbs_param_types(class_name, method_name.to_s, true)
|
|
4060
|
+
params_desc = func.params.each_with_index.map do |p, i|
|
|
4061
|
+
rbs_t = rbs_param_types && i < rbs_param_types.size ? rbs_param_types[i] : nil
|
|
4062
|
+
t = (rbs_t && rbs_t != :value) ? rbs_t : param_type(p)
|
|
4063
|
+
t = :value if t == :void
|
|
4064
|
+
type_to_descriptor(t)
|
|
4065
|
+
end.join
|
|
4066
|
+
|
|
4067
|
+
rbs_ret = resolve_rbs_return_type(class_name, method_name.to_s, true)
|
|
4068
|
+
if rbs_ret && rbs_ret != :value
|
|
4069
|
+
ret_type = rbs_ret
|
|
4070
|
+
else
|
|
4071
|
+
frt = function_return_type(func)
|
|
4072
|
+
ret_type = (frt == :void) ? :value : frt
|
|
4073
|
+
end
|
|
4074
|
+
|
|
4075
|
+
@method_descriptors[key] = "(#{params_desc})#{type_to_descriptor(ret_type)}"
|
|
4076
|
+
end
|
|
4032
4077
|
end
|
|
4033
4078
|
|
|
4034
4079
|
def resolve_class_fields(class_def)
|
|
@@ -4407,9 +4452,17 @@ module Konpeito
|
|
|
4407
4452
|
@current_generating_func_name = func.name.to_s
|
|
4408
4453
|
reset_function_state(func)
|
|
4409
4454
|
|
|
4410
|
-
# Pre-determine return type so generate_return knows if method returns Object
|
|
4411
|
-
|
|
4412
|
-
|
|
4455
|
+
# Pre-determine return type so generate_return knows if method returns Object.
|
|
4456
|
+
# If a descriptor was pre-registered (from RBS), use its return type to ensure
|
|
4457
|
+
# the generated return instruction matches the pre-registered descriptor.
|
|
4458
|
+
pre_registered = @method_descriptors["#{class_def.name}.#{func.name}"]
|
|
4459
|
+
if pre_registered
|
|
4460
|
+
pre_ret = parse_descriptor_return_type(pre_registered)
|
|
4461
|
+
else
|
|
4462
|
+
pre_ret = function_return_type(func)
|
|
4463
|
+
pre_ret = :value if pre_ret == :void
|
|
4464
|
+
end
|
|
4465
|
+
@current_function_return_type = pre_ret
|
|
4413
4466
|
|
|
4414
4467
|
# Try to resolve param types from RBS if HIR types are unresolved
|
|
4415
4468
|
rbs_param_types = resolve_rbs_param_types(class_def.name.to_s, func.name.to_s, true)
|
|
@@ -4435,9 +4488,36 @@ module Konpeito
|
|
|
4435
4488
|
instructions << default_return(ret_type)
|
|
4436
4489
|
end
|
|
4437
4490
|
|
|
4438
|
-
# Build descriptor
|
|
4439
|
-
|
|
4440
|
-
descriptor
|
|
4491
|
+
# Build descriptor: if pre-registered, use that descriptor directly for consistency.
|
|
4492
|
+
# The pre-registered descriptor was computed from param_type/RBS before code gen,
|
|
4493
|
+
# matching what the call site will use. The code-gen-derived descriptor may differ
|
|
4494
|
+
# because detect_return_type_from_instructions can miss returns in branching code.
|
|
4495
|
+
if pre_registered
|
|
4496
|
+
descriptor = pre_registered
|
|
4497
|
+
# Ensure final return instruction matches the pre-registered return type
|
|
4498
|
+
pre_ret_type = parse_descriptor_return_type(pre_registered)
|
|
4499
|
+
if pre_ret_type != :void && pre_ret_type != :value
|
|
4500
|
+
# Replace trailing void return with typed default return if needed
|
|
4501
|
+
if instructions.last && instructions.last["op"] == "return"
|
|
4502
|
+
instructions.pop
|
|
4503
|
+
case pre_ret_type
|
|
4504
|
+
when :i64
|
|
4505
|
+
instructions << { "op" => "lconst_0" }
|
|
4506
|
+
instructions << { "op" => "lreturn" }
|
|
4507
|
+
when :double
|
|
4508
|
+
instructions << { "op" => "dconst_0" }
|
|
4509
|
+
instructions << { "op" => "dreturn" }
|
|
4510
|
+
when :i8
|
|
4511
|
+
instructions << { "op" => "iconst_0" }
|
|
4512
|
+
instructions << { "op" => "ireturn" }
|
|
4513
|
+
end
|
|
4514
|
+
end
|
|
4515
|
+
end
|
|
4516
|
+
else
|
|
4517
|
+
# Build descriptor from actual param types (which may have been corrected from RBS)
|
|
4518
|
+
params_desc = func.params.map { |p| type_to_descriptor(@variable_types[p.name.to_s] || :value) }.join
|
|
4519
|
+
descriptor = "(#{params_desc})#{type_to_descriptor(ret_type)}"
|
|
4520
|
+
end
|
|
4441
4521
|
|
|
4442
4522
|
# When renaming for conflict avoidance, use prefixed JVM name
|
|
4443
4523
|
jvm_name = if rename_prefix
|
|
@@ -5396,6 +5476,10 @@ module Konpeito
|
|
|
5396
5476
|
|
|
5397
5477
|
return [] unless target_func
|
|
5398
5478
|
|
|
5479
|
+
# Look up the registered descriptor first to determine expected param types
|
|
5480
|
+
registered = @method_descriptors["#{class_name}.#{method_name}"]
|
|
5481
|
+
registered_param_types = registered ? parse_descriptor_param_types(registered) : nil
|
|
5482
|
+
|
|
5399
5483
|
# Resolve param types: prefer RBS types over HIR types (which may be unresolved TypeVars)
|
|
5400
5484
|
rbs_param_types = resolve_rbs_param_types(class_name, method_name, true)
|
|
5401
5485
|
|
|
@@ -5416,18 +5500,17 @@ module Konpeito
|
|
|
5416
5500
|
end
|
|
5417
5501
|
arg_types << :hash
|
|
5418
5502
|
elsif i < args.size
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
end
|
|
5503
|
+
# Use registered descriptor's param type if available (most accurate)
|
|
5504
|
+
param_t = if registered_param_types && i < registered_param_types.size
|
|
5505
|
+
registered_param_types[i]
|
|
5506
|
+
else
|
|
5507
|
+
pt = param_type(param)
|
|
5508
|
+
if pt == :value && rbs_param_types && i < rbs_param_types.size && rbs_param_types[i] != :value
|
|
5509
|
+
rbs_param_types[i]
|
|
5510
|
+
else
|
|
5511
|
+
pt
|
|
5512
|
+
end
|
|
5513
|
+
end
|
|
5431
5514
|
instructions.concat(load_value(args[i], param_t))
|
|
5432
5515
|
arg_types << param_t
|
|
5433
5516
|
else
|
|
@@ -5439,7 +5522,6 @@ module Konpeito
|
|
|
5439
5522
|
end
|
|
5440
5523
|
|
|
5441
5524
|
# Build descriptor — prefer registered descriptor for consistency
|
|
5442
|
-
registered = @method_descriptors["#{class_name}.#{method_name}"]
|
|
5443
5525
|
if registered
|
|
5444
5526
|
descriptor = registered
|
|
5445
5527
|
ret_type = parse_descriptor_return_type(registered)
|
data/lib/konpeito/hir/builder.rb
CHANGED
|
@@ -4038,13 +4038,15 @@ module Konpeito
|
|
|
4038
4038
|
key_var = param_names[0] || "k"
|
|
4039
4039
|
value_var = param_names[1] || "v"
|
|
4040
4040
|
|
|
4041
|
-
# Build block body
|
|
4041
|
+
# Build block body — save/restore @current_block so NativeHashEach
|
|
4042
|
+
# is emitted into the original block, not the loop body block
|
|
4043
|
+
saved_block = @current_block
|
|
4042
4044
|
body_child = block_child.children.find { |c| c.node_type == :statements }
|
|
4043
4045
|
block_body = []
|
|
4044
4046
|
|
|
4045
4047
|
if body_child
|
|
4046
4048
|
# Create a new basic block for the iteration body
|
|
4047
|
-
loop_bb = BasicBlock.new(label:
|
|
4049
|
+
loop_bb = BasicBlock.new(label: "hash_each_body_#{@block_counter}")
|
|
4048
4050
|
@current_block = loop_bb
|
|
4049
4051
|
block_body << loop_bb
|
|
4050
4052
|
|
|
@@ -4052,6 +4054,7 @@ module Konpeito
|
|
|
4052
4054
|
visit(stmt)
|
|
4053
4055
|
end
|
|
4054
4056
|
end
|
|
4057
|
+
@current_block = saved_block
|
|
4055
4058
|
|
|
4056
4059
|
result_var = new_temp_var
|
|
4057
4060
|
inst = NativeHashEach.new(
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* yyjson_wrapper.c - Non-inline wrappers for yyjson functions
|
|
3
|
+
*
|
|
4
|
+
* yyjson uses static inline functions in its header. When linking with
|
|
5
|
+
* LLVM-generated code, we need non-inline versions that can be called
|
|
6
|
+
* via external symbol references. This file provides those wrappers.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
#include "yyjson.h"
|
|
10
|
+
#include <stddef.h>
|
|
11
|
+
#include <stdint.h>
|
|
12
|
+
#include <stdbool.h>
|
|
13
|
+
|
|
14
|
+
yyjson_doc *konpeito_yyjson_read(const char *dat, size_t len, yyjson_read_flag flg) {
|
|
15
|
+
return yyjson_read(dat, len, flg);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
yyjson_val *konpeito_yyjson_doc_get_root(yyjson_doc *doc) {
|
|
19
|
+
return yyjson_doc_get_root(doc);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
void konpeito_yyjson_doc_free(yyjson_doc *doc) {
|
|
23
|
+
yyjson_doc_free(doc);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
yyjson_val *konpeito_yyjson_obj_get(yyjson_val *obj, const char *key) {
|
|
27
|
+
return yyjson_obj_get(obj, key);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
int64_t konpeito_yyjson_get_sint(yyjson_val *val) {
|
|
31
|
+
return yyjson_get_sint(val);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
uint64_t konpeito_yyjson_get_uint(yyjson_val *val) {
|
|
35
|
+
return yyjson_get_uint(val);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
double konpeito_yyjson_get_real(yyjson_val *val) {
|
|
39
|
+
return yyjson_get_real(val);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
bool konpeito_yyjson_get_bool(yyjson_val *val) {
|
|
43
|
+
return yyjson_get_bool(val);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const char *konpeito_yyjson_get_str(yyjson_val *val) {
|
|
47
|
+
return yyjson_get_str(val);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
size_t konpeito_yyjson_get_len(yyjson_val *val) {
|
|
51
|
+
return yyjson_get_len(val);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
size_t konpeito_yyjson_arr_size(yyjson_val *arr) {
|
|
55
|
+
return yyjson_arr_size(arr);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
yyjson_val *konpeito_yyjson_arr_get(yyjson_val *arr, size_t idx) {
|
|
59
|
+
return yyjson_arr_get(arr, idx);
|
|
60
|
+
}
|
data/lib/konpeito/version.rb
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Setup vendored dependencies for Konpeito
|
|
3
|
+
#
|
|
4
|
+
# Downloads yyjson (fast JSON library) source files.
|
|
5
|
+
# Run this after cloning the repository:
|
|
6
|
+
# bash scripts/setup_vendor.sh
|
|
7
|
+
|
|
8
|
+
set -e
|
|
9
|
+
|
|
10
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
11
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
12
|
+
YYJSON_DIR="$PROJECT_ROOT/vendor/yyjson"
|
|
13
|
+
|
|
14
|
+
YYJSON_VERSION="0.10.0"
|
|
15
|
+
YYJSON_BASE_URL="https://raw.githubusercontent.com/ibireme/yyjson/${YYJSON_VERSION}/src"
|
|
16
|
+
|
|
17
|
+
echo "Setting up vendored dependencies..."
|
|
18
|
+
|
|
19
|
+
# --- yyjson ---
|
|
20
|
+
mkdir -p "$YYJSON_DIR"
|
|
21
|
+
|
|
22
|
+
if [ -f "$YYJSON_DIR/yyjson.h" ] && [ -f "$YYJSON_DIR/yyjson.c" ]; then
|
|
23
|
+
echo " yyjson: already present (skipping download)"
|
|
24
|
+
else
|
|
25
|
+
echo " yyjson: downloading v${YYJSON_VERSION}..."
|
|
26
|
+
curl -sL "${YYJSON_BASE_URL}/yyjson.h" -o "$YYJSON_DIR/yyjson.h"
|
|
27
|
+
curl -sL "${YYJSON_BASE_URL}/yyjson.c" -o "$YYJSON_DIR/yyjson.c"
|
|
28
|
+
echo " yyjson: downloaded yyjson.h and yyjson.c"
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
echo ""
|
|
32
|
+
echo "Done. Vendor dependencies are ready."
|
|
33
|
+
echo " yyjson: $YYJSON_DIR"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: konpeito
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yasushi Itoh
|
|
@@ -131,6 +131,7 @@ files:
|
|
|
131
131
|
- lib/konpeito/stdlib/json/json.rb
|
|
132
132
|
- lib/konpeito/stdlib/json/json.rbs
|
|
133
133
|
- lib/konpeito/stdlib/json/json_native.c
|
|
134
|
+
- lib/konpeito/stdlib/json/yyjson_wrapper.c
|
|
134
135
|
- lib/konpeito/stdlib/ui/extconf.rb
|
|
135
136
|
- lib/konpeito/stdlib/ui/konpeito_ui_native.cpp
|
|
136
137
|
- lib/konpeito/stdlib/ui/konpeito_ui_native.h
|
|
@@ -202,6 +203,7 @@ files:
|
|
|
202
203
|
- lib/konpeito/ui/widgets/text.rb
|
|
203
204
|
- lib/konpeito/ui/widgets/tree.rb
|
|
204
205
|
- lib/konpeito/version.rb
|
|
206
|
+
- scripts/setup_vendor.sh
|
|
205
207
|
- test_native_array.rb
|
|
206
208
|
- test_native_array_class.rb
|
|
207
209
|
- test_native_class.rb
|