blob: 9345d1867097ad751fe53cbd66163a920b981ee9 [file] [log] [blame]
/*
* 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.BuiltInOperatorNames
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.ir.util.IdSignatureComposer
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
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_HEADER.txt").readText())
p.println()
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\"")
this += Operation("code", listOf("Char"), customExpression = "(a as Char).code")
// TODO next operation can be dropped after serialization introduction
this += Operation("toString", listOf("Unit"), customExpression = "Unit.toString()")
})
generateInterpretBinaryFunction(
p,
getOperationMap(2) + getBinaryIrOperationMap(irBuiltIns) + getExtensionOperationMap() + getAdditionalEqualsOperationMap()
)
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)
return when {
name == BuiltInOperatorNames.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, builtIns.throwable
)
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
}
val excludedBinaryOperations = listOf("rangeUntil").map { Name.identifier(it) }
for (classDescriptor in allPrimitiveTypes + additionalBuiltIns + arrays) {
val compileTimeFunctions = classDescriptor.unsubstitutedMemberScope.getContributedDescriptors()
.filterIsInstance<CallableDescriptor>()
.filter { !it.isFakeOverride(classDescriptor) && it.valueParameters.size + 1 == argumentsCount }
.filter { it.name !in excludedBinaryOperations }
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
}
// TODO can be drop after serialization introduction
private fun getExtensionOperationMap(): List<Operation> {
val operationMap = mutableListOf<Operation>()
val integerTypes = listOf(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.LONG).map { it.typeName.asString() }
val fpTypes = listOf(PrimitiveType.FLOAT, PrimitiveType.DOUBLE).map { it.typeName.asString() }
for (type in integerTypes) {
for (otherType in integerTypes) {
operationMap.add(Operation("mod", listOf(type, otherType), isFunction = true))
operationMap.add(Operation("floorDiv", listOf(type, otherType), isFunction = true))
}
}
for (type in fpTypes) {
for (otherType in fpTypes) {
operationMap.add(Operation("mod", listOf(type, otherType), isFunction = true))
}
}
return operationMap
}
// We need this additional list to properly interpret functions like `Boolean.equals(Boolean) in Native`
// Probably can be dropped after KT-57344 fix
private fun getAdditionalEqualsOperationMap(): List<Operation> {
val builtIns = DefaultBuiltIns.Instance
return PrimitiveType.values().map { builtIns.getBuiltInClassByFqName(it.typeFqName) }.map {
val type = it.defaultType.constructor.toString()
Operation("equals", listOf(type, type), isFunction = true)
}
}
private fun getIrMethodSymbolByName(methodName: String): String {
return when (methodName) {
BuiltInOperatorNames.LESS -> "<"
BuiltInOperatorNames.LESS_OR_EQUAL -> "<="
BuiltInOperatorNames.GREATER -> ">"
BuiltInOperatorNames.GREATER_OR_EQUAL -> ">="
BuiltInOperatorNames.EQEQ -> "=="
BuiltInOperatorNames.EQEQEQ -> "==="
BuiltInOperatorNames.IEEE754_EQUALS -> "=="
BuiltInOperatorNames.ANDAND -> "&&"
BuiltInOperatorNames.OROR -> "||"
else -> throw UnsupportedOperationException("Unknown ir operation \"$methodName\"")
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
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
override fun composeAnonInitSignature(descriptor: ClassDescriptor): IdSignature? = null
override fun composeFieldSignature(descriptor: PropertyDescriptor): IdSignature? = null
override fun withFileSignature(fileSignature: IdSignature.FileSignature, body: () -> Unit) {
body()
}
}
val symbolTable = SymbolTable(signaturer, IrFactoryImpl)
val typeTranslator = TypeTranslatorImpl(symbolTable, languageSettings, moduleDescriptor)
return IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
}