[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 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)
withExceptionHandler(environment) {
@@ -170,8 +170,8 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
if (environment.configuration.platform.isJs()) {
if (signature.name == "toString") return signature.args[0].value.specialToStringForJs()
if (signature.name == "toFloat") signature.name = "toDouble"
signature.args.filter { it.type == "Float" }.forEach {
it.type = "Double"
signature.args.filter { it.type == "kotlin.Float" }.forEach {
it.type = "kotlin.Double"
it.value = it.value.toString().toDouble()
}
}
@@ -204,6 +204,14 @@ internal fun IrFunction.getArgsForMethodInvocation(
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 {
if (this !is IrSimpleType) return this.render()
return (this.classifierOrFail.owner as IrDeclarationWithName).name.asString() + when (nullability) {
+1
View File
@@ -45,6 +45,7 @@ dependencies {
wasmApi(kotlinStdlib())
interpreterApi(project(":compiler:ir.tree"))
interpreterApi(project(":compiler:ir.psi2ir"))
interpreterApi(project(":compiler:ir.serialization.jvm"))
protobufApi(kotlinStdlib())
protobufCompareApi(projectTests(":kotlin-build-common"))
nativeInteropRuntimeApi(kotlinStdlib())
@@ -5,11 +5,10 @@
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.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
@@ -19,10 +18,7 @@ import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.types.impl.originalKotlinType
import org.jetbrains.kotlin.ir.util.IdSignature
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.ir.util.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
@@ -39,6 +35,30 @@ fun main() {
fun generateMap(): String {
val sb = StringBuilder()
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()
p.println("@file:Suppress(\"DEPRECATION\", \"DEPRECATION_ERROR\", \"UNCHECKED_CAST\")")
@@ -50,26 +70,6 @@ fun generateMap(): String {
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\"")
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>) {
@@ -138,9 +138,8 @@ private fun generateInterpretTernaryFunction(p: Printer, ternaryOperations: List
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}")
for (op in operations) {
p.println("\"${op.typeA}\" -> if (typeB == \"${op.typeB}\" && typeC == \"${op.typeC}\") return ${op.expressionString}")
}
p.popIndent()
p.println("}")
@@ -163,18 +162,22 @@ private fun castValue(name: String, type: String): String = when (type) {
private fun castValueParenthesized(name: String, type: String): String =
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(
val name: String,
val parameterTypes: List<String>,
private 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 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", typeA)
val receiver = castValueParenthesized("a", parameterTypes[0])
return when {
name == BuiltInOperatorNames.EQEQEQ && parameterTypes.all { it == "Any?" } ->
"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 }
for (function in compileTimeFunctions) {
operationMap.add(
Operation(
function.name.asString(),
listOf(classDescriptor.defaultType.constructor.toString()) + function.valueParameters.map { it.type.toString() },
function is FunctionDescriptor
)
)
val parameterTypes = listOf(classDescriptor.defaultType.constructor.toString()) +
function.valueParameters.map { it.type.toString() }
operationMap.add(Operation(function.name.asString(), parameterTypes, function is FunctionDescriptor))
}
}
@@ -278,16 +277,6 @@ private fun getExtensionOperationMap(): List<Operation> {
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 {
return when (methodName) {
BuiltInOperatorNames.LESS -> "<"
@@ -305,26 +294,8 @@ private fun getIrMethodSymbolByName(methodName: String): String {
@OptIn(ObsoleteDescriptorBasedAPI::class)
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
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)
val symbolTable = SymbolTable(DisabledIdSignatureDescriptor, IrFactoryImpl)
val typeTranslator = TypeTranslatorImpl(symbolTable, LanguageVersionSettingsImpl.DEFAULT, moduleDescriptor)
return IrBuiltInsOverDescriptors(moduleDescriptor.builtIns, typeTranslator, symbolTable)
}