diff --git a/classpath/avian/Assembler.java b/classpath/avian/Assembler.java index 5b69314a91..64a06fdf81 100644 --- a/classpath/avian/Assembler.java +++ b/classpath/avian/Assembler.java @@ -60,6 +60,7 @@ public class Assembler { int name, int super_, int[] interfaces, + FieldData[] fields, MethodData[] methods) throws IOException { @@ -83,7 +84,13 @@ public class Assembler { write2(out, i + 1); } - write2(out, 0); // field count + write2(out, fields.length); + for (FieldData f: fields) { + write2(out, f.flags); + write2(out, f.nameIndex + 1); + write2(out, f.specIndex + 1); + write2(out, 0); // attribute count + } write2(out, methods.length); for (MethodData m: methods) { @@ -113,4 +120,16 @@ public class Assembler { this.code = code; } } + + public static class FieldData { + public final int flags; + public final int nameIndex; + public final int specIndex; + + public FieldData(int flags, int nameIndex, int specIndex) { + this.flags = flags; + this.nameIndex = nameIndex; + this.specIndex = specIndex; + } + } } diff --git a/classpath/avian/ClassAddendum.java b/classpath/avian/ClassAddendum.java index 94a9f09ab1..156c4df4a1 100644 --- a/classpath/avian/ClassAddendum.java +++ b/classpath/avian/ClassAddendum.java @@ -25,4 +25,6 @@ public class ClassAddendum extends Addendum { public byte[] enclosingClass; public Pair enclosingMethod; + + public VMMethod[] bootstrapMethodTable; } diff --git a/classpath/avian/Classes.java b/classpath/avian/Classes.java index 2b84f1cbd4..7d428b28d4 100644 --- a/classpath/avian/Classes.java +++ b/classpath/avian/Classes.java @@ -45,11 +45,13 @@ 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; - private static VMClass loadVMClass(ClassLoader loader, - byte[] nameBytes, int offset, int length) + public static VMClass loadVMClass(ClassLoader loader, + byte[] nameBytes, int offset, int length) { byte[] spec = new byte[length + 1]; System.arraycopy(nameBytes, offset, spec, 0, length); @@ -576,6 +578,6 @@ public class Classes { private static native void acquireClassLock(); private static native void releaseClassLock(); - + public static native String makeString(byte[] array, int offset, int length); } diff --git a/classpath/java/lang/InternalError.java b/classpath/java/lang/InternalError.java new file mode 100644 index 0000000000..69ac254db4 --- /dev/null +++ b/classpath/java/lang/InternalError.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class InternalError extends VirtualMachineError { + public InternalError(String message) { + super(message); + } + + public InternalError() { + this(null); + } +} diff --git a/classpath/java/lang/TypeNotPresentException.java b/classpath/java/lang/TypeNotPresentException.java new file mode 100644 index 0000000000..eb1f31ed73 --- /dev/null +++ b/classpath/java/lang/TypeNotPresentException.java @@ -0,0 +1,21 @@ +/* Copyright (c) 2008-2015, Avian Contributors + + Permission to use, copy, modify, and/or distribute this software + for any purpose with or without fee is hereby granted, provided + that the above copyright notice and this permission notice appear + in all copies. + + There is NO WARRANTY for this software. See license.txt for + details. */ + +package java.lang; + +public class TypeNotPresentException extends Exception { + public TypeNotPresentException(String message) { + super(message); + } + + public TypeNotPresentException() { + this(null); + } +} diff --git a/classpath/java/lang/invoke/CallSite.java b/classpath/java/lang/invoke/CallSite.java new file mode 100644 index 0000000000..a78ed5dcd2 --- /dev/null +++ b/classpath/java/lang/invoke/CallSite.java @@ -0,0 +1,9 @@ +package java.lang.invoke; + +public class CallSite { + private final MethodHandle target; + + CallSite(MethodHandle target) { + this.target = target; + } +} diff --git a/classpath/java/lang/invoke/LambdaConversionException.java b/classpath/java/lang/invoke/LambdaConversionException.java new file mode 100644 index 0000000000..4502b1c7d8 --- /dev/null +++ b/classpath/java/lang/invoke/LambdaConversionException.java @@ -0,0 +1,9 @@ +package java.lang.invoke; + +public class LambdaConversionException extends java.lang.Exception { + public LambdaConversionException() { throw new RuntimeException(); } + public LambdaConversionException(String s) { throw new RuntimeException(); } + public LambdaConversionException(String s, Throwable th) { throw new RuntimeException(); } + public LambdaConversionException(Throwable th) { throw new RuntimeException(); } + public LambdaConversionException(String s, Throwable th, boolean b, boolean b2) { throw new RuntimeException(); } +} diff --git a/classpath/java/lang/invoke/LambdaMetafactory.java b/classpath/java/lang/invoke/LambdaMetafactory.java new file mode 100644 index 0000000000..5a814d1649 --- /dev/null +++ b/classpath/java/lang/invoke/LambdaMetafactory.java @@ -0,0 +1,284 @@ +package java.lang.invoke; + +import static avian.Stream.write1; +import static avian.Stream.write2; +import static avian.Stream.write4; +import static avian.Stream.set4; +import static avian.Assembler.*; + +import java.lang.reflect.Proxy; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.ArrayList; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import avian.Classes; +import avian.ConstantPool; +import avian.Assembler; +import avian.ConstantPool.PoolEntry; +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; + + Class c = SystemClassLoader.getClass + (Classes.loadVMClass(type.loader, s, index, end - index)); + + if (! c.isInterface()) throw new AssertionError(); + + return c; + } + + private static int indexOf(int c, byte[] array) { + int i = 0; + 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, + MethodType type) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, type.footprint() + 2); // max stack + write2(out, type.footprint()); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, new_); + write2(out, ConstantPool.addClass(pool, className) + 1); + write1(out, dup); + + for (MethodType.Parameter p: type.parameters()) { + write1(out, p.load()); + write1(out, p.position()); + } + + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, className, "", constructorSpec) + 1); + + write1(out, areturn); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeConstructorCode(List pool, + String className, + MethodType type) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, 3); // max stack + write2(out, type.footprint() + 1); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + write1(out, invokespecial); + write2(out, ConstantPool.addMethodRef + (pool, "java/lang/Object", "", "()V") + 1); + + for (MethodType.Parameter p: type.parameters()) { + write1(out, aload_0); + write1(out, p.load()); + write1(out, p.position() + 1); + write1(out, putfield); + write2(out, ConstantPool.addFieldRef + (pool, className, "field" + p.index(), p.spec()) + 1); + } + + write1(out, return_); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + private static byte[] makeInvocationCode(List pool, + String className, + String constructorSpec, + MethodType fieldType, + MethodType localType, + MethodHandle implementation) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + write2(out, fieldType.footprint() + + localType.footprint() + 2); // max stack + write2(out, localType.footprint() + 1); // max locals + write4(out, 0); // length (we'll set the real value later) + + write1(out, aload_0); + + for (MethodType.Parameter p: fieldType.parameters()) { + write1(out, aload_0); + write1(out, getfield); + write2(out, ConstantPool.addFieldRef + (pool, className, "field" + p.index(), p.spec()) + 1); + } + + for (MethodType.Parameter p: localType.parameters()) { + write1(out, p.load()); + write1(out, p.position() + 1); + } + + 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, + implementation.method.class_.name.length - 1), + Classes.makeString(implementation.method.name, 0, + implementation.method.name.length - 1), + Classes.makeString(implementation.method.spec, 0, + implementation.method.spec.length - 1)) + 1); + + write1(out, implementation.type().result().return_()); + + write2(out, 0); // exception handler table length + write2(out, 0); // attribute count + + byte[] result = out.toByteArray(); + set4(result, 4, result.length - 12); + + return result; + } + + public static CallSite metafactory(MethodHandles.Lookup caller, + String invokedName, + MethodType invokedType, + MethodType methodType, + MethodHandle methodImplementation, + MethodType instantiatedMethodType) + throws LambdaConversionException + { + String className; + { int number; + synchronized (LambdaMetafactory.class) { + number = nextNumber++; + } + className = "Lambda-" + number; + } + + List pool = new ArrayList(); + + int interfaceIndex = ConstantPool.addClass + (pool, invokedType.returnType().getName().replace('.', '/')); + + List fieldTable = new ArrayList(); + + for (MethodType.Parameter p: invokedType.parameters()) { + fieldTable.add + (new FieldData(0, + ConstantPool.addUtf8(pool, "field" + p.index()), + ConstantPool.addUtf8(pool, p.spec()))); + } + + String constructorSpec = constructorSpec(invokedType); + + List methodTable = new ArrayList(); + + try { + methodTable.add + (new MethodData + (Modifier.PUBLIC | Modifier.STATIC, + ConstantPool.addUtf8(pool, "make"), + ConstantPool.addUtf8(pool, invokedType.toMethodDescriptorString()), + makeFactoryCode(pool, className, constructorSpec, invokedType))); + + methodTable.add + (new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, ""), + ConstantPool.addUtf8(pool, constructorSpec), + makeConstructorCode(pool, className, invokedType))); + + methodTable.add + (new MethodData + (Modifier.PUBLIC, + ConstantPool.addUtf8(pool, invokedName), + ConstantPool.addUtf8(pool, methodType.toMethodDescriptorString()), + makeInvocationCode(pool, className, constructorSpec, invokedType, + methodType, methodImplementation))); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + + int nameIndex = ConstantPool.addClass(pool, className); + int superIndex = ConstantPool.addClass(pool, "java/lang/Object"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + Assembler.writeClass + (out, pool, nameIndex, superIndex, new int[] { interfaceIndex }, + fieldTable.toArray(new FieldData[fieldTable.size()]), + methodTable.toArray(new MethodData[methodTable.size()])); + } catch (IOException e) { + AssertionError error = new AssertionError(); + error.initCause(e); + throw error; + } + + byte[] classData = out.toByteArray(); + + try { + return new CallSite + (new MethodHandle + (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); + throw error; + } + } +} diff --git a/classpath/java/lang/invoke/MethodHandle.java b/classpath/java/lang/invoke/MethodHandle.java new file mode 100644 index 0000000000..4efbfbabd6 --- /dev/null +++ b/classpath/java/lang/invoke/MethodHandle.java @@ -0,0 +1,40 @@ +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(int kind, ClassLoader loader, avian.VMMethod method) { + this.kind = kind; + this.loader = loader; + this.method = method; + } + + public String 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) { + type = new MethodType(loader, method.spec); + } + return type; + } +} diff --git a/classpath/java/lang/invoke/MethodHandles.java b/classpath/java/lang/invoke/MethodHandles.java new file mode 100644 index 0000000000..f17c05daab --- /dev/null +++ b/classpath/java/lang/invoke/MethodHandles.java @@ -0,0 +1,18 @@ +package java.lang.invoke; + +public class MethodHandles { + public static class Lookup { + final avian.VMClass class_; + private final int modes; + + private Lookup(avian.VMClass class_, int modes) { + this.class_ = class_; + this.modes = modes; + } + + public String toString() { + return "lookup[" + avian.SystemClassLoader.getClass(class_) + ", " + + modes + "]"; + } + } +} diff --git a/classpath/java/lang/invoke/MethodType.java b/classpath/java/lang/invoke/MethodType.java new file mode 100644 index 0000000000..c8bf5bce62 --- /dev/null +++ b/classpath/java/lang/invoke/MethodType.java @@ -0,0 +1,305 @@ +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; + private volatile Result result; + private volatile int footprint; + + MethodType(ClassLoader loader, byte[] spec) { + this.loader = loader; + 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); + } + + public int footprint() { + parameters(); // ensure spec is parsed + + return footprint; + } + + public Class returnType() { + parameters(); // ensure spec is parsed + + return result.type; + } + + public Class[] parameterArray() { + parameters(); // ensure spec is parsed + + Class[] array = new Class[parameters.size()]; + for (int i = 0; i < parameters.size(); ++i) { + array[i] = parameters.get(i).type; + } + + return array; + } + + public Iterable parameters() { + if (parameters == null) { + List list = new ArrayList(); + int i; + int index = 0; + int position = 0; + for (i = 1; spec[i] != ')'; ++i) { + int start = i; + switch (spec[i]) { + case 'L': { + ++ i; + while (spec[i] != ';') ++ i; + } break; + + case '[': { + ++ i; + while (spec[i] == '[') ++ i; + + switch (spec[i]) { + case 'L': + ++ i; + while (spec[i] != ';') ++ i; + break; + + default: + break; + } + } break; + + case 'Z': + case 'B': + case 'S': + case 'C': + case 'I': + case 'F': + case 'J': + case 'D': + 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 += type.size; + } + + footprint = position; + + ++ i; + + 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; + } + + public Result result() { + parameters(); // ensure spec has been parsed + + return result; + } + + 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; + private final Class type; + private final int load; + + private Parameter(int index, + int position, + String spec, + Class type, + int load) + { + this.index = index; + this.position = position; + this.spec = spec; + this.type = type; + this.load = load; + } + + public int index() { + return index; + } + + public int position() { + return position; + } + + public String spec() { + return spec; + } + + public int load() { + return load; + } + } + + public static class Result { + private final String spec; + private final Class type; + private final int return_; + + public Result(String spec, Class type, int return_) { + this.spec = spec; + this.type = type; + this.return_ = return_; + } + + public int return_() { + return return_; // :) + } + } +} diff --git a/classpath/java/lang/reflect/Method.java b/classpath/java/lang/reflect/Method.java index 98b545ac56..705fc8ec16 100644 --- a/classpath/java/lang/reflect/Method.java +++ b/classpath/java/lang/reflect/Method.java @@ -18,7 +18,7 @@ import avian.Classes; import java.lang.annotation.Annotation; public class Method extends AccessibleObject implements Member { - private final VMMethod vmMethod; + public final VMMethod vmMethod; private boolean accessible; public Method(VMMethod vmMethod) { @@ -58,7 +58,7 @@ public class Method extends AccessibleObject implements Member { private String getSpec() { return getSpec(vmMethod); } - + public static String getSpec(VMMethod vmMethod) { return Classes.makeString(vmMethod.spec, 0, vmMethod.spec.length - 1); } diff --git a/classpath/java/lang/reflect/Proxy.java b/classpath/java/lang/reflect/Proxy.java index 306a48d916..60bbedc7ec 100644 --- a/classpath/java/lang/reflect/Proxy.java +++ b/classpath/java/lang/reflect/Proxy.java @@ -23,6 +23,7 @@ import avian.ConstantPool; import avian.ConstantPool.PoolEntry; import avian.Assembler; +import avian.Assembler.FieldData; import avian.Assembler.MethodData; import java.util.List; @@ -402,6 +403,7 @@ public class Proxy { ByteArrayOutputStream out = new ByteArrayOutputStream(); Assembler.writeClass (out, pool, nameIndex, superIndex, interfaceIndexes, + new FieldData[0], methodTable.toArray(new MethodData[methodTable.size()])); byte[] classData = out.toByteArray(); diff --git a/makefile b/makefile index 906fafb89c..ddf9a55bd3 100755 --- a/makefile +++ b/makefile @@ -3,6 +3,10 @@ MAKEFLAGS = -s name = avian version := $(shell grep version gradle.properties | cut -d'=' -f2) +java-version := $(shell $(JAVA_HOME)/bin/java -version 2>&1 \ + | head -n 1 \ + | sed 's/.*version "1.\([^.]*\).*/\1/') + build-arch := $(shell uname -m \ | sed 's/^i.86$$/i386/' \ | sed 's/^x86pc$$/i386/' \ @@ -1473,7 +1477,13 @@ 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/LambdaConversionException.java \ + $(classpath-src)/java/lang/invoke/CallSite.java ifeq ($(openjdk),) classpath-sources := $(classpath-sources) \ @@ -1509,7 +1519,10 @@ vm-classes = \ avian/resource/*.class test-support-sources = $(shell find $(test)/avian/ -name '*.java') -test-sources = $(wildcard $(test)/*.java) +test-sources := $(wildcard $(test)/*.java) +ifeq (7,$(java-version)) + test-sources := $(subst $(test)/InvokeDynamic.java,,$(test-sources)) +endif test-cpp-sources = $(wildcard $(test)/*.cpp) test-sources += $(test-support-sources) test-support-classes = $(call java-classes, $(test-support-sources),$(test),$(test-build)) @@ -1689,7 +1702,7 @@ $(classpath-dep): $(classpath-sources) $(classpath-jar-dep) classes="$(shell $(MAKE) -s --no-print-directory build=$(build) \ $(classpath-classes) arch=$(build-arch) platform=$(bootimage-platform))"; \ if [ -n "$${classes}" ]; then \ - $(javac) -source 1.6 -target 1.6 \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ -d $(classpath-build) -bootclasspath $(boot-classpath) \ $${classes}; fi @touch $(@) @@ -1765,7 +1778,7 @@ $(test-dep): $(test-sources) $(test-library) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \ if test -n "$${files}"; then \ - $(javac) -source 1.6 -target 1.6 \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ -classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi $(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \ @@ -1777,7 +1790,7 @@ $(test-extra-dep): $(test-extra-sources) @mkdir -p $(test-build) files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \ if test -n "$${files}"; then \ - $(javac) -source 1.6 -target 1.6 \ + $(javac) -source 1.$(java-version) -target 1.$(java-version) \ -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \ fi @touch $(@) diff --git a/openjdk-src.mk b/openjdk-src.mk index 4e1d1bdf5e..f357df6520 100644 --- a/openjdk-src.mk +++ b/openjdk-src.mk @@ -49,6 +49,7 @@ openjdk-sources = \ $(openjdk-src)/share/native/java/util/zip/zip_util.c \ $(openjdk-src)/share/native/sun/management/VMManagementImpl.c \ $(openjdk-src)/share/native/sun/misc/GC.c \ + $(openjdk-src)/share/native/sun/misc/URLClassPath.c \ $(openjdk-src)/share/native/sun/misc/MessageUtils.c \ $(openjdk-src)/share/native/sun/misc/NativeSignalHandler.c \ $(openjdk-src)/share/native/sun/misc/Signal.c \ @@ -121,6 +122,7 @@ openjdk-headers-classes = \ sun.misc.VM \ sun.misc.VMSupport \ sun.misc.Version \ + sun.misc.URLClassPath \ sun.net.spi.DefaultProxySelector \ sun.nio.ch.FileKey \ sun.nio.ch.FileChannelImpl \ diff --git a/src/avian/constants.h b/src/avian/constants.h index 7939a2ec68..ef2bc24364 100644 --- a/src/avian/constants.h +++ b/src/avian/constants.h @@ -146,6 +146,7 @@ enum OpCode { imul = 0x68, ineg = 0x74, instanceof = 0xc1, + invokedynamic = 0xba, invokeinterface = 0xb9, invokespecial = 0xb7, invokestatic = 0xb8, @@ -262,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/avian/machine.h b/src/avian/machine.h index f56b9720ac..9f5654303d 100644 --- a/src/avian/machine.h +++ b/src/avian/machine.h @@ -2349,6 +2349,8 @@ GcClass* findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec); GcJclass* getDeclaringClass(Thread* t, GcClass* c); +GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation); + inline bool emptyMethod(Thread* t UNUSED, GcMethod* method) { return ((method->flags() & ACC_NATIVE) == 0) @@ -3408,18 +3410,18 @@ inline GcClass* resolveClassInPool(Thread* t, inline object resolve( Thread* t, GcClassLoader* loader, - GcMethod* method, + GcSingleton* pool, unsigned index, object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*), Gc::Type errorType, bool throw_ = true) { - object o = singletonObject(t, method->code()->pool(), index); + object o = singletonObject(t, pool, index); loadMemoryBarrier(); if (objectClass(t, o) == type(t, GcReference::Type)) { - PROTECT(t, method); + PROTECT(t, pool); GcReference* reference = cast(t, o); PROTECT(t, reference); @@ -3439,8 +3441,7 @@ inline object resolve( if (o) { storeStoreMemoryBarrier(); - method->code()->pool()->setBodyElement( - t, index, reinterpret_cast(o)); + pool->setBodyElement(t, index, reinterpret_cast(o)); } } else { o = 0; @@ -3459,7 +3460,7 @@ inline GcField* resolveField(Thread* t, return cast(t, resolve(t, loader, - method, + method->code()->pool(), index, findFieldInClass, GcNoSuchFieldError::Type, @@ -3562,7 +3563,7 @@ inline GcMethod* resolveMethod(Thread* t, return cast(t, resolve(t, loader, - method, + method->code()->pool(), index, findMethodInClass, GcNoSuchMethodError::Type, diff --git a/src/avian/target-fields.h b/src/avian/target-fields.h index 10421f5ce0..ac4402ece1 100644 --- a/src/avian/target-fields.h +++ b/src/avian/target-fields.h @@ -30,7 +30,8 @@ #define TARGET_THREAD_HEAPIMAGE 2312 #define TARGET_THREAD_CODEIMAGE 2320 #define TARGET_THREAD_THUNKTABLE 2328 -#define TARGET_THREAD_STACKLIMIT 2376 +#define TARGET_THREAD_DYNAMICTABLE 2336 +#define TARGET_THREAD_STACKLIMIT 2384 #elif(TARGET_BYTES_PER_WORD == 4) @@ -50,7 +51,8 @@ #define TARGET_THREAD_HEAPIMAGE 2192 #define TARGET_THREAD_CODEIMAGE 2196 #define TARGET_THREAD_THUNKTABLE 2200 -#define TARGET_THREAD_STACKLIMIT 2224 +#define TARGET_THREAD_DYNAMICTABLE 2204 +#define TARGET_THREAD_STACKLIMIT 2228 #else #error diff --git a/src/bootimage-fields.cpp b/src/bootimage-fields.cpp index 761eef88e5..1cd72c73ea 100644 --- a/src/bootimage-fields.cpp +++ b/src/bootimage-fields.cpp @@ -34,6 +34,7 @@ FIELD(virtualThunks) THUNK_FIELD(default_); THUNK_FIELD(defaultVirtual); +THUNK_FIELD(defaultDynamic); THUNK_FIELD(native); THUNK_FIELD(aioob); THUNK_FIELD(stackOverflow); 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 10a3f89204..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)); } @@ -2091,12 +2092,21 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData) if (fileInputStreamFdField) { cp->fileInputStreamFdField = fileInputStreamFdField->offset(); - intercept(t, - fileInputStreamClass, - "open", - "(Ljava/lang/String;)V", - voidPointer(openFile), - updateRuntimeData); + if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) { + intercept(t, + fileInputStreamClass, + "open0", + "(Ljava/lang/String;)V", + voidPointer(openFile), + updateRuntimeData); + } else { + intercept(t, + fileInputStreamClass, + "open", + "(Ljava/lang/String;)V", + voidPointer(openFile), + updateRuntimeData); + } if (findMethodOrNull(t, fileInputStreamClass, "read0", "()I") != 0) { intercept(t, diff --git a/src/compile.cpp b/src/compile.cpp index 276481580c..74605cc9b5 100644 --- a/src/compile.cpp +++ b/src/compile.cpp @@ -83,6 +83,7 @@ const unsigned InitialZoneCapacityInBytes = 64 * 1024; enum ThunkIndex { compileMethodIndex, compileVirtualMethodIndex, + linkDynamicMethodIndex, invokeNativeIndex, throwArrayIndexOutOfBoundsIndex, throwStackOverflowIndex, @@ -295,6 +296,7 @@ class MyThread : public Thread { uintptr_t* heapImage; uint8_t* codeImage; void** thunkTable; + void** dynamicTable; CallTrace* trace; Reference* reference; avian::codegen::Architecture* arch; @@ -1278,6 +1280,100 @@ class Context { Slice argumentBuffer; }; +unsigned& dynamicIndex(MyThread* t); + +void**& dynamicTable(MyThread* t); + +unsigned& dynamicTableSize(MyThread* t); + +void updateDynamicTable(MyThread* t, MyThread* o) +{ + o->dynamicTable = dynamicTable(t); + if (t->peer) + updateDynamicTable(static_cast(t->peer), o); + if (t->child) + updateDynamicTable(static_cast(t->child), o); +} + +uintptr_t defaultDynamicThunk(MyThread* t); + +uintptr_t compileVirtualThunk(MyThread* t, + unsigned index, + unsigned* size, + uintptr_t thunk, + const char* baseName); + +Allocator* allocator(MyThread* t); + +unsigned addDynamic(MyThread* t, GcInvocation* invocation) +{ + ACQUIRE(t, t->m->classLock); + + int index = invocation->index(); + if (index == -1) { + index = dynamicIndex(t)++; + invocation->index() = index; + + unsigned oldCapacity = roots(t)->invocations() + ? roots(t)->invocations()->length() + : 0; + + if (static_cast(index) >= oldCapacity) { + unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096; + + void** newTable = static_cast( + allocator(t)->allocate(newCapacity * BytesPerWord)); + + GcArray* newData = makeArray(t, newCapacity); + PROTECT(t, newData); + + GcWordArray* newThunks = makeWordArray(t, newCapacity * 2); + PROTECT(t, newThunks); + + if (dynamicTable(t)) { + memcpy(newTable, dynamicTable(t), oldCapacity * BytesPerWord); + + for(size_t i = 0; i < oldCapacity; i++) { + newData->setBodyElement(t, i, + roots(t)->invocations()->body()[i]); + } + + + mark(t, newData, ArrayBody, oldCapacity); + + memcpy(newThunks->body().begin(), + compileRoots(t)->dynamicThunks()->body().begin(), + compileRoots(t)->dynamicThunks()->length() * BytesPerWord); + } + + ENTER(t, Thread::ExclusiveState); + + if (dynamicTable(t)) { + allocator(t)->free(dynamicTable(t), dynamicTableSize(t)); + } + dynamicTable(t) = newTable; + dynamicTableSize(t) = newCapacity * BytesPerWord; + roots(t)->setInvocations(t, newData); + + updateDynamicTable(static_cast(t->m->rootThread), t); + + compileRoots(t)->setDynamicThunks(t, newThunks); + } + + unsigned size; + uintptr_t thunk = compileVirtualThunk( + t, index, &size, defaultDynamicThunk(t), "dynamicThunk"); + compileRoots(t)->dynamicThunks()->body()[index * 2] = thunk; + compileRoots(t)->dynamicThunks()->body()[(index * 2) + 1] = size; + + t->dynamicTable[index] = reinterpret_cast(thunk); + + roots(t)->invocations()->setBodyElement(t, index, invocation); + } + + return index; +} + unsigned translateLocalIndex(Context* context, unsigned footprint, unsigned index) @@ -4944,6 +5040,53 @@ loop: args(c->threadRegister(), frame->append(argument), instance))); } break; + case invokedynamic: { + context->leaf = false; + + uint16_t poolIndex = codeReadInt16(t, code, ip); + ip += 2; + + GcInvocation* invocation = cast( + t, + singletonObject(t, context->method->code()->pool(), poolIndex - 1)); + + PROTECT(t, invocation); + + invocation->setClass(t, context->method->class_()); + + unsigned index = addDynamic(t, invocation); + + GcMethod* template_ = invocation->template_(); + unsigned returnCode = template_->returnCode(); + unsigned rSize = resultSize(t, returnCode); + unsigned parameterFootprint = template_->parameterFootprint(); + + // TODO: can we allow tailCalls in general? + // e.g. what happens if the call site is later bound to a method that can't be tail called? + // NOTE: calling isTailCall right now would cause an segfault, since + // invocation->template_()->class_() will be null. + // bool tailCall + // = isTailCall(t, code, ip, context->method, invocation->template_()); + bool tailCall = false; + + // todo: do we need to tell the compiler to add a load barrier + // here for VolatileCallSite instances? + + ir::Value* result = c->stackCall( + c->memory(c->memory(c->threadRegister(), ir::Type::object(), + TARGET_THREAD_DYNAMICTABLE), + ir::Type::object(), index * TargetBytesPerWord), + tailCall ? Compiler::TailJump : 0, frame->trace(0, 0), + operandTypeForFieldCode(t, returnCode), + frame->peekMethodArguments(parameterFootprint)); + + frame->popFootprint(parameterFootprint); + + if (rSize) { + frame->pushReturnValue(returnCode, result); + } + } break; + case invokeinterface: { context->leaf = false; @@ -7135,6 +7278,61 @@ uint64_t compileVirtualMethod(MyThread* t) return reinterpret_cast(compileVirtualMethod2(t, class_, index)); } +void* linkDynamicMethod2(MyThread* t, unsigned index) +{ + GcInvocation* invocation + = cast(t, roots(t)->invocations()->body()[index]); + + GcCallSite* site = invocation->site(); + + loadMemoryBarrier(); + + if (site == 0) { + t->trace->targetMethod = invocation->template_(); + + THREAD_RESOURCE0(t, static_cast(t)->trace->targetMethod = 0;); + + PROTECT(t, invocation); + + site = resolveDynamic(t, invocation); + PROTECT(t, site); + + compile(t, codeAllocator(t), 0, site->target()->method()); + + ACQUIRE(t, t->m->classLock); + + if (invocation->site() == 0) { + void* address + = reinterpret_cast(methodAddress(t, site->target()->method())); + + if ((site->target()->method()->flags() & ACC_NATIVE) == 0) { + t->dynamicTable[index] = address; + } + } + + storeStoreMemoryBarrier(); + + invocation->setSite(t, site); + site->setInvocation(t, invocation); + } + + GcMethod* target = invocation->site()->target()->method(); + + if (target->flags() & ACC_NATIVE) { + t->trace->nativeMethod = target; + } + + return reinterpret_cast(methodAddress(t, target)); +} + +uint64_t linkDynamicMethod(MyThread* t) +{ + unsigned index = t->virtualCallIndex; + t->virtualCallIndex = 0; + + return reinterpret_cast(linkDynamicMethod2(t, index)); +} + uint64_t invokeNativeFast(MyThread* t, GcMethod* method, void* function) { FastNativeFunction f; @@ -8317,6 +8515,7 @@ class MyProcessor : public Processor { public: Thunk default_; Thunk defaultVirtual; + Thunk defaultDynamic; Thunk native; Thunk aioob; Thunk stackOverflow; @@ -8342,11 +8541,15 @@ class MyProcessor : public Processor { GcArithmeticException::FixedSize), codeAllocator(s, Slice(0, 0)), callTableSize(0), + dynamicIndex(0), useNativeFeatures(useNativeFeatures), - compilationHandlers(0) + compilationHandlers(0), + dynamicTable(0), + dynamicTableSize(0) { thunkTable[compileMethodIndex] = voidPointer(local::compileMethod); thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod); + thunkTable[linkDynamicMethodIndex] = voidPointer(linkDynamicMethod); thunkTable[invokeNativeIndex] = voidPointer(invokeNative); thunkTable[throwArrayIndexOutOfBoundsIndex] = voidPointer(throwArrayIndexOutOfBounds); @@ -8429,6 +8632,10 @@ class MyProcessor : public Processor { TARGET_THREAD_THUNKTABLE, &MyThread::thunkTable, "TARGET_THREAD_THUNKTABLE") + + checkConstant(t, + TARGET_THREAD_DYNAMICTABLE, + &MyThread::dynamicTable, + "TARGET_THREAD_DYNAMICTABLE") + checkConstant(t, TARGET_THREAD_STACKLIMIT, &MyThread::stackLimit, @@ -8813,6 +9020,10 @@ class MyProcessor : public Processor { signals.unregisterHandler(SignalRegistrar::DivideByZero); signals.setCrashDumpDirectory(0); + if (dynamicTable) { + allocator->free(dynamicTable, dynamicTableSize); + } + this->~MyProcessor(); allocator->free(this, sizeof(*this)); @@ -8993,7 +9204,7 @@ class MyProcessor : public Processor { if (image and code) { local::boot(static_cast(t), image, code); } else { - roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0); + roots = makeCompileRoots(t, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); { GcArray* ct = makeArray(t, 128); @@ -9096,11 +9307,29 @@ class MyProcessor : public Processor { ThunkCollection thunks; ThunkCollection bootThunks; unsigned callTableSize; + unsigned dynamicIndex; bool useNativeFeatures; void* thunkTable[dummyIndex + 1]; CompilationHandlerList* compilationHandlers; + void** dynamicTable; + unsigned dynamicTableSize; }; +unsigned& dynamicIndex(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicIndex; +} + +void**& dynamicTable(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicTable; +} + +unsigned& dynamicTableSize(MyThread* t) +{ + return static_cast(t->m->processor)->dynamicTableSize; +} + const char* stringOrNull(const char* str) { if (str) { @@ -9240,15 +9469,16 @@ bool isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip) bool isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip) { - const unsigned NamedThunkCount = 5; + const unsigned NamedThunkCount = 6; MyProcessor::Thunk table[NamedThunkCount + ThunkCount]; table[0] = thunks->default_; table[1] = thunks->defaultVirtual; - table[2] = thunks->native; - table[3] = thunks->aioob; - table[4] = thunks->stackOverflow; + table[2] = thunks->defaultDynamic; + table[3] = thunks->native; + table[4] = thunks->aioob; + table[5] = thunks->stackOverflow; for (unsigned i = 0; i < ThunkCount; ++i) { new (table + NamedThunkCount + i) @@ -9598,8 +9828,8 @@ void findThunks(MyThread* t, BootImage* image, uint8_t* code) MyProcessor* p = processor(t); p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code); - p->bootThunks.defaultVirtual - = thunkToThunk(image->thunks.defaultVirtual, code); + p->bootThunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code); + p->bootThunks.defaultDynamic = thunkToThunk(image->thunks.defaultDynamic, code); p->bootThunks.native = thunkToThunk(image->thunks.native, code); p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code); p->bootThunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, code); @@ -9807,6 +10037,68 @@ void compileCall(MyThread* t, Context* c, ThunkIndex index, bool call = true) } } +void compileDefaultThunk(MyThread* t, + FixedAllocator* allocator, + MyProcessor::Thunk* thunk, + const char* name, + ThunkIndex thunkIndex, + bool hasTarget) +{ + Context context(t); + avian::codegen::Assembler* a = context.assembler; + + if(hasTarget) { + lir::RegisterPair class_(t->arch->virtualCallTarget()); + lir::Memory virtualCallTargetSrc( + t->arch->stack(), + (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) + * TargetBytesPerWord); + + a->apply(lir::Move, + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_)); + + lir::Memory virtualCallTargetDst(t->arch->thread(), + TARGET_THREAD_VIRTUALCALLTARGET); + + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_), + OperandInfo( + TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst)); + } + + lir::RegisterPair index(t->arch->virtualCallIndex()); + lir::Memory virtualCallIndex(t->arch->thread(), + TARGET_THREAD_VIRTUALCALLINDEX); + + a->apply( + lir::Move, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index), + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex)); + + a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); + + thunk->frameSavedOffset = a->length(); + + lir::RegisterPair thread(t->arch->thread()); + a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); + + compileCall(t, &context, thunkIndex); + + a->popFrame(t->arch->alignFrameSize(1)); + + lir::RegisterPair result(t->arch->returnLow()); + a->apply(lir::Jump, + OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result)); + + thunk->length = a->endBlock(false)->resolve(0, 0); + + thunk->start = finish( + t, allocator, a, name, thunk->length); +} + void compileThunks(MyThread* t, FixedAllocator* allocator) { MyProcessor* p = processor(t); @@ -9836,59 +10128,13 @@ void compileThunks(MyThread* t, FixedAllocator* allocator) = finish(t, allocator, a, "default", p->thunks.default_.length); } - { - Context context(t); - avian::codegen::Assembler* a = context.assembler; + compileDefaultThunk + (t, allocator, &(p->thunks.defaultVirtual), "defaultVirtual", + compileVirtualMethodIndex, true); - lir::RegisterPair class_(t->arch->virtualCallTarget()); - lir::Memory virtualCallTargetSrc( - t->arch->stack(), - (t->arch->frameFooterSize() + t->arch->frameReturnAddressSize()) - * TargetBytesPerWord); - - a->apply(lir::Move, - OperandInfo( - TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetSrc), - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_)); - - lir::Memory virtualCallTargetDst(t->arch->thread(), - TARGET_THREAD_VIRTUALCALLTARGET); - - a->apply( - lir::Move, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &class_), - OperandInfo( - TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallTargetDst)); - - lir::RegisterPair index(t->arch->virtualCallIndex()); - lir::Memory virtualCallIndex(t->arch->thread(), - TARGET_THREAD_VIRTUALCALLINDEX); - - a->apply( - lir::Move, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &index), - OperandInfo(TargetBytesPerWord, lir::Operand::Type::Memory, &virtualCallIndex)); - - a->saveFrame(TARGET_THREAD_STACK, TARGET_THREAD_IP); - - p->thunks.defaultVirtual.frameSavedOffset = a->length(); - - lir::RegisterPair thread(t->arch->thread()); - a->pushFrame(1, TargetBytesPerWord, lir::Operand::Type::RegisterPair, &thread); - - compileCall(t, &context, compileVirtualMethodIndex); - - a->popFrame(t->arch->alignFrameSize(1)); - - lir::RegisterPair result(t->arch->returnLow()); - a->apply(lir::Jump, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &result)); - - p->thunks.defaultVirtual.length = a->endBlock(false)->resolve(0, 0); - - p->thunks.defaultVirtual.start = finish( - t, allocator, a, "defaultVirtual", p->thunks.defaultVirtual.length); - } + compileDefaultThunk + (t, allocator, &(p->thunks.defaultDynamic), "defaultDynamic", + linkDynamicMethodIndex, false); { Context context(t); @@ -10040,6 +10286,11 @@ uintptr_t defaultVirtualThunk(MyThread* t) return reinterpret_cast(processor(t)->thunks.defaultVirtual.start); } +uintptr_t defaultDynamicThunk(MyThread* t) +{ + return reinterpret_cast(processor(t)->thunks.defaultDynamic.start); +} + uintptr_t nativeThunk(MyThread* t) { return reinterpret_cast(processor(t)->thunks.native.start); @@ -10056,7 +10307,11 @@ bool unresolved(MyThread* t, uintptr_t methodAddress) or methodAddress == bootDefaultThunk(t); } -uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) +uintptr_t compileVirtualThunk(MyThread* t, + unsigned index, + unsigned* size, + uintptr_t thunk, + const char* baseName) { Context context(t); avian::codegen::Assembler* a = context.assembler; @@ -10069,11 +10324,10 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &indexConstant), OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &indexRegister)); - avian::codegen::ResolvedPromise defaultVirtualThunkPromise( - defaultVirtualThunk(t)); - lir::Constant thunk(&defaultVirtualThunkPromise); + avian::codegen::ResolvedPromise promise(thunk); + lir::Constant target(&promise); a->apply(lir::Jump, - OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &thunk)); + OperandInfo(TargetBytesPerWord, lir::Operand::Type::Constant, &target)); *size = a->endBlock(false)->resolve(0, 0); @@ -10083,8 +10337,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) a->setDestination(start); a->write(); - const char* const virtualThunkBaseName = "virtualThunk"; - const size_t virtualThunkBaseNameLength = strlen(virtualThunkBaseName); + const size_t virtualThunkBaseNameLength = strlen(baseName); const size_t maxIntStringLength = 10; THREAD_RUNTIME_ARRAY(t, @@ -10094,7 +10347,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size) sprintf(RUNTIME_ARRAY_BODY(virtualThunkName), "%s%d", - virtualThunkBaseName, + baseName, index); logCompile(t, start, *size, 0, RUNTIME_ARRAY_BODY(virtualThunkName), 0); @@ -10120,7 +10373,7 @@ uintptr_t virtualThunk(MyThread* t, unsigned index) if (oldArray->body()[index * 2] == 0) { unsigned size; - uintptr_t thunk = compileVirtualThunk(t, index, &size); + uintptr_t thunk = compileVirtualThunk(t, index, &size, defaultVirtualThunk(t), "virtualThunk"); oldArray->body()[index * 2] = thunk; oldArray->body()[(index * 2) + 1] = size; } @@ -10254,6 +10507,11 @@ avian::util::FixedAllocator* codeAllocator(MyThread* t) return &(processor(t)->codeAllocator); } +Allocator* allocator(MyThread* t) +{ + return processor(t)->allocator; +} + } // namespace local } // namespace diff --git a/src/interpret.cpp b/src/interpret.cpp index 49507ffca5..67871b40d8 100644 --- a/src/interpret.cpp +++ b/src/interpret.cpp @@ -1981,6 +1981,34 @@ loop: } goto loop; + case invokedynamic: { + uint16_t index = codeReadInt16(t, code, ip); + + ip += 2; + + GcInvocation* invocation = cast(t, singletonObject(t, code->pool(), index - 1)); + + GcCallSite* site = invocation->site(); + + loadMemoryBarrier(); + + if (site == 0) { + PROTECT(t, invocation); + + invocation->setClass(t, frameMethod(t, frame)->class_()); + + site = resolveDynamic(t, invocation); + PROTECT(t, site); + + storeStoreMemoryBarrier(); + + invocation->setSite(t, site); + site->setInvocation(t, invocation); + } + + method = site->target()->method(); + } goto invoke; + case invokeinterface: { uint16_t index = codeReadInt16(t, code, ip); diff --git a/src/jnienv.cpp b/src/jnienv.cpp index 5a7fc27d66..fdfe120347 100644 --- a/src/jnienv.cpp +++ b/src/jnienv.cpp @@ -3784,3 +3784,22 @@ extern "C" AVIAN_EXPORT jint JNICALL return run(*t, local::boot, 0) ? 0 : -1; } + +extern "C" AVIAN_EXPORT jstring JNICALL JVM_GetTemporaryDirectory(JNIEnv* e UNUSED) +{ + // Unimplemented + // This is used in newer builds of openjdk8, as a place to store statistics or something... + abort(); +} + +extern "C" AVIAN_EXPORT jboolean JNICALL JVM_KnownToNotExist(JNIEnv* e UNUSED, jobject loader UNUSED, jstring classname UNUSED) +{ + // Unimplemented + abort(); +} + +extern "C" AVIAN_EXPORT jintArray JNICALL JVM_GetResourceLookupCache(JNIEnv* e UNUSED, jobject loader UNUSED, jstring resourcename UNUSED) +{ + // Unimplemented + abort(); +} diff --git a/src/machine.cpp b/src/machine.cpp index 7e2c95c9be..87cdf5f626 100644 --- a/src/machine.cpp +++ b/src/machine.cpp @@ -1220,7 +1220,7 @@ GcClassAddendum* getClassAddendum(Thread* t, GcClass* class_, GcSingleton* pool) if (addendum == 0) { PROTECT(t, class_); - addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0); + addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0, 0); setField(t, class_, ClassAddendum, addendum); } return addendum; @@ -2811,6 +2811,25 @@ void parseAttributeTable(Thread* t, GcClassAddendum* addendum = getClassAddendum(t, class_, pool); addendum->setAnnotationTable(t, body); + } else if (vm::strcmp(reinterpret_cast("BootstrapMethods"), + name->body().begin()) == 0) { + unsigned count = s.read2(); + GcArray* array = makeArray(t, count); + PROTECT(t, array); + + for (unsigned i = 0; i < count; ++i) { + unsigned reference = s.read2() - 1; + unsigned argumentCount = s.read2(); + GcCharArray* element = makeCharArray(t, 1 + argumentCount); + element->body()[0] = reference; + for (unsigned ai = 0; ai < argumentCount; ++ai) { + element->body()[1 + ai] = s.read2() - 1; + } + array->setBodyElement(t, i, element); + } + + GcClassAddendum* addendum = getClassAddendum(t, class_, pool); + addendum->setBootstrapMethodTable(t, array); } else if (vm::strcmp(reinterpret_cast("EnclosingMethod"), name->body().begin()) == 0) { int16_t enclosingClass = s.read2(); @@ -6013,6 +6032,140 @@ GcJclass* getDeclaringClass(Thread* t, GcClass* c) return 0; } +GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation) +{ + PROTECT(t, invocation); + + GcClass* c = invocation->class_(); + PROTECT(t, c); + + GcCharArray* bootstrapArray = cast( + t, + cast(t, c->addendum()->bootstrapMethodTable()) + ->body()[invocation->bootstrap()]); + + PROTECT(t, bootstrapArray); + + GcMethod* bootstrap = cast(t, + resolve(t, + c->loader(), + invocation->pool(), + bootstrapArray->body()[0], + findMethodInClass, + 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); + + GcByteArray* nameBytes = invocation->template_()->name(); + GcString* name + = t->m->classpath->makeString(t, nameBytes, 0, nameBytes->length() - 1); + PROTECT(t, name); + + GcMethodType* type = makeMethodType( + t, c->loader(), invocation->template_()->spec(), 0, 0, 0); + PROTECT(t, type); + + GcArray* array = makeArray(t, bootstrap->parameterCount()); + PROTECT(t, array); + + unsigned argument = 0; + array->setBodyElement(t, argument++, lookup); + array->setBodyElement(t, argument++, name); + array->setBodyElement(t, argument++, type); + + MethodSpecIterator it( + t, reinterpret_cast(bootstrap->spec()->body().begin())); + + for (unsigned i = 0; i < argument; ++i) + it.next(); + + unsigned i = 0; + while (it.hasNext()) { + const char* p = it.next(); + switch (*p) { + case 'L': { + const char* const methodType = "Ljava/lang/invoke/MethodType;"; + const char* const methodHandle = "Ljava/lang/invoke/MethodHandle;"; + if (strncmp(p, methodType, strlen(methodType)) == 0) { + GcMethodType* type = makeMethodType( + t, + c->loader(), + cast( + t, + singletonObject( + t, invocation->pool(), bootstrapArray->body()[i + 1])), + 0, + 0, + 0); + + 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(), + invocation->pool(), + bootstrapArray->body()[i + 1], + findMethodInClass, + GcNoSuchMethodError::Type)); + + GcMethodHandle* handle + = makeMethodHandle(t, kind, c->loader(), method, 0); + + array->setBodyElement(t, i + argument, handle); + } else { + abort(t); + } + } break; + + case 'I': + case 'F': { + GcInt* box = makeInt( + t, + singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1])); + + array->setBodyElement(t, i + argument, box); + } break; + + case 'J': + case 'D': { + uint64_t v; + memcpy( + &v, + &singletonValue(t, invocation->pool(), bootstrapArray->body()[i + 1]), + 8); + + GcLong* box = makeLong(t, v); + + array->setBodyElement(t, i + argument, box); + } break; + + default: + abort(t); + } + + ++i; + } + + 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)); +} + void noop() { } diff --git a/src/tools/bootimage-generator/main.cpp b/src/tools/bootimage-generator/main.cpp index 3a9736dc8e..9d5ab08769 100644 --- a/src/tools/bootimage-generator/main.cpp +++ b/src/tools/bootimage-generator/main.cpp @@ -398,7 +398,8 @@ GcTriple* makeCodeImage(Thread* t, RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t; for (unsigned i = 2; i < count + 2; ++i) { - switch (s.read1()) { + unsigned constType = s.read1(); + switch (constType) { case CONSTANT_Class: case CONSTANT_String: RUNTIME_ARRAY_BODY(types)[i] = Type_object; @@ -436,7 +437,25 @@ GcTriple* makeCodeImage(Thread* t, s.skip(s.read2()); break; + + + case CONSTANT_MethodHandle: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(3); + break; + + case CONSTANT_MethodType: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(2); + break; + + case CONSTANT_InvokeDynamic: + RUNTIME_ARRAY_BODY(types)[i] = Type_object; + s.skip(4); + break; + default: + fprintf(stderr, "unknown class constant: %d\n", constType); abort(t); } } diff --git a/src/types.def b/src/types.def index a140503eba..da247c9d26 100644 --- a/src/types.def +++ b/src/types.def @@ -22,6 +22,16 @@ (type cloneable java/lang/Cloneable) +(type callSite java/lang/invoke/CallSite + (require invocation invocation)) + +(type methodHandle java/lang/invoke/MethodHandle + (alias method method vmtarget)) + +(type methodType java/lang/invoke/MethodType) + +(type lookup java/lang/invoke/MethodHandles$Lookup) + (type singleton avian/Singleton (array maybe_object body)) @@ -76,10 +86,10 @@ (type invocation (uint16_t bootstrap) (int32_t index) - (object class) - (object pool) - (object template) - (object site)) + (class class) + (singleton pool) + (method template) + (callSite site)) (type triple (object first) @@ -406,7 +416,8 @@ (finder virtualFileFinders) (field array virtualFiles) (field array arrayInterfaceTable) - (object threadTerminated)) + (object threadTerminated) + (field array invocations)) (type compileRoots (field array callTable) @@ -415,6 +426,7 @@ (object objectPools) (object staticTableArray) (wordArray virtualThunks) + (wordArray dynamicThunks) (method receiveMethod) (method windMethod) (method rewindMethod)) diff --git a/test/InvokeDynamic.java b/test/InvokeDynamic.java new file mode 100644 index 0000000000..90d88d6fc6 --- /dev/null +++ b/test/InvokeDynamic.java @@ -0,0 +1,31 @@ +public class InvokeDynamic { + private final int foo; + + private InvokeDynamic(int foo) { + this.foo = foo; + } + + private interface Operation { + int operate(int a, int b); + } + + 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; + expect(op.operate(2, 3) == (2 + 3) - 4); + + for (int i = 0; i < 4; ++i) { + new InvokeDynamic(i).test(); + } + } + + private void test() { + int c = 2; + Operation op = (a, b) -> ((a + b) * c) - foo; + expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); + } +} diff --git a/test/Subroutine.java b/test/Subroutine.java index 5fb5bfa223..e03686a9f5 100644 --- a/test/Subroutine.java +++ b/test/Subroutine.java @@ -2,6 +2,7 @@ import avian.Stream; import avian.ConstantPool; import avian.ConstantPool.PoolEntry; import avian.Assembler; +import avian.Assembler.FieldData; import avian.Assembler.MethodData; import java.util.ArrayList; @@ -95,7 +96,7 @@ public class Subroutine { Assembler.writeClass (out, pool, ConstantPool.addClass(pool, name), ConstantPool.addClass(pool, "java/lang/Object"), - new int[0], new MethodData[] + new int[0], new FieldData[0], new MethodData[] { new MethodData(Assembler.ACC_STATIC | Assembler.ACC_PUBLIC, ConstantPool.addUtf8(pool, "test"), ConstantPool.addUtf8(pool, "()V"),