mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
LambdaMetafactory support
This commit is contained in:
parent
d5f5e2633b
commit
13353d0bde
|
@ -15,7 +15,13 @@
|
|||
*/
|
||||
package org.teavm.classlib.impl;
|
||||
|
||||
import java.lang.invoke.CallSite;
|
||||
import java.lang.invoke.LambdaMetafactory;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ServiceLoader;
|
||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
@ -41,5 +47,8 @@ public class JCLPlugin implements TeaVMPlugin {
|
|||
host.registerService(CLDRReader.class, new CLDRReader(host.getProperties(), host.getClassLoader()));
|
||||
|
||||
host.add(new AnnotationDependencyListener());
|
||||
host.add(new MethodReference(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class,
|
||||
String.class, MethodType.class, MethodType.class, MethodHandle.class, MethodType.class,
|
||||
CallSite.class), new LambdaMetafactorySubstitutor());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,314 @@
|
|||
package org.teavm.classlib.impl.lambda;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||
import org.teavm.dependency.DynamicCallSite;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodHandle;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.PrimitiveType;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.model.instructions.CastIntegerDirection;
|
||||
import org.teavm.model.instructions.IntegerSubtype;
|
||||
import org.teavm.model.instructions.NumericOperandType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class LambdaMetafactorySubstitutor implements BootstrapMethodSubstitutor {
|
||||
private int lambdaIndex = 0;
|
||||
|
||||
@Override
|
||||
public ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter callerPe) {
|
||||
ValueType[] invokedType = callSite.getCalledMethod().getSignature();
|
||||
ValueType[] samMethodType = callSite.getBootstrapArguments().get(0).getMethodType();
|
||||
MethodHandle implMethod = callSite.getBootstrapArguments().get(1).getMethodHandle();
|
||||
ValueType[] instantiatedMethodType = callSite.getBootstrapArguments().get(2).getMethodType();
|
||||
|
||||
String samName = ((ValueType.Object) callSite.getCalledMethod().getResultType()).getClassName();
|
||||
ClassReaderSource classSource = callSite.getAgent().getClassSource();
|
||||
ClassReader samClass = classSource.get(samName);
|
||||
|
||||
ClassHolder implementor = new ClassHolder("$$LAMBDA" + (lambdaIndex++) + "$$");
|
||||
implementor.setLevel(AccessLevel.PUBLIC);
|
||||
if (samClass != null && samClass.hasModifier(ElementModifier.INTERFACE)) {
|
||||
implementor.setParent("java.lang.Object");
|
||||
implementor.getInterfaces().add(samName);
|
||||
} else {
|
||||
implementor.setParent(samName);
|
||||
}
|
||||
|
||||
int capturedVarCount = callSite.getCalledMethod().parameterCount();
|
||||
MethodHolder ctor = createConstructor(implementor, Arrays.copyOfRange(invokedType, 0, capturedVarCount));
|
||||
createBridge(implementor, callSite.getCalledMethod().getName(), instantiatedMethodType, samMethodType);
|
||||
|
||||
MethodHolder worker = new MethodHolder(callSite.getCalledMethod().getName(), instantiatedMethodType);
|
||||
worker.setLevel(AccessLevel.PUBLIC);
|
||||
ProgramEmitter pe = ProgramEmitter.create(worker);
|
||||
ValueEmitter thisVar = pe.newVar();
|
||||
ValueEmitter[] arguments = new ValueEmitter[instantiatedMethodType.length - 1];
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
arguments[i] = pe.newVar();
|
||||
}
|
||||
|
||||
ValueType[] implementorSignature = getSignature(implMethod);
|
||||
ValueEmitter[] passedArguments = new ValueEmitter[implementorSignature.length - 1];
|
||||
for (int i = 0; i < capturedVarCount; ++i) {
|
||||
passedArguments[i] = thisVar.getField(new FieldReference(implementor.getName(), "_" + i), invokedType[i]);
|
||||
}
|
||||
for (int i = 0; i < instantiatedMethodType.length - 1; ++i) {
|
||||
passedArguments[i + capturedVarCount] = tryConvertArgument(arguments[i], instantiatedMethodType[i],
|
||||
implementorSignature[i + capturedVarCount]);
|
||||
}
|
||||
|
||||
ValueEmitter result = invoke(pe, implMethod, callSite.getInstance(), passedArguments);
|
||||
if (result != null) {
|
||||
ValueType actualResult = implementorSignature[implementorSignature.length - 1];
|
||||
ValueType expectedResult = instantiatedMethodType[instantiatedMethodType.length - 1];
|
||||
tryConvertArgument(result, actualResult, expectedResult).returnValue();
|
||||
} else {
|
||||
pe.exit();
|
||||
}
|
||||
|
||||
implementor.addMethod(worker);
|
||||
|
||||
callSite.getAgent().submitClass(implementor);
|
||||
return callerPe.construct(ctor.getReference(), callSite.getArguments().toArray(new ValueEmitter[0]));
|
||||
}
|
||||
|
||||
private ValueEmitter invoke(ProgramEmitter pe, MethodHandle handle, ValueEmitter instance,
|
||||
ValueEmitter[] arguments) {
|
||||
switch (handle.getKind()) {
|
||||
case GET_FIELD:
|
||||
return instance.getField(new FieldReference(handle.getClassName(), handle.getName()),
|
||||
handle.getValueType());
|
||||
case GET_STATIC_FIELD:
|
||||
return pe.getField(new FieldReference(handle.getClassName(), handle.getName()),
|
||||
handle.getValueType());
|
||||
case PUT_FIELD:
|
||||
instance.setField(new FieldReference(handle.getClassName(), handle.getName()), handle.getValueType(),
|
||||
arguments[0]);
|
||||
return null;
|
||||
case PUT_STATIC_FIELD:
|
||||
pe.setField(new FieldReference(handle.getClassName(), handle.getName()), handle.getValueType(),
|
||||
arguments[0]);
|
||||
return null;
|
||||
case INVOKE_VIRTUAL:
|
||||
case INVOKE_INTERFACE:
|
||||
case INVOKE_SPECIAL:
|
||||
return instance.invokeVirtual(new MethodReference(handle.getClassName(), handle.getName(),
|
||||
handle.signature()), arguments);
|
||||
case INVOKE_STATIC:
|
||||
return pe.invoke(new MethodReference(handle.getClassName(), handle.getName(),
|
||||
handle.signature()), arguments);
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
return pe.construct(new MethodReference(handle.getClassName(), handle.getName(),
|
||||
handle.signature()), arguments);
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected handle type: " + handle.getKind());
|
||||
}
|
||||
}
|
||||
|
||||
private ValueEmitter tryConvertArgument(ValueEmitter arg, ValueType from, ValueType to) {
|
||||
if (from.equals(to)) {
|
||||
return arg;
|
||||
}
|
||||
if (from instanceof ValueType.Primitive && to instanceof ValueType.Primitive) {
|
||||
PrimitiveType fromType = ((ValueType.Primitive) from).getKind();
|
||||
PrimitiveType toType = ((ValueType.Primitive) to).getKind();
|
||||
IntegerSubtype fromSubtype = convert(fromType);
|
||||
if (fromSubtype != null) {
|
||||
arg = arg.cast(fromSubtype, CastIntegerDirection.TO_INTEGER);
|
||||
}
|
||||
arg = arg.cast(convertNumeric(fromType), convertNumeric(toType));
|
||||
IntegerSubtype toSubtype = convert(toType);
|
||||
if (toSubtype != null) {
|
||||
arg = arg.cast(fromSubtype, CastIntegerDirection.FROM_INTEGER);
|
||||
}
|
||||
return arg;
|
||||
} else if (from instanceof ValueType.Primitive && to instanceof ValueType.Object) {
|
||||
String primitiveClass = ((ValueType.Object) to).getClassName();
|
||||
PrimitiveType toType = getWrappedPrimitive(primitiveClass);
|
||||
if (toType == null) {
|
||||
return arg;
|
||||
}
|
||||
arg = tryConvertArgument(arg, from, ValueType.primitive(toType));
|
||||
return arg.getProgramEmitter().invoke(new MethodReference(primitiveClass, "valueOf",
|
||||
ValueType.primitive(toType), to), arg);
|
||||
} else if (from instanceof ValueType.Object && to instanceof ValueType.Primitive) {
|
||||
String primitiveClass = ((ValueType.Object) from).getClassName();
|
||||
PrimitiveType fromType = getWrappedPrimitive(primitiveClass);
|
||||
if (fromType == null) {
|
||||
return arg;
|
||||
}
|
||||
arg = arg.invokeVirtual(new MethodReference(primitiveClass, primitiveName(fromType) + "Value",
|
||||
ValueType.primitive(fromType)));
|
||||
return tryConvertArgument(arg, ValueType.primitive(fromType), to);
|
||||
} else {
|
||||
return arg.cast(to);
|
||||
}
|
||||
}
|
||||
|
||||
private IntegerSubtype convert(PrimitiveType type) {
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
return IntegerSubtype.BYTE;
|
||||
case SHORT:
|
||||
return IntegerSubtype.SHORT;
|
||||
case CHARACTER:
|
||||
return IntegerSubtype.CHARACTER;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private NumericOperandType convertNumeric(PrimitiveType type) {
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
case BYTE:
|
||||
case SHORT:
|
||||
case CHARACTER:
|
||||
case INTEGER:
|
||||
return NumericOperandType.INT;
|
||||
case LONG:
|
||||
return NumericOperandType.LONG;
|
||||
case FLOAT:
|
||||
return NumericOperandType.FLOAT;
|
||||
case DOUBLE:
|
||||
return NumericOperandType.DOUBLE;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private PrimitiveType getWrappedPrimitive(String name) {
|
||||
switch (name) {
|
||||
case "java.lang.Boolean":
|
||||
return PrimitiveType.BOOLEAN;
|
||||
case "java.lang.Byte":
|
||||
return PrimitiveType.BYTE;
|
||||
case "java.lang.Short":
|
||||
return PrimitiveType.SHORT;
|
||||
case "java.lang.Character":
|
||||
return PrimitiveType.CHARACTER;
|
||||
case "java.lang.Integer":
|
||||
return PrimitiveType.INTEGER;
|
||||
case "java.lang.Long":
|
||||
return PrimitiveType.LONG;
|
||||
case "java.lang.Float":
|
||||
return PrimitiveType.FLOAT;
|
||||
case "java.lang.Double":
|
||||
return PrimitiveType.DOUBLE;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String primitiveName(PrimitiveType type) {
|
||||
switch (type) {
|
||||
case BOOLEAN:
|
||||
return "boolean";
|
||||
case BYTE:
|
||||
return "byte";
|
||||
case SHORT:
|
||||
return "short";
|
||||
case CHARACTER:
|
||||
return "char";
|
||||
case INTEGER:
|
||||
return "int";
|
||||
case LONG:
|
||||
return "long";
|
||||
case FLOAT:
|
||||
return "float";
|
||||
case DOUBLE:
|
||||
return "double";
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected primitive " + type);
|
||||
}
|
||||
}
|
||||
|
||||
private ValueType[] getSignature(MethodHandle handle) {
|
||||
switch (handle.getKind()) {
|
||||
case GET_FIELD:
|
||||
case GET_STATIC_FIELD:
|
||||
return new ValueType[] { handle.getValueType() };
|
||||
case PUT_FIELD:
|
||||
case PUT_STATIC_FIELD:
|
||||
return new ValueType[] { handle.getValueType(), ValueType.VOID };
|
||||
default:
|
||||
return handle.signature();
|
||||
}
|
||||
}
|
||||
|
||||
private MethodHolder createConstructor(ClassHolder implementor, ValueType[] types) {
|
||||
ValueType[] signature = Arrays.copyOf(types, types.length + 1);
|
||||
signature[types.length] = ValueType.VOID;
|
||||
MethodHolder ctor = new MethodHolder("<init>", signature);
|
||||
ctor.setLevel(AccessLevel.PUBLIC);
|
||||
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor);
|
||||
ValueEmitter thisVar = pe.newVar();
|
||||
thisVar.invokeSpecial(new MethodReference(implementor.getParent(), "<init>", ValueType.VOID));
|
||||
|
||||
for (int i = 0; i < types.length; ++i) {
|
||||
FieldHolder field = new FieldHolder("_" + i);
|
||||
field.setLevel(AccessLevel.PRIVATE);
|
||||
field.setType(types[i]);
|
||||
implementor.addField(field);
|
||||
thisVar.setField(field.getReference(), types[i], pe.newVar());
|
||||
}
|
||||
|
||||
pe.exit();
|
||||
implementor.addMethod(ctor);
|
||||
return ctor;
|
||||
}
|
||||
|
||||
private void createBridge(ClassHolder implementor, String name, ValueType[] types, ValueType[] bridgeTypes) {
|
||||
if (Arrays.equals(types, bridgeTypes)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MethodHolder bridge = new MethodHolder(name, bridgeTypes);
|
||||
bridge.setLevel(AccessLevel.PUBLIC);
|
||||
bridge.getModifiers().add(ElementModifier.BRIDGE);
|
||||
ProgramEmitter pe = ProgramEmitter.create(bridge);
|
||||
ValueEmitter thisVar = pe.newVar();
|
||||
ValueEmitter[] arguments = new ValueEmitter[bridgeTypes.length - 1];
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
arguments[i] = pe.newVar();
|
||||
}
|
||||
|
||||
for (int i = 0; i < bridgeTypes.length - 1; ++i) {
|
||||
ValueType type = types[i];
|
||||
ValueType bridgeType = types[i];
|
||||
if (type.equals(bridgeType)) {
|
||||
continue;
|
||||
}
|
||||
arguments[i] = arguments[i].cast(type);
|
||||
}
|
||||
|
||||
ValueEmitter result = thisVar.invokeVirtual(new MethodReference(implementor.getName(), name, types),
|
||||
arguments);
|
||||
if (result != null) {
|
||||
if (!types[types.length - 1].equals(bridgeTypes[bridgeTypes.length - 1])) {
|
||||
result = result.cast(bridgeTypes[bridgeTypes.length - 1]);
|
||||
}
|
||||
result.returnValue();
|
||||
} else {
|
||||
pe.exit();
|
||||
}
|
||||
|
||||
implementor.addMethod(bridge);
|
||||
}
|
||||
}
|
|
@ -188,7 +188,6 @@ class DependencyGraphBuilder {
|
|||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
System.out.println(new ListingBuilder().buildListing(program, ""));
|
||||
ProgramEmitter pe = ProgramEmitter.create(program);
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
|
@ -207,17 +206,18 @@ class DependencyGraphBuilder {
|
|||
nullInsn.setReceiver(indy.getReceiver());
|
||||
nullInsn.setLocation(indy.getLocation());
|
||||
block.getInstructions().set(j, nullInsn);
|
||||
CallLocation location = new CallLocation(bootstrapMethod, indy.getLocation());
|
||||
dependencyChecker.getDiagnostics().error(location, "Substitutor for this dependency method "
|
||||
+ "was not found");
|
||||
CallLocation location = new CallLocation(caller.getMethod(), currentLocation);
|
||||
dependencyChecker.getDiagnostics().error(location, "Substitutor for bootstrap "
|
||||
+ "method {{m0}} was not found", bootstrapMethod);
|
||||
continue;
|
||||
}
|
||||
|
||||
BasicBlock splitBlock = program.createBasicBlock();
|
||||
List<Instruction> splitInstructions = block.getInstructions().subList(j + 1,
|
||||
block.getInstructions().size());
|
||||
splitBlock.getInstructions().addAll(splitInstructions);
|
||||
List<Instruction> splitInstructionsBackup = new ArrayList<>(splitInstructions);
|
||||
splitInstructions.clear();
|
||||
splitBlock.getInstructions().addAll(splitInstructionsBackup);
|
||||
|
||||
for (int k = 0; k < program.basicBlockCount() - 1; ++k) {
|
||||
BasicBlock replaceBlock = program.basicBlockAt(k);
|
||||
|
@ -240,15 +240,13 @@ class DependencyGraphBuilder {
|
|||
indy.getArguments().stream().map(arg -> pe.var(arg)).collect(Collectors.toList()),
|
||||
indy.getBootstrapMethod(),
|
||||
indy.getBootstrapArguments(),
|
||||
dependencyChecker.getAgent(),
|
||||
indy.getInstance() != null ? nodes[indy.getInstance().getIndex()] : null,
|
||||
indy.getArguments().stream().map(arg -> nodes[arg.getIndex()]).collect(Collectors.toList()));
|
||||
dependencyChecker.getAgent());
|
||||
ValueEmitter result = substitutor.substitute(callSite, pe);
|
||||
if (result.getVariable() != null && result.getVariable() != indy.getReceiver()) {
|
||||
AssignInstruction assign = new AssignInstruction();
|
||||
assign.setAssignee(result.getVariable());
|
||||
assign.setReceiver(indy.getReceiver());
|
||||
pe.addInstruction(insn);
|
||||
pe.addInstruction(assign);
|
||||
}
|
||||
pe.jump(splitBlock);
|
||||
}
|
||||
|
|
|
@ -34,20 +34,15 @@ public class DynamicCallSite {
|
|||
private MethodHandle bootstrapMethod;
|
||||
private List<RuntimeConstant> bootstrapArguments;
|
||||
private DependencyAgent agent;
|
||||
private DependencyNode instanceNode;
|
||||
private List<DependencyNode> argumentNodes = new ArrayList<>();
|
||||
|
||||
DynamicCallSite(MethodDescriptor calledMethod, ValueEmitter instance, List<ValueEmitter> arguments,
|
||||
MethodHandle bootstrapMethod, List<RuntimeConstant> bootstrapArguments, DependencyAgent agent,
|
||||
DependencyNode instanceNode, List<DependencyNode> argumentNodes) {
|
||||
MethodHandle bootstrapMethod, List<RuntimeConstant> bootstrapArguments, DependencyAgent agent) {
|
||||
this.calledMethod = calledMethod;
|
||||
this.instance = instance;
|
||||
this.arguments = Collections.unmodifiableList(new ArrayList<>(arguments));
|
||||
this.bootstrapMethod = bootstrapMethod;
|
||||
this.bootstrapArguments = Collections.unmodifiableList(new ArrayList<>(bootstrapArguments));
|
||||
this.agent = agent;
|
||||
this.instanceNode = instanceNode;
|
||||
this.argumentNodes = Collections.unmodifiableList(new ArrayList<>(argumentNodes));
|
||||
}
|
||||
|
||||
public MethodDescriptor getCalledMethod() {
|
||||
|
@ -73,12 +68,4 @@ public class DynamicCallSite {
|
|||
public ValueEmitter getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public DependencyNode getInstanceNode() {
|
||||
return instanceNode;
|
||||
}
|
||||
|
||||
public List<DependencyNode> getArgumentNodes() {
|
||||
return argumentNodes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public interface ClassReaderSource {
|
|||
default Stream<ClassReader> getAncestors(String name) {
|
||||
return StreamSupport.stream(((Iterable<ClassReader>) () -> {
|
||||
return new Iterator<ClassReader>() {
|
||||
private Deque<Deque<ClassReader>> state = new ArrayDeque<>();
|
||||
Deque<Deque<ClassReader>> state = new ArrayDeque<>();
|
||||
private Set<ClassReader> visited = new HashSet<>();
|
||||
{
|
||||
state.push(new ArrayDeque<>());
|
||||
|
@ -72,7 +72,7 @@ public interface ClassReaderSource {
|
|||
return null;
|
||||
}
|
||||
@Override public boolean hasNext() {
|
||||
return !state.isEmpty();
|
||||
return !this.state.stream().allMatch(e -> e.isEmpty());
|
||||
}
|
||||
private void follow(ClassReader cls) {
|
||||
state.push(new ArrayDeque<>());
|
||||
|
@ -112,4 +112,25 @@ public interface ClassReaderSource {
|
|||
.map(cls -> cls.getMethod(method.getDescriptor()))
|
||||
.filter(candidate -> candidate != null);
|
||||
}
|
||||
|
||||
default boolean isSuperType(String superType, String subType) {
|
||||
if (superType.equals(subType)) {
|
||||
return true;
|
||||
}
|
||||
ClassReader cls = get(subType);
|
||||
if (subType == null) {
|
||||
return false;
|
||||
}
|
||||
if (cls.getParent() != null && !cls.getParent().equals(cls.getName())) {
|
||||
if (isSuperType(superType, cls.getParent())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (String iface : cls.getInterfaces()) {
|
||||
if (isSuperType(superType, iface)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -228,6 +228,9 @@ public class ValueEmitter {
|
|||
}
|
||||
|
||||
public ValueEmitter cast(NumericOperandType from, NumericOperandType to) {
|
||||
if (from == to) {
|
||||
return this;
|
||||
}
|
||||
Variable result = pe.getProgram().createVariable();
|
||||
CastNumberInstruction insn = new CastNumberInstruction(from, to);
|
||||
insn.setValue(variable);
|
||||
|
|
|
@ -1733,31 +1733,31 @@ public class ProgramParser implements VariableDebugInformation {
|
|||
static MethodHandle parseHandle(Handle handle) {
|
||||
switch (handle.getTag()) {
|
||||
case Opcodes.H_GETFIELD:
|
||||
return MethodHandle.fieldGetter(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.fieldGetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_GETSTATIC:
|
||||
return MethodHandle.staticFieldGetter(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.staticFieldGetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_PUTFIELD:
|
||||
return MethodHandle.fieldSetter(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.fieldSetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_PUTSTATIC:
|
||||
return MethodHandle.staticFieldSetter(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.staticFieldSetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_INVOKEVIRTUAL:
|
||||
return MethodHandle.virtualCaller(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.virtualCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_INVOKESTATIC:
|
||||
return MethodHandle.staticCaller(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.staticCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_INVOKESPECIAL:
|
||||
return MethodHandle.specialCaller(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.specialCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_NEWINVOKESPECIAL:
|
||||
return MethodHandle.constructorCaller(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.constructorCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_INVOKEINTERFACE:
|
||||
return MethodHandle.interfaceCaller(handle.getOwner(), handle.getName(),
|
||||
return MethodHandle.interfaceCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown handle tag: " + handle.getTag());
|
||||
|
|
|
@ -127,6 +127,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
methodInjectors.put(methodRef, injector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(MethodReference methodRef, BootstrapMethodSubstitutor substitutor) {
|
||||
dependencyChecker.addBootstrapMethodSubstitutor(methodRef, substitutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(RendererListener listener) {
|
||||
rendererListeners.add(listener);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.vm.spi;
|
||||
|
||||
import java.util.Properties;
|
||||
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.javascript.spi.Generator;
|
||||
import org.teavm.javascript.spi.Injector;
|
||||
|
@ -39,6 +40,8 @@ public interface TeaVMHost {
|
|||
|
||||
void add(MethodReference methodRef, Injector injector);
|
||||
|
||||
void add(MethodReference methodRef, BootstrapMethodSubstitutor substitutor);
|
||||
|
||||
void add(RendererListener listener);
|
||||
|
||||
<T> void registerService(Class<T> type, T instance);
|
||||
|
|
Loading…
Reference in New Issue
Block a user