From 69426b9945a3324fe47940e9af2854f7a4412904 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Sun, 4 Dec 2016 20:53:40 -0700 Subject: [PATCH] fix some lambda bugs For lambdas that implement java.io.Serializable, the compiler emits calls to LambdaMetaFactory.altMetafactory, not LambdaMetaFactory.metafactory, so I've provided a stub implementation that ignores that currently ignores the extra parameters it receives. This also fixes a bug in compiling lambda glue code for lambdas that take longs and/or doubles. --- .../java/lang/invoke/LambdaMetafactory.java | 42 ++++++++++------- classpath/java/lang/invoke/MethodType.java | 36 +++++++------- src/machine.cpp | 47 ++++++++++++++++--- test/InvokeDynamic.java | 39 +++++++++++++-- 4 files changed, 119 insertions(+), 45 deletions(-) diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java index f0d57163b3..4752b35d68 100644 --- a/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -25,17 +25,17 @@ import avian.SystemClassLoader; public class LambdaMetafactory { private static int nextNumber = 0; - + private static Class resolveReturnInterface(MethodType type) { int index = 1; byte[] s = type.spec; - + while (s[index] != ')') ++ index; - + if (s[++ index] != 'L') throw new AssertionError(); - + ++ index; - + int end = index + 1; while (s[end] != ';') ++ end; @@ -52,11 +52,11 @@ public class LambdaMetafactory { while (array[i] != c) ++i; return i; } - + private static String constructorSpec(MethodType type) { return Classes.makeString(type.spec, 0, indexOf(')', type.spec) + 1) + "V"; } - + private static byte[] makeFactoryCode(List pool, String className, String constructorSpec, @@ -89,7 +89,7 @@ public class LambdaMetafactory { byte[] result = out.toByteArray(); set4(result, 4, result.length - 12); - return result; + return result; } private static byte[] makeConstructorCode(List pool, @@ -124,7 +124,7 @@ public class LambdaMetafactory { byte[] result = out.toByteArray(); set4(result, 4, result.length - 12); - return result; + return result; } private static byte[] makeInvocationCode(List pool, @@ -167,7 +167,7 @@ public class LambdaMetafactory { default: throw new AssertionError ("todo: implement per http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5"); } - + write2(out, ConstantPool.addMethodRef (pool, Classes.makeString(implementation.method.class_.name, 0, @@ -233,7 +233,7 @@ public class LambdaMetafactory { } String constructorSpec = constructorSpec(invokedType); - + List methodTable = new ArrayList(); try { @@ -261,9 +261,9 @@ public class LambdaMetafactory { } catch (IOException e) { AssertionError error = new AssertionError(); error.initCause(e); - throw error; + throw error; } - + int nameIndex = ConstantPool.addClass(pool, className); int superIndex = ConstantPool.addClass(pool, "java/lang/Object"); @@ -276,12 +276,12 @@ public class LambdaMetafactory { } catch (IOException e) { AssertionError error = new AssertionError(); error.initCause(e); - throw error; + throw error; } return out.toByteArray(); } - + public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, @@ -291,7 +291,7 @@ public class LambdaMetafactory { throws LambdaConversionException { byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation); - + try { return new CallSite (new MethodHandle @@ -306,4 +306,14 @@ public class LambdaMetafactory { throw error; } } + + public static CallSite altMetafactory(MethodHandles.Lookup caller, + String invokedName, + MethodType invokedType, + Object... args) + throws LambdaConversionException + { + // todo: handle flags + return metafactory(caller, invokedName, invokedType, (MethodType) args[0], (MethodHandle) args[1], (MethodType) args[2]); + } } diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java index e2030403d9..9c866e574f 100644 --- a/classpath/java/lang/invoke/MethodType.java +++ b/classpath/java/lang/invoke/MethodType.java @@ -14,7 +14,7 @@ public final class MethodType implements java.io.Serializable { private static final char[] Primitives = new char[] { 'V', 'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D' }; - + final ClassLoader loader; final byte[] spec; private volatile List parameters; @@ -31,11 +31,11 @@ public final class MethodType implements java.io.Serializable { this.spec = new byte[spec.length() + 1]; spec.getBytes(0, spec.length(), this.spec, 0); } - + public String toMethodDescriptorString() { return Classes.makeString(spec, 0, spec.length - 1); } - + private static String spec(Class c) { if (c.isPrimitive()) { VMClass vmc = Classes.toVMClass(c); @@ -56,7 +56,7 @@ public final class MethodType implements java.io.Serializable { Class ... ptypes) { loader = rtype.getClassLoader(); - + StringBuilder sb = new StringBuilder(); sb.append('('); parameters = new ArrayList(ptypes.length); @@ -66,7 +66,7 @@ public final class MethodType implements java.io.Serializable { sb.append(spec); Type type = type(spec); - + parameters.add(new Parameter(i, position, spec, @@ -86,7 +86,7 @@ public final class MethodType implements java.io.Serializable { this.spec = sb.toString().getBytes(); } - + public static MethodType methodType(Class rtype, Class ptype0, Class ... ptypes) @@ -129,7 +129,7 @@ public final class MethodType implements java.io.Serializable { return array; } - + public Iterable parameters() { if (parameters == null) { List list = new ArrayList(); @@ -147,7 +147,7 @@ public final class MethodType implements java.io.Serializable { case '[': { ++ i; while (spec[i] == '[') ++ i; - + switch (spec[i]) { case 'L': ++ i; @@ -174,7 +174,7 @@ public final class MethodType implements java.io.Serializable { String paramSpec = Classes.makeString(spec, start, (i - start) + 1); Type type = type(paramSpec); - + list.add(new Parameter (index, position, @@ -192,14 +192,14 @@ public final class MethodType implements java.io.Serializable { String paramSpec = Classes.makeString(spec, i, spec.length - i - 1); Type type = type(paramSpec); - + result = new Result(paramSpec, Classes.forCanonicalName(loader, paramSpec), type.return_); - + parameters = list; } - + return parameters; } @@ -234,18 +234,18 @@ public final class MethodType implements java.io.Serializable { case 'V': return Type.VoidType; - default: throw new AssertionError(); - } + default: throw new AssertionError(); + } } private static enum Type { ObjectType(aload, areturn, 1), IntegerType(iload, ireturn, 1), FloatType(fload, freturn, 1), - LongType(lload, lreturn, 1), - DoubleType(dload, dreturn, 1), + LongType(lload, lreturn, 2), + DoubleType(dload, dreturn, 2), VoidType(-1, Assembler.return_, -1); - + public final int load; public final int return_; public final int size; @@ -256,7 +256,7 @@ public final class MethodType implements java.io.Serializable { this.size = size; } } - + public static class Parameter { private final int index; private final int position; diff --git a/src/machine.cpp b/src/machine.cpp index 4b15155503..5fe8f2136d 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -6055,8 +6055,6 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) GcNoSuchMethodError::Type)); PROTECT(t, bootstrap); - assertT(t, bootstrap->parameterCount() == 2 + bootstrapArray->length()); - GcLookup* lookup = makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC); PROTECT(t, lookup); @@ -6078,14 +6076,47 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) array->setBodyElement(t, argument++, name); array->setBodyElement(t, argument++, type); - MethodSpecIterator it( - t, reinterpret_cast(bootstrap->spec()->body().begin())); + const char* spec; + GcArray* argArray = array; + PROTECT(t, argArray); + + if (::strcmp(reinterpret_cast(bootstrap->spec()->body().begin()), + "(Ljava/lang/invoke/MethodHandles$Lookup;" + "Ljava/lang/String;" + "Ljava/lang/invoke/MethodType;" + "[Ljava/lang/Object;)" + "Ljava/lang/invoke/CallSite;") == 0) { + // LambdaMetaFactory.altMetafactory + array = makeArray(t, bootstrapArray->length() - 1); + spec = "(Ljava/lang/invoke/MethodHandles$Lookup;" + "Ljava/lang/String;" + "Ljava/lang/invoke/MethodType;" + "Ljava/lang/invoke/MethodType;" + "Ljava/lang/invoke/MethodHandle;" + "Ljava/lang/invoke/MethodType;" + "I" + "I" + "[Ljava/lang/Class;" + "I" + "[Ljava/lang/invoke/MethodType;" + ")Ljava/lang/invoke/CallSite;"; + } else if (bootstrap->parameterCount() == 2 + bootstrapArray->length()) { + spec = reinterpret_cast(bootstrap->spec()->body().begin()); + } else { + abort(t); + } + + MethodSpecIterator it(t, spec); for (unsigned i = 0; i < argument; ++i) it.next(); + if (argArray != array) { + argument = 0; + } + unsigned i = 0; - while (it.hasNext()) { + while (i + 1 < bootstrapArray->length() && it.hasNext()) { const char* p = it.next(); switch (*p) { case 'L': { @@ -6162,8 +6193,12 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) ? 0 : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); + if (argArray != array) { + argArray->setBodyElement(t, 3, array); + } + return cast( - t, t->m->processor->invokeArray(t, bootstrap, handle, array)); + t, t->m->processor->invokeArray(t, bootstrap, handle, argArray)); } void noop() diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java index 90d88d6fc6..05a2876c12 100644 --- a/test/InvokeDynamic.java +++ b/test/InvokeDynamic.java @@ -4,15 +4,33 @@ public class InvokeDynamic { private InvokeDynamic(int foo) { this.foo = foo; } - + private interface Operation { int operate(int a, int b); } + private interface Operation2 { + long operate(long a, int b); + } + + private static class Pair { + public final A first; + public final B second; + + public Pair(A first, B second) { + this.first = first; + this.second = second; + } + } + + private interface Supplier extends java.io.Serializable { + T get(); + } + private static void expect(boolean v) { if (! v) throw new RuntimeException(); } - + public static void main(String[] args) { int c = 4; Operation op = (a, b) -> a + b - c; @@ -24,8 +42,19 @@ public class InvokeDynamic { } private void test() { - int c = 2; - Operation op = (a, b) -> ((a + b) * c) - foo; - expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); + { int c = 2; + Operation op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); + } + + { int c = 2; + Operation2 op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); + } + + { Supplier> s = () -> new Pair(42L, 77.1D); + expect(s.get().first == 42L); + expect(s.get().second == 77.1D); + } } }