299 lines
12 KiB
Kotlin
299 lines
12 KiB
Kotlin
/*
|
|
* 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
|
|
}
|