[IR] Simplify calculateBuiltIns method in interpreter

This commit is contained in:
Ivan Kylchik
2023-05-22 18:39:54 +02:00
committed by Space Team
parent 7acaf6e473
commit 19b0ff8fb4
7 changed files with 30 additions and 41 deletions
@@ -144,6 +144,9 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
return true
}
private data class Signature(var name: String, var args: List<Arg>)
private data class Arg(var type: String, var value: Any?)
private fun calculateBuiltIns(irFunction: IrFunction, args: List<State>) {
val methodName = when (val property = irFunction.property?.symbol) {
null -> irFunction.name.asString()
@@ -151,42 +154,37 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
}
val receiverType = irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type
val argsType = (listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }).map {
// TODO: for consistency with current K/JS implementation Float constant should be treated as a Double (KT-35422)
if (environment.configuration.treatFloatInSpecialWay && it.makeNotNull().isFloat()) {
if (it.isNullable()) irBuiltIns.doubleType.makeNullable() else irBuiltIns.doubleType
} else {
it
}
}
val argsType = (listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }).map { it.getOnlyName() }
val argsValues = args.wrap(this, irFunction)
// TODO replace unary, binary, ternary functions with vararg
withExceptionHandler(environment) {
val result = when (argsType.size) {
1 -> when {
methodName == "toString" && environment.configuration.treatFloatInSpecialWay && (argsValues[0] is Double || argsValues[0] is Float) -> {
argsValues[0].specialToStringForJs()
}
else -> interpretUnaryFunction(methodName, argsType[0].getOnlyName(), argsValues[0])
}
2 -> when (methodName) {
"rangeTo" -> return calculateRangeTo(irFunction.returnType, args)
else -> interpretBinaryFunction(
methodName, argsType[0].getOnlyName(), argsType[1].getOnlyName(), argsValues[0], argsValues[1]
)
}
3 -> interpretTernaryFunction(
methodName, argsType[0].getOnlyName(), argsType[1].getOnlyName(), argsType[2].getOnlyName(),
argsValues[0], argsValues[1], argsValues[2]
)
else -> throw InterpreterError("Unsupported number of arguments for invocation as builtin function: $methodName")
}
if (methodName == "rangeTo") return calculateRangeTo(irFunction.returnType, args)
val result = interpretBuiltinFunction(Signature(methodName, argsType.zip(argsValues).map { Arg(it.first, it.second) }))
// TODO check "result is Unit"
callStack.pushState(environment.convertToState(result, result.getType(irFunction.returnType)))
}
}
private fun interpretBuiltinFunction(signature: Signature): Any? {
if (environment.configuration.treatFloatInSpecialWay) {
if (signature.name == "toString") return signature.args[0].value.specialToStringForJs()
if (signature.name == "toFloat") signature.name = "toDouble"
signature.args.filter { it.type == "Float" }.forEach {
it.type = "Double"
it.value = it.value.toString().toDouble()
}
}
val name = signature.name
val args = signature.args
return when (args.size) {
1 -> interpretUnaryFunction(name, args[0].type, args[0].value)
2 -> interpretBinaryFunction(name, args[0].type, args[1].type, args[0].value, args[1].value)
3 -> interpretTernaryFunction(name, args[0].type, args[1].type, args[2].type, args[0].value, args[1].value, args[2].value)
else -> throw InterpreterError("Unsupported number of arguments for invocation as builtin function: $name")
}
}
private fun calculateRangeTo(type: IrType, args: List<State>) {
val constructor = type.classOrNull!!.owner.constructors.first()
val constructorCall = constructor.createConstructorCall()
@@ -395,7 +395,7 @@ private fun unfoldStringConcatenation(expression: IrStringConcatenation, environ
val explicitToStringCheck = fun() {
when (val state = callStack.peekState()) {
is Primitive<*> -> {
// This block is not really needed, but this way it is easier to handle `toString` with `treatFloatInSpecialWay` enabled.
// This block is not really needed, but this way it is easier to handle `toString` for JS.
callStack.popState()
val toStringCall = IrCallImpl.fromSymbolOwner(
UNDEFINED_OFFSET, UNDEFINED_OFFSET,
@@ -36,7 +36,7 @@ import org.jetbrains.kotlin.name.Name
internal val TEMP_CLASS_FOR_INTERPRETER = object : IrDeclarationOriginImpl("TEMP_CLASS_FOR_INTERPRETER") {}
internal val TEMP_FUNCTION_FOR_INTERPRETER = object : IrDeclarationOriginImpl("TEMP_FUNCTION_FOR_INTERPRETER") {}
fun Any?.toIrConstOrNull(irType: IrType, startOffset: Int = SYNTHETIC_OFFSET, endOffset: Int = SYNTHETIC_OFFSET): IrConst<*>? {
private fun Any?.toIrConstOrNull(irType: IrType, startOffset: Int = SYNTHETIC_OFFSET, endOffset: Int = SYNTHETIC_OFFSET): IrConst<*>? {
if (this == null) return IrConstImpl.constNull(startOffset, endOffset, irType)
val constType = irType.makeNotNull().removeAnnotations()
@@ -61,13 +61,6 @@ class IrInterpreterCommonChecker : IrInterpreterChecker {
val owner = expression.symbol.owner
if (!data.mode.canEvaluateFunction(owner)) return false
// We disable `toFloat` folding on K/JS till `toFloat` is fixed (KT-35422)
// This check must be placed here instead of CallInterceptor because we still
// want to evaluate (1) `const val` expressions and (2) values in annotations.
if (owner.name.asString() == "toFloat" && data.interpreterConfiguration.treatFloatInSpecialWay) {
return super.visitCall(expression, data)
}
if (expression.dispatchReceiver.isAccessToNotNullableObject()) {
return expression.isGetterToConstVal()
}
@@ -35,8 +35,6 @@ internal fun State.wrap(callInterceptor: CallInterceptor, remainArraysAsIs: Bool
is Primitive<*> -> when {
this.isNull() -> null
this.type.isArray() || this.type.isPrimitiveArray() -> if (remainArraysAsIs) this else this.value
// TODO: for consistency with current K/JS implementation Float constant should be treated as a Double (KT-35422)
this.type.isFloat() && callInterceptor.environment.configuration.treatFloatInSpecialWay -> this.value.toString().toDouble()
else -> this.value
}
is Common -> this.asProxy(callInterceptor, extendFrom)
@@ -63,7 +63,7 @@ internal abstract class IrConstAnnotationTransformer(
}
private fun IrExpression.transformSingleArg(expectedType: IrType): IrExpression {
if (this.canBeInterpreted(configuration = interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) {
if (this.canBeInterpreted()) {
return this.interpret(failAsError = true).convertToConstIfPossible(expectedType)
} else if (this is IrConstructorCall) {
transformAnnotation(this)
@@ -44,7 +44,7 @@ internal class IrConstExpressionTransformer(
if (!isConst) return super.visitField(declaration, data)
val getField = declaration.createGetField()
if (getField.canBeInterpreted(interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) {
if (getField.canBeInterpreted()) {
initializer.expression = expression.interpret(failAsError = true)
}