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) {
|
if (irFunction.fileOrNull != null) {
|
||||||
val fileName = irFunction.file.name
|
val fileName = irFunction.file.name
|
||||||
val lineNum = irFunction.fileEntry.getLineNumber(irFunction.startOffset) + 1
|
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) {
|
if (stackTrace.size == MAX_STACK_SIZE) {
|
||||||
|
|||||||
+9
-5
@@ -6,10 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.interpreter
|
package org.jetbrains.kotlin.backend.common.interpreter
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.interpreter.builtins.evaluateIntrinsicAnnotation
|
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.*
|
||||||
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.builtins.functions.FunctionInvokeDescriptor
|
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
@@ -173,7 +170,14 @@ fun IrType.getFqName(): String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun IrFunction.getArgsForMethodInvocation(data: Frame): List<Any?> {
|
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
|
// TODO if vararg isn't last parameter
|
||||||
// must convert vararg array into separated elements for correct invoke
|
// must convert vararg array into separated elements for correct invoke
|
||||||
|
|||||||
+49
-6
@@ -300,10 +300,21 @@ class ExceptionState private constructor(
|
|||||||
instance = this
|
instance = this
|
||||||
this.stackTrace = stackTrace.reversed()
|
this.stackTrace = stackTrace.reversed()
|
||||||
if (!this::exceptionFqName.isInitialized) this.exceptionFqName = irClass.fqNameForIrSerialization.asString()
|
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(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)
|
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(
|
constructor(
|
||||||
exception: Throwable, irClass: IrClass, stackTrace: List<String>
|
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 {
|
fun isSubtypeOf(ancestor: IrClass): Boolean {
|
||||||
if (exceptionHierarchy.isNotEmpty()) {
|
if (exceptionHierarchy.isNotEmpty()) {
|
||||||
return exceptionHierarchy.any { it.contains(ancestor.name.asString()) }
|
return exceptionHierarchy.any { it.contains(ancestor.name.asString()) }
|
||||||
@@ -325,18 +345,32 @@ class ExceptionState private constructor(
|
|||||||
return irClass.isSubclassOf(ancestor)
|
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 {
|
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 prefix = if (stackTrace.isNotEmpty()) "\n\t" else ""
|
||||||
val postfix = if (stackTrace.size > 10) "\n\t..." else ""
|
val postfix = if (stackTrace.size > 10) "\n\t..." else ""
|
||||||
|
val causeMessage = getCause()?.getFullDescription()?.let { "\nCaused by: $it" } ?: ""
|
||||||
return "Exception $exceptionFqName$message" +
|
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 {
|
override fun copy(): State {
|
||||||
return ExceptionState(irClass, fields, stackTrace).apply { this@apply.instance = this@ExceptionState.instance }
|
return ExceptionState(irClass, fields, stackTrace).apply { this@apply.instance = this@ExceptionState.instance }
|
||||||
}
|
}
|
||||||
@@ -359,3 +393,12 @@ class ExceptionState private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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