From 9542eb36ec3b3cf99adedd2743b4715ca0c326ce Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 10 Jun 2020 23:04:07 +0300 Subject: [PATCH] Support interpretation of String's plus method and its extension version --- .../common/interpreter/IrInterpreter.kt | 44 ++++++++++++------- .../backend/common/interpreter/Utils.kt | 4 +- .../builtins/IrBuiltInsMapGenerated.kt | 1 + .../common/interpreter/state/Lambda.kt | 7 ++- generators/evaluate/GenerateBuiltInsMap.kt | 1 + 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt index ff7c03fe885..9b389e3f1fd 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -172,13 +172,18 @@ class IrInterpreter(irModule: IrModuleFragment) { } val args = stack.getAll().map { it.state } - val receiverType = descriptor.dispatchReceiverParameter?.type ?: descriptor.extensionReceiverParameter?.type + val receiverType = descriptor.dispatchReceiverParameter?.type val argsType = listOfNotNull(receiverType) + descriptor.valueParameters.map { it.original.type } val argsValues = args.map { when (it) { - is Complex -> it.getOriginal() + 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 - else -> it // lambda can be used in built in calculation, for example, in null check + is Lambda -> it // lambda can be used in built in calculation, for example, in null check or toString + else -> TODO("unsupported type of argument for builtins calculations: ${it::class.java}") } } val signature = CompileTimeFunction(methodName, argsType.map { it.toString() }) @@ -684,26 +689,31 @@ class IrInterpreter(irModule: IrModuleFragment) { val result = StringBuilder() expression.arguments.forEach { it.interpret().check { executionResult -> return executionResult } - result.append( - when (val returnValue = stack.popReturnValue()) { - is Primitive<*> -> returnValue.value.toString() - is Wrapper -> returnValue.value.toString() - is Common -> { - val toStringFun = returnValue.getToStringFunction() - stack.newFrame(initPool = mutableListOf(Variable(toStringFun.getReceiver()!!, returnValue))) { - toStringFun.body?.let { toStringFun.interpret() } ?: calculateBuiltIns(toStringFun) - }.check { executionResult -> return executionResult } - stack.popReturnValue().asString() - } - else -> throw InterpreterException("$returnValue cannot be used in StringConcatenation expression") - } - ) + interpretToString(stack.popReturnValue()).check { executionResult -> return executionResult } + result.append(stack.popReturnValue().asString()) } stack.pushReturnValue(result.toString().toState(expression.type)) return Next } + private suspend fun interpretToString(state: State): ExecutionResult { + val result = when (state) { + is Primitive<*> -> state.value.toString() + is Wrapper -> state.value.toString() + is Common -> { + val toStringFun = state.getToStringFunction() + return stack.newFrame(initPool = mutableListOf(Variable(toStringFun.getReceiver()!!, state))) { + toStringFun.body?.let { toStringFun.interpret() } ?: calculateBuiltIns(toStringFun) + } + } + is Lambda -> state.toString() + else -> throw InterpreterException("${state::class.java} cannot be used in StringConcatenation expression") + } + stack.pushReturnValue(result.toState(irBuiltIns.stringType)) + return Next + } + private fun interpretFunctionExpression(expression: IrFunctionExpression): ExecutionResult { val lambda = Lambda(expression.function, expression.type.classOrNull!!.owner) if (expression.function.isLocal) lambda.fields.addAll(stack.getAll()) // TODO save only necessary declarations diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt index 081aea44204..5d03205c1f2 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/Utils.kt @@ -163,8 +163,8 @@ fun getPrimitiveClass(fqName: String, asObject: Boolean = false): Class<*>? { } } -fun IrType.getFqName(): String? { - return this.classOrNull?.owner?.fqNameWhenAvailable?.asString() +fun IrType.getFqName(withNullableSymbol: Boolean = false): String? { + return this.classOrNull?.owner?.fqNameWhenAvailable?.asString()?.let { if (this.isNullable() && withNullableSymbol) "$it?" else it } } fun IrFunction.getArgsForMethodInvocation(args: List): List { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt index 790b8e44504..bcf6d3358f1 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt @@ -470,6 +470,7 @@ val ternaryFunctions = mapOf this.toString() is State -> "${this.irClass.name}@" + 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/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt index 223c36a7120..cc1d42259ce 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/state/Lambda.kt @@ -6,11 +6,11 @@ package org.jetbrains.kotlin.backend.common.interpreter.state import org.jetbrains.kotlin.backend.common.interpreter.equalTo +import org.jetbrains.kotlin.backend.common.interpreter.getFqName import org.jetbrains.kotlin.backend.common.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.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.nameForIrSerialization class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State { @@ -32,6 +32,9 @@ class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State } override fun toString(): String { - return "Lambda(${irClass.fqNameForIrSerialization})" + val receiver = (irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type)?.getFqName(true) + val arguments = irFunction.valueParameters.map { it.type.getFqName(true) }.joinToString(prefix = "(", postfix = ")") + val returnType = irFunction.returnType.getFqName(true) + return ("$arguments -> $returnType").let { if (receiver != null) "$receiver.$it" else it } } } \ No newline at end of file diff --git a/generators/evaluate/GenerateBuiltInsMap.kt b/generators/evaluate/GenerateBuiltInsMap.kt index 888695ed22c..c322d40089b 100644 --- a/generators/evaluate/GenerateBuiltInsMap.kt +++ b/generators/evaluate/GenerateBuiltInsMap.kt @@ -71,6 +71,7 @@ fun generateMap(): String { """ private fun Any.defaultToString(): String { return when (this) { + is Lambda -> this.toString() is State -> "${'$'}{this.irClass.name}@" + System.identityHashCode(this).toString(16).padStart(8, '0') else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0')) }