mirror of
https://github.com/corda/corda.git
synced 2025-03-18 10:05:28 +00:00
commit
e04c9b22be
@ -33,9 +33,17 @@ import avian.Assembler;
|
||||
import avian.ConstantPool.PoolEntry;
|
||||
import avian.SystemClassLoader;
|
||||
|
||||
// To understand what this is all about, please read:
|
||||
//
|
||||
// http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
|
||||
|
||||
public class LambdaMetafactory {
|
||||
private static int nextNumber = 0;
|
||||
|
||||
public static final int FLAG_SERIALIZABLE = 1;
|
||||
public static final int FLAG_MARKERS = 2;
|
||||
public static final int FLAG_BRIDGES = 4;
|
||||
|
||||
private static Class resolveReturnInterface(MethodType type) {
|
||||
int index = 1;
|
||||
byte[] s = type.spec;
|
||||
@ -212,13 +220,15 @@ public class LambdaMetafactory {
|
||||
new MethodHandle(implementationClass,
|
||||
implementationName,
|
||||
implementationSpec,
|
||||
implementationKind));
|
||||
implementationKind),
|
||||
emptyInterfaceList);
|
||||
}
|
||||
|
||||
private static byte[] makeLambda(String invokedName,
|
||||
MethodType invokedType,
|
||||
MethodType methodType,
|
||||
MethodHandle methodImplementation)
|
||||
MethodHandle methodImplementation,
|
||||
Class[] interfaces)
|
||||
{
|
||||
String className;
|
||||
{ int number;
|
||||
@ -230,8 +240,12 @@ public class LambdaMetafactory {
|
||||
|
||||
List<PoolEntry> pool = new ArrayList();
|
||||
|
||||
int interfaceIndex = ConstantPool.addClass
|
||||
(pool, invokedType.returnType().getName().replace('.', '/'));
|
||||
int[] interfaceIndexes = new int[interfaces.length + 1];
|
||||
interfaceIndexes[0] = ConstantPool.addClass(pool, invokedType.returnType().getName().replace('.', '/'));
|
||||
for (int i = 0; i < interfaces.length; i++) {
|
||||
String name = interfaces[i].getName().replace('.', '/');
|
||||
interfaceIndexes[i + 1] = ConstantPool.addClass(pool, name);
|
||||
}
|
||||
|
||||
List<FieldData> fieldTable = new ArrayList();
|
||||
|
||||
@ -280,7 +294,7 @@ public class LambdaMetafactory {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
try {
|
||||
Assembler.writeClass
|
||||
(out, pool, nameIndex, superIndex, new int[] { interfaceIndex },
|
||||
(out, pool, nameIndex, superIndex, interfaceIndexes,
|
||||
fieldTable.toArray(new FieldData[fieldTable.size()]),
|
||||
methodTable.toArray(new MethodData[methodTable.size()]));
|
||||
} catch (IOException e) {
|
||||
@ -292,6 +306,24 @@ public class LambdaMetafactory {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
private static CallSite makeCallSite(MethodType invokedType, byte[] classData) throws AssertionError {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Class[] emptyInterfaceList = new Class[] {};
|
||||
|
||||
public static CallSite metafactory(MethodHandles.Lookup caller,
|
||||
String invokedName,
|
||||
MethodType invokedType,
|
||||
@ -300,30 +332,79 @@ public class LambdaMetafactory {
|
||||
MethodType instantiatedMethodType)
|
||||
throws LambdaConversionException
|
||||
{
|
||||
byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation);
|
||||
|
||||
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;
|
||||
}
|
||||
byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation, emptyInterfaceList);
|
||||
return makeCallSite(invokedType, classData);
|
||||
}
|
||||
|
||||
public static CallSite altMetafactory(MethodHandles.Lookup caller,
|
||||
String invokedName,
|
||||
MethodType invokedType,
|
||||
Object... args)
|
||||
throws LambdaConversionException
|
||||
{
|
||||
// todo: handle flags
|
||||
return metafactory(caller, invokedName, invokedType, (MethodType) args[0], (MethodHandle) args[1], (MethodType) args[2]);
|
||||
Object... args) throws LambdaConversionException {
|
||||
// See openjdk8/jdk/src/share/classes/java/lang/invoke/LambdaMetafactory.java
|
||||
// Behaves as if the prototype is like this:
|
||||
//
|
||||
// CallSite altMetafactory(
|
||||
// MethodHandles.Lookup caller,
|
||||
// String invokedName,
|
||||
// MethodType invokedType,
|
||||
// MethodType methodType,
|
||||
// MethodHandle methodImplementation,
|
||||
// MethodType instantiatedMethodType,
|
||||
// int flags,
|
||||
// int markerInterfaceCount, // IF flags has MARKERS set
|
||||
// Class... markerInterfaces, // IF flags has MARKERS set
|
||||
// int bridgeCount, // IF flags has BRIDGES set
|
||||
// MethodType... bridges // IF flags has BRIDGES set
|
||||
// )
|
||||
MethodType methodType = (MethodType) args[0];
|
||||
MethodHandle methodImplementation = (MethodHandle) args[1];
|
||||
|
||||
int flags = (Integer) args[3];
|
||||
boolean serializable = (flags & FLAG_SERIALIZABLE) != 0;
|
||||
|
||||
// Marker interfaces are added to a lambda when they're written like this:
|
||||
//
|
||||
// Runnable r = (Runnable & Serializable) () -> foo()
|
||||
//
|
||||
// The intersection type in the cast here indicates to the compiler what interfaces
|
||||
// the generated lambda class should implement. Because a lambda has (by definition)
|
||||
// one method only, it is meaningless for these interfaces to contain anything, thus
|
||||
// they are only allowed to be empty marker interfaces. In practice the Serializable
|
||||
// interface is handled specially and the use of markers is extremely rare. Adding
|
||||
// support would be easy though.
|
||||
if ((flags & FLAG_MARKERS) != 0)
|
||||
throw new UnsupportedOperationException("Marker interfaces on lambdas are not supported on Avian yet. Sorry.");
|
||||
|
||||
// In some cases there is a mismatch between what the JVM type system supports and
|
||||
// what the Java language supports. In other cases the type of a lambda expression
|
||||
// may not perfectly match the functional interface which represents it. Consider the
|
||||
// following case:
|
||||
//
|
||||
// interface I { void foo(Integer i, String s1, Strings s2) }
|
||||
// class Foo { static void m(Number i, Object... rest) {} }
|
||||
//
|
||||
// I lambda = Foo::m
|
||||
//
|
||||
// This is allowed by the Java language, even though the interface representing the
|
||||
// lambda specifies three specific arguments and the method implementing the lambda
|
||||
// uses varargs and a different type signature. Behind the scenes the compiler generates
|
||||
// a "bridge" method that does the adaptation.
|
||||
//
|
||||
// You can learn more here: http://www.oracle.com/technetwork/java/jvmls2013heid-2013922.pdf
|
||||
// and here: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html
|
||||
if ((flags & FLAG_BRIDGES) != 0) {
|
||||
int bridgeCount = (Integer) args[4];
|
||||
if (bridgeCount > 0)
|
||||
throw new UnsupportedOperationException("A lambda that requires bridge methods was used, this is not yet supported by Avian. Sorry.");
|
||||
}
|
||||
|
||||
// TODO: This is not necessary if the function type interface is already inheriting
|
||||
// from Serializable.
|
||||
Class[] interfaces = new Class[serializable ? 1 : 0];
|
||||
if (serializable)
|
||||
interfaces[0] = java.io.Serializable.class;
|
||||
|
||||
byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation, interfaces);
|
||||
return makeCallSite(invokedType, classData);
|
||||
}
|
||||
}
|
||||
|
@ -6032,13 +6032,26 @@ GcJclass* getDeclaringClass(Thread* t, GcClass* c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Called when interpreting invokedynamic. `invocation` points to
|
||||
// static data in the bootstrap method table, which in turn points to
|
||||
// a bootstrap method and stores additional data to be passed to
|
||||
// it. `resolveDynamic` will then call this bootstrap method after
|
||||
// resolving the arguments as required. The called method is assumed
|
||||
// to be a lambda `metafactory` or `altMetafactory`.
|
||||
//
|
||||
// Note that capture/bridging etc happens within the bootstrap method,
|
||||
// this is just the code that dispatches to it.
|
||||
//
|
||||
// Returns the CallSite returned by the bootstrap method.
|
||||
GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
{
|
||||
PROTECT(t, invocation);
|
||||
|
||||
// Use the invocation's Class to get the bootstrap method table and get a classloader.
|
||||
GcClass* c = invocation->class_();
|
||||
PROTECT(t, c);
|
||||
|
||||
// First element points to the bootstrap method. The rest are static data passed to the BSM.
|
||||
GcCharArray* bootstrapArray = cast<GcCharArray>(
|
||||
t,
|
||||
cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
|
||||
@ -6046,6 +6059,7 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
|
||||
PROTECT(t, bootstrapArray);
|
||||
|
||||
// Resolve the bootstrap method itself.
|
||||
GcMethod* bootstrap = cast<GcMethod>(t,
|
||||
resolve(t,
|
||||
c->loader(),
|
||||
@ -6055,22 +6069,29 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
GcNoSuchMethodError::Type));
|
||||
PROTECT(t, bootstrap);
|
||||
|
||||
// Caller context info to be passed to the bootstrap method.
|
||||
GcLookup* lookup
|
||||
= makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC);
|
||||
PROTECT(t, lookup);
|
||||
|
||||
// The name of the linked-to method.
|
||||
GcByteArray* nameBytes = invocation->template_()->name();
|
||||
GcString* name
|
||||
= t->m->classpath->makeString(t, nameBytes, 0, nameBytes->length() - 1);
|
||||
PROTECT(t, name);
|
||||
|
||||
// This is the type of the linked-to method (e.g. lambda).
|
||||
GcMethodType* type = makeMethodType(
|
||||
t, c->loader(), invocation->template_()->spec(), 0, 0, 0);
|
||||
PROTECT(t, type);
|
||||
|
||||
// `array` stores either
|
||||
// 1. All the arguments to be passed to the bootstrap method in the case of `metafactory`
|
||||
// 2. The vararg object array to be passed to `altMetafactory`
|
||||
GcArray* array = makeArray(t, bootstrap->parameterCount());
|
||||
PROTECT(t, array);
|
||||
|
||||
// These are common arguments to metafactory and altMetafactory
|
||||
unsigned argument = 0;
|
||||
array->setBodyElement(t, argument++, lookup);
|
||||
array->setBodyElement(t, argument++, name);
|
||||
@ -6079,16 +6100,28 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
THREAD_RUNTIME_ARRAY(t, char, specBuffer, bootstrap->spec()->length());
|
||||
|
||||
const char* spec;
|
||||
// `argArray` stores the final arguments to be passed to the bootstrap method.
|
||||
// Later in this function we iterate through the method signature +
|
||||
// bootstrap array and resolve the arguments as required into `array`.
|
||||
//
|
||||
// In the case of a `metafactory` call:
|
||||
// `argArray = [caller, invokedName, invokedType, methodType, methodImplementation, instantiatedType]`
|
||||
// `array = argArray`
|
||||
//
|
||||
// In the case of an `altMetafactory` call:
|
||||
// `argArray = [caller, invokedName, invokedType, array]`
|
||||
// `array = [methodType, methodImplementation, instantiatedType, flags, ...]`
|
||||
GcArray* argArray = array;
|
||||
PROTECT(t, argArray);
|
||||
|
||||
// Check if the bootstrap method's signature matches that of an altMetafactory
|
||||
if (::strcmp(reinterpret_cast<char*>(bootstrap->spec()->body().begin()),
|
||||
"(Ljava/lang/invoke/MethodHandles$Lookup;"
|
||||
"Ljava/lang/String;"
|
||||
"Ljava/lang/invoke/MethodType;"
|
||||
"[Ljava/lang/Object;)"
|
||||
"Ljava/lang/invoke/CallSite;") == 0) {
|
||||
// LambdaMetaFactory.altMetafactory
|
||||
// If so, create a new array to store the varargs in, and hardcode the BSM signature.
|
||||
array = makeArray(t, bootstrapArray->length() - 1);
|
||||
spec = "(Ljava/lang/invoke/MethodHandles$Lookup;"
|
||||
"Ljava/lang/String;"
|
||||
@ -6103,6 +6136,9 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
"[Ljava/lang/invoke/MethodType;"
|
||||
")Ljava/lang/invoke/CallSite;";
|
||||
} else if (bootstrap->parameterCount() == 2 + bootstrapArray->length()) {
|
||||
// We're calling the simpler `metafactory`. 2 + bootstrapArray->length() is the
|
||||
// arguments to the bootstrap method (bootstrapArray->length() - 1), plus the 3 static
|
||||
// arguments (lookup, name, type).
|
||||
memcpy(RUNTIME_ARRAY_BODY(specBuffer),
|
||||
bootstrap->spec()->body().begin(),
|
||||
bootstrap->spec()->length());
|
||||
@ -6113,16 +6149,24 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
|
||||
MethodSpecIterator it(t, spec);
|
||||
|
||||
// Skip over the already handled 3 arguments.
|
||||
for (unsigned i = 0; i < argument; ++i)
|
||||
it.next();
|
||||
|
||||
// If we're calling altMetafactory then we reset the argument
|
||||
// offset, because we are filling the vararg array instead of the
|
||||
// final argument array.
|
||||
if (argArray != array) {
|
||||
argument = 0;
|
||||
}
|
||||
|
||||
// `i` iterates through the bootstrap arguments (the +1 is because we skip
|
||||
// the boostrap method's name), `it` iterates through the corresponding types
|
||||
// in the method signature
|
||||
unsigned i = 0;
|
||||
while (i + 1 < bootstrapArray->length() && it.hasNext()) {
|
||||
const char* p = it.next();
|
||||
|
||||
switch (*p) {
|
||||
case 'L': {
|
||||
const char* const methodType = "Ljava/lang/invoke/MethodType;";
|
||||
@ -6198,10 +6242,12 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
|
||||
? 0
|
||||
: makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0);
|
||||
|
||||
// If we're calling altMetafactory we set the fourth argument to the vararg array.
|
||||
if (argArray != array) {
|
||||
argArray->setBodyElement(t, 3, array);
|
||||
}
|
||||
|
||||
// Finally we make the bootstrap call.
|
||||
return cast<GcCallSite>(
|
||||
t, t->m->processor->invokeArray(t, bootstrap, handle, argArray));
|
||||
}
|
||||
|
@ -41,6 +41,32 @@ public class InvokeDynamic {
|
||||
}
|
||||
}
|
||||
|
||||
private interface Foo extends java.io.Serializable {
|
||||
void someFunction(Integer a, Integer b, String s);
|
||||
}
|
||||
|
||||
private interface UnboxedSerializable extends java.io.Serializable {
|
||||
int add(int a, int b);
|
||||
}
|
||||
|
||||
private interface Unboxed {
|
||||
int add(int a, int b);
|
||||
}
|
||||
|
||||
private void requiresBridge(Number a, Object... rest) {
|
||||
String s = "" + a;
|
||||
for (Object r : rest) {
|
||||
s += r;
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer addBoxed(Integer a, Integer b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
private interface Marker {
|
||||
}
|
||||
|
||||
private void test() {
|
||||
{ int c = 2;
|
||||
Operation op = (a, b) -> ((a + b) * c) - foo;
|
||||
@ -62,5 +88,24 @@ public class InvokeDynamic {
|
||||
expect(s.get().first == 42L);
|
||||
expect(s.get().second == 3.14D);
|
||||
}
|
||||
|
||||
{ Foo s = this::requiresBridge;
|
||||
s.someFunction(1, 2, "");
|
||||
}
|
||||
|
||||
// This abort()s in machine.cpp
|
||||
// { Foo s = (Foo & Marker) this::requiresBridge;
|
||||
// s.someFunction(1, 2, "");
|
||||
// }
|
||||
|
||||
// NPE
|
||||
// { UnboxedSerializable s = InvokeDynamic::addBoxed;
|
||||
// expect(s.add(1, 2) == 3);
|
||||
// }
|
||||
|
||||
// NPE
|
||||
// { Unboxed s = InvokeDynamic::addBoxed;
|
||||
// expect(s.add(1, 2) == 3);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user