From f6373a647eb22450e69bb39af60c5bb820cbf1bf Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Mon, 4 Nov 2019 13:38:18 +0300 Subject: [PATCH] Create simple interpreter that evaluate constant values --- .../common/interpreter/IrInterpreter.kt | 88 +++++++++++++++++++ .../interpreter/builtins/CompileTimeUtils.kt | 4 + generators/build.gradle.kts | 1 + generators/evaluate/GenerateBuiltInsMap.kt | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt 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 new file mode 100644 index 00000000000..3272cbdde4e --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrInterpreter.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.common.interpreter + +import org.jetbrains.kotlin.backend.common.interpreter.builtins.CompileTimeFunction +import org.jetbrains.kotlin.backend.common.interpreter.builtins.binaryFunctions +import org.jetbrains.kotlin.backend.common.interpreter.builtins.unaryFunctions +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrConstKind +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.visitors.IrElementVisitor +import org.jetbrains.kotlin.types.TypeUtils + +class IrInterpreter : IrElementVisitor { + fun interpret(expression: IrExpression): IrExpression { + return visitExpression(expression, null) + } + + private fun calculateBuiltIns(descriptor: FunctionDescriptor, args: List): Any { + val methodName = descriptor.name.asString() + val receiverType = descriptor.dispatchReceiverParameter?.type ?: descriptor.extensionReceiverParameter?.type + val argsType = listOfNotNull(receiverType) + descriptor.valueParameters.map { TypeUtils.makeNotNullable(it.original.type) } + val argsValues = args.map { (it as? IrConst<*>)?.value ?: throw IllegalArgumentException("Builtin functions accept only const args") } + val signature = CompileTimeFunction(methodName, argsType.map { it.toString() }) + return when (argsType.size) { + 1 -> { + val function = unaryFunctions[signature] + ?: throw NoSuchMethodException("For given function $signature there is no entry in unary map") + function.invoke(argsValues.first()) + } + 2 -> { + val function = binaryFunctions[signature] + ?: throw NoSuchMethodException("For given function $signature there is no entry in binary map") + function.invoke(argsValues[0], argsValues[1]) + } + else -> throw UnsupportedOperationException("Unsupported number of arguments") + } + } + + private fun Any.toIrConst(expression: IrExpression): IrConst<*> { + return when (this) { + is Boolean -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Boolean, this) + is Char -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Char, this) + is Byte -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Byte, this) + is Short -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Short, this) + is Int -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Int, this) + is Long -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Long, this) + is String -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.String, this) + is Float -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Float, this) + is Double -> IrConstImpl(expression.startOffset, expression.endOffset, expression.type, IrConstKind.Double, this) + else -> throw UnsupportedOperationException("Unsupported const element type $this") + } + } + + override fun visitElement(element: IrElement, data: Nothing?): IrExpression { + return when (element) { + is IrCall -> visitCall(element, data) + else -> TODO("not supported") + } + } + + override fun visitCall(expression: IrCall, data: Nothing?): IrExpression { + val dispatchReceiver = expression.dispatchReceiver?.accept(this, data) + val extensionReceiver = expression.extensionReceiver?.accept(this, data) + val receiverValue = dispatchReceiver ?: extensionReceiver + val args = mutableListOf() + for (i in 0 until expression.valueArgumentsCount) { + expression.getValueArgument(i)?.accept(this, data)?.also { args += it } + } + + if (expression.symbol.owner.body == null) { + return calculateBuiltIns(expression.symbol.descriptor, args + listOfNotNull(receiverValue)).toIrConst(expression) + } + + return super.visitCall(expression, data) + } + + override fun visitConst(expression: IrConst, data: Nothing?): IrExpression { + return expression + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt index f417dd55e2a..69da24f64bb 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/CompileTimeUtils.kt @@ -5,6 +5,10 @@ package org.jetbrains.kotlin.backend.common.interpreter.builtins +import org.jetbrains.kotlin.name.FqName + +val compileTimeAnnotation = FqName("kotlin.CompileTimeCalculation") + data class CompileTimeFunction(val methodName: String, val args: List) @Suppress("UNCHECKED_CAST") diff --git a/generators/build.gradle.kts b/generators/build.gradle.kts index 748cba6cfca..d72fca870c4 100644 --- a/generators/build.gradle.kts +++ b/generators/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { builtinsApi("org.jetbrains.kotlin:kotlin-stdlib:$bootstrapKotlinVersion") { isTransitive = false } evaluateApi(project(":core:deserialization")) evaluateApi(project(":compiler:ir.serialization.jvm")) // used to get ir builtins + evaluateApi(project(":compiler:ir.backend.common")) testCompile(builtinsSourceSet.output) testCompile(evaluateSourceSet.output) diff --git a/generators/evaluate/GenerateBuiltInsMap.kt b/generators/evaluate/GenerateBuiltInsMap.kt index 07ef20efe46..191e4eefac1 100644 --- a/generators/evaluate/GenerateBuiltInsMap.kt +++ b/generators/evaluate/GenerateBuiltInsMap.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.generators.evaluate +import org.jetbrains.kotlin.backend.common.interpreter.builtins.compileTimeAnnotation import org.jetbrains.kotlin.backend.jvm.serialization.JvmIdSignatureDescriptor import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns @@ -29,7 +30,6 @@ import org.jetbrains.kotlin.utils.Printer import java.io.File val DESTINATION = File("compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/builtins/IrBuiltInsMapGenerated.kt") -val compileTimeAnnotation = FqName("kotlin.CompileTimeCalculation") fun main() { DESTINATION.writeText(generateMap())