mirror of
https://github.com/corda/corda.git
synced 2025-01-31 16:35:43 +00:00
commit
c5d7e5b8c5
@ -27,4 +27,6 @@ public class ClassAddendum extends Addendum {
|
|||||||
public Pair enclosingMethod;
|
public Pair enclosingMethod;
|
||||||
|
|
||||||
public VMMethod[] bootstrapMethodTable;
|
public VMMethod[] bootstrapMethodTable;
|
||||||
|
|
||||||
|
public VMMethod[] bootstrapLambdaTable;
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
@ -25,17 +35,17 @@ import avian.SystemClassLoader;
|
|||||||
|
|
||||||
public class LambdaMetafactory {
|
public class LambdaMetafactory {
|
||||||
private static int nextNumber = 0;
|
private static int nextNumber = 0;
|
||||||
|
|
||||||
private static Class resolveReturnInterface(MethodType type) {
|
private static Class resolveReturnInterface(MethodType type) {
|
||||||
int index = 1;
|
int index = 1;
|
||||||
byte[] s = type.spec;
|
byte[] s = type.spec;
|
||||||
|
|
||||||
while (s[index] != ')') ++ index;
|
while (s[index] != ')') ++ index;
|
||||||
|
|
||||||
if (s[++ index] != 'L') throw new AssertionError();
|
if (s[++ index] != 'L') throw new AssertionError();
|
||||||
|
|
||||||
++ index;
|
++ index;
|
||||||
|
|
||||||
int end = index + 1;
|
int end = index + 1;
|
||||||
while (s[end] != ';') ++ end;
|
while (s[end] != ';') ++ end;
|
||||||
|
|
||||||
@ -52,11 +62,11 @@ public class LambdaMetafactory {
|
|||||||
while (array[i] != c) ++i;
|
while (array[i] != c) ++i;
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String constructorSpec(MethodType type) {
|
private static String constructorSpec(MethodType type) {
|
||||||
return Classes.makeString(type.spec, 0, indexOf(')', type.spec) + 1) + "V";
|
return Classes.makeString(type.spec, 0, indexOf(')', type.spec) + 1) + "V";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] makeFactoryCode(List<PoolEntry> pool,
|
private static byte[] makeFactoryCode(List<PoolEntry> pool,
|
||||||
String className,
|
String className,
|
||||||
String constructorSpec,
|
String constructorSpec,
|
||||||
@ -89,7 +99,7 @@ public class LambdaMetafactory {
|
|||||||
byte[] result = out.toByteArray();
|
byte[] result = out.toByteArray();
|
||||||
set4(result, 4, result.length - 12);
|
set4(result, 4, result.length - 12);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] makeConstructorCode(List<PoolEntry> pool,
|
private static byte[] makeConstructorCode(List<PoolEntry> pool,
|
||||||
@ -124,7 +134,7 @@ public class LambdaMetafactory {
|
|||||||
byte[] result = out.toByteArray();
|
byte[] result = out.toByteArray();
|
||||||
set4(result, 4, result.length - 12);
|
set4(result, 4, result.length - 12);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] makeInvocationCode(List<PoolEntry> pool,
|
private static byte[] makeInvocationCode(List<PoolEntry> pool,
|
||||||
@ -167,7 +177,7 @@ public class LambdaMetafactory {
|
|||||||
default: throw new AssertionError
|
default: throw new AssertionError
|
||||||
("todo: implement per http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5");
|
("todo: implement per http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3.5");
|
||||||
}
|
}
|
||||||
|
|
||||||
write2(out, ConstantPool.addMethodRef
|
write2(out, ConstantPool.addMethodRef
|
||||||
(pool,
|
(pool,
|
||||||
Classes.makeString(implementation.method.class_.name, 0,
|
Classes.makeString(implementation.method.class_.name, 0,
|
||||||
@ -233,7 +243,7 @@ public class LambdaMetafactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String constructorSpec = constructorSpec(invokedType);
|
String constructorSpec = constructorSpec(invokedType);
|
||||||
|
|
||||||
List<MethodData> methodTable = new ArrayList();
|
List<MethodData> methodTable = new ArrayList();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -261,9 +271,9 @@ public class LambdaMetafactory {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
AssertionError error = new AssertionError();
|
AssertionError error = new AssertionError();
|
||||||
error.initCause(e);
|
error.initCause(e);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nameIndex = ConstantPool.addClass(pool, className);
|
int nameIndex = ConstantPool.addClass(pool, className);
|
||||||
int superIndex = ConstantPool.addClass(pool, "java/lang/Object");
|
int superIndex = ConstantPool.addClass(pool, "java/lang/Object");
|
||||||
|
|
||||||
@ -276,12 +286,12 @@ public class LambdaMetafactory {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
AssertionError error = new AssertionError();
|
AssertionError error = new AssertionError();
|
||||||
error.initCause(e);
|
error.initCause(e);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CallSite metafactory(MethodHandles.Lookup caller,
|
public static CallSite metafactory(MethodHandles.Lookup caller,
|
||||||
String invokedName,
|
String invokedName,
|
||||||
MethodType invokedType,
|
MethodType invokedType,
|
||||||
@ -291,7 +301,7 @@ public class LambdaMetafactory {
|
|||||||
throws LambdaConversionException
|
throws LambdaConversionException
|
||||||
{
|
{
|
||||||
byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation);
|
byte[] classData = makeLambda(invokedName, invokedType, methodType, methodImplementation);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return new CallSite
|
return new CallSite
|
||||||
(new MethodHandle
|
(new MethodHandle
|
||||||
@ -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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
@ -6,12 +16,12 @@ import avian.SystemClassLoader;
|
|||||||
public class MethodHandle {
|
public class MethodHandle {
|
||||||
static final int REF_invokeStatic = 6;
|
static final int REF_invokeStatic = 6;
|
||||||
static final int REF_invokeSpecial = 7;
|
static final int REF_invokeSpecial = 7;
|
||||||
|
|
||||||
final int kind;
|
final int kind;
|
||||||
private final ClassLoader loader;
|
private final ClassLoader loader;
|
||||||
final avian.VMMethod method;
|
final avian.VMMethod method;
|
||||||
private volatile MethodType type;
|
private volatile MethodType type;
|
||||||
|
|
||||||
MethodHandle(int kind, ClassLoader loader, avian.VMMethod method) {
|
MethodHandle(int kind, ClassLoader loader, avian.VMMethod method) {
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
@ -44,7 +54,7 @@ public class MethodHandle {
|
|||||||
sb.append(Classes.makeString(method.spec, 0,
|
sb.append(Classes.makeString(method.spec, 0,
|
||||||
method.spec.length - 1));
|
method.spec.length - 1));
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodType type() {
|
public MethodType type() {
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
|
@ -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 {
|
||||||
|
@ -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.*;
|
||||||
@ -14,7 +24,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
private static final char[] Primitives = new char[] {
|
private static final char[] Primitives = new char[] {
|
||||||
'V', 'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D'
|
'V', 'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D'
|
||||||
};
|
};
|
||||||
|
|
||||||
final ClassLoader loader;
|
final ClassLoader loader;
|
||||||
final byte[] spec;
|
final byte[] spec;
|
||||||
private volatile List<Parameter> parameters;
|
private volatile List<Parameter> parameters;
|
||||||
@ -31,11 +41,11 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
this.spec = new byte[spec.length() + 1];
|
this.spec = new byte[spec.length() + 1];
|
||||||
spec.getBytes(0, spec.length(), this.spec, 0);
|
spec.getBytes(0, spec.length(), this.spec, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toMethodDescriptorString() {
|
public String toMethodDescriptorString() {
|
||||||
return Classes.makeString(spec, 0, spec.length - 1);
|
return Classes.makeString(spec, 0, spec.length - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String spec(Class c) {
|
private static String spec(Class c) {
|
||||||
if (c.isPrimitive()) {
|
if (c.isPrimitive()) {
|
||||||
VMClass vmc = Classes.toVMClass(c);
|
VMClass vmc = Classes.toVMClass(c);
|
||||||
@ -56,7 +66,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
Class ... ptypes)
|
Class ... ptypes)
|
||||||
{
|
{
|
||||||
loader = rtype.getClassLoader();
|
loader = rtype.getClassLoader();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append('(');
|
sb.append('(');
|
||||||
parameters = new ArrayList(ptypes.length);
|
parameters = new ArrayList(ptypes.length);
|
||||||
@ -66,7 +76,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
sb.append(spec);
|
sb.append(spec);
|
||||||
|
|
||||||
Type type = type(spec);
|
Type type = type(spec);
|
||||||
|
|
||||||
parameters.add(new Parameter(i,
|
parameters.add(new Parameter(i,
|
||||||
position,
|
position,
|
||||||
spec,
|
spec,
|
||||||
@ -86,7 +96,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
|
|
||||||
this.spec = sb.toString().getBytes();
|
this.spec = sb.toString().getBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MethodType methodType(Class rtype,
|
public static MethodType methodType(Class rtype,
|
||||||
Class ptype0,
|
Class ptype0,
|
||||||
Class ... ptypes)
|
Class ... ptypes)
|
||||||
@ -129,7 +139,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Iterable<Parameter> parameters() {
|
public Iterable<Parameter> parameters() {
|
||||||
if (parameters == null) {
|
if (parameters == null) {
|
||||||
List<Parameter> list = new ArrayList();
|
List<Parameter> list = new ArrayList();
|
||||||
@ -147,7 +157,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
case '[': {
|
case '[': {
|
||||||
++ i;
|
++ i;
|
||||||
while (spec[i] == '[') ++ i;
|
while (spec[i] == '[') ++ i;
|
||||||
|
|
||||||
switch (spec[i]) {
|
switch (spec[i]) {
|
||||||
case 'L':
|
case 'L':
|
||||||
++ i;
|
++ i;
|
||||||
@ -174,7 +184,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
|
|
||||||
String paramSpec = Classes.makeString(spec, start, (i - start) + 1);
|
String paramSpec = Classes.makeString(spec, start, (i - start) + 1);
|
||||||
Type type = type(paramSpec);
|
Type type = type(paramSpec);
|
||||||
|
|
||||||
list.add(new Parameter
|
list.add(new Parameter
|
||||||
(index,
|
(index,
|
||||||
position,
|
position,
|
||||||
@ -192,14 +202,14 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
|
|
||||||
String paramSpec = Classes.makeString(spec, i, spec.length - i - 1);
|
String paramSpec = Classes.makeString(spec, i, spec.length - i - 1);
|
||||||
Type type = type(paramSpec);
|
Type type = type(paramSpec);
|
||||||
|
|
||||||
result = new Result(paramSpec,
|
result = new Result(paramSpec,
|
||||||
Classes.forCanonicalName(loader, paramSpec),
|
Classes.forCanonicalName(loader, paramSpec),
|
||||||
type.return_);
|
type.return_);
|
||||||
|
|
||||||
parameters = list;
|
parameters = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,18 +244,18 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
case 'V':
|
case 'V':
|
||||||
return Type.VoidType;
|
return Type.VoidType;
|
||||||
|
|
||||||
default: throw new AssertionError();
|
default: throw new AssertionError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static enum Type {
|
private static enum Type {
|
||||||
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;
|
||||||
public final int return_;
|
public final int return_;
|
||||||
public final int size;
|
public final int size;
|
||||||
@ -256,7 +266,7 @@ public final class MethodType implements java.io.Serializable {
|
|||||||
this.size = size;
|
this.size = size;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Parameter {
|
public static class Parameter {
|
||||||
private final int index;
|
private final int index;
|
||||||
private final int position;
|
private final int position;
|
||||||
|
48
classpath/java/lang/invoke/SerializedLambda.java
Normal file
48
classpath/java/lang/invoke/SerializedLambda.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
274
src/compile.cpp
274
src/compile.cpp
@ -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/"
|
||||||
"invoke/MethodType;)Ljava/lang/invoke/CallSite;"),
|
"MethodType;Ljava/lang/invoke/MethodHandle;Ljava/"
|
||||||
bootstrap->spec()->body().begin()) == 0;
|
"lang/"
|
||||||
|
"invoke/MethodType;)Ljava/lang/invoke/CallSite;"),
|
||||||
|
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,120 +5144,147 @@ loop:
|
|||||||
GcClass* c = context->method->class_();
|
GcClass* c = context->method->class_();
|
||||||
PROTECT(t, c);
|
PROTECT(t, c);
|
||||||
|
|
||||||
GcCharArray* bootstrapArray = cast<GcCharArray>(
|
GcMethod* target
|
||||||
t,
|
= c->addendum()->bootstrapLambdaTable()
|
||||||
cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
|
? cast<GcMethod>(
|
||||||
->body()[invocation->bootstrap()]);
|
t,
|
||||||
PROTECT(t, bootstrapArray);
|
cast<GcArray>(t, c->addendum()->bootstrapLambdaTable())
|
||||||
|
->body()[invocation->bootstrap()])
|
||||||
|
: nullptr;
|
||||||
|
PROTECT(t, target);
|
||||||
|
|
||||||
if (isLambda(t, c->loader(), bootstrapArray, invocation)) {
|
if (target == nullptr) {
|
||||||
if (bc->hostVM == 0) {
|
GcCharArray* bootstrapArray = cast<GcCharArray>(
|
||||||
|
t,
|
||||||
|
cast<GcArray>(t, c->addendum()->bootstrapMethodTable())
|
||||||
|
->body()[invocation->bootstrap()]);
|
||||||
|
PROTECT(t, bootstrapArray);
|
||||||
|
|
||||||
|
if (isLambda(t, c->loader(), bootstrapArray, invocation)) {
|
||||||
|
if (bc->hostVM == 0) {
|
||||||
|
throwNew(
|
||||||
|
t,
|
||||||
|
GcVirtualMachineError::Type,
|
||||||
|
"lambda expression encountered, but host VM is not "
|
||||||
|
"available; use -hostvm option to bootimage-generator to "
|
||||||
|
"fix this");
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv* e;
|
||||||
|
if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0)
|
||||||
|
== 0) {
|
||||||
|
e->vtable->PushLocalFrame(e, 256);
|
||||||
|
|
||||||
|
jclass lmfClass = e->vtable->FindClass(
|
||||||
|
e, "java/lang/invoke/LambdaMetafactory");
|
||||||
|
jmethodID makeLambda = e->vtable->GetStaticMethodID(
|
||||||
|
e,
|
||||||
|
lmfClass,
|
||||||
|
"makeLambda",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
|
||||||
|
"String;Ljava/"
|
||||||
|
"lang/String;Ljava/lang/String;Ljava/lang/String;I)[B");
|
||||||
|
|
||||||
|
GcReference* reference = cast<GcReference>(
|
||||||
|
t,
|
||||||
|
singletonObject(
|
||||||
|
t, invocation->pool(), bootstrapArray->body()[2]));
|
||||||
|
int kind = reference->kind();
|
||||||
|
|
||||||
|
GcMethod* method
|
||||||
|
= cast<GcMethod>(t,
|
||||||
|
resolve(t,
|
||||||
|
c->loader(),
|
||||||
|
invocation->pool(),
|
||||||
|
bootstrapArray->body()[2],
|
||||||
|
findMethodInClass,
|
||||||
|
GcNoSuchMethodError::Type));
|
||||||
|
|
||||||
|
jarray lambda = e->vtable->CallStaticObjectMethod(
|
||||||
|
e,
|
||||||
|
lmfClass,
|
||||||
|
makeLambda,
|
||||||
|
e->vtable->NewStringUTF(
|
||||||
|
e,
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
invocation->template_()->name()->body().begin())),
|
||||||
|
e->vtable->NewStringUTF(
|
||||||
|
e,
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
invocation->template_()->spec()->body().begin())),
|
||||||
|
e->vtable->NewStringUTF(
|
||||||
|
e,
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
cast<GcByteArray>(
|
||||||
|
t,
|
||||||
|
singletonObject(t,
|
||||||
|
invocation->pool(),
|
||||||
|
bootstrapArray->body()[1]))
|
||||||
|
->body()
|
||||||
|
.begin())),
|
||||||
|
e->vtable->NewStringUTF(
|
||||||
|
e,
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
method->class_()->name()->body().begin())),
|
||||||
|
e->vtable->NewStringUTF(e,
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
method->name()->body().begin())),
|
||||||
|
e->vtable->NewStringUTF(e,
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
method->spec()->body().begin())),
|
||||||
|
kind);
|
||||||
|
|
||||||
|
uint8_t* bytes = reinterpret_cast<uint8_t*>(
|
||||||
|
e->vtable->GetPrimitiveArrayCritical(e, lambda, 0));
|
||||||
|
|
||||||
|
GcClass* lambdaClass
|
||||||
|
= defineClass(t,
|
||||||
|
roots(t)->appLoader(),
|
||||||
|
bytes,
|
||||||
|
e->vtable->GetArrayLength(e, lambda));
|
||||||
|
|
||||||
|
bc->resolver->addClass(
|
||||||
|
t, lambdaClass, bytes, e->vtable->GetArrayLength(e, lambda));
|
||||||
|
|
||||||
|
e->vtable->ReleasePrimitiveArrayCritical(e, lambda, bytes, 0);
|
||||||
|
|
||||||
|
e->vtable->PopLocalFrame(e, 0);
|
||||||
|
|
||||||
|
THREAD_RUNTIME_ARRAY(
|
||||||
|
t, char, spec, invocation->template_()->spec()->length());
|
||||||
|
memcpy(RUNTIME_ARRAY_BODY(spec),
|
||||||
|
invocation->template_()->spec()->body().begin(),
|
||||||
|
invocation->template_()->spec()->length());
|
||||||
|
|
||||||
|
target = resolveMethod(
|
||||||
|
t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec));
|
||||||
|
|
||||||
|
GcArray* table
|
||||||
|
= 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 {
|
||||||
|
throwNew(t,
|
||||||
|
GcVirtualMachineError::Type,
|
||||||
|
"unable to attach to host VM");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
throwNew(t,
|
throwNew(t,
|
||||||
GcVirtualMachineError::Type,
|
GcVirtualMachineError::Type,
|
||||||
"lambda expression encountered, but host VM is not "
|
"invokedynamic not supported for AOT-compiled code except "
|
||||||
"available; use -hostvm option to bootimage-generator to "
|
"in the case of lambda expressions");
|
||||||
"fix this");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv* e;
|
|
||||||
if (bc->hostVM->vtable->AttachCurrentThread(bc->hostVM, &e, 0) == 0) {
|
|
||||||
e->vtable->PushLocalFrame(e, 256);
|
|
||||||
|
|
||||||
jclass lmfClass
|
|
||||||
= e->vtable->FindClass(e, "java/lang/invoke/LambdaMetafactory");
|
|
||||||
jmethodID makeLambda = e->vtable->GetStaticMethodID(
|
|
||||||
e,
|
|
||||||
lmfClass,
|
|
||||||
"makeLambda",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/"
|
|
||||||
"lang/String;Ljava/lang/String;Ljava/lang/String;I)[B");
|
|
||||||
|
|
||||||
GcReference* reference = cast<GcReference>(
|
|
||||||
t,
|
|
||||||
singletonObject(
|
|
||||||
t, invocation->pool(), bootstrapArray->body()[2]));
|
|
||||||
int kind = reference->kind();
|
|
||||||
|
|
||||||
GcMethod* method
|
|
||||||
= cast<GcMethod>(t,
|
|
||||||
resolve(t,
|
|
||||||
c->loader(),
|
|
||||||
invocation->pool(),
|
|
||||||
bootstrapArray->body()[2],
|
|
||||||
findMethodInClass,
|
|
||||||
GcNoSuchMethodError::Type));
|
|
||||||
|
|
||||||
jarray lambda = e->vtable->CallStaticObjectMethod(
|
|
||||||
e,
|
|
||||||
lmfClass,
|
|
||||||
makeLambda,
|
|
||||||
e->vtable->NewStringUTF(
|
|
||||||
e,
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
invocation->template_()->name()->body().begin())),
|
|
||||||
e->vtable->NewStringUTF(
|
|
||||||
e,
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
invocation->template_()->spec()->body().begin())),
|
|
||||||
e->vtable->NewStringUTF(
|
|
||||||
e,
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
cast<GcByteArray>(
|
|
||||||
t,
|
|
||||||
singletonObject(t,
|
|
||||||
invocation->pool(),
|
|
||||||
bootstrapArray->body()[1]))
|
|
||||||
->body()
|
|
||||||
.begin())),
|
|
||||||
e->vtable->NewStringUTF(
|
|
||||||
e,
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
method->class_()->name()->body().begin())),
|
|
||||||
e->vtable->NewStringUTF(e,
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
method->name()->body().begin())),
|
|
||||||
e->vtable->NewStringUTF(e,
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
method->spec()->body().begin())),
|
|
||||||
kind);
|
|
||||||
|
|
||||||
uint8_t* bytes = reinterpret_cast<uint8_t*>(
|
|
||||||
e->vtable->GetPrimitiveArrayCritical(e, lambda, 0));
|
|
||||||
|
|
||||||
GcClass* lambdaClass
|
|
||||||
= defineClass(t,
|
|
||||||
roots(t)->appLoader(),
|
|
||||||
bytes,
|
|
||||||
e->vtable->GetArrayLength(e, lambda));
|
|
||||||
|
|
||||||
bc->resolver->addClass(
|
|
||||||
t, lambdaClass, bytes, e->vtable->GetArrayLength(e, lambda));
|
|
||||||
|
|
||||||
e->vtable->ReleasePrimitiveArrayCritical(e, lambda, bytes, 0);
|
|
||||||
|
|
||||||
e->vtable->PopLocalFrame(e, 0);
|
|
||||||
|
|
||||||
THREAD_RUNTIME_ARRAY(
|
|
||||||
t, char, spec, invocation->template_()->spec()->length());
|
|
||||||
memcpy(RUNTIME_ARRAY_BODY(spec),
|
|
||||||
invocation->template_()->spec()->body().begin(),
|
|
||||||
invocation->template_()->spec()->length());
|
|
||||||
|
|
||||||
GcMethod* target = resolveMethod(
|
|
||||||
t, lambdaClass, "make", RUNTIME_ARRAY_BODY(spec));
|
|
||||||
|
|
||||||
bool tailCall = isTailCall(t, code, ip, context->method, target);
|
|
||||||
compileDirectInvoke(t, frame, target, tailCall);
|
|
||||||
} else {
|
|
||||||
throwNew(
|
|
||||||
t, GcVirtualMachineError::Type, "unable to attach to host VM");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throwNew(t,
|
|
||||||
GcVirtualMachineError::Type,
|
|
||||||
"invokedynamic not supported for AOT-compiled code except "
|
|
||||||
"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);
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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\n");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeAccessors(Output* out, Module& module)
|
void writeAccessors(Output* out, Module& module)
|
||||||
|
@ -4,15 +4,33 @@ public class InvokeDynamic {
|
|||||||
private InvokeDynamic(int foo) {
|
private InvokeDynamic(int foo) {
|
||||||
this.foo = foo;
|
this.foo = foo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface Operation {
|
private interface Operation {
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
int c = 4;
|
int c = 4;
|
||||||
Operation op = (a, b) -> a + b - c;
|
Operation op = (a, b) -> a + b - c;
|
||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user