Support interpretation of String's plus method and its extension version

This commit is contained in:
Ivan Kylchik
2020-06-10 23:04:07 +03:00
parent 94e36411fa
commit 9542eb36ec
5 changed files with 36 additions and 21 deletions
@@ -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
@@ -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<Variable>): List<Any?> {
@@ -470,6 +470,7 @@ val ternaryFunctions = mapOf<CompileTimeFunction, Function3<Any?, Any?, Any?, An
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'))
}
@@ -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 }
}
}
@@ -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'))
}