mirror of
https://github.com/corda/corda.git
synced 2025-01-07 13:38:47 +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 name,
|
||||||
int super_,
|
int super_,
|
||||||
int[] interfaces,
|
int[] interfaces,
|
||||||
|
FieldData[] fields,
|
||||||
MethodData[] methods)
|
MethodData[] methods)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -83,7 +84,13 @@ public class Assembler {
|
|||||||
write2(out, i + 1);
|
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);
|
write2(out, methods.length);
|
||||||
for (MethodData m: methods) {
|
for (MethodData m: methods) {
|
||||||
@ -113,4 +120,16 @@ public class Assembler {
|
|||||||
this.code = code;
|
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 byte[] enclosingClass;
|
||||||
|
|
||||||
public Pair enclosingMethod;
|
public Pair enclosingMethod;
|
||||||
|
|
||||||
|
public VMMethod[] bootstrapMethodTable;
|
||||||
}
|
}
|
||||||
|
@ -45,11 +45,13 @@ public class Classes {
|
|||||||
|
|
||||||
public static native VMClass toVMClass(Class c);
|
public static native VMClass toVMClass(Class c);
|
||||||
|
|
||||||
|
public static native VMMethod toVMMethod(Method m);
|
||||||
|
|
||||||
private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec)
|
private static native VMClass resolveVMClass(ClassLoader loader, byte[] spec)
|
||||||
throws ClassNotFoundException;
|
throws ClassNotFoundException;
|
||||||
|
|
||||||
private static VMClass loadVMClass(ClassLoader loader,
|
public static VMClass loadVMClass(ClassLoader loader,
|
||||||
byte[] nameBytes, int offset, int length)
|
byte[] nameBytes, int offset, int length)
|
||||||
{
|
{
|
||||||
byte[] spec = new byte[length + 1];
|
byte[] spec = new byte[length + 1];
|
||||||
System.arraycopy(nameBytes, offset, spec, 0, length);
|
System.arraycopy(nameBytes, offset, spec, 0, 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;
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
public class Method<T> extends AccessibleObject implements Member {
|
public class Method<T> extends AccessibleObject implements Member {
|
||||||
private final VMMethod vmMethod;
|
public final VMMethod vmMethod;
|
||||||
private boolean accessible;
|
private boolean accessible;
|
||||||
|
|
||||||
public Method(VMMethod vmMethod) {
|
public Method(VMMethod vmMethod) {
|
||||||
|
@ -23,6 +23,7 @@ import avian.ConstantPool;
|
|||||||
import avian.ConstantPool.PoolEntry;
|
import avian.ConstantPool.PoolEntry;
|
||||||
|
|
||||||
import avian.Assembler;
|
import avian.Assembler;
|
||||||
|
import avian.Assembler.FieldData;
|
||||||
import avian.Assembler.MethodData;
|
import avian.Assembler.MethodData;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -402,6 +403,7 @@ public class Proxy {
|
|||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
Assembler.writeClass
|
Assembler.writeClass
|
||||||
(out, pool, nameIndex, superIndex, interfaceIndexes,
|
(out, pool, nameIndex, superIndex, interfaceIndexes,
|
||||||
|
new FieldData[0],
|
||||||
methodTable.toArray(new MethodData[methodTable.size()]));
|
methodTable.toArray(new MethodData[methodTable.size()]));
|
||||||
|
|
||||||
byte[] classData = out.toByteArray();
|
byte[] classData = out.toByteArray();
|
||||||
|
23
makefile
23
makefile
@ -3,6 +3,10 @@ MAKEFLAGS = -s
|
|||||||
name = avian
|
name = avian
|
||||||
version := $(shell grep version gradle.properties | cut -d'=' -f2)
|
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 \
|
build-arch := $(shell uname -m \
|
||||||
| sed 's/^i.86$$/i386/' \
|
| sed 's/^i.86$$/i386/' \
|
||||||
| sed 's/^x86pc$$/i386/' \
|
| sed 's/^x86pc$$/i386/' \
|
||||||
@ -1473,7 +1477,13 @@ ifneq ($(classpath),avian)
|
|||||||
$(classpath-src)/avian/VMField.java \
|
$(classpath-src)/avian/VMField.java \
|
||||||
$(classpath-src)/avian/VMMethod.java \
|
$(classpath-src)/avian/VMMethod.java \
|
||||||
$(classpath-src)/avian/avianvmresource/Handler.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),)
|
ifeq ($(openjdk),)
|
||||||
classpath-sources := $(classpath-sources) \
|
classpath-sources := $(classpath-sources) \
|
||||||
@ -1509,7 +1519,10 @@ vm-classes = \
|
|||||||
avian/resource/*.class
|
avian/resource/*.class
|
||||||
|
|
||||||
test-support-sources = $(shell find $(test)/avian/ -name '*.java')
|
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-cpp-sources = $(wildcard $(test)/*.cpp)
|
||||||
test-sources += $(test-support-sources)
|
test-sources += $(test-support-sources)
|
||||||
test-support-classes = $(call java-classes, $(test-support-sources),$(test),$(test-build))
|
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) \
|
classes="$(shell $(MAKE) -s --no-print-directory build=$(build) \
|
||||||
$(classpath-classes) arch=$(build-arch) platform=$(bootimage-platform))"; \
|
$(classpath-classes) arch=$(build-arch) platform=$(bootimage-platform))"; \
|
||||||
if [ -n "$${classes}" ]; then \
|
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) \
|
-d $(classpath-build) -bootclasspath $(boot-classpath) \
|
||||||
$${classes}; fi
|
$${classes}; fi
|
||||||
@touch $(@)
|
@touch $(@)
|
||||||
@ -1765,7 +1778,7 @@ $(test-dep): $(test-sources) $(test-library)
|
|||||||
@mkdir -p $(test-build)
|
@mkdir -p $(test-build)
|
||||||
files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \
|
files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-classes))"; \
|
||||||
if test -n "$${files}"; then \
|
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}; \
|
-classpath $(test-build) -d $(test-build) -bootclasspath $(boot-classpath) $${files}; \
|
||||||
fi
|
fi
|
||||||
$(javac) -source 1.2 -target 1.1 -XDjsrlimit=0 -d $(test-build) \
|
$(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)
|
@mkdir -p $(test-build)
|
||||||
files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \
|
files="$(shell $(MAKE) -s --no-print-directory build=$(build) $(test-extra-classes))"; \
|
||||||
if test -n "$${files}"; then \
|
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}; \
|
-d $(test-build) -bootclasspath $(boot-classpath) $${files}; \
|
||||||
fi
|
fi
|
||||||
@touch $(@)
|
@touch $(@)
|
||||||
|
@ -49,6 +49,7 @@ openjdk-sources = \
|
|||||||
$(openjdk-src)/share/native/java/util/zip/zip_util.c \
|
$(openjdk-src)/share/native/java/util/zip/zip_util.c \
|
||||||
$(openjdk-src)/share/native/sun/management/VMManagementImpl.c \
|
$(openjdk-src)/share/native/sun/management/VMManagementImpl.c \
|
||||||
$(openjdk-src)/share/native/sun/misc/GC.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/MessageUtils.c \
|
||||||
$(openjdk-src)/share/native/sun/misc/NativeSignalHandler.c \
|
$(openjdk-src)/share/native/sun/misc/NativeSignalHandler.c \
|
||||||
$(openjdk-src)/share/native/sun/misc/Signal.c \
|
$(openjdk-src)/share/native/sun/misc/Signal.c \
|
||||||
@ -121,6 +122,7 @@ openjdk-headers-classes = \
|
|||||||
sun.misc.VM \
|
sun.misc.VM \
|
||||||
sun.misc.VMSupport \
|
sun.misc.VMSupport \
|
||||||
sun.misc.Version \
|
sun.misc.Version \
|
||||||
|
sun.misc.URLClassPath \
|
||||||
sun.net.spi.DefaultProxySelector \
|
sun.net.spi.DefaultProxySelector \
|
||||||
sun.nio.ch.FileKey \
|
sun.nio.ch.FileKey \
|
||||||
sun.nio.ch.FileChannelImpl \
|
sun.nio.ch.FileChannelImpl \
|
||||||
|
@ -146,6 +146,7 @@ enum OpCode {
|
|||||||
imul = 0x68,
|
imul = 0x68,
|
||||||
ineg = 0x74,
|
ineg = 0x74,
|
||||||
instanceof = 0xc1,
|
instanceof = 0xc1,
|
||||||
|
invokedynamic = 0xba,
|
||||||
invokeinterface = 0xb9,
|
invokeinterface = 0xb9,
|
||||||
invokespecial = 0xb7,
|
invokespecial = 0xb7,
|
||||||
invokestatic = 0xb8,
|
invokestatic = 0xb8,
|
||||||
@ -262,6 +263,16 @@ const unsigned ACC_INTERFACE = 1 << 9;
|
|||||||
const unsigned ACC_ABSTRACT = 1 << 10;
|
const unsigned ACC_ABSTRACT = 1 << 10;
|
||||||
const unsigned ACC_STRICT = 1 << 11;
|
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_COMMIT = 1;
|
||||||
const int AVIAN_JNI_ABORT = 2;
|
const int AVIAN_JNI_ABORT = 2;
|
||||||
|
|
||||||
|
@ -2349,6 +2349,8 @@ GcClass* findLoadedClass(Thread* t, GcClassLoader* loader, GcByteArray* spec);
|
|||||||
|
|
||||||
GcJclass* getDeclaringClass(Thread* t, GcClass* c);
|
GcJclass* getDeclaringClass(Thread* t, GcClass* c);
|
||||||
|
|
||||||
|
GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation);
|
||||||
|
|
||||||
inline bool emptyMethod(Thread* t UNUSED, GcMethod* method)
|
inline bool emptyMethod(Thread* t UNUSED, GcMethod* method)
|
||||||
{
|
{
|
||||||
return ((method->flags() & ACC_NATIVE) == 0)
|
return ((method->flags() & ACC_NATIVE) == 0)
|
||||||
@ -3408,18 +3410,18 @@ inline GcClass* resolveClassInPool(Thread* t,
|
|||||||
inline object resolve(
|
inline object resolve(
|
||||||
Thread* t,
|
Thread* t,
|
||||||
GcClassLoader* loader,
|
GcClassLoader* loader,
|
||||||
GcMethod* method,
|
GcSingleton* pool,
|
||||||
unsigned index,
|
unsigned index,
|
||||||
object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*),
|
object (*find)(vm::Thread*, GcClass*, GcByteArray*, GcByteArray*),
|
||||||
Gc::Type errorType,
|
Gc::Type errorType,
|
||||||
bool throw_ = true)
|
bool throw_ = true)
|
||||||
{
|
{
|
||||||
object o = singletonObject(t, method->code()->pool(), index);
|
object o = singletonObject(t, pool, index);
|
||||||
|
|
||||||
loadMemoryBarrier();
|
loadMemoryBarrier();
|
||||||
|
|
||||||
if (objectClass(t, o) == type(t, GcReference::Type)) {
|
if (objectClass(t, o) == type(t, GcReference::Type)) {
|
||||||
PROTECT(t, method);
|
PROTECT(t, pool);
|
||||||
|
|
||||||
GcReference* reference = cast<GcReference>(t, o);
|
GcReference* reference = cast<GcReference>(t, o);
|
||||||
PROTECT(t, reference);
|
PROTECT(t, reference);
|
||||||
@ -3439,8 +3441,7 @@ inline object resolve(
|
|||||||
if (o) {
|
if (o) {
|
||||||
storeStoreMemoryBarrier();
|
storeStoreMemoryBarrier();
|
||||||
|
|
||||||
method->code()->pool()->setBodyElement(
|
pool->setBodyElement(t, index, reinterpret_cast<uintptr_t>(o));
|
||||||
t, index, reinterpret_cast<uintptr_t>(o));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
o = 0;
|
o = 0;
|
||||||
@ -3459,7 +3460,7 @@ inline GcField* resolveField(Thread* t,
|
|||||||
return cast<GcField>(t,
|
return cast<GcField>(t,
|
||||||
resolve(t,
|
resolve(t,
|
||||||
loader,
|
loader,
|
||||||
method,
|
method->code()->pool(),
|
||||||
index,
|
index,
|
||||||
findFieldInClass,
|
findFieldInClass,
|
||||||
GcNoSuchFieldError::Type,
|
GcNoSuchFieldError::Type,
|
||||||
@ -3562,7 +3563,7 @@ inline GcMethod* resolveMethod(Thread* t,
|
|||||||
return cast<GcMethod>(t,
|
return cast<GcMethod>(t,
|
||||||
resolve(t,
|
resolve(t,
|
||||||
loader,
|
loader,
|
||||||
method,
|
method->code()->pool(),
|
||||||
index,
|
index,
|
||||||
findMethodInClass,
|
findMethodInClass,
|
||||||
GcNoSuchMethodError::Type,
|
GcNoSuchMethodError::Type,
|
||||||
|
@ -30,7 +30,8 @@
|
|||||||
#define TARGET_THREAD_HEAPIMAGE 2312
|
#define TARGET_THREAD_HEAPIMAGE 2312
|
||||||
#define TARGET_THREAD_CODEIMAGE 2320
|
#define TARGET_THREAD_CODEIMAGE 2320
|
||||||
#define TARGET_THREAD_THUNKTABLE 2328
|
#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)
|
#elif(TARGET_BYTES_PER_WORD == 4)
|
||||||
|
|
||||||
@ -50,7 +51,8 @@
|
|||||||
#define TARGET_THREAD_HEAPIMAGE 2192
|
#define TARGET_THREAD_HEAPIMAGE 2192
|
||||||
#define TARGET_THREAD_CODEIMAGE 2196
|
#define TARGET_THREAD_CODEIMAGE 2196
|
||||||
#define TARGET_THREAD_THUNKTABLE 2200
|
#define TARGET_THREAD_THUNKTABLE 2200
|
||||||
#define TARGET_THREAD_STACKLIMIT 2224
|
#define TARGET_THREAD_DYNAMICTABLE 2204
|
||||||
|
#define TARGET_THREAD_STACKLIMIT 2228
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error
|
#error
|
||||||
|
@ -34,6 +34,7 @@ FIELD(virtualThunks)
|
|||||||
|
|
||||||
THUNK_FIELD(default_);
|
THUNK_FIELD(default_);
|
||||||
THUNK_FIELD(defaultVirtual);
|
THUNK_FIELD(defaultVirtual);
|
||||||
|
THUNK_FIELD(defaultDynamic);
|
||||||
THUNK_FIELD(native);
|
THUNK_FIELD(native);
|
||||||
THUNK_FIELD(aioob);
|
THUNK_FIELD(aioob);
|
||||||
THUNK_FIELD(stackOverflow);
|
THUNK_FIELD(stackOverflow);
|
||||||
|
@ -113,6 +113,13 @@ extern "C" AVIAN_EXPORT int64_t JNICALL
|
|||||||
cast<GcJclass>(t, reinterpret_cast<object>(arguments[0]))->vmClass());
|
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
|
extern "C" AVIAN_EXPORT void JNICALL
|
||||||
Avian_avian_Classes_initialize(Thread* t, object, uintptr_t* arguments)
|
Avian_avian_Classes_initialize(Thread* t, object, uintptr_t* arguments)
|
||||||
{
|
{
|
||||||
|
@ -484,6 +484,7 @@ class MyClasspath : public Classpath {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oarray = charArray;
|
oarray = charArray;
|
||||||
|
offset = 0;
|
||||||
} else {
|
} else {
|
||||||
expect(t, objectClass(t, oarray) == type(t, GcCharArray::Type));
|
expect(t, objectClass(t, oarray) == type(t, GcCharArray::Type));
|
||||||
}
|
}
|
||||||
@ -2091,12 +2092,21 @@ void interceptFileOperations(Thread* t, bool updateRuntimeData)
|
|||||||
if (fileInputStreamFdField) {
|
if (fileInputStreamFdField) {
|
||||||
cp->fileInputStreamFdField = fileInputStreamFdField->offset();
|
cp->fileInputStreamFdField = fileInputStreamFdField->offset();
|
||||||
|
|
||||||
intercept(t,
|
if (findMethodOrNull(t, fileInputStreamClass, "open0", "(Ljava/lang/String;)V") != 0) {
|
||||||
fileInputStreamClass,
|
intercept(t,
|
||||||
"open",
|
fileInputStreamClass,
|
||||||
"(Ljava/lang/String;)V",
|
"open0",
|
||||||
voidPointer(openFile),
|
"(Ljava/lang/String;)V",
|
||||||
updateRuntimeData);
|
voidPointer(openFile),
|
||||||
|
updateRuntimeData);
|
||||||
|
} else {
|
||||||
|
intercept(t,
|
||||||
|
fileInputStreamClass,
|
||||||
|
"open",
|
||||||
|
"(Ljava/lang/String;)V",
|
||||||
|
voidPointer(openFile),
|
||||||
|
updateRuntimeData);
|
||||||
|
}
|
||||||
|
|
||||||
if (findMethodOrNull(t, fileInputStreamClass, "read0", "()I") != 0) {
|
if (findMethodOrNull(t, fileInputStreamClass, "read0", "()I") != 0) {
|
||||||
intercept(t,
|
intercept(t,
|
||||||
|
396
src/compile.cpp
396
src/compile.cpp
@ -83,6 +83,7 @@ const unsigned InitialZoneCapacityInBytes = 64 * 1024;
|
|||||||
enum ThunkIndex {
|
enum ThunkIndex {
|
||||||
compileMethodIndex,
|
compileMethodIndex,
|
||||||
compileVirtualMethodIndex,
|
compileVirtualMethodIndex,
|
||||||
|
linkDynamicMethodIndex,
|
||||||
invokeNativeIndex,
|
invokeNativeIndex,
|
||||||
throwArrayIndexOutOfBoundsIndex,
|
throwArrayIndexOutOfBoundsIndex,
|
||||||
throwStackOverflowIndex,
|
throwStackOverflowIndex,
|
||||||
@ -295,6 +296,7 @@ class MyThread : public Thread {
|
|||||||
uintptr_t* heapImage;
|
uintptr_t* heapImage;
|
||||||
uint8_t* codeImage;
|
uint8_t* codeImage;
|
||||||
void** thunkTable;
|
void** thunkTable;
|
||||||
|
void** dynamicTable;
|
||||||
CallTrace* trace;
|
CallTrace* trace;
|
||||||
Reference* reference;
|
Reference* reference;
|
||||||
avian::codegen::Architecture* arch;
|
avian::codegen::Architecture* arch;
|
||||||
@ -1278,6 +1280,100 @@ class Context {
|
|||||||
Slice<ir::Value*> argumentBuffer;
|
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 translateLocalIndex(Context* context,
|
||||||
unsigned footprint,
|
unsigned footprint,
|
||||||
unsigned index)
|
unsigned index)
|
||||||
@ -4944,6 +5040,53 @@ loop:
|
|||||||
args(c->threadRegister(), frame->append(argument), instance)));
|
args(c->threadRegister(), frame->append(argument), instance)));
|
||||||
} break;
|
} 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: {
|
case invokeinterface: {
|
||||||
context->leaf = false;
|
context->leaf = false;
|
||||||
|
|
||||||
@ -7135,6 +7278,61 @@ uint64_t compileVirtualMethod(MyThread* t)
|
|||||||
return reinterpret_cast<uintptr_t>(compileVirtualMethod2(t, class_, index));
|
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)
|
uint64_t invokeNativeFast(MyThread* t, GcMethod* method, void* function)
|
||||||
{
|
{
|
||||||
FastNativeFunction f;
|
FastNativeFunction f;
|
||||||
@ -8317,6 +8515,7 @@ class MyProcessor : public Processor {
|
|||||||
public:
|
public:
|
||||||
Thunk default_;
|
Thunk default_;
|
||||||
Thunk defaultVirtual;
|
Thunk defaultVirtual;
|
||||||
|
Thunk defaultDynamic;
|
||||||
Thunk native;
|
Thunk native;
|
||||||
Thunk aioob;
|
Thunk aioob;
|
||||||
Thunk stackOverflow;
|
Thunk stackOverflow;
|
||||||
@ -8342,11 +8541,15 @@ class MyProcessor : public Processor {
|
|||||||
GcArithmeticException::FixedSize),
|
GcArithmeticException::FixedSize),
|
||||||
codeAllocator(s, Slice<uint8_t>(0, 0)),
|
codeAllocator(s, Slice<uint8_t>(0, 0)),
|
||||||
callTableSize(0),
|
callTableSize(0),
|
||||||
|
dynamicIndex(0),
|
||||||
useNativeFeatures(useNativeFeatures),
|
useNativeFeatures(useNativeFeatures),
|
||||||
compilationHandlers(0)
|
compilationHandlers(0),
|
||||||
|
dynamicTable(0),
|
||||||
|
dynamicTableSize(0)
|
||||||
{
|
{
|
||||||
thunkTable[compileMethodIndex] = voidPointer(local::compileMethod);
|
thunkTable[compileMethodIndex] = voidPointer(local::compileMethod);
|
||||||
thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod);
|
thunkTable[compileVirtualMethodIndex] = voidPointer(compileVirtualMethod);
|
||||||
|
thunkTable[linkDynamicMethodIndex] = voidPointer(linkDynamicMethod);
|
||||||
thunkTable[invokeNativeIndex] = voidPointer(invokeNative);
|
thunkTable[invokeNativeIndex] = voidPointer(invokeNative);
|
||||||
thunkTable[throwArrayIndexOutOfBoundsIndex]
|
thunkTable[throwArrayIndexOutOfBoundsIndex]
|
||||||
= voidPointer(throwArrayIndexOutOfBounds);
|
= voidPointer(throwArrayIndexOutOfBounds);
|
||||||
@ -8429,6 +8632,10 @@ class MyProcessor : public Processor {
|
|||||||
TARGET_THREAD_THUNKTABLE,
|
TARGET_THREAD_THUNKTABLE,
|
||||||
&MyThread::thunkTable,
|
&MyThread::thunkTable,
|
||||||
"TARGET_THREAD_THUNKTABLE")
|
"TARGET_THREAD_THUNKTABLE")
|
||||||
|
+ checkConstant(t,
|
||||||
|
TARGET_THREAD_DYNAMICTABLE,
|
||||||
|
&MyThread::dynamicTable,
|
||||||
|
"TARGET_THREAD_DYNAMICTABLE")
|
||||||
+ checkConstant(t,
|
+ checkConstant(t,
|
||||||
TARGET_THREAD_STACKLIMIT,
|
TARGET_THREAD_STACKLIMIT,
|
||||||
&MyThread::stackLimit,
|
&MyThread::stackLimit,
|
||||||
@ -8813,6 +9020,10 @@ class MyProcessor : public Processor {
|
|||||||
signals.unregisterHandler(SignalRegistrar::DivideByZero);
|
signals.unregisterHandler(SignalRegistrar::DivideByZero);
|
||||||
signals.setCrashDumpDirectory(0);
|
signals.setCrashDumpDirectory(0);
|
||||||
|
|
||||||
|
if (dynamicTable) {
|
||||||
|
allocator->free(dynamicTable, dynamicTableSize);
|
||||||
|
}
|
||||||
|
|
||||||
this->~MyProcessor();
|
this->~MyProcessor();
|
||||||
|
|
||||||
allocator->free(this, sizeof(*this));
|
allocator->free(this, sizeof(*this));
|
||||||
@ -8993,7 +9204,7 @@ class MyProcessor : public Processor {
|
|||||||
if (image and code) {
|
if (image and code) {
|
||||||
local::boot(static_cast<MyThread*>(t), image, code);
|
local::boot(static_cast<MyThread*>(t), image, code);
|
||||||
} else {
|
} 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);
|
GcArray* ct = makeArray(t, 128);
|
||||||
@ -9096,11 +9307,29 @@ class MyProcessor : public Processor {
|
|||||||
ThunkCollection thunks;
|
ThunkCollection thunks;
|
||||||
ThunkCollection bootThunks;
|
ThunkCollection bootThunks;
|
||||||
unsigned callTableSize;
|
unsigned callTableSize;
|
||||||
|
unsigned dynamicIndex;
|
||||||
bool useNativeFeatures;
|
bool useNativeFeatures;
|
||||||
void* thunkTable[dummyIndex + 1];
|
void* thunkTable[dummyIndex + 1];
|
||||||
CompilationHandlerList* compilationHandlers;
|
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)
|
const char* stringOrNull(const char* str)
|
||||||
{
|
{
|
||||||
if (str) {
|
if (str) {
|
||||||
@ -9240,15 +9469,16 @@ bool isThunkUnsafeStack(MyProcessor::Thunk* thunk, void* ip)
|
|||||||
|
|
||||||
bool isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
|
bool isThunkUnsafeStack(MyProcessor::ThunkCollection* thunks, void* ip)
|
||||||
{
|
{
|
||||||
const unsigned NamedThunkCount = 5;
|
const unsigned NamedThunkCount = 6;
|
||||||
|
|
||||||
MyProcessor::Thunk table[NamedThunkCount + ThunkCount];
|
MyProcessor::Thunk table[NamedThunkCount + ThunkCount];
|
||||||
|
|
||||||
table[0] = thunks->default_;
|
table[0] = thunks->default_;
|
||||||
table[1] = thunks->defaultVirtual;
|
table[1] = thunks->defaultVirtual;
|
||||||
table[2] = thunks->native;
|
table[2] = thunks->defaultDynamic;
|
||||||
table[3] = thunks->aioob;
|
table[3] = thunks->native;
|
||||||
table[4] = thunks->stackOverflow;
|
table[4] = thunks->aioob;
|
||||||
|
table[5] = thunks->stackOverflow;
|
||||||
|
|
||||||
for (unsigned i = 0; i < ThunkCount; ++i) {
|
for (unsigned i = 0; i < ThunkCount; ++i) {
|
||||||
new (table + NamedThunkCount + i)
|
new (table + NamedThunkCount + i)
|
||||||
@ -9598,8 +9828,8 @@ void findThunks(MyThread* t, BootImage* image, uint8_t* code)
|
|||||||
MyProcessor* p = processor(t);
|
MyProcessor* p = processor(t);
|
||||||
|
|
||||||
p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code);
|
p->bootThunks.default_ = thunkToThunk(image->thunks.default_, code);
|
||||||
p->bootThunks.defaultVirtual
|
p->bootThunks.defaultVirtual = thunkToThunk(image->thunks.defaultVirtual, code);
|
||||||
= thunkToThunk(image->thunks.defaultVirtual, code);
|
p->bootThunks.defaultDynamic = thunkToThunk(image->thunks.defaultDynamic, code);
|
||||||
p->bootThunks.native = thunkToThunk(image->thunks.native, code);
|
p->bootThunks.native = thunkToThunk(image->thunks.native, code);
|
||||||
p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code);
|
p->bootThunks.aioob = thunkToThunk(image->thunks.aioob, code);
|
||||||
p->bootThunks.stackOverflow = thunkToThunk(image->thunks.stackOverflow, 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)
|
void compileThunks(MyThread* t, FixedAllocator* allocator)
|
||||||
{
|
{
|
||||||
MyProcessor* p = processor(t);
|
MyProcessor* p = processor(t);
|
||||||
@ -9836,59 +10128,13 @@ void compileThunks(MyThread* t, FixedAllocator* allocator)
|
|||||||
= finish(t, allocator, a, "default", p->thunks.default_.length);
|
= finish(t, allocator, a, "default", p->thunks.default_.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
compileDefaultThunk
|
||||||
Context context(t);
|
(t, allocator, &(p->thunks.defaultVirtual), "defaultVirtual",
|
||||||
avian::codegen::Assembler* a = context.assembler;
|
compileVirtualMethodIndex, true);
|
||||||
|
|
||||||
lir::RegisterPair class_(t->arch->virtualCallTarget());
|
compileDefaultThunk
|
||||||
lir::Memory virtualCallTargetSrc(
|
(t, allocator, &(p->thunks.defaultDynamic), "defaultDynamic",
|
||||||
t->arch->stack(),
|
linkDynamicMethodIndex, false);
|
||||||
(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Context context(t);
|
Context context(t);
|
||||||
@ -10040,6 +10286,11 @@ uintptr_t defaultVirtualThunk(MyThread* t)
|
|||||||
return reinterpret_cast<uintptr_t>(processor(t)->thunks.defaultVirtual.start);
|
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)
|
uintptr_t nativeThunk(MyThread* t)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<uintptr_t>(processor(t)->thunks.native.start);
|
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);
|
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);
|
Context context(t);
|
||||||
avian::codegen::Assembler* a = context.assembler;
|
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::Constant, &indexConstant),
|
||||||
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &indexRegister));
|
OperandInfo(TargetBytesPerWord, lir::Operand::Type::RegisterPair, &indexRegister));
|
||||||
|
|
||||||
avian::codegen::ResolvedPromise defaultVirtualThunkPromise(
|
avian::codegen::ResolvedPromise promise(thunk);
|
||||||
defaultVirtualThunk(t));
|
lir::Constant target(&promise);
|
||||||
lir::Constant thunk(&defaultVirtualThunkPromise);
|
|
||||||
a->apply(lir::Jump,
|
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);
|
*size = a->endBlock(false)->resolve(0, 0);
|
||||||
|
|
||||||
@ -10083,8 +10337,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size)
|
|||||||
a->setDestination(start);
|
a->setDestination(start);
|
||||||
a->write();
|
a->write();
|
||||||
|
|
||||||
const char* const virtualThunkBaseName = "virtualThunk";
|
const size_t virtualThunkBaseNameLength = strlen(baseName);
|
||||||
const size_t virtualThunkBaseNameLength = strlen(virtualThunkBaseName);
|
|
||||||
const size_t maxIntStringLength = 10;
|
const size_t maxIntStringLength = 10;
|
||||||
|
|
||||||
THREAD_RUNTIME_ARRAY(t,
|
THREAD_RUNTIME_ARRAY(t,
|
||||||
@ -10094,7 +10347,7 @@ uintptr_t compileVirtualThunk(MyThread* t, unsigned index, unsigned* size)
|
|||||||
|
|
||||||
sprintf(RUNTIME_ARRAY_BODY(virtualThunkName),
|
sprintf(RUNTIME_ARRAY_BODY(virtualThunkName),
|
||||||
"%s%d",
|
"%s%d",
|
||||||
virtualThunkBaseName,
|
baseName,
|
||||||
index);
|
index);
|
||||||
|
|
||||||
logCompile(t, start, *size, 0, RUNTIME_ARRAY_BODY(virtualThunkName), 0);
|
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) {
|
if (oldArray->body()[index * 2] == 0) {
|
||||||
unsigned size;
|
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] = thunk;
|
||||||
oldArray->body()[(index * 2) + 1] = size;
|
oldArray->body()[(index * 2) + 1] = size;
|
||||||
}
|
}
|
||||||
@ -10254,6 +10507,11 @@ avian::util::FixedAllocator* codeAllocator(MyThread* t)
|
|||||||
return &(processor(t)->codeAllocator);
|
return &(processor(t)->codeAllocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Allocator* allocator(MyThread* t)
|
||||||
|
{
|
||||||
|
return processor(t)->allocator;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace local
|
} // namespace local
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -1981,6 +1981,34 @@ loop:
|
|||||||
}
|
}
|
||||||
goto 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: {
|
case invokeinterface: {
|
||||||
uint16_t index = codeReadInt16(t, code, ip);
|
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;
|
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) {
|
if (addendum == 0) {
|
||||||
PROTECT(t, class_);
|
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);
|
setField(t, class_, ClassAddendum, addendum);
|
||||||
}
|
}
|
||||||
return addendum;
|
return addendum;
|
||||||
@ -2811,6 +2811,25 @@ void parseAttributeTable(Thread* t,
|
|||||||
|
|
||||||
GcClassAddendum* addendum = getClassAddendum(t, class_, pool);
|
GcClassAddendum* addendum = getClassAddendum(t, class_, pool);
|
||||||
addendum->setAnnotationTable(t, body);
|
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"),
|
} else if (vm::strcmp(reinterpret_cast<const int8_t*>("EnclosingMethod"),
|
||||||
name->body().begin()) == 0) {
|
name->body().begin()) == 0) {
|
||||||
int16_t enclosingClass = s.read2();
|
int16_t enclosingClass = s.read2();
|
||||||
@ -6013,6 +6032,140 @@ GcJclass* getDeclaringClass(Thread* t, GcClass* c)
|
|||||||
return 0;
|
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()
|
void noop()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -398,7 +398,8 @@ GcTriple* makeCodeImage(Thread* t,
|
|||||||
RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t;
|
RUNTIME_ARRAY_BODY(types)[1] = Type_intptr_t;
|
||||||
|
|
||||||
for (unsigned i = 2; i < count + 2; ++i) {
|
for (unsigned i = 2; i < count + 2; ++i) {
|
||||||
switch (s.read1()) {
|
unsigned constType = s.read1();
|
||||||
|
switch (constType) {
|
||||||
case CONSTANT_Class:
|
case CONSTANT_Class:
|
||||||
case CONSTANT_String:
|
case CONSTANT_String:
|
||||||
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
|
RUNTIME_ARRAY_BODY(types)[i] = Type_object;
|
||||||
@ -436,7 +437,25 @@ GcTriple* makeCodeImage(Thread* t,
|
|||||||
s.skip(s.read2());
|
s.skip(s.read2());
|
||||||
break;
|
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:
|
default:
|
||||||
|
fprintf(stderr, "unknown class constant: %d\n", constType);
|
||||||
abort(t);
|
abort(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,16 @@
|
|||||||
|
|
||||||
(type cloneable java/lang/Cloneable)
|
(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
|
(type singleton avian/Singleton
|
||||||
(array maybe_object body))
|
(array maybe_object body))
|
||||||
|
|
||||||
@ -76,10 +86,10 @@
|
|||||||
(type invocation
|
(type invocation
|
||||||
(uint16_t bootstrap)
|
(uint16_t bootstrap)
|
||||||
(int32_t index)
|
(int32_t index)
|
||||||
(object class)
|
(class class)
|
||||||
(object pool)
|
(singleton pool)
|
||||||
(object template)
|
(method template)
|
||||||
(object site))
|
(callSite site))
|
||||||
|
|
||||||
(type triple
|
(type triple
|
||||||
(object first)
|
(object first)
|
||||||
@ -406,7 +416,8 @@
|
|||||||
(finder virtualFileFinders)
|
(finder virtualFileFinders)
|
||||||
(field array virtualFiles)
|
(field array virtualFiles)
|
||||||
(field array arrayInterfaceTable)
|
(field array arrayInterfaceTable)
|
||||||
(object threadTerminated))
|
(object threadTerminated)
|
||||||
|
(field array invocations))
|
||||||
|
|
||||||
(type compileRoots
|
(type compileRoots
|
||||||
(field array callTable)
|
(field array callTable)
|
||||||
@ -415,6 +426,7 @@
|
|||||||
(object objectPools)
|
(object objectPools)
|
||||||
(object staticTableArray)
|
(object staticTableArray)
|
||||||
(wordArray virtualThunks)
|
(wordArray virtualThunks)
|
||||||
|
(wordArray dynamicThunks)
|
||||||
(method receiveMethod)
|
(method receiveMethod)
|
||||||
(method windMethod)
|
(method windMethod)
|
||||||
(method rewindMethod))
|
(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;
|
||||||
import avian.ConstantPool.PoolEntry;
|
import avian.ConstantPool.PoolEntry;
|
||||||
import avian.Assembler;
|
import avian.Assembler;
|
||||||
|
import avian.Assembler.FieldData;
|
||||||
import avian.Assembler.MethodData;
|
import avian.Assembler.MethodData;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -95,7 +96,7 @@ public class Subroutine {
|
|||||||
Assembler.writeClass
|
Assembler.writeClass
|
||||||
(out, pool, ConstantPool.addClass(pool, name),
|
(out, pool, ConstantPool.addClass(pool, name),
|
||||||
ConstantPool.addClass(pool, "java/lang/Object"),
|
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,
|
{ new MethodData(Assembler.ACC_STATIC | Assembler.ACC_PUBLIC,
|
||||||
ConstantPool.addUtf8(pool, "test"),
|
ConstantPool.addUtf8(pool, "test"),
|
||||||
ConstantPool.addUtf8(pool, "()V"),
|
ConstantPool.addUtf8(pool, "()V"),
|
||||||
|
Loading…
Reference in New Issue
Block a user