mirror of
https://github.com/corda/corda.git
synced 2025-01-03 19:54:13 +00:00
Merge pull request #451 from dicej/invoke-dynamic-stub
implement basic Java 8 lambda support
This commit is contained in:
commit
9f70aa753e
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +25,6 @@ public class ClassAddendum extends Addendum {
|
||||
public byte[] enclosingClass;
|
||||
|
||||
public Pair enclosingMethod;
|
||||
|
||||
public VMMethod[] bootstrapMethodTable;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
21
classpath/java/lang/InternalError.java
Normal file
21
classpath/java/lang/InternalError.java
Normal 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);
|
||||
}
|
||||
}
|
21
classpath/java/lang/TypeNotPresentException.java
Normal file
21
classpath/java/lang/TypeNotPresentException.java
Normal 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);
|
||||
}
|
||||
}
|
9
classpath/java/lang/invoke/CallSite.java
Normal file
9
classpath/java/lang/invoke/CallSite.java
Normal file
@ -0,0 +1,9 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
public class CallSite {
|
||||
private final MethodHandle target;
|
||||
|
||||
CallSite(MethodHandle target) {
|
||||
this.target = target;
|
||||
}
|
||||
}
|
@ -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(); }
|
||||
}
|
284
classpath/java/lang/invoke/LambdaMetafactory.java
Normal file
284
classpath/java/lang/invoke/LambdaMetafactory.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
40
classpath/java/lang/invoke/MethodHandle.java
Normal file
40
classpath/java/lang/invoke/MethodHandle.java
Normal 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;
|
||||
}
|
||||
}
|
18
classpath/java/lang/invoke/MethodHandles.java
Normal file
18
classpath/java/lang/invoke/MethodHandles.java
Normal 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 + "]";
|
||||
}
|
||||
}
|
||||
}
|
305
classpath/java/lang/invoke/MethodType.java
Normal file
305
classpath/java/lang/invoke/MethodType.java
Normal 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_; // :)
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
23
makefile
23
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 $(@)
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -34,6 +34,7 @@ FIELD(virtualThunks)
|
||||
|
||||
THUNK_FIELD(default_);
|
||||
THUNK_FIELD(defaultVirtual);
|
||||
THUNK_FIELD(defaultDynamic);
|
||||
THUNK_FIELD(native);
|
||||
THUNK_FIELD(aioob);
|
||||
THUNK_FIELD(stackOverflow);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
396
src/compile.cpp
396
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<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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
155
src/machine.cpp
155
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<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()
|
||||
{
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
31
test/InvokeDynamic.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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"),
|
||||
|
Loading…
Reference in New Issue
Block a user