Support NPE and exceptions thrown from methods being called
This commit is contained in:
@@ -59,7 +59,7 @@ class LongValue(value: Long): AbstractValue<Long>(value, Type.LONG_TYPE)
|
|||||||
class FloatValue(value: Float): AbstractValue<Float>(value, Type.FLOAT_TYPE)
|
class FloatValue(value: Float): AbstractValue<Float>(value, Type.FLOAT_TYPE)
|
||||||
class DoubleValue(value: Double): AbstractValue<Double>(value, Type.DOUBLE_TYPE)
|
class DoubleValue(value: Double): AbstractValue<Double>(value, Type.DOUBLE_TYPE)
|
||||||
class ObjectValue(value: Any?, asmType: Type): AbstractValue<Any?>(value, asmType)
|
class ObjectValue(value: Any?, asmType: Type): AbstractValue<Any?>(value, asmType)
|
||||||
class NewObjectValue(val _class: Class<Any?>, asmType: Type): AbstractValueBase<Any?>(asmType) {
|
class NewObjectValue(val _class: Class<*>, asmType: Type): AbstractValueBase<Any?>(asmType) {
|
||||||
override var value: Any? = null
|
override var value: Any? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ class TestData {
|
|||||||
static C newC() {
|
static C newC() {
|
||||||
return new C();
|
return new C();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void throwException() {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getInstanceField() {
|
static int getInstanceField() {
|
||||||
@@ -252,4 +256,25 @@ class TestData {
|
|||||||
static String classLiteral() {
|
static String classLiteral() {
|
||||||
return String.class.toString();
|
return String.class.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int callThrowingMethod() {
|
||||||
|
try {
|
||||||
|
C.throwException();
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int NPE() {
|
||||||
|
try {
|
||||||
|
Object x = null;
|
||||||
|
x.toString();
|
||||||
|
}
|
||||||
|
catch (NullPointerException e) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import junit.framework.TestCase
|
|||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Constructor
|
import java.lang.reflect.Constructor
|
||||||
|
import java.lang.reflect.InvocationTargetException
|
||||||
|
|
||||||
fun suite(): TestSuite {
|
fun suite(): TestSuite {
|
||||||
val suite = TestSuite()
|
val suite = TestSuite()
|
||||||
@@ -132,13 +133,38 @@ object REFLECTION_EVAL : Eval {
|
|||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> mayThrow(f: () -> T): T {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
return f()
|
||||||
|
}
|
||||||
|
catch (ite: InvocationTargetException) {
|
||||||
|
throw ite.getCause() ?: ite
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
throw ThrownFromEvalException(ObjectValue(e, Type.getType(e.javaClass)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Any?.checkNull(): Any {
|
||||||
|
if (this == null) {
|
||||||
|
throw ThrownFromEvalException(ObjectValue(NullPointerException(), Type.getType(javaClass<NullPointerException>())))
|
||||||
|
}
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
override fun getStaticField(fieldDesc: FieldDescription): Value {
|
override fun getStaticField(fieldDesc: FieldDescription): Value {
|
||||||
val result = findStaticField(fieldDesc).get(null)
|
val field = findStaticField(fieldDesc)
|
||||||
|
|
||||||
|
val result = mayThrow {field.get(null)}
|
||||||
return objectToValue(result, fieldDesc.fieldType)
|
return objectToValue(result, fieldDesc.fieldType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) {
|
override fun setStaticField(fieldDesc: FieldDescription, newValue: Value) {
|
||||||
findStaticField(fieldDesc).set(null, newValue.obj)
|
val field = findStaticField(fieldDesc)
|
||||||
|
val obj = newValue.obj
|
||||||
|
mayThrow {field.set(null, obj)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findStaticField(fieldDesc: FieldDescription): Field {
|
fun findStaticField(fieldDesc: FieldDescription): Field {
|
||||||
@@ -153,36 +179,36 @@ object REFLECTION_EVAL : Eval {
|
|||||||
assertTrue(methodDesc.isStatic)
|
assertTrue(methodDesc.isStatic)
|
||||||
val method = findClass(methodDesc).findMethod(methodDesc)
|
val method = findClass(methodDesc).findMethod(methodDesc)
|
||||||
assertNotNull("Method not found: $methodDesc", method)
|
assertNotNull("Method not found: $methodDesc", method)
|
||||||
val result = method!!.invoke(null, *arguments.map { v -> v.obj }.copyToArray())
|
val args = arguments.map { v -> v.obj }.copyToArray()
|
||||||
|
val result = mayThrow {method!!.invoke(null, *args)}
|
||||||
return objectToValue(result, methodDesc.returnType)
|
return objectToValue(result, methodDesc.returnType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findClass(memberDesc: MemberDescription): Class<Any?> = findClass(memberDesc.ownerInternalName)
|
fun findClass(memberDesc: MemberDescription): Class<Any> = findClass(memberDesc.ownerInternalName)
|
||||||
|
|
||||||
fun findClass(internalName: String): Class<Any?> {
|
fun findClass(internalName: String): Class<Any> {
|
||||||
val owner = lookup.findClass(internalName)
|
val owner = lookup.findClass(internalName)
|
||||||
assertNotNull("Class not found: ${internalName}", owner)
|
assertNotNull("Class not found: ${internalName}", owner)
|
||||||
return owner!!
|
return owner!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
|
override fun getField(instance: Value, fieldDesc: FieldDescription): Value {
|
||||||
val obj = instance.obj
|
val obj = instance.obj.checkNull()
|
||||||
val field = findInstanceField(obj, fieldDesc)
|
val field = findInstanceField(obj, fieldDesc)
|
||||||
|
|
||||||
return objectToValue(field.get(obj), fieldDesc.fieldType)
|
return objectToValue(mayThrow {field.get(obj)}, fieldDesc.fieldType)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
|
override fun setField(instance: Value, fieldDesc: FieldDescription, newValue: Value) {
|
||||||
val obj = instance.obj
|
val obj = instance.obj.checkNull()
|
||||||
val field = findInstanceField(obj, fieldDesc)
|
val field = findInstanceField(obj, fieldDesc)
|
||||||
|
|
||||||
field.set(obj, newValue.obj)
|
val newObj = newValue.obj
|
||||||
|
mayThrow {field.set(obj, newObj)}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun findInstanceField(obj: Any?, fieldDesc: FieldDescription): Field {
|
fun findInstanceField(obj: Any, fieldDesc: FieldDescription): Field {
|
||||||
if (obj == null) throw NullPointerException()
|
val _class = obj.javaClass
|
||||||
|
|
||||||
val _class: Class<Any?> = obj.javaClass
|
|
||||||
val field = _class.findField(fieldDesc)
|
val field = _class.findField(fieldDesc)
|
||||||
assertNotNull("Field not found: $fieldDesc", field)
|
assertNotNull("Field not found: $fieldDesc", field)
|
||||||
return field!!
|
return field!!
|
||||||
@@ -192,11 +218,12 @@ object REFLECTION_EVAL : Eval {
|
|||||||
if (invokespecial) {
|
if (invokespecial) {
|
||||||
if (methodDesc.name == "<init>") {
|
if (methodDesc.name == "<init>") {
|
||||||
// Constructor call
|
// Constructor call
|
||||||
val _class = (instance as NewObjectValue)._class
|
[suppress("UNCHECKED_CAST")]
|
||||||
|
val _class = (instance as NewObjectValue)._class as Class<Any>
|
||||||
val ctor = _class.findConstructor(methodDesc)
|
val ctor = _class.findConstructor(methodDesc)
|
||||||
assertNotNull("Constructor not found: $methodDesc", ctor)
|
assertNotNull("Constructor not found: $methodDesc", ctor)
|
||||||
val args = arguments.map { v -> v.obj }.copyToArray()
|
val args = arguments.map { v -> v.obj }.copyToArray()
|
||||||
val result = ctor!!.newInstance(*args)
|
val result = mayThrow {ctor!!.newInstance(*args)}
|
||||||
instance.value = result
|
instance.value = result
|
||||||
return objectToValue(result, instance.asmType)
|
return objectToValue(result, instance.asmType)
|
||||||
}
|
}
|
||||||
@@ -205,30 +232,31 @@ object REFLECTION_EVAL : Eval {
|
|||||||
throw UnsupportedOperationException("invokespecial is not suported yet")
|
throw UnsupportedOperationException("invokespecial is not suported yet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val obj = instance.obj
|
val obj = instance.obj.checkNull()
|
||||||
val method = obj.javaClass.findMethod(methodDesc)
|
val method = obj.javaClass.findMethod(methodDesc)
|
||||||
assertNotNull("Method not found: $methodDesc", method)
|
assertNotNull("Method not found: $methodDesc", method)
|
||||||
val result = method!!.invoke(obj, *arguments.map { v -> v.obj }.copyToArray())
|
val args = arguments.map { v -> v.obj }.copyToArray()
|
||||||
|
val result = mayThrow {method!!.invoke(obj, *args)}
|
||||||
return objectToValue(result, methodDesc.returnType)
|
return objectToValue(result, methodDesc.returnType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReflectionLookup(val classLoader: ClassLoader) {
|
class ReflectionLookup(val classLoader: ClassLoader) {
|
||||||
[suppress("UNCHECKED_CAST")]
|
[suppress("UNCHECKED_CAST")]
|
||||||
fun findClass(internalName: String): Class<Any?>? = classLoader.loadClass(internalName.replace('/', '.')) as Class<Any?>
|
fun findClass(internalName: String): Class<Any>? = classLoader.loadClass(internalName.replace('/', '.')) as Class<Any>
|
||||||
}
|
}
|
||||||
|
|
||||||
[suppress("UNCHECKED_CAST")]
|
[suppress("UNCHECKED_CAST")]
|
||||||
fun Class<Any?>.findMethod(methodDesc: MethodDescription): Method? {
|
fun Class<Any>.findMethod(methodDesc: MethodDescription): Method? {
|
||||||
for (declared in getDeclaredMethods()) {
|
for (declared in getDeclaredMethods()) {
|
||||||
if (methodDesc.matches(declared)) return declared
|
if (methodDesc.matches(declared)) return declared
|
||||||
}
|
}
|
||||||
|
|
||||||
val fromSuperClass = (getSuperclass() as Class<Any?>).findMethod(methodDesc)
|
val fromSuperClass = (getSuperclass() as Class<Any>).findMethod(methodDesc)
|
||||||
if (fromSuperClass != null) return fromSuperClass
|
if (fromSuperClass != null) return fromSuperClass
|
||||||
|
|
||||||
for (supertype in getInterfaces()) {
|
for (supertype in getInterfaces()) {
|
||||||
val fromSuper = (supertype as Class<Any?>).findMethod(methodDesc)
|
val fromSuper = (supertype as Class<Any>).findMethod(methodDesc)
|
||||||
if (fromSuper != null) return fromSuper
|
if (fromSuper != null) return fromSuper
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,7 +264,7 @@ fun Class<Any?>.findMethod(methodDesc: MethodDescription): Method? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[suppress("UNCHECKED_CAST")]
|
[suppress("UNCHECKED_CAST")]
|
||||||
fun Class<Any?>.findConstructor(methodDesc: MethodDescription): Constructor<Any?>? {
|
fun Class<Any>.findConstructor(methodDesc: MethodDescription): Constructor<Any?>? {
|
||||||
for (declared in getDeclaredConstructors()) {
|
for (declared in getDeclaredConstructors()) {
|
||||||
if (methodDesc.matches(declared)) return declared as Constructor<Any?>
|
if (methodDesc.matches(declared)) return declared as Constructor<Any?>
|
||||||
}
|
}
|
||||||
@@ -266,19 +294,19 @@ fun MethodDescription.matches(method: Method): Boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[suppress("UNCHECKED_CAST")]
|
[suppress("UNCHECKED_CAST")]
|
||||||
fun Class<Any?>.findField(fieldDesc: FieldDescription): Field? {
|
fun Class<Any>.findField(fieldDesc: FieldDescription): Field? {
|
||||||
for (declared in getDeclaredFields()) {
|
for (declared in getDeclaredFields()) {
|
||||||
if (fieldDesc.matches(declared)) return declared
|
if (fieldDesc.matches(declared)) return declared
|
||||||
}
|
}
|
||||||
|
|
||||||
val superclass = getSuperclass()
|
val superclass = getSuperclass()
|
||||||
if (superclass != null) {
|
if (superclass != null) {
|
||||||
val fromSuperClass = (superclass as Class<Any?>).findField(fieldDesc)
|
val fromSuperClass = (superclass as Class<Any>).findField(fieldDesc)
|
||||||
if (fromSuperClass != null) return fromSuperClass
|
if (fromSuperClass != null) return fromSuperClass
|
||||||
}
|
}
|
||||||
|
|
||||||
for (supertype in getInterfaces()) {
|
for (supertype in getInterfaces()) {
|
||||||
val fromSuper = (supertype as Class<Any?>).findField(fieldDesc)
|
val fromSuper = (supertype as Class<Any>).findField(fieldDesc)
|
||||||
if (fromSuper != null) return fromSuper
|
if (fromSuper != null) return fromSuper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user