diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index e0f4d8ab06c..e6cd4de4dd8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -1261,10 +1261,13 @@ public class KotlinTypeMapper { } private void writeAdditionalConstructorParameters(@NotNull ClassConstructorDescriptor descriptor, @NotNull JvmSignatureWriter sw) { + boolean isSynthesized = descriptor.getKind() == CallableMemberDescriptor.Kind.SYNTHESIZED; + //if (isSynthesized) return; + MutableClosure closure = bindingContext.get(CodegenBinding.CLOSURE, descriptor.getContainingDeclaration()); ClassDescriptor captureThis = getDispatchReceiverParameterForConstructorCall(descriptor, closure); - if (captureThis != null) { + if (!isSynthesized && captureThis != null) { writeParameter(sw, JvmMethodParameterKind.OUTER, captureThis.getDefaultType(), descriptor); } @@ -1274,12 +1277,16 @@ public class KotlinTypeMapper { } ClassDescriptor containingDeclaration = descriptor.getContainingDeclaration(); - if (descriptor.getKind() != CallableMemberDescriptor.Kind.SYNTHESIZED && - (containingDeclaration.getKind() == ClassKind.ENUM_CLASS || containingDeclaration.getKind() == ClassKind.ENUM_ENTRY)) { - writeParameter( - sw, JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL, DescriptorUtilsKt.getBuiltIns(descriptor).getStringType(), descriptor); - writeParameter( - sw, JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL, DescriptorUtilsKt.getBuiltIns(descriptor).getIntType(), descriptor); + + if (!isSynthesized) { + if (containingDeclaration.getKind() == ClassKind.ENUM_CLASS || containingDeclaration.getKind() == ClassKind.ENUM_ENTRY) { + writeParameter( + sw, JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL, DescriptorUtilsKt.getBuiltIns(descriptor).getStringType(), + descriptor); + writeParameter( + sw, JvmMethodParameterKind.ENUM_NAME_OR_ORDINAL, DescriptorUtilsKt.getBuiltIns(descriptor).getIntType(), + descriptor); + } } if (closure == null) return; diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt index 01e165d4586..ab27a21cca4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/DeclarationOrigins.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.jvm import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrDeclarationOriginImpl import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.IrStatementOriginImpl interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object CLASS_STATIC_INITIALIZER : IrDeclarationOriginImpl("CLASS_STATIC_INITIALIZER") @@ -26,10 +27,10 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin { object FIELD_FOR_ENUM_ENTRY : IrDeclarationOriginImpl("FIELD_FOR_ENUM_ENTRY") object FIELD_FOR_ENUM_VALUES : IrDeclarationOriginImpl("FIELD_FOR_ENUM_VALUES") object FIELD_FOR_OBJECT_INSTANCE : IrDeclarationOriginImpl("FIELD_FOR_OBJECT_INSTANCE") + object FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS") object SYNTHETIC_ACCESSOR : IrDeclarationOriginImpl("SYNTHETIC_ACCESSOR") } interface JvmLoweredStatementOrigin : IrStatementOrigin { - object DEFAULT_IMPLS_DELEGATION : IrStatementOrigin.IrStatementOriginImpl("DEFAULT_IMPL_DELEGATION") - + object DEFAULT_IMPLS_DELEGATION : IrStatementOriginImpl("DEFAULT_IMPL_DELEGATION") } \ 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 903caebd855..f2fba8d6bc1 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 @@ -35,6 +35,8 @@ class JvmLower(val context: JvmBackendContext) { InterfaceLowering(context.state).runOnFilePostfix(irFile) InterfaceDelegationLowering(context.state).runOnFilePostfix(irFile) SharedVariablesLowering(context).runOnFilePostfix(irFile) + InnerClassesLowering(context).runOnFilePostfix(irFile) + InnerClassConstructorCallsLowering(context).runOnFilePostfix(irFile) LocalFunctionsLowering(context).runOnFilePostfix(irFile) EnumClassLowering(context).runOnFilePostfix(irFile) ObjectClassLowering(context).runOnFilePostfix(irFile) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 89b2364fcca..5f5bfbc80f4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -26,6 +26,7 @@ import org.jetbrains.kotlin.codegen.MemberCodegen.badDescriptor import org.jetbrains.kotlin.codegen.binding.CodegenBinding import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -201,6 +202,9 @@ fun MemberDescriptor.calculateCommonFlags(): Int { else if (visibility == Visibilities.PROTECTED) { flags = flags.or(Opcodes.ACC_PROTECTED) } + else if (visibility == JavaVisibilities.PACKAGE_VISIBILITY) { + // default visibility + } else { throw RuntimeException("Unsupported visibility $visibility for descriptor $this") } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index 2c619c29f7a..5fb716ecf39 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -24,6 +24,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.psi.KtParameter import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.jvm.AsmTypes @@ -41,6 +42,15 @@ class FunctionCodegen(val irFunction: IrFunction, val classCodegen: ClassCodegen val descriptor = irFunction.descriptor fun generate() { + try { + doGenerate() + } + catch (e: Throwable) { + throw RuntimeException("${e.message} + while generating code for:\n${irFunction.dump()}", e) + } + } + + private fun doGenerate() { val signature = classCodegen.typeMapper.mapSignatureWithGeneric(descriptor, OwnerKind.IMPLEMENTATION) val isStatic = isStaticMethod(classCodegen.descriptor.getMemberOwnerKind(), descriptor) || DescriptorUtils.isStaticDeclaration(descriptor) val frameMap = createFrameMapWithReceivers(classCodegen.state, descriptor, signature, isStatic) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorWithExtraFlags.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorWithExtraFlags.kt index 1bfeb8ff786..4928b2c0628 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorWithExtraFlags.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/JvmDescriptorWithExtraFlags.kt @@ -70,5 +70,19 @@ class JvmPropertyDescriptorImpl( containingDeclaration, null, annotations, modality, visibility, extraFlags, false, name, CallableMemberDescriptor.Kind.SYNTHESIZED, source, false, false ).initialize(type) + + fun createFinalField( + name: Name, + type: KotlinType, + classDescriptor: ClassDescriptor, + annotations: Annotations, + visibility: Visibility, + extraFlags: Int, + source: SourceElement + ): PropertyDescriptorImpl = + JvmPropertyDescriptorImpl( + classDescriptor, null, annotations, Modality.FINAL, visibility, extraFlags, false, name, + CallableMemberDescriptor.Kind.SYNTHESIZED, source, false, false + ).initialize(type, dispatchReceiverParameter = classDescriptor.thisAsReceiverParameter) } } \ No newline at end of file diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/SpecialDescriptorsFactory.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/SpecialDescriptorsFactory.kt index 9cf0e4ad1a8..f98272e2519 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/SpecialDescriptorsFactory.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/SpecialDescriptorsFactory.kt @@ -19,11 +19,14 @@ package org.jetbrains.kotlin.backend.jvm.descriptors import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil import org.jetbrains.kotlin.ir.SourceManager +import org.jetbrains.kotlin.load.java.JavaVisibilities import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi2ir.PsiSourceManager +import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.source.KotlinSourceElement import org.jetbrains.org.objectweb.asm.Opcodes import java.util.* @@ -33,6 +36,8 @@ class SpecialDescriptorsFactory( val builtIns: KotlinBuiltIns ) { private val singletonFieldDescriptors = HashMap() + private val outerThisDescriptors = HashMap() + private val innerClassConstructors = HashMap() fun getFieldDescriptorForEnumEntry(enumEntryDescriptor: ClassDescriptor): PropertyDescriptor = singletonFieldDescriptors.getOrPut(enumEntryDescriptor) { @@ -52,6 +57,47 @@ class SpecialDescriptorsFactory( ) } + fun getOuterThisFieldDescriptor(innerClassDescriptor: ClassDescriptor): PropertyDescriptor = + if (!innerClassDescriptor.isInner) throw AssertionError("Class is not inner: $innerClassDescriptor") + else outerThisDescriptors.getOrPut(innerClassDescriptor) { + val outerClassDescriptor = DescriptorUtils.getContainingClass(innerClassDescriptor) ?: + throw AssertionError("No containing class for inner class $innerClassDescriptor") + + JvmPropertyDescriptorImpl.createFinalField( + Name.identifier("this$0"), outerClassDescriptor.defaultType, innerClassDescriptor, + Annotations.EMPTY, JavaVisibilities.PACKAGE_VISIBILITY, Opcodes.ACC_SYNTHETIC, SourceElement.NO_SOURCE + ) + } + + fun getInnerClassConstructorWithOuterThisParameter(innerClassConstructor: ClassConstructorDescriptor): ClassConstructorDescriptor { + val innerClass = innerClassConstructor.containingDeclaration + assert(innerClass.isInner) { "Class is not inner: $innerClass" } + + return innerClassConstructors.getOrPut(innerClassConstructor) { + createInnerClassConstructorWithOuterThisParameter(innerClassConstructor) + } + } + + private fun createInnerClassConstructorWithOuterThisParameter(oldDescriptor: ClassConstructorDescriptor): ClassConstructorDescriptor { + val classDescriptor = oldDescriptor.containingDeclaration + val outerThisType = (classDescriptor.containingDeclaration as ClassDescriptor).defaultType + + val newDescriptor = ClassConstructorDescriptorImpl.createSynthesized( + classDescriptor, oldDescriptor.annotations, oldDescriptor.isPrimary, oldDescriptor.source + ) + + val outerThisValueParameter = newDescriptor.createValueParameter(0, "\$outer", outerThisType) + + val newValueParameters = + listOf(outerThisValueParameter) + + oldDescriptor.valueParameters.map { it.copy(newDescriptor, it.name, it.index + 1) } + newDescriptor.initialize(newValueParameters, oldDescriptor.visibility) + newDescriptor.returnType = oldDescriptor.returnType + return newDescriptor + } + + + private fun createEnumEntryFieldDescriptor(enumEntryDescriptor: ClassDescriptor): PropertyDescriptor { assert(enumEntryDescriptor.kind == ClassKind.ENUM_ENTRY) { "Should be enum entry: $enumEntryDescriptor" } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/Util.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/Util.kt index c1b7a7491e0..1de68006071 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/Util.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/descriptors/Util.kt @@ -16,12 +16,13 @@ package org.jetbrains.kotlin.backend.jvm.descriptors -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.PropertySetterDescriptorImpl +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.descriptors.impl.* +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeSubstitutor fun PropertyDescriptorImpl.initialize( type: KotlinType, @@ -34,4 +35,14 @@ fun PropertyDescriptorImpl.initialize( setType(type, typeParameters, dispatchReceiverParameter, extensionReceiverParameter) initialize(getter, setter) return this -} \ No newline at end of file +} + +fun CallableMemberDescriptor.createValueParameter(index: Int, name: String, type: KotlinType): ValueParameterDescriptor = + ValueParameterDescriptorImpl( + this, null, + index, + Annotations.EMPTY, + Name.identifier(name), + type, + false, false, false, false, null, SourceElement.NO_SOURCE + ) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt index 7c31aa8b1b1..acd2fd80bfa 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/EnumClassLowering.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.jvm.ClassLoweringPass import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.descriptors.JvmPropertyDescriptorImpl +import org.jetbrains.kotlin.backend.jvm.descriptors.createValueParameter import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.ClassConstructorDescriptorImpl @@ -137,8 +138,8 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass { val valueParameters = listOf( - loweredConstructorDescriptor.createSpecialParameter(0, "name", context.builtIns.stringType), - loweredConstructorDescriptor.createSpecialParameter(1, "ordinal", context.builtIns.intType) + loweredConstructorDescriptor.createValueParameter(0, "name", context.builtIns.stringType), + loweredConstructorDescriptor.createValueParameter(1, "ordinal", context.builtIns.intType) ) + constructorDescriptor.valueParameters.map { lowerConstructorValueParameter(loweredConstructorDescriptor, it) @@ -152,16 +153,6 @@ class EnumClassLowering(val context: JvmBackendContext) : ClassLoweringPass { return loweredConstructorDescriptor } - private fun ClassConstructorDescriptor.createSpecialParameter(index: Int, name: String, type: KotlinType): ValueParameterDescriptor = - ValueParameterDescriptorImpl( - this, null, - index, - Annotations.EMPTY, - Name.identifier(name), - type, - false, false, false, false, null, SourceElement.NO_SOURCE - ) - private fun lowerConstructorValueParameter( loweredConstructorDescriptor: ClassConstructorDescriptor, valueParameterDescriptor: ValueParameterDescriptor diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InnerClassesLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InnerClassesLowering.kt new file mode 100644 index 00000000000..7e826725c4f --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/InnerClassesLowering.kt @@ -0,0 +1,213 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.backend.jvm.lower + +import org.jetbrains.kotlin.backend.jvm.BodyLoweringPass +import org.jetbrains.kotlin.backend.jvm.ClassLoweringPass +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.transformFlat +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid +import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver +import org.jetbrains.kotlin.utils.addToStdlib.singletonList + +class InnerClassesLowering(val context: JvmBackendContext) : ClassLoweringPass { + override fun lower(irClass: IrClass) { + InnerClassTransformer(irClass).lowerInnerClass() + } + + private inner class InnerClassTransformer(val irClass: IrClass) { + val classDescriptor = irClass.descriptor + + private lateinit var outerThisFieldDescriptor: PropertyDescriptor + + fun lowerInnerClass() { + if (!irClass.descriptor.isInner) return + + createOuterThisField() + lowerConstructors() + lowerOuterThisReferences() + } + + private fun createOuterThisField() { + outerThisFieldDescriptor = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(irClass.descriptor) + + irClass.declarations.add(IrFieldImpl( + irClass.startOffset, irClass.endOffset, + JvmLoweredDeclarationOrigin.FIELD_FOR_OUTER_THIS, + outerThisFieldDescriptor + )) + } + + private fun lowerConstructors() { + irClass.declarations.transformFlat { irMember -> + if (irMember is IrConstructor) + lowerConstructor(irMember).singletonList() + else + null + } + } + + private fun lowerConstructor(irConstructor: IrConstructor): IrConstructor { + val oldDescriptor = irConstructor.descriptor + val startOffset = irConstructor.startOffset + val endOffset = irConstructor.endOffset + + val newDescriptor = context.specialDescriptorsFactory.getInnerClassConstructorWithOuterThisParameter(oldDescriptor) + val outerThisValueParameter = newDescriptor.valueParameters[0] + + val blockBody = irConstructor.body as? IrBlockBody ?: throw AssertionError("Unexpected constructor body: ${irConstructor.body}") + + val instanceInitializerIndex = blockBody.statements.indexOfFirst { it is IrInstanceInitializerCall } + if (instanceInitializerIndex >= 0) { + // Initializing constructor: initialize 'this.this$0' with '$outer' + blockBody.statements.add( + instanceInitializerIndex, + IrSetFieldImpl( + startOffset, endOffset, outerThisFieldDescriptor, + IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter), + IrGetValueImpl(startOffset, endOffset, outerThisValueParameter) + ) + ) + } + else { + // Delegating constructor: invoke old constructor with dispatch receiver '$outer' + val delegatingConstructorCall = (blockBody.statements.find { it is IrDelegatingConstructorCall } ?: + throw AssertionError("Delegating constructor call expected: ${irConstructor.dump()}") + ) as IrDelegatingConstructorCall + delegatingConstructorCall.dispatchReceiver = IrGetValueImpl( + delegatingConstructorCall.startOffset, delegatingConstructorCall.endOffset, outerThisValueParameter + ) + } + + return IrConstructorImpl( + startOffset, endOffset, + irConstructor.origin, // TODO special origin for lowered inner class constructors? + newDescriptor, + blockBody + ) + } + + private fun lowerOuterThisReferences() { + irClass.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid(this) + + val implicitThisClass = expression.descriptor.getClassDescriptorForImplicitThis() ?: + return expression + + if (implicitThisClass == classDescriptor) return expression + + val startOffset = expression.startOffset + val endOffset = expression.endOffset + val origin = expression.origin + + var irThis: IrExpression = IrGetValueImpl(startOffset, endOffset, classDescriptor.thisAsReceiverParameter, origin) + var innerClass = classDescriptor + + while (innerClass != implicitThisClass) { + if (!innerClass.isInner) { + // Captured 'this' unrelated to inner classes nesting hierarchy, leave it as is - + // should be transformed by closures conversion. + return expression + } + + val outerThisField = context.specialDescriptorsFactory.getOuterThisFieldDescriptor(innerClass) + irThis = IrGetFieldImpl(startOffset, endOffset, outerThisField, irThis, origin) + + val outer = classDescriptor.containingDeclaration + innerClass = outer as? ClassDescriptor ?: + throw AssertionError("Unexpected containing declaration for inner class $innerClass: $outer") + } + + return irThis + } + }) + } + + private fun ValueDescriptor.getClassDescriptorForImplicitThis(): ClassDescriptor? { + if (this is ReceiverParameterDescriptor) { + val receiverValue = value + if (receiverValue is ImplicitClassReceiver) { + return receiverValue.classDescriptor + } + } + return null + } + } +} + +class InnerClassConstructorCallsLowering(val context: JvmBackendContext) : BodyLoweringPass { + override fun lower(irBody: IrBody) { + irBody.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid(this) + + val dispatchReceiver = expression.dispatchReceiver ?: return expression + val callee = expression.descriptor as? ClassConstructorDescriptor ?: return expression + if (!callee.constructedClass.isInner) return expression + + val newCallee = context.specialDescriptorsFactory.getInnerClassConstructorWithOuterThisParameter(callee) + val newCall = IrCallImpl( + expression.startOffset, expression.endOffset, newCallee, + null, // TODO type arguments map + expression.origin + ) + + newCall.putValueArgument(0, dispatchReceiver) + for (i in 1 .. newCallee.valueParameters.lastIndex) { + newCall.putValueArgument(i, expression.getValueArgument(i)) + } + + return newCall + } + + override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall): IrExpression { + expression.transformChildrenVoid(this) + + val dispatchReceiver = expression.dispatchReceiver ?: return expression + val callee = expression.descriptor + if (!callee.constructedClass.isInner) return expression + + val newCallee = context.specialDescriptorsFactory.getInnerClassConstructorWithOuterThisParameter(callee) + val newCall = IrDelegatingConstructorCallImpl( + expression.startOffset, expression.endOffset, newCallee, + null // TODO type arguments map + ) + + newCall.putValueArgument(0, dispatchReceiver) + for (i in 1 .. newCallee.valueParameters.lastIndex) { + newCall.putValueArgument(i, expression.getValueArgument(i)) + } + + return newCall + } + + // TODO callable references? + }) + } +} + diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt index 1f72e5db190..eb17c4fd906 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrStatementOrigin.kt @@ -16,11 +16,11 @@ package org.jetbrains.kotlin.ir.expressions -interface IrStatementOrigin { - abstract class IrStatementOriginImpl(val debugName: String): IrStatementOrigin { - override fun toString(): String = debugName - } +abstract class IrStatementOriginImpl(val debugName: String): IrStatementOrigin { + override fun toString(): String = debugName +} +interface IrStatementOrigin { object SAFE_CALL : IrStatementOriginImpl("SAFE_CALL") object UMINUS : IrStatementOriginImpl("UMINUS") diff --git a/compiler/testData/ir/box/closureConversion/innerClass1.kt b/compiler/testData/ir/box/closureConversion/innerClass1.kt new file mode 100644 index 00000000000..bdb37488833 --- /dev/null +++ b/compiler/testData/ir/box/closureConversion/innerClass1.kt @@ -0,0 +1,8 @@ +class Outer { + val x = "O" + inner class Inner { + val y = x + "K" + } +} + +fun box() = Outer().Inner().y \ No newline at end of file diff --git a/compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.kt b/compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.kt new file mode 100644 index 00000000000..195f37bce01 --- /dev/null +++ b/compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.kt @@ -0,0 +1,5 @@ +class Outer { + inner class Inner(val x: Int) { + constructor() : this(0) + } +} \ No newline at end of file diff --git a/compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.txt b/compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.txt new file mode 100644 index 00000000000..35bf2200b7a --- /dev/null +++ b/compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.txt @@ -0,0 +1,25 @@ +FILE /innerClassWithDelegatingConstructor.kt + CLASS CLASS Outer + CONSTRUCTOR public constructor Outer() + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='Outer' + CLASS CLASS Inner + CONSTRUCTOR public constructor Inner(x: kotlin.Int) + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Any()' + INSTANCE_INITIALIZER_CALL classDescriptor='Inner' + PROPERTY public final val x: kotlin.Int + FIELD PROPERTY_BACKING_FIELD public final val x: kotlin.Int + EXPRESSION_BODY + GET_VAR 'value-parameter x: Int' type=kotlin.Int origin=INITIALIZE_PROPERTY_FROM_PARAMETER + FUN DEFAULT_PROPERTY_ACCESSOR public final fun (): kotlin.Int + BLOCK_BODY + RETURN type=kotlin.Nothing from='(): Int' + GET_FIELD 'x: Int' type=kotlin.Int origin=null + receiver: GET_VAR '' type=Outer.Inner origin=null + CONSTRUCTOR public constructor Inner() + BLOCK_BODY + DELEGATING_CONSTRUCTOR_CALL 'constructor Inner(Int)' + $this: GET_VAR '' type=Outer origin=null + x: CONST Int type=kotlin.Int value='0' diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java index 97c46aaa92f..ede6dd32ed3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrOnlyBoxCodegenTestGenerated.java @@ -109,6 +109,12 @@ public class IrOnlyBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTest doTest(fileName); } + @TestMetadata("innerClass1.kt") + public void testInnerClass1() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/closureConversion/innerClass1.kt"); + doTest(fileName); + } + @TestMetadata("mutable1.kt") public void testMutable1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/box/closureConversion/mutable1.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java index 03b75523dcd..fcb987ad6c9 100644 --- a/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/ir/IrTextTestCaseGenerated.java @@ -127,6 +127,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase { doTest(fileName); } + @TestMetadata("innerClassWithDelegatingConstructor.kt") + public void testInnerClassWithDelegatingConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/classes/innerClassWithDelegatingConstructor.kt"); + doTest(fileName); + } + @TestMetadata("localClasses.kt") public void testLocalClasses() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/classes/localClasses.kt");