diff --git a/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml b/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml
index f66428094e3..924d69cca1f 100644
--- a/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml
+++ b/annotations/org.ow2.asm_asm-debug-all_4.1/org/objectweb/asm/tree/annotations.xml
@@ -329,6 +329,9 @@
-
+ -
+
+
-
@@ -338,6 +341,9 @@
+ -
+
+
-
diff --git a/src/org/jetbrains/eval4j/interpreter.kt b/src/org/jetbrains/eval4j/interpreter.kt
index e6a75c4ffb1..528606163b5 100644
--- a/src/org/jetbrains/eval4j/interpreter.kt
+++ b/src/org/jetbrains/eval4j/interpreter.kt
@@ -13,7 +13,27 @@ import org.objectweb.asm.tree.MultiANewArrayInsnNode
import org.objectweb.asm.tree.TypeInsnNode
import org.objectweb.asm.tree.JumpInsnNode
import org.objectweb.asm.tree.IincInsnNode
-import org.objectweb.asm.tree.LabelNode
+
+data class MethodDescription(
+ val ownerInternalName: String,
+ val name: String,
+ val desc: String,
+ val isStatic: Boolean
+)
+
+fun MethodDescription(insn: MethodInsnNode): MethodDescription =
+ MethodDescription(
+ insn.owner,
+ insn.name,
+ insn.desc,
+ insn.getOpcode() == INVOKESTATIC
+ )
+
+val MethodDescription.returnType: Type
+ get() = Type.getReturnType(desc)
+
+val MethodDescription.parameterTypes: List
+ get() = Type.getArgumentTypes(desc).toList()
class UnsupportedByteCodeException(message: String) : RuntimeException(message)
@@ -32,11 +52,11 @@ trait Eval {
fun getStaticField(fieldDesc: String): Value
fun setStaticField(fieldDesc: String, newValue: Value)
- fun invokeStaticMethod(methodDesc: String, arguments: List): Value
+ fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List): Value
fun getField(instance: Value, fieldDesc: String): Value
fun setField(instance: Value, fieldDesc: String, newValue: Value)
- fun invokeMethod(instance: Value, methodDesc: String, arguments: List, invokespecial: Boolean = false): Value
+ fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List, invokespecial: Boolean = false): Value
}
class SingleInstructionInterpreter(private val eval: Eval) : Interpreter(ASM4) {
@@ -334,16 +354,15 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter(
}
INVOKEVIRTUAL, INVOKESPECIAL, INVOKEINTERFACE -> {
- val desc = (insn as MethodInsnNode).desc
eval.invokeMethod(
values[0],
- desc,
+ MethodDescription(insn as MethodInsnNode),
values.subList(1, values.size()),
insn.getOpcode() == INVOKESPECIAL
)
}
- INVOKESTATIC -> eval.invokeStaticMethod((insn as MethodInsnNode).desc, values)
+ INVOKESTATIC -> eval.invokeStaticMethod(MethodDescription(insn as MethodInsnNode), values)
INVOKEDYNAMIC -> throw UnsupportedByteCodeException("INVOKEDYNAMIC is not supported")
else -> throw UnsupportedByteCodeException("$insn")
diff --git a/src/org/jetbrains/eval4j/values.kt b/src/org/jetbrains/eval4j/values.kt
index bde6987d79c..c0f93054736 100644
--- a/src/org/jetbrains/eval4j/values.kt
+++ b/src/org/jetbrains/eval4j/values.kt
@@ -74,4 +74,4 @@ val Value.int: Int get() = (this as IntValue).value
val Value.long: Long get() = (this as LongValue).value
val Value.float: Float get() = (this as FloatValue).value
val Value.double: Double get() = (this as DoubleValue).value
-val Value.obj: Any? get() = (this as ObjectValue).value
\ No newline at end of file
+val Value.obj: Any? get() = (this as AbstractValue<*>).value
\ No newline at end of file
diff --git a/test/org/jetbrains/eval4j/test/TestData.java b/test/org/jetbrains/eval4j/test/TestData.java
index 71a5acbd233..ca446e9ace7 100644
--- a/test/org/jetbrains/eval4j/test/TestData.java
+++ b/test/org/jetbrains/eval4j/test/TestData.java
@@ -144,4 +144,12 @@ class TestData {
return i;
}
+ static Object testCall() {
+ return Integer.valueOf(1);
+ }
+
+ static Object testCallWithObject() {
+ return String.valueOf("str");
+ }
+
}
diff --git a/test/org/jetbrains/eval4j/test/main.kt b/test/org/jetbrains/eval4j/test/main.kt
index 5ea8a7f1099..158c3796cd9 100644
--- a/test/org/jetbrains/eval4j/test/main.kt
+++ b/test/org/jetbrains/eval4j/test/main.kt
@@ -8,6 +8,7 @@ import org.jetbrains.eval4j.*
import org.junit.Assert.*
import junit.framework.TestSuite
import junit.framework.TestCase
+import java.lang.reflect.Method
fun suite(): TestSuite {
val suite = TestSuite()
@@ -48,23 +49,12 @@ fun doTest(ownerClass: Class, methodNode: MethodNode): TestCase? {
method.setAccessible(true)
val result = method.invoke(null)
val returnType = Type.getType(method.getReturnType()!!)
- expected = when (returnType.getSort()) {
- Type.VOID -> ValueReturned(VOID_VALUE)
- Type.BOOLEAN -> ValueReturned(boolean(result as Boolean))
- Type.BYTE -> ValueReturned(byte(result as Byte))
- Type.SHORT -> ValueReturned(short(result as Short))
- Type.CHAR -> ValueReturned(char(result as Char))
- Type.INT -> ValueReturned(int(result as Int))
- Type.LONG -> ValueReturned(long(result as Long))
- Type.DOUBLE -> ValueReturned(double(result as Double))
- Type.FLOAT -> ValueReturned(float(result as Float))
- Type.OBJECT -> ValueReturned(if (result == null) NULL_VALUE else ObjectValue(result, returnType))
- else -> {
- println("Unsupported result type: $returnType")
- return null
- }
+ try {
+ expected = ValueReturned(objectToValue(result, returnType))
+ }
+ catch (e: UnsupportedOperationException) {
+ println("Skipping $method: $e")
}
- println("Testing $method")
}
}
}
@@ -88,7 +78,26 @@ fun doTest(ownerClass: Class, methodNode: MethodNode): TestCase? {
}
}
+fun objectToValue(obj: Any?, expectedType: Type): Value {
+ return when (expectedType.getSort()) {
+ Type.VOID -> VOID_VALUE
+ Type.BOOLEAN -> boolean(obj as Boolean)
+ Type.BYTE -> byte(obj as Byte)
+ Type.SHORT -> short(obj as Short)
+ Type.CHAR -> char(obj as Char)
+ Type.INT -> int(obj as Int)
+ Type.LONG -> long(obj as Long)
+ Type.DOUBLE -> double(obj as Double)
+ Type.FLOAT -> float(obj as Float)
+ Type.OBJECT -> if (obj == null) NULL_VALUE else ObjectValue(obj, expectedType)
+ else -> throw UnsupportedOperationException("Unsupported result type: $expectedType")
+ }
+}
+
object REFLECTION_EVAL : Eval {
+
+ val lookup = ReflectionLookup(javaClass().getClassLoader()!!)
+
override fun loadClass(classType: Type): Value {
throw UnsupportedOperationException()
}
@@ -103,6 +112,7 @@ object REFLECTION_EVAL : Eval {
override fun isInsetanceOf(value: Value, targetType: Type): Boolean {
throw UnsupportedOperationException()
}
+
override fun newArray(arrayType: Type, size: Int): Value {
throw UnsupportedOperationException()
}
@@ -118,22 +128,67 @@ object REFLECTION_EVAL : Eval {
override fun setArrayElement(array: Value, index: Value, newValue: Value) {
throw UnsupportedOperationException()
}
+
override fun getStaticField(fieldDesc: String): Value {
throw UnsupportedOperationException()
}
override fun setStaticField(fieldDesc: String, newValue: Value) {
throw UnsupportedOperationException()
}
- override fun invokeStaticMethod(methodDesc: String, arguments: List): Value {
- throw UnsupportedOperationException()
+
+ override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List): Value {
+ assertTrue(methodDesc.isStatic)
+ val owner = lookup.findClass(methodDesc.ownerInternalName)
+ assertNotNull("Class not found: ${methodDesc.ownerInternalName}", owner)
+ val method = owner!!.findMethod(methodDesc)
+ assertNotNull("Method not found: $methodDesc", method)
+ val result = method!!.invoke(null, *arguments.map { v -> v.obj }.copyToArray())
+ return objectToValue(result, methodDesc.returnType)
}
+
override fun getField(instance: Value, fieldDesc: String): Value {
throw UnsupportedOperationException()
}
override fun setField(instance: Value, fieldDesc: String, newValue: Value) {
throw UnsupportedOperationException()
}
- override fun invokeMethod(instance: Value, methodDesc: String, arguments: List, invokespecial: Boolean): Value {
+ override fun invokeMethod(instance: Value, methodDesc: MethodDescription, arguments: List, invokespecial: Boolean): Value {
throw UnsupportedOperationException()
}
-}
\ No newline at end of file
+}
+
+class ReflectionLookup(val classLoader: ClassLoader) {
+ [suppress("UNCHECKED_CAST")]
+ fun findClass(internalName: String): Class? = classLoader.loadClass(internalName.replace('/', '.')) as Class
+}
+
+[suppress("UNCHECKED_CAST")]
+fun Class.findMethod(methodDesc: MethodDescription): Method? {
+ for (declared in getDeclaredMethods()) {
+ if (methodDesc.matches(declared)) return declared
+ }
+
+ val fromSuperClass = (getSuperclass() as Class).findMethod(methodDesc)
+ if (fromSuperClass != null) return fromSuperClass
+
+ for (supertype in getInterfaces()) {
+ val fromSuper = (supertype as Class).findMethod(methodDesc)
+ if (fromSuper != null) return fromSuper
+ }
+
+ return null
+}
+
+fun MethodDescription.matches(method: Method): Boolean {
+ if (name != method.getName()) return false
+
+ val methodParams = method.getParameterTypes()!!
+ if (parameterTypes.size() != methodParams.size) return false
+ for ((i, p) in parameterTypes.withIndices()) {
+ if (!p.matches(methodParams[i])) return false
+ }
+
+ return returnType.matches(method.getReturnType()!!)
+}
+
+fun Type.matches(_class: Class<*>): Boolean = this == Type.getType(_class)
\ No newline at end of file