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:
Joel Dice 2015-08-05 15:55:52 -06:00
parent 792684b935
commit 2465459079
16 changed files with 870 additions and 27 deletions

View File

@ -60,6 +60,7 @@ public class Assembler {
int name,
int super_,
int[] interfaces,
FieldData[] fields,
MethodData[] methods)
throws IOException
{
@ -83,7 +84,13 @@ public class Assembler {
write2(out, i + 1);
}
write2(out, 0); // field count
write2(out, fields.length);
for (FieldData f: fields) {
write2(out, f.flags);
write2(out, f.nameIndex + 1);
write2(out, f.specIndex + 1);
write2(out, 0); // attribute count
}
write2(out, methods.length);
for (MethodData m: methods) {
@ -113,4 +120,16 @@ public class Assembler {
this.code = code;
}
}
public static class FieldData {
public final int flags;
public final int nameIndex;
public final int specIndex;
public FieldData(int flags, int nameIndex, int specIndex) {
this.flags = flags;
this.nameIndex = nameIndex;
this.specIndex = specIndex;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ import avian.Classes;
import java.lang.annotation.Annotation;
public class Method<T> extends AccessibleObject implements Member {
private final VMMethod vmMethod;
public final VMMethod vmMethod;
private boolean accessible;
public Method(VMMethod vmMethod) {
@ -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);
}

View File

@ -23,6 +23,7 @@ import avian.ConstantPool;
import avian.ConstantPool.PoolEntry;
import avian.Assembler;
import avian.Assembler.FieldData;
import avian.Assembler.MethodData;
import java.util.List;
@ -402,6 +403,7 @@ public class Proxy {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Assembler.writeClass
(out, pool, nameIndex, superIndex, interfaceIndexes,
new FieldData[0],
methodTable.toArray(new MethodData[methodTable.size()]));
byte[] classData = out.toByteArray();

View File

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

View File

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

View File

@ -1220,7 +1220,7 @@ GcClassAddendum* getClassAddendum(Thread* t, GcClass* class_, GcSingleton* pool)
if (addendum == 0) {
PROTECT(t, class_);
addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0);
addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0, 0);
setField(t, class_, ClassAddendum, addendum);
}
return addendum;
@ -2811,6 +2811,25 @@ void parseAttributeTable(Thread* t,
GcClassAddendum* addendum = getClassAddendum(t, class_, pool);
addendum->setAnnotationTable(t, body);
} else if (vm::strcmp(reinterpret_cast<const int8_t*>("BootstrapMethods"),
name->body().begin()) == 0) {
unsigned count = s.read2();
GcArray* array = makeArray(t, count);
PROTECT(t, array);
for (unsigned i = 0; i < count; ++i) {
unsigned reference = s.read2() - 1;
unsigned argumentCount = s.read2();
GcCharArray* element = makeCharArray(t, 1 + argumentCount);
element->body()[0] = reference;
for (unsigned ai = 0; ai < argumentCount; ++ai) {
element->body()[1 + ai] = s.read2() - 1;
}
array->setBodyElement(t, i, element);
}
GcClassAddendum* addendum = getClassAddendum(t, class_, pool);
addendum->setBootstrapMethodTable(t, array);
} else if (vm::strcmp(reinterpret_cast<const int8_t*>("EnclosingMethod"),
name->body().begin()) == 0) {
int16_t enclosingClass = s.read2();

View File

@ -22,6 +22,16 @@
(type cloneable java/lang/Cloneable)
(type callSite java/lang/invoke/CallSite
(require invocation invocation))
(type methodHandle java/lang/invoke/MethodHandle
(alias method method vmtarget))
(type methodType java/lang/invoke/MethodType)
(type lookup java/lang/invoke/MethodHandles$Lookup)
(type singleton avian/Singleton
(array maybe_object body))
@ -76,10 +86,10 @@
(type invocation
(uint16_t bootstrap)
(int32_t index)
(object class)
(object pool)
(class class)
(singleton pool)
(method template)
(object site))
(callSite site))
(type triple
(object first)

View File

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

View File

@ -2,6 +2,7 @@ import avian.Stream;
import avian.ConstantPool;
import avian.ConstantPool.PoolEntry;
import avian.Assembler;
import avian.Assembler.FieldData;
import avian.Assembler.MethodData;
import java.util.ArrayList;
@ -95,7 +96,7 @@ public class Subroutine {
Assembler.writeClass
(out, pool, ConstantPool.addClass(pool, name),
ConstantPool.addClass(pool, "java/lang/Object"),
new int[0], new MethodData[]
new int[0], new FieldData[0], new MethodData[]
{ new MethodData(Assembler.ACC_STATIC | Assembler.ACC_PUBLIC,
ConstantPool.addUtf8(pool, "test"),
ConstantPool.addUtf8(pool, "()V"),