konpeito 0.1.2 → 0.1.3
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 +10 -0
- data/README.md +139 -1
- data/lib/konpeito/codegen/jvm_generator.rb +101 -19
- data/lib/konpeito/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b559f8a6de1d213b466939fb5fbde0a797a6808a7ce664f28d55ed0496696dde
|
|
4
|
+
data.tar.gz: 6809762994527a50fcf96c1644f24cde5f2324b668e1dc646bf32077ea679af0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c8c43cca2f5e3812ee706a9b2b2c833ad9036d98e997b4481ce701d0edfa98b03a967f935c2b6aec7b8b2dfb19d27225b0e7a6f5e091ecc993a0786cec3cc2d7
|
|
7
|
+
data.tar.gz: 27acd8361e403febf7fd4e45b5ab832f1c767deedf329b8e164c8b62f7f1aa127c0ea75eeccaee7d5ef6767ea7f45c787baa526461bfa6dbf6ba034f90815bff
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,15 @@ 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.1.3] - 2026-02-18
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- JVM backend: resolve class method descriptor mismatch when called from instance methods (pre-register singleton method descriptors)
|
|
12
|
+
- Documentation: correct JWM and Skija GitHub URLs in README
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- Documentation: Castella UI calculator demo with Style composition
|
|
16
|
+
|
|
8
17
|
## [0.1.2] - 2026-02-17
|
|
9
18
|
|
|
10
19
|
### Fixed
|
|
@@ -99,6 +108,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
99
108
|
- `%a{extern}` - external C struct wrappers
|
|
100
109
|
- `%a{simd}` - SIMD vectorization
|
|
101
110
|
|
|
111
|
+
[0.1.3]: https://github.com/i2y/konpeito/compare/v0.1.2...v0.1.3
|
|
102
112
|
[0.1.2]: https://github.com/i2y/konpeito/compare/v0.1.1...v0.1.2
|
|
103
113
|
[0.1.1]: https://github.com/i2y/konpeito/compare/v0.1.0...v0.1.1
|
|
104
114
|
[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.
|
|
@@ -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/version.rb
CHANGED