konpeito 0.2.2 → 0.2.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 +18 -0
- data/lib/konpeito/codegen/builtin_methods.rb +4 -2
- data/lib/konpeito/codegen/cruby_backend.rb +122 -10
- data/lib/konpeito/codegen/inliner.rb +9 -3
- data/lib/konpeito/codegen/jvm_generator.rb +3986 -919
- data/lib/konpeito/codegen/llvm_generator.rb +334 -45
- data/lib/konpeito/codegen/monomorphizer.rb +14 -2
- data/lib/konpeito/hir/builder.rb +150 -20
- data/lib/konpeito/hir/nodes.rb +16 -0
- data/lib/konpeito/type_checker/hm_inferrer.rb +100 -41
- data/lib/konpeito/type_checker/types.rb +6 -6
- data/lib/konpeito/type_checker/unification.rb +8 -1
- data/lib/konpeito/version.rb +1 -1
- data/tools/konpeito-asm/build.sh +1 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KArray.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KFiber.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KMatchData.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KRubyException.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class +0 -0
- data/tools/konpeito-asm/runtime-classes/konpeito/runtime/RubyDispatch.class +0 -0
- data/tools/konpeito-asm/src/KonpeitoAssembler.java +17 -1
- data/tools/konpeito-asm/src/konpeito/runtime/KArray.java +97 -4
- data/tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java +20 -25
- data/tools/konpeito-asm/src/konpeito/runtime/KFiber.java +112 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KHash.java +67 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KMatchData.java +55 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KRubyException.java +79 -0
- data/tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java +5 -0
- data/tools/konpeito-asm/src/konpeito/runtime/RubyDispatch.java +285 -19
- metadata +7 -1
|
@@ -102,18 +102,88 @@ public class RubyDispatch {
|
|
|
102
102
|
Object[] methodArgs = new Object[args.length - 1];
|
|
103
103
|
System.arraycopy(args, 1, methodArgs, 0, methodArgs.length);
|
|
104
104
|
|
|
105
|
-
// Handle nil (null) receiver — Ruby's NilClass methods
|
|
105
|
+
// Handle nil (null) receiver — Ruby's NilClass methods and Kernel methods
|
|
106
106
|
if (receiver == null) {
|
|
107
107
|
Object nilResult = tryNilDispatch(methodName, methodArgs);
|
|
108
108
|
if (nilResult != SENTINEL) {
|
|
109
109
|
return nilResult;
|
|
110
110
|
}
|
|
111
|
+
// Kernel methods (top-level functions with null receiver)
|
|
112
|
+
if (methodName.equals("Integer") && methodArgs.length >= 1) {
|
|
113
|
+
Object arg = methodArgs[0];
|
|
114
|
+
if (arg instanceof Long) return arg;
|
|
115
|
+
if (arg instanceof Double) return Long.valueOf(((Double) arg).longValue());
|
|
116
|
+
if (arg instanceof String) {
|
|
117
|
+
String s = ((String) arg).strip();
|
|
118
|
+
if (s.startsWith("0x") || s.startsWith("0X")) {
|
|
119
|
+
return Long.parseLong(s.substring(2), 16);
|
|
120
|
+
}
|
|
121
|
+
return Long.parseLong(s);
|
|
122
|
+
}
|
|
123
|
+
throw new IllegalArgumentException("invalid value for Integer(): \"" + arg + "\"");
|
|
124
|
+
}
|
|
125
|
+
if (methodName.equals("Float") && methodArgs.length >= 1) {
|
|
126
|
+
Object arg = methodArgs[0];
|
|
127
|
+
if (arg instanceof Double) return arg;
|
|
128
|
+
if (arg instanceof Long) return Double.valueOf(((Long) arg).doubleValue());
|
|
129
|
+
if (arg instanceof String) {
|
|
130
|
+
String s = ((String) arg).strip();
|
|
131
|
+
return Double.parseDouble(s);
|
|
132
|
+
}
|
|
133
|
+
throw new IllegalArgumentException("invalid value for Float(): \"" + arg + "\"");
|
|
134
|
+
}
|
|
135
|
+
// Kernel#sleep
|
|
136
|
+
if (methodName.equals("sleep") && methodArgs.length >= 1) {
|
|
137
|
+
double seconds;
|
|
138
|
+
if (methodArgs[0] instanceof Long) {
|
|
139
|
+
seconds = ((Long) methodArgs[0]).doubleValue();
|
|
140
|
+
} else if (methodArgs[0] instanceof Double) {
|
|
141
|
+
seconds = (Double) methodArgs[0];
|
|
142
|
+
} else if (methodArgs[0] instanceof Number) {
|
|
143
|
+
seconds = ((Number) methodArgs[0]).doubleValue();
|
|
144
|
+
} else {
|
|
145
|
+
seconds = 0;
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
Thread.sleep((long) (seconds * 1000));
|
|
149
|
+
} catch (InterruptedException e) {
|
|
150
|
+
Thread.currentThread().interrupt();
|
|
151
|
+
}
|
|
152
|
+
return Long.valueOf((long) seconds);
|
|
153
|
+
}
|
|
154
|
+
// Kernel#rand
|
|
155
|
+
if (methodName.equals("rand")) {
|
|
156
|
+
if (methodArgs.length == 0) {
|
|
157
|
+
return Math.random();
|
|
158
|
+
} else if (methodArgs[0] instanceof Long) {
|
|
159
|
+
return Long.valueOf((long)(Math.random() * (Long) methodArgs[0]));
|
|
160
|
+
}
|
|
161
|
+
return Math.random();
|
|
162
|
+
}
|
|
111
163
|
throw new NullPointerException("Method '" + methodName + "' called on null receiver");
|
|
112
164
|
}
|
|
113
165
|
|
|
114
166
|
Class<?> clazz = receiver.getClass();
|
|
115
167
|
|
|
116
168
|
// Handle Ruby exception methods on Java Throwable/Exception objects
|
|
169
|
+
if (receiver instanceof KRubyException) {
|
|
170
|
+
KRubyException kexc = (KRubyException) receiver;
|
|
171
|
+
switch (methodName) {
|
|
172
|
+
case "message":
|
|
173
|
+
String kmsg = kexc.getMessage();
|
|
174
|
+
return kmsg != null ? kmsg : "";
|
|
175
|
+
case "to_s":
|
|
176
|
+
String kts = kexc.getMessage();
|
|
177
|
+
return kts != null ? kts : kexc.getRubyClassName();
|
|
178
|
+
case "k_class":
|
|
179
|
+
return kexc.getRubyClassName();
|
|
180
|
+
case "inspect":
|
|
181
|
+
String kim = kexc.getMessage();
|
|
182
|
+
return "#<" + kexc.getRubyClassName() + ": " + (kim != null ? kim : "") + ">";
|
|
183
|
+
case "backtrace":
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
117
187
|
if (receiver instanceof Throwable) {
|
|
118
188
|
Throwable throwable = (Throwable) receiver;
|
|
119
189
|
switch (methodName) {
|
|
@@ -221,6 +291,29 @@ public class RubyDispatch {
|
|
|
221
291
|
return arr[idx];
|
|
222
292
|
}
|
|
223
293
|
|
|
294
|
+
// Comparable methods: between?, clamp — delegate to <=> if available
|
|
295
|
+
if (methodName.equals("between_q") && methodArgs.length == 2) {
|
|
296
|
+
Method spaceship = findMethod(clazz, "op_cmp", 1);
|
|
297
|
+
if (spaceship != null) {
|
|
298
|
+
spaceship.setAccessible(true);
|
|
299
|
+
long cmpLo = ((Number) spaceship.invoke(receiver, methodArgs[0])).longValue();
|
|
300
|
+
long cmpHi = ((Number) spaceship.invoke(receiver, methodArgs[1])).longValue();
|
|
301
|
+
return Boolean.valueOf(cmpLo >= 0 && cmpHi <= 0);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (methodName.equals("clamp") && methodArgs.length == 2) {
|
|
306
|
+
Method spaceship = findMethod(clazz, "op_cmp", 1);
|
|
307
|
+
if (spaceship != null) {
|
|
308
|
+
spaceship.setAccessible(true);
|
|
309
|
+
long cmpLo = ((Number) spaceship.invoke(receiver, methodArgs[0])).longValue();
|
|
310
|
+
if (cmpLo < 0) return methodArgs[0]; // below min → return min
|
|
311
|
+
long cmpHi = ((Number) spaceship.invoke(receiver, methodArgs[1])).longValue();
|
|
312
|
+
if (cmpHi > 0) return methodArgs[1]; // above max → return max
|
|
313
|
+
return receiver; // in range → return self
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
224
317
|
throw new NoSuchMethodError(
|
|
225
318
|
clazz.getName() + "." + methodName + " (arity " + methodArgs.length + ")");
|
|
226
319
|
}
|
|
@@ -334,6 +427,7 @@ public class RubyDispatch {
|
|
|
334
427
|
case "op_xor": return Long.valueOf(lv ^ rv);
|
|
335
428
|
case "op_lshift": return Long.valueOf(lv << rv);
|
|
336
429
|
case "op_rshift": return Long.valueOf(lv >> rv);
|
|
430
|
+
case "op_aref": return Long.valueOf((lv >> rv) & 1);
|
|
337
431
|
case "op_eq": return Boolean.valueOf(lv == rv);
|
|
338
432
|
case "op_neq": return Boolean.valueOf(lv != rv);
|
|
339
433
|
case "op_lt": return Boolean.valueOf(lv < rv);
|
|
@@ -369,6 +463,17 @@ public class RubyDispatch {
|
|
|
369
463
|
case "integer_q": return Boolean.TRUE;
|
|
370
464
|
case "op_uminus": return Long.valueOf(-lv);
|
|
371
465
|
case "op_uplus": return receiver;
|
|
466
|
+
case "digits": {
|
|
467
|
+
// Integer#digits — returns array of digits in reverse order (base 10)
|
|
468
|
+
KArray<Object> result = new KArray<>();
|
|
469
|
+
long val = Math.abs(lv);
|
|
470
|
+
if (val == 0) { result.add(0L); return result; }
|
|
471
|
+
while (val > 0) {
|
|
472
|
+
result.add(Long.valueOf(val % 10));
|
|
473
|
+
val /= 10;
|
|
474
|
+
}
|
|
475
|
+
return result;
|
|
476
|
+
}
|
|
372
477
|
}
|
|
373
478
|
}
|
|
374
479
|
}
|
|
@@ -603,6 +708,16 @@ public class RubyDispatch {
|
|
|
603
708
|
}
|
|
604
709
|
}
|
|
605
710
|
|
|
711
|
+
// String#split(separator, limit) — split with limit
|
|
712
|
+
if (methodName.equals("split") && args.length == 2 && args[0] instanceof String && args[1] instanceof Number) {
|
|
713
|
+
String pattern = (String) args[0];
|
|
714
|
+
int limit = ((Number) args[1]).intValue();
|
|
715
|
+
String[] parts = sv.split(java.util.regex.Pattern.quote(pattern), limit);
|
|
716
|
+
KArray<Object> splitArr = new KArray<>();
|
|
717
|
+
for (String part : parts) splitArr.push(part);
|
|
718
|
+
return splitArr;
|
|
719
|
+
}
|
|
720
|
+
|
|
606
721
|
// String#tr(from, to) — 2 string args
|
|
607
722
|
if (methodName.equals("tr") && args.length == 2 && args[0] instanceof String && args[1] instanceof String) {
|
|
608
723
|
return stringTr(sv, (String) args[0], (String) args[1]);
|
|
@@ -865,7 +980,7 @@ public class RubyDispatch {
|
|
|
865
980
|
case "clone": return sv;
|
|
866
981
|
case "k_class": return "String";
|
|
867
982
|
case "nil_q": return Boolean.FALSE;
|
|
868
|
-
case "frozen_q": return Boolean.
|
|
983
|
+
case "frozen_q": return Boolean.FALSE; // Strings are not frozen by default in Ruby
|
|
869
984
|
case "to_a": {
|
|
870
985
|
// Range#to_a — ranges are stored as strings "start..end" or "start...end"
|
|
871
986
|
return rangeToArray(sv);
|
|
@@ -1355,6 +1470,86 @@ public class RubyDispatch {
|
|
|
1355
1470
|
}
|
|
1356
1471
|
break;
|
|
1357
1472
|
}
|
|
1473
|
+
case "group_by": {
|
|
1474
|
+
if (args.length == 1) {
|
|
1475
|
+
KHash<Object, Object> result = new KHash<>();
|
|
1476
|
+
for (Object elem : arr) {
|
|
1477
|
+
Object key = invokeBlock(args[0], elem);
|
|
1478
|
+
result.groupByAdd(key, elem);
|
|
1479
|
+
}
|
|
1480
|
+
return result;
|
|
1481
|
+
}
|
|
1482
|
+
break;
|
|
1483
|
+
}
|
|
1484
|
+
case "each_with_object": {
|
|
1485
|
+
if (args.length == 2) {
|
|
1486
|
+
// args[0] = memo object, args[1] = block
|
|
1487
|
+
Object memo = args[0];
|
|
1488
|
+
Object block = args[1];
|
|
1489
|
+
for (Object elem : arr) {
|
|
1490
|
+
invokeBlock(block, elem, memo);
|
|
1491
|
+
}
|
|
1492
|
+
return memo;
|
|
1493
|
+
}
|
|
1494
|
+
break;
|
|
1495
|
+
}
|
|
1496
|
+
case "take_while": {
|
|
1497
|
+
if (args.length == 1) {
|
|
1498
|
+
KArray<Object> result = new KArray<>();
|
|
1499
|
+
for (Object elem : arr) {
|
|
1500
|
+
if (!isTruthy(invokeBlock(args[0], elem))) break;
|
|
1501
|
+
result.push(elem);
|
|
1502
|
+
}
|
|
1503
|
+
return result;
|
|
1504
|
+
}
|
|
1505
|
+
break;
|
|
1506
|
+
}
|
|
1507
|
+
case "drop_while": {
|
|
1508
|
+
if (args.length == 1) {
|
|
1509
|
+
KArray<Object> result = new KArray<>();
|
|
1510
|
+
boolean dropping = true;
|
|
1511
|
+
for (Object elem : arr) {
|
|
1512
|
+
if (dropping && isTruthy(invokeBlock(args[0], elem))) continue;
|
|
1513
|
+
dropping = false;
|
|
1514
|
+
result.push(elem);
|
|
1515
|
+
}
|
|
1516
|
+
return result;
|
|
1517
|
+
}
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
case "partition": {
|
|
1521
|
+
if (args.length == 1) {
|
|
1522
|
+
KArray<Object> trueArr = new KArray<>();
|
|
1523
|
+
KArray<Object> falseArr = new KArray<>();
|
|
1524
|
+
for (Object elem : arr) {
|
|
1525
|
+
if (isTruthy(invokeBlock(args[0], elem))) {
|
|
1526
|
+
trueArr.push(elem);
|
|
1527
|
+
} else {
|
|
1528
|
+
falseArr.push(elem);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
KArray<Object> result = new KArray<>();
|
|
1532
|
+
result.push(trueArr);
|
|
1533
|
+
result.push(falseArr);
|
|
1534
|
+
return result;
|
|
1535
|
+
}
|
|
1536
|
+
break;
|
|
1537
|
+
}
|
|
1538
|
+
case "tally": {
|
|
1539
|
+
if (args.length == 0) {
|
|
1540
|
+
KHash<Object, Object> result = new KHash<>();
|
|
1541
|
+
for (Object elem : arr) {
|
|
1542
|
+
Object count = result.get(elem);
|
|
1543
|
+
if (count instanceof Long) {
|
|
1544
|
+
result.put(elem, (Long) count + 1L);
|
|
1545
|
+
} else {
|
|
1546
|
+
result.put(elem, 1L);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return result;
|
|
1550
|
+
}
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1358
1553
|
case "sum": {
|
|
1359
1554
|
if (args.length == 0) return arr.sumLong();
|
|
1360
1555
|
break;
|
|
@@ -1369,8 +1564,8 @@ public class RubyDispatch {
|
|
|
1369
1564
|
}
|
|
1370
1565
|
return Long.valueOf(arr.size());
|
|
1371
1566
|
}
|
|
1372
|
-
case "frozen_q": return Boolean.
|
|
1373
|
-
case "freeze": return arr;
|
|
1567
|
+
case "frozen_q": return Boolean.valueOf(arr.isFrozen());
|
|
1568
|
+
case "freeze": { arr.freeze(); return arr; }
|
|
1374
1569
|
}
|
|
1375
1570
|
}
|
|
1376
1571
|
|
|
@@ -1613,10 +1808,9 @@ public class RubyDispatch {
|
|
|
1613
1808
|
entries.push(pair);
|
|
1614
1809
|
}
|
|
1615
1810
|
entries.sort((a, b) -> {
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
Object
|
|
1619
|
-
Object kb = invokeBlock(args[0], pb.get(0), pb.get(1));
|
|
1811
|
+
// Pass pair as single arg (Ruby Hash#sort_by yields [k,v] pair)
|
|
1812
|
+
Object ka = invokeBlock(args[0], a);
|
|
1813
|
+
Object kb = invokeBlock(args[0], b);
|
|
1620
1814
|
if (ka instanceof Comparable && kb instanceof Comparable) {
|
|
1621
1815
|
return ((Comparable) ka).compareTo(kb);
|
|
1622
1816
|
}
|
|
@@ -1690,17 +1884,32 @@ public class RubyDispatch {
|
|
|
1690
1884
|
// General respond_to?
|
|
1691
1885
|
if (methodName.equals("respond_to_q") && args.length == 1) {
|
|
1692
1886
|
String checkName = String.valueOf(args[0]);
|
|
1693
|
-
|
|
1694
|
-
|
|
1887
|
+
// Try multiple arities (0, 1, 2) since Ruby methods have various arities
|
|
1888
|
+
for (int arity = 0; arity <= 2; arity++) {
|
|
1889
|
+
Method found2 = findMethod(receiver.getClass(), checkName, arity);
|
|
1890
|
+
if (found2 != null) return Boolean.TRUE;
|
|
1695
1891
|
String[] aliases2 = RUBY_NAME_ALIASES.get(checkName);
|
|
1696
1892
|
if (aliases2 != null) {
|
|
1697
1893
|
for (String alias : aliases2) {
|
|
1698
|
-
found2 = findMethod(receiver.getClass(), alias,
|
|
1699
|
-
if (found2 != null)
|
|
1894
|
+
found2 = findMethod(receiver.getClass(), alias, arity);
|
|
1895
|
+
if (found2 != null) return Boolean.TRUE;
|
|
1700
1896
|
}
|
|
1701
1897
|
}
|
|
1702
1898
|
}
|
|
1703
|
-
|
|
1899
|
+
// Check well-known built-in methods handled in tryBuiltinOperator
|
|
1900
|
+
if (receiver instanceof KArray) {
|
|
1901
|
+
switch (checkName) {
|
|
1902
|
+
case "push": case "pop": case "shift": case "unshift": case "first":
|
|
1903
|
+
case "last": case "length": case "size": case "empty_q": case "include_q":
|
|
1904
|
+
case "sort": case "reverse": case "flatten": case "compact": case "uniq":
|
|
1905
|
+
case "each": case "map": case "select": case "reject": case "reduce":
|
|
1906
|
+
case "find": case "any_q": case "all_q": case "none_q": case "join":
|
|
1907
|
+
case "min": case "max": case "count": case "delete": case "index":
|
|
1908
|
+
case "freeze": case "frozen_q":
|
|
1909
|
+
return Boolean.TRUE;
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
return Boolean.FALSE;
|
|
1704
1913
|
}
|
|
1705
1914
|
|
|
1706
1915
|
return SENTINEL;
|
|
@@ -1728,6 +1937,9 @@ public class RubyDispatch {
|
|
|
1728
1937
|
if (obj instanceof KArray) return "Array";
|
|
1729
1938
|
if (obj instanceof KHash) return "Hash";
|
|
1730
1939
|
if (obj instanceof java.util.regex.Pattern) return "Regexp";
|
|
1940
|
+
if (obj instanceof KFiber) return "Fiber";
|
|
1941
|
+
if (obj instanceof KThread) return "Thread";
|
|
1942
|
+
if (obj instanceof java.util.concurrent.locks.ReentrantLock) return "Mutex";
|
|
1731
1943
|
// User-defined class: use simple name
|
|
1732
1944
|
String name = obj.getClass().getSimpleName();
|
|
1733
1945
|
return name;
|
|
@@ -2083,16 +2295,35 @@ public class RubyDispatch {
|
|
|
2083
2295
|
private static Object invokeBlock(Object block, Object... blockArgs) {
|
|
2084
2296
|
if (block == null) return null;
|
|
2085
2297
|
try {
|
|
2086
|
-
//
|
|
2298
|
+
// Collect all `call` methods (both from getMethods and getDeclaredMethods)
|
|
2299
|
+
java.util.List<Method> callMethods = new java.util.ArrayList<>();
|
|
2087
2300
|
for (Method m : block.getClass().getMethods()) {
|
|
2088
|
-
if (m.getName().equals("call")
|
|
2301
|
+
if (m.getName().equals("call")) callMethods.add(m);
|
|
2302
|
+
}
|
|
2303
|
+
for (Method m : block.getClass().getDeclaredMethods()) {
|
|
2304
|
+
if (m.getName().equals("call")) {
|
|
2305
|
+
boolean duplicate = false;
|
|
2306
|
+
for (Method existing : callMethods) {
|
|
2307
|
+
if (java.util.Arrays.equals(existing.getParameterTypes(), m.getParameterTypes())) {
|
|
2308
|
+
duplicate = true;
|
|
2309
|
+
break;
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
if (!duplicate) callMethods.add(m);
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
// First pass: exact arity match
|
|
2317
|
+
for (Method m : callMethods) {
|
|
2318
|
+
if (m.getParameterCount() == blockArgs.length) {
|
|
2089
2319
|
m.setAccessible(true);
|
|
2090
2320
|
return m.invoke(block, blockArgs);
|
|
2091
2321
|
}
|
|
2092
2322
|
}
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2323
|
+
|
|
2324
|
+
// Second pass: adapted arity (fewer params than args, or more params with null padding)
|
|
2325
|
+
for (Method m : callMethods) {
|
|
2326
|
+
if (!m.isBridge()) {
|
|
2096
2327
|
m.setAccessible(true);
|
|
2097
2328
|
int paramCount = m.getParameterCount();
|
|
2098
2329
|
Object[] adapted = new Object[paramCount];
|
|
@@ -2102,6 +2333,17 @@ public class RubyDispatch {
|
|
|
2102
2333
|
return m.invoke(block, adapted);
|
|
2103
2334
|
}
|
|
2104
2335
|
}
|
|
2336
|
+
|
|
2337
|
+
// Third pass: use any call method (even bridge) as last resort
|
|
2338
|
+
for (Method m : callMethods) {
|
|
2339
|
+
m.setAccessible(true);
|
|
2340
|
+
int paramCount = m.getParameterCount();
|
|
2341
|
+
Object[] adapted = new Object[paramCount];
|
|
2342
|
+
for (int i = 0; i < paramCount; i++) {
|
|
2343
|
+
adapted[i] = i < blockArgs.length ? blockArgs[i] : null;
|
|
2344
|
+
}
|
|
2345
|
+
return m.invoke(block, adapted);
|
|
2346
|
+
}
|
|
2105
2347
|
} catch (Exception e) {
|
|
2106
2348
|
throw new RuntimeException("Failed to invoke block: " + e.getMessage(), e);
|
|
2107
2349
|
}
|
|
@@ -2111,7 +2353,7 @@ public class RubyDispatch {
|
|
|
2111
2353
|
/**
|
|
2112
2354
|
* Ruby truthiness check: everything is truthy except null (nil) and false.
|
|
2113
2355
|
*/
|
|
2114
|
-
|
|
2356
|
+
public static boolean isTruthy(Object value) {
|
|
2115
2357
|
if (value == null) return false;
|
|
2116
2358
|
if (value instanceof Boolean) return (Boolean) value;
|
|
2117
2359
|
return true;
|
|
@@ -2231,4 +2473,28 @@ public class RubyDispatch {
|
|
|
2231
2473
|
return parts[1];
|
|
2232
2474
|
}
|
|
2233
2475
|
}
|
|
2476
|
+
|
|
2477
|
+
/**
|
|
2478
|
+
* Null-safe unboxing: Object → long. Returns 0L if null.
|
|
2479
|
+
*/
|
|
2480
|
+
public static long unboxLong(Object o) {
|
|
2481
|
+
if (o == null) return 0L;
|
|
2482
|
+
return ((Number) o).longValue();
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
/**
|
|
2486
|
+
* Null-safe unboxing: Object → double. Returns 0.0 if null.
|
|
2487
|
+
*/
|
|
2488
|
+
public static double unboxDouble(Object o) {
|
|
2489
|
+
if (o == null) return 0.0;
|
|
2490
|
+
return ((Number) o).doubleValue();
|
|
2491
|
+
}
|
|
2492
|
+
|
|
2493
|
+
/**
|
|
2494
|
+
* Null-safe unboxing: Object → boolean. Returns false if null.
|
|
2495
|
+
*/
|
|
2496
|
+
public static boolean unboxBoolean(Object o) {
|
|
2497
|
+
if (o == null) return false;
|
|
2498
|
+
return ((Boolean) o).booleanValue();
|
|
2499
|
+
}
|
|
2234
2500
|
}
|
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.2.
|
|
4
|
+
version: 0.2.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yasushi Itoh
|
|
@@ -213,14 +213,17 @@ files:
|
|
|
213
213
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KCompression.class
|
|
214
214
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KConditionVariable.class
|
|
215
215
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KCrypto.class
|
|
216
|
+
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KFiber.class
|
|
216
217
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KFile.class
|
|
217
218
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KHTTP.class
|
|
218
219
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KHash.class
|
|
219
220
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON$Parser.class
|
|
220
221
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KJSON.class
|
|
222
|
+
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KMatchData.class
|
|
221
223
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KMath.class
|
|
222
224
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactor.class
|
|
223
225
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KRactorPort.class
|
|
226
|
+
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KRubyException.class
|
|
224
227
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KSizedQueue.class
|
|
225
228
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KThread.class
|
|
226
229
|
- tools/konpeito-asm/runtime-classes/konpeito/runtime/KTime.class
|
|
@@ -231,13 +234,16 @@ files:
|
|
|
231
234
|
- tools/konpeito-asm/src/konpeito/runtime/KCompression.java
|
|
232
235
|
- tools/konpeito-asm/src/konpeito/runtime/KConditionVariable.java
|
|
233
236
|
- tools/konpeito-asm/src/konpeito/runtime/KCrypto.java
|
|
237
|
+
- tools/konpeito-asm/src/konpeito/runtime/KFiber.java
|
|
234
238
|
- tools/konpeito-asm/src/konpeito/runtime/KFile.java
|
|
235
239
|
- tools/konpeito-asm/src/konpeito/runtime/KHTTP.java
|
|
236
240
|
- tools/konpeito-asm/src/konpeito/runtime/KHash.java
|
|
237
241
|
- tools/konpeito-asm/src/konpeito/runtime/KJSON.java
|
|
242
|
+
- tools/konpeito-asm/src/konpeito/runtime/KMatchData.java
|
|
238
243
|
- tools/konpeito-asm/src/konpeito/runtime/KMath.java
|
|
239
244
|
- tools/konpeito-asm/src/konpeito/runtime/KRactor.java
|
|
240
245
|
- tools/konpeito-asm/src/konpeito/runtime/KRactorPort.java
|
|
246
|
+
- tools/konpeito-asm/src/konpeito/runtime/KRubyException.java
|
|
241
247
|
- tools/konpeito-asm/src/konpeito/runtime/KSizedQueue.java
|
|
242
248
|
- tools/konpeito-asm/src/konpeito/runtime/KThread.java
|
|
243
249
|
- tools/konpeito-asm/src/konpeito/runtime/KTime.java
|