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