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.
This commit is contained in:
Svyatoslav Scherbina
2023-01-24 13:05:29 +01:00
committed by Space Team
parent 86a29a7b07
commit 322a8a443b
5 changed files with 40 additions and 5 deletions
@@ -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<String>
get() = manifestProperties.propertyList(KLIB_PROPERTY_EXPORT_FORWARD_DECLARATIONS, escapeInQuotes = true)
val KotlinLibrary.includedForwardDeclarations: List<String>
get() = manifestProperties.propertyList(KLIB_PROPERTY_INCLUDED_FORWARD_DECLARATIONS, escapeInQuotes = true)
val BaseKotlinLibrary.nativeTargets: List<String>
get() = manifestProperties.propertyList(KLIB_PROPERTY_NATIVE_TARGETS)
@@ -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")
@@ -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
@@ -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) }
@@ -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<String>()
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.
}