From 318d4bff9318301615ae5aab43df6a3cb5a4cd33 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 19 Nov 2023 20:00:06 +0100 Subject: [PATCH] Fix boxing/unboxing arguments and return values in method references --- .../lambda/LambdaMetafactorySubstitutor.java | 30 ++++++++++++++----- .../test/java/org/teavm/vm/LambdaTest.java | 28 +++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java b/classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java index 2e7c6d53a..f60f1658e 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/lambda/LambdaMetafactorySubstitutor.java @@ -188,19 +188,35 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor } else if (from instanceof ValueType.Primitive && to instanceof ValueType.Object) { String primitiveClass = ((ValueType.Object) to).getClassName(); PrimitiveType toType = getWrappedPrimitive(primitiveClass); + var fromType = (ValueType.Primitive) from; if (toType == null) { - return arg; + return arg.getProgramEmitter().invoke(fromType.getBoxedType().getClassName(), "valueOf", + fromType.getBoxedType(), arg); } arg = tryConvertArgument(arg, from, ValueType.primitive(toType)); return arg.getProgramEmitter().invoke(primitiveClass, "valueOf", to, arg); } else if (from instanceof ValueType.Object && to instanceof ValueType.Primitive) { - String primitiveClass = ((ValueType.Object) from).getClassName(); - PrimitiveType fromType = getWrappedPrimitive(primitiveClass); - if (fromType == null) { - return arg; + var fromClass = ((ValueType.Object) from).getClassName(); + var primitiveType = (ValueType.Primitive) to; + if (fromClass.equals("java.lang.Object")) { + switch (primitiveType.getKind()) { + case BYTE: + case SHORT: + case INTEGER: + case LONG: + case FLOAT: + case DOUBLE: + arg = arg.cast(ValueType.object("java.lang.Number")); + break; + case BOOLEAN: + arg = arg.cast(ValueType.object("java.lang.Boolean")); + break; + case CHARACTER: + arg = arg.cast(ValueType.object("java.lang.Character")); + break; + } } - arg = arg.invokeVirtual(primitiveName(fromType) + "Value", ValueType.primitive(fromType)); - return tryConvertArgument(arg, ValueType.primitive(fromType), to); + return arg.invokeVirtual(primitiveName(primitiveType.getKind()) + "Value", primitiveType); } else { return arg.cast(to); } diff --git a/tests/src/test/java/org/teavm/vm/LambdaTest.java b/tests/src/test/java/org/teavm/vm/LambdaTest.java index d16ab5afc..9ed1c4e00 100644 --- a/tests/src/test/java/org/teavm/vm/LambdaTest.java +++ b/tests/src/test/java/org/teavm/vm/LambdaTest.java @@ -17,12 +17,15 @@ package org.teavm.vm; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.lang.reflect.Array; +import java.util.Map; import java.util.function.Consumer; import java.util.function.IntPredicate; import java.util.function.Supplier; +import java.util.function.ToDoubleFunction; import org.junit.Test; import org.junit.runner.RunWith; import org.teavm.junit.EachTestCompiledSeparately; @@ -80,6 +83,31 @@ public class LambdaTest { Consumer foo = bar -> { }; } + @Test + public void methodReferenceUnboxing() { + var map = Map.of(1, 23.0, 2, 42.0); + ToDoubleFunction f = map::get; + assertEquals(23.0, f.applyAsDouble(1), 0.01); + assertNotEquals(24.0, f.applyAsDouble(1), 0.01); + assertEquals(42.0, f.applyAsDouble(2), 0.01); + + var intMap = Map.of(1, 23, 2, 42); + ToDoubleFunction g = intMap::get; + assertEquals(23.0, g.applyAsDouble(1), 0.01); + assertNotEquals(24.0, g.applyAsDouble(1), 0.01); + assertEquals(42.0, g.applyAsDouble(2), 0.01); + } + + @Test + public void methodReferenceBoxing() { + java.util.function.Function f = this::intFunction; + assertEquals(25, f.apply(23)); + } + + private int intFunction(int a) { + return a + 2; + } + private String acceptIntPredicate(IntPredicate p) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10; ++i) {