Eval4j: add exception type in ExceptionThrown result

This commit is contained in:
Natalia Ukhorskaya
2014-06-20 12:20:02 +04:00
parent 28cbe95e0c
commit 1687b9cbca
8 changed files with 75 additions and 24 deletions
@@ -194,7 +194,7 @@ class SingleInstructionInterpreter(private val eval: Eval) : Interpreter<Value>(
ObjectValue(value.obj(), targetType)
}
else {
throwEvalException(ClassCastException("Value '$value' cannot be cast to $targetType"))
throwEvalException(ClassCastException("${value.asmType.getClassName()} cannot be cast to ${targetType.getClassName()}"))
}
}
@@ -26,13 +26,20 @@ import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
import org.jetbrains.org.objectweb.asm.util.Printer
import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode
import java.util.ArrayList
import org.jetbrains.eval4j.ExceptionThrown.ExceptionKind
trait InterpreterResult {
override fun toString(): String
}
class ExceptionThrown(val exception: Value): InterpreterResult {
override fun toString(): String = "Thrown $exception"
class ExceptionThrown(val exception: Value, val kind: ExceptionKind): InterpreterResult {
override fun toString(): String = "Thrown $exception: $kind"
enum class ExceptionKind {
FROM_EVALUATED_CODE
FROM_EVALUATOR
BROKEN_CODE
}
}
data class ValueReturned(val result: Value): InterpreterResult {
@@ -60,10 +67,13 @@ trait InterpretationEventHandler {
fun exceptionCaught(currentState: Frame<Value>, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
}
class ThrownFromEvalException(cause: Throwable): RuntimeException(cause) {
abstract class ThrownFromEvalExceptionBase(cause: Throwable): RuntimeException(cause) {
override fun toString(): String = "Thrown by evaluator: ${getCause()}"
}
class BrokenCode(cause: Throwable): ThrownFromEvalExceptionBase(cause)
class ThrownFromEvalException(cause: Throwable): ThrownFromEvalExceptionBase(cause)
class ThrownFromEvaluatedCodeException(val exception: Value): RuntimeException() {
override fun toString(): String = "Thrown from evaluated code: $exception"
}
@@ -206,7 +216,7 @@ fun interpreterLoop(
val handled = handler.exceptionThrown(frame, currentInsn, exceptionValue)
if (handled != null) return handled
if (exceptionCaught(exceptionValue)) continue
return ExceptionThrown(exceptionValue)
return ExceptionThrown(exceptionValue, ExceptionKind.FROM_EVALUATED_CODE)
}
// Workaround for a bug in Kotlin: NoPatterMatched exception is thrown otherwise!
@@ -216,20 +226,22 @@ fun interpreterLoop(
try {
frame.execute(currentInsn, interpreter)
}
catch (e: ThrownFromEvalException) {
catch (e: ThrownFromEvalExceptionBase) {
val exception = e.getCause()!!
val exceptionValue = ObjectValue(exception, Type.getType(exception.javaClass))
val handled = handler.exceptionThrown(frame, currentInsn,
exceptionValue)
if (handled != null) return handled
if (exceptionFromEvalCaught(exception, exceptionValue)) continue
return ExceptionThrown(exceptionValue)
val exceptionType = if (e is BrokenCode) ExceptionKind.BROKEN_CODE else ExceptionKind.FROM_EVALUATOR
return ExceptionThrown(exceptionValue, exceptionType)
}
catch (e: ThrownFromEvaluatedCodeException) {
val handled = handler.exceptionThrown(frame, currentInsn, e.exception)
if (handled != null) return handled
if (exceptionCaught(e.exception)) continue
return ExceptionThrown(e.exception)
return ExceptionThrown(e.exception, ExceptionKind.FROM_EVALUATED_CODE)
}
}
}
@@ -245,7 +257,7 @@ fun interpreterLoop(
}
}
private fun <T: Value> Frame<T>.getStackTop(i: Int = 0) = this.getStack(this.getStackSize() - 1 - i) ?: throwEvalException(IllegalArgumentException("Couldn't get value with index = $i from top of stack"))
private fun <T: Value> Frame<T>.getStackTop(i: Int = 0) = this.getStack(this.getStackSize() - 1 - i) ?: throwBrokenCodeException(IllegalArgumentException("Couldn't get value with index = $i from top of stack"))
// Copied from org.jetbrains.org.objectweb.asm.tree.analysis.Analyzer.analyze()
fun computeHandlers(m: MethodNode): Array<out List<TryCatchBlockNode>?> {
+10 -7
View File
@@ -138,7 +138,7 @@ class JDIEval(
val _class = fieldDesc.ownerType.asReferenceType()
val field = _class.fieldByName(fieldDesc.name)
if (field == null) {
throwEvalException(NoSuchFieldError("Field not found: $fieldDesc"))
throwBrokenCodeException(NoSuchFieldError("Field not found: $fieldDesc"))
}
return field
}
@@ -146,7 +146,7 @@ class JDIEval(
private fun findStaticField(fieldDesc: FieldDescription): jdi.Field {
val field = findField(fieldDesc)
if (!field.isStatic()) {
throwEvalException(NoSuchFieldError("Field is not static: $fieldDesc"))
throwBrokenCodeException(NoSuchFieldError("Field is not static: $fieldDesc"))
}
return field
}
@@ -160,12 +160,12 @@ class JDIEval(
val field = findStaticField(fieldDesc)
if (field.isFinal()) {
throwEvalException(NoSuchFieldError("Can't modify a final field: $field"))
throwBrokenCodeException(NoSuchFieldError("Can't modify a final field: $field"))
}
val _class = field.declaringType()
if (_class !is jdi.ClassType) {
throwEvalException(NoSuchFieldError("Can't a field in a non-class: $field"))
throwBrokenCodeException(NoSuchFieldError("Can't a field in a non-class: $field"))
}
val jdiValue = newValue.asJdiValue(vm, field.`type`().asType())
@@ -181,7 +181,7 @@ class JDIEval(
else -> _class.methodsByName(methodDesc.name, methodDesc.desc)
}
if (method.isEmpty()) {
throwEvalException(NoSuchMethodError("Method not found: $methodDesc"))
throwBrokenCodeException(NoSuchMethodError("Method not found: $methodDesc"))
}
return method[0]
}
@@ -189,10 +189,10 @@ class JDIEval(
override fun invokeStaticMethod(methodDesc: MethodDescription, arguments: List<Value>): Value {
val method = findMethod(methodDesc)
if (!method.isStatic()) {
throwEvalException(NoSuchMethodError("Method is not static: $methodDesc"))
throwBrokenCodeException(NoSuchMethodError("Method is not static: $methodDesc"))
}
val _class = method.declaringType()
if (_class !is jdi.ClassType) throwEvalException(NoSuchMethodError("Static method is a non-class type: $method"))
if (_class !is jdi.ClassType) throwBrokenCodeException(NoSuchMethodError("Static method is a non-class type: $method"))
val args = mapArguments(arguments, method.safeArgumentTypes())
val result = mayThrow { _class.invokeMethod(thread, method, args, invokePolicy) }
@@ -277,4 +277,7 @@ fun <T> mayThrow(f: () -> T): T {
catch (e: jdi.InvocationException) {
throw ThrownFromEvaluatedCodeException(e.exception().asValue())
}
catch (e: Throwable) {
throwBrokenCodeException(e)
}
}
@@ -129,4 +129,8 @@ fun <T: Any> T?.checkNull(): T {
fun throwEvalException(e: Throwable): Nothing {
throw ThrownFromEvalException(e)
}
fun throwBrokenCodeException(e: Throwable): Nothing {
throw BrokenCode(e)
}
@@ -29,6 +29,7 @@ import java.io.File
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.eval4j.test.getTestName
import com.sun.jdi.ObjectReference
val DEBUGEE_CLASS = javaClass<Debugee>()
@@ -133,14 +134,14 @@ fun suite(): TestSuite {
}
try {
if (value is ExceptionThrown) {
val str = value.exception.jdiObj.callToString()
System.err.println("Exception: $str")
}
if (expected is ValueReturned && value is ValueReturned && value.result is ObjectValue) {
assertEquals(expected.result.obj().toString(), value.result.jdiObj.callToString())
}
else if (expected is ExceptionThrown && value is ExceptionThrown) {
val valueObj = value.exception.obj()
val actual = if (valueObj is ObjectReference) valueObj.callToString() else valueObj.toString()
assertEquals(expected.exception.obj().toString(), actual)
}
else {
assertEquals(expected, value)
}
@@ -710,6 +710,27 @@ class TestData extends BaseTestData {
private String invokeSpecialPrivateFun(String s) { return "Base"; }
static Throwable exception1() {
throw new IllegalStateException();
}
static void exception2() {
new ExceptionsTest().f1();
}
static void exceptionClassCast() {
ExceptionsTest.Derived test = (ExceptionsTest.Derived) new ExceptionsTest.Base();
}
static class ExceptionsTest {
void f1() {
throw new IllegalStateException();
}
static class Base {}
static class Derived extends Base {}
}
public TestData() {
}
}
@@ -45,7 +45,13 @@ fun suite(): TestSuite = buildTestSuite {
),
REFLECTION_EVAL
)
assertEquals(expected, value)
if (expected is ExceptionThrown && value is ExceptionThrown) {
assertEquals(expected.exception.toString(), value.exception.toString())
}
else {
assertEquals(expected.toString(), value.toString())
}
}
}
@@ -72,14 +72,18 @@ fun buildTestCase(ownerClass: Class<TestData>,
}
else {
method.setAccessible(true)
val result = method.invoke(if (isStatic) null else ownerClass.newInstance())
val returnType = Type.getType(method.getReturnType()!!)
try {
val result = method.invoke(if (isStatic) null else ownerClass.newInstance())
val returnType = Type.getType(method.getReturnType()!!)
expected = ValueReturned(objectToValue(result, returnType))
}
catch (e: UnsupportedOperationException) {
println("Skipping $method: $e")
}
catch (e: Throwable) {
val cause = e.getCause() ?: e
expected = ExceptionThrown(objectToValue(cause, Type.getType(cause.javaClass)), ExceptionThrown.ExceptionKind.FROM_EVALUATOR)
}
}
}
}