Merge pull request #511 from dicej/lambda-fixes

Lambda fixes
This commit is contained in:
Joel Dice 2016-12-06 09:50:16 -07:00 committed by GitHub
commit c5d7e5b8c5
13 changed files with 410 additions and 170 deletions

View File

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

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2016, 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.invoke; package java.lang.invoke;
public class CallSite { public class CallSite {

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2016, 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.invoke; package java.lang.invoke;
public class LambdaConversionException extends java.lang.Exception { public class LambdaConversionException extends java.lang.Exception {

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2016, 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.invoke; package java.lang.invoke;
import static avian.Stream.write1; import static avian.Stream.write1;
@ -306,4 +316,14 @@ public class LambdaMetafactory {
throw error; throw error;
} }
} }
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]);
}
} }

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2016, 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.invoke; package java.lang.invoke;
import avian.Classes; import avian.Classes;

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2016, 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.invoke; package java.lang.invoke;
public class MethodHandles { public class MethodHandles {

View File

@ -1,3 +1,13 @@
/* Copyright (c) 2008-2016, 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.invoke; package java.lang.invoke;
import static avian.Assembler.*; import static avian.Assembler.*;
@ -242,8 +252,8 @@ public final class MethodType implements java.io.Serializable {
ObjectType(aload, areturn, 1), ObjectType(aload, areturn, 1),
IntegerType(iload, ireturn, 1), IntegerType(iload, ireturn, 1),
FloatType(fload, freturn, 1), FloatType(fload, freturn, 1),
LongType(lload, lreturn, 1), LongType(lload, lreturn, 2),
DoubleType(dload, dreturn, 1), DoubleType(dload, dreturn, 2),
VoidType(-1, Assembler.return_, -1); VoidType(-1, Assembler.return_, -1);
public final int load; public final int load;

View File

@ -0,0 +1,48 @@
/* Copyright (c) 2008-2016, 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.invoke;
public class SerializedLambda implements java.io.Serializable {
public int getImplMethodKind() {
// todo
return 0;
}
public String getImplClass() {
// todo
return null;
}
public String getImplMethodName() {
// todo
return null;
}
public String getImplMethodSignature() {
// todo
return null;
}
public String getFunctionalInterfaceClass() {
// todo
return null;
}
public String getFunctionalInterfaceMethodName() {
// todo
return null;
}
public String getFunctionalInterfaceMethodSignature() {
// todo
return null;
}
}

View File

@ -523,6 +523,9 @@ class MyClasspath : public Classpath {
GcJobject* blockerLock = makeJobject(t); GcJobject* blockerLock = makeJobject(t);
thread->setBlockerLock(t, blockerLock); thread->setBlockerLock(t, blockerLock);
#if HAVE_ThreadName_Ljava_lang_String_
GcString* name = vm::makeString(t, "Thread-%p", thread);
#else
const unsigned BufferSize = 256; const unsigned BufferSize = 256;
char buffer[BufferSize]; char buffer[BufferSize];
unsigned length = vm::snprintf(buffer, BufferSize, "Thread-%p", thread); unsigned length = vm::snprintf(buffer, BufferSize, "Thread-%p", thread);
@ -530,6 +533,7 @@ class MyClasspath : public Classpath {
for (unsigned i = 0; i < length; ++i) { for (unsigned i = 0; i < length; ++i) {
name->body()[i] = buffer[i]; name->body()[i] = buffer[i];
} }
#endif
thread->setName(t, name); thread->setName(t, name);
return thread; return thread;

View File

@ -4040,15 +4040,26 @@ bool isLambda(Thread* t,
return vm::strcmp(reinterpret_cast<const int8_t*>( return vm::strcmp(reinterpret_cast<const int8_t*>(
"java/lang/invoke/LambdaMetafactory"), "java/lang/invoke/LambdaMetafactory"),
bootstrap->class_()->name()->body().begin()) == 0 bootstrap->class_()->name()->body().begin()) == 0
and vm::strcmp(reinterpret_cast<const int8_t*>("metafactory"), and ((vm::strcmp(reinterpret_cast<const int8_t*>("metafactory"),
bootstrap->name()->body().begin()) == 0 bootstrap->name()->body().begin()) == 0
and vm::strcmp( and vm::strcmp(
reinterpret_cast<const int8_t*>( reinterpret_cast<const int8_t*>(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/" "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/"
"String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/" "String;Ljava/lang/invoke/MethodType;Ljava/lang/"
"MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/" "invoke/"
"MethodType;Ljava/lang/invoke/MethodHandle;Ljava/"
"lang/"
"invoke/MethodType;)Ljava/lang/invoke/CallSite;"), "invoke/MethodType;)Ljava/lang/invoke/CallSite;"),
bootstrap->spec()->body().begin()) == 0; bootstrap->spec()->body().begin()) == 0)
or (vm::strcmp(reinterpret_cast<const int8_t*>("altMetafactory"),
bootstrap->name()->body().begin()) == 0
and vm::strcmp(
reinterpret_cast<const int8_t*>(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/"
"lang/"
"String;Ljava/lang/invoke/MethodType;[Ljava/lang/"
"Object;)Ljava/lang/invoke/CallSite;"),
bootstrap->spec()->body().begin()) == 0));
} }
void compile(MyThread* t, void compile(MyThread* t,
@ -5133,6 +5144,16 @@ loop:
GcClass* c = context->method->class_(); GcClass* c = context->method->class_();
PROTECT(t, c); PROTECT(t, c);
GcMethod* target
= c->addendum()->bootstrapLambdaTable()
? cast<GcMethod>(
t,
cast<GcArray>(t, c->addendum()->bootstrapLambdaTable())
->body()[invocation->bootstrap()])
: nullptr;
PROTECT(t, target);
if (target == nullptr) {
GcCharArray* bootstrapArray = cast<GcCharArray>( GcCharArray* bootstrapArray = cast<GcCharArray>(
t, t,
cast<GcArray>(t, c->addendum()->bootstrapMethodTable()) cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
@ -5141,7 +5162,8 @@ loop:
if (isLambda(t, c->loader(), bootstrapArray, invocation)) { if (isLambda(t, c->loader(), bootstrapArray, invocation)) {
if (bc->hostVM == 0) { if (bc->hostVM == 0) {
throwNew(t, throwNew(
t,
GcVirtualMachineError::Type, GcVirtualMachineError::Type,
"lambda expression encountered, but host VM is not " "lambda expression encountered, but host VM is not "
"available; use -hostvm option to bootimage-generator to " "available; use -hostvm option to bootimage-generator to "
@ -5149,16 +5171,18 @@ loop:
} }
JNIEnv* e; JNIEnv* e;
if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0) == 0) { if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0)
== 0) {
e->vtable->PushLocalFrame(e, 256); e->vtable->PushLocalFrame(e, 256);
jclass lmfClass jclass lmfClass = e->vtable->FindClass(
= e->vtable->FindClass(e, "java/lang/invoke/LambdaMetafactory"); e, "java/lang/invoke/LambdaMetafactory");
jmethodID makeLambda = e->vtable->GetStaticMethodID( jmethodID makeLambda = e->vtable->GetStaticMethodID(
e, e,
lmfClass, lmfClass,
"makeLambda", "makeLambda",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/" "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
"String;Ljava/"
"lang/String;Ljava/lang/String;Ljava/lang/String;I)[B"); "lang/String;Ljava/lang/String;Ljava/lang/String;I)[B");
GcReference* reference = cast<GcReference>( GcReference* reference = cast<GcReference>(
@ -5232,14 +5256,24 @@ loop:
invocation->template_()->spec()->body().begin(), invocation->template_()->spec()->body().begin(),
invocation->template_()->spec()->length()); invocation->template_()->spec()->length());
GcMethod* target = resolveMethod( target = resolveMethod(
t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec)); t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec));
bool tailCall = isTailCall(t, code, ip, context->method, target); GcArray* table
compileDirectInvoke(t, frame, target, tailCall); = cast<GcArray>(t, c->addendum()->bootstrapLambdaTable());
if (table == nullptr) {
table = makeArray(
t,
cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
->length());
c->addendum()->setBootstrapLambdaTable(t, table);
}
table->setBodyElement(t, invocation->bootstrap(), target);
} else { } else {
throwNew( throwNew(t,
t, GcVirtualMachineError::Type, "unable to attach to host VM"); GcVirtualMachineError::Type,
"unable to attach to host VM");
} }
} else { } else {
throwNew(t, throwNew(t,
@ -5247,6 +5281,10 @@ loop:
"invokedynamic not supported for AOT-compiled code except " "invokedynamic not supported for AOT-compiled code except "
"in the case of lambda expressions"); "in the case of lambda expressions");
} }
}
bool tailCall = isTailCall(t, code, ip, context->method, target);
compileDirectInvoke(t, frame, target, tailCall);
} else { } else {
unsigned index = addDynamic(t, invocation); unsigned index = addDynamic(t, invocation);

View File

@ -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, 0); addendum = makeClassAddendum(t, pool, 0, 0, 0, 0, -1, 0, 0, 0, 0);
setField(t, class_, ClassAddendum, addendum); setField(t, class_, ClassAddendum, addendum);
} }
return addendum; return addendum;
@ -6055,8 +6055,6 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
GcNoSuchMethodError::Type)); GcNoSuchMethodError::Type));
PROTECT(t, bootstrap); PROTECT(t, bootstrap);
assertT(t, bootstrap->parameterCount() == 2 + bootstrapArray->length());
GcLookup* lookup GcLookup* lookup
= makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC); = makeLookup(t, c, ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC);
PROTECT(t, lookup); PROTECT(t, lookup);
@ -6078,14 +6076,47 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
array->setBodyElement(t, argument++, name); array->setBodyElement(t, argument++, name);
array->setBodyElement(t, argument++, type); array->setBodyElement(t, argument++, type);
MethodSpecIterator it( const char* spec;
t, reinterpret_cast<const char*>(bootstrap->spec()->body().begin())); GcArray* argArray = array;
PROTECT(t, argArray);
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
array = makeArray(t, bootstrapArray->length() - 1);
spec = "(Ljava/lang/invoke/MethodHandles$Lookup;"
"Ljava/lang/String;"
"Ljava/lang/invoke/MethodType;"
"Ljava/lang/invoke/MethodType;"
"Ljava/lang/invoke/MethodHandle;"
"Ljava/lang/invoke/MethodType;"
"I"
"I"
"[Ljava/lang/Class;"
"I"
"[Ljava/lang/invoke/MethodType;"
")Ljava/lang/invoke/CallSite;";
} else if (bootstrap->parameterCount() == 2 + bootstrapArray->length()) {
spec = reinterpret_cast<char*>(bootstrap->spec()->body().begin());
} else {
abort(t);
}
MethodSpecIterator it(t, spec);
for (unsigned i = 0; i < argument; ++i) for (unsigned i = 0; i < argument; ++i)
it.next(); it.next();
if (argArray != array) {
argument = 0;
}
unsigned i = 0; unsigned i = 0;
while (it.hasNext()) { while (i + 1 < bootstrapArray->length() && it.hasNext()) {
const char* p = it.next(); const char* p = it.next();
switch (*p) { switch (*p) {
case 'L': { case 'L': {
@ -6162,8 +6193,12 @@ GcCallSite* resolveDynamic(Thread* t, GcInvocation* invocation)
? 0 ? 0
: makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0); : makeMethodHandle(t, REF_invokeSpecial, c->loader(), bootstrap, 0);
if (argArray != array) {
argArray->setBodyElement(t, 3, array);
}
return cast<GcCallSite>( return cast<GcCallSite>(
t, t->m->processor->invokeArray(t, bootstrap, handle, array)); t, t->m->processor->invokeArray(t, bootstrap, handle, argArray));
} }
void noop() void noop()

View File

@ -19,6 +19,7 @@
#include <vector> #include <vector>
#include <set> #include <set>
#include <sstream> #include <sstream>
#include <algorithm>
#include "avian/constants.h" #include "avian/constants.h"
#include "avian/finder.h" #include "avian/finder.h"
@ -908,8 +909,6 @@ std::string cppFieldType(Module& module, Field* f)
void writeAccessor(Output* out, Class* cl, Field* f) void writeAccessor(Output* out, Class* cl, Field* f)
{ {
std::string typeName = f->typeName;
out->write("const unsigned "); out->write("const unsigned ");
out->write(capitalize(cl->name)); out->write(capitalize(cl->name));
out->write(capitalize(f->name)); out->write(capitalize(f->name));
@ -920,7 +919,22 @@ void writeAccessor(Output* out, Class* cl, Field* f)
out->write("#define HAVE_"); out->write("#define HAVE_");
out->write(capitalize(cl->name)); out->write(capitalize(cl->name));
out->write(capitalize(f->name)); out->write(capitalize(f->name));
out->write(" 1\n");
if (! f->javaSpec.empty()) {
std::string s = f->javaSpec;
std::replace(s.begin(), s.end(), '/', '_');
std::replace(s.begin(), s.end(), '$', '_');
std::replace(s.begin(), s.end(), ';', '_');
std::replace(s.begin(), s.end(), '[', '_');
out->write("#define HAVE_");
out->write(capitalize(cl->name));
out->write(capitalize(f->name));
out->write("_");
out->write(s);
out->write(" 1\n\n"); out->write(" 1\n\n");
}
} }
void writeAccessors(Output* out, Module& module) void writeAccessors(Output* out, Module& module)

View File

@ -9,6 +9,24 @@ public class InvokeDynamic {
int operate(int a, int b); int operate(int a, int b);
} }
private interface Operation2 {
long operate(long a, int b);
}
private static class Pair<A, B> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
private interface Supplier<T> extends java.io.Serializable {
T get();
}
private static void expect(boolean v) { private static void expect(boolean v) {
if (! v) throw new RuntimeException(); if (! v) throw new RuntimeException();
} }
@ -24,8 +42,19 @@ public class InvokeDynamic {
} }
private void test() { private void test() {
int c = 2; { int c = 2;
Operation op = (a, b) -> ((a + b) * c) - foo; Operation op = (a, b) -> ((a + b) * c) - foo;
expect(op.operate(2, 3) == ((2 + 3) * 2) - foo); expect(op.operate(2, 3) == ((2 + 3) * 2) - foo);
} }
{ int c = 2;
Operation2 op = (a, b) -> ((a + b) * c) - foo;
expect(op.operate(2, 3) == ((2 + 3) * 2) - foo);
}
{ Supplier<Pair<Long, Double>> s = () -> new Pair<Long, Double>(42L, 77.1D);
expect(s.get().first == 42L);
expect(s.get().second == 77.1D);
}
}
} }