mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Add write replace method to lambdas that implement java.io.Serializable (#453)
Add write replace method to lambdas that implement java.io.Serializable * Peripheral java library classes added as part of the toString method of SerializedLambda to align functionality with the standard library. * Test added that checks the SerializedLambda can be returned and that it is populated with the correct values including it's toString method.
This commit is contained in:
parent
9b6f5e7895
commit
34aed76714
|
@ -15,8 +15,10 @@
|
|||
*/
|
||||
package org.teavm.classlib.impl.lambda;
|
||||
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -58,7 +60,8 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
MethodHandle implMethod = callSite.getBootstrapArguments().get(1).getMethodHandle();
|
||||
ValueType[] instantiatedMethodType = callSite.getBootstrapArguments().get(2).getMethodType();
|
||||
|
||||
String samName = ((ValueType.Object) callSite.getCalledMethod().getResultType()).getClassName();
|
||||
ValueType.Object lambdaInterfaceType = (ValueType.Object) callSite.getCalledMethod().getResultType();
|
||||
String samName = lambdaInterfaceType.getClassName();
|
||||
ClassHierarchy hierarchy = callSite.getAgent().getClassHierarchy();
|
||||
ClassReader samClass = hierarchy.getClassSource().get(samName);
|
||||
|
||||
|
@ -128,6 +131,19 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
|
||||
if ((flags & FLAG_SERIALIZABLE) != 0) {
|
||||
implementor.getInterfaces().add("java.io.Serializable");
|
||||
String functionInterfaceMethodName = callSite.getCalledMethod().getName();
|
||||
addWriteReplaceMethod(
|
||||
callerPe.getCurrentLocation(),
|
||||
hierarchy,
|
||||
implementor,
|
||||
ValueType.object(callSite.getCaller().getClassName()),
|
||||
lambdaInterfaceType,
|
||||
new MethodDescriptor(functionInterfaceMethodName, samMethodType),
|
||||
implMethod.getKind(),
|
||||
ValueType.object(implMethod.getClassName()),
|
||||
new MethodDescriptor(implMethod.getName(), implMethod.signature()),
|
||||
new MethodDescriptor(functionInterfaceMethodName, instantiatedMethodType)
|
||||
);
|
||||
}
|
||||
|
||||
int bootstrapArgIndex = 4;
|
||||
|
@ -375,4 +391,51 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
|||
|
||||
implementor.addMethod(bridge);
|
||||
}
|
||||
|
||||
private static void addWriteReplaceMethod(
|
||||
TextLocation location,
|
||||
ClassHierarchy classHierarchy,
|
||||
ClassHolder lambdaClassDefinition,
|
||||
ValueType.Object capturingClass,
|
||||
ValueType.Object functionalInterfaceClass,
|
||||
MethodDescriptor functionalInterfaceMethodDescriptor,
|
||||
MethodHandleType implMethodKind,
|
||||
ValueType.Object implClass,
|
||||
MethodDescriptor implMethodDescriptor,
|
||||
MethodDescriptor instantiatedMethodDescriptor
|
||||
) {
|
||||
MethodHolder writeReplace =
|
||||
new MethodHolder("writeReplace", new ValueType.Object(SerializedLambda.class.getName()));
|
||||
writeReplace.setLevel(AccessLevel.PRIVATE);
|
||||
writeReplace.getModifiers().add(ElementModifier.FINAL);
|
||||
ProgramEmitter programEmitter = ProgramEmitter.create(writeReplace, classHierarchy);
|
||||
programEmitter.setCurrentLocation(location);
|
||||
Collection<FieldHolder> fields = lambdaClassDefinition.getFields();
|
||||
ValueEmitter capturedParametersArray = programEmitter.constructArray(Object.class, fields.size());
|
||||
ValueEmitter lambdaThis = programEmitter.var(0, lambdaClassDefinition);
|
||||
int index = 0;
|
||||
for (FieldHolder fieldHolder : fields) {
|
||||
ValueType fieldType = fieldHolder.getType();
|
||||
ValueEmitter fieldValue = lambdaThis.getField(fieldHolder.getName(), fieldType);
|
||||
if (fieldType instanceof ValueType.Primitive) {
|
||||
fieldValue = fieldValue.cast(((ValueType.Primitive) fieldType).getBoxedType());
|
||||
}
|
||||
capturedParametersArray.setElement(index++, fieldValue);
|
||||
}
|
||||
ValueEmitter newSerializedLambda = programEmitter.construct(
|
||||
SerializedLambda.class,
|
||||
programEmitter.constant(capturingClass),
|
||||
programEmitter.constant(functionalInterfaceClass.getClassName().replace('.', '/')),
|
||||
programEmitter.constant(functionalInterfaceMethodDescriptor.getName()),
|
||||
programEmitter.constant(functionalInterfaceMethodDescriptor.signatureToString()),
|
||||
programEmitter.constant(implMethodKind.getReferenceKind()),
|
||||
programEmitter.constant(implClass.getClassName().replace('.', '/')),
|
||||
programEmitter.constant(implMethodDescriptor.getName()),
|
||||
programEmitter.constant(implMethodDescriptor.signatureToString()),
|
||||
programEmitter.constant(instantiatedMethodDescriptor.signatureToString()),
|
||||
capturedParametersArray
|
||||
);
|
||||
newSerializedLambda.returnValue();
|
||||
lambdaClassDefinition.addMethod(writeReplace);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,12 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
|
||||
+ getName();
|
||||
}
|
||||
|
||||
public PlatformClass getPlatformClass() {
|
||||
return platformClass;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2020 adam.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang.invoke;
|
||||
|
||||
public
|
||||
interface TMethodHandleInfo {
|
||||
static String referenceKindToString(int referenceKind) {
|
||||
if (!TMethodHandleNatives.refKindIsValid(referenceKind)) {
|
||||
throw new IllegalArgumentException("invalid reference kind: " + referenceKind);
|
||||
}
|
||||
return TMethodHandleNatives.refKindName((byte) referenceKind);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2020 adam.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang.invoke;
|
||||
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_LIMIT;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_NONE;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_getField;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_getStatic;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_invokeInterface;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_invokeSpecial;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_invokeStatic;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_invokeVirtual;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_newInvokeSpecial;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_putField;
|
||||
import static org.teavm.classlib.java.lang.invoke.TMethodHandleNatives.Constants.REF_putStatic;
|
||||
|
||||
class TMethodHandleNatives {
|
||||
static class Constants {
|
||||
Constants() { } // static only
|
||||
|
||||
static final byte REF_NONE = 0;
|
||||
static final byte REF_getField = 1;
|
||||
static final byte REF_getStatic = 2;
|
||||
static final byte REF_putField = 3;
|
||||
static final byte REF_putStatic = 4;
|
||||
static final byte REF_invokeVirtual = 5;
|
||||
static final byte REF_invokeStatic = 6;
|
||||
static final byte REF_invokeSpecial = 7;
|
||||
static final byte REF_newInvokeSpecial = 8;
|
||||
static final byte REF_invokeInterface = 9;
|
||||
static final byte REF_LIMIT = 10;
|
||||
}
|
||||
|
||||
static boolean refKindIsValid(int refKind) {
|
||||
return refKind > REF_NONE && refKind < REF_LIMIT;
|
||||
}
|
||||
static String refKindName(byte refKind) {
|
||||
assert refKindIsValid(refKind);
|
||||
switch (refKind) {
|
||||
case REF_getField: return "getField";
|
||||
case REF_getStatic: return "getStatic";
|
||||
case REF_putField: return "putField";
|
||||
case REF_putStatic: return "putStatic";
|
||||
case REF_invokeVirtual: return "invokeVirtual";
|
||||
case REF_invokeStatic: return "invokeStatic";
|
||||
case REF_invokeSpecial: return "invokeSpecial";
|
||||
case REF_newInvokeSpecial: return "newInvokeSpecial";
|
||||
case REF_invokeInterface: return "invokeInterface";
|
||||
default: return "REF_???";
|
||||
}
|
||||
}
|
||||
|
||||
private TMethodHandleNatives() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright 2020 adam.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang.invoke;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.MethodHandleInfo;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class TSerializedLambda implements Serializable {
|
||||
private static final long serialVersionUID = 8025925345765570181L;
|
||||
private final Class<?> capturingClass;
|
||||
private final String functionalInterfaceClass;
|
||||
private final String functionalInterfaceMethodName;
|
||||
private final String functionalInterfaceMethodSignature;
|
||||
private final String implClass;
|
||||
private final String implMethodName;
|
||||
private final String implMethodSignature;
|
||||
private final int implMethodKind;
|
||||
private final String instantiatedMethodType;
|
||||
private final Object[] capturedArgs;
|
||||
|
||||
public TSerializedLambda(Class<?> capturingClass,
|
||||
String functionalInterfaceClass,
|
||||
String functionalInterfaceMethodName,
|
||||
String functionalInterfaceMethodSignature,
|
||||
int implMethodKind,
|
||||
String implClass,
|
||||
String implMethodName,
|
||||
String implMethodSignature,
|
||||
String instantiatedMethodType,
|
||||
Object[] capturedArgs) {
|
||||
this.capturingClass = capturingClass;
|
||||
this.functionalInterfaceClass = functionalInterfaceClass;
|
||||
this.functionalInterfaceMethodName = functionalInterfaceMethodName;
|
||||
this.functionalInterfaceMethodSignature = functionalInterfaceMethodSignature;
|
||||
this.implMethodKind = implMethodKind;
|
||||
this.implClass = implClass;
|
||||
this.implMethodName = implMethodName;
|
||||
this.implMethodSignature = implMethodSignature;
|
||||
this.instantiatedMethodType = instantiatedMethodType;
|
||||
this.capturedArgs = Objects.requireNonNull(capturedArgs).clone();
|
||||
}
|
||||
|
||||
public String getCapturingClass() {
|
||||
return capturingClass.getName().replace('.', '/');
|
||||
}
|
||||
|
||||
public String getFunctionalInterfaceClass() {
|
||||
return functionalInterfaceClass;
|
||||
}
|
||||
|
||||
public String getFunctionalInterfaceMethodName() {
|
||||
return functionalInterfaceMethodName;
|
||||
}
|
||||
|
||||
public String getFunctionalInterfaceMethodSignature() {
|
||||
return functionalInterfaceMethodSignature;
|
||||
}
|
||||
|
||||
public String getImplClass() {
|
||||
return implClass;
|
||||
}
|
||||
|
||||
public String getImplMethodName() {
|
||||
return implMethodName;
|
||||
}
|
||||
|
||||
public String getImplMethodSignature() {
|
||||
return implMethodSignature;
|
||||
}
|
||||
|
||||
public int getImplMethodKind() {
|
||||
return implMethodKind;
|
||||
}
|
||||
|
||||
public String getInstantiatedMethodType() {
|
||||
return instantiatedMethodType;
|
||||
}
|
||||
|
||||
public int getCapturedArgCount() {
|
||||
return capturedArgs.length;
|
||||
}
|
||||
|
||||
public Object getCapturedArg(int i) {
|
||||
return capturedArgs[i];
|
||||
}
|
||||
|
||||
private Object readResolve() throws ReflectiveOperationException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String implKind = MethodHandleInfo.referenceKindToString(implMethodKind);
|
||||
return "SerializedLambda[capturingClass=" + capturingClass + ", functionalInterfaceMethod="
|
||||
+ functionalInterfaceClass + "." + functionalInterfaceMethodName + ":"
|
||||
+ functionalInterfaceMethodSignature + ", "
|
||||
+ "implementation=" + implKind + " " + implClass + "." + implMethodName + ":" + implMethodSignature
|
||||
+ ", instantiatedMethodType=" + instantiatedMethodType + ", numCaptured=" + capturedArgs.length + "]";
|
||||
}
|
||||
}
|
|
@ -16,13 +16,23 @@
|
|||
package org.teavm.model;
|
||||
|
||||
public enum MethodHandleType {
|
||||
GET_FIELD,
|
||||
GET_STATIC_FIELD,
|
||||
PUT_FIELD,
|
||||
PUT_STATIC_FIELD,
|
||||
INVOKE_VIRTUAL,
|
||||
INVOKE_STATIC,
|
||||
INVOKE_SPECIAL,
|
||||
INVOKE_CONSTRUCTOR,
|
||||
INVOKE_INTERFACE
|
||||
GET_FIELD(1),
|
||||
GET_STATIC_FIELD(2),
|
||||
PUT_FIELD(3),
|
||||
PUT_STATIC_FIELD(4),
|
||||
INVOKE_VIRTUAL(5),
|
||||
INVOKE_STATIC(6),
|
||||
INVOKE_SPECIAL(7),
|
||||
INVOKE_CONSTRUCTOR(8),
|
||||
INVOKE_INTERFACE(9),
|
||||
;
|
||||
private final int referenceKind;
|
||||
|
||||
MethodHandleType(int referenceKind) {
|
||||
this.referenceKind = referenceKind;
|
||||
}
|
||||
|
||||
public int getReferenceKind() {
|
||||
return referenceKind;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,10 +73,12 @@ public abstract class ValueType implements Serializable {
|
|||
|
||||
public static class Primitive extends ValueType {
|
||||
private PrimitiveType kind;
|
||||
private final ValueType.Object boxedType;
|
||||
private int hash;
|
||||
|
||||
private Primitive(PrimitiveType kind) {
|
||||
private Primitive(PrimitiveType kind, ValueType.Object boxedType) {
|
||||
this.kind = kind;
|
||||
this.boxedType = boxedType;
|
||||
hash = 17988782 ^ (kind.ordinal() * 31);
|
||||
}
|
||||
|
||||
|
@ -84,6 +86,10 @@ public abstract class ValueType implements Serializable {
|
|||
return kind;
|
||||
}
|
||||
|
||||
public ValueType.Object getBoxedType() {
|
||||
return boxedType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (kind) {
|
||||
|
@ -198,21 +204,25 @@ public abstract class ValueType implements Serializable {
|
|||
|
||||
public static final Void VOID = new Void();
|
||||
|
||||
public static final Primitive BOOLEAN = new Primitive(PrimitiveType.BOOLEAN);
|
||||
public static final Primitive BOOLEAN =
|
||||
new Primitive(PrimitiveType.BOOLEAN, ValueType.object(Boolean.class.getName()));
|
||||
|
||||
public static final Primitive BYTE = new Primitive(PrimitiveType.BYTE);
|
||||
public static final Primitive BYTE = new Primitive(PrimitiveType.BYTE, ValueType.object(Byte.class.getName()));
|
||||
|
||||
public static final Primitive SHORT = new Primitive(PrimitiveType.SHORT);
|
||||
public static final Primitive SHORT = new Primitive(PrimitiveType.SHORT, ValueType.object(Short.class.getName()));
|
||||
|
||||
public static final Primitive INTEGER = new Primitive(PrimitiveType.INTEGER);
|
||||
public static final Primitive INTEGER =
|
||||
new Primitive(PrimitiveType.INTEGER, ValueType.object(Integer.class.getName()));
|
||||
|
||||
public static final Primitive FLOAT = new Primitive(PrimitiveType.FLOAT);
|
||||
public static final Primitive FLOAT = new Primitive(PrimitiveType.FLOAT, ValueType.object(Float.class.getName()));
|
||||
|
||||
public static final Primitive LONG = new Primitive(PrimitiveType.LONG);
|
||||
public static final Primitive LONG = new Primitive(PrimitiveType.LONG, ValueType.object(Long.class.getName()));
|
||||
|
||||
public static final Primitive DOUBLE = new Primitive(PrimitiveType.DOUBLE);
|
||||
public static final Primitive DOUBLE =
|
||||
new Primitive(PrimitiveType.DOUBLE, ValueType.object(Double.class.getName()));
|
||||
|
||||
public static final Primitive CHARACTER = new Primitive(PrimitiveType.CHARACTER);
|
||||
public static final Primitive CHARACTER =
|
||||
new Primitive(PrimitiveType.CHARACTER, ValueType.object(Character.class.getName()));
|
||||
|
||||
static {
|
||||
primitiveMap.put(boolean.class, BOOLEAN);
|
||||
|
@ -226,7 +236,7 @@ public abstract class ValueType implements Serializable {
|
|||
primitiveMap.put(void.class, VOID);
|
||||
}
|
||||
|
||||
public static ValueType object(String cls) {
|
||||
public static ValueType.Object object(String cls) {
|
||||
return new Object(cls);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2020 adam.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.teavm.classlib.java.lang.invoke;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import java.io.Serializable;
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.teavm.junit.TeaVMTestRunner;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
public class SerializedLambdaTest {
|
||||
@Test
|
||||
public void serializableLambdaHasWriteReplaceMethod() throws NoSuchMethodException, InvocationTargetException,
|
||||
IllegalAccessException {
|
||||
SerializableFunction<Object, String> lambda = Object::toString;
|
||||
Method writeReplace = lambda.getClass().getDeclaredMethod("writeReplace");
|
||||
writeReplace.setAccessible(true);
|
||||
SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(lambda);
|
||||
assertEquals("org/teavm/classlib/java/lang/invoke/SerializedLambdaTest", serializedLambda.getCapturingClass());
|
||||
assertEquals(0, serializedLambda.getCapturedArgCount());
|
||||
assertEquals("org/teavm/classlib/java/lang/invoke/SerializedLambdaTest$SerializableFunction",
|
||||
serializedLambda.getFunctionalInterfaceClass());
|
||||
assertEquals("apply", serializedLambda.getFunctionalInterfaceMethodName());
|
||||
assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
serializedLambda.getFunctionalInterfaceMethodSignature());
|
||||
assertEquals("java/lang/Object", serializedLambda.getImplClass());
|
||||
assertEquals(5, serializedLambda.getImplMethodKind());
|
||||
assertEquals("toString", serializedLambda.getImplMethodName());
|
||||
assertEquals("()Ljava/lang/String;", serializedLambda.getImplMethodSignature());
|
||||
assertEquals("(Ljava/lang/Object;)Ljava/lang/String;", serializedLambda.getInstantiatedMethodType());
|
||||
assertEquals(
|
||||
"SerializedLambda[capturingClass=class org.teavm.classlib.java.lang.invoke.SerializedLambdaTest, "
|
||||
+ "functionalInterfaceMethod=org/teavm/classlib/java/lang/invoke"
|
||||
+ "/SerializedLambdaTest$SerializableFunction.apply:(Ljava/lang/Object;)Ljava/lang/Object;, "
|
||||
+ "implementation=invokeVirtual java/lang/Object.toString:()Ljava/lang/String;, "
|
||||
+ "instantiatedMethodType=(Ljava/lang/Object;)Ljava/lang/String;, numCaptured=0]",
|
||||
serializedLambda.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void serializableLambdaWriteReplaceCapturesArguments() throws NoSuchMethodException,
|
||||
InvocationTargetException,
|
||||
IllegalAccessException {
|
||||
String captureValue = "captured-value";
|
||||
SerializableFunction<Object, String> lambda = o -> captureValue;
|
||||
Method writeReplace = lambda.getClass().getDeclaredMethod("writeReplace");
|
||||
writeReplace.setAccessible(true);
|
||||
SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(lambda);
|
||||
assertEquals("org/teavm/classlib/java/lang/invoke/SerializedLambdaTest", serializedLambda.getCapturingClass());
|
||||
assertEquals(1, serializedLambda.getCapturedArgCount());
|
||||
assertEquals(captureValue, serializedLambda.getCapturedArg(0));
|
||||
assertEquals("org/teavm/classlib/java/lang/invoke/SerializedLambdaTest$SerializableFunction",
|
||||
serializedLambda.getFunctionalInterfaceClass());
|
||||
assertEquals("apply", serializedLambda.getFunctionalInterfaceMethodName());
|
||||
assertEquals("(Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
serializedLambda.getFunctionalInterfaceMethodSignature());
|
||||
assertEquals("org/teavm/classlib/java/lang/invoke/SerializedLambdaTest", serializedLambda.getImplClass());
|
||||
assertEquals(6, serializedLambda.getImplMethodKind());
|
||||
assertEquals("(Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;",
|
||||
serializedLambda.getImplMethodSignature());
|
||||
assertEquals("(Ljava/lang/Object;)Ljava/lang/String;", serializedLambda.getInstantiatedMethodType());
|
||||
}
|
||||
|
||||
private interface SerializableFunction<T, R> extends Function<T, R>, Serializable {
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.support;
|
||||
|
||||
import java.lang.invoke.SerializedLambda;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
@ -45,6 +46,10 @@ public class ReflectionSupplierImpl implements ReflectionSupplier {
|
|||
for (MethodReader method : cls.getMethods()) {
|
||||
if (method.getAnnotations().get(Reflectable.class.getName()) != null) {
|
||||
methods.add(method.getDescriptor());
|
||||
} else if ("writeReplace".equals(method.getName())
|
||||
&& method.getResultType().isObject(SerializedLambda.class)) {
|
||||
//Required by org.teavm.classlib.java.lang.invoke.SerializedLambdaTest.
|
||||
methods.add(method.getDescriptor());
|
||||
}
|
||||
}
|
||||
return methods;
|
||||
|
|
Loading…
Reference in New Issue
Block a user