Add support for cause field in exceptions
This commit is contained in:
+1
-1
@@ -146,7 +146,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
||||
if (irFunction.fileOrNull != null) {
|
||||
val fileName = irFunction.file.name
|
||||
val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1
|
||||
stackTrace += "at ${fileName.replace(".kt", "Kt")}.${irFunction.fqNameForIrSerialization}($fileName:$lineNum)"
|
||||
stackTrace += "at ${fileName.replace(".kt", "Kt").capitalize()}.${irFunction.fqNameForIrSerialization}($fileName:$lineNum)"
|
||||
}
|
||||
|
||||
if (stackTrace.size == MAX_STACK_SIZE) {
|
||||
|
||||
+9
-5
@@ -6,10 +6,7 @@
|
||||
package org.jetbrains.kotlin.backend.common.interpreter
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation
|
||||
import org.jetbrains.kotlin.backend.common.interpreter.stack.Frame
|
||||
import org.jetbrains.kotlin.backend.common.interpreter.stack.Primitive
|
||||
import org.jetbrains.kotlin.backend.common.interpreter.stack.State
|
||||
import org.jetbrains.kotlin.backend.common.interpreter.stack.Wrapper
|
||||
import org.jetbrains.kotlin.backend.common.interpreter.stack.*
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
@@ -173,7 +170,14 @@ fun IrType.getFqName(): String? {
|
||||
}
|
||||
|
||||
fun IrFunction.getArgsForMethodInvocation(data: Frame): List<Any?> {
|
||||
val argsValues = data.getAll().map { (it.state as? Wrapper)?.value ?: (it.state as Primitive<*>).value }.toMutableList()
|
||||
val argsValues = data.getAll().map {
|
||||
when (val state = it.state) {
|
||||
is ExceptionState -> state.getThisAsCauseForException()
|
||||
is Wrapper -> state.value
|
||||
is Primitive<*> -> state.value
|
||||
else -> throw AssertionError("${state::class} is unsupported as argument for wrapper method invocation")
|
||||
}
|
||||
}.toMutableList()
|
||||
|
||||
// TODO if vararg isn't last parameter
|
||||
// must convert vararg array into separated elements for correct invoke
|
||||
|
||||
+49
-6
@@ -300,10 +300,21 @@ class ExceptionState private constructor(
|
||||
instance = this
|
||||
this.stackTrace = stackTrace.reversed()
|
||||
if (!this::exceptionFqName.isInitialized) this.exceptionFqName = irClass.fqNameForIrSerialization.asString()
|
||||
|
||||
if (fields.none { it.descriptor.equalTo(messageProperty.descriptor) }) {
|
||||
setMessage()
|
||||
}
|
||||
}
|
||||
|
||||
constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace)
|
||||
constructor(wrapper: Wrapper, stackTrace: List<String>) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace)
|
||||
constructor(common: Common, stackTrace: List<String>) : this(common.irClass, common.fields, stackTrace) {
|
||||
var wrapperSuperType: Complex? = common
|
||||
while (wrapperSuperType != null && wrapperSuperType !is Wrapper) wrapperSuperType = (wrapperSuperType as Common).superType
|
||||
setUpCauseIfNeeded(wrapperSuperType as? Wrapper)
|
||||
}
|
||||
|
||||
constructor(wrapper: Wrapper, stackTrace: List<String>) : this(wrapper.value as Throwable, wrapper.irClass, stackTrace) {
|
||||
setUpCauseIfNeeded(wrapper)
|
||||
}
|
||||
|
||||
constructor(
|
||||
exception: Throwable, irClass: IrClass, stackTrace: List<String>
|
||||
@@ -318,6 +329,15 @@ class ExceptionState private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpCauseIfNeeded(wrapper: Wrapper?) {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
fun isSubtypeOf(ancestor: IrClass): Boolean {
|
||||
if (exceptionHierarchy.isNotEmpty()) {
|
||||
return exceptionHierarchy.any { it.contains(ancestor.name.asString()) }
|
||||
@@ -325,18 +345,32 @@ class ExceptionState private constructor(
|
||||
return irClass.isSubclassOf(ancestor)
|
||||
}
|
||||
|
||||
fun getMessage(): Primitive<String> = getState(messageProperty.descriptor) as Primitive<String>
|
||||
private fun setMessage(messageValue: String? = null) {
|
||||
setState(Variable(messageProperty.descriptor, Primitive(messageValue, messageProperty.getter!!.returnType)))
|
||||
}
|
||||
|
||||
fun getCause(): ExceptionState = getState(causeProperty.descriptor) as ExceptionState
|
||||
private fun setCause(causeValue: State?) {
|
||||
setState(Variable(causeProperty.descriptor, causeValue ?: Primitive<Throwable?>(null, causeProperty.getter!!.returnType)))
|
||||
}
|
||||
|
||||
fun getMessage(): Primitive<String?> = getState(messageProperty.descriptor) as Primitive<String?>
|
||||
fun getMessageWithName(): String = getMessage().value?.let { "$exceptionFqName: $it" } ?: exceptionFqName
|
||||
|
||||
fun getCause(): ExceptionState? = getState(causeProperty.descriptor)?.let { if (it is ExceptionState) it else null }
|
||||
|
||||
fun getFullDescription(): String {
|
||||
val message = getMessage().value.let { if (it.isNotEmpty()) ": $it" else ""}
|
||||
// TODO remainder of the stack trace with "..."
|
||||
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" } ?: ""
|
||||
return "Exception $exceptionFqName$message" +
|
||||
stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix)
|
||||
stackTrace.subList(0, min(stackTrace.size, 10)).joinToString(separator = "\n\t", prefix = prefix, postfix = postfix) +
|
||||
causeMessage
|
||||
}
|
||||
|
||||
fun getThisAsCauseForException() = ExceptionData(this)
|
||||
|
||||
override fun copy(): State {
|
||||
return ExceptionState(irClass, fields, stackTrace).apply { this@apply.instance = this@ExceptionState.instance }
|
||||
}
|
||||
@@ -358,4 +392,13 @@ class ExceptionState private constructor(
|
||||
return listOfNotNull(messageVar, causeVar).toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove this data class and make ExceptionState a child of Throwable
|
||||
// this is possible by converting Complex to an interface
|
||||
data class ExceptionData(val state: ExceptionState) : Throwable() {
|
||||
override val message: String? = state.getMessage().value
|
||||
override fun fillInStackTrace() = this
|
||||
|
||||
override fun toString(): String = state.getMessageWithName()
|
||||
}
|
||||
Reference in New Issue
Block a user