From 8a7944d25c4daaeb40942bd08a125ec5e3729b39 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 6 Aug 2015 13:24:06 -0600 Subject: [PATCH] add support for openjdk=$JDK8_HOME All tests pass for the process=compile build. Next step: process=interpret. --- .travis.yml | 3 + classpath/avian/Classes.java | 2 + .../java/lang/invoke/LambdaMetafactory.java | 33 ++- classpath/java/lang/invoke/MethodHandle.java | 25 +- classpath/java/lang/invoke/MethodType.java | 254 ++++++++++-------- classpath/java/lang/reflect/Method.java | 12 - makefile | 7 +- src/avian/constants.h | 10 + src/builtin.cpp | 7 + src/classpath-openjdk.cpp | 1 + src/compile.cpp | 16 +- test/InvokeDynamic.java | 16 +- 12 files changed, 247 insertions(+), 139 deletions(-) diff --git a/.travis.yml b/.travis.yml index d18b9c7fd3..8a029d50da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: cpp cache: apt +jdk: + - oraclejdk8 + os: - linux - osx diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index def0047be5..7d428b28d4 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -45,6 +45,8 @@ public class Classes { public static native VMClass toVMClass(Class c); + public static native VMMethod toVMMethod(Method m); + private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec) throws ClassNotFoundException; diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java index fc26c54fa9..5a814d1649 100644 --- a/classpath/java/lang/invoke/LambdaMetafactory.java +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -155,7 +155,19 @@ public class LambdaMetafactory { write1(out, p.position() + 1); } - write1(out, invokestatic); + switch (implementation.kind) { + case MethodHandle.REF_invokeStatic: + write1(out, invokestatic); + break; + + case MethodHandle.REF_invokeSpecial: + write1(out, invokespecial); + break; + + 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, @@ -213,11 +225,9 @@ public class LambdaMetafactory { try { methodTable.add (new MethodData - (Modifier.STATIC, + (Modifier.PUBLIC | Modifier.STATIC, ConstantPool.addUtf8(pool, "make"), - ConstantPool.addUtf8(pool, Classes.makeString - (invokedType.spec, 0, - invokedType.spec.length - 1)), + ConstantPool.addUtf8(pool, invokedType.toMethodDescriptorString()), makeFactoryCode(pool, className, constructorSpec, invokedType))); methodTable.add @@ -231,9 +241,7 @@ public class LambdaMetafactory { (new MethodData (Modifier.PUBLIC, ConstantPool.addUtf8(pool, invokedName), - ConstantPool.addUtf8(pool, Classes.makeString - (methodType.spec, 0, - methodType.spec.length - 1)), + ConstantPool.addUtf8(pool, methodType.toMethodDescriptorString()), makeInvocationCode(pool, className, constructorSpec, invokedType, methodType, methodImplementation))); } catch (IOException e) { @@ -262,10 +270,11 @@ public class LambdaMetafactory { try { return new CallSite (new MethodHandle - (invokedType.loader, avian.SystemClassLoader.getClass - (avian.Classes.defineVMClass - (invokedType.loader, classData, 0, classData.length)) - .getMethod("make", invokedType.parameterArray()).vmMethod)); + (MethodHandle.REF_invokeStatic, invokedType.loader, Classes.toVMMethod + (avian.SystemClassLoader.getClass + (avian.Classes.defineVMClass + (invokedType.loader, classData, 0, classData.length)) + .getMethod("make", invokedType.parameterArray())))); } catch (NoSuchMethodException e) { AssertionError error = new AssertionError(); error.initCause(e); diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java index 386d2354d4..4efbfbabd6 100644 --- a/classpath/java/lang/invoke/MethodHandle.java +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -1,18 +1,35 @@ package java.lang.invoke; +import avian.Classes; + public class MethodHandle { + static final int REF_invokeStatic = 6; + static final int REF_invokeSpecial = 7; + + final int kind; private final ClassLoader loader; final avian.VMMethod method; private volatile MethodType type; - MethodHandle(ClassLoader loader, avian.VMMethod method) { + MethodHandle(int kind, ClassLoader loader, avian.VMMethod method) { + this.kind = kind; this.loader = loader; this.method = method; } - + public String toString() { - return new java.lang.reflect.Method(method).toString(); - } + StringBuilder sb = new StringBuilder(); + if (method.class_ != null) { + sb.append(Classes.makeString(method.class_.name, 0, + method.class_.name.length - 1)); + sb.append("."); + } + sb.append(Classes.makeString(method.name, 0, + method.name.length - 1)); + sb.append(Classes.makeString(method.spec, 0, + method.spec.length - 1)); + return sb.toString(); + } public MethodType type() { if (type == null) { diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java index 44dd79e3cc..c8bf5bce62 100644 --- a/classpath/java/lang/invoke/MethodType.java +++ b/classpath/java/lang/invoke/MethodType.java @@ -2,12 +2,18 @@ package java.lang.invoke; import static avian.Assembler.*; +import avian.Assembler; import avian.Classes; +import avian.VMClass; import java.util.List; import java.util.ArrayList; 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; @@ -19,6 +25,77 @@ public final class MethodType implements java.io.Serializable { this.spec = spec; } + 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); + for (char p: Primitives) { + if (vmc == Classes.primitiveClass(p)) { + return String.valueOf(p); + } + } + throw new AssertionError(); + } else if (c.isArray()) { + return "[" + spec(c.getComponentType()); + } else { + return "L" + c.getName().replace('.', '/') + ";"; + } + } + + private MethodType(Class rtype, + Class ... ptypes) + { + loader = rtype.getClassLoader(); + + StringBuilder sb = new StringBuilder(); + sb.append('('); + parameters = new ArrayList(ptypes.length); + int position = 0; + for (int i = 0; i < ptypes.length; ++i) { + String spec = spec(ptypes[i]); + sb.append(spec); + + Type type = type(spec); + + parameters.add(new Parameter(i, + position, + spec, + ptypes[i], + type.load)); + + position += type.size; + } + sb.append(')'); + + footprint = position; + + String spec = spec(rtype); + sb.append(spec); + + result = new Result(spec, rtype, type(spec).return_); + + this.spec = sb.toString().getBytes(); + } + + public static MethodType methodType(Class rtype, + Class ptype0, + Class ... ptypes) + { + Class[] array = new Class[ptypes.length + 1]; + array[0] = ptype0; + System.arraycopy(ptypes, 0, array, 1, ptypes.length); + return methodType(rtype, array); + } + + public static MethodType methodType(Class rtype, + Class ... ptypes) + { + return new MethodType(rtype, ptypes); + } + public String toString() { return Classes.makeString(spec, 0, spec.length - 1); } @@ -53,21 +130,14 @@ public final class MethodType implements java.io.Serializable { int index = 0; int position = 0; for (i = 1; spec[i] != ')'; ++i) { + int start = i; switch (spec[i]) { case 'L': { - int start = i; ++ i; while (spec[i] != ';') ++ i; - - list.add(new Parameter - (index, - position, - Classes.makeString(spec, start, (i - start) + 1), - aload)); } break; case '[': { - int start = i; ++ i; while (spec[i] == '[') ++ i; @@ -80,12 +150,6 @@ public final class MethodType implements java.io.Serializable { default: break; } - - list.add(new Parameter - (index, - position, - Classes.makeString(spec, start, (i - start) + 1), - aload)); } break; case 'Z': @@ -93,107 +157,38 @@ public final class MethodType implements java.io.Serializable { case 'S': case 'C': case 'I': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - iload)); - break; - case 'F': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - fload)); - break; - case 'J': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - lload)); - - ++ position; - break; - case 'D': - list.add(new Parameter - (index, - position, - Classes.makeString(spec, i, 1), - dload)); - - ++ position; break; default: throw new AssertionError(); } + String paramSpec = Classes.makeString(spec, start, (i - start) + 1); + Type type = type(paramSpec); + + list.add(new Parameter + (index, + position, + paramSpec, + Classes.forCanonicalName(loader, paramSpec), + type.load)); + ++ index; - ++ position; + position += type.size; } footprint = position; ++ i; - switch (spec[i]) { - case 'L': { - int start = i; - ++ i; - while (spec[i] != ';') ++ i; - - result = new Result - (Classes.makeString(spec, start, (i - start) + 1), areturn); - } break; - - case '[': { - int start = i; - ++ i; - while (spec[i] == '[') ++ i; - - switch (spec[i]) { - case 'L': - ++ i; - while (spec[i] != ';') ++ i; - break; - - default: - break; - } - - result = new Result(Classes.makeString(spec, start, (i - start) + 1), - areturn); - } break; - - case 'V': - result = new Result(Classes.makeString(spec, i, 1), return_); - break; - - case 'Z': - case 'B': - case 'S': - case 'C': - case 'I': - result = new Result(Classes.makeString(spec, i, 1), ireturn); - break; - - case 'F': - result = new Result(Classes.makeString(spec, i, 1), freturn); - break; - - case 'J': - result = new Result(Classes.makeString(spec, i, 1), lreturn); - break; - - case 'D': - result = new Result(Classes.makeString(spec, i, 1), dreturn); - break; - - default: throw new AssertionError(); - } + 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; } @@ -207,7 +202,55 @@ public final class MethodType implements java.io.Serializable { return result; } - public class Parameter { + private static Type type(String spec) { + switch (spec.charAt(0)) { + case 'L': + case '[': + return Type.ObjectType; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + return Type.IntegerType; + + case 'F': + return Type.FloatType; + + case 'J': + return Type.LongType; + + case 'D': + return Type.DoubleType; + + case 'V': + return Type.VoidType; + + 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), + VoidType(-1, Assembler.return_, -1); + + public final int load; + public final int return_; + public final int size; + + private Type(int load, int return_, int size) { + this.load = load; + this.return_ = return_; + this.size = size; + } + } + + public static class Parameter { private final int index; private final int position; private final String spec; @@ -217,12 +260,13 @@ public final class MethodType implements java.io.Serializable { private Parameter(int index, int position, String spec, + Class type, int load) { this.index = index; this.position = position; this.spec = spec; - this.type = Classes.forCanonicalName(loader, spec); + this.type = type; this.load = load; } @@ -243,14 +287,14 @@ public final class MethodType implements java.io.Serializable { } } - public class Result { + public static class Result { private final String spec; private final Class type; private final int return_; - public Result(String spec, int return_) { + public Result(String spec, Class type, int return_) { this.spec = spec; - this.type = Classes.forCanonicalName(loader, spec); + this.type = type; this.return_ = return_; } diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 58d788d5dd..705fc8ec16 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -58,18 +58,6 @@ public class Method extends AccessibleObject implements Member { private String getSpec() { return getSpec(vmMethod); } - - public String toString() { - StringBuilder sb = new StringBuilder(); - if (vmMethod.class_ != null) { - sb.append(Classes.makeString(vmMethod.class_.name, 0, - vmMethod.class_.name.length - 1)); - sb.append("."); - } - sb.append(getName()); - sb.append(getSpec()); - return sb.toString(); - } public static String getSpec(VMMethod vmMethod) { return Classes.makeString(vmMethod.spec, 0, vmMethod.spec.length - 1); diff --git a/makefile b/makefile index 037724ad2e..d102e06bdb 100755 --- a/makefile +++ b/makefile @@ -1473,7 +1473,12 @@ ifneq ($(classpath),avian) $(classpath-src)/avian/VMField.java \ $(classpath-src)/avian/VMMethod.java \ $(classpath-src)/avian/avianvmresource/Handler.java \ - $(classpath-src)/avian/file/Handler.java + $(classpath-src)/avian/file/Handler.java \ + $(classpath-src)/java/lang/invoke/MethodHandle.java \ + $(classpath-src)/java/lang/invoke/MethodHandles.java \ + $(classpath-src)/java/lang/invoke/MethodType.java \ + $(classpath-src)/java/lang/invoke/LambdaMetafactory.java \ + $(classpath-src)/java/lang/invoke/CallSite.java ifeq ($(openjdk),) classpath-sources := $(classpath-sources) \ diff --git a/src/avian/constants.h b/src/avian/constants.h index d40d58b055..ef2bc24364 100644 --- a/src/avian/constants.h +++ b/src/avian/constants.h @@ -263,6 +263,16 @@ const unsigned ACC_INTERFACE = 1 << 9; const unsigned ACC_ABSTRACT = 1 << 10; const unsigned ACC_STRICT = 1 << 11; +const unsigned REF_getField = 1; +const unsigned REF_getStatic = 2; +const unsigned REF_putField = 3; +const unsigned REF_putStatic = 4; +const unsigned REF_invokeVirtual = 5; +const unsigned REF_invokeStatic = 6; +const unsigned REF_invokeSpecial = 7; +const unsigned REF_newInvokeSpecial = 8; +const unsigned REF_invokeInterface = 9; + const int AVIAN_JNI_COMMIT = 1; const int AVIAN_JNI_ABORT = 2; diff --git a/src/builtin.cpp b/src/builtin.cpp index 979b52f6e1..8cecd382ad 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -113,6 +113,13 @@ extern "C" AVIAN_EXPORT int64_t JNICALL cast(t, reinterpret_cast(arguments[0]))->vmClass()); } +extern "C" AVIAN_EXPORT int64_t JNICALL + Avian_avian_Classes_toVMMethod(Thread* t, object, uintptr_t* arguments) +{ + return reinterpret_cast(t->m->classpath->getVMMethod( + t, cast(t, reinterpret_cast(arguments[0])))); +} + extern "C" AVIAN_EXPORT void JNICALL Avian_avian_Classes_initialize(Thread* t, object, uintptr_t* arguments) { diff --git a/src/classpath-openjdk.cpp b/src/classpath-openjdk.cpp index 946528aaca..5cbed68a1a 100644 --- a/src/classpath-openjdk.cpp +++ b/src/classpath-openjdk.cpp @@ -484,6 +484,7 @@ class MyClasspath : public Classpath { } oarray = charArray; + offset = 0; } else { expect(t, objectClass(t, oarray) == type(t, GcCharArray::Type)); } diff --git a/src/compile.cpp b/src/compile.cpp index 3825132c8e..21b81af337 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -7351,6 +7351,12 @@ GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) array->setBodyElement(t, i + argument, type); } else if (strncmp(p, methodHandle, strlen(methodHandle)) == 0) { + GcReference* reference = cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[i + 1])); + int kind = reference->kind(); + GcMethod* method = cast(t, resolve(t, c->loader(), @@ -7359,7 +7365,8 @@ GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) findMethodInClass, GcNoSuchMethodError::Type)); - GcMethodHandle* handle = makeMethodHandle(t, c->loader(), method, 0); + GcMethodHandle* handle + = makeMethodHandle(t, kind, c->loader(), method, 0); array->setBodyElement(t, i + argument, handle); } else { @@ -7396,9 +7403,10 @@ GcCallSite* resolveDynamic(MyThread* t, GcInvocation* invocation) ++i; } - GcMethodHandle* handle = (bootstrap->flags() & ACC_STATIC) - ? 0 - : makeMethodHandle(t, c->loader(), bootstrap, 0); + GcMethodHandle* handle + = (bootstrap->flags() & ACC_STATIC) + ? 0 + : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); return cast( t, t->m->processor->invokeArray(t, bootstrap, handle, array)); diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java index 7f54990324..86f1d1a412 100644 --- a/test/InvokeDynamic.java +++ b/test/InvokeDynamic.java @@ -1,4 +1,10 @@ public class InvokeDynamic { + private final int foo; + + private InvokeDynamic(int foo) { + this.foo = foo; + } + private interface Operation { int operate(int a, int b); } @@ -10,6 +16,14 @@ public class InvokeDynamic { public static void main(String[] args) { int c = 4; Operation op = (a, b) -> a + b - c; - expect(op.operate(2, 3) == 1); + expect(op.operate(2, 3) == (2 + 3) - 4); + + new InvokeDynamic(3).test(); + } + + private void test() { + int c = 2; + Operation op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); } }