| /* |
| * 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<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 (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<String>, |
| 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<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 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) { |
| 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<Operation> { |
| val operationMap = mutableListOf<Operation>() |
| |
| 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<Operation> { |
| val operationMap = mutableListOf<Operation>() |
| |
| 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 |
| } |