Add facility to restrict extra classes in Mobile ObjC Generator (#4498)

- Implement way to restrict extra classes from other modules in Mobile ObjC generator
- Split generation into smaller steps
- Add API to retrieve stubs and foundation imports
This commit is contained in:
Florian Kistner
2020-11-09 21:22:14 +01:00
committed by Stanislav Erokhin
parent edfdc88d6d
commit 598d0e07e3
3 changed files with 94 additions and 29 deletions
@@ -240,7 +240,7 @@ internal class ObjCExportTranslatorImpl(
.asSequence() .asSequence()
.filter { mapper.shouldBeExposed(it) } .filter { mapper.shouldBeExposed(it) }
.map { .map {
generator?.generateInterface(it) generator?.generateExtraInterfaceEarly(it)
referenceProtocol(it).objCName referenceProtocol(it).objCName
} }
.toList() .toList()
@@ -249,7 +249,7 @@ internal class ObjCExportTranslatorImpl(
classDescriptor: ClassDescriptor, classDescriptor: ClassDescriptor,
declarations: List<CallableMemberDescriptor> declarations: List<CallableMemberDescriptor>
): ObjCInterface { ): ObjCInterface {
generator?.generateClass(classDescriptor) generator?.generateExtraClassEarly(classDescriptor)
val name = referenceClass(classDescriptor).objCName val name = referenceClass(classDescriptor).objCName
val members = buildMembers { val members = buildMembers {
@@ -301,7 +301,7 @@ internal class ObjCExportTranslatorImpl(
val superName = if (superClass == null) { val superName = if (superClass == null) {
kotlinAnyName kotlinAnyName
} else { } else {
generator?.generateClass(superClass) generator?.generateExtraClassEarly(superClass)
referenceClass(superClass) referenceClass(superClass)
} }
@@ -1002,16 +1002,8 @@ abstract class ObjCExportHeaderGenerator internal constructor(
private val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>() private val topLevel = mutableMapOf<SourceFile, MutableList<CallableMemberDescriptor>>()
fun build(): List<String> = mutableListOf<String>().apply { fun build(): List<String> = mutableListOf<String>().apply {
add("#import <Foundation/NSArray.h>") addImports(foundationImports)
add("#import <Foundation/NSDictionary.h>") addImports(getAdditionalImports())
add("#import <Foundation/NSError.h>")
add("#import <Foundation/NSObject.h>")
add("#import <Foundation/NSSet.h>")
add("#import <Foundation/NSString.h>")
add("#import <Foundation/NSValue.h>")
getAdditionalImports().forEach {
add("#import <$it>")
}
add("") add("")
if (classForwardDeclarations.isNotEmpty()) { if (classForwardDeclarations.isNotEmpty()) {
@@ -1054,19 +1046,32 @@ abstract class ObjCExportHeaderGenerator internal constructor(
return ObjCExportedInterface(generatedClasses, extensions, topLevel, headerLines, namer, mapper) return ObjCExportedInterface(generatedClasses, extensions, topLevel, headerLines, namer, mapper)
} }
fun getExportStubs(): ObjCExportedStubs =
ObjCExportedStubs(classForwardDeclarations, protocolForwardDeclarations, stubs)
protected abstract fun reportWarning(text: String) protected abstract fun reportWarning(text: String)
protected abstract fun reportWarning(method: FunctionDescriptor, text: String) protected abstract fun reportWarning(method: FunctionDescriptor, text: String)
protected open fun getAdditionalImports(): List<String> = emptyList() protected open fun getAdditionalImports(): List<String> = emptyList()
fun translateModule() {
fun translateModule(): List<Stub<*>> {
// TODO: make the translation order stable // TODO: make the translation order stable
// to stabilize name mangling. // to stabilize name mangling.
translateBaseDeclarations()
translateModuleDeclarations()
}
fun translateBaseDeclarations() {
stubs += translator.generateBaseDeclarations() stubs += translator.generateBaseDeclarations()
}
fun translateModuleDeclarations() {
translatePackageFragments()
translateExtraClasses()
}
private fun translatePackageFragments() {
val packageFragments = moduleDescriptors.flatMap { it.getPackageFragments() } val packageFragments = moduleDescriptors.flatMap { it.getPackageFragments() }
packageFragments.forEach { packageFragment -> packageFragments.forEach { packageFragment ->
@@ -1119,18 +1124,31 @@ abstract class ObjCExportHeaderGenerator internal constructor(
topLevel.forEach { sourceFile, declarations -> topLevel.forEach { sourceFile, declarations ->
generateFile(sourceFile, declarations) generateFile(sourceFile, declarations)
} }
}
/**
* Translates additional classes referenced from the module's declarations, such as parameter types, return types,
* thrown exception types, and underlying enum types.
*
* This is required for classes from dependencies to be exported correctly. However, we also currently rely on this
* for a few edge cases, such as some inner classes. Sub classes may reject certain descriptors to be translated.
* Some referenced descriptors may be translated early for ordering reasons.
* @see shouldTranslateExtraClass
* @see generateExtraClassEarly
* @see generateExtraInterfaceEarly
*/
private fun translateExtraClasses() {
while (extraClassesToTranslate.isNotEmpty()) { while (extraClassesToTranslate.isNotEmpty()) {
val descriptor = extraClassesToTranslate.first() val descriptor = extraClassesToTranslate.first()
extraClassesToTranslate -= descriptor extraClassesToTranslate -= descriptor
assert(shouldTranslateExtraClass(descriptor)) { "Shouldn't be queued for translation: $descriptor" }
if (descriptor.isInterface) { if (descriptor.isInterface) {
generateInterface(descriptor) generateInterface(descriptor)
} else { } else {
generateClass(descriptor) generateClass(descriptor)
} }
} }
return stubs
} }
private fun generateFile(sourceFile: SourceFile, declarations: List<CallableMemberDescriptor>) { private fun generateFile(sourceFile: SourceFile, declarations: List<CallableMemberDescriptor>) {
@@ -1141,18 +1159,28 @@ abstract class ObjCExportHeaderGenerator internal constructor(
stubs.add(translator.translateExtensions(classDescriptor, declarations)) stubs.add(translator.translateExtensions(classDescriptor, declarations))
} }
internal fun generateClass(descriptor: ClassDescriptor) { protected open fun shouldTranslateExtraClass(descriptor: ClassDescriptor): Boolean = true
internal fun generateExtraClassEarly(descriptor: ClassDescriptor) {
if (shouldTranslateExtraClass(descriptor)) generateClass(descriptor)
}
internal fun generateExtraInterfaceEarly(descriptor: ClassDescriptor) {
if (shouldTranslateExtraClass(descriptor)) generateInterface(descriptor)
}
private fun generateClass(descriptor: ClassDescriptor) {
if (!generatedClasses.add(descriptor)) return if (!generatedClasses.add(descriptor)) return
stubs.add(translator.translateClass(descriptor)) stubs.add(translator.translateClass(descriptor))
} }
internal fun generateInterface(descriptor: ClassDescriptor) { private fun generateInterface(descriptor: ClassDescriptor) {
if (!generatedClasses.add(descriptor)) return if (!generatedClasses.add(descriptor)) return
stubs.add(translator.translateInterface(descriptor)) stubs.add(translator.translateInterface(descriptor))
} }
internal fun requireClassOrInterface(descriptor: ClassDescriptor) { internal fun requireClassOrInterface(descriptor: ClassDescriptor) {
if (descriptor !in generatedClasses) { if (shouldTranslateExtraClass(descriptor) && descriptor !in generatedClasses) {
extraClassesToTranslate += descriptor extraClassesToTranslate += descriptor
} }
} }
@@ -1164,6 +1192,24 @@ abstract class ObjCExportHeaderGenerator internal constructor(
internal fun referenceProtocol(objCName: String) { internal fun referenceProtocol(objCName: String) {
protocolForwardDeclarations += objCName protocolForwardDeclarations += objCName
} }
companion object {
val foundationImports = listOf(
"Foundation/NSArray.h",
"Foundation/NSDictionary.h",
"Foundation/NSError.h",
"Foundation/NSObject.h",
"Foundation/NSSet.h",
"Foundation/NSString.h",
"Foundation/NSValue.h"
)
private fun MutableList<String>.addImports(imports: Iterable<String>) {
imports.forEach {
add("#import <$it>")
}
}
}
} }
private fun objCInterface( private fun objCInterface(
@@ -0,0 +1,12 @@
/*
* Copyright 2010-2020 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.
*/
package org.jetbrains.kotlin.backend.konan.objcexport
data class ObjCExportedStubs(
val classForwardDeclarations: Set<String>,
val protocolForwardDeclarations: Set<String>,
val stubs: List<Stub<*>>
)
@@ -1,16 +1,19 @@
package org.jetbrains.kotlin.backend.konan.objcexport package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
import org.jetbrains.kotlin.resolve.descriptorUtil.module
class ObjcExportHeaderGeneratorMobile internal constructor( class ObjcExportHeaderGeneratorMobile internal constructor(
moduleDescriptors: List<ModuleDescriptor>, moduleDescriptors: List<ModuleDescriptor>,
mapper: ObjCExportMapper, mapper: ObjCExportMapper,
namer: ObjCExportNamer, namer: ObjCExportNamer,
private val warningCollector: ObjCExportWarningCollector, private val warningCollector: ObjCExportWarningCollector,
objcGenerics: Boolean objcGenerics: Boolean,
private val restrictToLocalModules: Boolean
) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics) { ) : ObjCExportHeaderGenerator(moduleDescriptors, mapper, namer, objcGenerics) {
companion object { companion object {
@@ -20,21 +23,26 @@ class ObjcExportHeaderGeneratorMobile internal constructor(
builtIns: KotlinBuiltIns, builtIns: KotlinBuiltIns,
moduleDescriptors: List<ModuleDescriptor>, moduleDescriptors: List<ModuleDescriptor>,
deprecationResolver: DeprecationResolver? = null, deprecationResolver: DeprecationResolver? = null,
local: Boolean = false): ObjCExportHeaderGenerator { local: Boolean = false,
restrictToLocalModules: Boolean = false): ObjCExportHeaderGenerator {
val mapper = ObjCExportMapper(deprecationResolver, local) val mapper = ObjCExportMapper(deprecationResolver, local)
val namerConfiguration = createNamerConfiguration(configuration) val namerConfiguration = createNamerConfiguration(configuration)
val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, local) val namer = ObjCExportNamerImpl(namerConfiguration, builtIns, mapper, local)
return ObjcExportHeaderGeneratorMobile( return ObjcExportHeaderGeneratorMobile(
moduleDescriptors, moduleDescriptors,
mapper, mapper,
namer, namer,
warningCollector, warningCollector,
configuration.objcGenerics) configuration.objcGenerics,
restrictToLocalModules
)
} }
} }
override fun shouldTranslateExtraClass(descriptor: ClassDescriptor): Boolean =
!restrictToLocalModules || descriptor.module in moduleDescriptors
override fun reportWarning(text: String) { override fun reportWarning(text: String) {
warningCollector.reportWarning(text) warningCollector.reportWarning(text)
} }
@@ -42,5 +50,4 @@ class ObjcExportHeaderGeneratorMobile internal constructor(
override fun reportWarning(method: FunctionDescriptor, text: String) { override fun reportWarning(method: FunctionDescriptor, text: String) {
warningCollector.reportWarning(method, text) warningCollector.reportWarning(method, text)
} }
} }