diff --git a/classlib/teavm-classlib.iml b/classlib/teavm-classlib.iml
index ead5bc4ab..c1979e489 100644
--- a/classlib/teavm-classlib.iml
+++ b/classlib/teavm-classlib.iml
@@ -38,6 +38,7 @@
+
diff --git a/core/pom.xml b/core/pom.xml
index 27ccebc97..858dfdf9d 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -58,6 +58,11 @@
jackson-annotations
true
+
+ org.objenesis
+ objenesis
+ 2.4
+
diff --git a/core/src/main/java/org/teavm/model/IncomingReader.java b/core/src/main/java/org/teavm/model/IncomingReader.java
index 1098a532f..d02de8e83 100644
--- a/core/src/main/java/org/teavm/model/IncomingReader.java
+++ b/core/src/main/java/org/teavm/model/IncomingReader.java
@@ -15,10 +15,6 @@
*/
package org.teavm.model;
-/**
- *
- * @author Alexey Andreev
- */
public interface IncomingReader {
VariableReader getValue();
diff --git a/core/src/main/java/org/teavm/model/InterpretException.java b/core/src/main/java/org/teavm/model/InterpretException.java
new file mode 100644
index 000000000..f9ab23833
--- /dev/null
+++ b/core/src/main/java/org/teavm/model/InterpretException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2016 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;
+
+public class InterpretException extends Exception {
+ private final BasicBlockReader block;
+ private final int index;
+
+ public InterpretException(BasicBlockReader block, int index, Throwable cause) {
+ super(cause);
+ this.block = block;
+ this.index = index;
+ }
+
+ public BasicBlockReader getBlock() {
+ return block;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+}
diff --git a/core/src/main/java/org/teavm/model/Interpreter.java b/core/src/main/java/org/teavm/model/Interpreter.java
new file mode 100644
index 000000000..46c1feac1
--- /dev/null
+++ b/core/src/main/java/org/teavm/model/Interpreter.java
@@ -0,0 +1,838 @@
+/*
+ * Copyright 2016 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.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.objenesis.Objenesis;
+import org.objenesis.ObjenesisStd;
+import org.teavm.model.instructions.ArrayElementType;
+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.NumericOperandType;
+import org.teavm.model.instructions.SwitchTableEntryReader;
+
+public class Interpreter {
+ private ClassLoader classLoader;
+ private BasicBlockReader currentBlock;
+ private List> outgoings;
+ private Object[] variables;
+ private Object result;
+ private State state;
+
+ public Interpreter(ClassLoader classLoader) {
+ this.classLoader = classLoader;
+ }
+
+ public Object interpret(ProgramReader program, Object[] parameters) throws InterpretException {
+ variables = new Object[program.variableCount()];
+ System.arraycopy(parameters, 0, variables, 0, parameters.length);
+ currentBlock = program.basicBlockAt(0);
+ state = State.EXECUTING;
+
+ outgoings = new ArrayList<>();
+ for (int i = 0; i < program.basicBlockCount(); ++i) {
+ outgoings.add(new ArrayList<>());
+ }
+ for (int i = 0; i < program.basicBlockCount(); ++i) {
+ BasicBlockReader block = program.basicBlockAt(i);
+ for (PhiReader phi : block.readPhis()) {
+ for (IncomingReader incoming : phi.readIncomings()) {
+ outgoings.get(incoming.getSource().getIndex()).add(incoming);
+ }
+ }
+ }
+
+ try {
+ while (true) {
+ int instructionIndex = 0;
+ try {
+ while (instructionIndex < currentBlock.instructionCount()) {
+ currentBlock.readInstruction(instructionIndex++, reader);
+ }
+ } catch (RuntimeException e) {
+ if (!pickExceptionHandler(e)) {
+ throw new InterpretException(currentBlock, instructionIndex, e);
+ }
+ }
+ switch (state) {
+ case EXITED: {
+ return result;
+ }
+ case THROWN: {
+ Throwable ex = (Throwable) result;
+ throw new InterpretException(currentBlock, currentBlock.instructionCount() - 1, ex);
+ }
+ case EXECUTING:
+ break;
+ }
+ }
+
+ } finally {
+ currentBlock = null;
+ variables = null;
+ outgoings = null;
+ result = null;
+ }
+ }
+
+ private boolean pickExceptionHandler(Throwable e) {
+ for (TryCatchBlockReader tryCatch : currentBlock.readTryCatchBlocks()) {
+ Class> exceptionType;
+ try {
+ exceptionType = tryCatch.getExceptionType() != null
+ ? Class.forName(tryCatch.getExceptionType(), false, classLoader)
+ : null;
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException("Can't find exception class " + tryCatch.getExceptionType());
+ }
+ if (exceptionType == null || exceptionType.isInstance(e)) {
+ currentBlock = tryCatch.getProtectedBlock();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private InstructionReader reader = new InstructionReader() {
+ private Objenesis objenesis = new ObjenesisStd();
+
+ @Override
+ public void location(InstructionLocation location) {
+ }
+
+ @Override
+ public void nop() {
+ }
+
+ @Override
+ public void classConstant(VariableReader receiver, ValueType cst) {
+ variables[receiver.getIndex()] = asJvmClass(cst);
+ }
+
+ @Override
+ public void nullConstant(VariableReader receiver) {
+ variables[receiver.getIndex()] = null;
+ }
+
+ @Override
+ public void integerConstant(VariableReader receiver, int cst) {
+ variables[receiver.getIndex()] = cst;
+ }
+
+ @Override
+ public void longConstant(VariableReader receiver, long cst) {
+ variables[receiver.getIndex()] = cst;
+ }
+
+ @Override
+ public void floatConstant(VariableReader receiver, float cst) {
+ variables[receiver.getIndex()] = cst;
+ }
+
+ @Override
+ public void doubleConstant(VariableReader receiver, double cst) {
+ variables[receiver.getIndex()] = cst;
+ }
+
+ @Override
+ public void stringConstant(VariableReader receiver, String cst) {
+ variables[receiver.getIndex()] = cst;
+ }
+
+ @Override
+ public void binary(BinaryOperation op, VariableReader receiver, VariableReader first, VariableReader second,
+ NumericOperandType type) {
+ switch (type) {
+ case INT: {
+ int a = (Integer) variables[first.getIndex()];
+ int b = (Integer) variables[second.getIndex()];
+ int result;
+ switch (op) {
+ case ADD:
+ result = a + b;
+ break;
+ case SUBTRACT:
+ result = a - b;
+ break;
+ case MULTIPLY:
+ result = a * b;
+ break;
+ case DIVIDE:
+ result = a * b;
+ break;
+ case MODULO:
+ result = a % b;
+ break;
+ case COMPARE:
+ result = Integer.compare(a, b);
+ break;
+ case AND:
+ result = a & b;
+ break;
+ case OR:
+ result = a | b;
+ break;
+ case XOR:
+ result = a ^ b;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown operation: " + op);
+ }
+ variables[receiver.getIndex()] = result;
+ break;
+ }
+ case LONG: {
+ long a = (Long) variables[first.getIndex()];
+ long b = (Long) variables[second.getIndex()];
+ long result;
+ switch (op) {
+ case ADD:
+ result = a + b;
+ break;
+ case SUBTRACT:
+ result = a - b;
+ break;
+ case MULTIPLY:
+ result = a * b;
+ break;
+ case DIVIDE:
+ result = a * b;
+ break;
+ case MODULO:
+ result = a % b;
+ break;
+ case COMPARE:
+ result = Long.compare(a, b);
+ break;
+ case AND:
+ result = a & b;
+ break;
+ case OR:
+ result = a | b;
+ break;
+ case XOR:
+ result = a ^ b;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown operation: " + op);
+ }
+ variables[receiver.getIndex()] = result;
+ break;
+ }
+ case FLOAT: {
+ float a = (Float) variables[first.getIndex()];
+ float b = (Float) variables[second.getIndex()];
+ float result;
+ switch (op) {
+ case ADD:
+ result = a + b;
+ break;
+ case SUBTRACT:
+ result = a - b;
+ break;
+ case MULTIPLY:
+ result = a * b;
+ break;
+ case DIVIDE:
+ result = a * b;
+ break;
+ case MODULO:
+ result = a % b;
+ break;
+ case COMPARE:
+ result = Float.compare(a, b);
+ break;
+ case AND:
+ case OR:
+ case XOR:
+ throw new IllegalArgumentException("Unsupported operation " + op
+ + " for operands of type" + type);
+ default:
+ throw new IllegalArgumentException("Unknown operation: " + op);
+ }
+ variables[receiver.getIndex()] = result;
+ break;
+ }
+ case DOUBLE: {
+ double a = (Double) variables[first.getIndex()];
+ double b = (Double) variables[second.getIndex()];
+ double result;
+ switch (op) {
+ case ADD:
+ result = a + b;
+ break;
+ case SUBTRACT:
+ result = a - b;
+ break;
+ case MULTIPLY:
+ result = a * b;
+ break;
+ case DIVIDE:
+ result = a * b;
+ break;
+ case MODULO:
+ result = a % b;
+ break;
+ case COMPARE:
+ result = Double.compare(a, b);
+ break;
+ case AND:
+ case OR:
+ case XOR:
+ throw new IllegalArgumentException("Unsupported operation " + op
+ + " for operands of type" + type);
+ default:
+ throw new IllegalArgumentException("Unknown operation: " + op);
+ }
+ variables[receiver.getIndex()] = result;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void negate(VariableReader receiver, VariableReader operand, NumericOperandType type) {
+ Object result;
+ Object a = variables[operand.getIndex()];
+ switch (type) {
+ case INT:
+ result = -(Integer) a;
+ break;
+ case LONG:
+ result = -(Long) a;
+ break;
+ case FLOAT:
+ result = -(Float) a;
+ break;
+ case DOUBLE:
+ result = -(Double) a;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ variables[receiver.getIndex()] = result;
+ }
+
+ @Override
+ public void assign(VariableReader receiver, VariableReader assignee) {
+ variables[receiver.getIndex()] = variables[assignee.getIndex()];
+ }
+
+ @Override
+ public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
+ variables[receiver.getIndex()] = asJvmClass(targetType).cast(variables[value.getIndex()]);
+ }
+
+ @Override
+ public void cast(VariableReader receiver, VariableReader value, NumericOperandType sourceType,
+ NumericOperandType targetType) {
+ Object result;
+ switch (sourceType) {
+ case INT: {
+ int a = (Integer) variables[value.getIndex()];
+ switch (targetType) {
+ case INT:
+ result = a;
+ break;
+ case LONG:
+ result = (long) a;
+ break;
+ case FLOAT:
+ result = (float) a;
+ break;
+ case DOUBLE:
+ result = (double) a;
+ break;
+ default:
+ throw new IllegalArgumentException("Can't cast " + sourceType + " to " + targetType);
+ }
+ break;
+ }
+ case LONG: {
+ long a = (Long) variables[value.getIndex()];
+ switch (targetType) {
+ case INT:
+ result = (int) a;
+ break;
+ case LONG:
+ result = a;
+ break;
+ case FLOAT:
+ result = (float) a;
+ break;
+ case DOUBLE:
+ result = (double) a;
+ break;
+ default:
+ throw new IllegalArgumentException("Can't cast " + sourceType + " to " + targetType);
+ }
+ break;
+ }
+ case FLOAT: {
+ float a = (Float) variables[value.getIndex()];
+ switch (targetType) {
+ case INT:
+ result = (int) a;
+ break;
+ case LONG:
+ result = (long) a;
+ break;
+ case FLOAT:
+ result = a;
+ break;
+ case DOUBLE:
+ result = (double) a;
+ break;
+ default:
+ throw new IllegalArgumentException("Can't cast " + sourceType + " to " + targetType);
+ }
+ break;
+ }
+ case DOUBLE: {
+ double a = (Double) variables[value.getIndex()];
+ switch (targetType) {
+ case INT:
+ result = (int) a;
+ break;
+ case LONG:
+ result = (long) a;
+ break;
+ case FLOAT:
+ result = (float) a;
+ break;
+ case DOUBLE:
+ result = a;
+ break;
+ default:
+ throw new IllegalArgumentException("Can't cast " + sourceType + " to " + targetType);
+ }
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Can't cast " + sourceType + " to " + targetType);
+ }
+ variables[receiver.getIndex()] = result;
+ }
+
+ @Override
+ public void cast(VariableReader receiver, VariableReader value, IntegerSubtype type,
+ CastIntegerDirection direction) {
+ switch (direction) {
+ case FROM_INTEGER: {
+ int a = (Integer) variables[value.getIndex()];
+ Object result;
+ switch (type) {
+ case BYTE:
+ result = (byte) a;
+ break;
+ case SHORT:
+ result = (short) a;
+ break;
+ case CHARACTER:
+ result = (char) a;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ variables[receiver.getIndex()] = result;
+ break;
+ }
+ case TO_INTEGER: {
+ Object a = variables[value.getIndex()];
+ int result;
+ switch (type) {
+ case BYTE:
+ result = (Byte) a;
+ break;
+ case SHORT:
+ result = (Short) a;
+ break;
+ case CHARACTER:
+ result = (Character) a;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ variables[receiver.getIndex()] = result;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void jumpIf(BranchingCondition cond, VariableReader operand, BasicBlockReader consequent,
+ BasicBlockReader alternative) {
+ Object a = variables[operand.getIndex()];
+ boolean c;
+ switch (cond) {
+ case EQUAL:
+ c = (Integer) a == 0;
+ break;
+ case NOT_EQUAL:
+ c = (Integer) a != 0;
+ break;
+ case LESS:
+ c = (Integer) a < 0;
+ break;
+ case LESS_OR_EQUAL:
+ c = (Integer) a <= 0;
+ break;
+ case GREATER:
+ c = (Integer) a > 0;
+ break;
+ case GREATER_OR_EQUAL:
+ c = (Integer) a >= 0;
+ break;
+ case NULL:
+ c = a == null;
+ break;
+ case NOT_NULL:
+ c = a != null;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown condition: " + cond);
+ }
+ jump(c ? consequent : alternative);
+ }
+
+ @Override
+ public void jumpIf(BinaryBranchingCondition cond, VariableReader first, VariableReader second,
+ BasicBlockReader consequent, BasicBlockReader alternative) {
+ Object a = variables[first.getIndex()];
+ Object b = variables[second.getIndex()];
+ boolean c;
+ switch (cond) {
+ case EQUAL:
+ c = ((Integer) a).intValue() == (Integer) b;
+ break;
+ case NOT_EQUAL:
+ c = ((Integer) a).intValue() != (Integer) b;
+ break;
+ case REFERENCE_EQUAL:
+ c = a == b;
+ break;
+ case REFERENCE_NOT_EQUAL:
+ c = a != b;
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown condition: " + cond);
+ }
+ jump(c ? consequent : alternative);
+ }
+
+ @Override
+ public void jump(BasicBlockReader target) {
+ Object[] newVariables = variables.clone();
+
+ for (IncomingReader outgoing : outgoings.get(currentBlock.getIndex())) {
+ if (outgoing.getPhi().getBasicBlock() != target) {
+ continue;
+ }
+ newVariables[outgoing.getPhi().getReceiver().getIndex()] = variables[outgoing.getValue().getIndex()];
+ }
+
+ variables = newVariables;
+ currentBlock = target;
+ }
+
+ @Override
+ public void choose(VariableReader condition, List extends SwitchTableEntryReader> table,
+ BasicBlockReader defaultTarget) {
+ int value = (Integer) variables[condition.getIndex()];
+ for (SwitchTableEntryReader entry : table) {
+ if (value == entry.getCondition()) {
+ jump(entry.getTarget());
+ return;
+ }
+ }
+ jump(defaultTarget);
+ }
+
+ @Override
+ public void exit(VariableReader valueToReturn) {
+ state = State.EXITED;
+ result = variables[valueToReturn.getIndex()];
+ }
+
+ @Override
+ public void raise(VariableReader exception) {
+ Throwable e = (Throwable) variables[exception.getIndex()];
+ if (!pickExceptionHandler(e)) {
+ state = State.EXITED;
+ result = e;
+ }
+ }
+
+ @Override
+ public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) {
+ Class> itemJvmType = asJvmClass(itemType);
+ int sizeValue = (int) variables[size.getIndex()];
+ variables[receiver.getIndex()] = Array.newInstance(itemJvmType, sizeValue);
+ }
+
+ @Override
+ public void createArray(VariableReader receiver, ValueType itemType,
+ List extends VariableReader> dimensions) {
+ Class> itemJvmType = asJvmClass(itemType);
+ for (int i = 1; i < dimensions.size(); ++i) {
+ itemJvmType = Array.newInstance(itemJvmType, 0).getClass();
+ }
+ variables[receiver.getIndex()] = createArray(itemJvmType, dimensions, 0);
+ }
+
+ private Object createArray(Class> itemType, List extends VariableReader> dimensions, int dimensionIndex) {
+ int dimensionValue = (int) variables[dimensions.get(dimensionIndex).getIndex()];
+ Object result = Array.newInstance(itemType, dimensionValue);
+ if (dimensionIndex < dimensions.size() - 1) {
+ for (int i = 0; i < dimensionValue; ++i) {
+ Array.set(result, i, createArray(itemType.getComponentType(), dimensions, dimensionIndex + 1));
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public void create(VariableReader receiver, String type) {
+ Class> cls;
+ try {
+ cls = Class.forName(type, false, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Class not found: " + type);
+ }
+ variables[receiver.getIndex()] = objenesis.newInstance(cls);
+ }
+
+ @Override
+ public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
+ ValueType fieldType) {
+ Field jvmField = getJvmField(field);
+
+ Object jvmInstance = instance != null ? variables[instance.getIndex()] : null;
+ Object result;
+ try {
+ result = jvmField.get(jvmInstance);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Can't get field value: " + field);
+ }
+
+ variables[receiver.getIndex()] = result;
+ }
+
+ @Override
+ public void putField(VariableReader instance, FieldReference field, VariableReader value, ValueType fieldType) {
+ Field jvmField = getJvmField(field);
+
+ Object jvmInstance = instance != null ? variables[instance.getIndex()] : null;
+ try {
+ jvmField.set(jvmInstance, variables[value.getIndex()]);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("Can't get field value: " + field);
+ }
+ }
+
+ private Field getJvmField(FieldReference field) {
+ Class> cls;
+ try {
+ cls = Class.forName(field.getClassName(), false, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Class not found: " + field.getClassName());
+ }
+
+ Field jvmField;
+ try {
+ jvmField = cls.getDeclaredField(field.getFieldName());
+ } catch (NoSuchFieldException e) {
+ throw new RuntimeException("Field not found: " + field);
+ }
+
+ jvmField.setAccessible(true);
+ return jvmField;
+ }
+
+ @Override
+ public void arrayLength(VariableReader receiver, VariableReader array) {
+ int value = Array.getLength(variables[array.getIndex()]);
+ variables[receiver.getIndex()] = value;
+ }
+
+ @Override
+ public void cloneArray(VariableReader receiver, VariableReader array) {
+ Object jvmArray = variables[array.getIndex()];
+ int length = Array.getLength(jvmArray);
+ Object copy = Array.newInstance(jvmArray.getClass().getComponentType(), length);
+ for (int i = 0; i < length; ++i) {
+ Array.set(copy, i, Array.get(array, i));
+ }
+ variables[receiver.getIndex()] = copy;
+ }
+
+ @Override
+ public void unwrapArray(VariableReader receiver, VariableReader array, ArrayElementType elementType) {
+ variables[receiver.getIndex()] = variables[array.getIndex()];
+ }
+
+ @Override
+ public void getElement(VariableReader receiver, VariableReader array, VariableReader index) {
+ Object jvmArray = variables[array.getIndex()];
+ int indexValue = (Integer) variables[index.getIndex()];
+ variables[receiver.getIndex()] = Array.get(jvmArray, indexValue);
+ }
+
+ @Override
+ public void putElement(VariableReader array, VariableReader index, VariableReader value) {
+ Object jvmArray = variables[array.getIndex()];
+ int indexValue = (Integer) variables[index.getIndex()];
+ Array.set(jvmArray, indexValue, variables[value.getIndex()]);
+ }
+
+ @Override
+ public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
+ List extends VariableReader> arguments, InvocationType type) {
+ Method jvmMethod = asJvmMethod(method);
+ Object[] jvmArgs = new Object[arguments.size()];
+ for (int i = 0; i < jvmArgs.length; ++i) {
+ jvmArgs[i] = variables[arguments.get(i).getIndex()];
+ }
+ Object jvmInstance = instance != null ? variables[instance.getIndex()] : null;
+ Object result;
+ try {
+ result = jvmMethod.invoke(jvmInstance, jvmArgs);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException("Error calling method " + method, e);
+ }
+ if (receiver != null) {
+ variables[receiver.getIndex()] = result;
+ }
+ }
+
+ private Method asJvmMethod(MethodReference method) {
+ Class> cls;
+ try {
+ cls = Class.forName(method.getClassName(), false, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Can't find class " + method.getClassName());
+ }
+
+ Class>[] jvmParameters = new Class[method.parameterCount()];
+ for (int i = 0; i < method.parameterCount(); ++i) {
+ jvmParameters[i] = asJvmClass(method.parameterType(i));
+ }
+ Class> jvmReturnType = asJvmClass(method.getReturnType());
+ for (Method jvmMethod : cls.getDeclaredMethods()) {
+ if (Arrays.equals(jvmMethod.getParameterTypes(), jvmParameters)
+ && jvmReturnType.equals(jvmMethod.getReturnType())) {
+ return jvmMethod;
+ }
+ }
+
+ throw new RuntimeException("Method not found: " + method);
+ }
+
+ @Override
+ public void invokeDynamic(VariableReader receiver, VariableReader instance, MethodDescriptor method,
+ List extends VariableReader> arguments, MethodHandle bootstrapMethod,
+ List bootstrapArguments) {
+ throw new RuntimeException("InvokeDynamic is not supported");
+ }
+
+ @Override
+ public void isInstance(VariableReader receiver, VariableReader value, ValueType type) {
+ Object jvmValue = variables[value.getIndex()];
+ Class> jvmType = asJvmClass(type);
+ variables[receiver.getIndex()] = jvmType.isInstance(jvmValue);
+ }
+
+ @Override
+ public void initClass(String className) {
+ try {
+ Class.forName(className);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("Class not found: " + className);
+ }
+ }
+
+ @Override
+ public void nullCheck(VariableReader receiver, VariableReader value) {
+ Object jvmValue = variables[value.getIndex()];
+ if (jvmValue == null) {
+ throw new NullPointerException();
+ }
+ variables[receiver.getIndex()] = jvmValue;
+ }
+
+ @Override
+ public void monitorEnter(VariableReader objectRef) {
+ }
+
+ @Override
+ public void monitorExit(VariableReader objectRef) {
+ }
+
+ private Class> asJvmClass(ValueType type) {
+ if (type instanceof ValueType.Primitive) {
+ switch (((ValueType.Primitive) type).getKind()) {
+ case BOOLEAN:
+ return boolean.class;
+ case BYTE:
+ return byte.class;
+ case SHORT:
+ return short.class;
+ case CHARACTER:
+ return char.class;
+ case INTEGER:
+ return int.class;
+ case LONG:
+ return long.class;
+ case FLOAT:
+ return float.class;
+ case DOUBLE:
+ return double.class;
+ default:
+ break;
+ }
+ } else if (type instanceof ValueType.Void) {
+ return void.class;
+ } else if (type instanceof ValueType.Array) {
+ Class> itemJvmClass = asJvmClass(((ValueType.Array) type).getItemType());
+ return Array.newInstance(itemJvmClass, 0).getClass();
+ } else if (type instanceof ValueType.Object) {
+ try {
+ Class.forName(((ValueType.Object) type).getClassName(), false, classLoader);
+ } catch (ClassNotFoundException e) {
+ throw new IllegalArgumentException("Class not found: " + type);
+ }
+ }
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ };
+
+ private enum State {
+ EXECUTING,
+ EXITED,
+ THROWN
+ }
+}
diff --git a/core/teavm-core.iml b/core/teavm-core.iml
index f97c35bec..f17c7919e 100644
--- a/core/teavm-core.iml
+++ b/core/teavm-core.iml
@@ -32,5 +32,6 @@
+
\ No newline at end of file
diff --git a/extras-slf4j/teavm-extras-slf4j.iml b/extras-slf4j/teavm-extras-slf4j.iml
index c0b542c8e..7a2455365 100644
--- a/extras-slf4j/teavm-extras-slf4j.iml
+++ b/extras-slf4j/teavm-extras-slf4j.iml
@@ -15,6 +15,7 @@
+
diff --git a/html4j/teavm-html4j.iml b/html4j/teavm-html4j.iml
index d94d778d1..51e5d7983 100644
--- a/html4j/teavm-html4j.iml
+++ b/html4j/teavm-html4j.iml
@@ -24,6 +24,7 @@
+
diff --git a/jso/impl/teavm-jso-impl.iml b/jso/impl/teavm-jso-impl.iml
index 28870086b..1a7a66562 100644
--- a/jso/impl/teavm-jso-impl.iml
+++ b/jso/impl/teavm-jso-impl.iml
@@ -16,6 +16,7 @@
+
diff --git a/metaprogramming/impl/teavm-metaprogramming-impl.iml b/metaprogramming/impl/teavm-metaprogramming-impl.iml
index ba78f6942..7a00afce3 100644
--- a/metaprogramming/impl/teavm-metaprogramming-impl.iml
+++ b/metaprogramming/impl/teavm-metaprogramming-impl.iml
@@ -28,6 +28,7 @@
+
diff --git a/platform/teavm-platform.iml b/platform/teavm-platform.iml
index edf328ac2..42e576e8f 100644
--- a/platform/teavm-platform.iml
+++ b/platform/teavm-platform.iml
@@ -28,6 +28,7 @@
+
diff --git a/samples/async/teavm-samples-async.iml b/samples/async/teavm-samples-async.iml
index afff7f298..8f024ece9 100644
--- a/samples/async/teavm-samples-async.iml
+++ b/samples/async/teavm-samples-async.iml
@@ -28,6 +28,7 @@
+
diff --git a/samples/benchmark/teavm-samples-benchmark.iml b/samples/benchmark/teavm-samples-benchmark.iml
index cdcbc7296..a9218567c 100644
--- a/samples/benchmark/teavm-samples-benchmark.iml
+++ b/samples/benchmark/teavm-samples-benchmark.iml
@@ -38,6 +38,7 @@
+
diff --git a/samples/hello/teavm-samples-hello.iml b/samples/hello/teavm-samples-hello.iml
index 08f923ab9..ae6580c94 100644
--- a/samples/hello/teavm-samples-hello.iml
+++ b/samples/hello/teavm-samples-hello.iml
@@ -28,6 +28,7 @@
+
diff --git a/samples/kotlin/teavm-samples-kotlin.iml b/samples/kotlin/teavm-samples-kotlin.iml
index da681ff4e..d85add9ff 100644
--- a/samples/kotlin/teavm-samples-kotlin.iml
+++ b/samples/kotlin/teavm-samples-kotlin.iml
@@ -32,6 +32,7 @@
+
diff --git a/samples/scala/teavm-samples-scala.iml b/samples/scala/teavm-samples-scala.iml
index 9d3abcbc8..ee90882ee 100644
--- a/samples/scala/teavm-samples-scala.iml
+++ b/samples/scala/teavm-samples-scala.iml
@@ -28,6 +28,7 @@
+
diff --git a/samples/storage/teavm-samples-storage.iml b/samples/storage/teavm-samples-storage.iml
index ce450beb1..a14ad037d 100644
--- a/samples/storage/teavm-samples-storage.iml
+++ b/samples/storage/teavm-samples-storage.iml
@@ -28,6 +28,7 @@
+
diff --git a/samples/video/teavm-samples-video.iml b/samples/video/teavm-samples-video.iml
index ce450beb1..a14ad037d 100644
--- a/samples/video/teavm-samples-video.iml
+++ b/samples/video/teavm-samples-video.iml
@@ -28,6 +28,7 @@
+
diff --git a/tests/teavm-tests.iml b/tests/teavm-tests.iml
index eb265e73c..d806c3b07 100644
--- a/tests/teavm-tests.iml
+++ b/tests/teavm-tests.iml
@@ -15,6 +15,7 @@
+
diff --git a/tools/chrome-rdp/teavm-chrome-rdp.iml b/tools/chrome-rdp/teavm-chrome-rdp.iml
index 995dfdf10..d50747558 100644
--- a/tools/chrome-rdp/teavm-chrome-rdp.iml
+++ b/tools/chrome-rdp/teavm-chrome-rdp.iml
@@ -49,6 +49,7 @@
+
diff --git a/tools/cli/teavm-cli.iml b/tools/cli/teavm-cli.iml
index e67285d0e..431354a90 100644
--- a/tools/cli/teavm-cli.iml
+++ b/tools/cli/teavm-cli.iml
@@ -15,6 +15,7 @@
+
\ No newline at end of file
diff --git a/tools/core/teavm-tooling.iml b/tools/core/teavm-tooling.iml
index cfd432be7..a636e0d00 100644
--- a/tools/core/teavm-tooling.iml
+++ b/tools/core/teavm-tooling.iml
@@ -28,6 +28,7 @@
+
\ No newline at end of file
diff --git a/tools/junit/teavm-junit.iml b/tools/junit/teavm-junit.iml
index cbd5028f7..b99d88a1a 100644
--- a/tools/junit/teavm-junit.iml
+++ b/tools/junit/teavm-junit.iml
@@ -18,6 +18,7 @@
+
diff --git a/tools/maven/plugin/teavm-maven-plugin.iml b/tools/maven/plugin/teavm-maven-plugin.iml
index bfb311088..f65ba1b46 100644
--- a/tools/maven/plugin/teavm-maven-plugin.iml
+++ b/tools/maven/plugin/teavm-maven-plugin.iml
@@ -45,6 +45,7 @@
+