From 322a8a443be059725bb55ccad9ac970203fd7e1b Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Tue, 24 Jan 2023 13:05:29 +0100 Subject: [PATCH] Native: add included forward declarations into cinterop klib manifest This allows the compiler to limit imports from "magic" packages like cnames.structs to forward declarations actually present in dependent cinterop klibs. The manifest already had `exportForwardDeclarations` field, but - it didn't include Objective-C forward declarations (@class, @protocol) - it has a slightly different meaning (the export list enables importing the listed declarations through the interop package of the library, while the include list shouldn't do that), so we better avoid mixing them. --- .../jetbrains/kotlin/library/KotlinLibrary.kt | 4 +++ .../native/interop/gen/KotlinCodeModel.kt | 2 ++ .../kotlin/native/interop/gen/Mappings.kt | 6 ++-- .../native/interop/gen/StubIrBuilder.kt | 5 ++-- .../kotlin/native/interop/gen/StubIrDriver.kt | 28 ++++++++++++++++++- 5 files changed, 40 insertions(+), 5 deletions(-) diff --git a/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinLibrary.kt b/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinLibrary.kt index 8bfb03af202..f49dbfc3ba6 100644 --- a/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinLibrary.kt +++ b/compiler/util-klib/src/org/jetbrains/kotlin/library/KotlinLibrary.kt @@ -20,6 +20,7 @@ const val KLIB_PROPERTY_CONTAINS_ERROR_CODE = "contains_error_code" // Native-specific: const val KLIB_PROPERTY_INTEROP = "interop" const val KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS = "exportForwardDeclarations" +const val KLIB_PROPERTY_INCLUDED_FORWARD_DECLARATIONS = "includedForwardDeclarations" /** * Copy-pasted to `kotlin-native/build-tools/src/main/kotlin/org/jetbrains/kotlin/Utils.kt` @@ -104,6 +105,9 @@ val KotlinLibrary.packageFqName: String? val KotlinLibrary.exportForwardDeclarations: List get() = manifestProperties.propertyList(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS, escapeInQuotes = true) +val KotlinLibrary.includedForwardDeclarations: List + get() = manifestProperties.propertyList(KLIB_PROPERTY_INCLUDED_FORWARD_DECLARATIONS, escapeInQuotes = true) + val BaseKotlinLibrary.nativeTargets: List get() = manifestProperties.propertyList(KLIB_PROPERTY_NATIVE_TARGETS) diff --git a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt index 91b2cda44a1..502092a779c 100644 --- a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt +++ b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/KotlinCodeModel.kt @@ -162,6 +162,8 @@ data class KotlinFunctionType( } internal val cnamesStructsPackageName = "cnames.structs" +internal val objcnamesClassesPackageName = "objcnames.classes" +internal val objcnamesProtocolsPackageName = "objcnames.protocols" object KotlinTypes { val independent = Classifier.topLevel("kotlin.native.internal", "Independent") diff --git a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt index f4f406d7baf..74916e01e7b 100644 --- a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt +++ b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/Mappings.kt @@ -37,8 +37,8 @@ fun DeclarationMapper.getKotlinClassFor( ): Classifier { val pkg = if (objCClassOrProtocol.isForwardDeclaration) { when (objCClassOrProtocol) { - is ObjCClass -> "objcnames.classes" - is ObjCProtocol -> "objcnames.protocols" + is ObjCClass -> objcnamesClassesPackageName + is ObjCProtocol -> objcnamesProtocolsPackageName } } else { this.getPackageFor(objCClassOrProtocol) @@ -539,6 +539,8 @@ internal tailrec fun ObjCClass.isNSStringOrSubclass(): Boolean = when (this.name internal fun ObjCClass.isNSStringSubclass(): Boolean = this.baseClass?.isNSStringOrSubclass() == true +internal fun ObjCClass.shouldBeIncludedIntoKotlinAPI(): Boolean = !this.isNSStringSubclass() + private fun objCPointerMirror(declarationMapper: DeclarationMapper, type: ObjCPointer): TypeMirror.ByValue { if (type is ObjCObjectPointer && type.def.isNSStringOrSubclass()) { val valueType = KotlinTypes.string diff --git a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt index 166bd375a47..65cd797a16a 100644 --- a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt +++ b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrBuilder.kt @@ -329,8 +329,9 @@ class StubIrBuilder(private val context: StubIrContext) { fun build(): StubIrBuilderResult { nativeIndex.objCProtocols.filter { !it.isForwardDeclaration }.forEach { generateStubsForObjCProtocol(it) } - nativeIndex.objCClasses.filter { !it.isForwardDeclaration && !it.isNSStringSubclass()} .forEach { generateStubsForObjCClass(it) } - nativeIndex.objCCategories.filter { !it.clazz.isNSStringSubclass() }.forEach { generateStubsForObjCCategory(it) } + nativeIndex.objCClasses.filter { !it.isForwardDeclaration && it.shouldBeIncludedIntoKotlinAPI() } + .forEach { generateStubsForObjCClass(it) } + nativeIndex.objCCategories.filter { it.clazz.shouldBeIncludedIntoKotlinAPI() }.forEach { generateStubsForObjCCategory(it) } nativeIndex.structs.forEach { generateStubsForStruct(it) } nativeIndex.enums.forEach { generateStubsForEnum(it) } nativeIndex.functions.filter { it.name !in excludedFunctions }.forEach { generateStubsForFunction(it) } diff --git a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt index 7e83f3e7d1f..417f1940182 100644 --- a/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt +++ b/kotlin-native/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/StubIrDriver.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.native.interop.gen import kotlinx.metadata.klib.KlibModuleMetadata +import org.jetbrains.kotlin.library.KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS +import org.jetbrains.kotlin.library.KLIB_PROPERTY_INCLUDED_FORWARD_DECLARATIONS import org.jetbrains.kotlin.native.interop.gen.jvm.GenerationMode import org.jetbrains.kotlin.native.interop.gen.jvm.InteropConfiguration import org.jetbrains.kotlin.native.interop.gen.jvm.KotlinPlatform @@ -93,7 +95,31 @@ class StubIrContext( "$cnamesStructsPackageName.${getKotlinName(it)}" } - properties["exportForwardDeclarations"] = exportForwardDeclarations.joinToString(" ") + // Note: includedForwardDeclarations is somewhat similar to exportForwardDeclarations. But reusing the latter + // instead is undesirable: exportForwardDeclarations makes the compiler enable importing the listed declarations + // through interop library package. E.g., if `cnames.structs.Foo` is in exportForwardDeclarations of a cinterop + // klib bar with package bar, then `import bar.Foo` is valid. This is an arguable feature and a candidate for + // deprecation, so enabling it for new declarations instead is undesirable. + // That's why, to make the included Obj-C forward declarations known to the compiler, we have to create a new + // manifest property for that. + val includedForwardDeclarations = mutableListOf() + includedForwardDeclarations.addAll(exportForwardDeclarations) + + // TODO: should we add meta classes? + nativeIndex.objCClasses + .filter { it.isForwardDeclaration && it.shouldBeIncludedIntoKotlinAPI() } + .mapTo(includedForwardDeclarations) { + "$objcnamesClassesPackageName.${it.kotlinClassName(isMeta = false)}" + } + + nativeIndex.objCProtocols + .filter { it.isForwardDeclaration } + .mapTo(includedForwardDeclarations) { + "$objcnamesProtocolsPackageName.${it.kotlinClassName(isMeta = false)}" + } + + properties[KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS] = exportForwardDeclarations.joinToString(" ") + properties[KLIB_PROPERTY_INCLUDED_FORWARD_DECLARATIONS] = includedForwardDeclarations.joinToString(" ") // TODO: consider exporting Objective-C class and protocol forward refs. }