diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt index c69f786c8fc..0e2217f3a4e 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/IrInterpreter.kt @@ -89,6 +89,84 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map } } + internal class Proxy(val state: Common, val interpreter: IrInterpreter) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Proxy + + val valueArguments = mutableListOf() + val equalsFun = state.getEqualsFunction() + if (equalsFun.isFakeOverriddenFromAny()) return this.state.getOriginal() === other.state.getOriginal() + + equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) } + valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, other.state)) + + return with(interpreter) { + stack.newFrame(initPool = valueArguments) { + equalsFun.interpret() + } + (stack.popReturnValue() as Primitive<*>).value as Boolean + } // TODO check + } + + override fun hashCode(): Int { + val valueArguments = mutableListOf() + val hashCodeFun = state.getHashCodeFunction() + if (hashCodeFun.isFakeOverriddenFromAny()) return System.identityHashCode(state.getOriginal()) + + hashCodeFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) } + return with(interpreter) { + stack.newFrame(initPool = valueArguments) { + hashCodeFun.interpret() + } + (stack.popReturnValue() as Primitive<*>).value as Int + }// TODO check + } + + override fun toString(): String { + val valueArguments = mutableListOf() + val toStringFun = state.getToStringFunction() + if (toStringFun.isFakeOverriddenFromAny()) { + return "${state.getOriginal().irClass.internalName()}@" + System.identityHashCode(state).toString(16).padStart(8, '0') + } + + toStringFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, state)) } + return with(interpreter) { + stack.newFrame(initPool = valueArguments) { + toStringFun.interpret() + } + (stack.popReturnValue() as Primitive<*>).value as String + }// TODO check + } + } + + internal fun State.wrapAsProxyIfNeeded(): Any? { + return when (this) { + is ExceptionState -> this.getThisAsCauseForException() + is Wrapper -> this.value + is Primitive<*> -> this.value + is Common -> Proxy(this, this@IrInterpreter) + is Lambda -> this // TODO as Proxy + else -> throw AssertionError("${this::class} is unsupported as argument for wrap function") + } + } + + internal fun IrFunction.getArgsForMethodInvocation(args: List): List { + val argsValues = args.map { it.state.wrapAsProxyIfNeeded() }.toMutableList() + + // TODO if vararg isn't last parameter + // must convert vararg array into separated elements for correct invoke + if (this.valueParameters.lastOrNull()?.varargElementType != null) { + val varargValue = argsValues.last() + argsValues.removeAt(argsValues.size - 1) + argsValues.addAll(varargValue as Array) + } + + return argsValues + } + private fun IrElement.interpret(): ExecutionResult { try { incrementAndCheckCommands() @@ -172,19 +250,7 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map val receiverType = irFunction.dispatchReceiverParameter?.type val argsType = listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type } - val argsValues = args.map { - when (it) { - is Wrapper -> it.value // wrapper can be used in built in calculation, for example, in null or equality checks - is Complex -> when (irFunction.fqNameWhenAvailable?.asString()) { - // must explicitly convert Common to String in String plus method or else will be taken default toString from Common - "kotlin.String.plus" -> stack.apply { interpretToString(it) }.popReturnValue().asString() - else -> it.getOriginal() - } - is Primitive<*> -> it.value - is Lambda -> it // lambda also can be used, for example, in null check or toString - else -> TODO("unsupported type of argument for builtins calculations: ${it::class.java}") - } - } + val argsValues = args.map { it.wrapAsProxyIfNeeded() } fun IrType.getOnlyName(): String { return when { @@ -209,7 +275,6 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map ?: throw InterpreterMethodNotFoundError("For given function $signature there is no entry in binary map") when (methodName) { "rangeTo" -> return calculateRangeTo(irFunction.returnType) - "EQEQ" -> return calculateEquals(argsValues[0], argsValues[1]) else -> function.invoke(argsValues[0], argsValues[1]) } } @@ -241,24 +306,6 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map } } - private fun calculateEquals(first: Any?, second: Any?): ExecutionResult { - if (first == null || second == null || first !is Common || second !is Common) { - return Next.apply { stack.pushReturnValue((first == second).toState(irBuiltIns.booleanType)) } - } - - val valueArguments = mutableListOf() - val equalsFun = first.getEqualsFunction() - if (equalsFun.isFakeOverriddenFromAny()) { - return Next.apply { stack.pushReturnValue((first == second).toState(irBuiltIns.booleanType)) } - } - - equalsFun.getDispatchReceiver()!!.let { valueArguments.add(Variable(it, first)) } - valueArguments.add(Variable(equalsFun.valueParameters.single().symbol, second)) - return stack.newFrame(initPool = valueArguments) { - equalsFun.interpret() - }.check { return it } - } - private fun interpretValueParameters( expression: IrFunctionAccessExpression, irFunction: IrFunction, pool: MutableList ): ExecutionResult { @@ -722,7 +769,8 @@ class IrInterpreter(private val irBuiltIns: IrBuiltIns, private val bodyMap: Map is Primitive<*> -> arrayToList(result.value) is Common -> when { result.irClass.defaultType.isUnsignedArray() -> arrayToList((result.fields.single().state as Primitive<*>).value) - else -> listOf(result) + result.irClass.defaultType.isUnsigned() -> listOf(result) + else -> listOf(Proxy(result, this)) } else -> listOf(result) } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt index ff5f9287241..b078e124a4b 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/Utils.kt @@ -57,6 +57,7 @@ internal fun State.toIrExpression(expression: IrExpression): IrExpression { internal fun Any?.toState(irType: IrType): State { return when (this) { + is IrInterpreter.Proxy -> this.state is State -> this is Boolean, is Char, is Byte, is Short, is Int, is Long, is String, is Float, is Double, is Array<*>, is ByteArray, is CharArray, is ShortArray, is IntArray, is LongArray, is FloatArray, is DoubleArray, is BooleanArray -> Primitive(this, irType) @@ -130,27 +131,6 @@ internal fun getPrimitiveClass(irType: IrType, asObject: Boolean = false): Class } } -internal fun IrFunction.getArgsForMethodInvocation(args: List): List { - val argsValues = args.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 - if (this.valueParameters.lastOrNull()?.varargElementType != null) { - val varargValue = argsValues.last() - argsValues.removeAt(argsValues.size - 1) - argsValues.addAll(varargValue as Array) - } - - return argsValues -} - fun IrFunction.getLastOverridden(): IrFunction { if (this !is IrSimpleFunction) return this diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/builtins/IrBuiltInsMapGenerated.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/builtins/IrBuiltInsMapGenerated.kt index f6f7ae18de7..571aa96fe8d 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/builtins/IrBuiltInsMapGenerated.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/builtins/IrBuiltInsMapGenerated.kt @@ -16,7 +16,7 @@ package org.jetbrains.kotlin.ir.interpreter.builtins -import org.jetbrains.kotlin.ir.interpreter.internalName +import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.state.* /** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */ @@ -139,7 +139,7 @@ val unaryFunctions = mapOf>( unaryOperation>("size", "Array") { a -> a.size }, unaryOperation>("iterator", "Array") { a -> a.iterator() }, unaryOperation("hashCode", "Any") { a -> a.hashCode() }, - unaryOperation("toString", "Any") { a -> a.defaultToString() }, + unaryOperation("toString", "Any") { a -> a.toString() }, unaryOperation("CHECK_NOT_NULL", "T0?") { a -> a!! }, unaryOperation("message", "Throwable") { a -> a.getMessage() }, unaryOperation("cause", "Throwable") { a -> a.getCause() } @@ -450,7 +450,7 @@ val binaryFunctions = mapOf>( binaryOperation("greaterOrEqual", "Long", "Long") { a, b -> a >= b }, binaryOperation("greaterOrEqual", "Double", "Double") { a, b -> a >= b }, binaryOperation("EQEQ", "Any?", "Any?") { a, b -> a == b }, - binaryOperation("EQEQEQ", "Any?", "Any?") { a, b -> a === b }, + binaryOperation("EQEQEQ", "Any?", "Any?") { a, b -> if (a is IrInterpreter.Proxy && b is IrInterpreter.Proxy) a.state === b.state else a === b }, binaryOperation("ieee754equals", "Float?", "Float?") { a, b -> a == b }, binaryOperation("ieee754equals", "Double?", "Double?") { a, b -> a == b }, binaryOperation("ANDAND", "Boolean", "Boolean") { a, b -> a && b }, @@ -470,11 +470,3 @@ val ternaryFunctions = mapOf, Int, Any?>("set", "Array", "Int", "T") { a, b, c -> a.set(b, c) } ) -private fun Any.defaultToString(): String { - return when (this) { - is Lambda -> this.toString() - is State -> "${this.irClass.internalName()}@" + System.identityHashCode(this).toString(16).padStart(8, '0') - else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0')) - } -} - diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt index 619646d59a3..9bea2d637d8 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/intrinsics/IntrinsicImplementations.kt @@ -44,7 +44,7 @@ internal object ArrayOf : IntrinsicBase() { override fun evaluate(irFunction: IrFunction, stack: Stack, interpret: IrElement.() -> ExecutionResult): ExecutionResult { val elementsVariable = irFunction.valueParameters.single().symbol - val array = irFunction.getArgsForMethodInvocation(listOf(stack.getVariable(elementsVariable))).toTypedArray() + val array = (stack.getVariable(elementsVariable).state as Primitive<*>).value as Array val typeArguments = irFunction.typeParameters.map { stack.getVariable(it.symbol) } stack.pushReturnValue(array.toState(irFunction.returnType).apply { addTypeArguments(typeArguments) }) return Next diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Common.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Common.kt index 8239a2129e6..ae9c8d93240 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Common.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Common.kt @@ -5,15 +5,11 @@ package org.jetbrains.kotlin.ir.interpreter.state +import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.interpreter.isInterface 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.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.types.classOrNull -import org.jetbrains.kotlin.ir.types.isNullableAny import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization -import org.jetbrains.kotlin.name.Name internal class Common private constructor( override val irClass: IrClass, override val fields: MutableList @@ -43,23 +39,6 @@ internal class Common private constructor( return owner } - fun getToStringFunction(): IrFunction { - return irClass.declarations.filterIsInstance() - .filter { it.name.asString() == "toString" } - .first { it.valueParameters.isEmpty() } - .let { getOverridden(it as IrSimpleFunction, this) } - } - - fun getEqualsFunction(): IrFunction { - val equalsFun = irClass.declarations - .filterIsInstance() - .single { - it.name == Name.identifier("equals") && it.dispatchReceiverParameter != null - && it.valueParameters.size == 1 && it.valueParameters[0].type.isNullableAny() - } - return getOverridden(equalsFun, this) - } - override fun toString(): String { return "Common(obj='${irClass.fqNameForIrSerialization}', super=$superClass, values=$fields)" } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt index 5f873b3251e..fa610d3c217 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Complex.kt @@ -14,9 +14,11 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrCall import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol +import org.jetbrains.kotlin.ir.types.isNullableAny import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.isInterface import org.jetbrains.kotlin.ir.util.overrides +import org.jetbrains.kotlin.name.Name internal abstract class Complex(override val irClass: IrClass, override val fields: MutableList) : State { var superClass: Complex? = null @@ -90,4 +92,28 @@ internal abstract class Complex(override val irClass: IrClass, override val fiel else -> irFunction } } + + fun getEqualsFunction(): IrSimpleFunction { + val equalsFun = irClass.declarations + .filterIsInstance() + .single { + it.name == Name.identifier("equals") && it.dispatchReceiverParameter != null + && it.valueParameters.size == 1 && it.valueParameters[0].type.isNullableAny() + } + return getOverridden(equalsFun, this) + } + + fun getHashCodeFunction(): IrSimpleFunction { + return irClass.declarations.filterIsInstance() + .filter { it.name.asString() == "hashCode" } + .first { it.valueParameters.isEmpty() } + .let { getOverridden(it, this) } + } + + fun getToStringFunction(): IrSimpleFunction { + return irClass.declarations.filterIsInstance() + .filter { it.name.asString() == "toString" } + .first { it.valueParameters.isEmpty() } + .let { getOverridden(it, this) } + } } \ No newline at end of file diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt index 2bfb0626101..cea91c8a04c 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/state/Primitive.kt @@ -19,7 +19,7 @@ import org.jetbrains.kotlin.ir.util.defaultType import org.jetbrains.kotlin.ir.util.isFakeOverride import org.jetbrains.kotlin.ir.util.overrides -internal class Primitive(var value: T, val type: IrType) : State { +internal class Primitive(val value: T, val type: IrType) : State { override val fields: MutableList = mutableListOf() override val typeArguments: MutableList = mutableListOf() override val irClass: IrClass = type.classOrNull!!.owner diff --git a/generators/interpreter/GenerateInterpreterMap.kt b/generators/interpreter/GenerateInterpreterMap.kt index 1d3ab58abc0..9a22bf0f75a 100644 --- a/generators/interpreter/GenerateInterpreterMap.kt +++ b/generators/interpreter/GenerateInterpreterMap.kt @@ -39,7 +39,7 @@ fun generateMap(): String { p.println(File("license/COPYRIGHT.txt").readText()) p.println("package org.jetbrains.kotlin.ir.interpreter.builtins") p.println() - p.println("import org.jetbrains.kotlin.ir.interpreter.internalName") + p.println("import org.jetbrains.kotlin.ir.interpreter.IrInterpreter") p.println("import org.jetbrains.kotlin.ir.interpreter.state.*") p.println() p.println("/** This file is generated by org.jetbrains.kotlin.backend.common.interpreter.builtins.GenerateBuiltInsMap.generateMap(). DO NOT MODIFY MANUALLY */") @@ -68,19 +68,6 @@ fun generateMap(): String { p.println(")") p.println() - p.println( - """ - private fun Any.defaultToString(): String { - return when (this) { - is Lambda -> this.toString() - is State -> "${'$'}{this.irClass.internalName()}@" + System.identityHashCode(this).toString(16).padStart(8, '0') - else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0')) - } - } - """.trimIndent() - ) - p.println() - return sb.toString() } @@ -150,10 +137,7 @@ private fun generateUnaryBody(unaryOperationsMap: Map val methodName = "${function.name}" val parentheses = if (function is FunctionDescriptor) "()" else "" - val body = - if (methodName == "toString" && parameters.first == "") "a.defaultToString()" - else "a.$methodName$parentheses" - " unaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a -> $body }" + " unaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a -> a.$methodName$parentheses }" } + " unaryOperation(\"${irNullCheck.name}\", \"${irNullCheck.valueParameters.first().type.originalKotlinType}\") { a -> a!! },\n" + " unaryOperation(\"message\", \"Throwable\") { a -> a.getMessage() },\n" + @@ -169,7 +153,11 @@ private fun generateBinaryBody( } + binaryIrOperationsMap.entries.joinToString(separator = ",\n") { (function, parameters) -> val methodName = "${function.name}" val methodSymbol = getIrMethodSymbolByName(methodName) - " binaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a, b -> a $methodSymbol b }" + val body = when (methodName) { + IrBuiltIns.OperatorNames.EQEQEQ -> "if (a is IrInterpreter.Proxy && b is IrInterpreter.Proxy) a.state === b.state else a === b" + else -> "a $methodSymbol b" + } + " binaryOperation${parameters.first}(\"$methodName\", ${parameters.second}) { a, b -> $body }" } }