/* * 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.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil import org.jetbrains.kotlin.ir.BuiltInOperatorNames import org.jetbrains.kotlin.name.Name 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") private val integerTypes = listOf(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.LONG).map { it.typeName.asString() } private val fpTypes = listOf(PrimitiveType.FLOAT, PrimitiveType.DOUBLE).map { it.typeName.asString() } private val numericTypes = PrimitiveType.NUMBER_TYPES.map { it.typeName.asString() } fun main() { GeneratorsFileUtil.writeFileIfContentChanged(DESTINATION, generateMap()) } fun generateMap(): String { val sb = StringBuilder() val p = Printer(sb) printPreamble(p) val unaryOperations = getOperationMap(1).apply { this += Operation(BuiltInOperatorNames.CHECK_NOT_NULL, 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()") } val binaryOperations = getOperationMap(2) + getBinaryIrOperationMap() + getExtensionOperationMap() val ternaryOperations = getOperationMap(3) generateInterpretUnaryFunction(p, unaryOperations) generateInterpretBinaryFunction(p, binaryOperations) generateInterpretTernaryFunction(p, ternaryOperations) return sb.toString() } private fun printPreamble(p: Printer) { 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() } 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 (op in operations) { p.println("\"${op.typeA}\" -> if (typeB == \"${op.typeB}\" && typeC == \"${op.typeC}\") return ${op.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 data class Operation( val name: String, private val parameterTypes: List, val isFunction: Boolean = true, val customExpression: String? = null, ) { val typeA: String get() = parameterTypes[0].addKotlinPackage() val typeB: String get() = parameterTypes[1].addKotlinPackage() val typeC: String get() = parameterTypes[2].addKotlinPackage() val expressionString: String get() { val receiver = castValueParenthesized("a", parameterTypes[0]) return when { name == BuiltInOperatorNames.EQEQEQ -> "if (a is Proxy && b is Proxy) a.state === b.state else a === b" customExpression != null -> customExpression getIrMethodSymbolByName(name) != null -> { buildString { append(castValueParenthesized("a", parameterTypes[0])) append(" ") append(getIrMethodSymbolByName(name)) append(" ") append(castValueParenthesized("b", parameterTypes[0])) } } 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 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 -> null } } private fun String.addKotlinPackage(): String = if (this == "T" || this == "T0?") this else "kotlin.$this" 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 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) { val parameterTypes = listOf(classDescriptor.defaultType.constructor.toString()) + function.valueParameters.map { it.type.toString() } operationMap.add(Operation(function.name.asString(), parameterTypes, function is FunctionDescriptor)) } } return operationMap } private fun getBinaryIrOperationMap(): List { val operationMap = mutableListOf() fun addOperation(function: String, type: String) { operationMap.add(Operation(function, listOf(type, type))) } val compareFunction = setOf( BuiltInOperatorNames.LESS, BuiltInOperatorNames.LESS_OR_EQUAL, BuiltInOperatorNames.GREATER, BuiltInOperatorNames.GREATER_OR_EQUAL ) for (function in compareFunction) { for (type in numericTypes) { addOperation(function, type) } } addOperation(BuiltInOperatorNames.EQEQ, "Any?") addOperation(BuiltInOperatorNames.EQEQEQ, "Any?") for (type in fpTypes) { addOperation(BuiltInOperatorNames.IEEE754_EQUALS, "$type?") } for (function in setOf(BuiltInOperatorNames.ANDAND, BuiltInOperatorNames.OROR)) { addOperation(function, PrimitiveType.BOOLEAN.typeName.asString()) } return operationMap } // TODO can be drop after serialization introduction private fun getExtensionOperationMap(): List { val operationMap = mutableListOf() for (type in integerTypes) { for (otherType in integerTypes) { operationMap.add(Operation("mod", listOf(type, otherType))) operationMap.add(Operation("floorDiv", listOf(type, otherType))) } } for (type in fpTypes) { for (otherType in fpTypes) { operationMap.add(Operation("mod", listOf(type, otherType))) } } return operationMap }