mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Merge branch 'master' into emit-api
Conflicts: teavm-core/src/main/java/org/teavm/model/emit/ProgramEmitter.java teavm-core/src/main/java/org/teavm/model/emit/ValueEmitter.java
This commit is contained in:
commit
00a751ef13
|
@ -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,261 @@
|
|||
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.MethodHandle;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.PrimitiveType;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @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.var(0, implementor);
|
||||
ValueEmitter[] arguments = new ValueEmitter[instantiatedMethodType.length - 1];
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
arguments[i] = pe.var(i + 1, instantiatedMethodType[i]);
|
||||
}
|
||||
|
||||
ValueType[] implementorSignature = getSignature(implMethod);
|
||||
ValueEmitter[] passedArguments = new ValueEmitter[implementorSignature.length - 1];
|
||||
for (int i = 0; i < capturedVarCount; ++i) {
|
||||
passedArguments[i] = thisVar.getField("_" + 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.getOwnerName(), 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(handle.getName(), handle.getValueType());
|
||||
case GET_STATIC_FIELD:
|
||||
return pe.getField(handle.getClassName(), handle.getName(), handle.getValueType());
|
||||
case PUT_FIELD:
|
||||
instance.setField(handle.getName(), arguments[0].cast(handle.getValueType()));
|
||||
return null;
|
||||
case PUT_STATIC_FIELD:
|
||||
pe.setField(handle.getClassName(), handle.getName(), arguments[0].cast(handle.getValueType()));
|
||||
return null;
|
||||
case INVOKE_VIRTUAL:
|
||||
case INVOKE_INTERFACE:
|
||||
case INVOKE_SPECIAL:
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
arguments[i] = arguments[i].cast(handle.getArgumentType(i));
|
||||
}
|
||||
return instance.invokeVirtual(handle.getName(), handle.getValueType(), arguments);
|
||||
case INVOKE_STATIC:
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
arguments[i] = arguments[i].cast(handle.getArgumentType(i));
|
||||
}
|
||||
return pe.invoke(handle.getClassName(), handle.getName(), handle.getValueType(), arguments);
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
return pe.construct(handle.getClassName(), 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) {
|
||||
return arg.cast(to);
|
||||
} 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(primitiveClass, "valueOf", ValueType.primitive(toType), 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(primitiveName(fromType) + "Value", ValueType.primitive(fromType));
|
||||
return tryConvertArgument(arg, ValueType.primitive(fromType), to);
|
||||
} else {
|
||||
return arg.cast(to);
|
||||
}
|
||||
}
|
||||
|
||||
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.var(0, implementor);
|
||||
thisVar.invokeSpecial(implementor.getParent(), "<init>");
|
||||
|
||||
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.getName(), pe.var(i + 1, types[i]));
|
||||
}
|
||||
|
||||
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.var(0, implementor);
|
||||
ValueEmitter[] arguments = new ValueEmitter[bridgeTypes.length - 1];
|
||||
for (int i = 0; i < arguments.length; ++i) {
|
||||
arguments[i] = pe.var(i + 1, bridgeTypes[i]);
|
||||
}
|
||||
|
||||
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(name, types[types.length - 1], 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);
|
||||
}
|
||||
}
|
|
@ -91,7 +91,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
|
||||
MethodHolder accessor = new MethodHolder(methodDecl.getDescriptor());
|
||||
ProgramEmitter pe = ProgramEmitter.create(accessor);
|
||||
ValueEmitter thisVal = pe.newVar(implementor);
|
||||
ValueEmitter thisVal = pe.var(0, implementor);
|
||||
ValueEmitter result = thisVal.getField(field.getName(), field.getType());
|
||||
if (field.getType() instanceof ValueType.Array) {
|
||||
result = result.cloneArray();
|
||||
|
@ -105,13 +105,14 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
|
||||
MethodHolder ctor = new MethodHolder("<init>", ctorSignature.toArray(new ValueType[ctorSignature.size()]));
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor);
|
||||
ValueEmitter thisVar = pe.newVar(implementor);
|
||||
ValueEmitter thisVar = pe.var(0, implementor);
|
||||
thisVar.invokeSpecial(Object.class, "<init>");
|
||||
int index = 1;
|
||||
for (MethodReader methodDecl : annotation.getMethods()) {
|
||||
if (methodDecl.hasModifier(ElementModifier.STATIC)) {
|
||||
continue;
|
||||
}
|
||||
ValueEmitter param = pe.newVar(methodDecl.getResultType());
|
||||
ValueEmitter param = pe.var(index++, methodDecl.getResultType());
|
||||
thisVar.setField("$" + methodDecl.getName(), param);
|
||||
}
|
||||
pe.exit();
|
||||
|
@ -119,7 +120,6 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
|
||||
MethodHolder annotTypeMethod = new MethodHolder("annotationType", ValueType.parse(Class.class));
|
||||
pe = ProgramEmitter.create(annotTypeMethod);
|
||||
pe.newVar(implementor);
|
||||
pe.constant(ValueType.object(annotationType)).returnValue();
|
||||
implementor.addMethod(annotTypeMethod);
|
||||
|
||||
|
@ -165,7 +165,7 @@ public class AnnotationDependencyListener extends AbstractDependencyListener {
|
|||
MethodHolder ctor = new MethodHolder("<init>", ValueType.VOID);
|
||||
ctor.setLevel(AccessLevel.PUBLIC);
|
||||
ProgramEmitter pe = ProgramEmitter.create(ctor);
|
||||
ValueEmitter thisVar = pe.newVar(cls);
|
||||
ValueEmitter thisVar = pe.var(0, cls);
|
||||
thisVar.invokeSpecial(Object.class, "<init>").exit();
|
||||
|
||||
ClassReader annotatedClass = agent.getClassSource().get(className);
|
||||
|
|
|
@ -122,6 +122,15 @@ public class DiskProgramCache implements ProgramCache {
|
|||
@Override public void visit(InvokeInstruction insn) {
|
||||
dependencies.add(insn.getMethod().getClassName());
|
||||
}
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
for (RuntimeConstant cst : insn.getBootstrapArguments()) {
|
||||
if (cst.getKind() == RuntimeConstant.METHOD_HANDLE) {
|
||||
MethodHandle handle = cst.getMethodHandle();
|
||||
dependencies.add(handle.getClassName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override public void visit(EmptyInstruction insn) { }
|
||||
@Override public void visit(ClassConstantInstruction insn) { }
|
||||
@Override public void visit(NullConstantInstruction insn) { }
|
||||
|
@ -153,15 +162,7 @@ public class DiskProgramCache implements ProgramCache {
|
|||
@Override public void visit(IsInstanceInstruction insn) { }
|
||||
@Override public void visit(InitClassInstruction insn) { }
|
||||
@Override public void visit(NullCheckInstruction insn) { }
|
||||
|
||||
@Override
|
||||
public void visit(MonitorEnterInstruction insn) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitInstruction insn) {
|
||||
|
||||
}
|
||||
@Override public void visit(MonitorEnterInstruction insn) { }
|
||||
@Override public void visit(MonitorExitInstruction insn) { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -562,6 +562,26 @@ public class ProgramIO {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
try {
|
||||
output.writeByte(41);
|
||||
output.writeShort(insn.getReceiver() != null ? insn.getReceiver().getIndex() : -1);
|
||||
output.writeShort(insn.getInstance().getIndex());
|
||||
output.writeInt(symbolTable.lookup(insn.getMethod().toString()));
|
||||
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||
output.writeShort(insn.getArguments().get(i).getIndex());
|
||||
}
|
||||
write(insn.getBootstrapMethod());
|
||||
output.writeByte(insn.getBootstrapArguments().size());
|
||||
for (int i = 0; i < insn.getBootstrapArguments().size(); ++i) {
|
||||
write(insn.getBootstrapArguments().get(i));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOExceptionWrapper(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
try {
|
||||
|
@ -614,6 +634,89 @@ public class ProgramIO {
|
|||
throw new IOExceptionWrapper(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void write(MethodHandle handle) throws IOException {
|
||||
switch (handle.getKind()) {
|
||||
case GET_FIELD:
|
||||
output.writeByte(0);
|
||||
break;
|
||||
case GET_STATIC_FIELD:
|
||||
output.writeByte(1);
|
||||
break;
|
||||
case PUT_FIELD:
|
||||
output.writeByte(2);
|
||||
break;
|
||||
case PUT_STATIC_FIELD:
|
||||
output.writeByte(3);
|
||||
break;
|
||||
case INVOKE_VIRTUAL:
|
||||
output.writeByte(4);
|
||||
break;
|
||||
case INVOKE_STATIC:
|
||||
output.writeByte(5);
|
||||
break;
|
||||
case INVOKE_SPECIAL:
|
||||
output.writeByte(6);
|
||||
break;
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
output.writeByte(7);
|
||||
break;
|
||||
case INVOKE_INTERFACE:
|
||||
output.writeByte(8);
|
||||
break;
|
||||
}
|
||||
output.writeInt(symbolTable.lookup(handle.getClassName()));
|
||||
switch (handle.getKind()) {
|
||||
case GET_FIELD:
|
||||
case GET_STATIC_FIELD:
|
||||
case PUT_FIELD:
|
||||
case PUT_STATIC_FIELD:
|
||||
output.writeInt(symbolTable.lookup(handle.getName()));
|
||||
output.writeInt(symbolTable.lookup(handle.getValueType().toString()));
|
||||
break;
|
||||
default:
|
||||
output.writeInt(symbolTable.lookup(new MethodDescriptor(handle.getName(),
|
||||
handle.signature()).toString()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void write(RuntimeConstant cst) throws IOException {
|
||||
switch (cst.getKind()) {
|
||||
case RuntimeConstant.INT:
|
||||
output.writeByte(0);
|
||||
output.writeInt(cst.getInt());
|
||||
break;
|
||||
case RuntimeConstant.LONG:
|
||||
output.writeByte(1);
|
||||
output.writeLong(cst.getLong());
|
||||
break;
|
||||
case RuntimeConstant.FLOAT:
|
||||
output.writeByte(2);
|
||||
output.writeFloat(cst.getFloat());
|
||||
break;
|
||||
case RuntimeConstant.DOUBLE:
|
||||
output.writeByte(3);
|
||||
output.writeDouble(cst.getDouble());
|
||||
break;
|
||||
case RuntimeConstant.STRING:
|
||||
output.writeByte(4);
|
||||
output.writeUTF(cst.getString());
|
||||
break;
|
||||
case RuntimeConstant.TYPE:
|
||||
output.writeByte(5);
|
||||
output.writeInt(symbolTable.lookup(cst.getValueType().toString()));
|
||||
break;
|
||||
case RuntimeConstant.METHOD:
|
||||
output.writeByte(6);
|
||||
output.writeInt(symbolTable.lookup(ValueType.methodTypeToString(cst.getMethodType())));
|
||||
break;
|
||||
case RuntimeConstant.METHOD_HANDLE:
|
||||
output.writeByte(7);
|
||||
write(cst.getMethodHandle());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class IOExceptionWrapper extends RuntimeException {
|
||||
|
@ -928,8 +1031,97 @@ public class ProgramIO {
|
|||
insn.setObjectRef(program.variableAt(input.readShort()));
|
||||
return insn;
|
||||
}
|
||||
case 41: {
|
||||
/*
|
||||
output.writeByte(41);
|
||||
output.writeShort(insn.getReceiver() != null ? insn.getReceiver().getIndex() : -1);
|
||||
output.writeShort(insn.getInstance().getIndex());
|
||||
output.writeInt(symbolTable.lookup(insn.getMethod().toString()));
|
||||
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||
output.writeShort(insn.getArguments().get(i).getIndex());
|
||||
}
|
||||
write(insn.getBootstrapMethod());
|
||||
output.writeByte(insn.getBootstrapArguments().size());
|
||||
for (int i = 0; i < insn.getBootstrapArguments().size(); ++i) {
|
||||
write(insn.getBootstrapArguments().get(i));
|
||||
*/
|
||||
InvokeDynamicInstruction insn = new InvokeDynamicInstruction();
|
||||
short receiver = input.readShort();
|
||||
insn.setReceiver(receiver >= 0 ? program.variableAt(receiver) : null);
|
||||
insn.setInstance(program.variableAt(input.readShort()));
|
||||
insn.setMethod(MethodDescriptor.parse(symbolTable.at(input.readInt())));
|
||||
int argsCount = insn.getMethod().parameterCount();
|
||||
for (int i = 0; i < argsCount; ++i) {
|
||||
insn.getArguments().add(program.variableAt(input.readShort()));
|
||||
}
|
||||
insn.setBootstrapMethod(readMethodHandle(input));
|
||||
int bootstrapArgsCount = input.readByte();
|
||||
for (int i = 0; i < bootstrapArgsCount; ++i) {
|
||||
insn.getBootstrapArguments().add(readRuntimeConstant(input));
|
||||
}
|
||||
return insn;
|
||||
}
|
||||
default:
|
||||
throw new RuntimeException("Unknown instruction type: " + insnType);
|
||||
}
|
||||
}
|
||||
|
||||
private MethodHandle readMethodHandle(DataInput input) throws IOException {
|
||||
byte kind = input.readByte();
|
||||
switch (kind) {
|
||||
case 0:
|
||||
return MethodHandle.fieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
case 1:
|
||||
return MethodHandle.staticFieldGetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
case 2:
|
||||
return MethodHandle.fieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
case 3:
|
||||
return MethodHandle.staticFieldSetter(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
ValueType.parse(symbolTable.at(input.readInt())));
|
||||
case 4:
|
||||
return MethodHandle.virtualCaller(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
case 5:
|
||||
return MethodHandle.staticCaller(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
case 6:
|
||||
return MethodHandle.specialCaller(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
case 7:
|
||||
return MethodHandle.constructorCaller(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
case 8:
|
||||
return MethodHandle.interfaceCaller(symbolTable.at(input.readInt()), symbolTable.at(input.readInt()),
|
||||
MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected method handle type: " + kind);
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeConstant readRuntimeConstant(DataInput input) throws IOException {
|
||||
byte kind = input.readByte();
|
||||
switch (kind) {
|
||||
case 0:
|
||||
return new RuntimeConstant(input.readInt());
|
||||
case 1:
|
||||
return new RuntimeConstant(input.readLong());
|
||||
case 2:
|
||||
return new RuntimeConstant(input.readFloat());
|
||||
case 3:
|
||||
return new RuntimeConstant(input.readDouble());
|
||||
case 4:
|
||||
return new RuntimeConstant(input.readUTF());
|
||||
case 5:
|
||||
return new RuntimeConstant(ValueType.parse(symbolTable.at(input.readInt())));
|
||||
case 6:
|
||||
return new RuntimeConstant(MethodDescriptor.parseSignature(symbolTable.at(input.readInt())));
|
||||
case 7:
|
||||
return new RuntimeConstant(readMethodHandle(input));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected runtime constant type: " + kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.dependency;
|
||||
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface BootstrapMethodSubstitutor {
|
||||
ValueEmitter substitute(DynamicCallSite callSite, ProgramEmitter pe);
|
||||
}
|
|
@ -307,6 +307,13 @@ public class DataFlowGraphBuilder implements InstructionReader {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
// Should be eliminated by bootstrap method substitutor
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
}
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
*/
|
||||
package org.teavm.dependency;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import org.teavm.callgraph.CallGraph;
|
||||
import org.teavm.callgraph.DefaultCallGraph;
|
||||
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||
|
@ -29,10 +38,12 @@ import org.teavm.model.ClassHolder;
|
|||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.InstructionLocation;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
@ -47,8 +58,8 @@ public class DependencyChecker implements DependencyInfo {
|
|||
private int classNameSuffix;
|
||||
private DependencyClassSource classSource;
|
||||
private ClassLoader classLoader;
|
||||
private Mapper<MethodReference, MethodReader> methodReaderCache;
|
||||
private Mapper<FieldReference, FieldReader> fieldReaderCache;
|
||||
private Mapper<MethodReference, MethodHolder> methodReaderCache;
|
||||
private Mapper<FieldReference, FieldHolder> fieldReaderCache;
|
||||
private CachedMapper<MethodReference, MethodDependency> methodCache;
|
||||
private CachedMapper<FieldReference, FieldDependency> fieldCache;
|
||||
private CachedMapper<String, ClassDependency> classCache;
|
||||
|
@ -64,6 +75,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
private DependencyAgent agent;
|
||||
List<DependencyNode> nodes = new ArrayList<>();
|
||||
List<BitSet> typeBitSets = new ArrayList<>();
|
||||
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
||||
|
||||
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||
Diagnostics diagnostics) {
|
||||
|
@ -71,10 +83,10 @@ public class DependencyChecker implements DependencyInfo {
|
|||
this.classSource = new DependencyClassSource(classSource, diagnostics);
|
||||
this.classLoader = classLoader;
|
||||
this.services = services;
|
||||
methodReaderCache = new CachedMapper<>(preimage -> findMethodReader(preimage));
|
||||
fieldReaderCache = new CachedMapper<>(preimage -> findFieldReader(preimage));
|
||||
methodReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage));
|
||||
fieldReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage));
|
||||
methodCache = new CachedMapper<>(preimage -> {
|
||||
MethodReader method = methodReaderCache.map(preimage);
|
||||
MethodHolder method = methodReaderCache.map(preimage);
|
||||
if (method != null && !method.getReference().equals(preimage)) {
|
||||
return methodCache.map(method.getReference());
|
||||
}
|
||||
|
@ -268,53 +280,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
}
|
||||
}
|
||||
|
||||
private MethodReader findMethodReader(MethodReference methodRef) {
|
||||
String clsName = methodRef.getClassName();
|
||||
MethodDescriptor desc = methodRef.getDescriptor();
|
||||
ClassReader cls = classSource.get(clsName);
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
MethodReader reader = cls.getMethod(desc);
|
||||
if (reader != null) {
|
||||
return reader;
|
||||
}
|
||||
if (cls.getParent() != null && cls.getParent().equals(cls.getParent())) {
|
||||
reader = methodReaderCache.map(new MethodReference(cls.getParent(), desc));
|
||||
if (reader != null) {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
for (String ifaceName : cls.getInterfaces()) {
|
||||
reader = methodReaderCache.map(new MethodReference(ifaceName, desc));
|
||||
if (reader != null) {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private FieldReader findFieldReader(FieldReference fieldRef) {
|
||||
String clsName = fieldRef.getClassName();
|
||||
String name = fieldRef.getFieldName();
|
||||
while (clsName != null) {
|
||||
ClassReader cls = classSource.get(clsName);
|
||||
if (cls == null) {
|
||||
return null;
|
||||
}
|
||||
FieldReader field = cls.getField(name);
|
||||
if (field != null) {
|
||||
return field;
|
||||
}
|
||||
if (clsName.equals(cls.getParent())) {
|
||||
break;
|
||||
}
|
||||
clsName = cls.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MethodDependency createMethodDep(MethodReference methodRef, MethodReader method) {
|
||||
private MethodDependency createMethodDep(MethodReference methodRef, MethodHolder method) {
|
||||
ValueType[] arguments = methodRef.getParameterTypes();
|
||||
int paramCount = arguments.length + 1;
|
||||
DependencyNode[] parameterNodes = new DependencyNode[arguments.length + 1];
|
||||
|
@ -482,4 +448,8 @@ public class DependencyChecker implements DependencyInfo {
|
|||
public CallGraph getCallGraph() {
|
||||
return callGraph;
|
||||
}
|
||||
|
||||
public void addBootstrapMethodSubstitutor(MethodReference method, BootstrapMethodSubstitutor substitutor) {
|
||||
bootstrapMethodSubstitutors.put(method, substitutor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ import java.util.Collection;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.teavm.common.CachedMapper;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
|
@ -32,12 +32,12 @@ import org.teavm.model.util.ModelUtils;
|
|||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
class DependencyClassSource implements ClassReaderSource {
|
||||
class DependencyClassSource implements ClassHolderSource {
|
||||
private ClassReaderSource innerSource;
|
||||
private Diagnostics diagnostics;
|
||||
private Map<String, ClassHolder> generatedClasses = new HashMap<>();
|
||||
private List<ClassHolderTransformer> transformers = new ArrayList<>();
|
||||
private CachedMapper<String, ClassReader> cache = new CachedMapper<>(preimage -> findAndTransformClass(preimage));
|
||||
private Map<String, ClassHolder> cache = new HashMap<>();
|
||||
|
||||
public DependencyClassSource(ClassReaderSource innerSource, Diagnostics diagnostics) {
|
||||
this.innerSource = innerSource;
|
||||
|
@ -45,8 +45,8 @@ class DependencyClassSource implements ClassReaderSource {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ClassReader get(String name) {
|
||||
return cache.map(name);
|
||||
public ClassHolder get(String name) {
|
||||
return cache.computeIfAbsent(name, n -> findAndTransformClass(n));
|
||||
}
|
||||
|
||||
public void submit(ClassHolder cls) {
|
||||
|
@ -54,10 +54,10 @@ class DependencyClassSource implements ClassReaderSource {
|
|||
throw new IllegalArgumentException("Class " + cls.getName() + " is already defined");
|
||||
}
|
||||
generatedClasses.put(cls.getName(), cls);
|
||||
cache.invalidate(cls.getName());
|
||||
cache.remove(cls.getName());
|
||||
}
|
||||
|
||||
private ClassReader findAndTransformClass(String name) {
|
||||
private ClassHolder findAndTransformClass(String name) {
|
||||
ClassHolder cls = findClass(name);
|
||||
if (cls != null && !transformers.isEmpty()) {
|
||||
for (ClassHolderTransformer transformer : transformers) {
|
||||
|
|
|
@ -21,8 +21,43 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.callgraph.DefaultCallGraphNode;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.CallLocation;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.IncomingReader;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.InstructionLocation;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHandle;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.PhiReader;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.RuntimeConstant;
|
||||
import org.teavm.model.TryCatchBlockReader;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.emit.ProgramEmitter;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||
import org.teavm.model.instructions.BinaryOperation;
|
||||
import org.teavm.model.instructions.BranchingCondition;
|
||||
import org.teavm.model.instructions.CastIntegerDirection;
|
||||
import org.teavm.model.instructions.InstructionReader;
|
||||
import org.teavm.model.instructions.IntegerSubtype;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.NullConstantInstruction;
|
||||
import org.teavm.model.instructions.NumericOperandType;
|
||||
import org.teavm.model.instructions.SwitchTableEntryReader;
|
||||
import org.teavm.model.util.ListingBuilder;
|
||||
|
||||
/**
|
||||
|
@ -33,7 +68,7 @@ class DependencyGraphBuilder {
|
|||
private DependencyChecker dependencyChecker;
|
||||
private DependencyNode[] nodes;
|
||||
private DependencyNode resultNode;
|
||||
private ProgramReader program;
|
||||
private Program program;
|
||||
private DefaultCallGraphNode caller;
|
||||
private InstructionLocation currentLocation;
|
||||
private ExceptionConsumer currentExceptionConsumer;
|
||||
|
@ -44,13 +79,15 @@ class DependencyGraphBuilder {
|
|||
|
||||
public void buildGraph(MethodDependency dep) {
|
||||
caller = dependencyChecker.callGraph.getNode(dep.getReference());
|
||||
MethodReader method = dep.getMethod();
|
||||
MethodHolder method = dep.method;
|
||||
if (method.getProgram() == null || method.getProgram().basicBlockCount() == 0) {
|
||||
return;
|
||||
}
|
||||
program = method.getProgram();
|
||||
resultNode = dep.getResult();
|
||||
|
||||
processInvokeDynamic(dep);
|
||||
|
||||
DataFlowGraphBuilder dfgBuilder = new DataFlowGraphBuilder();
|
||||
boolean[] significantParams = new boolean[dep.getParameterCount()];
|
||||
significantParams[0] = true;
|
||||
|
@ -146,6 +183,77 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
private void processInvokeDynamic(MethodDependency methodDep) {
|
||||
if (program == null) {
|
||||
return;
|
||||
}
|
||||
ProgramEmitter pe = ProgramEmitter.create(program);
|
||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||
BasicBlock block = program.basicBlockAt(i);
|
||||
for (int j = 0; j < block.getInstructions().size(); ++j) {
|
||||
Instruction insn = block.getInstructions().get(j);
|
||||
if (!(insn instanceof InvokeDynamicInstruction)) {
|
||||
continue;
|
||||
}
|
||||
InvokeDynamicInstruction indy = (InvokeDynamicInstruction) insn;
|
||||
MethodReference bootstrapMethod = new MethodReference(indy.getBootstrapMethod().getClassName(),
|
||||
indy.getBootstrapMethod().getName(), indy.getBootstrapMethod().signature());
|
||||
BootstrapMethodSubstitutor substitutor = dependencyChecker.bootstrapMethodSubstitutors
|
||||
.get(bootstrapMethod);
|
||||
if (substitutor == null) {
|
||||
NullConstantInstruction nullInsn = new NullConstantInstruction();
|
||||
nullInsn.setReceiver(indy.getReceiver());
|
||||
nullInsn.setLocation(indy.getLocation());
|
||||
block.getInstructions().set(j, nullInsn);
|
||||
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());
|
||||
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);
|
||||
for (Phi phi : replaceBlock.getPhis()) {
|
||||
for (Incoming incoming : phi.getIncomings()) {
|
||||
if (incoming.getSource() == block) {
|
||||
incoming.setSource(splitBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pe.setBlock(block);
|
||||
pe.setCurrentLocation(indy.getLocation());
|
||||
block.getInstructions().remove(j);
|
||||
|
||||
List<ValueEmitter> arguments = new ArrayList<>();
|
||||
for (int k = 0; k < indy.getArguments().size(); ++k) {
|
||||
arguments.add(pe.var(indy.getArguments().get(k), indy.getMethod().parameterType(k)));
|
||||
}
|
||||
DynamicCallSite callSite = new DynamicCallSite(indy.getMethod(),
|
||||
indy.getInstance() != null ? pe.var(indy.getInstance(),
|
||||
ValueType.object(methodDep.getMethod().getOwnerName())) : null,
|
||||
arguments, indy.getBootstrapMethod(), indy.getBootstrapArguments(),
|
||||
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(assign);
|
||||
}
|
||||
pe.jump(splitBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ExceptionConsumer createExceptionConsumer(MethodDependency methodDep, BasicBlockReader block) {
|
||||
List<? extends TryCatchBlockReader> tryCatchBlocks = block.readTryCatchBlocks();
|
||||
ClassReader[] exceptions = new ClassReader[tryCatchBlocks.size()];
|
||||
|
@ -613,6 +721,13 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
// Should be eliminated by processInvokeDynamic method
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initClass(final String className) {
|
||||
CallLocation callLocation = new CallLocation(caller.getMethod(), currentLocation);
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.dependency;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHandle;
|
||||
import org.teavm.model.RuntimeConstant;
|
||||
import org.teavm.model.emit.ValueEmitter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class DynamicCallSite {
|
||||
private MethodDescriptor calledMethod;
|
||||
private ValueEmitter instance;
|
||||
private List<ValueEmitter> arguments;
|
||||
private MethodHandle bootstrapMethod;
|
||||
private List<RuntimeConstant> bootstrapArguments;
|
||||
private DependencyAgent agent;
|
||||
|
||||
DynamicCallSite(MethodDescriptor calledMethod, ValueEmitter instance, List<ValueEmitter> arguments,
|
||||
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;
|
||||
}
|
||||
|
||||
public MethodDescriptor getCalledMethod() {
|
||||
return calledMethod;
|
||||
}
|
||||
|
||||
public MethodHandle getBootstrapMethod() {
|
||||
return bootstrapMethod;
|
||||
}
|
||||
|
||||
public List<ValueEmitter> getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public List<RuntimeConstant> getBootstrapArguments() {
|
||||
return bootstrapArguments;
|
||||
}
|
||||
|
||||
public DependencyAgent getAgent() {
|
||||
return agent;
|
||||
}
|
||||
|
||||
public ValueEmitter getInstance() {
|
||||
return instance;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.dependency;
|
||||
|
||||
import java.util.Arrays;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
|
@ -29,14 +30,14 @@ public class MethodDependency implements MethodDependencyInfo {
|
|||
private int parameterCount;
|
||||
private DependencyNode resultNode;
|
||||
private DependencyNode thrown;
|
||||
private MethodReader method;
|
||||
MethodHolder method;
|
||||
private MethodReference reference;
|
||||
private boolean used;
|
||||
DependencyPlugin dependencyPlugin;
|
||||
boolean dependencyPluginAttached;
|
||||
|
||||
MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount,
|
||||
DependencyNode resultNode, DependencyNode thrown, MethodReader method, MethodReference reference) {
|
||||
DependencyNode resultNode, DependencyNode thrown, MethodHolder method, MethodReference reference) {
|
||||
this.dependencyChecker = dependencyChecker;
|
||||
this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length);
|
||||
this.parameterCount = parameterCount;
|
||||
|
|
|
@ -563,6 +563,11 @@ class StatementGenerator implements InstructionVisitor {
|
|||
statements.add(stmt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
// InvokeDynamic should be eliminated at previous phases
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
assign(Expr.instanceOf(Expr.var(insn.getValue().getIndex()), insn.getType()), insn.getReceiver());
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
|
@ -22,4 +24,24 @@ package org.teavm.model;
|
|||
public interface ClassHolderSource extends ClassReaderSource {
|
||||
@Override
|
||||
ClassHolder get(String name);
|
||||
|
||||
default Stream<ClassHolder> getMutableAncestors(String name) {
|
||||
return getAncestors(name).map(cls -> (ClassHolder) cls);
|
||||
}
|
||||
|
||||
default Stream<ClassHolder> getMutableAncestorClasses(String name) {
|
||||
return getAncestorClasses(name).map(cls -> (ClassHolder) cls);
|
||||
}
|
||||
|
||||
default MethodHolder resolveMutable(MethodReference method) {
|
||||
return (MethodHolder) resolve(method);
|
||||
}
|
||||
|
||||
default FieldHolder resolveMutable(FieldReference field) {
|
||||
return (FieldHolder) resolve(field);
|
||||
}
|
||||
|
||||
default Stream<MethodHolder> mutableOverridenMethods(MethodReference method) {
|
||||
return overridenMethods(method).map(m -> (MethodHolder) m);
|
||||
}
|
||||
}
|
|
@ -15,10 +15,122 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public interface ClassReaderSource {
|
||||
ClassReader get(String name);
|
||||
|
||||
default Stream<ClassReader> getAncestorClasses(String name) {
|
||||
return StreamSupport.stream(((Iterable<ClassReader>) () -> {
|
||||
return new Iterator<ClassReader>() {
|
||||
ClassReader currentClass = get(name);
|
||||
@Override public ClassReader next() {
|
||||
ClassReader result = currentClass;
|
||||
if (currentClass.getParent() != null && !currentClass.getName().equals(currentClass.getParent())) {
|
||||
currentClass = get(currentClass.getParent());
|
||||
} else {
|
||||
currentClass = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@Override public boolean hasNext() {
|
||||
return currentClass != null;
|
||||
}
|
||||
};
|
||||
}).spliterator(), false);
|
||||
}
|
||||
|
||||
default Stream<ClassReader> getAncestors(String name) {
|
||||
return StreamSupport.stream(((Iterable<ClassReader>) () -> {
|
||||
return new Iterator<ClassReader>() {
|
||||
Deque<Deque<ClassReader>> state = new ArrayDeque<>();
|
||||
private Set<ClassReader> visited = new HashSet<>();
|
||||
{
|
||||
state.push(new ArrayDeque<>());
|
||||
add(name);
|
||||
}
|
||||
@Override public ClassReader next() {
|
||||
while (!state.isEmpty()) {
|
||||
Deque<ClassReader> level = state.peek();
|
||||
if (!level.isEmpty()) {
|
||||
ClassReader result = level.removeFirst();
|
||||
follow(result);
|
||||
return result;
|
||||
}
|
||||
state.pop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override public boolean hasNext() {
|
||||
return !this.state.stream().allMatch(e -> e.isEmpty());
|
||||
}
|
||||
private void follow(ClassReader cls) {
|
||||
state.push(new ArrayDeque<>());
|
||||
if (cls.getParent() != null) {
|
||||
add(cls.getParent());
|
||||
}
|
||||
for (String iface : cls.getInterfaces()) {
|
||||
add(iface);
|
||||
}
|
||||
}
|
||||
private void add(String name) {
|
||||
ClassReader cls = get(name);
|
||||
if (cls != null && visited.add(cls)) {
|
||||
state.peek().addLast(cls);
|
||||
}
|
||||
}
|
||||
};
|
||||
}).spliterator(), false);
|
||||
}
|
||||
|
||||
default MethodReader resolve(MethodReference method) {
|
||||
return getAncestors(method.getClassName())
|
||||
.map(cls -> cls.getMethod(method.getDescriptor()))
|
||||
.filter(candidate -> candidate != null)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
default FieldReader resolve(FieldReference field) {
|
||||
return getAncestors(field.getClassName())
|
||||
.map(cls -> cls.getField(field.getFieldName()))
|
||||
.filter(candidate -> candidate != null)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
default Stream<MethodReader> overridenMethods(MethodReference method) {
|
||||
return getAncestorClasses(method.getClassName())
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,6 +187,13 @@ class InstructionReadVisitor implements InstructionVisitor {
|
|||
Collections.unmodifiableList(insn.getArguments()), insn.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
reader.invokeDynamic(insn.getReceiver(), insn.getInstance(), insn.getMethod(),
|
||||
Collections.unmodifiableList(insn.getArguments()), insn.getBootstrapMethod(),
|
||||
Collections.unmodifiableList(insn.getBootstrapArguments()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
reader.isInstance(insn.getReceiver(), insn.getValue(), insn.getType());
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.teavm.model.instructions.InstructionVisitor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class InvokeDynamicInstruction extends Instruction {
|
||||
private MethodDescriptor method;
|
||||
private MethodHandle bootstrapMethod;
|
||||
private List<RuntimeConstant> bootstrapArguments = new ArrayList<>();
|
||||
private Variable instance;
|
||||
private List<Variable> arguments = new ArrayList<>();
|
||||
private Variable receiver;
|
||||
|
||||
public MethodDescriptor getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(MethodDescriptor method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public MethodHandle getBootstrapMethod() {
|
||||
return bootstrapMethod;
|
||||
}
|
||||
|
||||
public void setBootstrapMethod(MethodHandle bootstrapMethod) {
|
||||
this.bootstrapMethod = bootstrapMethod;
|
||||
}
|
||||
|
||||
public Variable getReceiver() {
|
||||
return receiver;
|
||||
}
|
||||
|
||||
public void setReceiver(Variable receiver) {
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
public List<RuntimeConstant> getBootstrapArguments() {
|
||||
return bootstrapArguments;
|
||||
}
|
||||
|
||||
public Variable getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void setInstance(Variable instance) {
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public List<Variable> getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptVisitor(InstructionVisitor visitor) {
|
||||
visitor.visit(this);
|
||||
}
|
||||
}
|
174
teavm-core/src/main/java/org/teavm/model/MethodHandle.java
Normal file
174
teavm-core/src/main/java/org/teavm/model/MethodHandle.java
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class MethodHandle {
|
||||
private MethodHandleType kind;
|
||||
private String className;
|
||||
private String name;
|
||||
private ValueType valueType;
|
||||
private ValueType[] argumentTypes;
|
||||
|
||||
MethodHandle(MethodHandleType kind, String className, String name, ValueType valueType,
|
||||
ValueType[] argumentTypes) {
|
||||
this.kind = kind;
|
||||
this.className = className;
|
||||
this.name = name;
|
||||
this.valueType = valueType;
|
||||
this.argumentTypes = argumentTypes;
|
||||
}
|
||||
|
||||
public MethodHandleType getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public ValueType getValueType() {
|
||||
return valueType;
|
||||
}
|
||||
|
||||
public ValueType[] getArgumentTypes() {
|
||||
return argumentTypes != null ? argumentTypes.clone() : null;
|
||||
}
|
||||
|
||||
public int getArgumentCount() {
|
||||
return argumentTypes != null ? argumentTypes.length : 0;
|
||||
}
|
||||
|
||||
public ValueType getArgumentType(int index) {
|
||||
if (argumentTypes == null) {
|
||||
throw new IllegalArgumentException("Can't get argument of non-parameterized method handle");
|
||||
}
|
||||
return argumentTypes[index];
|
||||
}
|
||||
|
||||
public ValueType[] signature() {
|
||||
ValueType[] result = Arrays.copyOf(argumentTypes, argumentTypes.length + 1);
|
||||
result[argumentTypes.length] = valueType;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static MethodHandle fieldGetter(String className, String name, ValueType valueType) {
|
||||
if (valueType == ValueType.VOID) {
|
||||
throw new IllegalArgumentException("Field can't be of void type");
|
||||
}
|
||||
return new MethodHandle(MethodHandleType.GET_FIELD, className, name, valueType, null);
|
||||
}
|
||||
|
||||
public static MethodHandle staticFieldGetter(String className, String name, ValueType valueType) {
|
||||
if (valueType == ValueType.VOID) {
|
||||
throw new IllegalArgumentException("Field can't be of void type");
|
||||
}
|
||||
return new MethodHandle(MethodHandleType.GET_STATIC_FIELD, className, name, valueType, null);
|
||||
}
|
||||
|
||||
public static MethodHandle fieldSetter(String className, String name, ValueType valueType) {
|
||||
if (valueType == ValueType.VOID) {
|
||||
throw new IllegalArgumentException("Field can't be of void type");
|
||||
}
|
||||
return new MethodHandle(MethodHandleType.PUT_FIELD, className, name, valueType, null);
|
||||
}
|
||||
|
||||
public static MethodHandle staticFieldSetter(String className, String name, ValueType valueType) {
|
||||
if (valueType == ValueType.VOID) {
|
||||
throw new IllegalArgumentException("Field can't be of void type");
|
||||
}
|
||||
return new MethodHandle(MethodHandleType.PUT_STATIC_FIELD, className, name, valueType, null);
|
||||
}
|
||||
|
||||
public static MethodHandle virtualCaller(String className, String name, ValueType... arguments) {
|
||||
ValueType valueType = arguments[0];
|
||||
arguments = Arrays.copyOfRange(arguments, 1, arguments.length);
|
||||
return new MethodHandle(MethodHandleType.INVOKE_VIRTUAL, className, name, valueType, arguments);
|
||||
}
|
||||
|
||||
public static MethodHandle virtualCaller(String className, MethodDescriptor desc) {
|
||||
return virtualCaller(className, desc.getName(), desc.getSignature());
|
||||
}
|
||||
|
||||
public static MethodHandle virtualCaller(MethodReference method) {
|
||||
return virtualCaller(method.getClassName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
public static MethodHandle staticCaller(String className, String name, ValueType... arguments) {
|
||||
ValueType valueType = arguments[arguments.length - 1];
|
||||
arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1);
|
||||
return new MethodHandle(MethodHandleType.INVOKE_STATIC, className, name, valueType, arguments);
|
||||
}
|
||||
|
||||
public static MethodHandle staticCaller(String className, MethodDescriptor desc) {
|
||||
return staticCaller(className, desc.getName(), desc.getSignature());
|
||||
}
|
||||
|
||||
public static MethodHandle staticCaller(MethodReference method) {
|
||||
return staticCaller(method.getClassName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
public static MethodHandle specialCaller(String className, String name, ValueType... arguments) {
|
||||
ValueType valueType = arguments[arguments.length - 1];
|
||||
arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1);
|
||||
return new MethodHandle(MethodHandleType.INVOKE_SPECIAL, className, name, valueType, arguments);
|
||||
}
|
||||
|
||||
public static MethodHandle specialCaller(String className, MethodDescriptor desc) {
|
||||
return specialCaller(className, desc.getName(), desc.getSignature());
|
||||
}
|
||||
|
||||
public static MethodHandle specialCaller(MethodReference method) {
|
||||
return specialCaller(method.getClassName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
public static MethodHandle constructorCaller(String className, String name, ValueType... arguments) {
|
||||
ValueType valueType = arguments[arguments.length - 1];
|
||||
arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1);
|
||||
return new MethodHandle(MethodHandleType.INVOKE_CONSTRUCTOR, className, name, valueType, arguments);
|
||||
}
|
||||
|
||||
public static MethodHandle constructorCaller(String className, MethodDescriptor desc) {
|
||||
return constructorCaller(className, desc.getName(), desc.getSignature());
|
||||
}
|
||||
|
||||
public static MethodHandle constructorCaller(MethodReference method) {
|
||||
return constructorCaller(method.getClassName(), method.getDescriptor());
|
||||
}
|
||||
|
||||
public static MethodHandle interfaceCaller(String className, String name, ValueType... arguments) {
|
||||
ValueType valueType = arguments[arguments.length - 1];
|
||||
arguments = Arrays.copyOfRange(arguments, 0, arguments.length - 1);
|
||||
return new MethodHandle(MethodHandleType.INVOKE_INTERFACE, className, name, valueType, arguments);
|
||||
}
|
||||
|
||||
public static MethodHandle interfaceCaller(String className, MethodDescriptor desc) {
|
||||
return interfaceCaller(className, desc.getName(), desc.getSignature());
|
||||
}
|
||||
|
||||
public static MethodHandle interfaceCaller(MethodReference method) {
|
||||
return interfaceCaller(method.getClassName(), method.getDescriptor());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public enum MethodHandleType {
|
||||
GET_FIELD,
|
||||
GET_STATIC_FIELD,
|
||||
PUT_FIELD,
|
||||
PUT_STATIC_FIELD,
|
||||
INVOKE_VIRTUAL,
|
||||
INVOKE_STATIC,
|
||||
INVOKE_SPECIAL,
|
||||
INVOKE_CONSTRUCTOR,
|
||||
INVOKE_INTERFACE
|
||||
}
|
108
teavm-core/src/main/java/org/teavm/model/RuntimeConstant.java
Normal file
108
teavm-core/src/main/java/org/teavm/model/RuntimeConstant.java
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2015 Alexey Andreev.
|
||||
*
|
||||
* 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.model;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public final class RuntimeConstant {
|
||||
public static final byte INT = 0;
|
||||
public static final byte LONG = 1;
|
||||
public static final byte FLOAT = 2;
|
||||
public static final byte DOUBLE = 3;
|
||||
public static final byte STRING = 4;
|
||||
public static final byte TYPE = 5;
|
||||
public static final byte METHOD = 6;
|
||||
public static final byte METHOD_HANDLE = 7;
|
||||
|
||||
private byte kind;
|
||||
private Object value;
|
||||
|
||||
RuntimeConstant(byte kind, Object value) {
|
||||
super();
|
||||
this.kind = kind;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public RuntimeConstant(int value) {
|
||||
this(INT, value);
|
||||
}
|
||||
|
||||
public RuntimeConstant(long value) {
|
||||
this(LONG, value);
|
||||
}
|
||||
|
||||
public RuntimeConstant(float value) {
|
||||
this(FLOAT, value);
|
||||
}
|
||||
|
||||
public RuntimeConstant(double value) {
|
||||
this(FLOAT, value);
|
||||
}
|
||||
|
||||
public RuntimeConstant(String value) {
|
||||
this(STRING, value);
|
||||
}
|
||||
|
||||
public RuntimeConstant(ValueType value) {
|
||||
this(TYPE, value);
|
||||
}
|
||||
|
||||
public RuntimeConstant(ValueType[] methodType) {
|
||||
this(METHOD, methodType.clone());
|
||||
}
|
||||
|
||||
public RuntimeConstant(MethodHandle value) {
|
||||
this(METHOD_HANDLE, value);
|
||||
}
|
||||
|
||||
public byte getKind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
public int getInt() {
|
||||
return (Integer) value;
|
||||
}
|
||||
|
||||
public long getLong() {
|
||||
return (Long) value;
|
||||
}
|
||||
|
||||
public float getFloat() {
|
||||
return (Float) value;
|
||||
}
|
||||
|
||||
public double getDouble() {
|
||||
return (Double) value;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return (String) value;
|
||||
}
|
||||
|
||||
public ValueType getValueType() {
|
||||
return (ValueType) value;
|
||||
}
|
||||
|
||||
public ValueType[] getMethodType() {
|
||||
return ((ValueType[]) value).clone();
|
||||
}
|
||||
|
||||
public MethodHandle getMethodHandle() {
|
||||
return (MethodHandle) value;
|
||||
}
|
||||
}
|
|
@ -15,10 +15,8 @@
|
|||
*/
|
||||
package org.teavm.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -260,6 +258,11 @@ public abstract class ValueType {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String methodTypeToString(ValueType[] types) {
|
||||
return "(" + Arrays.stream(types, 0, types.length - 1).map(ValueType::toString)
|
||||
.collect(Collectors.joining()) + ")" + types[types.length - 1];
|
||||
}
|
||||
|
||||
public abstract boolean isObject(String cls);
|
||||
|
||||
private static ValueType parseImpl(String string) {
|
||||
|
|
|
@ -271,6 +271,22 @@ public final class ProgramEmitter {
|
|||
return var(var, ValueType.parse(type));
|
||||
}
|
||||
|
||||
public ValueEmitter var(Variable var, ClassReader type) {
|
||||
return var(var, ValueType.object(type.getName()));
|
||||
}
|
||||
|
||||
public ValueEmitter var(int var, ValueType type) {
|
||||
return new ValueEmitter(this, block, program.variableAt(var), type);
|
||||
}
|
||||
|
||||
public ValueEmitter var(int var, Class<?> type) {
|
||||
return var(var, ValueType.parse(type));
|
||||
}
|
||||
|
||||
public ValueEmitter var(int var, ClassReader type) {
|
||||
return var(var, ValueType.object(type.getName()));
|
||||
}
|
||||
|
||||
public ValueEmitter newVar(ValueType type) {
|
||||
return var(program.createVariable(), type);
|
||||
}
|
||||
|
@ -308,6 +324,11 @@ public final class ProgramEmitter {
|
|||
insn.setTarget(block);
|
||||
zeroBlock.getInstructions().add(insn);
|
||||
|
||||
program.createVariable();
|
||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||
program.createVariable();
|
||||
}
|
||||
|
||||
return new ProgramEmitter(program, block);
|
||||
}
|
||||
|
||||
|
@ -348,4 +369,8 @@ public final class ProgramEmitter {
|
|||
insn.setCondition(value.getVariable());
|
||||
return new ChooseEmitter(this, insn, program.createBasicBlock());
|
||||
}
|
||||
|
||||
public static ProgramEmitter create(Program program) {
|
||||
return new ProgramEmitter(program, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,11 +83,11 @@ public class ValueEmitter {
|
|||
}
|
||||
|
||||
public ValueEmitter getField(String name, ValueType type) {
|
||||
if (!(type instanceof ValueType.Object)) {
|
||||
if (!(this.type instanceof ValueType.Object)) {
|
||||
throw new IllegalStateException("Can't get field of non-object type: " + type);
|
||||
}
|
||||
|
||||
String className = ((ValueType.Object) type).getClassName();
|
||||
String className = ((ValueType.Object) this.type).getClassName();
|
||||
Variable var = pe.getProgram().createVariable();
|
||||
GetFieldInstruction insn = new GetFieldInstruction();
|
||||
insn.setField(new FieldReference(className, name));
|
||||
|
|
|
@ -92,8 +92,11 @@ public interface InstructionReader {
|
|||
void putElement(VariableReader array, VariableReader index, VariableReader value);
|
||||
|
||||
void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments,
|
||||
InvocationType type);
|
||||
List<? extends VariableReader> arguments, InvocationType type);
|
||||
|
||||
void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments);
|
||||
|
||||
void isInstance(VariableReader receiver, VariableReader value, ValueType type);
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.model.instructions;
|
||||
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
|
@ -82,13 +84,15 @@ public interface InstructionVisitor {
|
|||
|
||||
void visit(InvokeInstruction insn);
|
||||
|
||||
void visit(InvokeDynamicInstruction insn);
|
||||
|
||||
void visit(IsInstanceInstruction insn);
|
||||
|
||||
void visit(InitClassInstruction insn);
|
||||
|
||||
void visit(NullCheckInstruction insn);
|
||||
|
||||
|
||||
void visit(MonitorEnterInstruction insn);
|
||||
|
||||
|
||||
void visit(MonitorExitInstruction insn);
|
||||
}
|
||||
|
|
|
@ -369,6 +369,12 @@ public class AsyncMethodFinder {
|
|||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
}
|
||||
|
|
|
@ -176,6 +176,10 @@ public abstract class BasicBlockMapper implements InstructionVisitor {
|
|||
public void visit(InvokeInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.model.util;
|
||||
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
|
@ -163,6 +164,15 @@ public class DefinitionExtractor implements InstructionVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
if (insn.getReceiver() == null) {
|
||||
definedVariables = new Variable[0];
|
||||
} else {
|
||||
definedVariables = new Variable[] { insn.getReceiver() };
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
definedVariables = new Variable[] { insn.getReceiver() };
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
*/
|
||||
package org.teavm.model.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
|
@ -337,6 +339,73 @@ public class InstructionStringifier implements InstructionReader {
|
|||
sb.append(")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
if (receiver != null) {
|
||||
sb.append("@").append(receiver.getIndex()).append(" := ");
|
||||
}
|
||||
if (instance != null) {
|
||||
sb.append("@").append(instance.getIndex()).append(".");
|
||||
}
|
||||
sb.append(method.getName()).append("(");
|
||||
sb.append(arguments.stream().map(arg -> "@" + arg.getIndex()).collect(Collectors.joining(", ")));
|
||||
sb.append(") ");
|
||||
sb.append("[").append(convert(bootstrapMethod)).append('(');
|
||||
sb.append(bootstrapArguments.stream().map(arg -> convert(arg)).collect(Collectors.joining(", ")));
|
||||
sb.append(")");
|
||||
}
|
||||
|
||||
private String convert(MethodHandle handle) {
|
||||
switch (handle.getKind()) {
|
||||
case INVOKE_VIRTUAL:
|
||||
case INVOKE_SPECIAL:
|
||||
case INVOKE_INTERFACE:
|
||||
return new MethodDescriptor(handle.getName(), handle.signature()).toString();
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
return "new" + handle.getClassName() + "." + new MethodDescriptor(handle.getName(),
|
||||
handle.signature()).toString();
|
||||
case INVOKE_STATIC:
|
||||
return handle.getClassName() + "." + new MethodDescriptor(handle.getName(),
|
||||
handle.signature()).toString();
|
||||
case GET_FIELD:
|
||||
return "GET " + handle.getName();
|
||||
case GET_STATIC_FIELD:
|
||||
return "GET " + handle.getClassName() + "." + handle.getName();
|
||||
case PUT_FIELD:
|
||||
return "PUT " + handle.getName();
|
||||
case PUT_STATIC_FIELD:
|
||||
return "PUT " + handle.getClassName() + "." + handle.getName();
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected handle type: " + handle.getKind());
|
||||
}
|
||||
|
||||
private String convert(RuntimeConstant cst) {
|
||||
switch (cst.getKind()) {
|
||||
case RuntimeConstant.INT:
|
||||
return String.valueOf(cst.getInt());
|
||||
case RuntimeConstant.LONG:
|
||||
return String.valueOf(cst.getLong());
|
||||
case RuntimeConstant.FLOAT:
|
||||
return String.valueOf(cst.getFloat());
|
||||
case RuntimeConstant.DOUBLE:
|
||||
return String.valueOf(cst.getDouble());
|
||||
case RuntimeConstant.STRING:
|
||||
return String.valueOf(cst.getString());
|
||||
case RuntimeConstant.TYPE:
|
||||
return String.valueOf(cst.getValueType());
|
||||
case RuntimeConstant.METHOD: {
|
||||
ValueType[] methodType = cst.getMethodType();
|
||||
return "(" + Arrays.stream(methodType, 0, methodType.length - 1).map(Object::toString)
|
||||
.collect(Collectors.joining()) + ")" + methodType[methodType.length - 1];
|
||||
}
|
||||
case RuntimeConstant.METHOD_HANDLE:
|
||||
return convert(cst.getMethodHandle());
|
||||
}
|
||||
throw new IllegalArgumentException("Unexpected runtime constant type: " + cst.getKind());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
sb.append("@").append(receiver.getIndex()).append(" := @").append(value.getIndex())
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.model.util;
|
|||
|
||||
import java.util.List;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
/**
|
||||
|
@ -160,6 +161,11 @@ public class InstructionTransitionExtractor implements InstructionVisitor {
|
|||
targets = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
targets = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
targets = null;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.model.util;
|
||||
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
|
@ -213,6 +214,19 @@ public abstract class InstructionVariableMapper implements InstructionVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
if (insn.getReceiver() != null) {
|
||||
insn.setReceiver(map(insn.getReceiver()));
|
||||
}
|
||||
if (insn.getInstance() != null) {
|
||||
insn.setInstance(map(insn.getInstance()));
|
||||
}
|
||||
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||
insn.getArguments().set(i, map(insn.getArguments().get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
insn.setReceiver(map(insn.getReceiver()));
|
||||
|
@ -238,8 +252,8 @@ public abstract class InstructionVariableMapper implements InstructionVisitor {
|
|||
public void visit(MonitorExitInstruction insn) {
|
||||
insn.setObjectRef(map(insn.getObjectRef()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -191,6 +191,10 @@ public class MissingItemsProcessor {
|
|||
checkMethod(insn.getLocation(), insn.getMethod());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(PutElementInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.model.util;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphBuilder;
|
||||
import org.teavm.model.*;
|
||||
|
@ -471,6 +472,23 @@ public final class ProgramUtils {
|
|||
copy.setLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
InvokeDynamicInstruction insnCopy = new InvokeDynamicInstruction();
|
||||
insnCopy.setMethod(method);
|
||||
insnCopy.setBootstrapMethod(bootstrapMethod);
|
||||
insnCopy.getBootstrapArguments().addAll(bootstrapArguments);
|
||||
if (instance != null) {
|
||||
insnCopy.setInstance(copyVar(instance));
|
||||
}
|
||||
insnCopy.getArguments().addAll(arguments.stream().map(v -> copyVar(v)).collect(Collectors.toList()));
|
||||
insnCopy.setReceiver(receiver != null ? copyVar(receiver) : null);
|
||||
copy = insnCopy;
|
||||
copy.setLocation(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
IsInstanceInstruction insnCopy = new IsInstanceInstruction();
|
||||
|
|
|
@ -296,6 +296,15 @@ public class TypeInferer {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
if (receiver != null) {
|
||||
types[receiver.getIndex()] = convert(method.getResultType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void integerConstant(VariableReader receiver, int cst) {
|
||||
types[receiver.getIndex()] = VariableType.INT;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.model.util;
|
||||
|
||||
import org.teavm.model.InvokeDynamicInstruction;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
|
@ -192,6 +193,18 @@ public class UsageExtractor implements InstructionVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
if (insn.getInstance() != null) {
|
||||
usedVariables = new Variable[insn.getArguments().size() + 1];
|
||||
insn.getArguments().toArray(usedVariables);
|
||||
usedVariables[insn.getArguments().size()] = insn.getInstance();
|
||||
} else {
|
||||
usedVariables = new Variable[insn.getArguments().size()];
|
||||
insn.getArguments().toArray(usedVariables);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
usedVariables = new Variable[] { insn.getValue() };
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
*/
|
||||
package org.teavm.optimization;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.function.UnaryOperator;
|
||||
import org.teavm.common.DominatorTree;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphUtils;
|
||||
|
@ -395,12 +393,17 @@ public class GlobalValueNumbering implements MethodOptimization {
|
|||
int instance = map[insn.getInstance().getIndex()];
|
||||
insn.setInstance(program.variableAt(instance));
|
||||
}
|
||||
for (int i = 0; i < insn.getArguments().size(); ++i) {
|
||||
int arg = map[insn.getArguments().get(i).getIndex()];
|
||||
insn.getArguments().set(i, program.variableAt(arg));
|
||||
}
|
||||
insn.getArguments().replaceAll(mapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
Optional.ofNullable(insn.getInstance()).map(mapper).ifPresent(var -> insn.setInstance(var));
|
||||
insn.getArguments().replaceAll(mapper);
|
||||
}
|
||||
|
||||
private UnaryOperator<Variable> mapper = var -> program.variableAt(map[var.getIndex()]);
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
int val = map[insn.getValue().getIndex()];
|
||||
|
|
|
@ -366,6 +366,10 @@ public class LoopInvariantMotion implements MethodOptimization {
|
|||
public void visit(InvokeInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
canMove = true;
|
||||
|
@ -560,6 +564,10 @@ public class LoopInvariantMotion implements MethodOptimization {
|
|||
public void visit(InvokeInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -235,6 +235,13 @@ public class UnusedVariableElimination implements MethodOptimization {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
if (insn.getReceiver() != null && !used[insn.getReceiver().getIndex()]) {
|
||||
insn.setReceiver(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
requestUsage(insn.getReceiver());
|
||||
|
@ -251,12 +258,12 @@ public class UnusedVariableElimination implements MethodOptimization {
|
|||
|
||||
@Override
|
||||
public void visit(MonitorEnterInstruction insn) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitInstruction insn) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,7 @@
|
|||
*/
|
||||
package org.teavm.optimization;
|
||||
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.Variable;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
||||
/**
|
||||
|
@ -193,6 +190,16 @@ public final class VariableEscapeAnalyzer {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
if (insn.getInstance() != null) {
|
||||
escaping[insn.getInstance().getIndex()] = true;
|
||||
}
|
||||
for (Variable arg : insn.getArguments()) {
|
||||
escaping[arg.getIndex()] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
}
|
||||
|
|
|
@ -197,6 +197,10 @@ public final class VariableUsageGraphBuilder {
|
|||
public void visit(InvokeInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
use(insn.getReceiver(), insn.getValue());
|
||||
|
@ -213,12 +217,12 @@ public final class VariableUsageGraphBuilder {
|
|||
|
||||
@Override
|
||||
public void visit(MonitorEnterInstruction insn) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(MonitorExitInstruction insn) {
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.parsing;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import org.teavm.common.Mapper;
|
||||
import org.teavm.javascript.spi.Remove;
|
||||
|
@ -101,6 +102,58 @@ public class ClassRefsRenamer implements InstructionVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
private ValueType[] rename(ValueType[] types) {
|
||||
return Arrays.stream(types).map(e -> rename(e)).toArray(sz -> new ValueType[sz]);
|
||||
}
|
||||
|
||||
private RuntimeConstant rename(RuntimeConstant cst) {
|
||||
switch (cst.getKind()) {
|
||||
case RuntimeConstant.TYPE:
|
||||
return new RuntimeConstant(rename(cst.getValueType()));
|
||||
case RuntimeConstant.METHOD:
|
||||
return new RuntimeConstant(rename(cst.getMethodType()));
|
||||
case RuntimeConstant.METHOD_HANDLE:
|
||||
return new RuntimeConstant(rename(cst.getMethodHandle()));
|
||||
default:
|
||||
return cst;
|
||||
}
|
||||
}
|
||||
|
||||
private MethodHandle rename(MethodHandle handle) {
|
||||
switch (handle.getKind()) {
|
||||
case GET_FIELD:
|
||||
return MethodHandle.fieldGetter(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.getValueType()));
|
||||
case GET_STATIC_FIELD:
|
||||
return MethodHandle.staticFieldGetter(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.getValueType()));
|
||||
case PUT_FIELD:
|
||||
return MethodHandle.fieldSetter(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.getValueType()));
|
||||
case PUT_STATIC_FIELD:
|
||||
return MethodHandle.staticFieldSetter(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.getValueType()));
|
||||
case INVOKE_VIRTUAL:
|
||||
return MethodHandle.virtualCaller(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.signature()));
|
||||
case INVOKE_STATIC:
|
||||
return MethodHandle.staticCaller(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.signature()));
|
||||
case INVOKE_SPECIAL:
|
||||
return MethodHandle.specialCaller(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.signature()));
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
return MethodHandle.constructorCaller(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.signature()));
|
||||
case INVOKE_INTERFACE:
|
||||
return MethodHandle.interfaceCaller(classNameMapper.map(handle.getClassName()), handle.getName(),
|
||||
rename(handle.signature()));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown method handle type: " + handle.getKind());
|
||||
}
|
||||
|
||||
private void rename(AnnotationContainer source, AnnotationContainer target) {
|
||||
for (AnnotationHolder annot : source.all()) {
|
||||
if (!annot.getType().equals(Rename.class.getName())
|
||||
|
@ -271,6 +324,18 @@ public class ClassRefsRenamer implements InstructionVisitor {
|
|||
insn.setMethod(new MethodReference(className, new MethodDescriptor(insn.getMethod().getName(), signature)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
ValueType[] signature = insn.getMethod().getSignature();
|
||||
for (int i = 0; i < signature.length; ++i) {
|
||||
signature[i] = rename(signature[i]);
|
||||
}
|
||||
insn.setMethod(new MethodDescriptor(insn.getMethod().getName(), signature));
|
||||
for (int i = 0; i < insn.getBootstrapArguments().size(); ++i) {
|
||||
insn.getBootstrapArguments().set(i, rename(insn.getBootstrapArguments().get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
insn.setType(rename(insn.getType()));
|
||||
|
|
|
@ -486,7 +486,63 @@ public class ProgramParser implements VariableDebugInformation {
|
|||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
|
||||
throw new IllegalStateException("InvokeDynamic is not supported in TeaVM");
|
||||
InvokeDynamicInstruction insn = new InvokeDynamicInstruction();
|
||||
insn.setBootstrapMethod(parseHandle(bsm));
|
||||
|
||||
switch (insn.getBootstrapMethod().getKind()) {
|
||||
case GET_STATIC_FIELD:
|
||||
case PUT_STATIC_FIELD:
|
||||
case INVOKE_STATIC:
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
break;
|
||||
default:
|
||||
insn.setInstance(getVariable(popSingle()));
|
||||
break;
|
||||
}
|
||||
Type[] types = Type.getArgumentTypes(desc);
|
||||
Variable[] args = new Variable[types.length];
|
||||
int j = args.length;
|
||||
for (int i = types.length - 1; i >= 0; --i) {
|
||||
args[--j] = types[i].getSize() == 2 ? getVariable(popDouble()) : getVariable(popSingle());
|
||||
}
|
||||
insn.getArguments().addAll(Arrays.asList(args));
|
||||
|
||||
Type returnType = Type.getReturnType(desc);
|
||||
if (returnType.getSize() > 0) {
|
||||
insn.setReceiver(getVariable(returnType.getSize() == 2 ? pushDouble() : pushSingle()));
|
||||
}
|
||||
|
||||
insn.setMethod(new MethodDescriptor(name, MethodDescriptor.parseSignature(desc)));
|
||||
for (int i = 0; i < bsmArgs.length; ++i) {
|
||||
insn.getBootstrapArguments().add(convertConstant(bsmArgs[i]));
|
||||
}
|
||||
|
||||
addInstruction(insn);
|
||||
}
|
||||
|
||||
private RuntimeConstant convertConstant(Object value) {
|
||||
if (value instanceof Integer) {
|
||||
return new RuntimeConstant((Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
return new RuntimeConstant((Long) value);
|
||||
} else if (value instanceof Float) {
|
||||
return new RuntimeConstant((Float) value);
|
||||
} else if (value instanceof Double) {
|
||||
return new RuntimeConstant((Double) value);
|
||||
} else if (value instanceof String) {
|
||||
return new RuntimeConstant((String) value);
|
||||
} else if (value instanceof Type) {
|
||||
Type type = (Type) value;
|
||||
if (type.getSort() == Type.METHOD) {
|
||||
return new RuntimeConstant(MethodDescriptor.parseSignature(type.getDescriptor()));
|
||||
} else {
|
||||
return new RuntimeConstant(ValueType.parse(type.getDescriptor()));
|
||||
}
|
||||
} else if (value instanceof Handle) {
|
||||
return new RuntimeConstant(parseHandle((Handle) value));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown runtime constant: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1673,4 +1729,38 @@ public class ProgramParser implements VariableDebugInformation {
|
|||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
static MethodHandle parseHandle(Handle handle) {
|
||||
switch (handle.getTag()) {
|
||||
case Opcodes.H_GETFIELD:
|
||||
return MethodHandle.fieldGetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_GETSTATIC:
|
||||
return MethodHandle.staticFieldGetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_PUTFIELD:
|
||||
return MethodHandle.fieldSetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_PUTSTATIC:
|
||||
return MethodHandle.staticFieldSetter(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
ValueType.parse(handle.getDesc()));
|
||||
case Opcodes.H_INVOKEVIRTUAL:
|
||||
return MethodHandle.virtualCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_INVOKESTATIC:
|
||||
return MethodHandle.staticCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_INVOKESPECIAL:
|
||||
return MethodHandle.specialCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_NEWINVOKESPECIAL:
|
||||
return MethodHandle.constructorCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
case Opcodes.H_INVOKEINTERFACE:
|
||||
return MethodHandle.interfaceCaller(handle.getOwner().replace('/', '.'), handle.getName(),
|
||||
MethodDescriptor.parseSignature(handle.getDesc()));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown handle tag: " + handle.getTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -393,6 +393,20 @@ public class SSATransformer {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(InvokeDynamicInstruction insn) {
|
||||
List<Variable> args = insn.getArguments();
|
||||
for (int i = 0; i < args.size(); ++i) {
|
||||
args.set(i, use(args.get(i)));
|
||||
}
|
||||
if (insn.getInstance() != null) {
|
||||
insn.setInstance(use(insn.getInstance()));
|
||||
}
|
||||
if (insn.getReceiver() != null) {
|
||||
insn.setReceiver(define(insn.getReceiver()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(IsInstanceInstruction insn) {
|
||||
insn.setValue(use(insn.getValue()));
|
||||
|
|
|
@ -17,12 +17,7 @@ package org.teavm.tooling;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.teavm.model.BasicBlockReader;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.InstructionLocation;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.ArrayElementType;
|
||||
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||
import org.teavm.model.instructions.BinaryOperation;
|
||||
|
@ -183,6 +178,12 @@ class InstructionLocationReader implements InstructionReader {
|
|||
List<? extends VariableReader> arguments, InvocationType type) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
|
||||
}
|
||||
|
|
|
@ -86,17 +86,12 @@ class ProgramSourceAggregator implements InstructionReader {
|
|||
@Override public void putElement(VariableReader array, VariableReader index, VariableReader value) { }
|
||||
@Override public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
List<? extends VariableReader> arguments, InvocationType type) { }
|
||||
@Override public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
|
||||
List<? extends VariableReader> arguments, MethodHandle bootstrapMethod,
|
||||
List<RuntimeConstant> bootstrapArguments) { }
|
||||
@Override public void isInstance(VariableReader receiver, VariableReader value, ValueType type) { }
|
||||
@Override public void initClass(String className) { }
|
||||
@Override public void nullCheck(VariableReader receiver, VariableReader value) { }
|
||||
|
||||
@Override
|
||||
public void monitorEnter(VariableReader objectRef) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void monitorExit(VariableReader objectRef) {
|
||||
|
||||
}
|
||||
@Override public void monitorEnter(VariableReader objectRef) { }
|
||||
@Override public void monitorExit(VariableReader objectRef) { }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -66,20 +66,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.orfjackal.retrolambda</groupId>
|
||||
<artifactId>retrolambda-maven-plugin</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>retrolambda</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process-main</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.teavm</groupId>
|
||||
<artifactId>teavm-maven-plugin</artifactId>
|
||||
|
|
|
@ -43,20 +43,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xs
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.orfjackal.retrolambda</groupId>
|
||||
<artifactId>retrolambda-maven-plugin</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>retrolambda</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process-main</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
|
|
|
@ -81,19 +81,6 @@
|
|||
</goals>
|
||||
<phase>process-classes</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>help-descriptor</id>
|
||||
<goals>
|
||||
<goal>helpmojo</goal>
|
||||
</goals>
|
||||
<phase>process-classes</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>mojo-descriptor</id>
|
||||
<goals>
|
||||
<goal>descriptor</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
|
|
|
@ -91,14 +91,15 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
|
|||
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||
ProgramEmitter pe = ProgramEmitter.create(method);
|
||||
ForkEmitter fork = pe.getField(field.getReference(), field.getType()).fork(
|
||||
BinaryBranchingCondition.REFERENCE_NOT_EQUAL, pe.constantNull());
|
||||
BinaryBranchingCondition.REFERENCE_NOT_EQUAL, pe.constantNull(field.getType()));
|
||||
|
||||
BasicBlock resourceFound = pe.createBlock();
|
||||
fork.setThen(resourceFound);
|
||||
pe.getField(field.getReference(), field.getType()).returnValue();
|
||||
|
||||
fork.setElse(pe.createBlock());
|
||||
pe.setField(field.getReference(), field.getType(), pe.invoke(createMethod.getReference()));
|
||||
pe.setField(field.getReference(), pe.invoke(createMethod.getReference().getClassName(),
|
||||
createMethod.getReference().getName(), createMethod.getResultType()));
|
||||
pe.jump(resourceFound);
|
||||
|
||||
AnnotationHolder noCacheAnnot = new AnnotationHolder(NoCache.class.getName());
|
||||
|
|
|
@ -39,20 +39,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.orfjackal.retrolambda</groupId>
|
||||
<artifactId>retrolambda-maven-plugin</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>retrolambda</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process-main</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
|
|
|
@ -58,20 +58,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.orfjackal.retrolambda</groupId>
|
||||
<artifactId>retrolambda-maven-plugin</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>retrolambda</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process-main</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
|
|
|
@ -15,11 +15,8 @@
|
|||
*/
|
||||
package org.teavm.samples.hello;
|
||||
|
||||
import org.teavm.dom.ajax.ReadyStateChangeHandler;
|
||||
import org.teavm.dom.ajax.XMLHttpRequest;
|
||||
import org.teavm.dom.browser.Window;
|
||||
import org.teavm.dom.events.EventListener;
|
||||
import org.teavm.dom.events.MouseEvent;
|
||||
import org.teavm.dom.html.HTMLButtonElement;
|
||||
import org.teavm.dom.html.HTMLDocument;
|
||||
import org.teavm.dom.html.HTMLElement;
|
||||
|
@ -36,22 +33,16 @@ public final class Client {
|
|||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
helloButton.addEventListener("click", new EventListener<MouseEvent>() {
|
||||
@Override public void handleEvent(MouseEvent evt) {
|
||||
sayHello();
|
||||
}
|
||||
});
|
||||
helloButton.addEventListener("click", evt -> sayHello());
|
||||
}
|
||||
|
||||
private static void sayHello() {
|
||||
helloButton.setDisabled(true);
|
||||
thinkingPanel.getStyle().setProperty("display", "");
|
||||
final XMLHttpRequest xhr = window.createXMLHttpRequest();
|
||||
xhr.setOnReadyStateChange(new ReadyStateChangeHandler() {
|
||||
@Override public void stateChanged() {
|
||||
if (xhr.getReadyState() == XMLHttpRequest.DONE) {
|
||||
receiveResponse(xhr.getResponseText());
|
||||
}
|
||||
xhr.setOnReadyStateChange(() -> {
|
||||
if (xhr.getReadyState() == XMLHttpRequest.DONE) {
|
||||
receiveResponse(xhr.getResponseText());
|
||||
}
|
||||
});
|
||||
xhr.open("GET", "hello");
|
||||
|
|
|
@ -58,20 +58,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.orfjackal.retrolambda</groupId>
|
||||
<artifactId>retrolambda-maven-plugin</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>retrolambda</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process-main</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
|
|
|
@ -58,20 +58,6 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>net.orfjackal.retrolambda</groupId>
|
||||
<artifactId>retrolambda-maven-plugin</artifactId>
|
||||
<version>1.8.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>retrolambda</id>
|
||||
<phase>process-classes</phase>
|
||||
<goals>
|
||||
<goal>process-main</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import java.lang.reflect.Array;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
*/
|
||||
public class LambdaTest {
|
||||
@Test
|
||||
public void lambdaWorks() {
|
||||
Integer[] src = { 1, 2, 3 };
|
||||
Integer[] array = map(src, (Integer n) -> n * 2 + src[0]);
|
||||
assertArrayEquals(new Integer[] { 3, 5, 7 }, array);
|
||||
}
|
||||
|
||||
static <T> T[] map(T[] array, Function<T> f) {
|
||||
@SuppressWarnings("unchecked")
|
||||
T[] result = (T[])Array.newInstance(array.getClass().getComponentType(), array.length);
|
||||
for (int i = 0; i < result.length; ++i) {
|
||||
result[i] = f.apply(array[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
interface Function<T> {
|
||||
T apply(T value);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user