mirror of
https://github.com/corda/corda.git
synced 2025-01-19 03:06:36 +00:00
implement basic Java 8 lambda support
The two big pieces here are basic invokedynamic support and a working version of LambdaMetaFactory.metafactory. The latter works by dynamically building a synthetic class with three methods: a static factory method, a constructor for the factory method to call, and a method to satisfy the requested interface which defers to the specified MethodHandle. This work relies heavily on Avian's specific MethodType and MethodHandle implementations, which provide extra, non-standard features to make code generation easier. That means we'll probably need to use Avian's versions of java.lang.invoke.* even when building with the OpenJDK or Android class libraries.
This commit is contained in:
parent
792684b935
commit
2465459079
@ -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;
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ public class Classes {
|
||||
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 +576,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);
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
public abstract class CallSite {
|
||||
public class CallSite {
|
||||
private final MethodHandle target;
|
||||
|
||||
CallSite(MethodHandle target) {
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,275 @@
|
||||
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 {
|
||||
public static CallSite metafactory(MethodHandles.Lookup l, String s, MethodType mt, MethodType mt2, MethodHandle mh, MethodType mt3) throws LambdaConversionException { throw new RuntimeException(); }
|
||||
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);
|
||||
}
|
||||
|
||||
write1(out, invokestatic);
|
||||
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.STATIC,
|
||||
ConstantPool.addUtf8(pool, "make"),
|
||||
ConstantPool.addUtf8(pool, Classes.makeString
|
||||
(invokedType.spec, 0,
|
||||
invokedType.spec.length - 1)),
|
||||
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, Classes.makeString
|
||||
(methodType.spec, 0,
|
||||
methodType.spec.length - 1)),
|
||||
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
|
||||
(invokedType.loader, avian.SystemClassLoader.getClass
|
||||
(avian.Classes.defineVMClass
|
||||
(invokedType.loader, classData, 0, classData.length))
|
||||
.getMethod("make", invokedType.parameterArray()).vmMethod));
|
||||
} catch (NoSuchMethodException e) {
|
||||
AssertionError error = new AssertionError();
|
||||
error.initCause(e);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,23 @@
|
||||
package java.lang.invoke;
|
||||
public abstract class MethodHandle {
|
||||
|
||||
public class MethodHandle {
|
||||
private final ClassLoader loader;
|
||||
final avian.VMMethod method;
|
||||
private volatile MethodType type;
|
||||
|
||||
MethodHandle(ClassLoader loader, avian.VMMethod method) {
|
||||
this.loader = loader;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new java.lang.reflect.Method(method).toString();
|
||||
}
|
||||
|
||||
public MethodType type() {
|
||||
if (type == null) {
|
||||
type = new MethodType(loader, method.spec);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +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 + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,261 @@
|
||||
package java.lang.invoke;
|
||||
|
||||
import static avian.Assembler.*;
|
||||
|
||||
import avian.Classes;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public final class MethodType implements java.io.Serializable {
|
||||
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 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) {
|
||||
switch (spec[i]) {
|
||||
case 'L': {
|
||||
int start = i;
|
||||
++ i;
|
||||
while (spec[i] != ';') ++ i;
|
||||
|
||||
list.add(new Parameter
|
||||
(index,
|
||||
position,
|
||||
Classes.makeString(spec, start, (i - start) + 1),
|
||||
aload));
|
||||
} break;
|
||||
|
||||
case '[': {
|
||||
int start = i;
|
||||
++ i;
|
||||
while (spec[i] == '[') ++ i;
|
||||
|
||||
switch (spec[i]) {
|
||||
case 'L':
|
||||
++ i;
|
||||
while (spec[i] != ';') ++ i;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
list.add(new Parameter
|
||||
(index,
|
||||
position,
|
||||
Classes.makeString(spec, start, (i - start) + 1),
|
||||
aload));
|
||||
} break;
|
||||
|
||||
case 'Z':
|
||||
case 'B':
|
||||
case 'S':
|
||||
case 'C':
|
||||
case 'I':
|
||||
list.add(new Parameter
|
||||
(index,
|
||||
position,
|
||||
Classes.makeString(spec, i, 1),
|
||||
iload));
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
list.add(new Parameter
|
||||
(index,
|
||||
position,
|
||||
Classes.makeString(spec, i, 1),
|
||||
fload));
|
||||
break;
|
||||
|
||||
case 'J':
|
||||
list.add(new Parameter
|
||||
(index,
|
||||
position,
|
||||
Classes.makeString(spec, i, 1),
|
||||
lload));
|
||||
|
||||
++ position;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
list.add(new Parameter
|
||||
(index,
|
||||
position,
|
||||
Classes.makeString(spec, i, 1),
|
||||
dload));
|
||||
|
||||
++ position;
|
||||
break;
|
||||
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
|
||||
++ index;
|
||||
++ position;
|
||||
}
|
||||
|
||||
footprint = position;
|
||||
|
||||
++ i;
|
||||
|
||||
switch (spec[i]) {
|
||||
case 'L': {
|
||||
int start = i;
|
||||
++ i;
|
||||
while (spec[i] != ';') ++ i;
|
||||
|
||||
result = new Result
|
||||
(Classes.makeString(spec, start, (i - start) + 1), areturn);
|
||||
} break;
|
||||
|
||||
case '[': {
|
||||
int start = i;
|
||||
++ i;
|
||||
while (spec[i] == '[') ++ i;
|
||||
|
||||
switch (spec[i]) {
|
||||
case 'L':
|
||||
++ i;
|
||||
while (spec[i] != ';') ++ i;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
result = new Result(Classes.makeString(spec, start, (i - start) + 1),
|
||||
areturn);
|
||||
} break;
|
||||
|
||||
case 'V':
|
||||
result = new Result(Classes.makeString(spec, i, 1), return_);
|
||||
break;
|
||||
|
||||
case 'Z':
|
||||
case 'B':
|
||||
case 'S':
|
||||
case 'C':
|
||||
case 'I':
|
||||
result = new Result(Classes.makeString(spec, i, 1), ireturn);
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
result = new Result(Classes.makeString(spec, i, 1), freturn);
|
||||
break;
|
||||
|
||||
case 'J':
|
||||
result = new Result(Classes.makeString(spec, i, 1), lreturn);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
result = new Result(Classes.makeString(spec, i, 1), dreturn);
|
||||
break;
|
||||
|
||||
default: throw new AssertionError();
|
||||
}
|
||||
|
||||
parameters = list;
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public Result result() {
|
||||
parameters(); // ensure spec has been parsed
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public 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,
|
||||
int load)
|
||||
{
|
||||
this.index = index;
|
||||
this.position = position;
|
||||
this.spec = spec;
|
||||
this.type = Classes.forCanonicalName(loader, spec);
|
||||
this.load = load;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public int position() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public String spec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public int load() {
|
||||
return load;
|
||||
}
|
||||
}
|
||||
|
||||
public class Result {
|
||||
private final String spec;
|
||||
private final Class type;
|
||||
private final int return_;
|
||||
|
||||
public Result(String spec, int return_) {
|
||||
this.spec = spec;
|
||||
this.type = Classes.forCanonicalName(loader, spec);
|
||||
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) {
|
||||
@ -59,6 +59,18 @@ public class Method<T> extends AccessibleObject implements Member {
|
||||
return getSpec(vmMethod);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (vmMethod.class_ != null) {
|
||||
sb.append(Classes.makeString(vmMethod.class_.name, 0,
|
||||
vmMethod.class_.name.length - 1));
|
||||
sb.append(".");
|
||||
}
|
||||
sb.append(getName());
|
||||
sb.append(getSpec());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getSpec(VMMethod vmMethod) {
|
||||
return Classes.makeString(vmMethod.spec, 0, vmMethod.spec.length - 1);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -3408,18 +3408,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 +3439,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 +3458,7 @@ inline GcField* resolveField(Thread* t,
|
||||
return cast<GcField>(t,
|
||||
resolve(t,
|
||||
loader,
|
||||
method,
|
||||
method->code()->pool(),
|
||||
index,
|
||||
findFieldInClass,
|
||||
GcNoSuchFieldError::Type,
|
||||
@ -3562,7 +3561,7 @@ inline GcMethod* resolveMethod(Thread* t,
|
||||
return cast<GcMethod>(t,
|
||||
resolve(t,
|
||||
loader,
|
||||
method,
|
||||
method->code()->pool(),
|
||||
index,
|
||||
findMethodInClass,
|
||||
GcNoSuchMethodError::Type,
|
||||
|
215
src/compile.cpp
215
src/compile.cpp
@ -1284,6 +1284,8 @@ unsigned& dynamicIndex(MyThread* t);
|
||||
|
||||
void**& dynamicTable(MyThread* t);
|
||||
|
||||
unsigned& dynamicTableSize(MyThread* t);
|
||||
|
||||
void updateDynamicTable(MyThread* t, MyThread* o)
|
||||
{
|
||||
o->dynamicTable = dynamicTable(t);
|
||||
@ -1301,6 +1303,7 @@ uintptr_t compileVirtualThunk(MyThread* t,
|
||||
uintptr_t thunk,
|
||||
const char* baseName);
|
||||
|
||||
Allocator* allocator(MyThread* t);
|
||||
|
||||
unsigned addDynamic(MyThread* t, GcInvocation* invocation)
|
||||
{
|
||||
@ -1319,7 +1322,7 @@ unsigned addDynamic(MyThread* t, GcInvocation* invocation)
|
||||
unsigned newCapacity = oldCapacity ? 2 * oldCapacity : 4096;
|
||||
|
||||
void** newTable = static_cast<void**>(
|
||||
t->m->heap->allocate(newCapacity * BytesPerWord));
|
||||
allocator(t)->allocate(newCapacity * BytesPerWord));
|
||||
|
||||
GcArray* newData = makeArray(t, newCapacity);
|
||||
PROTECT(t, newData);
|
||||
@ -1345,7 +1348,11 @@ unsigned addDynamic(MyThread* t, GcInvocation* invocation)
|
||||
|
||||
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);
|
||||
@ -7050,13 +7057,13 @@ void finish(MyThread* t, FixedAllocator* allocator, Context* context)
|
||||
reinterpret_cast<const char*>(context->method->spec()->body().begin()));
|
||||
|
||||
// for debugging:
|
||||
if (true
|
||||
if (false
|
||||
and ::strcmp(reinterpret_cast<const char*>(
|
||||
context->method->class_()->name()->body().begin()),
|
||||
"InvokeDynamic") == 0
|
||||
"java/lang/System") == 0
|
||||
and ::strcmp(reinterpret_cast<const char*>(
|
||||
context->method->name()->body().begin()),
|
||||
"main") == 0) {
|
||||
"<clinit>") == 0) {
|
||||
trap();
|
||||
}
|
||||
syncInstructionCache(start, codeSize);
|
||||
@ -7271,6 +7278,187 @@ uint64_t compileVirtualMethod(MyThread* t)
|
||||
return reinterpret_cast<uintptr_t>(compileVirtualMethod2(t, class_, index));
|
||||
}
|
||||
|
||||
GcCallSite* resolveDynamic(MyThread* 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) {
|
||||
GcMethod* method = cast<GcMethod>(t,
|
||||
resolve(t,
|
||||
c->loader(),
|
||||
invocation->pool(),
|
||||
bootstrapArray->body()[i + 1],
|
||||
findMethodInClass,
|
||||
GcNoSuchMethodError::Type));
|
||||
|
||||
GcMethodHandle* handle = makeMethodHandle(t, 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, c->loader(), bootstrap, 0);
|
||||
|
||||
return cast<GcCallSite>(
|
||||
t, t->m->processor->invokeArray(t, bootstrap, handle, array));
|
||||
}
|
||||
|
||||
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;
|
||||
@ -8482,10 +8670,12 @@ class MyProcessor : public Processor {
|
||||
dynamicIndex(0),
|
||||
useNativeFeatures(useNativeFeatures),
|
||||
compilationHandlers(0),
|
||||
dynamicTable(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);
|
||||
@ -8956,6 +9146,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));
|
||||
@ -9244,6 +9438,7 @@ class MyProcessor : public Processor {
|
||||
void* thunkTable[dummyIndex + 1];
|
||||
CompilationHandlerList* compilationHandlers;
|
||||
void** dynamicTable;
|
||||
unsigned dynamicTableSize;
|
||||
};
|
||||
|
||||
unsigned& dynamicIndex(MyThread* t)
|
||||
@ -9256,6 +9451,11 @@ 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) {
|
||||
@ -10433,6 +10633,11 @@ avian::util::FixedAllocator* codeAllocator(MyThread* t)
|
||||
return &(processor(t)->codeAllocator);
|
||||
}
|
||||
|
||||
Allocator* allocator(MyThread* t)
|
||||
{
|
||||
return processor(t)->allocator;
|
||||
}
|
||||
|
||||
} // namespace local
|
||||
|
||||
} // namespace
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
(class class)
|
||||
(singleton pool)
|
||||
(method template)
|
||||
(object site))
|
||||
(callSite site))
|
||||
|
||||
(type triple
|
||||
(object first)
|
||||
|
@ -1,7 +1,15 @@
|
||||
public class InvokeDynamic {
|
||||
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) {
|
||||
Runnable r = () -> System.out.println("success");
|
||||
r.run();
|
||||
int c = 4;
|
||||
Operation op = (a, b) -> a + b - c;
|
||||
expect(op.operate(2, 3) == 1);
|
||||
}
|
||||
}
|
||||
|
@ -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