Support interop modularity

* Add list of included headers into the manifest
* Implement importing "foreign" type declarations
* Implement forward declarations more natively in the compiler
* Represent Objective-C categories as Kotlin extension methods and
  properties

Also:
* Do some refactoring in stub generation
* Call bridges directly in stubs for Objective-C properties
This commit is contained in:
Svyatoslav Scherbina
2017-09-04 16:14:32 +03:00
committed by SvyatoslavScherbina
parent 82b2b76c09
commit 79455c5161
28 changed files with 1410 additions and 390 deletions
@@ -16,7 +16,12 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.descriptors.ClassifierAliasingPackageFragmentDescriptor
import org.jetbrains.kotlin.backend.konan.descriptors.ExportedForwardDeclarationsPackageFragmentDescriptor
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
@@ -25,6 +30,23 @@ import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.util.OperatorNameConventions
interface InteropLibrary {
fun createSyntheticPackages(
module: ModuleDescriptor,
kotlinPackageFragments: List<PackageFragmentDescriptor>
): List<PackageFragmentDescriptor>
}
fun createInteropLibrary(reader: KonanLibraryReader): InteropLibrary? {
val pkg = reader.manifestProperties.getProperty("pkg") ?: return null
val exportForwardDeclarations = reader.manifestProperties
.getProperty("exportForwardDeclarations").split(' ')
.map { it.trim() }.filter { it.isNotEmpty() }
.map { FqName(it) }
return InteropLibraryImpl(FqName(pkg), exportForwardDeclarations)
}
private val cPointerName = "CPointer"
private val nativePointedName = "NativePointed"
@@ -35,6 +57,13 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
val cPointer = packageName.child(Name.identifier(cPointerName)).toUnsafe()
val nativePointed = packageName.child(Name.identifier(nativePointedName)).toUnsafe()
val cNames = FqName("cnames")
val cNamesStructs = cNames.child(Name.identifier("structs"))
val objCNames = FqName("objcnames")
val objCNamesClasses = objCNames.child(Name.identifier("classes"))
val objCNamesProtocols = objCNames.child(Name.identifier("protocols"))
}
private val packageScope = builtIns.builtInsModule.getPackage(FqNames.packageName).memberScope
@@ -174,4 +203,32 @@ private fun MemberScope.getContributedClass(name: String): ClassDescriptor =
this.getContributedClassifier(Name.identifier(name), NoLookupLocation.FROM_BUILTINS) as ClassDescriptor
private fun MemberScope.getContributedFunctions(name: String) =
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
this.getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BUILTINS)
private class InteropLibraryImpl(
private val packageFqName: FqName,
private val exportForwardDeclarations: List<FqName>
) : InteropLibrary {
override fun createSyntheticPackages(
module: ModuleDescriptor,
kotlinPackageFragments: List<PackageFragmentDescriptor>
): List<PackageFragmentDescriptor> {
val interopPackageFragments = kotlinPackageFragments.filter { it.fqName == packageFqName }
val fqNames = InteropBuiltIns.FqNames
val result = mutableListOf<PackageFragmentDescriptor>()
// Allow references to forwarding declarations to be resolved into classifiers declared in this library:
listOf(fqNames.cNamesStructs, fqNames.objCNamesClasses, fqNames.objCNamesProtocols).mapTo(result) { fqName ->
ClassifierAliasingPackageFragmentDescriptor(interopPackageFragments, module, fqName)
}
// TODO: use separate namespaces for structs, enums, Objective-C protocols etc.
result.add(ExportedForwardDeclarationsPackageFragmentDescriptor(
module, packageFqName, exportForwardDeclarations
))
return result
}
}
@@ -17,12 +17,14 @@
package org.jetbrains.kotlin.backend.konan
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.backend.konan.descriptors.createForwardDeclarationsModule
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import org.jetbrains.kotlin.backend.konan.library.KonanLibrarySearchPathResolver
import org.jetbrains.kotlin.backend.konan.library.impl.LibraryReaderImpl
import org.jetbrains.kotlin.backend.konan.util.profile
import org.jetbrains.kotlin.backend.konan.util.removeSuffixIfPresent
import org.jetbrains.kotlin.backend.konan.util.suffixIfNot
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
@@ -34,6 +36,8 @@ import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.konan.target.TargetManager.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind.*
import org.jetbrains.kotlin.konan.util.DependencyProcessor
import org.jetbrains.kotlin.storage.LockBasedStorageManager
import org.jetbrains.kotlin.storage.StorageManager
class KonanConfig(val project: Project, val configuration: CompilerConfiguration) {
@@ -116,10 +120,25 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
return allMetadata
}
private var forwardDeclarationsModule: ModuleDescriptorImpl? = null
internal fun getOrCreateForwardDeclarationsModule(
builtIns: KotlinBuiltIns, storageManager: StorageManager? = null
): ModuleDescriptorImpl {
forwardDeclarationsModule?.let { return it }
val result = createForwardDeclarationsModule(
builtIns,
storageManager ?: LockBasedStorageManager()
)
forwardDeclarationsModule = result
return result
}
internal val moduleDescriptors: List<ModuleDescriptorImpl> by lazy {
for (module in loadedDescriptors) {
// Yes, just to all of them.
module.setDependencies(loadedDescriptors)
module.setDependencies(loadedDescriptors + getOrCreateForwardDeclarationsModule(module.builtIns))
}
loadedDescriptors
@@ -39,7 +39,8 @@ object TopDownAnalyzerFacadeForKonan {
builtIns.builtInsModule = module
if (!module.isStdlib()) {
context.setDependencies(listOf(module) + config.moduleDescriptors)
context.setDependencies(listOf(module) + config.moduleDescriptors +
config.getOrCreateForwardDeclarationsModule(builtIns, projectContext.storageManager))
} else {
assert (config.moduleDescriptors.isEmpty())
context.setDependencies(module)
@@ -0,0 +1,173 @@
/*
* Copyright 2010-2017 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.konan.descriptors
import org.jetbrains.kotlin.backend.konan.InteropBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.kotlin.descriptors.impl.PackageFragmentDescriptorImpl
import org.jetbrains.kotlin.incremental.components.LookupLocation
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.resolve.scopes.MemberScopeImpl
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
import org.jetbrains.kotlin.utils.Printer
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
/**
* The package fragment to export forward declarations from interop package namespace, i.e.
* redirect "$pkg.$name" to e.g. "cnames.structs.$name".
*/
class ExportedForwardDeclarationsPackageFragmentDescriptor(
module: ModuleDescriptor, fqName: FqName, declarations: List<FqName>
) : PackageFragmentDescriptorImpl(module, fqName) {
private val memberScope = object : MemberScopeImpl() {
private val nameToFqName = declarations.map { it.shortName() to it }.toMap()
override fun getContributedClassifier(name: Name, location: LookupLocation): ClassifierDescriptor? {
val declFqName = nameToFqName[name] ?: return null
val packageView = module.getPackage(declFqName.parent())
return packageView.memberScope.getContributedClassifier(name, location)
}
override fun printScopeStructure(p: Printer) {
p.println(this::class.java.simpleName, " {")
p.pushIndent()
// TODO
p.popIndent()
p.println("}")
}
}
override fun getMemberScope() = memberScope
}
/**
* The package fragment that redirects all requests for classifier lookup to its targets.
*/
class ClassifierAliasingPackageFragmentDescriptor(
targets: List<PackageFragmentDescriptor>, module: ModuleDescriptor, fqName: FqName
) : PackageFragmentDescriptorImpl(module, fqName) {
private val memberScope = object : MemberScopeImpl() {
override fun getContributedClassifier(name: Name, location: LookupLocation) =
targets.firstNotNullResult {
it.getMemberScope().getContributedClassifier(name, location)
}
override fun printScopeStructure(p: Printer) {
p.println(this::class.java.simpleName, " {")
p.pushIndent()
p.println("targets = " + targets)
p.popIndent()
p.println("}")
}
}
override fun getMemberScope(): MemberScope = memberScope
}
/**
* Package fragment which creates descriptors for forward declarations on demand.
*/
private class ForwardDeclarationsPackageFragmentDescriptor(
storageManager: StorageManager,
module: ModuleDescriptor, fqName: FqName, supertypeName: Name, classKind: ClassKind
) : PackageFragmentDescriptorImpl(module, fqName) {
private val memberScope = object : MemberScopeImpl() {
private val declarations = storageManager.createMemoizedFunction(this::createDeclaration)
private val supertype by storageManager.createLazyValue {
val descriptor = builtIns.builtInsModule.getPackage(InteropBuiltIns.FqNames.packageName)
.memberScope
.getContributedClassifier(supertypeName, NoLookupLocation.FROM_BACKEND) as ClassDescriptor
descriptor.defaultType
}
private fun createDeclaration(name: Name): ClassDescriptor {
return ClassDescriptorImpl(
this@ForwardDeclarationsPackageFragmentDescriptor,
name, Modality.FINAL, classKind,
listOf(supertype), SourceElement.NO_SOURCE, false
).apply {
this.initialize(MemberScope.Empty, emptySet(), null)
}
}
override fun getContributedClassifier(name: Name, location: LookupLocation) = declarations(name)
override fun printScopeStructure(p: Printer) {
p.println(this::class.java.simpleName, "{}")
}
}
override fun getMemberScope(): MemberScope = memberScope
}
/**
* Creates module which "contains" forward declarations.
* Note: this module should be unique per compilation and should always be the last dependency of any module.
*/
fun createForwardDeclarationsModule(builtIns: KotlinBuiltIns, storageManager: StorageManager): ModuleDescriptorImpl {
val module = ModuleDescriptorImpl(
Name.special("<forward declarations>"),
storageManager,
builtIns
)
fun createPackage(fqName: FqName, supertypeName: String, classKind: ClassKind = ClassKind.CLASS) =
ForwardDeclarationsPackageFragmentDescriptor(
storageManager,
module,
fqName,
Name.identifier(supertypeName),
classKind
)
val fqNames = InteropBuiltIns.FqNames
val packageFragmentProvider = PackageFragmentProviderImpl(
listOf(
createPackage(fqNames.cNamesStructs, "COpaque"),
createPackage(fqNames.objCNamesClasses, "ObjCObjectBase"),
createPackage(fqNames.objCNamesProtocols, "ObjCObject", ClassKind.INTERFACE)
)
)
module.initialize(packageFragmentProvider)
module.setDependencies(module)
return module
}
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.backend.konan.library
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.konan.properties.Properties
interface KonanLibraryReader {
val libraryName: String
@@ -25,6 +26,7 @@ interface KonanLibraryReader {
val includedPaths: List<String>
val linkerOpts: List<String>
val escapeAnalysis: ByteArray?
val manifestProperties: Properties
fun moduleDescriptor(specifics: LanguageVersionSettings): ModuleDescriptor
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.konan.library.impl
import org.jetbrains.kotlin.backend.konan.library.KonanLibraryReader
import org.jetbrains.kotlin.backend.konan.createInteropLibrary
import org.jetbrains.kotlin.backend.konan.serialization.deserializeModule
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.properties.*
@@ -34,7 +35,7 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, val t
private val reader = MetadataReaderImpl(inPlace)
val manifestProperties: Properties by lazy {
override val manifestProperties: Properties by lazy {
inPlace.manifestFile.loadProperties()
}
@@ -69,7 +70,7 @@ class LibraryReaderImpl(var libraryFile: File, val currentAbiVersion: Int, val t
reader.loadSerializedPackageFragment(fqName)
override fun moduleDescriptor(specifics: LanguageVersionSettings)
= deserializeModule(specifics, {packageMetadata(it)}, moduleHeaderData)
= deserializeModule(specifics, {packageMetadata(it)}, moduleHeaderData, createInteropLibrary(this))
}
@@ -473,7 +473,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
if (!useKotlinDispatch) {
val arguments = descriptor.valueParameters.map { expression.getValueArgument(it)!! }
assert(expression.extensionReceiver == null)
assert(expression.dispatchReceiver == null || expression.extensionReceiver == null)
if (expression.superQualifier?.isObjCMetaClass() == true) {
context.reportCompilationError(
@@ -493,7 +493,7 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
return builder.genLoweredObjCMethodCall(
methodInfo,
superQualifier = expression.superQualifierSymbol,
receiver = expression.dispatchReceiver!!,
receiver = expression.dispatchReceiver ?: expression.extensionReceiver!!,
arguments = arguments
)
}
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.InteropLibrary
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.backend.konan.library.LinkData
@@ -75,15 +76,19 @@ object NullFlexibleTypeDeserializer : FlexibleTypeDeserializer {
}
fun createKonanPackageFragmentProvider(
fragmentNames: List<String>,
fragmentNames: List<String>,
packageLoader: (String)->KonanLinkData.PackageFragment,
storageManager: StorageManager, module: ModuleDescriptor,
configuration: DeserializationConfiguration): PackageFragmentProvider {
storageManager: StorageManager, module: ModuleDescriptor,
configuration: DeserializationConfiguration,
interopLibrary: InteropLibrary?): PackageFragmentProvider {
val packageFragments = fragmentNames.map{
KonanPackageFragment(it, packageLoader, storageManager, module)
}
val provider = PackageFragmentProviderImpl(packageFragments)
val syntheticInteropPackageFragments =
interopLibrary?.createSyntheticPackages(module, packageFragments) ?: emptyList()
val provider = PackageFragmentProviderImpl(packageFragments + syntheticInteropPackageFragments)
val notFoundClasses = NotFoundClasses(storageManager, module)
@@ -115,7 +120,8 @@ public fun parseModuleHeader(libraryData: ByteArray): Library =
KonanSerializerProtocol.extensionRegistry)
internal fun deserializeModule(languageVersionSettings: LanguageVersionSettings,
packageLoader:(String)->ByteArray, library: ByteArray): ModuleDescriptorImpl {
packageLoader:(String)->ByteArray, library: ByteArray,
interopLibrary: InteropLibrary?): ModuleDescriptorImpl {
val libraryProto = parseModuleHeader(library)
val moduleName = libraryProto.moduleName
@@ -131,7 +137,7 @@ internal fun deserializeModule(languageVersionSettings: LanguageVersionSettings,
libraryProto.packageFragmentNameList,
{it -> parsePackageFragment(packageLoader(it))},
storageManager,
moduleDescriptor, deserializationConfiguration)
moduleDescriptor, deserializationConfiguration, interopLibrary)
moduleDescriptor.initialize(provider)
+1 -1
View File
@@ -2,7 +2,7 @@ import sysstat.*
import kotlinx.cinterop.*
fun main(args: Array<String>) {
val statBuf = nativeHeap.alloc<statStruct>()
val statBuf = nativeHeap.alloc<stat>()
val res = stat("/", statBuf.ptr)
println(res)
println(statBuf.st_uid)
+4 -1
View File
@@ -7,10 +7,13 @@
@interface Foo : NSObject
@property NSString* name;
-(void)hello;
-(void)helloWithPrinter:(id <Printer>)printer;
@end;
@interface Foo (FooExtensions)
-(void)hello;
@end;
@protocol MutablePair
@required
@property (readonly) int first;
+8 -5
View File
@@ -24,11 +24,6 @@
return self;
}
-(void)hello {
CPrinter* printer = [[CPrinter alloc] init];
[self helloWithPrinter:printer];
}
-(void)helloWithPrinter:(id <Printer>)printer {
NSString* message = [NSString stringWithFormat:@"Hello, %@!", self.name];
[printer print:message.UTF8String];
@@ -40,6 +35,14 @@
@end;
@implementation Foo (FooExtensions)
-(void)hello {
CPrinter* printer = [[CPrinter alloc] init];
[self helloWithPrinter:printer];
}
@end;
void replacePairElements(id <MutablePair> pair, int first, int second) {
[pair update:0 add:(first - pair.first)];
[pair update:1 sub:(pair.second - second)];