Change error message of EXCEPTION_IN_CONST_VAL_INITIALIZER

This commit is contained in:
Ivan Kylchik
2022-02-04 12:29:09 +03:00
parent 001ecaa9b2
commit 40d224d5fe
10 changed files with 63 additions and 51 deletions
@@ -76,6 +76,6 @@ object KtDefaultJvmErrorMessages : BaseDiagnosticRendererFactory() {
map.put(JvmBackendErrors.SCRIPT_CAPTURING_ENUM, "Enum class {0} captures the script class instance. Try to use class or anonymous object instead", STRING)
map.put(JvmBackendErrors.SCRIPT_CAPTURING_ENUM_ENTRY, "Enum entry {0} captures the script class instance. Try to use class or anonymous object instead", STRING)
map.put(JvmBackendErrors.EXCEPTION_IN_CONST_VAL_INITIALIZER, "An exception occur while evaluating const value initializer: {0}", STRING)
map.put(JvmBackendErrors.EXCEPTION_IN_CONST_VAL_INITIALIZER, "Cannot evaluate constant expression: {0}", STRING)
}
}
@@ -10,6 +10,8 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
import org.jetbrains.kotlin.ir.interpreter.checker.IrConstTransformer
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmBackendErrors
@@ -22,7 +24,8 @@ val constEvaluationPhase = makeIrModulePhase(
// TODO make context common
class ConstEvaluationLowering(val context: JvmBackendContext) : FileLoweringPass {
val interpreter = IrInterpreter(context.irBuiltIns)
val configuration = IrInterpreterConfiguration(printOnlyExceptionMessage = true)
val interpreter = IrInterpreter(IrInterpreterEnvironment(context.irBuiltIns, configuration), emptyMap())
override fun lower(irFile: IrFile) {
val transformer = IrConstTransformer(interpreter, irFile, mode = EvaluationMode.ONLY_INTRINSIC_CONST) { element, error ->
@@ -60,7 +60,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
callStack.popInstruction().handle()
}
return callStack.popState().toIrExpression(expression).apply { callStack.dropFrame() }
return environment.stateToIrExpression(callStack.popState(), expression).apply { callStack.dropFrame() }
}
internal fun withNewCallStack(call: IrCall, init: IrInterpreter.() -> Any?): State {
@@ -221,7 +221,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
// this check is used to be sure that object will be created once
val objectState = when (constructorCall) {
!is IrConstructorCall -> callStack.loadState(constructorCall.getThisReceiver())
else -> (if (irClass.isSubclassOfThrowable()) ExceptionState(irClass, callStack.getStackTrace()) else Common(irClass))
else -> (if (irClass.isSubclassOfThrowable()) ExceptionState(irClass, environment) else Common(irClass))
.apply {
// must set object's state here, before actual initialization, to avoid cyclic evaluation
if (irClass.isObject) environment.mapOfObjects[irClass.symbol] = this
@@ -17,4 +17,6 @@ class IrInterpreterConfiguration(
val maxStack: Int = 10_000,
val maxCommands: Int = 1_000_000,
val createNonCompileTimeObjects: Boolean = false,
val printOnlyExceptionMessage: Boolean = false,
val collapseStackTraceFromJDK: Boolean = true,
)
@@ -9,20 +9,21 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.state.Common
import org.jetbrains.kotlin.ir.interpreter.state.Complex
import org.jetbrains.kotlin.ir.interpreter.state.ExceptionState
import org.jetbrains.kotlin.ir.interpreter.state.Primitive
import org.jetbrains.kotlin.ir.interpreter.state.State
import org.jetbrains.kotlin.ir.interpreter.state.Wrapper
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.isSubclassOf
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
@@ -116,6 +117,32 @@ class IrInterpreterEnvironment(
}
}
internal fun stateToIrExpression(state: State, original: IrExpression): IrExpression {
val start = original.startOffset
val end = original.endOffset
val type = original.type.makeNotNull()
return when (state) {
is Primitive<*> ->
when {
state.value == null -> state.value.toIrConst(type, start, end)
type.isPrimitiveType() || type.isString() -> state.value.toIrConst(type, start, end)
else -> original // TODO support for arrays
}
is ExceptionState -> {
val message = if (configuration.printOnlyExceptionMessage) state.getShortDescription() else "\n" + state.getFullDescription()
IrErrorExpressionImpl(original.startOffset, original.endOffset, original.type, message)
}
is Complex -> {
val stateType = state.irClass.defaultType
when {
stateType.isUnsignedType() -> (state.fields.values.single() as Primitive<*>).value.toIrConst(type, start, end)
else -> original
}
}
else -> original // TODO support
}
}
private fun IrClassSymbol.getIrClassOfReflectionFromList(name: String): IrClassSymbol? {
val property = this.owner.declarations.singleOrNull { it.nameForIrSerialization.asString() == name } as? IrProperty
val list = property?.getter?.returnType as? IrSimpleType
@@ -65,31 +65,6 @@ fun Any?.toIrConst(irType: IrType, startOffset: Int = SYNTHETIC_OFFSET, endOffse
toIrConstOrNull(irType, startOffset, endOffset)
?: throw UnsupportedOperationException("Unsupported const element type ${irType.makeNotNull().render()}")
internal fun State.toIrExpression(expression: IrExpression): IrExpression {
val start = expression.startOffset
val end = expression.endOffset
val type = expression.type.makeNotNull()
return when (this) {
is Primitive<*> ->
when {
this.value == null -> this.value.toIrConst(type, start, end)
type.isPrimitiveType() || type.isString() -> this.value.toIrConst(type, start, end)
else -> expression // TODO support for arrays
}
is ExceptionState -> {
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, "\n" + this.getFullDescription())
}
is Complex -> {
val stateType = this.irClass.defaultType
when {
stateType.isUnsignedType() -> (this.fields.values.single() as Primitive<*>).value.toIrConst(type, start, end)
else -> expression
}
}
else -> expression // TODO support
}
}
internal fun IrFunction.createCall(origin: IrStatementOrigin? = null): IrCall {
this as IrSimpleFunction
return IrCallImpl(SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, returnType, symbol, typeParameters.size, valueParameters.size, origin)
@@ -34,6 +34,6 @@ internal fun Throwable.handleUserException(environment: IrInterpreterEnvironment
val exceptionName = this::class.java.simpleName
val irExceptionClass = environment.irExceptions.firstOrNull { it.name.asString() == exceptionName }
?: environment.irBuiltIns.throwableClass.owner
environment.callStack.pushState(ExceptionState(this, irExceptionClass, environment.callStack.getStackTrace()))
environment.callStack.pushState(ExceptionState(this, irExceptionClass, environment.callStack.getStackTrace(), environment))
environment.callStack.dropFramesUntilTryCatch()
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.interpreter.state
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
import org.jetbrains.kotlin.ir.interpreter.getOriginalPropertyByName
import org.jetbrains.kotlin.ir.interpreter.stack.Field
import org.jetbrains.kotlin.ir.interpreter.stack.Fields
@@ -39,11 +40,13 @@ internal class ExceptionState private constructor(
if (!fields.containsKey(causeProperty.symbol)) setCause(null)
}
constructor(irClass: IrClass, stackTrace: List<String>) : this(irClass, mutableMapOf(), stackTrace)
constructor(
irClass: IrClass, environment: IrInterpreterEnvironment
) : this(irClass, mutableMapOf(), environment.callStack.getStackTrace())
constructor(
exception: Throwable, irClass: IrClass, stackTrace: List<String>
) : this(irClass, evaluateFields(exception, irClass, stackTrace), stackTrace + evaluateAdditionalStackTrace(exception)) {
exception: Throwable, irClass: IrClass, stackTrace: List<String>, environment: IrInterpreterEnvironment
) : this(irClass, evaluateFields(exception, irClass, environment), stackTrace + evaluateAdditionalStackTrace(exception, environment)) {
setCause(null) // TODO check this fact
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
@@ -88,6 +91,10 @@ internal class ExceptionState private constructor(
setField(causeProperty.symbol, causeValue ?: Primitive.nullStateOfType(causeProperty.getter!!.returnType))
}
fun getShortDescription(): String {
return message.let { if (it?.isNotEmpty() == true) it else "???" }
}
fun getFullDescription(): String {
// TODO remainder of the stack trace with "..."
val message = message.let { if (it?.isNotEmpty() == true) ": $it" else "" }
@@ -102,18 +109,19 @@ internal class ExceptionState private constructor(
override fun toString(): String = message?.let { "$exceptionFqName: $it" } ?: exceptionFqName
companion object {
private fun evaluateFields(exception: Throwable, irClass: IrClass, stackTrace: List<String>): Fields {
private fun evaluateFields(exception: Throwable, irClass: IrClass, environment: IrInterpreterEnvironment): Fields {
val stackTrace = environment.callStack.getStackTrace()
val messageProperty = irClass.getOriginalPropertyByName("message")
val causeProperty = irClass.getOriginalPropertyByName("cause")
val messageVar = messageProperty.symbol to Primitive(exception.message, messageProperty.getter!!.returnType)
val causeVar = exception.cause?.let {
causeProperty.symbol to ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" })
causeProperty.symbol to ExceptionState(it, irClass, stackTrace + it.stackTrace.reversed().map { "at $it" }, environment)
}
return causeVar?.let { mutableMapOf(messageVar, it) } ?: mutableMapOf(messageVar)
}
private fun evaluateAdditionalStackTrace(e: Throwable): List<String> {
private fun evaluateAdditionalStackTrace(e: Throwable, environment: IrInterpreterEnvironment): List<String> {
// TODO do we really need this?... It will point to JVM stdlib
val additionalStack = mutableListOf<String>()
if (e.stackTrace.any { it.className == "java.lang.invoke.MethodHandle" }) {
@@ -137,6 +145,10 @@ internal class ExceptionState private constructor(
cause = cause.cause
}
}
if (environment.configuration.collapseStackTraceFromJDK && additionalStack.isNotEmpty()) {
return listOf("at <JDK>")
}
return additionalStack
}
}
@@ -1,11 +1,5 @@
/exceptionFromInterpreter_ir.kt:7:26: error: An exception occur while evaluating const value initializer:
Exception java.lang.ArithmeticException: / by zero
at ExceptionFromInterpreter_irKt.<clinit>(exceptionFromInterpreter_ir.kt:7)
/exceptionFromInterpreter_ir.kt:6:26: error: Cannot evaluate constant expression: / by zero
/exceptionFromInterpreter_ir.kt:8:39: error: An exception occur while evaluating const value initializer:
Exception java.lang.IllegalArgumentException: marginPrefix must be non-blank string.
at ExceptionFromInterpreter_irKt.<clinit>(exceptionFromInterpreter_ir.kt:8)
/exceptionFromInterpreter_ir.kt:7:39: error: Cannot evaluate constant expression: marginPrefix must be non-blank string.
/exceptionFromInterpreter_ir.kt:12:4: error: An exception occur while evaluating const value initializer:
Exception java.lang.ArithmeticException: / by zero
at ExceptionFromInterpreter_irKt.<clinit>(exceptionFromInterpreter_ir.kt:12)
/exceptionFromInterpreter_ir.kt:11:4: error: Cannot evaluate constant expression: / by zero
@@ -6,7 +6,6 @@ fun append(sb: StringBuilder, value: CharSequence, start: Int, end: Int): String
const val a = <!EVALUATED: `Some string with not zero length!!!`!>append(StringBuilder("Some string with not zero length"), "!!!", 0, 3)<!>
const val b = <!WAS_NOT_EVALUATED: `
Exception java.lang.IndexOutOfBoundsException: start -1, end 0, s.length() 3
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:539)
at java.lang.StringBuilder.append(StringBuilder.java:175)
at <JDK>
at ExceptionFromWrapperKt.append(exceptionFromWrapper.kt:3)
at ExceptionFromWrapperKt.<clinit>(exceptionFromWrapper.kt:7)`!>append(StringBuilder("Some string with not zero length"), "!!!", -1, 0)<!>