diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InventNamesForLocalClasses.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InventNamesForLocalClasses.kt index f05fb58b78a..901791bf6f1 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InventNamesForLocalClasses.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InventNamesForLocalClasses.kt @@ -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() private val localFunctionNames = mutableMapOf() + 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) diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 3c3ff7db140..3d83856b767 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -342,6 +342,7 @@ private val jvmFilePhases = listOf( // makePatchParentsPhase(), removeDuplicatedInlinedLocalClasses, + inventNamesForInlinedLocalClassesPhase, jvmLocalClassExtractionPhase, staticCallableReferencePhase, diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt index 5c17507e5ea..c16f138f35f 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt @@ -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 { - 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 { - 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) } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInventNamesForLocalClasses.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInventNamesForLocalClasses.kt index 51a866b2b71..3fef2f8d089 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInventNamesForLocalClasses.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmInventNamesForLocalClasses.kt @@ -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)) + } +}