Introduce ExceptionState class responsible for all kind of exceptions
This commit is contained in:
+25
-14
@@ -52,11 +52,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
return when (val code = expression.interpret(data)) {
|
return when (val code = expression.interpret(data)) {
|
||||||
Code.NEXT -> data.popReturnValue().toIrExpression(expression)
|
Code.NEXT -> data.popReturnValue().toIrExpression(expression)
|
||||||
Code.EXCEPTION -> {
|
Code.EXCEPTION -> {
|
||||||
val message = when (val exception = data.popReturnValue()) {
|
val message = (data.popReturnValue() as ExceptionState).getMessage().value
|
||||||
is Common -> (exception.fields.first { it.descriptor.name.asString() == "message" }.state as Primitive<*>).value.toString()
|
|
||||||
is Wrapper -> (exception.value as Throwable).message.toString()
|
|
||||||
else -> TODO("not supported")
|
|
||||||
}
|
|
||||||
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, message)
|
IrErrorExpressionImpl(expression.startOffset, expression.endOffset, expression.type, message)
|
||||||
}
|
}
|
||||||
else -> TODO("$code not supported as result of interpretation")
|
else -> TODO("$code not supported as result of interpretation")
|
||||||
@@ -118,14 +114,14 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
} catch (e: Exception) { // TODO catch Throwable
|
} catch (e: Exception) { // TODO catch Throwable
|
||||||
// catch exception from JVM such as: ArithmeticException, StackOverflowError and others
|
// catch exception from JVM such as: ArithmeticException, StackOverflowError and others
|
||||||
val exceptionName = e::class.java.simpleName
|
val exceptionName = e::class.java.simpleName
|
||||||
val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == exceptionName }
|
val irExceptionClass = irExceptions.firstOrNull { it.name.asString() == exceptionName } ?: irBuiltIns.throwableClass.owner
|
||||||
|
|
||||||
if (irExceptionClass == null && exceptionName == "KotlinNullPointerException") {
|
if (irExceptionClass == irBuiltIns.throwableClass.owner && exceptionName == "KotlinNullPointerException") {
|
||||||
// this block is used to replace jvm null pointer exception with common one
|
// this block is used to replace jvm null pointer exception with common one
|
||||||
// TODO figure something better
|
// TODO figure something better
|
||||||
data.pushReturnValue(Wrapper(e, irExceptions.first { it.name.asString() == "NullPointerException" }))
|
data.pushReturnValue(ExceptionState(e, irExceptions.first { it.name.asString() == "NullPointerException" }))
|
||||||
} else {
|
} else {
|
||||||
irExceptionClass?.let { data.pushReturnValue(Wrapper(e, it)) } ?: throw AssertionError("Cannot handle exception $e")
|
irExceptionClass.let { data.pushReturnValue(ExceptionState(e, it)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return Code.EXCEPTION
|
return Code.EXCEPTION
|
||||||
@@ -164,9 +160,17 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateOverridden(owner: IrFunctionImpl, data: Frame): Code {
|
private fun calculateOverridden(owner: IrFunctionImpl, data: Frame): Code {
|
||||||
|
fun IrFunctionImpl.getLastPossibleOverridden(): IrFunctionImpl {
|
||||||
|
// main usage: get throwable properties getter signature from exception classes
|
||||||
|
// then use that signatures to get right method from ir builtins map
|
||||||
|
var overridden = this
|
||||||
|
while (overridden.overriddenSymbols.isNotEmpty()) overridden = overridden.overriddenSymbols.first().owner as IrFunctionImpl
|
||||||
|
return overridden
|
||||||
|
}
|
||||||
|
|
||||||
val variableDescriptor = owner.symbol.getReceiver()!!
|
val variableDescriptor = owner.symbol.getReceiver()!!
|
||||||
val superQualifier = (data.getVariableState(variableDescriptor) as? Complex)?.superType
|
val superQualifier = (data.getVariableState(variableDescriptor) as? Complex)?.superType
|
||||||
?: return calculateBuiltIns(owner.overriddenSymbols.first().owner, data)
|
?: return calculateBuiltIns(owner.getLastPossibleOverridden(), data)
|
||||||
val overridden = owner.overriddenSymbols.first { it.getReceiver()?.equalTo(superQualifier.getReceiver()) == true }
|
val overridden = owner.overriddenSymbols.first { it.getReceiver()?.equalTo(superQualifier.getReceiver()) == true }
|
||||||
|
|
||||||
val newStates = InterpreterFrame(mutableListOf(Variable(overridden.getReceiver()!!, superQualifier)))
|
val newStates = InterpreterFrame(mutableListOf(Variable(overridden.getReceiver()!!, superQualifier)))
|
||||||
@@ -478,7 +482,7 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
val convertibleClassName = data.popReturnValue().irClass.fqNameForIrSerialization
|
val convertibleClassName = data.popReturnValue().irClass.fqNameForIrSerialization
|
||||||
val castClassName = expression.type.classOrNull?.owner?.fqNameForIrSerialization
|
val castClassName = expression.type.classOrNull?.owner?.fqNameForIrSerialization
|
||||||
val message = "$convertibleClassName cannot be cast to $castClassName"
|
val message = "$convertibleClassName cannot be cast to $castClassName"
|
||||||
data.pushReturnValue(Wrapper(ClassCastException(message), classCastException))
|
data.pushReturnValue(ExceptionState(ClassCastException(message), classCastException))
|
||||||
Code.EXCEPTION
|
Code.EXCEPTION
|
||||||
} else {
|
} else {
|
||||||
code
|
code
|
||||||
@@ -522,14 +526,15 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
private fun interpretTry(expression: IrTry, data: Frame): Code {
|
private fun interpretTry(expression: IrTry, data: Frame): Code {
|
||||||
var code = expression.tryResult.interpret(data)
|
var code = expression.tryResult.interpret(data)
|
||||||
if (code == Code.EXCEPTION) {
|
if (code == Code.EXCEPTION) {
|
||||||
val exception = data.peekReturnValue()
|
val exception = data.peekReturnValue() as ExceptionState
|
||||||
for (catchBlock in expression.catches) {
|
for (catchBlock in expression.catches) {
|
||||||
if (exception.irClass.defaultType.isSubtypeOf(catchBlock.catchParameter.type, irBuiltIns)) {
|
if (exception.isSubtypeOf(catchBlock.catchParameter.type.classOrNull!!.owner)) {
|
||||||
code = catchBlock.interpret(data)
|
code = catchBlock.interpret(data)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// TODO check flow correctness; should I return finally result code if in catch there was an exception?
|
||||||
return expression.finallyExpression?.interpret(data) ?: code
|
return expression.finallyExpression?.interpret(data) ?: code
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,7 +545,13 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun interpretThrow(expression: IrThrow, data: Frame): Code {
|
private fun interpretThrow(expression: IrThrow, data: Frame): Code {
|
||||||
expression.value.interpret(data)
|
expression.value.interpret(data).also { if (it != Code.NEXT) return it }
|
||||||
|
when (val exception = data.popReturnValue()) {
|
||||||
|
is Common -> data.pushReturnValue(ExceptionState(exception))
|
||||||
|
is Wrapper -> data.pushReturnValue(ExceptionState(exception))
|
||||||
|
is ExceptionState -> data.pushReturnValue(exception)
|
||||||
|
else -> throw AssertionError("${exception::class} cannot be used as exception state")
|
||||||
|
}
|
||||||
return Code.EXCEPTION
|
return Code.EXCEPTION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+74
-20
@@ -18,9 +18,7 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
|
|||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.defaultType
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
|
||||||
import org.jetbrains.kotlin.ir.util.isTypeParameter
|
|
||||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||||
import java.lang.invoke.MethodHandle
|
import java.lang.invoke.MethodHandle
|
||||||
import java.lang.invoke.MethodHandles
|
import java.lang.invoke.MethodHandles
|
||||||
@@ -53,12 +51,14 @@ class Primitive<T>(var value: T, val type: IrType) : State {
|
|||||||
override val irClass: IrClass = type.classOrNull!!.owner
|
override val irClass: IrClass = type.classOrNull!!.owner
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val properties = irClass.declarations.filterIsInstance<IrProperty>()
|
if (value != null) {
|
||||||
for (property in properties) {
|
val properties = irClass.declarations.filterIsInstance<IrProperty>()
|
||||||
val propertySignature = CompileTimeFunction(property.name.asString(), listOf(irClass.descriptor.defaultType.toString()))
|
for (property in properties) {
|
||||||
val propertyValue = unaryFunctions[propertySignature]?.invoke(value)
|
val propertySignature = CompileTimeFunction(property.name.asString(), listOf(irClass.descriptor.defaultType.toString()))
|
||||||
?: throw NoSuchMethodException("For given property $propertySignature there is no entry in unary map")
|
val propertyValue = unaryFunctions[propertySignature]?.invoke(value)
|
||||||
fields.add(Variable(property.descriptor, Primitive(propertyValue, property.backingField!!.type)))
|
?: throw NoSuchMethodException("For given property $propertySignature there is no entry in unary map")
|
||||||
|
fields.add(Variable(property.descriptor, Primitive(propertyValue, property.backingField!!.type)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ class Primitive<T>(var value: T, val type: IrType) : State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Complex(override var irClass: IrClass, override val fields: MutableList<Variable>) : State {
|
abstract class Complex(override val irClass: IrClass, override val fields: MutableList<Variable>) : State {
|
||||||
var superType: Complex? = null
|
var superType: Complex? = null
|
||||||
var instance: Complex? = null
|
var instance: Complex? = null
|
||||||
|
|
||||||
@@ -117,14 +117,6 @@ abstract class Complex(override var irClass: IrClass, override val fields: Mutab
|
|||||||
return irClass.thisReceiver!!.descriptor
|
return irClass.thisReceiver!!.descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? {
|
|
||||||
return irClass.declarations.filterIsInstance<IrFunction>()
|
|
||||||
.filter { it.descriptor.name == descriptor.name }
|
|
||||||
.firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Common(override var irClass: IrClass, override val fields: MutableList<Variable>) : Complex(irClass, fields) {
|
|
||||||
override fun setState(newVar: Variable) {
|
override fun setState(newVar: Variable) {
|
||||||
when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) {
|
when (val oldState = fields.firstOrNull { it.descriptor == newVar.descriptor }) {
|
||||||
null -> fields.add(newVar) // newVar isn't present in value list
|
null -> fields.add(newVar) // newVar isn't present in value list
|
||||||
@@ -132,6 +124,14 @@ class Common(override var irClass: IrClass, override val fields: MutableList<Var
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getIrFunction(descriptor: FunctionDescriptor): IrFunction? {
|
||||||
|
return irClass.declarations.filterIsInstance<IrFunction>()
|
||||||
|
.filter { it.descriptor.name == descriptor.name }
|
||||||
|
.firstOrNull { it.descriptor.valueParameters.map { it.type } == descriptor.valueParameters.map { it.type } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Common(override val irClass: IrClass, override val fields: MutableList<Variable>) : Complex(irClass, fields) {
|
||||||
fun getToStringFunction(): IrFunctionImpl {
|
fun getToStringFunction(): IrFunctionImpl {
|
||||||
return irClass.declarations.filterIsInstance<IrFunction>()
|
return irClass.declarations.filterIsInstance<IrFunction>()
|
||||||
.filter { it.descriptor.name.asString() == "toString" }
|
.filter { it.descriptor.name.asString() == "toString" }
|
||||||
@@ -150,7 +150,7 @@ class Common(override var irClass: IrClass, override val fields: MutableList<Var
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Wrapper(val value: Any, override var irClass: IrClass) : Complex(irClass, mutableListOf()) {
|
class Wrapper(val value: Any, override val irClass: IrClass) : Complex(irClass, mutableListOf()) {
|
||||||
private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe()
|
private val typeFqName = irClass.fqNameForIrSerialization.toUnsafe()
|
||||||
private val receiverClass = irClass.defaultType.getClass(true)
|
private val receiverClass = irClass.defaultType.getClass(true)
|
||||||
|
|
||||||
@@ -264,7 +264,7 @@ class Wrapper(val value: Any, override var irClass: IrClass) : Complex(irClass,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Lambda(val irFunction: IrFunction, override var irClass: IrClass) : Complex(irClass, mutableListOf()) {
|
class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : Complex(irClass, mutableListOf()) {
|
||||||
// irFunction is anonymous declaration, but irCall will contain descriptor of invoke method from Function interface
|
// irFunction is anonymous declaration, but irCall will contain descriptor of invoke method from Function interface
|
||||||
private val invokeDescriptor = irClass.declarations.first().descriptor
|
private val invokeDescriptor = irClass.declarations.first().descriptor
|
||||||
|
|
||||||
@@ -284,3 +284,57 @@ class Lambda(val irFunction: IrFunction, override var irClass: IrClass) : Comple
|
|||||||
return "Lambda(${irClass.fqNameForIrSerialization})"
|
return "Lambda(${irClass.fqNameForIrSerialization})"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ExceptionState private constructor(override val irClass: IrClass, override val fields: MutableList<Variable>) : Complex(irClass, fields) {
|
||||||
|
private val exceptionHierarchy = mutableListOf<String>()
|
||||||
|
private val messageProperty = irClass.getPropertyByName("message")
|
||||||
|
private val causeProperty = irClass.getPropertyByName("cause")
|
||||||
|
|
||||||
|
init {
|
||||||
|
instance = this
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(common: Common) : this(common.irClass, common.fields)
|
||||||
|
constructor(wrapper: Wrapper) : this(wrapper.value as Throwable, wrapper.irClass)
|
||||||
|
|
||||||
|
constructor(exception: Throwable, irClass: IrClass) : this(irClass, evaluateFields(exception, irClass)) {
|
||||||
|
if (irClass.name.asString() == "Throwable" && exception::class.java.name != "Throwable") {
|
||||||
|
// ir class wasn't found in classpath, a stub was passed => need to save java class hierarchy
|
||||||
|
exceptionHierarchy += exception::class.java.name
|
||||||
|
generateSequence(exception::class.java.superclass) { it.superclass }.forEach { exceptionHierarchy += it.name }
|
||||||
|
exceptionHierarchy.removeAt(exceptionHierarchy.lastIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSubtypeOf(ancestor: IrClass): Boolean {
|
||||||
|
if (exceptionHierarchy.isNotEmpty()) {
|
||||||
|
return exceptionHierarchy.any { it.contains(ancestor.name.asString()) }
|
||||||
|
}
|
||||||
|
return irClass.isSubclassOf(ancestor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getMessage(): Primitive<String> = getState(messageProperty.descriptor) as Primitive<String>
|
||||||
|
|
||||||
|
fun getCause(): ExceptionState = getState(causeProperty.descriptor) as ExceptionState
|
||||||
|
|
||||||
|
override fun copy(): State {
|
||||||
|
return ExceptionState(irClass, fields).apply { this@apply.instance = this@ExceptionState.instance }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private fun IrClass.getPropertyByName(name: String): IrProperty {
|
||||||
|
val getPropertyFun = this.declarations.firstOrNull { it.nameForIrSerialization.asString().contains("get-$name") }
|
||||||
|
return (getPropertyFun as? IrFunctionImpl)?.correspondingPropertySymbol?.owner
|
||||||
|
?: this.declarations.single { it.nameForIrSerialization.asString() == name } as IrProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun evaluateFields(exception: Throwable, irClass: IrClass): MutableList<Variable> {
|
||||||
|
val messageProperty = irClass.getPropertyByName("message")
|
||||||
|
val causeProperty = irClass.getPropertyByName("cause")
|
||||||
|
|
||||||
|
val messageVar = Variable(messageProperty.descriptor, exception.message.toState(messageProperty.getter!!.returnType))
|
||||||
|
val causeVar = exception.cause?.let { Variable(causeProperty.descriptor, ExceptionState(it, irClass)) }
|
||||||
|
return listOfNotNull(messageVar, causeVar).toMutableList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user