Files
kotlin-fork/generators/interpreter/GenerateInterpreterMap.kt
T
Ivan Kylchik 8234c9cec1 Add toString function with nullable receiver into builtins map
On JVM we don't have body for this function, so we must process it as
intrinsic or builtin
2021-06-07 15:35:06 +03:00

270 lines
12 KiB
Kotlin

/*
* 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.generators.interpreter
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.LanguageVersion
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.utils.Printer
import java.io.File
val DESTINATION = File("compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/builtins/IrBuiltInsMapGenerated.kt")
fun main() {
GeneratorsFileUtil.writeFileIfContentChanged(DESTINATION, generateMap())
}
fun generateMap(): String {
val sb = StringBuilder()
val p = Printer(sb)
p.println(File("license/COPYRIGHT.txt").readText())
p.println("@file:Suppress(\"DEPRECATION\", \"DEPRECATION_ERROR\", \"UNCHECKED_CAST\")")
p.println()
p.println("package org.jetbrains.kotlin.ir.interpreter.builtins")
p.println()
p.println("import org.jetbrains.kotlin.ir.interpreter.exceptions.InterpreterMethodNotFoundError")
p.println("import org.jetbrains.kotlin.ir.interpreter.proxy.Proxy")
p.println()
p.println("/** This file is generated by `./gradlew generateInterpreterMap`. DO NOT MODIFY MANUALLY */")
p.println()
val irBuiltIns = getIrBuiltIns()
generateInterpretUnaryFunction(p, getOperationMap(1).apply {
val irNullCheck = irBuiltIns.checkNotNullSymbol.owner
this += Operation(irNullCheck.name.asString(), listOf("T0?"), customExpression = "a!!")
this += Operation("toString", listOf("Any?"), customExpression = "a?.toString() ?: \"null\"")
})
generateInterpretBinaryFunction(p, getOperationMap(2) + getBinaryIrOperationMap(irBuiltIns))
generateInterpretTernaryFunction(p, getOperationMap(3))
return sb.toString()
}
private fun generateInterpretUnaryFunction(p: Printer, unaryOperations: List<Operation>) {
p.println("internal fun interpretUnaryFunction(name: String, type: String, a: Any?): Any? {")
p.pushIndent()
p.println("when (name) {")
p.pushIndent()
for ((name, operations) in unaryOperations.groupBy(Operation::name)) {
p.println("\"$name\" -> when (type) {")
p.pushIndent()
for (operation in operations) {
p.println("\"${operation.typeA}\" -> return ${operation.expressionString}")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println("}")
p.println("throw InterpreterMethodNotFoundError(\"Unknown function: \$name(\$type)\")")
p.popIndent()
p.println("}")
p.println()
}
private fun generateInterpretBinaryFunction(p: Printer, binaryOperations: List<Operation>) {
p.println("internal fun interpretBinaryFunction(name: String, typeA: String, typeB: String, a: Any?, b: Any?): Any? {")
p.pushIndent()
p.println("when (name) {")
p.pushIndent()
for ((name, operations) in binaryOperations.groupBy(Operation::name)) {
p.println("\"$name\" -> when (typeA) {")
p.pushIndent()
for ((typeA, operationsOnTypeA) in operations.groupBy(Operation::typeA)) {
val singleOperation = operationsOnTypeA.singleOrNull()
if (singleOperation != null) {
// Slightly improve readability if there's only one operation with such name and typeA.
p.println("\"$typeA\" -> if (typeB == \"${singleOperation.typeB}\") return ${singleOperation.expressionString}")
} else {
p.println("\"$typeA\" -> when (typeB) {")
p.pushIndent()
for ((typeB, operationsOnTypeB) in operationsOnTypeA.groupBy(Operation::typeB)) {
for (operation in operationsOnTypeB) {
p.println("\"$typeB\" -> return ${operation.expressionString}")
}
}
p.popIndent()
p.println("}")
}
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println("}")
p.println("throw InterpreterMethodNotFoundError(\"Unknown function: \$name(\$typeA, \$typeB)\")")
p.popIndent()
p.println("}")
p.println()
}
private fun generateInterpretTernaryFunction(p: Printer, ternaryOperations: List<Operation>) {
p.println("internal fun interpretTernaryFunction(name: String, typeA: String, typeB: String, typeC: String, a: Any?, b: Any?, c: Any?): Any {")
p.pushIndent()
p.println("when (name) {")
p.pushIndent()
for ((name, operations) in ternaryOperations.groupBy(Operation::name)) {
p.println("\"$name\" -> when (typeA) {")
p.pushIndent()
for (operation in operations) {
val (typeA, typeB, typeC) = operation.parameterTypes
p.println("\"$typeA\" -> if (typeB == \"$typeB\" && typeC == \"$typeC\") return ${operation.expressionString}")
}
p.popIndent()
p.println("}")
}
p.popIndent()
p.println("}")
p.println("throw InterpreterMethodNotFoundError(\"Unknown function: \$name(\$typeA, \$typeB, \$typeC)\")")
p.popIndent()
p.println("}")
p.println()
}
private fun castValue(name: String, type: String): String = when (type) {
"Any?", "T" -> name
"Array" -> "$name as Array<Any?>"
"Comparable" -> "$name as Comparable<Any?>"
else -> "$name as $type"
}
private fun castValueParenthesized(name: String, type: String): String =
if (type == "Any?") name else "(${castValue(name, type)})"
private data class Operation(
val name: String,
val parameterTypes: List<String>,
val isFunction: Boolean = true,
val customExpression: String? = null,
) {
val typeA: String get() = parameterTypes[0]
val typeB: String get() = parameterTypes[1]
val expressionString: String
get() {
val receiver = castValueParenthesized("a", typeA)
println(name)
return when {
name == IrBuiltIns.OperatorNames.EQEQEQ && parameterTypes.all { it == "Any?" } ->
"if (a is Proxy && b is Proxy) a.state === b.state else a === b"
customExpression != null -> customExpression
else -> buildString {
append(receiver)
append(".")
append(name)
if (isFunction) append("(")
parameterTypes.withIndex().drop(1).joinTo(this) { (index, type) ->
castValue(('a' + index).toString(), type)
}
if (isFunction) append(")")
}
}
}
}
private fun getOperationMap(argumentsCount: Int): MutableList<Operation> {
val builtIns = DefaultBuiltIns.Instance
val operationMap = mutableListOf<Operation>()
val allPrimitiveTypes = PrimitiveType.values().map { builtIns.getBuiltInClassByFqName(it.typeFqName) }
val arrays = PrimitiveType.values().map { builtIns.getPrimitiveArrayClassDescriptor(it) } + builtIns.array
val additionalBuiltIns = listOf(builtIns.string, builtIns.any, builtIns.charSequence, builtIns.number, builtIns.comparable)
fun CallableDescriptor.isFakeOverride(classDescriptor: ClassDescriptor): Boolean {
val isPrimitive = KotlinBuiltIns.isPrimitiveClass(classDescriptor) || KotlinBuiltIns.isString(classDescriptor.defaultType)
val isFakeOverridden = (this as? FunctionDescriptor)?.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE
return !isPrimitive && isFakeOverridden
}
for (classDescriptor in allPrimitiveTypes + additionalBuiltIns + arrays) {
val compileTimeFunctions = classDescriptor.unsubstitutedMemberScope.getContributedDescriptors()
.filterIsInstance<CallableDescriptor>()
.filter { !it.isFakeOverride(classDescriptor) && it.valueParameters.size + 1 == argumentsCount }
for (function in compileTimeFunctions) {
operationMap.add(
Operation(
function.name.asString(),
listOf(classDescriptor.defaultType.constructor.toString()) + function.valueParameters.map { it.type.toString() },
function is FunctionDescriptor
)
)
}
}
return operationMap
}
private fun getBinaryIrOperationMap(irBuiltIns: IrBuiltIns): List<Operation> {
val operationMap = mutableListOf<Operation>()
val irFunSymbols =
(irBuiltIns.lessFunByOperandType.values + irBuiltIns.lessOrEqualFunByOperandType.values +
irBuiltIns.greaterFunByOperandType.values + irBuiltIns.greaterOrEqualFunByOperandType.values +
irBuiltIns.eqeqSymbol + irBuiltIns.eqeqeqSymbol + irBuiltIns.ieee754equalsFunByOperandType.values +
irBuiltIns.andandSymbol + irBuiltIns.ororSymbol)
.map { it.owner }
for (function in irFunSymbols) {
val parametersTypes = function.valueParameters.map { it.type.originalKotlinType!!.toString() }
check(parametersTypes.size == 2) { "Couldn't add following method from ir builtins to operations map: ${function.name}" }
operationMap.add(
Operation(
function.name.asString(), parametersTypes,
customExpression = castValueParenthesized("a", parametersTypes[0]) + " " +
getIrMethodSymbolByName(function.name.asString()) + " " +
castValueParenthesized("b", parametersTypes[1])
)
)
}
return operationMap
}
private fun getIrMethodSymbolByName(methodName: String): String {
return when (methodName) {
IrBuiltIns.OperatorNames.LESS -> "<"
IrBuiltIns.OperatorNames.LESS_OR_EQUAL -> "<="
IrBuiltIns.OperatorNames.GREATER -> ">"
IrBuiltIns.OperatorNames.GREATER_OR_EQUAL -> ">="
IrBuiltIns.OperatorNames.EQEQ -> "=="
IrBuiltIns.OperatorNames.EQEQEQ -> "==="
IrBuiltIns.OperatorNames.IEEE754_EQUALS -> "=="
IrBuiltIns.OperatorNames.ANDAND -> "&&"
IrBuiltIns.OperatorNames.OROR -> "||"
else -> throw UnsupportedOperationException("Unknown ir operation \"$methodName\"")
}
}
private fun getIrBuiltIns(): IrBuiltIns {
val languageSettings = LanguageVersionSettingsImpl(LanguageVersion.KOTLIN_1_3, ApiVersion.KOTLIN_1_3)
val moduleDescriptor = ModuleDescriptorImpl(Name.special("<test-module>"), LockBasedStorageManager(""), DefaultBuiltIns.Instance)
val signaturer = object : IdSignatureComposer {
override fun composeSignature(descriptor: DeclarationDescriptor): IdSignature? = null
override fun composeEnumEntrySignature(descriptor: ClassDescriptor): IdSignature? = null
}
val symbolTable = SymbolTable(signaturer, IrFactoryImpl)
val typeTranslator = TypeTranslatorImpl(symbolTable, languageSettings, moduleDescriptor)
return IrBuiltIns(moduleDescriptor.builtIns, typeTranslator, symbolTable)
}