From 9327776706aa012526bd335070dd3a0a29c6c3b0 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Mon, 23 Dec 2019 19:46:37 +0300 Subject: [PATCH] Rewrite inner class lowerings --- .../backend/common/ir/DeclarationFactory.kt | 1 + .../common/lower/InnerClassesLowering.kt | 139 +++++++++++++----- .../ir/backend/js/JsDeclarationFactory.kt | 11 ++ .../kotlin/ir/backend/js/JsLoweringPhases.kt | 8 + .../kotlin/ir/backend/js/JsMapping.kt | 1 + .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 8 + .../jvm/descriptors/JvmDeclarationFactory.kt | 14 +- .../kotlin/backend/wasm/WasmLoweringPhases.kt | 8 + 8 files changed, 155 insertions(+), 35 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/DeclarationFactory.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/DeclarationFactory.kt index 9ce12f42c17..9cc3252bbb5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/DeclarationFactory.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/DeclarationFactory.kt @@ -14,5 +14,6 @@ interface DeclarationFactory { fun getFieldForEnumEntry(enumEntry: IrEnumEntry): IrField fun getOuterThisField(innerClass: IrClass): IrField fun getInnerClassConstructorWithOuterThisParameter(innerClassConstructor: IrConstructor): IrConstructor + fun getInnerClassOriginalPrimaryConstructorOrNull(innerClass: IrClass): IrConstructor? fun getFieldForObjectInstance(singleton: IrClass): IrField } \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt index 23d87d3bfca..e9b5971c3c7 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/InnerClassesLowering.kt @@ -17,15 +17,95 @@ import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol import org.jetbrains.kotlin.ir.symbols.IrValueSymbol import org.jetbrains.kotlin.ir.types.classOrNull +import org.jetbrains.kotlin.ir.util.deepCopyWithSymbols import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.util.parentAsClass import org.jetbrains.kotlin.ir.util.patchDeclarationParents -import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.utils.addToStdlib.cast -class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass { +class InnerClassesLowering(val context: BackendContext) : DeclarationTransformer { + + override fun lower(irFile: IrFile) { + runPostfix(true).toFileLoweringPass().lower(irFile) + } + + override fun transformFlat(declaration: IrDeclaration): List? { + + if (declaration is IrClass && declaration.isInner) { + declaration.declarations += context.declarationFactory.getOuterThisField(declaration) + } else if (declaration is IrConstructor) { + val irClass = declaration.parentAsClass + if (!irClass.isInner) return null + + val newConstructor = lowerConstructor(declaration) + val oldConstructorParameterToNew = context.primaryConstructorParameterMap(declaration) + for ((oldParam, newParam) in oldConstructorParameterToNew.entries) { + newParam.defaultValue = oldParam.defaultValue?.let { oldDefault -> + IrExpressionBodyImpl(oldDefault.startOffset, oldDefault.endOffset) { + expression = oldDefault.expression.patchDeclarationParents(newConstructor) + } + } + } + + return listOf(newConstructor) + } + + return null + } + + private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor { + val loweredConstructor = context.declarationFactory.getInnerClassConstructorWithOuterThisParameter(irConstructor) + val outerThisParameter = loweredConstructor.valueParameters[0] + + val irClass = irConstructor.parentAsClass + val parentThisField = context.declarationFactory.getOuterThisField(irClass) + + val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}") + + loweredConstructor.body = IrBlockBodyImpl(blockBody.startOffset, blockBody.endOffset) { + context.createIrBuilder(irConstructor.symbol, irConstructor.startOffset, irConstructor.endOffset).apply { + statements.add(0, irSetField(irGet(irClass.thisReceiver!!), parentThisField, irGet(outerThisParameter))) + } + + statements.addAll(blockBody.statements) + + if (statements.find { it is IrInstanceInitializerCall } == null) { + val delegatingConstructorCall = + statements.find { it is IrDelegatingConstructorCall } as IrDelegatingConstructorCall? + ?: throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}") + delegatingConstructorCall.apply { dispatchReceiver = IrGetValueImpl(startOffset, endOffset, outerThisParameter.symbol) } + } + patchDeclarationParents(loweredConstructor) + + val oldConstructorParameterToNew = context.primaryConstructorParameterMap(irConstructor) + transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew)) + } + + return loweredConstructor + } + +} + +private fun BackendContext.primaryConstructorParameterMap(originalConstructor: IrConstructor): Map { + val oldConstructorParameterToNew = HashMap() + + val loweredConstructor = declarationFactory.getInnerClassConstructorWithOuterThisParameter(originalConstructor) + + originalConstructor.valueParameters.forEach { old -> + oldConstructorParameterToNew[old] = loweredConstructor.valueParameters[old.index + 1] + } + + return oldConstructorParameterToNew +} + + +class InnerClassesMemberBodyLowering(val context: BackendContext) : BodyLoweringPass { + override fun lower(irFile: IrFile) { + runOnFilePostfix(irFile, true) + } + private val IrValueSymbol.classForImplicitThis: IrClass? // TODO: is this the correct way to get the class? get() = @@ -37,40 +117,31 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass { } else null - override fun lower(irClass: IrClass) { + override fun lower(irBody: IrBody, container: IrDeclaration) { + val irClass = container.parent as? IrClass ?: return + if (!irClass.isInner) return - val parentThisField = context.declarationFactory.getOuterThisField(irClass) - val oldConstructorParameterToNew = HashMap() - - fun lowerConstructor(irConstructor: IrConstructor): IrConstructor { - val loweredConstructor = context.declarationFactory.getInnerClassConstructorWithOuterThisParameter(irConstructor) - val outerThisParameter = loweredConstructor.valueParameters[0] - - irConstructor.valueParameters.forEach { old -> - oldConstructorParameterToNew[old] = loweredConstructor.valueParameters[old.index + 1] + if (container is IrField || container is IrAnonymousInitializer || container is IrValueParameter) { + val primaryConstructor = context.declarationFactory.getInnerClassOriginalPrimaryConstructorOrNull(irClass) + if (primaryConstructor != null) { + val oldConstructorParameterToNew = context.primaryConstructorParameterMap(primaryConstructor) + irBody.transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew)) } - - val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}") - context.createIrBuilder(irConstructor.symbol, irConstructor.startOffset, irConstructor.endOffset).apply { - blockBody.statements.add(0, irSetField(irGet(irClass.thisReceiver!!), parentThisField, irGet(outerThisParameter))) - } - if (blockBody.statements.find { it is IrInstanceInitializerCall } == null) { - val delegatingConstructorCall = - blockBody.statements.find { it is IrDelegatingConstructorCall } as IrDelegatingConstructorCall? - ?: throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}") - delegatingConstructorCall.apply { dispatchReceiver = IrGetValueImpl(startOffset, endOffset, outerThisParameter.symbol) } - } - blockBody.patchDeclarationParents(loweredConstructor) - loweredConstructor.body = blockBody - return loweredConstructor } - irClass.declarations += parentThisField - irClass.transformDeclarationsFlat { irMember -> (irMember as? IrConstructor)?.let { listOf(lowerConstructor(it)) } } - irClass.transformChildrenVoid(VariableRemapper(oldConstructorParameterToNew)) + irBody.fixThisReference(irClass, container) + } - irClass.transformChildrenVoid(object : IrElementTransformerVoidWithContext() { + fun IrBody.fixThisReference(irClass: IrClass, container: IrDeclaration) { + val enclosingFunction: IrDeclaration? = run { + var current: IrDeclaration? = container + while (current != null && current !is IrFunction) { + current = current.parent as? IrDeclaration + } + current + } + transformChildrenVoid(object : IrElementTransformerVoidWithContext() { override fun visitClassNew(declaration: IrClass): IrStatement = declaration @@ -83,7 +154,7 @@ class InnerClassesLowering(val context: BackendContext) : ClassLoweringPass { val startOffset = expression.startOffset val endOffset = expression.endOffset val origin = expression.origin - val function = currentFunction?.irElement as? IrFunction + val function = (currentFunction?.irElement ?: enclosingFunction) as? IrFunction val enclosingThisReceiver = function?.dispatchReceiverParameter ?: irClass.thisReceiver!! var irThis: IrExpression = IrGetValueImpl(startOffset, endOffset, enclosingThisReceiver.symbol, origin) @@ -117,9 +188,9 @@ val innerClassConstructorCallsPhase = makeIrFilePhase( description = "Handle constructor calls for inner classes" ) -class InnerClassConstructorCallsLowering(val context: BackendContext) : FileLoweringPass { - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(object : IrElementTransformerVoid() { +class InnerClassConstructorCallsLowering(val context: BackendContext) : BodyLoweringPass { + override fun lower(irBody: IrBody, container: IrDeclaration) { + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { override fun visitConstructorCall(expression: IrConstructorCall): IrExpression { expression.transformChildrenVoid(this) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsDeclarationFactory.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsDeclarationFactory.kt index 94ddd4dd03f..83fd18e34b8 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsDeclarationFactory.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsDeclarationFactory.kt @@ -32,6 +32,7 @@ class JsDeclarationFactory(mapping: JsMapping) : DeclarationFactory { private val singletonFieldDescriptors = mapping.singletonFieldDescriptors private val outerThisFieldSymbols = mapping.outerThisFieldSymbols private val innerClassConstructors = mapping.innerClassConstructors + private val originalInnerClassPrimaryConstructorByClass = mapping.originalInnerClassPrimaryConstructorByClass override fun getFieldForEnumEntry(enumEntry: IrEnumEntry): IrField = TODO() @@ -80,9 +81,19 @@ class JsDeclarationFactory(mapping: JsMapping) : DeclarationFactory { return innerClassConstructors.getOrPut(innerClassConstructor) { createInnerClassConstructorWithOuterThisParameter(innerClassConstructor) + }.also { + if (innerClassConstructor.isPrimary) { + originalInnerClassPrimaryConstructorByClass[innerClass] = innerClassConstructor + } } } + override fun getInnerClassOriginalPrimaryConstructorOrNull(innerClass: IrClass): IrConstructor? { + assert(innerClass.isInner) { "Class is not inner: $innerClass" } + + return originalInnerClassPrimaryConstructorByClass[innerClass] + } + private fun createInnerClassConstructorWithOuterThisParameter(oldConstructor: IrConstructor): IrConstructor { val irClass = oldConstructor.parent as IrClass val outerThisType = (irClass.parent as IrClass).defaultType diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 48e30aa068f..0b0da20e297 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -217,6 +217,13 @@ private val innerClassesLoweringPhase = makeJsModulePhase( description = "Capture outer this reference to inner class" ) +private val innerClassesMemberBodyLoweringPhase = makeJsModulePhase( + ::InnerClassesMemberBodyLowering, + name = "InnerClassesMemberBody", + description = "Replace `this` with 'outer this' field references", + prerequisite = setOf(innerClassesLoweringPhase) +) + private val innerClassConstructorCallsLoweringPhase = makeJsModulePhase( ::InnerClassConstructorCallsLowering, name = "InnerClassConstructorCallsLowering", @@ -444,6 +451,7 @@ val jsPhases = namedIrModulePhase( localDeclarationsLoweringPhase then localClassExtractionPhase then innerClassesLoweringPhase then + innerClassesMemberBodyLoweringPhase then innerClassConstructorCallsLoweringPhase then propertiesLoweringPhase then primaryConstructorLoweringPhase then diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt index 9723c46925c..49d8532e277 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsMapping.kt @@ -14,4 +14,5 @@ class JsMapping : DefaultMapping() { val singletonFieldDescriptors = newMapping() val outerThisFieldSymbols = newMapping() val innerClassConstructors = newMapping() + val originalInnerClassPrimaryConstructorByClass = newMapping() } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index ad1732704eb..e959bdc46f2 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -183,6 +183,13 @@ private val innerClassesPhase = makeIrFilePhase( prerequisite = setOf(localDeclarationsPhase) ) +private val innerClassesMemberBodyPhase = makeIrFilePhase( + ::InnerClassesMemberBodyLowering, + name = "InnerClassesMemberBody", + description = "Replace `this` with 'outer this' field references", + prerequisite = setOf(innerClassesPhase) +) + private val staticInitializersPhase = makeIrFilePhase( ::StaticInitializersLowering, name = "StaticInitializers", @@ -305,6 +312,7 @@ private val jvmFilePhases = addContinuationPhase then innerClassesPhase then + innerClassesMemberBodyPhase then innerClassConstructorCallsPhase then makePatchParentsPhase(2) then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt index a6279b726bd..12a5f406733 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDeclarationFactory.kt @@ -39,6 +39,7 @@ class JvmDeclarationFactory( private val interfaceCompanionFieldDeclarations = HashMap() private val outerThisDeclarations = HashMap() private val innerClassConstructors = HashMap() + private val originalInnerClassPrimaryConstructorByClass = HashMap() private val staticBackingFields = HashMap() private val defaultImplsMethods = HashMap() @@ -74,13 +75,24 @@ class JvmDeclarationFactory( } override fun getInnerClassConstructorWithOuterThisParameter(innerClassConstructor: IrConstructor): IrConstructor { - assert((innerClassConstructor.parent as IrClass).isInner) { "Class is not inner: ${(innerClassConstructor.parent as IrClass).dump()}" } + val innerClass = innerClassConstructor.parent as IrClass + assert(innerClass.isInner) { "Class is not inner: ${(innerClassConstructor.parent as IrClass).dump()}" } return innerClassConstructors.getOrPut(innerClassConstructor) { createInnerClassConstructorWithOuterThisParameter(innerClassConstructor) + }.also { + if (innerClassConstructor.isPrimary) { + originalInnerClassPrimaryConstructorByClass[innerClass] = innerClassConstructor + } } } + override fun getInnerClassOriginalPrimaryConstructorOrNull(innerClass: IrClass): IrConstructor? { + assert(innerClass.isInner) { "Class is not inner: $innerClass" } + + return originalInnerClassPrimaryConstructorByClass[innerClass] + } + private fun createInnerClassConstructorWithOuterThisParameter(oldConstructor: IrConstructor): IrConstructor { val newDescriptor = WrappedClassConstructorDescriptor(oldConstructor.descriptor.annotations) return IrConstructorImpl( diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt index 2e600972a77..6c965e2b710 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmLoweringPhases.kt @@ -165,6 +165,13 @@ private val innerClassesLoweringPhase = makeWasmModulePhase( description = "Capture outer this reference to inner class" ) +private val innerClassesMemberBodyLoweringPhase = makeWasmModulePhase( + ::InnerClassesMemberBodyLowering, + name = "InnerClassesMemberBody", + description = "Replace `this` with 'outer this' field references", + prerequisite = setOf(innerClassesLoweringPhase) +) + private val innerClassConstructorCallsLoweringPhase = makeWasmModulePhase( ::InnerClassConstructorCallsLowering, name = "InnerClassConstructorCallsLowering", @@ -354,6 +361,7 @@ val wasmPhases = namedIrModulePhase( localDeclarationsLoweringPhase then localClassExtractionPhase then innerClassesLoweringPhase then + innerClassesMemberBodyLoweringPhase then innerClassConstructorCallsLoweringPhase then propertiesLoweringPhase then primaryConstructorLoweringPhase then