Support interpretation of String's plus method and its extension version
This commit is contained in:
+27
-17
@@ -172,13 +172,18 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
}
|
}
|
||||||
val args = stack.getAll().map { it.state }
|
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 argsType = listOfNotNull(receiverType) + descriptor.valueParameters.map { it.original.type }
|
||||||
val argsValues = args.map {
|
val argsValues = args.map {
|
||||||
when (it) {
|
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
|
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() })
|
val signature = CompileTimeFunction(methodName, argsType.map { it.toString() })
|
||||||
@@ -684,26 +689,31 @@ class IrInterpreter(irModule: IrModuleFragment) {
|
|||||||
val result = StringBuilder()
|
val result = StringBuilder()
|
||||||
expression.arguments.forEach {
|
expression.arguments.forEach {
|
||||||
it.interpret().check { executionResult -> return executionResult }
|
it.interpret().check { executionResult -> return executionResult }
|
||||||
result.append(
|
interpretToString(stack.popReturnValue()).check { executionResult -> return executionResult }
|
||||||
when (val returnValue = stack.popReturnValue()) {
|
result.append(stack.popReturnValue().asString())
|
||||||
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")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.pushReturnValue(result.toString().toState(expression.type))
|
stack.pushReturnValue(result.toString().toState(expression.type))
|
||||||
return Next
|
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 {
|
private fun interpretFunctionExpression(expression: IrFunctionExpression): ExecutionResult {
|
||||||
val lambda = Lambda(expression.function, expression.type.classOrNull!!.owner)
|
val lambda = Lambda(expression.function, expression.type.classOrNull!!.owner)
|
||||||
if (expression.function.isLocal) lambda.fields.addAll(stack.getAll()) // TODO save only necessary declarations
|
if (expression.function.isLocal) lambda.fields.addAll(stack.getAll()) // TODO save only necessary declarations
|
||||||
|
|||||||
+2
-2
@@ -163,8 +163,8 @@ fun getPrimitiveClass(fqName: String, asObject: Boolean = false): Class<*>? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrType.getFqName(): String? {
|
fun IrType.getFqName(withNullableSymbol: Boolean = false): String? {
|
||||||
return this.classOrNull?.owner?.fqNameWhenAvailable?.asString()
|
return this.classOrNull?.owner?.fqNameWhenAvailable?.asString()?.let { if (this.isNullable() && withNullableSymbol) "$it?" else it }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun IrFunction.getArgsForMethodInvocation(args: List<Variable>): List<Any?> {
|
fun IrFunction.getArgsForMethodInvocation(args: List<Variable>): List<Any?> {
|
||||||
|
|||||||
+1
@@ -470,6 +470,7 @@ val ternaryFunctions = mapOf<CompileTimeFunction, Function3<Any?, Any?, Any?, An
|
|||||||
|
|
||||||
private fun Any.defaultToString(): String {
|
private fun Any.defaultToString(): String {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
|
is Lambda -> this.toString()
|
||||||
is State -> "${this.irClass.name}@" + System.identityHashCode(this).toString(16).padStart(8, '0')
|
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'))
|
else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0'))
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -6,11 +6,11 @@
|
|||||||
package org.jetbrains.kotlin.backend.common.interpreter.state
|
package org.jetbrains.kotlin.backend.common.interpreter.state
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.interpreter.equalTo
|
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.backend.common.interpreter.stack.Variable
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
|
|
||||||
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
import org.jetbrains.kotlin.ir.util.nameForIrSerialization
|
||||||
|
|
||||||
class Lambda(val irFunction: IrFunction, override val irClass: IrClass) : State {
|
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 {
|
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 {
|
private fun Any.defaultToString(): String {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
|
is Lambda -> this.toString()
|
||||||
is State -> "${'$'}{this.irClass.name}@" + System.identityHashCode(this).toString(16).padStart(8, '0')
|
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'))
|
else -> this.toString().replaceAfter("@", System.identityHashCode(this).toString(16).padStart(8, '0'))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user