From cb712409382cc14af345eafa05c6a9a582f8a6d2 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Thu, 16 Sep 2021 10:37:59 +0500 Subject: [PATCH] [K/N] Supported outer this references from cached inline bodies --- .../jetbrains/kotlin/backend/konan/Context.kt | 12 +- .../kotlin/backend/konan/InternalAbi.kt | 3 + .../kotlin/backend/konan/ToplevelPhases.kt | 76 ++++++++-- .../konan/descriptors/ClassLayoutBuilder.kt | 30 ++-- .../backend/konan/lower/InnerClassLowering.kt | 11 +- .../konan/serialization/KonanIrlinker.kt | 132 +++++++++++------- .../backend.native/tests/build.gradle | 18 +++ .../innerClass/inheritance2_linkTest_lib.kt | 13 ++ .../innerClass/inheritance2_linkTest_main.kt | 9 ++ .../innerClass/inheritance2_linkTest_main.out | 1 + .../innerClass/inheritance3_linkTest_lib.kt | 14 ++ .../innerClass/inheritance3_linkTest_main.kt | 10 ++ .../innerClass/inheritance3_linkTest_main.out | 2 + .../innerClass/inheritance_linkTest_lib.kt | 10 ++ .../innerClass/inheritance_linkTest_main.kt | 14 ++ .../innerClass/inheritance_linkTest_main.out | 2 + 16 files changed, 268 insertions(+), 89 deletions(-) create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_lib.kt create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.kt create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.out create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_lib.kt create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.kt create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.out create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_lib.kt create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.kt create mode 100644 kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.out diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 292c9c6b4a4..77443e0bd71 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -40,6 +40,7 @@ import kotlin.LazyThreadSafetyMode.PUBLICATION import kotlin.reflect.KProperty import org.jetbrains.kotlin.backend.common.ir.copyTo import org.jetbrains.kotlin.backend.common.ir.copyToWithoutSuperTypes +import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker @@ -77,9 +78,9 @@ internal class SpecialDeclarationsFactory(val context: Context) { object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS : IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS") - fun getOuterThisField(innerClass: IrClass): IrField = - if (!innerClass.isInner) throw AssertionError("Class is not inner: ${innerClass.descriptor}") - else outerThisFields.getOrPut(innerClass) { + fun getOuterThisField(innerClass: IrClass): IrField { + assert(innerClass.isInner) { "Class is not inner: ${innerClass.render()}" } + return outerThisFields.getOrPut(innerClass) { val outerClass = innerClass.parent as? IrClass ?: throw AssertionError("No containing class for inner class ${innerClass.descriptor}") @@ -112,6 +113,7 @@ internal class SpecialDeclarationsFactory(val context: Context) { parent = innerClass } } + } fun getLoweredEnumOrNull(enumClass: IrClass): LoweredEnumAccess? { assert(enumClass.kind == ClassKind.ENUM_CLASS) { "Expected enum class but was: ${enumClass.descriptor}" } @@ -291,12 +293,12 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { fun getLayoutBuilder(irClass: IrClass): ClassLayoutBuilder { if (irClass is IrLazyClass) return layoutBuilders.getOrPut(irClass) { - ClassLayoutBuilder(irClass, this, isLowered = shouldLower(this, irClass)) + ClassLayoutBuilder(irClass, this) } val metadata = irClass.metadata as? CodegenClassMetadata ?: CodegenClassMetadata(irClass).also { irClass.metadata = it } metadata.layoutBuilder?.let { return it } - val layoutBuilder = ClassLayoutBuilder(irClass, this, isLowered = shouldLower(this, irClass)) + val layoutBuilder = ClassLayoutBuilder(irClass, this) metadata.layoutBuilder = layoutBuilder return layoutBuilder } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt index d38aaed178d..ceca165c4bc 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt @@ -77,6 +77,9 @@ internal class InternalAbi(private val context: Context) { fun getEnumValuesAccessorName(enum: IrClass): Name = getMangledNameFor("getValues", enum) + fun getInnerClassOuterThisAccessorName(innerClass: IrClass): Name = + getMangledNameFor("outerThis", innerClass) + /** * Generate name for declaration that will be a part of internal ABI. */ diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index 8603555cb98..04c1dbe0ba6 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -15,15 +15,16 @@ import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.serialization.* import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.config.languageVersionSettings +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.buildFun -import org.jetbrains.kotlin.ir.builders.irBlockBody -import org.jetbrains.kotlin.ir.builders.irGetObjectValue -import org.jetbrains.kotlin.ir.builders.irReturn +import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue +import org.jetbrains.kotlin.ir.expressions.IrGetField import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid @@ -32,6 +33,7 @@ import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) { if (!context.config.needVerifyIr) return @@ -140,7 +142,8 @@ internal val buildAdditionalCacheInfoPhase = konanUnitPhase( override fun visitClass(declaration: IrClass) { declaration.acceptChildrenVoid(this) - if (declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS) + if (!declaration.isInterface && declaration.visibility != DescriptorVisibilities.LOCAL + && declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS) classFields.add(moduleDeserializer.buildClassFields(declaration, getLayoutBuilder(declaration).getDeclaredFields())) } @@ -165,11 +168,6 @@ internal val buildAdditionalCacheInfoPhase = konanUnitPhase( prerequisite = setOf(psiToIrPhase) ) -// Coupled with [psiToIrPhase] logic above. -internal fun shouldLower(context: Context, declaration: IrDeclaration): Boolean { - return context.llvmModuleSpecification.containsDeclaration(declaration) -} - internal val destroySymbolTablePhase = konanUnitPhase( op = { this.symbolTable = null // TODO: invalidate symbolTable itself. @@ -393,6 +391,28 @@ internal val exportInternalAbiPhase = makeKonanModuleOpPhase( context.internalAbi.declare(function, declaration.module) } + if (declaration.isInner) { + val function = context.irFactory.buildFun { + name = InternalAbi.getInnerClassOuterThisAccessorName(declaration) + origin = InternalAbi.INTERNAL_ABI_ORIGIN + returnType = declaration.parentAsClass.defaultType + } + function.addValueParameter { + name = Name.identifier("innerClass") + origin = InternalAbi.INTERNAL_ABI_ORIGIN + type = declaration.defaultType + } + + context.createIrBuilder(function.symbol).apply { + function.body = irBlockBody { + +irReturn(irGetField( + irGet(function.valueParameters[0]), + context.specialDeclarationsFactory.getOuterThisField(declaration)) + ) + } + } + context.internalAbi.declare(function, declaration.module) + } } } module.acceptChildrenVoid(visitor) @@ -404,7 +424,8 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase( description = "Use internal ABI functions to access private entities", prerequisite = emptySet(), op = { context, module -> - val accessors = mutableMapOf() + val companionObjectAccessors = mutableMapOf() + val outerThisAccessors = mutableMapOf() val transformer = object : IrElementTransformerVoid() { override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { val irClass = expression.symbol.owner @@ -416,7 +437,7 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase( // Access to Obj-C metaclass is done via intrinsic. return expression } - val accessor = accessors.getOrPut(irClass) { + val accessor = companionObjectAccessors.getOrPut(irClass) { context.irFactory.buildFun { name = InternalAbi.getCompanionObjectAccessorName(irClass) returnType = irClass.defaultType @@ -428,6 +449,39 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase( } return IrCallImpl(expression.startOffset, expression.endOffset, expression.type, accessor.symbol, accessor.typeParameters.size, accessor.valueParameters.size) } + + override fun visitGetField(expression: IrGetField): IrExpression { + val field = expression.symbol.owner + val irClass = field.parentClassOrNull ?: return expression + if (!irClass.isInner || context.llvmModuleSpecification.containsDeclaration(irClass) + || context.specialDeclarationsFactory.getOuterThisField(irClass) != field + ) { + return expression + } + val accessor = outerThisAccessors.getOrPut(irClass) { + context.irFactory.buildFun { + name = InternalAbi.getInnerClassOuterThisAccessorName(irClass) + returnType = irClass.parentAsClass.defaultType + origin = InternalAbi.INTERNAL_ABI_ORIGIN + isExternal = true + }.also { function -> + context.internalAbi.reference(function, irClass.module) + + function.addValueParameter { + name = Name.identifier("innerClass") + origin = InternalAbi.INTERNAL_ABI_ORIGIN + type = irClass.defaultType + } + } + } + return IrCallImpl( + expression.startOffset, expression.endOffset, + expression.type, accessor.symbol, + accessor.typeParameters.size, accessor.valueParameters.size + ).apply { + putValueArgument(0, expression.receiver) + } + } } module.transformChildrenVoid(transformer) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index 10d402adcb2..12141ad6326 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -260,7 +260,12 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod } } -internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, val isLowered: Boolean) { +internal fun IrField.toFieldInfo() = + ClassLayoutBuilder.FieldInfo(name.asString(), type, + correspondingPropertySymbol?.owner?.isConst ?: false, + initializer?.expression is IrConst<*>, this) + +internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { val vtableEntries: List by lazy { require(!irClass.isInterface) @@ -458,15 +463,13 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va lateinit var hierarchyInfo: ClassGlobalHierarchyInfo - private fun IrField.toFieldInfo() = - FieldInfo(name.asString(), type, - correspondingPropertySymbol?.owner?.isConst ?: false, - initializer?.expression is IrConst<*>, this) - /** * Fields declared in the class. */ fun getDeclaredFields(): List { + val outerThisField = if (irClass.isInner) + context.specialDeclarationsFactory.getOuterThisField(irClass) + else null if (context.config.lazyIrForCaches && !context.llvmModuleSpecification.containsDeclaration(irClass)) { val packageFragment = irClass.findPackage() val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration @@ -474,18 +477,13 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va return emptyList() val moduleDeserializer = context.irLinker.cachedLibraryModuleDeserializers[moduleDescriptor] ?: error("No module deserializer for ${irClass.render()}") - val fields = moduleDeserializer.deserializeClassFields(irClass).toMutableList() - if (irClass.isInner) - fields.add(InnerClassLowering.addOuterThisField(mutableListOf(), irClass, context).toFieldInfo()) - return fields + return moduleDeserializer.deserializeClassFields(irClass, outerThisField) } - val declarations: List = if (irClass.isInner && !isLowered) { - // Note: copying to avoid mutation of the original class. - irClass.declarations.toMutableList() - .also { InnerClassLowering.addOuterThisField(it, irClass, context) } - } else { - irClass.declarations + val declarations = irClass.declarations.toMutableList() + outerThisField?.let { + if (!declarations.contains(it)) + declarations += it } return declarations.mapNotNull { when (it) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt index bc2f1b16d91..4b6ba379907 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/InnerClassLowering.kt @@ -119,14 +119,6 @@ internal class InnerClassLowering(val context: Context) : ClassLoweringPass { InnerClassTransformer(irClass).lowerInnerClass() } - companion object { - fun addOuterThisField(declarations: MutableList, irClass: IrClass, context: Context): IrField { - val outerThisField = context.specialDeclarationsFactory.getOuterThisField(irClass) - declarations += outerThisField - return outerThisField - } - } - private inner class InnerClassTransformer(val irClass: IrClass) { lateinit var outerThisFieldSymbol: IrFieldSymbol @@ -138,7 +130,8 @@ internal class InnerClassLowering(val context: Context) : ClassLoweringPass { } private fun createOuterThisField() { - val outerThisField = addOuterThisField(irClass.declarations, irClass, context) + val outerThisField = context.specialDeclarationsFactory.getOuterThisField(irClass) + irClass.declarations += outerThisField outerThisFieldSymbol = outerThisField.symbol } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt index ea66e8072c6..0b62511c2bd 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder import org.jetbrains.kotlin.backend.konan.descriptors.findPackage import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary +import org.jetbrains.kotlin.backend.konan.descriptors.toFieldInfo import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.konan.DeserializedKlibModuleOrigin @@ -92,14 +93,14 @@ internal class ByteArrayStream(val buf: ByteArray) { class SerializedInlineFunctionReference(val file: Int, val functionSignature: Int, val body: Int, val startOffset: Int, val endOffset: Int, - val extensionReceiverSig: Int, val dispatchReceiverSig: Int, + val extensionReceiverSig: Int, val dispatchReceiverSig: Int, val outerReceiverSigs: IntArray, val valueParameterSigs: IntArray, val typeParameterSigs: IntArray, val defaultValues: IntArray) internal object InlineFunctionBodyReferenceSerializer { fun serialize(bodies: List): ByteArray { val size = bodies.sumOf { - Int.SIZE_BYTES * (10 + it.valueParameterSigs.size + it.typeParameterSigs.size + it.defaultValues.size) + Int.SIZE_BYTES * (11 + it.outerReceiverSigs.size + it.valueParameterSigs.size + it.typeParameterSigs.size + it.defaultValues.size) } val stream = ByteArrayStream(ByteArray(size)) bodies.forEach { @@ -110,6 +111,8 @@ internal object InlineFunctionBodyReferenceSerializer { stream.writeInt(it.endOffset) stream.writeInt(it.extensionReceiverSig) stream.writeInt(it.dispatchReceiverSig) + stream.writeInt(it.outerReceiverSigs.size) + it.outerReceiverSigs.forEach { sig -> stream.writeInt(sig) } stream.writeInt(it.valueParameterSigs.size) it.valueParameterSigs.forEach { sig -> stream.writeInt(sig) } stream.writeInt(it.typeParameterSigs.size) @@ -131,6 +134,8 @@ internal object InlineFunctionBodyReferenceSerializer { val endOffset = stream.readInt() val extensionReceiverSig = stream.readInt() val dispatchReceiverSig = stream.readInt() + val outerReceiverSigsCount = stream.readInt() + val outerReceiverSigs = IntArray(outerReceiverSigsCount) { stream.readInt() } val valueParameterSigsCount = stream.readInt() val valueParameterSigs = IntArray(valueParameterSigsCount) { stream.readInt() } val typeParameterSigsCount = stream.readInt() @@ -138,7 +143,7 @@ internal object InlineFunctionBodyReferenceSerializer { val defaultValuesCount = stream.readInt() val defaultValues = IntArray(defaultValuesCount) { stream.readInt() } result.add(SerializedInlineFunctionReference(file, functionSignature, body, startOffset, endOffset, - extensionReceiverSig, dispatchReceiverSig, valueParameterSigs, typeParameterSigs, defaultValues)) + extensionReceiverSig, dispatchReceiverSig, outerReceiverSigs, valueParameterSigs, typeParameterSigs, defaultValues)) } return result } @@ -153,18 +158,19 @@ class SerializedClassFieldInfo(val name: Int, val binaryType: Int, val type: Int } } -class SerializedClassFields(val file: Int, val classSignature: Int, - val typeParameterSigs: IntArray, val fields: Array) +class SerializedClassFields(val file: Int, val classSignature: Int, val typeParameterSigs: IntArray, + val outerThisIndex: Int, val fields: Array) internal object ClassFieldsSerializer { fun serialize(classFields: List): ByteArray { - val size = classFields.sumOf { Int.SIZE_BYTES * (4 + it.typeParameterSigs.size + it.fields.size * 4) } + val size = classFields.sumOf { Int.SIZE_BYTES * (5 + it.typeParameterSigs.size + it.fields.size * 4) } val stream = ByteArrayStream(ByteArray(size)) classFields.forEach { stream.writeInt(it.file) stream.writeInt(it.classSignature) stream.writeInt(it.typeParameterSigs.size) it.typeParameterSigs.forEach { stream.writeInt(it) } + stream.writeInt(it.outerThisIndex) stream.writeInt(it.fields.size) it.fields.forEach { field -> stream.writeInt(field.name) @@ -184,6 +190,7 @@ internal object ClassFieldsSerializer { val classSignature = stream.readInt() val typeParameterSigsCount = stream.readInt() val typeParameterSigs = IntArray(typeParameterSigsCount) { stream.readInt() } + val outerThisIndex = stream.readInt() val fieldsCount = stream.readInt() val fields = Array(fieldsCount) { val name = stream.readInt() @@ -192,7 +199,7 @@ internal object ClassFieldsSerializer { val flags = stream.readInt() SerializedClassFieldInfo(name, binaryType, type, flags) } - result.add(SerializedClassFields(file, classSignature, typeParameterSigs, fields)) + result.add(SerializedClassFields(file, classSignature, typeParameterSigs, outerThisIndex, fields)) } return result } @@ -412,6 +419,7 @@ internal class KonanIrLinker( } val typeParameterSigs = mutableListOf() + val outerReceiverSigs = mutableListOf() val protoFunction = if (outerClasses.isEmpty()) { val irProperty = (irFunction as? IrSimpleFunction)?.correspondingPropertySymbol?.owner if (irProperty == null) @@ -426,8 +434,11 @@ internal class KonanIrLinker( BinarySymbolData.decode(protoClass.getTypeParameter(it).base.symbol).signatureId } } - if (classIndex < outerClasses.size - 1) + if (classIndex < outerClasses.size - 1) { + if (classIndex >= firstNotInnerClassIndex) + outerReceiverSigs.add(BinarySymbolData.decode(protoClass.thisReceiver.base.symbol).signatureId) protoClass = protoClass.findClass(outerClasses[classIndex + 1], fileReader, symbolDeserializer) + } } protoClass.findInlineFunction(irFunction, fileReader, symbolDeserializer) } @@ -450,7 +461,7 @@ internal class KonanIrLinker( } ?: InvalidIndex return SerializedInlineFunctionReference(fileDeserializationState.fileIndex, functionSignature, protoFunction.base.body, - irFunction.startOffset, irFunction.endOffset, extensionReceiverSig, dispatchReceiverSig, + irFunction.startOffset, irFunction.endOffset, extensionReceiverSig, dispatchReceiverSig, outerReceiverSigs.toIntArray(), valueParameterSigs.toIntArray(), typeParameterSigs.toIntArray(), defaultValues.toIntArray()) } @@ -472,6 +483,7 @@ internal class KonanIrLinker( val typeParameterSigs = mutableListOf() var protoClass = protoDeclaration.irClass + val protoClasses = mutableListOf(protoClass) val firstNotInnerClassIndex = outerClasses.indexOfLast { !it.isInner } for (classIndex in outerClasses.indices) { if (classIndex >= firstNotInnerClassIndex /* owner's type parameters are always accessible */) { @@ -479,8 +491,10 @@ internal class KonanIrLinker( BinarySymbolData.decode(protoClass.getTypeParameter(it).base.symbol).signatureId } } - if (classIndex < outerClasses.size - 1) + if (classIndex < outerClasses.size - 1) { protoClass = protoClass.findClass(outerClasses[classIndex + 1], fileReader, symbolDeserializer) + protoClasses += protoClass + } } val protoFields = mutableListOf() @@ -504,31 +518,43 @@ internal class KonanIrLinker( protoFieldsMap[name] = it } + val outerThisIndex = fields.indexOfFirst { it.irField?.origin == SpecialDeclarationsFactory.DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS } val compatibleMode = CompatibilityMode(libraryAbiVersion).oldSignatures return SerializedClassFields( fileDeserializationState.fileIndex, BinarySymbolData.decode(protoClass.base.symbol).signatureId, typeParameterSigs.toIntArray(), + outerThisIndex, Array(fields.size) { val field = fields[it] val irField = field.irField ?: error("No IR for field ${field.name} of ${irClass.render()}") - val protoField = protoFieldsMap[field.name] ?: error("No proto for ${irField.render()}") - val nameAndType = BinaryNameAndType.decode(protoField.nameType) - var flags = 0 - if (field.isConst) - flags = flags or SerializedClassFieldInfo.FLAG_IS_CONST - if (field.hasConstInitializer) - flags = flags or SerializedClassFieldInfo.FLAG_CONST_INITIALIZER - val classifier = irField.type.classifierOrNull ?: error("Fields of type ${irField.type.render()} are not supported") - val primitiveBinaryType = irField.type.computePrimitiveBinaryTypeOrNull() + if (it == outerThisIndex) { + require(irClass.isInner) { "Expected an inner class: ${irClass.render()}" } + require(protoClasses.size > 1) { "An inner class must have at least one outer class" } + val outerProtoClass = protoClasses[protoClasses.size - 2] + val nameAndType = BinaryNameAndType.decode(outerProtoClass.thisReceiver.nameType) - SerializedClassFieldInfo( - nameAndType.nameIndex, - primitiveBinaryType?.ordinal ?: InvalidIndex, - if (with(KonanManglerIr) { (classifier as? IrClassSymbol)?.owner?.isExported(compatibleMode) } == false) - InvalidIndex - else nameAndType.typeIndex, - flags) + SerializedClassFieldInfo(name = InvalidIndex, binaryType = InvalidIndex, nameAndType.typeIndex, flags = 0) + } else { + val protoField = protoFieldsMap[field.name] ?: error("No proto for ${irField.render()}") + val nameAndType = BinaryNameAndType.decode(protoField.nameType) + var flags = 0 + if (field.isConst) + flags = flags or SerializedClassFieldInfo.FLAG_IS_CONST + if (field.hasConstInitializer) + flags = flags or SerializedClassFieldInfo.FLAG_CONST_INITIALIZER + val classifier = irField.type.classifierOrNull + ?: error("Fields of type ${irField.type.render()} are not supported") + val primitiveBinaryType = irField.type.computePrimitiveBinaryTypeOrNull() + + SerializedClassFieldInfo( + nameAndType.nameIndex, + primitiveBinaryType?.ordinal ?: InvalidIndex, + if (with(KonanManglerIr) { (classifier as? IrClassSymbol)?.owner?.isExported(compatibleMode) } == false) + InvalidIndex + else nameAndType.typeIndex, + flags) + } }) } } @@ -732,6 +758,10 @@ internal class KonanIrLinker( require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the dispatch receiver for ${function.render()}" } symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex)) } + for (index in 0 until outerClasses.size - 1) { + val sigIndex = inlineFunctionReference.outerReceiverSigs[index] + symbolDeserializer.referenceLocalIrSymbol(outerClasses[index].thisReceiver!!.symbol, symbolDeserializer.deserializeIdSignature(sigIndex)) + } function.body = declarationDeserializer.deserializeStatementBody(inlineFunctionReference.body) as IrBody @@ -759,7 +789,7 @@ internal class KonanIrLinker( } } - fun deserializeClassFields(irClass: IrClass): List { + fun deserializeClassFields(irClass: IrClass, outerThisField: IrField?): List { val signature = irClass.symbol.signature ?: error("No signature for ${irClass.render()}") val serializedClassFields = classesFields[signature] @@ -786,29 +816,35 @@ internal class KonanIrLinker( return symbolDeserializer.deserializePublicSymbol(classIdSig, BinarySymbolData.SymbolKind.CLASS_SYMBOL) as IrClassSymbol } - return serializedClassFields.fields.map { - val name = fileDeserializationInfo.fileReader.string(it.name) - val type = when { - it.type != InvalidIndex -> declarationDeserializer.deserializeIrType(it.type) - it.binaryType == InvalidIndex -> builtIns.anyNType - else -> when (PrimitiveBinaryType.values().getOrNull(it.binaryType)) { - PrimitiveBinaryType.BOOLEAN -> builtIns.booleanType - PrimitiveBinaryType.BYTE -> builtIns.byteType - PrimitiveBinaryType.SHORT -> builtIns.shortType - PrimitiveBinaryType.INT -> builtIns.intType - PrimitiveBinaryType.LONG -> builtIns.longType - PrimitiveBinaryType.FLOAT -> builtIns.floatType - PrimitiveBinaryType.DOUBLE -> builtIns.doubleType - PrimitiveBinaryType.POINTER -> getByClassId(KonanPrimitiveType.NON_NULL_NATIVE_PTR.classId).defaultType - PrimitiveBinaryType.VECTOR128 -> getByClassId(KonanPrimitiveType.VECTOR128.classId).defaultType - else -> error("Bad binary type of field $name of ${irClass.render()}") + return serializedClassFields.fields.mapIndexed { index, field -> + if (index == serializedClassFields.outerThisIndex) { + require(irClass.isInner) { "Expected an inner class: ${irClass.render()}" } + require(outerThisField != null) { "For an inner class ${irClass.render()} there should be field" } + outerThisField.toFieldInfo() + } else { + val name = fileDeserializationInfo.fileReader.string(field.name) + val type = when { + field.type != InvalidIndex -> declarationDeserializer.deserializeIrType(field.type) + field.binaryType == InvalidIndex -> builtIns.anyNType + else -> when (PrimitiveBinaryType.values().getOrNull(field.binaryType)) { + PrimitiveBinaryType.BOOLEAN -> builtIns.booleanType + PrimitiveBinaryType.BYTE -> builtIns.byteType + PrimitiveBinaryType.SHORT -> builtIns.shortType + PrimitiveBinaryType.INT -> builtIns.intType + PrimitiveBinaryType.LONG -> builtIns.longType + PrimitiveBinaryType.FLOAT -> builtIns.floatType + PrimitiveBinaryType.DOUBLE -> builtIns.doubleType + PrimitiveBinaryType.POINTER -> getByClassId(KonanPrimitiveType.NON_NULL_NATIVE_PTR.classId).defaultType + PrimitiveBinaryType.VECTOR128 -> getByClassId(KonanPrimitiveType.VECTOR128.classId).defaultType + else -> error("Bad binary type of field $name of ${irClass.render()}") + } } + ClassLayoutBuilder.FieldInfo( + name, type, + isConst = (field.flags and SerializedClassFieldInfo.FLAG_IS_CONST) != 0, + hasConstInitializer = (field.flags and SerializedClassFieldInfo.FLAG_CONST_INITIALIZER) != 0, + irField = null) } - ClassLayoutBuilder.FieldInfo( - name, type, - isConst = (it.flags and SerializedClassFieldInfo.FLAG_IS_CONST) != 0, - hasConstInitializer = (it.flags and SerializedClassFieldInfo.FLAG_CONST_INITIALIZER) != 0, - irField = null) } } } diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index d3176762bd6..e2ba0643703 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -1416,6 +1416,24 @@ linkTest("innerClass_linkTest") { lib = "codegen/innerClass/linkTest_lib.kt" } +linkTest("innerClass_inheritance_linkTest") { + useGoldenData = true + source = "codegen/innerClass/inheritance_linkTest_main.kt" + lib = "codegen/innerClass/inheritance_linkTest_lib.kt" +} + +linkTest("innerClass_inheritance2_linkTest") { + useGoldenData = true + source = "codegen/innerClass/inheritance2_linkTest_main.kt" + lib = "codegen/innerClass/inheritance2_linkTest_lib.kt" +} + +linkTest("innerClass_inheritance3_linkTest") { + useGoldenData = true + source = "codegen/innerClass/inheritance3_linkTest_main.kt" + lib = "codegen/innerClass/inheritance3_linkTest_lib.kt" +} + task localClass_localHierarchy(type: KonanLocalTest) { useGoldenData = true source = "codegen/localClass/localHierarchy.kt" diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_lib.kt b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_lib.kt new file mode 100644 index 00000000000..ac36c27f53b --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_lib.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +open class Outer(val x: Int) { + open inner class Inner1 + inner class Middle(x: Int) : Outer(x) { + inner class Inner2 : Inner1() { + fun foo() = this@Outer.x + this@Middle.x + } + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.kt b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.kt new file mode 100644 index 00000000000..1b8b308b1df --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.kt @@ -0,0 +1,9 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +fun main() { + val o = Outer(42).Middle(117).Inner2() + println(o.foo()) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.out b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.out new file mode 100644 index 00000000000..3f7d1915f71 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance2_linkTest_main.out @@ -0,0 +1 @@ +159 diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_lib.kt b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_lib.kt new file mode 100644 index 00000000000..da3f0f4a75f --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_lib.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +open class Outer { + open inner class Inner1 + inner class Middle { + inner class Inner2 : Inner1() { + fun getOuter() = this@Outer + fun getMiddle() = this@Middle + } + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.kt b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.kt new file mode 100644 index 00000000000..c747f110088 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +fun main() { + val o = Outer().Middle().Inner2() + println(o.getOuter() != Outer()) + println(o.getMiddle() != Outer().Middle()) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.out b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.out new file mode 100644 index 00000000000..bb101b641b9 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance3_linkTest_main.out @@ -0,0 +1,2 @@ +true +true diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_lib.kt b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_lib.kt new file mode 100644 index 00000000000..0c60c133801 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_lib.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +open class Foo(val z: Int) { + open inner class FooInner { + fun foo() = z + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.kt b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.kt new file mode 100644 index 00000000000..40e50d3d05e --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ + +class Bar : Foo(42) { + inner class BarInner(val x: Int) : FooInner() +} + +fun main() { + val o = Bar().BarInner(117) + println(o.x) + println(o.foo()) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.out b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.out new file mode 100644 index 00000000000..01c576313a0 --- /dev/null +++ b/kotlin-native/backend.native/tests/codegen/innerClass/inheritance_linkTest_main.out @@ -0,0 +1,2 @@ +117 +42