diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index df82b3e4040..66f33ce475f 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -126,12 +126,36 @@ class IrInterpreter(irModule: IrModuleFragment) { val exceptionName = e::class.java.simpleName val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == exceptionName } ?: irBuiltIns.throwableClass.owner + // TODO do we really need this?... It will point to JVM stdlib + val additionalStack = mutableListOf() + if (e.stackTrace.any { it.className == "java.lang.invoke.MethodHandle" }) { + for ((index, stackTraceElement) in e.stackTrace.withIndex()) { + if (stackTraceElement.methodName == "invokeWithArguments") { + additionalStack.addAll(e.stackTrace.slice(0 until index).reversed().map { "at $it" }) + break + } + } + + var cause = e.cause + val lastNeededValue = e.stackTrace.first().className + "." + e.stackTrace.first().methodName + while (cause != null) { + for ((causeStackIndex, causeStackTraceElement) in cause.stackTrace.withIndex()) { + val currentStackTraceValue = causeStackTraceElement.className + "." + causeStackTraceElement.methodName + if (currentStackTraceValue== lastNeededValue) { + cause.stackTrace = cause.stackTrace.sliceArray(0 until causeStackIndex).reversedArray() + break + } + } + cause = cause.cause + } + } + val exceptionState = when { // this block is used to replace jvm null pointer exception with common one // TODO figure something better irExceptionClass == irBuiltIns.throwableClass.owner && exceptionName == "KotlinNullPointerException" -> - ExceptionState(e, irExceptions.first { it.name.asString() == "NullPointerException" }, stackTrace) - else -> ExceptionState(e, irExceptionClass, stackTrace) + ExceptionState(e, irExceptions.first { it.name.asString() == "NullPointerException" }, stackTrace + additionalStack) + else -> ExceptionState(e, irExceptionClass, stackTrace + additionalStack) } data.pushReturnValue(exceptionState) @@ -161,7 +185,6 @@ class IrInterpreter(irModule: IrModuleFragment) { } private suspend fun MethodHandle.invokeMethod(irFunction: IrFunction, data: Frame): Code { - // TODO try catch val result = this.invokeWithArguments(irFunction.getArgsForMethodInvocation(data)) data.pushReturnValue(result.toState(result.getType(irFunction.returnType))) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt index 0d40c4e454a..2a0de75a21a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/stack/State.kt @@ -318,14 +318,13 @@ class ExceptionState private constructor( constructor( exception: Throwable, irClass: IrClass, stackTrace: List - ) : this(irClass, evaluateFields(exception, irClass), stackTrace) { - if (irClass.name.asString() == "Throwable" && exception::class.java.name != "Throwable") { + ) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace) { + if (irClass.name.asString() != exception::class.java.simpleName) { // ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy - exceptionHierarchy += exception::class.java.name + this.exceptionFqName = exception::class.java.name + exceptionHierarchy += this.exceptionFqName generateSequence(exception::class.java.superclass) { it.superclass }.forEach { exceptionHierarchy += it.name } exceptionHierarchy.removeAt(exceptionHierarchy.lastIndex) // remove unnecessary java.lang.Object - - this.exceptionFqName = exception::class.java.name } } @@ -333,8 +332,8 @@ class ExceptionState private constructor( val cause = (wrapper?.value as? Throwable)?.cause as? ExceptionData setCause(cause?.state) if (getMessage().value == null && cause != null) { - val causeMessage = cause.state.getMessage().value?.let { ": $it" } ?: "" - setMessage("$exceptionFqName$causeMessage") + val causeMessage = cause.state.exceptionFqName + (cause.state.getMessage().value?.let { ": $it" } ?: "") + setMessage(causeMessage) } } @@ -363,7 +362,7 @@ class ExceptionState private constructor( val message = getMessage().value.let { if (it?.isNotEmpty() == true) ": $it" else "" } val prefix = if (stackTrace.isNotEmpty()) "\n\t" else "" val postfix = if (stackTrace.size > 10) "\n\t..." else "" - val causeMessage = getCause()?.getFullDescription()?.let { "\nCaused by: $it" } ?: "" + val causeMessage = getCause()?.getFullDescription()?.replaceFirst("Exception ", "\nCaused by: ") ?: "" return "Exception $exceptionFqName$message" + stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) + causeMessage @@ -382,13 +381,14 @@ class ExceptionState private constructor( ?: this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty } - private fun evaluateFields(exception: Throwable, irClass: IrClass): MutableList { + private fun evaluateFields(exception: Throwable, irClass: IrClass, stackTrace: List): MutableList { val messageProperty = irClass.getPropertyByName("message") val causeProperty = irClass.getPropertyByName("cause") val messageVar = Variable(messageProperty.descriptor, exception.message.toState(messageProperty.getter!!.returnType)) - // TODO load stack trace from cause - val causeVar = exception.cause?.let { Variable(causeProperty.descriptor, ExceptionState(it, irClass, listOf())) } + val causeVar = exception.cause?.let { + Variable(causeProperty.descriptor, ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" })) + } return listOfNotNull(messageVar, causeVar).toMutableList() } }