Add new "invent name" phase to generate names for regenerated classes

This commit is contained in:
Ivan Kylchik
2022-12-16 14:43:13 +01:00
committed by Space Team
parent e159f7f682
commit 707c5f03b8
4 changed files with 85 additions and 20 deletions
@@ -6,19 +6,25 @@
package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.ir.getAdditionalStatementsFromInlinedBlock
import org.jetbrains.kotlin.backend.common.ir.getNonDefaultAdditionalStatementsFromInlinedBlock
import org.jetbrains.kotlin.backend.common.ir.getOriginalStatementsFromInlinedBlock
import org.jetbrains.kotlin.backend.common.ir.isFunctionInlining
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.isAnonymousObject
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.NameUtils
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
import kotlin.collections.set
abstract class InventNamesForLocalClasses(private val allowTopLevelCallables: Boolean) : FileLoweringPass {
abstract class InventNamesForLocalClasses(
private val allowTopLevelCallables: Boolean,
private val generateNamesForRegeneratedObjects: Boolean = false
) : FileLoweringPass {
protected abstract fun computeTopLevelClassName(clazz: IrClass): String
protected abstract fun sanitizeNameIfNeeded(name: String): String
@@ -33,7 +39,7 @@ abstract class InventNamesForLocalClasses(private val allowTopLevelCallables: Bo
* @property enclosingName internal name of the enclosing class (including anonymous classes, local objects and callable references)
* @property isLocal true if the next declaration to be encountered in the IR tree is local
*/
private data class Data(val enclosingName: String?, val isLocal: Boolean) {
private data class Data(val enclosingName: String?, val isLocal: Boolean, val processingInlinedFunction: Boolean = false) {
fun makeLocal(): Data = if (isLocal) this else copy(isLocal = true)
}
@@ -41,6 +47,25 @@ abstract class InventNamesForLocalClasses(private val allowTopLevelCallables: Bo
private val anonymousClassesCount = mutableMapOf<String, Int>()
private val localFunctionNames = mutableMapOf<IrFunctionSymbol, String>()
override fun visitContainerExpression(expression: IrContainerExpression, data: Data) {
if (expression is IrInlinedFunctionBlock && !generateNamesForRegeneratedObjects) {
return expression.getNonDefaultAdditionalStatementsFromInlinedBlock().forEach { it.accept(this, data) }
}
if (!data.processingInlinedFunction && expression is IrInlinedFunctionBlock && expression.isFunctionInlining()) {
expression.getAdditionalStatementsFromInlinedBlock().forEach { it.accept(this, data) }
val inlinedAt = expression.inlineCall.symbol.owner.name.asString()
val newData = data.copy(
enclosingName = data.enclosingName + "$\$inlined\$$inlinedAt", isLocal = true, processingInlinedFunction = true
)
expression.getOriginalStatementsFromInlinedBlock().forEach { it.accept(this, newData) }
return
}
super.visitContainerExpression(expression, data)
}
override fun visitClass(declaration: IrClass, data: Data) {
if (!data.isLocal) {
// This is not a local class, so we need not invent a name for it, the type mapper will correctly compute it
@@ -99,6 +124,8 @@ abstract class InventNamesForLocalClasses(private val allowTopLevelCallables: Bo
localFunctionNames[declaration.symbol] = name
}
}
declaration is IrVariable && generateNamesForRegeneratedObjects || data.processingInlinedFunction -> enclosingName
enclosingName != null -> "$enclosingName$$simpleName"
else -> simpleName
}
@@ -115,6 +142,10 @@ abstract class InventNamesForLocalClasses(private val allowTopLevelCallables: Bo
}
override fun visitFunctionReference(expression: IrFunctionReference, data: Data) {
if (data.processingInlinedFunction && expression.attributeOwnerIdBeforeInline == null) {
// skip IrFunctionReference from `singleArgumentInlineFunction`
return
}
val internalName = localFunctionNames[expression.symbol] ?: inventName(null, data)
putLocalClassName(expression, internalName)
@@ -342,6 +342,7 @@ private val jvmFilePhases = listOf(
// makePatchParentsPhase(),
removeDuplicatedInlinedLocalClasses,
inventNamesForInlinedLocalClassesPhase,
jvmLocalClassExtractionPhase,
staticCallableReferencePhase,
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.codegen.coroutines.*
import org.jetbrains.kotlin.codegen.inline.coroutines.FOR_INLINE_SUFFIX
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
@@ -36,9 +37,7 @@ import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.extractTypeParameters
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.ir.visitors.*
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -291,9 +290,8 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower
}
private fun addContinuationObjectAndContinuationParameterToSuspendFunctions(irFile: IrFile) {
class MutableFlag(var capturesCrossinline: Boolean)
irFile.accept(object : IrElementTransformer<MutableFlag?> {
override fun visitClass(declaration: IrClass, data: MutableFlag?): IrStatement {
irFile.accept(object : IrElementTransformerVoid() {
override fun visitClass(declaration: IrClass): IrStatement {
declaration.transformDeclarationsFlat {
if (it is IrSimpleFunction && it.isSuspend)
return@transformDeclarationsFlat transformToView(it)
@@ -304,9 +302,9 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower
}
private fun transformToView(function: IrSimpleFunction): List<IrFunction> {
val flag = MutableFlag(false)
function.accept(this, flag)
function.accept(this, null)
val capturesCrossinline = function.isCapturingCrossinline()
val view = function.suspendFunctionViewOrStub(context)
val continuationParameter = view.continuationParameter()
val parameterMap = function.explicitParameters.zip(view.explicitParameters.filter { it != continuationParameter }).toMap()
@@ -327,7 +325,7 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower
)
}
if (flag.capturesCrossinline || function.isInline) {
if (capturesCrossinline || function.isInline) {
result += context.irFactory.buildFun {
containerSource = view.containerSource
name = Name.identifier(context.defaultMethodSignatureMapper.mapFunctionName(view) + FOR_INLINE_SUFFIX)
@@ -359,7 +357,7 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower
newFunction,
view.dispatchReceiverParameter,
function as IrAttributeContainer,
flag.capturesCrossinline
capturesCrossinline
)
if (newFunction.body is IrExpressionBody) {
+irReturn(newFunction.body!!.statements[0] as IrExpression)
@@ -372,10 +370,26 @@ private class AddContinuationLowering(context: JvmBackendContext) : SuspendLower
return result
}
override fun visitFieldAccess(expression: IrFieldAccessExpression, data: MutableFlag?): IrExpression {
if (expression.symbol.owner.origin == LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE)
data?.capturesCrossinline = true
return super.visitFieldAccess(expression, data)
private fun IrSimpleFunction.isCapturingCrossinline(): Boolean {
var capturesCrossinline = false
(this.attributeOwnerIdBeforeInline ?: this).acceptVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitFieldAccess(expression: IrFieldAccessExpression) {
if (expression.symbol.owner.origin == LocalDeclarationsLowering.DECLARATION_ORIGIN_FIELD_FOR_CROSSINLINE_CAPTURED_VALUE) {
capturesCrossinline = true
return
}
super.visitFieldAccess(expression)
}
override fun visitClass(declaration: IrClass) {
return
}
})
return capturesCrossinline
}
}, null)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.lower.InventNamesForLocalClasses
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.phaser.makeIrModulePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.codegen.JvmCodegenUtil
@@ -25,7 +26,17 @@ val inventNamesForLocalClassesPhase = makeIrModulePhase(
prerequisite = setOf(mainMethodGenerationPhase)
)
class JvmInventNamesForLocalClasses(private val context: JvmBackendContext) : InventNamesForLocalClasses(allowTopLevelCallables = false) {
val inventNamesForInlinedLocalClassesPhase = makeIrFilePhase(
{ context -> JvmInventNamesForInlinedAnonymousObjects(context) },
name = "InventNamesForInlinedLocalClasses",
description = "Invent names for INLINED local classes and anonymous objects",
prerequisite = setOf(inventNamesForLocalClassesPhase, removeDuplicatedInlinedLocalClasses)
)
open class JvmInventNamesForLocalClasses(
protected val context: JvmBackendContext,
generateNamesForRegeneratedObjects: Boolean = false
) : InventNamesForLocalClasses(allowTopLevelCallables = true, generateNamesForRegeneratedObjects) {
override fun computeTopLevelClassName(clazz: IrClass): String {
val file = clazz.parent as? IrFile
?: throw AssertionError("Top-level class expected: ${clazz.render()}")
@@ -48,3 +59,11 @@ class JvmInventNamesForLocalClasses(private val context: JvmBackendContext) : In
context.putLocalClassType(declaration, Type.getObjectType(localClassName))
}
}
// TODO try to use only one "InventNames"
class JvmInventNamesForInlinedAnonymousObjects(context: JvmBackendContext) : JvmInventNamesForLocalClasses(context, true) {
override fun putLocalClassName(declaration: IrAttributeContainer, localClassName: String) {
if (declaration.attributeOwnerIdBeforeInline == null) return
context.putLocalClassType(declaration, Type.getObjectType(localClassName))
}
}