[IR] Rewrite generator for interpreter map

1. Reuse `DisabledIdSignatureDescriptor` to create `IrBuiltIns`
2. Provide FQ names for types to ensure method find correctness
This commit is contained in:
Ivan Kylchik
2023-05-31 15:41:33 +02:00
committed by Space Team
parent 6501d0b743
commit fe538a6174
5 changed files with 592 additions and 636 deletions
@@ -155,7 +155,7 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
} }
val receiverType = irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type val receiverType = irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type
val argsType = (listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }).map { it.getOnlyName() } val argsType = (listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }).map { it.fqNameWithNullability() }
val argsValues = args.wrap(this, irFunction) val argsValues = args.wrap(this, irFunction)
withExceptionHandler(environment) { withExceptionHandler(environment) {
@@ -170,8 +170,8 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
if (environment.configuration.platform.isJs()) { if (environment.configuration.platform.isJs()) {
if (signature.name == "toString") return signature.args[0].value.specialToStringForJs() if (signature.name == "toString") return signature.args[0].value.specialToStringForJs()
if (signature.name == "toFloat") signature.name = "toDouble" if (signature.name == "toFloat") signature.name = "toDouble"
signature.args.filter { it.type == "Float" }.forEach { signature.args.filter { it.type == "kotlin.Float" }.forEach {
it.type = "Double" it.type = "kotlin.Double"
it.value = it.value.toString().toDouble() it.value = it.value.toString().toDouble()
} }
} }
@@ -204,6 +204,14 @@ internal fun IrFunction.getArgsForMethodInvocation(
return argsValues return argsValues
} }
internal fun IrType.fqNameWithNullability(): String {
val fqName = classFqName?.toString()
?: (this.classifierOrNull?.owner as? IrDeclarationWithName)?.name?.asString()
?: render()
val nullability = if (this is IrSimpleType && this.nullability == SimpleTypeNullability.MARKED_NULLABLE) "?" else ""
return fqName + nullability
}
internal fun IrType.getOnlyName(): String { internal fun IrType.getOnlyName(): String {
if (this !is IrSimpleType) return this.render() if (this !is IrSimpleType) return this.render()
return (this.classifierOrFail.owner as IrDeclarationWithName).name.asString() + when (nullability) { return (this.classifierOrFail.owner as IrDeclarationWithName).name.asString() + when (nullability) {
+1
View File
@@ -45,6 +45,7 @@ dependencies {
wasmApi(kotlinStdlib()) wasmApi(kotlinStdlib())
interpreterApi(project(":compiler:ir.tree")) interpreterApi(project(":compiler:ir.tree"))
interpreterApi(project(":compiler:ir.psi2ir")) interpreterApi(project(":compiler:ir.psi2ir"))
interpreterApi(project(":compiler:ir.serialization.jvm"))
protobufApi(kotlinStdlib()) protobufApi(kotlinStdlib())
protobufCompareApi(projectTests(":kotlin-build-common")) protobufCompareApi(projectTests(":kotlin-build-common"))
nativeInteropRuntimeApi(kotlinStdlib()) nativeInteropRuntimeApi(kotlinStdlib())
@@ -5,11 +5,10 @@
package org.jetbrains.kotlin.generators.interpreter package org.jetbrains.kotlin.generators.interpreter
import org.jetbrains.kotlin.backend.jvm.serialization.DisabledIdSignatureDescriptor
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.PrimitiveType 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.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
@@ -19,10 +18,7 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.IdSignature import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.IdSignatureComposer
import org.jetbrains.kotlin.ir.util.KotlinMangler
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
@@ -39,6 +35,30 @@ fun main() {
fun generateMap(): String { fun generateMap(): String {
val sb = StringBuilder() val sb = StringBuilder()
val p = Printer(sb) val p = Printer(sb)
printPreamble(p)
val irBuiltIns = getIrBuiltIns()
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(irBuiltIns) + 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(File("license/COPYRIGHT_HEADER.txt").readText())
p.println() p.println()
p.println("@file:Suppress(\"DEPRECATION\", \"DEPRECATION_ERROR\", \"UNCHECKED_CAST\")") p.println("@file:Suppress(\"DEPRECATION\", \"DEPRECATION_ERROR\", \"UNCHECKED_CAST\")")
@@ -50,26 +70,6 @@ fun generateMap(): String {
p.println() p.println()
p.println("/** This file is generated by `./gradlew generateInterpreterMap`. DO NOT MODIFY MANUALLY */") p.println("/** This file is generated by `./gradlew generateInterpreterMap`. DO NOT MODIFY MANUALLY */")
p.println() 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>) { private fun generateInterpretUnaryFunction(p: Printer, unaryOperations: List<Operation>) {
@@ -138,9 +138,8 @@ private fun generateInterpretTernaryFunction(p: Printer, ternaryOperations: List
for ((name, operations) in ternaryOperations.groupBy(Operation::name)) { for ((name, operations) in ternaryOperations.groupBy(Operation::name)) {
p.println("\"$name\" -> when (typeA) {") p.println("\"$name\" -> when (typeA) {")
p.pushIndent() p.pushIndent()
for (operation in operations) { for (op in operations) {
val (typeA, typeB, typeC) = operation.parameterTypes p.println("\"${op.typeA}\" -> if (typeB == \"${op.typeB}\" && typeC == \"${op.typeC}\") return ${op.expressionString}")
p.println("\"$typeA\" -> if (typeB == \"$typeB\" && typeC == \"$typeC\") return ${operation.expressionString}")
} }
p.popIndent() p.popIndent()
p.println("}") p.println("}")
@@ -163,18 +162,22 @@ private fun castValue(name: String, type: String): String = when (type) {
private fun castValueParenthesized(name: String, type: String): String = private fun castValueParenthesized(name: String, type: String): String =
if (type == "Any?") name else "(${castValue(name, type)})" if (type == "Any?") name else "(${castValue(name, type)})"
private fun String.addKotlinPackage(): String =
if (this == "T" || this == "T0?") this else "kotlin.$this"
private data class Operation( private data class Operation(
val name: String, val name: String,
val parameterTypes: List<String>, private val parameterTypes: List<String>,
val isFunction: Boolean = true, val isFunction: Boolean = true,
val customExpression: String? = null, val customExpression: String? = null,
) { ) {
val typeA: String get() = parameterTypes[0] val typeA: String get() = parameterTypes[0].addKotlinPackage()
val typeB: String get() = parameterTypes[1] val typeB: String get() = parameterTypes[1].addKotlinPackage()
val typeC: String get() = parameterTypes[2].addKotlinPackage()
val expressionString: String val expressionString: String
get() { get() {
val receiver = castValueParenthesized("a", typeA) val receiver = castValueParenthesized("a", parameterTypes[0])
return when { return when {
name == BuiltInOperatorNames.EQEQEQ && parameterTypes.all { it == "Any?" } -> name == BuiltInOperatorNames.EQEQEQ && parameterTypes.all { it == "Any?" } ->
"if (a is Proxy && b is Proxy) a.state === b.state else a === b" "if (a is Proxy && b is Proxy) a.state === b.state else a === b"
@@ -217,13 +220,9 @@ private fun getOperationMap(argumentsCount: Int): MutableList<Operation> {
.filter { it.name !in excludedBinaryOperations } .filter { it.name !in excludedBinaryOperations }
for (function in compileTimeFunctions) { for (function in compileTimeFunctions) {
operationMap.add( val parameterTypes = listOf(classDescriptor.defaultType.constructor.toString()) +
Operation( function.valueParameters.map { it.type.toString() }
function.name.asString(), operationMap.add(Operation(function.name.asString(), parameterTypes, function is FunctionDescriptor))
listOf(classDescriptor.defaultType.constructor.toString()) + function.valueParameters.map { it.type.toString() },
function is FunctionDescriptor
)
)
} }
} }
@@ -278,16 +277,6 @@ private fun getExtensionOperationMap(): List<Operation> {
return operationMap 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 { private fun getIrMethodSymbolByName(methodName: String): String {
return when (methodName) { return when (methodName) {
BuiltInOperatorNames.LESS -> "<" BuiltInOperatorNames.LESS -> "<"
@@ -305,26 +294,8 @@ private fun getIrMethodSymbolByName(methodName: String): String {
@OptIn(ObsoleteDescriptorBasedAPI::class) @OptIn(ObsoleteDescriptorBasedAPI::class)
private fun getIrBuiltIns(): IrBuiltIns { 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 moduleDescriptor = ModuleDescriptorImpl(Name.special("<test-module>"), LockBasedStorageManager(""), DefaultBuiltIns.Instance)
val signaturer = object : IdSignatureComposer { val symbolTable = SymbolTable(DisabledIdSignatureDescriptor, IrFactoryImpl)
override fun composeSignature(descriptor: DeclarationDescriptor): IdSignature? = null val typeTranslator = TypeTranslatorImpl(symbolTable, LanguageVersionSettingsImpl.DEFAULT, moduleDescriptor)
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()
}
override val mangler: KotlinMangler.DescriptorMangler
get() = throw UnsupportedOperationException("Mangler is unavailable")
}
val symbolTable = SymbolTable(signaturer, IrFactoryImpl)
val typeTranslator = TypeTranslatorImpl(symbolTable, languageSettings, moduleDescriptor)
return IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable) return IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
} }