Create simple interpreter that evaluate constant values

This commit is contained in:
Ivan Kylchik
2019-11-04 13:38:18 +03:00
parent a27a07ce81
commit f6373a647e
4 changed files with 94 additions and 1 deletions
@@ -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<IrExpression, Nothing?> {
fun interpret(expression: IrExpression): IrExpression {
return visitExpression(expression, null)
}
private fun calculateBuiltIns(descriptor: FunctionDescriptor, args: List<IrExpression>): 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<IrExpression>()
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 <T> visitConst(expression: IrConst<T>, data: Nothing?): IrExpression {
return expression
}
}
@@ -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<String>)
@Suppress("UNCHECKED_CAST")
+1
View File
@@ -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)
+1 -1
View File
@@ -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())