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;
|
package org.teavm.classlib.impl.lambda;
|
||||||
|
|
||||||
|
import java.lang.invoke.SerializedLambda;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -58,7 +60,8 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
MethodHandle implMethod = callSite.getBootstrapArguments().get(1).getMethodHandle();
|
MethodHandle implMethod = callSite.getBootstrapArguments().get(1).getMethodHandle();
|
||||||
ValueType[] instantiatedMethodType = callSite.getBootstrapArguments().get(2).getMethodType();
|
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();
|
ClassHierarchy hierarchy = callSite.getAgent().getClassHierarchy();
|
||||||
ClassReader samClass = hierarchy.getClassSource().get(samName);
|
ClassReader samClass = hierarchy.getClassSource().get(samName);
|
||||||
|
|
||||||
|
@ -128,6 +131,19 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
|
|
||||||
if ((flags & FLAG_SERIALIZABLE) != 0) {
|
if ((flags & FLAG_SERIALIZABLE) != 0) {
|
||||||
implementor.getInterfaces().add("java.io.Serializable");
|
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;
|
int bootstrapArgIndex = 4;
|
||||||
|
@ -375,4 +391,51 @@ public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor
|
||||||
|
|
||||||
implementor.addMethod(bridge);
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return (isInterface() ? "interface " : (isPrimitive() ? "" : "class "))
|
||||||
|
+ getName();
|
||||||
|
}
|
||||||
|
|
||||||
public PlatformClass getPlatformClass() {
|
public PlatformClass getPlatformClass() {
|
||||||
return platformClass;
|
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;
|
package org.teavm.model;
|
||||||
|
|
||||||
public enum MethodHandleType {
|
public enum MethodHandleType {
|
||||||
GET_FIELD,
|
GET_FIELD(1),
|
||||||
GET_STATIC_FIELD,
|
GET_STATIC_FIELD(2),
|
||||||
PUT_FIELD,
|
PUT_FIELD(3),
|
||||||
PUT_STATIC_FIELD,
|
PUT_STATIC_FIELD(4),
|
||||||
INVOKE_VIRTUAL,
|
INVOKE_VIRTUAL(5),
|
||||||
INVOKE_STATIC,
|
INVOKE_STATIC(6),
|
||||||
INVOKE_SPECIAL,
|
INVOKE_SPECIAL(7),
|
||||||
INVOKE_CONSTRUCTOR,
|
INVOKE_CONSTRUCTOR(8),
|
||||||
INVOKE_INTERFACE
|
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 {
|
public static class Primitive extends ValueType {
|
||||||
private PrimitiveType kind;
|
private PrimitiveType kind;
|
||||||
|
private final ValueType.Object boxedType;
|
||||||
private int hash;
|
private int hash;
|
||||||
|
|
||||||
private Primitive(PrimitiveType kind) {
|
private Primitive(PrimitiveType kind, ValueType.Object boxedType) {
|
||||||
this.kind = kind;
|
this.kind = kind;
|
||||||
|
this.boxedType = boxedType;
|
||||||
hash = 17988782 ^ (kind.ordinal() * 31);
|
hash = 17988782 ^ (kind.ordinal() * 31);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +86,10 @@ public abstract class ValueType implements Serializable {
|
||||||
return kind;
|
return kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ValueType.Object getBoxedType() {
|
||||||
|
return boxedType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
switch (kind) {
|
switch (kind) {
|
||||||
|
@ -198,21 +204,25 @@ public abstract class ValueType implements Serializable {
|
||||||
|
|
||||||
public static final Void VOID = new Void();
|
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 {
|
static {
|
||||||
primitiveMap.put(boolean.class, BOOLEAN);
|
primitiveMap.put(boolean.class, BOOLEAN);
|
||||||
|
@ -226,7 +236,7 @@ public abstract class ValueType implements Serializable {
|
||||||
primitiveMap.put(void.class, VOID);
|
primitiveMap.put(void.class, VOID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueType object(String cls) {
|
public static ValueType.Object object(String cls) {
|
||||||
return new Object(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;
|
package org.teavm.classlib.support;
|
||||||
|
|
||||||
|
import java.lang.invoke.SerializedLambda;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -45,6 +46,10 @@ public class ReflectionSupplierImpl implements ReflectionSupplier {
|
||||||
for (MethodReader method : cls.getMethods()) {
|
for (MethodReader method : cls.getMethods()) {
|
||||||
if (method.getAnnotations().get(Reflectable.class.getName()) != null) {
|
if (method.getAnnotations().get(Reflectable.class.getName()) != null) {
|
||||||
methods.add(method.getDescriptor());
|
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;
|
return methods;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user