/* * 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()) generateInterpretTernaryFunction(p, getOperationMap(3)) return sb.toString() } private fun generateInterpretUnaryFunction(p: Printer, unaryOperations: List) { 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) { 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) { 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" "Comparable" -> "$name as Comparable" 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, 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 { val builtIns = DefaultBuiltIns.Instance val operationMap = mutableListOf() 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() .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 { val operationMap = mutableListOf() 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 { val operationMap = mutableListOf() 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 } 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(""), 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) }