Rewrite exceptions handler in ir interpreter
For now there are 2 types of exceptions: 1. UserExceptions - express user and jvm like exceptions 2. InterpreterError - thrown only if something is wrong with interpreter
This commit is contained in:
committed by
TeamCityServer
parent
c3b0c9c6b2
commit
f3d7dc5f22
+33
-35
@@ -14,9 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.builtins.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterException
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundException
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterTimeOutException
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.intrinsics.IntrinsicEvaluator
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.StackImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
@@ -70,7 +68,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
|
||||
private fun incrementAndCheckCommands() {
|
||||
commandCount++
|
||||
if (commandCount >= MAX_COMMANDS) throw InterpreterTimeOutException()
|
||||
if (commandCount >= MAX_COMMANDS) throw InterpreterTimeOutError()
|
||||
}
|
||||
|
||||
fun interpret(expression: IrExpression): IrExpression {
|
||||
@@ -133,13 +131,11 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
}
|
||||
|
||||
return executionResult.getNextLabel(this) { this@getNextLabel.interpret() }
|
||||
} catch (e: InterpreterException) {
|
||||
throw e
|
||||
} catch (e: Throwable) {
|
||||
// catch exception from JVM such as: ArithmeticException, StackOverflowError and others
|
||||
val exceptionName = e::class.java.simpleName
|
||||
} catch (e: UserException) {
|
||||
// can handle only user exceptions, all others must be rethrown
|
||||
val exceptionName = e.exception::class.java.simpleName
|
||||
val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == exceptionName } ?: irBuiltIns.throwableClass.owner
|
||||
stack.pushReturnValue(ExceptionState(e, irExceptionClass, stack.getStackTrace()))
|
||||
stack.pushReturnValue(ExceptionState(e.exception, irExceptionClass, stack.getStackTrace()))
|
||||
return Exception
|
||||
}
|
||||
}
|
||||
@@ -149,12 +145,12 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
if (irFunction.fileOrNull != null) stack.setCurrentFrameName(irFunction)
|
||||
|
||||
if (irFunction.body is IrSyntheticBody) return handleIntrinsicMethods(irFunction)
|
||||
return irFunction.body?.interpret() ?: throw InterpreterException("Ir function must be with body")
|
||||
return irFunction.body?.interpret() ?: throw InterpreterError("Ir function must be with body")
|
||||
}
|
||||
|
||||
private fun MethodHandle?.invokeMethod(irFunction: IrFunction): ExecutionResult {
|
||||
this ?: return handleIntrinsicMethods(irFunction)
|
||||
val result = this.invokeWithArguments(irFunction.getArgsForMethodInvocation(stack.getAll()))
|
||||
val result = withExceptionHandler { this.invokeWithArguments(irFunction.getArgsForMethodInvocation(stack.getAll())) }
|
||||
stack.pushReturnValue(result.toState(result.getType(irFunction.returnType)))
|
||||
|
||||
return Next
|
||||
@@ -197,26 +193,28 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
val signature = CompileTimeFunction(methodName, argsType.map { it.getOnlyName() })
|
||||
|
||||
// TODO replace unary, binary, ternary functions with vararg
|
||||
val result = when (argsType.size) {
|
||||
1 -> {
|
||||
val function = unaryFunctions[signature]
|
||||
?: throw InterpreterMethodNotFoundException("For given function $signature there is no entry in unary map")
|
||||
function.invoke(argsValues.first())
|
||||
}
|
||||
2 -> {
|
||||
val function = binaryFunctions[signature]
|
||||
?: throw InterpreterMethodNotFoundException("For given function $signature there is no entry in binary map")
|
||||
when (methodName) {
|
||||
"rangeTo" -> return calculateRangeTo(irFunction.returnType)
|
||||
else -> function.invoke(argsValues[0], argsValues[1])
|
||||
val result = withExceptionHandler {
|
||||
when (argsType.size) {
|
||||
1 -> {
|
||||
val function = unaryFunctions[signature]
|
||||
?: throw InterpreterMethodNotFoundError("For given function $signature there is no entry in unary map")
|
||||
function.invoke(argsValues.first())
|
||||
}
|
||||
2 -> {
|
||||
val function = binaryFunctions[signature]
|
||||
?: throw InterpreterMethodNotFoundError("For given function $signature there is no entry in binary map")
|
||||
when (methodName) {
|
||||
"rangeTo" -> return calculateRangeTo(irFunction.returnType)
|
||||
else -> function.invoke(argsValues[0], argsValues[1])
|
||||
}
|
||||
}
|
||||
3 -> {
|
||||
val function = ternaryFunctions[signature]
|
||||
?: throw InterpreterMethodNotFoundError("For given function $signature there is no entry in ternary map")
|
||||
function.invoke(argsValues[0], argsValues[1], argsValues[2])
|
||||
}
|
||||
else -> throw InterpreterError("Unsupported number of arguments for invocation as builtin functions")
|
||||
}
|
||||
3 -> {
|
||||
val function = ternaryFunctions[signature]
|
||||
?: throw InterpreterMethodNotFoundException("For given function $signature there is no entry in ternary map")
|
||||
function.invoke(argsValues[0], argsValues[1], argsValues[2])
|
||||
}
|
||||
else -> throw InterpreterException("Unsupported number of arguments")
|
||||
}
|
||||
|
||||
stack.pushReturnValue(result.toState(result.getType(irFunction.returnType)))
|
||||
@@ -259,7 +257,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
stack.peekReturnValue().checkNullability(valueParametersSymbols[i].owner.type) {
|
||||
val method = irFunction.getCapitalizedFileName() + "." + irFunction.fqNameWhenAvailable
|
||||
val parameter = valueParametersSymbols[i].owner.name
|
||||
throw IllegalArgumentException("Parameter specified as non-null is null: method $method, parameter $parameter")
|
||||
IllegalArgumentException("Parameter specified as non-null is null: method $method, parameter $parameter").throwAsUserException()
|
||||
}
|
||||
|
||||
with(Variable(valueParametersSymbols[i], stack.popReturnValue())) {
|
||||
@@ -637,7 +635,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
|
||||
val executionResult = enumEntry.initializerExpression?.interpret()?.check { return it }
|
||||
enumSuperCall?.apply { (0 until this.valueArgumentsCount).forEach { putValueArgument(it, null) } } // restore to null
|
||||
return executionResult ?: throw InterpreterException("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
|
||||
return executionResult ?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqNameWhenAvailable} is null")
|
||||
}
|
||||
|
||||
private fun interpretTypeOperatorCall(expression: IrTypeOperatorCall): ExecutionResult {
|
||||
@@ -653,7 +651,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
IrTypeOperator.CAST, IrTypeOperator.IMPLICIT_CAST -> {
|
||||
if (!isErased && !stack.peekReturnValue().isSubtypeOf(typeOperand)) {
|
||||
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
|
||||
throw ClassCastException("$convertibleClassName cannot be cast to ${typeOperand.render()}")
|
||||
ClassCastException("$convertibleClassName cannot be cast to ${typeOperand.render()}").throwAsUserException()
|
||||
}
|
||||
}
|
||||
IrTypeOperator.SAFE_CAST -> {
|
||||
@@ -750,7 +748,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
is Common -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
|
||||
is Wrapper -> stack.pushReturnValue(ExceptionState(exception, stack.getStackTrace()))
|
||||
is ExceptionState -> stack.pushReturnValue(exception)
|
||||
else -> throw InterpreterException("${exception::class} cannot be used as exception state")
|
||||
else -> throw InterpreterError("${exception::class} cannot be used as exception state")
|
||||
}
|
||||
return Exception
|
||||
}
|
||||
@@ -778,7 +776,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map
|
||||
}
|
||||
}
|
||||
is Lambda -> state.toString()
|
||||
else -> throw InterpreterException("${state::class.java} cannot be used in StringConcatenation expression")
|
||||
else -> throw InterpreterError("${state::class.java} cannot be used in StringConcatenation expression")
|
||||
}
|
||||
stack.pushReturnValue(result.toState(irBuiltIns.stringType))
|
||||
return Next
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
|
||||
import org.jetbrains.kotlin.ir.expressions.IrWhen
|
||||
import org.jetbrains.kotlin.ir.expressions.IrWhileLoop
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrFail
|
||||
@@ -72,7 +73,7 @@ internal fun ExecutionResult.implicitCastIfNeeded(expectedType: IrType, actualTy
|
||||
|
||||
if (!actualState.isSubtypeOf(expectedType)) {
|
||||
val convertibleClassName = stack.popReturnValue().irClass.fqNameWhenAvailable
|
||||
throw ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}")
|
||||
ClassCastException("$convertibleClassName cannot be cast to ${expectedType.render()}").throwAsUserException()
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -14,12 +14,10 @@ import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||
import org.jetbrains.kotlin.ir.interpreter.builtins.evaluateIntrinsicAnnotation
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
@@ -240,3 +238,11 @@ fun IrClass.internalName(): String {
|
||||
}
|
||||
return internalName.toString()
|
||||
}
|
||||
|
||||
inline fun withExceptionHandler(block: () -> Any?): Any? {
|
||||
try {
|
||||
return block()
|
||||
} catch (e: Throwable) {
|
||||
e.throwAsUserException()
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.exceptions
|
||||
|
||||
open class InterpreterError(override val message: String) : InterpreterException()
|
||||
|
||||
class InterpreterMethodNotFoundError(override val message: String) : InterpreterError(message)
|
||||
|
||||
class InterpreterTimeOutError : InterpreterError("Exceeded execution limit of constexpr expression")
|
||||
|
||||
class InterpreterEmptyReturnStackError : InterpreterError("Return values stack is empty")
|
||||
+1
-2
@@ -5,5 +5,4 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.exceptions
|
||||
|
||||
open class InterpreterException(override val message: String) : Exception(message) {
|
||||
}
|
||||
abstract class InterpreterException : Exception()
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.exceptions
|
||||
|
||||
class InterpreterMethodNotFoundException(override val message: String) : InterpreterException(message) {
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.exceptions
|
||||
|
||||
class InterpreterTimeOutException : InterpreterException("Exceeded execution limit of constexpr expression") {
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.exceptions
|
||||
|
||||
/**
|
||||
* This class used to wrap 2 types of exceptions:
|
||||
* 1. exceptions from JVM such as: ArithmeticException, StackOverflowError and others
|
||||
* 2. exceptions defined by user
|
||||
*/
|
||||
class UserException(val exception: Throwable) : InterpreterException() {
|
||||
override fun fillInStackTrace(): Throwable = this
|
||||
}
|
||||
|
||||
fun Throwable.throwAsUserException(): Nothing {
|
||||
throw UserException(this)
|
||||
}
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
package org.jetbrains.kotlin.ir.interpreter.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundException
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundError
|
||||
import org.jetbrains.kotlin.ir.interpreter.stack.Stack
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
@@ -23,7 +23,7 @@ internal class IntrinsicEvaluator {
|
||||
EnumHashCode.equalTo(irFunction) -> EnumHashCode.evaluate(irFunction, stack, interpret)
|
||||
JsPrimitives.equalTo(irFunction) -> JsPrimitives.evaluate(irFunction, stack, interpret)
|
||||
ArrayConstructor.equalTo(irFunction) -> ArrayConstructor.evaluate(irFunction, stack, interpret)
|
||||
else -> throw InterpreterMethodNotFoundException("Method ${irFunction.name} hasn't implemented")
|
||||
else -> throw InterpreterMethodNotFoundError("Method ${irFunction.name} hasn't implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrEnumEntry
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
@@ -96,7 +97,7 @@ internal object EnumValueOf : IntrinsicBase() {
|
||||
val enumEntryName = stack.getVariable(irFunction.valueParameters.first().symbol).state.asString()
|
||||
val enumEntry = enumClass.declarations.filterIsInstance<IrEnumEntry>().singleOrNull { it.name.asString() == enumEntryName }
|
||||
enumEntry?.interpret()?.check { return it }
|
||||
?: throw IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName")
|
||||
?: IllegalArgumentException("No enum constant ${enumClass.fqNameWhenAvailable}.$enumEntryName").throwAsUserException()
|
||||
|
||||
return Next
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.stack
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterEmptyReturnStackError
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
@@ -65,7 +66,7 @@ internal class InterpreterFrame(
|
||||
if (returnStack.isNotEmpty()) {
|
||||
return returnStack.last()
|
||||
}
|
||||
throw NoSuchElementException("Return values stack is empty")
|
||||
throw InterpreterEmptyReturnStackError()
|
||||
}
|
||||
|
||||
override fun popReturnValue(): State {
|
||||
@@ -74,6 +75,6 @@ internal class InterpreterFrame(
|
||||
returnStack.removeAt(returnStack.size - 1)
|
||||
return item
|
||||
}
|
||||
throw NoSuchElementException("Return values stack is empty")
|
||||
throw InterpreterEmptyReturnStackError()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
package org.jetbrains.kotlin.ir.interpreter.stack
|
||||
|
||||
import org.jetbrains.kotlin.ir.interpreter.ExecutionResult
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterException
|
||||
import org.jetbrains.kotlin.ir.interpreter.getCapitalizedFileName
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.name
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterError
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.util.file
|
||||
@@ -132,7 +132,7 @@ private class FrameContainer(current: Frame = InterpreterFrame()) {
|
||||
fun getAll() = innerStack.flatMap { it.getAll() }
|
||||
fun getVariable(symbol: IrSymbol): Variable {
|
||||
return innerStack.firstNotNullOfOrNull { it.getVariable(symbol) }
|
||||
?: throw InterpreterException("$symbol not found") // TODO better message
|
||||
?: throw InterpreterError("$symbol not found") // TODO better message
|
||||
}
|
||||
|
||||
fun contains(symbol: IrSymbol) = innerStack.any { it.contains(symbol) }
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.interpreter.stack.Variable
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.throwAsUserException
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
@@ -60,7 +61,9 @@ internal fun State.isSubtypeOf(other: IrType): Boolean {
|
||||
/**
|
||||
* This method used to check if for not null parameter there was passed null argument.
|
||||
*/
|
||||
internal fun State.checkNullability(irType: IrType?, throwException: () -> Nothing = { throw NullPointerException() }): State {
|
||||
internal fun State.checkNullability(
|
||||
irType: IrType?, throwException: () -> Nothing = { NullPointerException().throwAsUserException() }
|
||||
): State {
|
||||
if (irType !is IrSimpleType) return this
|
||||
if (this.isNull() && !irType.isNullable()) {
|
||||
throwException()
|
||||
|
||||
@@ -56,7 +56,7 @@ internal class Wrapper(val value: Any, override val irClass: IrClass) : Complex(
|
||||
|
||||
fun getCompanionObject(irClass: IrClass): Wrapper {
|
||||
val objectName = irClass.getEvaluateIntrinsicValue()!!
|
||||
val objectValue = companionObjectValue[objectName] ?: throw AssertionError("Companion object $objectName cannot be interpreted")
|
||||
val objectValue = companionObjectValue[objectName] ?: throw InternalError("Companion object $objectName cannot be interpreted")
|
||||
return Wrapper(objectValue, irClass)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user