Fix more -produce framework bugs (#1152)

* Support array constructors as factory methods when producing framework

 #KT-21802 Fixed

* Improve name mangling for produced framework

* Improve multi-inheritance handling when producing framework

 #KT-21817 Fixed
 #KT-21836 Fixed
This commit is contained in:
SvyatoslavScherbina
2017-12-18 12:29:09 +03:00
committed by Nikolay Igotti
parent c194c89346
commit 61dc49b4b7
5 changed files with 139 additions and 40 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.konan.llvm.objcexport package org.jetbrains.kotlin.backend.konan.llvm.objcexport
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.common.descriptors.allParameters import org.jetbrains.kotlin.backend.common.descriptors.allParameters
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.descriptors.*
@@ -222,6 +223,8 @@ internal class ObjCExportCodeGenerator(
private val impType = pointerType(functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)) private val impType = pointerType(functionType(int8TypePtr, true, int8TypePtr, int8TypePtr))
internal val directMethodAdapters = mutableMapOf<DirectAdapterRequest, ObjCToKotlinMethodAdapter>()
inner class ObjCToKotlinMethodAdapter( inner class ObjCToKotlinMethodAdapter(
selector: String, selector: String,
encoding: String, encoding: String,
@@ -482,6 +485,41 @@ private fun ObjCExportCodeGenerator.generateObjCImp(
return result return result
} }
private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor(
target: ConstructorDescriptor,
methodBridge: MethodBridge
): LLVMValueRef {
// TODO: adapt exceptions.
val result = LLVMAddFunction(context.llvmModule, "", objCFunctionType(methodBridge))!!
generateFunction(codegen, result) {
callFromBridge(context.llvm.initRuntimeIfNeeded, emptyList())
val kotlinValueArgs = methodBridge.paramBridges
.drop(1) // Drop class method receiver.
.mapIndexed { index, typeBridge ->
objCToKotlin(param(index + 2), typeBridge, Lifetime.ARGUMENT)
}
val arrayInstance = callFromBridge(
context.llvm.allocArrayFunction,
listOf(target.constructedClass.llvmTypeInfoPtr, kotlinValueArgs.first()),
resultLifetime = Lifetime.ARGUMENT
)
callFromBridge(
target.llvmFunction,
listOf(arrayInstance) + kotlinValueArgs
)
ret(kotlinToObjC(arrayInstance, ReferenceBridge))
}
LLVMSetLinkage(result, LLVMLinkage.LLVMPrivateLinkage)
return result
}
// TODO: cache bridges. // TODO: cache bridges.
private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge( private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
descriptor: FunctionDescriptor, descriptor: FunctionDescriptor,
@@ -585,19 +623,35 @@ private fun ObjCExportCodeGenerator.createMethodVirtualAdapter(
private fun ObjCExportCodeGenerator.createMethodAdapter( private fun ObjCExportCodeGenerator.createMethodAdapter(
implementation: FunctionDescriptor?, implementation: FunctionDescriptor?,
baseMethod: FunctionDescriptor baseMethod: FunctionDescriptor
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { ) = createMethodAdapter(DirectAdapterRequest(implementation, baseMethod))
val selectorName = namer.getSelector(baseMethod)
val methodBridge = mapper.bridgeMethod(baseMethod)
val objCEncoding = getEncoding(methodBridge)
val objCToKotlin = constPointer(generateObjCImp(implementation, methodBridge))
return ObjCToKotlinMethodAdapter(selectorName, objCEncoding, objCToKotlin) private fun ObjCExportCodeGenerator.createMethodAdapter(
request: DirectAdapterRequest
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = this.directMethodAdapters.getOrPut(request) {
val selectorName = namer.getSelector(request.base)
val methodBridge = mapper.bridgeMethod(request.base)
val objCEncoding = getEncoding(methodBridge)
val objCToKotlin = constPointer(generateObjCImp(request.implementation, methodBridge))
ObjCToKotlinMethodAdapter(selectorName, objCEncoding, objCToKotlin)
} }
private fun ObjCExportCodeGenerator.createConstructorAdapter( private fun ObjCExportCodeGenerator.createConstructorAdapter(
descriptor: ConstructorDescriptor descriptor: ConstructorDescriptor
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = createMethodAdapter(descriptor, descriptor) ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = createMethodAdapter(descriptor, descriptor)
private fun ObjCExportCodeGenerator.createArrayConstructorAdapter(
descriptor: ConstructorDescriptor
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val selectorName = namer.getSelector(descriptor)
val methodBridge = mapper.bridgeMethod(descriptor)
val objCEncoding = getEncoding(methodBridge)
val objCToKotlin = constPointer(generateObjCImpForArrayConstructor(descriptor, methodBridge))
return ObjCToKotlinMethodAdapter(selectorName, objCEncoding, objCToKotlin)
}
private fun ObjCExportCodeGenerator.vtableIndex(descriptor: FunctionDescriptor): Int? { private fun ObjCExportCodeGenerator.vtableIndex(descriptor: FunctionDescriptor): Int? {
assert(descriptor.isOverridable) assert(descriptor.isOverridable)
val classDescriptor = descriptor.containingDeclaration as ClassDescriptor val classDescriptor = descriptor.containingDeclaration as ClassDescriptor
@@ -634,10 +688,17 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
descriptor: ClassDescriptor descriptor: ClassDescriptor
): ObjCExportCodeGenerator.ObjCTypeAdapter { ): ObjCExportCodeGenerator.ObjCTypeAdapter {
val adapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>() val adapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
val classAdapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
if (descriptor != context.builtIns.any) { if (descriptor != context.builtIns.any) {
descriptor.constructors.forEach { descriptor.constructors.forEach {
if (mapper.shouldBeExposed(it)) adapters += createConstructorAdapter(it) if (mapper.shouldBeExposed(it)) {
if (it.constructedClass.isArray) {
classAdapters += createArrayConstructorAdapter(it)
} else {
adapters += createConstructorAdapter(it)
}
}
} }
} }
@@ -645,15 +706,8 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
val exposedMethods = descriptor.contributedMethods.filter { mapper.shouldBeExposed(it) } + categoryMethods val exposedMethods = descriptor.contributedMethods.filter { mapper.shouldBeExposed(it) } + categoryMethods
exposedMethods.filter { it.kind.isReal }.forEach { method -> exposedMethods.forEach {
mapper.getBaseMethods(method).mapTo(adapters) { base -> adapters += createDirectAdapters(it)
val implementation = if (method.modality == Modality.ABSTRACT) {
null
} else {
OverriddenFunctionDescriptor(method, base).getImplementation(context)
}
createMethodAdapter(implementation, base)
}
} }
val reverseAdapters = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>() val reverseAdapters = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>()
@@ -692,7 +746,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
} else { } else {
// Mark it as non-overridable: // Mark it as non-overridable:
baseMethods.forEach { base -> baseMethods.distinctBy { namer.getSelector(it) }.forEach { base ->
reverseAdapters += KotlinToObjCMethodAdapter( reverseAdapters += KotlinToObjCMethodAdapter(
namer.getSelector(base), namer.getSelector(base),
-1, -1,
@@ -732,8 +786,6 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
emptyList() emptyList()
} }
val classAdapters = mutableListOf<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter>()
if (descriptor.isUnit()) { if (descriptor.isUnit()) {
classAdapters += createUnitInstanceAdapter() classAdapters += createUnitInstanceAdapter()
} }
@@ -752,6 +804,34 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
) )
} }
internal data class DirectAdapterRequest(val implementation: FunctionDescriptor?, val base: FunctionDescriptor)
private fun ObjCExportCodeGenerator.createDirectAdapters(
method: FunctionDescriptor
): List<ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter> {
fun FunctionDescriptor.getAllRequiredDirectAdapters() = mapper.getBaseMethods(this).map { base ->
val implementation = if (this.modality == Modality.ABSTRACT) {
null
} else {
OverriddenFunctionDescriptor(this, base).getImplementation(context)
}
DirectAdapterRequest(implementation, base)
}
val superClassMethod = method.overriddenDescriptors
.atMostOne { !(it.containingDeclaration as ClassDescriptor).isInterface }
val inheritedAdapters = superClassMethod?.getAllRequiredDirectAdapters().orEmpty()
val requiredAdapters = method.getAllRequiredDirectAdapters()
return (requiredAdapters - inheritedAdapters)
.distinctBy { namer.getSelector(it.base) }
.map {
createMethodAdapter(it)
}
}
private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { private fun ObjCExportCodeGenerator.createUnitInstanceAdapter(): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val selector = "unit" val selector = "unit"
val methodBridge = MethodBridge(ReferenceBridge, listOf(ReferenceBridge, ReferenceBridge)) val methodBridge = MethodBridge(ReferenceBridge, listOf(ReferenceBridge, ReferenceBridge))
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.KonanCompilationException import org.jetbrains.kotlin.backend.konan.KonanCompilationException
import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments import org.jetbrains.kotlin.backend.konan.descriptors.getPackageFragments
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.backend.konan.descriptors.isUnit import org.jetbrains.kotlin.backend.konan.descriptors.isUnit
import org.jetbrains.kotlin.backend.konan.reportCompilationError import org.jetbrains.kotlin.backend.konan.reportCompilationError
@@ -210,12 +211,18 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
val presentConstructors = mutableSetOf<String>() val presentConstructors = mutableSetOf<String>()
descriptor.constructors.filter { mapper.shouldBeExposed(it) }.forEach { descriptor.constructors.filter { mapper.shouldBeExposed(it) }.forEach {
presentConstructors += getSelector(it) if (!descriptor.isArray) presentConstructors += getSelector(it)
+"${getSignature(it, it)};" +"${getSignature(it, it)};"
+"" +""
} }
if (descriptor.isArray) {
+"+(instancetype)alloc __attribute__((unavailable));"
+"+(instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable));"
+""
}
// Hide "unimplemented" super constructors: // Hide "unimplemented" super constructors:
superClass?.constructors?.filter { mapper.shouldBeExposed(it) }?.forEach { superClass?.constructors?.filter { mapper.shouldBeExposed(it) }?.forEach {
if (getSelector(it) !in presentConstructors) { if (getSelector(it) !in presentConstructors) {
@@ -320,8 +327,7 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
val propertySetter = property.setter val propertySetter = property.setter
if (propertySetter != null && mapper.shouldBeExposed(propertySetter)) { if (propertySetter != null && mapper.shouldBeExposed(propertySetter)) {
val baseSetter = mapper.getBaseMethods(propertySetter).single() val setterSelector = mapper.getBaseMethods(propertySetter).map { namer.getSelector(it) }.distinct().single()
val setterSelector = getSelector(baseSetter)
if (setterSelector != "set" + name.capitalize() + ":") { if (setterSelector != "set" + name.capitalize() + ":") {
attributes += "setter=$setterSelector" attributes += "setter=$setterSelector"
} }
@@ -342,7 +348,8 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
val selectorParts = getSelector(baseMethod).split(':') val selectorParts = getSelector(baseMethod).split(':')
if (methodBridge.isKotlinTopLevel) { val isArrayConstructor = method is ConstructorDescriptor && method.constructedClass.isArray
if (methodBridge.isKotlinTopLevel || isArrayConstructor) {
append("+") append("+")
} else { } else {
append("-") append("-")
@@ -392,7 +399,7 @@ internal class ObjCExportHeaderGenerator(val context: Context) {
append(" NS_SWIFT_NAME($swiftName)") append(" NS_SWIFT_NAME($swiftName)")
if (method is ConstructorDescriptor) { if (method is ConstructorDescriptor && !isArrayConstructor) {
append(" NS_DESIGNATED_INITIALIZER") append(" NS_DESIGNATED_INITIALIZER")
} }
} }
@@ -16,10 +16,11 @@
package org.jetbrains.kotlin.backend.konan.objcexport package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.common.descriptors.allParameters import org.jetbrains.kotlin.backend.common.descriptors.explicitParameters
import org.jetbrains.kotlin.backend.common.descriptors.isSuspend import org.jetbrains.kotlin.backend.common.descriptors.isSuspend
import org.jetbrains.kotlin.backend.konan.ValueType import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.correspondingValueType import org.jetbrains.kotlin.backend.konan.correspondingValueType
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.isObjCObjectType import org.jetbrains.kotlin.backend.konan.isObjCObjectType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
@@ -67,7 +68,9 @@ internal fun ObjCExportMapper.getBaseMethods(descriptor: FunctionDescriptor): Li
if (isBaseMethod(descriptor)) { if (isBaseMethod(descriptor)) {
listOf(descriptor) listOf(descriptor)
} else { } else {
descriptor.overriddenDescriptors.filter { shouldBeExposed(it) }.flatMap { getBaseMethods(it.original)} descriptor.overriddenDescriptors.filter { shouldBeExposed(it) }
.flatMap { getBaseMethods(it.original)}
.distinct()
} }
internal fun ObjCExportMapper.isBaseProperty(descriptor: PropertyDescriptor) = internal fun ObjCExportMapper.isBaseProperty(descriptor: PropertyDescriptor) =
@@ -77,7 +80,9 @@ internal fun ObjCExportMapper.getBaseProperties(descriptor: PropertyDescriptor):
if (isBaseProperty(descriptor)) { if (isBaseProperty(descriptor)) {
listOf(descriptor) listOf(descriptor)
} else { } else {
descriptor.overriddenDescriptors.flatMap { getBaseProperties(it.original) } descriptor.overriddenDescriptors
.flatMap { getBaseProperties(it.original) }
.distinct()
} }
internal tailrec fun KotlinType.getErasedTypeClass(): ClassDescriptor = internal tailrec fun KotlinType.getErasedTypeClass(): ClassDescriptor =
@@ -151,12 +156,22 @@ internal fun ObjCExportMapper.bridgeMethod(descriptor: FunctionDescriptor): Meth
val paramBridges = mutableListOf<TypeBridge>() val paramBridges = mutableListOf<TypeBridge>()
if (descriptor is ConstructorDescriptor) {
if (descriptor.constructedClass.isArray) {
// Generated as class factory method.
paramBridges += ReferenceBridge // Receiver of class method.
} else {
// Generated as Objective-C instance init method.
paramBridges += ReferenceBridge // Receiver of init method.
}
}
val isTopLevel = isTopLevel(descriptor) val isTopLevel = isTopLevel(descriptor)
if (isTopLevel) { if (isTopLevel) {
paramBridges += ReferenceBridge paramBridges += ReferenceBridge
} }
descriptor.allParameters.mapTo(paramBridges) { bridgeType(it.type) } descriptor.explicitParameters.mapTo(paramBridges) { bridgeType(it.type) }
return MethodBridge(returnBridge, paramBridges, isKotlinTopLevel = isTopLevel) return MethodBridge(returnBridge, paramBridges, isKotlinTopLevel = isTopLevel)
} }
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.konan.objcexport package org.jetbrains.kotlin.backend.konan.objcexport
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.isArray
import org.jetbrains.kotlin.backend.konan.descriptors.isInterface import org.jetbrains.kotlin.backend.konan.descriptors.isInterface
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
@@ -94,7 +95,7 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
val parameters = mapper.objCValueParameters(method) val parameters = mapper.objCValueParameters(method)
StringBuilder().apply { StringBuilder().apply {
append(method.mangledName) append(method.getMangledName(forSwift = false))
parameters.forEachIndexed { index, it -> parameters.forEachIndexed { index, it ->
val name = when { val name = when {
@@ -132,7 +133,7 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
val parameters = mapper.objCValueParameters(method) val parameters = mapper.objCValueParameters(method)
StringBuilder().apply { StringBuilder().apply {
append(method.mangledName) append(method.getMangledName(forSwift = true))
append("(") append("(")
parameters.forEach { parameters.forEach {
@@ -150,13 +151,9 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
append(")") append(")")
}.mangledSequence { }.mangledSequence {
if (parameters.isNotEmpty()) { // "foo(label:)" -> "foo(label_:)"
// "foo(label:)" -> "foo(label_:)" // "foo()" -> "foo_()"
insert(lastIndex - 1, '_') insert(lastIndex - 1, '_')
} else {
// "foo()" -> "foo_()"
insert(lastIndex - 2, '_')
}
} }
} }
@@ -196,9 +193,9 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
methodSwiftNames.forceAssign(equals, "isEqual(:)") methodSwiftNames.forceAssign(equals, "isEqual(:)")
} }
private val FunctionDescriptor.mangledName: String get() { private fun FunctionDescriptor.getMangledName(forSwift: Boolean): String {
if (this is ConstructorDescriptor) { if (this is ConstructorDescriptor) {
return "init" return if (this.constructedClass.isArray && !forSwift) "array" else "init"
} }
val candidate = when (this) { val candidate = when (this) {
+1 -1
View File
@@ -152,7 +152,7 @@ static void initializeClass(Class clazz);
if (typeInfo->instanceSize_ < 0) { if (typeInfo->instanceSize_ < 0) {
[NSException raise:NSGenericException [NSException raise:NSGenericException
format:@"Allocating %s is not supported yet", format:@"%s must be allocated and initialized with a factory method",
class_getName(object_getClass(self))]; class_getName(object_getClass(self))];
} }