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:
Alexey Andreev 2015-07-26 23:44:24 +03:00
commit 00a751ef13
57 changed files with 1749 additions and 240 deletions

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) { }
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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) {
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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());

View File

@ -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);
}
}

View 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());
}
}

View File

@ -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
}

View 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;
}
}

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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));

View File

@ -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);

View File

@ -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);
}

View File

@ -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) {
}

View File

@ -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) {
}

View File

@ -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() };

View File

@ -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())

View File

@ -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;

View File

@ -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()));
}
}

View File

@ -191,6 +191,10 @@ public class MissingItemsProcessor {
checkMethod(insn.getLocation(), insn.getMethod());
}
@Override
public void visit(InvokeDynamicInstruction insn) {
}
@Override
public void visit(PutElementInstruction insn) {
}

View File

@ -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();

View File

@ -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;

View File

@ -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() };

View File

@ -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()];

View File

@ -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) {
}

View File

@ -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) {
}
}
}

View File

@ -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) {
}

View File

@ -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) {
}
}
}

View File

@ -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()));

View File

@ -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());
}
}
}

View File

@ -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()));

View File

@ -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) {
}

View File

@ -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) { }
}

View File

@ -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);

View File

@ -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);

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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());

View File

@ -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>

View File

@ -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>

View File

@ -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");

View File

@ -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>

View File

@ -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>

View File

@ -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);
}
}