Merge pull request #451 from dicej/invoke-dynamic-stub

implement basic Java 8 lambda support
This commit is contained in:
Joshua Warner 2015-08-18 12:53:16 -06:00
commit 9f70aa753e
29 changed files with 1403 additions and 103 deletions

View File

@ -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;
}
}
}

View File

@ -25,4 +25,6 @@ public class ClassAddendum extends Addendum {
public byte[] enclosingClass;
public Pair enclosingMethod;
public VMMethod[] bootstrapMethodTable;
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,9 @@
package java.lang.invoke;
public class CallSite {
private final MethodHandle target;
CallSite(MethodHandle target) {
this.target = target;
}
}

View File

@ -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(); }
}

View File

@ -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<PoolEntry> 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, "<init>", 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<PoolEntry> 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", "<init>", "()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<PoolEntry> 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<PoolEntry> pool = new ArrayList();
int interfaceIndex = ConstantPool.addClass
(pool, invokedType.returnType().getName().replace('.', '/'));
List<FieldData> 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<MethodData> 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, "<init>"),
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;
}
}
}

View File

@ -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;
}
}

View File

@ -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 + "]";
}
}
}

View File

@ -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<Parameter> 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<Parameter> parameters() {
if (parameters == null) {
List<Parameter> 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_; // :)
}
}
}

View File

@ -18,7 +18,7 @@ import avian.Classes;
import java.lang.annotation.Annotation;
public class Method<T> 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<T> 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);
}

View File

@ -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();

View File

@ -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 $(@)

View File

@ -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 \

View File

@ -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;

View File

@ -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<GcReference>(t, o);
PROTECT(t, reference);
@ -3439,8 +3441,7 @@ inline object resolve(
if (o) {
storeStoreMemoryBarrier();
method->code()->pool()->setBodyElement(
t, index, reinterpret_cast<uintptr_t>(o));
pool->setBodyElement(t, index, reinterpret_cast<uintptr_t>(o));
}
} else {
o = 0;
@ -3459,7 +3460,7 @@ inline GcField* resolveField(Thread* t,
return cast<GcField>(t,
resolve(t,
loader,
method,
method->code()->pool(),
index,
findFieldInClass,
GcNoSuchFieldError::Type,
@ -3562,7 +3563,7 @@ inline GcMethod* resolveMethod(Thread* t,
return cast<GcMethod>(t,
resolve(t,
loader,
method,
method->code()->pool(),
index,
findMethodInClass,
GcNoSuchMethodError::Type,

View File

@ -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

View File

@ -34,6 +34,7 @@ FIELD(virtualThunks)
THUNK_FIELD(default_);
THUNK_FIELD(defaultVirtual);
THUNK_FIELD(defaultDynamic);
THUNK_FIELD(native);
THUNK_FIELD(aioob);
THUNK_FIELD(stackOverflow);

View File

@ -113,6 +113,13 @@ extern "C" AVIAN_EXPORT int64_t JNICALL
cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]))->vmClass());
}
extern "C" AVIAN_EXPORT int64_t JNICALL
Avian_avian_Classes_toVMMethod(Thread* t, object, uintptr_t* arguments)
{
return reinterpret_cast<intptr_t>(t->m->classpath->getVMMethod(
t, cast<GcJmethod>(t, reinterpret_cast<object>(arguments[0]))));
}
extern "C" AVIAN_EXPORT void JNICALL
Avian_avian_Classes_initialize(Thread* t, object, uintptr_t* arguments)
{

View File

@ -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,

View File

@ -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<ir::Value*> 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<MyThread*>(t->peer), o);
if (t->child)
updateDynamicTable(static_cast<MyThread*>(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<unsigned>(index) >= oldCapacity) {
unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096;
void** newTable = static_cast<void**>(
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<MyThread*>(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<void*>(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<GcInvocation>(
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<uintptr_t>(compileVirtualMethod2(t, class_, index));
}
void* linkDynamicMethod2(MyThread* t, unsigned index)
{
GcInvocation* invocation
= cast<GcInvocation>(t, roots(t)->invocations()->body()[index]);
GcCallSite* site = invocation->site();
loadMemoryBarrier();
if (site == 0) {
t->trace->targetMethod = invocation->template_();
THREAD_RESOURCE0(t, static_cast<MyThread*>(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<void*>(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<void*>(methodAddress(t, target));
}
uint64_t linkDynamicMethod(MyThread* t)
{
unsigned index = t->virtualCallIndex;
t->virtualCallIndex = 0;
return reinterpret_cast<uintptr_t>(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<uint8_t>(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<MyThread*>(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<MyProcessor*>(t->m->processor)->dynamicIndex;
}
void**& dynamicTable(MyThread* t)
{
return static_cast<MyProcessor*>(t->m->processor)->dynamicTable;
}
unsigned& dynamicTableSize(MyThread* t)
{
return static_cast<MyProcessor*>(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<uintptr_t>(processor(t)->thunks.defaultVirtual.start);
}
uintptr_t defaultDynamicThunk(MyThread* t)
{
return reinterpret_cast<uintptr_t>(processor(t)->thunks.defaultDynamic.start);
}
uintptr_t nativeThunk(MyThread* t)
{
return reinterpret_cast<uintptr_t>(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

View File

@ -1981,6 +1981,34 @@ loop:
}
goto loop;
case invokedynamic: {
uint16_t index = codeReadInt16(t, code, ip);
ip += 2;
GcInvocation* invocation = cast<GcInvocation>(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);

View File

@ -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();
}

View File

@ -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<const int8_t*>("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<const int8_t*>("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<GcCharArray>(
t,
cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
->body()[invocation->bootstrap()]);
PROTECT(t, bootstrapArray);
GcMethod* bootstrap = cast<GcMethod>(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<const char*>(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<GcByteArray>(
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<GcReference>(
t,
singletonObject(
t, invocation->pool(), bootstrapArray->body()[i + 1]));
int kind = reference->kind();
GcMethod* method = cast<GcMethod>(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<GcCallSite>(
t, t->m->processor->invokeArray(t, bootstrap, handle, array));
}
void noop()
{
}

View File

@ -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);
}
}

View File

@ -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))

31
test/InvokeDynamic.java Normal file
View File

@ -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);
}
}

View File

@ -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"),