| /* |
| * 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) |
| } |