Rework Objective-C header generation (#1618)
Provide simple tree representation for generated header to be used in IDE for cross-resolve. Also do some cleanup.
This commit is contained in:
committed by
SvyatoslavScherbina
parent
40a58a9a61
commit
9b9f0412ce
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.builtins.getValueParameterTypesFromFunctionType
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
|
||||||
|
internal interface CustomTypeMapper {
|
||||||
|
val mappedClassDescriptor: ClassDescriptor
|
||||||
|
fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType
|
||||||
|
|
||||||
|
class Simple(
|
||||||
|
override val mappedClassDescriptor: ClassDescriptor,
|
||||||
|
private val objCClassName: String
|
||||||
|
) : CustomTypeMapper {
|
||||||
|
|
||||||
|
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType =
|
||||||
|
ObjCClassType(objCClassName)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Collection(
|
||||||
|
private val generator: ObjCExportHeaderGenerator,
|
||||||
|
override val mappedClassDescriptor: ClassDescriptor,
|
||||||
|
private val objCClassName: String
|
||||||
|
) : CustomTypeMapper {
|
||||||
|
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType {
|
||||||
|
val typeArguments = mappedSuperType.arguments.map {
|
||||||
|
val argument = it.type
|
||||||
|
if (TypeUtils.isNullableType(argument)) {
|
||||||
|
// Kotlin `null` keys and values are represented as `NSNull` singleton.
|
||||||
|
ObjCIdType
|
||||||
|
} else {
|
||||||
|
generator.mapReferenceTypeIgnoringNullability(argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObjCClassType(objCClassName, typeArguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Function(
|
||||||
|
private val generator: ObjCExportHeaderGenerator,
|
||||||
|
parameterCount: Int
|
||||||
|
) : CustomTypeMapper {
|
||||||
|
override val mappedClassDescriptor = generator.builtIns.getFunction(parameterCount)
|
||||||
|
|
||||||
|
override fun mapType(mappedSuperType: KotlinType): ObjCNonNullReferenceType {
|
||||||
|
val functionType = mappedSuperType
|
||||||
|
|
||||||
|
val returnType = functionType.getReturnTypeFromFunctionType()
|
||||||
|
val parameterTypes = listOfNotNull(functionType.getReceiverTypeFromFunctionType()) +
|
||||||
|
functionType.getValueParameterTypesFromFunctionType().map { it.type }
|
||||||
|
|
||||||
|
return ObjCBlockPointerType(
|
||||||
|
generator.mapReferenceType(returnType),
|
||||||
|
parameterTypes.map { generator.mapReferenceType(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.descriptors.allParameters
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||||
|
|
||||||
|
internal sealed class TypeBridge
|
||||||
|
internal object ReferenceBridge : TypeBridge()
|
||||||
|
internal data class ValueTypeBridge(val objCValueType: ObjCValueType) : TypeBridge()
|
||||||
|
|
||||||
|
internal sealed class MethodBridgeParameter
|
||||||
|
|
||||||
|
internal sealed class MethodBridgeReceiver : MethodBridgeParameter() {
|
||||||
|
object Static : MethodBridgeReceiver()
|
||||||
|
object Factory : MethodBridgeReceiver()
|
||||||
|
object Instance : MethodBridgeReceiver()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object MethodBridgeSelector : MethodBridgeParameter()
|
||||||
|
|
||||||
|
internal sealed class MethodBridgeValueParameter : MethodBridgeParameter() {
|
||||||
|
data class Mapped(val bridge: TypeBridge) : MethodBridgeValueParameter()
|
||||||
|
object ErrorOutParameter : MethodBridgeValueParameter()
|
||||||
|
data class KotlinResultOutParameter(val bridge: TypeBridge) : MethodBridgeValueParameter()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal data class MethodBridge(
|
||||||
|
val returnBridge: ReturnValue,
|
||||||
|
val receiver: MethodBridgeReceiver,
|
||||||
|
val valueParameters: List<MethodBridgeValueParameter>
|
||||||
|
) {
|
||||||
|
|
||||||
|
sealed class ReturnValue {
|
||||||
|
object Void : ReturnValue()
|
||||||
|
object HashCode : ReturnValue()
|
||||||
|
data class Mapped(val bridge: TypeBridge) : ReturnValue()
|
||||||
|
sealed class Instance : ReturnValue() {
|
||||||
|
object InitResult : Instance()
|
||||||
|
object FactoryResult : Instance()
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class WithError : ReturnValue() {
|
||||||
|
object Success : WithError()
|
||||||
|
data class RefOrNull(val successBridge: ReturnValue) : WithError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val paramBridges: List<MethodBridgeParameter> =
|
||||||
|
listOf(receiver) + MethodBridgeSelector + valueParameters
|
||||||
|
|
||||||
|
// TODO: it is not exactly true in potential future cases.
|
||||||
|
val isInstance: Boolean get() = when (receiver) {
|
||||||
|
MethodBridgeReceiver.Static,
|
||||||
|
MethodBridgeReceiver.Factory -> false
|
||||||
|
|
||||||
|
MethodBridgeReceiver.Instance -> true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun MethodBridge.valueParametersAssociated(
|
||||||
|
descriptor: FunctionDescriptor
|
||||||
|
): List<Pair<MethodBridgeValueParameter, ParameterDescriptor?>> {
|
||||||
|
val kotlinParameters = descriptor.allParameters.iterator()
|
||||||
|
val skipFirstKotlinParameter = when (this.receiver) {
|
||||||
|
MethodBridgeReceiver.Static -> false
|
||||||
|
MethodBridgeReceiver.Factory, MethodBridgeReceiver.Instance -> true
|
||||||
|
}
|
||||||
|
if (skipFirstKotlinParameter) {
|
||||||
|
kotlinParameters.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.valueParameters.map {
|
||||||
|
when (it) {
|
||||||
|
is MethodBridgeValueParameter.Mapped -> it to kotlinParameters.next()
|
||||||
|
|
||||||
|
is MethodBridgeValueParameter.ErrorOutParameter,
|
||||||
|
is MethodBridgeValueParameter.KotlinResultOutParameter -> it to null
|
||||||
|
}
|
||||||
|
}.also { assert(!kotlinParameters.hasNext()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun MethodBridge.parametersAssociated(
|
||||||
|
descriptor: FunctionDescriptor
|
||||||
|
): List<Pair<MethodBridgeParameter, ParameterDescriptor?>> {
|
||||||
|
val kotlinParameters = descriptor.allParameters.iterator()
|
||||||
|
|
||||||
|
return this.paramBridges.map {
|
||||||
|
when (it) {
|
||||||
|
is MethodBridgeValueParameter.Mapped, MethodBridgeReceiver.Instance ->
|
||||||
|
it to kotlinParameters.next()
|
||||||
|
|
||||||
|
MethodBridgeReceiver.Static, MethodBridgeSelector, MethodBridgeValueParameter.ErrorOutParameter,
|
||||||
|
is MethodBridgeValueParameter.KotlinResultOutParameter ->
|
||||||
|
it to null
|
||||||
|
|
||||||
|
MethodBridgeReceiver.Factory -> {
|
||||||
|
kotlinParameters.next()
|
||||||
|
it to null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.also { assert(!kotlinParameters.hasNext()) }
|
||||||
|
}
|
||||||
+2
-2
@@ -46,7 +46,7 @@ internal class ObjCExport(val codegen: CodeGenerator) {
|
|||||||
val topLevelDeclarations: Map<FqName, List<CallableMemberDescriptor>>
|
val topLevelDeclarations: Map<FqName, List<CallableMemberDescriptor>>
|
||||||
|
|
||||||
if (context.config.produce == CompilerOutputKind.FRAMEWORK) {
|
if (context.config.produce == CompilerOutputKind.FRAMEWORK) {
|
||||||
val headerGenerator = ObjCExportHeaderGenerator(context)
|
val headerGenerator = ObjCExportHeaderGeneratorImpl(context)
|
||||||
produceFrameworkSpecific(headerGenerator)
|
produceFrameworkSpecific(headerGenerator)
|
||||||
|
|
||||||
generatedClasses = headerGenerator.generatedClasses
|
generatedClasses = headerGenerator.generatedClasses
|
||||||
@@ -63,7 +63,7 @@ internal class ObjCExport(val codegen: CodeGenerator) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val namer = ObjCExportNamer(context, mapper)
|
val namer = ObjCExportNamer(context.moduleDescriptor, context.builtIns, mapper)
|
||||||
objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper)
|
objCCodeGenerator = ObjCExportCodeGenerator(codegen, namer, mapper)
|
||||||
|
|
||||||
generatedClasses = emptySet()
|
generatedClasses = emptySet()
|
||||||
|
|||||||
+386
-515
File diff suppressed because it is too large
Load Diff
+22
@@ -0,0 +1,22 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.reportCompilationWarning
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.ir.util.report
|
||||||
|
|
||||||
|
internal class ObjCExportHeaderGeneratorImpl(val context: Context)
|
||||||
|
: ObjCExportHeaderGenerator(context.moduleDescriptor, context.builtIns) {
|
||||||
|
|
||||||
|
override fun reportWarning(text: String) {
|
||||||
|
context.reportCompilationWarning(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun reportWarning(method: FunctionDescriptor, text: String) {
|
||||||
|
context.report(
|
||||||
|
context.ir.get(method),
|
||||||
|
text,
|
||||||
|
isError = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-127
@@ -36,7 +36,7 @@ internal abstract class ObjCExportMapper {
|
|||||||
|
|
||||||
private val methodBridgeCache = mutableMapOf<FunctionDescriptor, MethodBridge>()
|
private val methodBridgeCache = mutableMapOf<FunctionDescriptor, MethodBridge>()
|
||||||
|
|
||||||
fun bridgeMethod(descriptor: FunctionDescriptor) = methodBridgeCache.getOrPut(descriptor) {
|
fun bridgeMethod(descriptor: FunctionDescriptor): MethodBridge = methodBridgeCache.getOrPut(descriptor) {
|
||||||
bridgeMethodImpl(descriptor)
|
bridgeMethodImpl(descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,105 +110,6 @@ internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = m
|
|||||||
it.overriddenDescriptors.isEmpty() && it.annotations.hasAnnotation(KonanBuiltIns.FqNames.throws)
|
it.overriddenDescriptors.isEmpty() && it.annotations.hasAnnotation(KonanBuiltIns.FqNames.throws)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: generalize type bridges to support such things as selectors, ignored class method receivers etc.
|
|
||||||
|
|
||||||
internal sealed class TypeBridge
|
|
||||||
internal object ReferenceBridge : TypeBridge()
|
|
||||||
internal data class ValueTypeBridge(val objCValueType: ObjCValueType) : TypeBridge()
|
|
||||||
|
|
||||||
internal sealed class MethodBridgeParameter
|
|
||||||
|
|
||||||
internal sealed class MethodBridgeReceiver : MethodBridgeParameter() {
|
|
||||||
object Static : MethodBridgeReceiver()
|
|
||||||
object Factory : MethodBridgeReceiver()
|
|
||||||
object Instance : MethodBridgeReceiver()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object MethodBridgeSelector : MethodBridgeParameter()
|
|
||||||
|
|
||||||
internal sealed class MethodBridgeValueParameter : MethodBridgeParameter() {
|
|
||||||
data class Mapped(val bridge: TypeBridge) : MethodBridgeValueParameter()
|
|
||||||
object ErrorOutParameter : MethodBridgeValueParameter()
|
|
||||||
data class KotlinResultOutParameter(val bridge: TypeBridge) : MethodBridgeValueParameter()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal data class MethodBridge(
|
|
||||||
val returnBridge: ReturnValue,
|
|
||||||
val receiver: MethodBridgeReceiver,
|
|
||||||
val valueParameters: List<MethodBridgeValueParameter>
|
|
||||||
) {
|
|
||||||
|
|
||||||
sealed class ReturnValue {
|
|
||||||
object Void : ReturnValue()
|
|
||||||
object HashCode : ReturnValue()
|
|
||||||
data class Mapped(val bridge: TypeBridge) : ReturnValue()
|
|
||||||
sealed class Instance : ReturnValue() {
|
|
||||||
object InitResult : Instance()
|
|
||||||
object FactoryResult : Instance()
|
|
||||||
}
|
|
||||||
|
|
||||||
sealed class WithError : ReturnValue() {
|
|
||||||
object Success : WithError()
|
|
||||||
data class RefOrNull(val successBridge: ReturnValue) : WithError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val paramBridges: List<MethodBridgeParameter> =
|
|
||||||
listOf(receiver) + MethodBridgeSelector + valueParameters
|
|
||||||
|
|
||||||
// TODO: it is not exactly true in potential future cases.
|
|
||||||
val isInstance: Boolean get() = when (receiver) {
|
|
||||||
MethodBridgeReceiver.Static,
|
|
||||||
MethodBridgeReceiver.Factory -> false
|
|
||||||
|
|
||||||
MethodBridgeReceiver.Instance -> true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun MethodBridge.valueParametersAssociated(
|
|
||||||
descriptor: FunctionDescriptor
|
|
||||||
): List<Pair<MethodBridgeValueParameter, ParameterDescriptor?>> {
|
|
||||||
val kotlinParameters = descriptor.allParameters.iterator()
|
|
||||||
val skipFirstKotlinParameter = when (this.receiver) {
|
|
||||||
MethodBridgeReceiver.Static -> false
|
|
||||||
MethodBridgeReceiver.Factory, MethodBridgeReceiver.Instance -> true
|
|
||||||
}
|
|
||||||
if (skipFirstKotlinParameter) {
|
|
||||||
kotlinParameters.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.valueParameters.map {
|
|
||||||
when (it) {
|
|
||||||
is MethodBridgeValueParameter.Mapped -> it to kotlinParameters.next()
|
|
||||||
|
|
||||||
is MethodBridgeValueParameter.ErrorOutParameter,
|
|
||||||
is MethodBridgeValueParameter.KotlinResultOutParameter -> it to null
|
|
||||||
}
|
|
||||||
}.also { assert(!kotlinParameters.hasNext()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun MethodBridge.parametersAssociated(
|
|
||||||
descriptor: FunctionDescriptor
|
|
||||||
): List<Pair<MethodBridgeParameter, ParameterDescriptor?>> {
|
|
||||||
val kotlinParameters = descriptor.allParameters.iterator()
|
|
||||||
|
|
||||||
return this.paramBridges.map {
|
|
||||||
when (it) {
|
|
||||||
is MethodBridgeValueParameter.Mapped, MethodBridgeReceiver.Instance ->
|
|
||||||
it to kotlinParameters.next()
|
|
||||||
|
|
||||||
MethodBridgeReceiver.Static, MethodBridgeSelector, MethodBridgeValueParameter.ErrorOutParameter,
|
|
||||||
is MethodBridgeValueParameter.KotlinResultOutParameter ->
|
|
||||||
it to null
|
|
||||||
|
|
||||||
MethodBridgeReceiver.Factory -> {
|
|
||||||
kotlinParameters.next()
|
|
||||||
it to null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.also { assert(!kotlinParameters.hasNext()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge {
|
private fun ObjCExportMapper.bridgeType(kotlinType: KotlinType): TypeBridge {
|
||||||
val valueType = kotlinType.correspondingValueType
|
val valueType = kotlinType.correspondingValueType
|
||||||
?: return ReferenceBridge
|
?: return ReferenceBridge
|
||||||
@@ -305,30 +206,4 @@ internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor)
|
|||||||
assert(isBaseProperty(descriptor))
|
assert(isBaseProperty(descriptor))
|
||||||
|
|
||||||
return bridgeType(descriptor.type)
|
return bridgeType(descriptor.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum class ObjCValueType(
|
|
||||||
val kotlinValueType: ValueType, // It is here for simplicity.
|
|
||||||
val encoding: String
|
|
||||||
) {
|
|
||||||
|
|
||||||
BOOL(ValueType.BOOLEAN, "c"),
|
|
||||||
CHAR(ValueType.BYTE, "c"),
|
|
||||||
UNSIGNED_SHORT(ValueType.CHAR, "S"),
|
|
||||||
SHORT(ValueType.SHORT, "s"),
|
|
||||||
INT(ValueType.INT, "i"),
|
|
||||||
LONG_LONG(ValueType.LONG, "q"),
|
|
||||||
FLOAT(ValueType.FLOAT, "f"),
|
|
||||||
DOUBLE(ValueType.DOUBLE, "d")
|
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
// UNSIGNED_SHORT -> unsignedShort
|
|
||||||
val nsNumberName = this.name.split('_').mapIndexed { index, s ->
|
|
||||||
val lower = s.toLowerCase()
|
|
||||||
if (index > 0) lower.capitalize() else lower
|
|
||||||
}.joinToString("")
|
|
||||||
|
|
||||||
val nsNumberValueSelector get() = "${nsNumberName}Value"
|
|
||||||
val nsNumberFactorySelector get() = "numberWith${nsNumberName.capitalize()}:"
|
|
||||||
}
|
|
||||||
+12
-9
@@ -16,9 +16,9 @@
|
|||||||
|
|
||||||
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.descriptors.isArray
|
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.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
@@ -28,11 +28,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
|||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
|
||||||
|
|
||||||
internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMapper) {
|
internal class ObjCExportNamer(val moduleDescriptor: ModuleDescriptor,
|
||||||
|
val builtIns: KotlinBuiltIns,
|
||||||
|
val mapper: ObjCExportMapper,
|
||||||
|
private val topLevelNamePrefix: String = moduleDescriptor.namePrefix
|
||||||
|
) {
|
||||||
val kotlinAnyName = "KotlinBase"
|
val kotlinAnyName = "KotlinBase"
|
||||||
|
|
||||||
private val commonPackageSegments = context.moduleDescriptor.guessMainPackage().pathSegments()
|
private val commonPackageSegments = moduleDescriptor.guessMainPackage().pathSegments()
|
||||||
private val topLevelNamePrefix = context.moduleDescriptor.namePrefix
|
|
||||||
|
|
||||||
val mutableSetName = "${topLevelNamePrefix}MutableSet"
|
val mutableSetName = "${topLevelNamePrefix}MutableSet"
|
||||||
val mutableMapName = "${topLevelNamePrefix}MutableDictionary"
|
val mutableMapName = "${topLevelNamePrefix}MutableDictionary"
|
||||||
@@ -116,7 +119,7 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
|
|||||||
StringBuilder().apply {
|
StringBuilder().apply {
|
||||||
append(topLevelNamePrefix)
|
append(topLevelNamePrefix)
|
||||||
|
|
||||||
if (descriptor.module != context.moduleDescriptor) {
|
if (descriptor.module != moduleDescriptor) {
|
||||||
append(descriptor.module.namePrefix)
|
append(descriptor.module.namePrefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,11 +249,11 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val any = context.builtIns.any
|
val any = builtIns.any
|
||||||
|
|
||||||
classNames.forceAssign(any, kotlinAnyName)
|
classNames.forceAssign(any, kotlinAnyName)
|
||||||
classNames.forceAssign(context.builtIns.mutableSet, mutableSetName)
|
classNames.forceAssign(builtIns.mutableSet, mutableSetName)
|
||||||
classNames.forceAssign(context.builtIns.mutableMap, mutableMapName)
|
classNames.forceAssign(builtIns.mutableMap, mutableMapName)
|
||||||
|
|
||||||
fun ClassDescriptor.method(name: String) =
|
fun ClassDescriptor.method(name: String) =
|
||||||
this.unsubstitutedMemberScope.getContributedFunctions(
|
this.unsubstitutedMemberScope.getContributedFunctions(
|
||||||
@@ -439,7 +442,7 @@ private fun ObjCExportMapper.canHaveSameName(first: PropertyDescriptor, second:
|
|||||||
return bridgePropertyType(first) == bridgePropertyType(second)
|
return bridgePropertyType(first) == bridgePropertyType(second)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val ModuleDescriptor.namePrefix: String get() {
|
internal val ModuleDescriptor.namePrefix: String get() {
|
||||||
// <fooBar> -> FooBar
|
// <fooBar> -> FooBar
|
||||||
val moduleName = this.name.asString().let { it.substring(1, it.lastIndex) }.capitalize()
|
val moduleName = this.name.asString().let { it.substring(1, it.lastIndex) }.capitalize()
|
||||||
|
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
internal class StubBuilder {
|
||||||
|
private val children = mutableListOf<Stub<*>>()
|
||||||
|
|
||||||
|
operator fun Stub<*>.unaryPlus() {
|
||||||
|
children.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun plusAssign(set: Collection<Stub<*>>) {
|
||||||
|
children += set
|
||||||
|
}
|
||||||
|
|
||||||
|
fun build() = children
|
||||||
|
}
|
||||||
|
|
||||||
|
internal inline fun buildMembers(block: StubBuilder.() -> Unit): List<Stub<*>> = StubBuilder().let {
|
||||||
|
it.block()
|
||||||
|
it.build()
|
||||||
|
}
|
||||||
+179
@@ -0,0 +1,179 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
|
||||||
|
object StubRenderer {
|
||||||
|
fun render(stub: Stub<*>): List<String> = collect {
|
||||||
|
stub.run {
|
||||||
|
when (this) {
|
||||||
|
is ObjCProtocol -> {
|
||||||
|
+renderProtocolHeader()
|
||||||
|
+"@required"
|
||||||
|
renderMembers(this)
|
||||||
|
+"@end;"
|
||||||
|
}
|
||||||
|
is ObjCInterface -> {
|
||||||
|
attributes.forEach {
|
||||||
|
+renderAttribute(it)
|
||||||
|
}
|
||||||
|
+renderInterfaceHeader()
|
||||||
|
renderMembers(this)
|
||||||
|
+"@end;"
|
||||||
|
}
|
||||||
|
is ObjCMethod -> {
|
||||||
|
+renderMethod(this)
|
||||||
|
}
|
||||||
|
is ObjCProperty -> {
|
||||||
|
+renderProperty(this)
|
||||||
|
}
|
||||||
|
else -> throw IllegalArgumentException("unsupported stub: " + stub::class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderProperty(property: ObjCProperty): String = buildString {
|
||||||
|
fun StringBuilder.appendTypeAndName() {
|
||||||
|
append(' ')
|
||||||
|
append(property.type.render(property.name))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ObjCProperty.getAllAttributes(): List<String> {
|
||||||
|
if (getterName == null && setterName == null) return attributes
|
||||||
|
|
||||||
|
val allAttributes = attributes.toMutableList()
|
||||||
|
getterName?.let { allAttributes += "getter=$it" }
|
||||||
|
setterName?.let { allAttributes += "setter=$it" }
|
||||||
|
return allAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
fun StringBuilder.appendAttributes() {
|
||||||
|
val attributes = property.getAllAttributes()
|
||||||
|
if (attributes.isNotEmpty()) {
|
||||||
|
append(' ')
|
||||||
|
attributes.joinTo(this, prefix = "(", postfix = ")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append("@property")
|
||||||
|
appendAttributes()
|
||||||
|
appendTypeAndName()
|
||||||
|
append(';')
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderMethod(method: ObjCMethod): String = buildString {
|
||||||
|
fun appendStaticness() {
|
||||||
|
if (method.isInstanceMethod) {
|
||||||
|
append('-')
|
||||||
|
} else {
|
||||||
|
append('+')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendReturnType() {
|
||||||
|
append(" (")
|
||||||
|
append(method.returnType.render())
|
||||||
|
append(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendParameters() {
|
||||||
|
assert(method.selectors.size == method.parameters.size ||
|
||||||
|
method.selectors.size == 1 && method.parameters.size == 0)
|
||||||
|
|
||||||
|
if (method.selectors.size == 1 && method.parameters.size == 0) {
|
||||||
|
append(method.selectors[0])
|
||||||
|
} else {
|
||||||
|
for (i in 0 until method.selectors.size) {
|
||||||
|
if (i > 0) append(' ')
|
||||||
|
|
||||||
|
val parameter = method.parameters[i]
|
||||||
|
val selector = method.selectors[i]
|
||||||
|
append(selector)
|
||||||
|
append("(")
|
||||||
|
append(parameter.type.render())
|
||||||
|
append(")")
|
||||||
|
append(parameter.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendAttributes() {
|
||||||
|
if (method.attributes.isNotEmpty()) append(' ')
|
||||||
|
method.attributes.joinTo(this, separator = " ", transform = ::renderAttribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
appendStaticness()
|
||||||
|
appendReturnType()
|
||||||
|
appendParameters()
|
||||||
|
appendAttributes()
|
||||||
|
append(';')
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjCProtocol.renderProtocolHeader() = buildString {
|
||||||
|
append("@protocol ")
|
||||||
|
append(name)
|
||||||
|
appendSuperProtocols(this@renderProtocolHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun StringBuilder.appendSuperProtocols(clazz: ObjCClass<ClassDescriptor>) {
|
||||||
|
val protocols = clazz.superProtocols
|
||||||
|
if (protocols.isNotEmpty()) {
|
||||||
|
protocols.joinTo(this, separator = ", ", prefix = " <", postfix = ">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjCInterface.renderInterfaceHeader() = buildString {
|
||||||
|
fun appendSuperClass() {
|
||||||
|
if (superClass != null) append(" : $superClass")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendGenerics() {
|
||||||
|
val generics = generics
|
||||||
|
if (generics.isNotEmpty()) {
|
||||||
|
generics.joinTo(this, separator = ", ", prefix = "<", postfix = ">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendCategoryName() {
|
||||||
|
if (categoryName != null) {
|
||||||
|
append(" (")
|
||||||
|
append(categoryName)
|
||||||
|
append(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append("@interface ")
|
||||||
|
append(name)
|
||||||
|
appendGenerics()
|
||||||
|
appendCategoryName()
|
||||||
|
appendSuperClass()
|
||||||
|
appendSuperProtocols(this@renderInterfaceHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Collector.renderMembers(clazz: ObjCClass<*>) {
|
||||||
|
clazz.members.forEach {
|
||||||
|
+render(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderAttribute(attribute: String) = "__attribute__(($attribute))"
|
||||||
|
|
||||||
|
private fun collect(p: Collector.() -> Unit): List<String> {
|
||||||
|
val collector = Collector()
|
||||||
|
collector.p()
|
||||||
|
return collector.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Collector {
|
||||||
|
private val collection: MutableList<String> = mutableListOf()
|
||||||
|
fun build(): List<String> = collection
|
||||||
|
|
||||||
|
operator fun String.unaryPlus() {
|
||||||
|
collection += this
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun List<String>.unaryPlus() {
|
||||||
|
collection += this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+129
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* 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.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||||
|
|
||||||
|
sealed class ObjCType {
|
||||||
|
final override fun toString(): String = this.render()
|
||||||
|
|
||||||
|
abstract fun render(attrsAndName: String): String
|
||||||
|
|
||||||
|
fun render() = render("")
|
||||||
|
|
||||||
|
protected fun String.withAttrsAndName(attrsAndName: String) =
|
||||||
|
if (attrsAndName.isEmpty()) this else "$this ${attrsAndName.trimStart()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCRawType(private val rawText: String) : ObjCType() {
|
||||||
|
override fun render(attrsAndName: String): String = rawText.withAttrsAndName(attrsAndName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class ObjCReferenceType : ObjCType()
|
||||||
|
|
||||||
|
internal sealed class ObjCNonNullReferenceType : ObjCReferenceType()
|
||||||
|
|
||||||
|
internal data class ObjCNullableReferenceType(val nonNullType: ObjCNonNullReferenceType) : ObjCReferenceType() {
|
||||||
|
override fun render(attrsAndName: String) = nonNullType.render(" _Nullable".withAttrsAndName(attrsAndName))
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCClassType(
|
||||||
|
val className: String,
|
||||||
|
val typeArguments: List<ObjCNonNullReferenceType> = emptyList()
|
||||||
|
) : ObjCNonNullReferenceType() {
|
||||||
|
|
||||||
|
override fun render(attrsAndName: String) = buildString {
|
||||||
|
append(className)
|
||||||
|
if (typeArguments.isNotEmpty()) {
|
||||||
|
append("<")
|
||||||
|
typeArguments.joinTo(this) { it.render() }
|
||||||
|
append(">")
|
||||||
|
}
|
||||||
|
append(" *")
|
||||||
|
append(attrsAndName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCProtocolType(val protocolName: String) : ObjCNonNullReferenceType() {
|
||||||
|
|
||||||
|
override fun render(attrsAndName: String) = "id<$protocolName>".withAttrsAndName(attrsAndName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object ObjCIdType : ObjCNonNullReferenceType() {
|
||||||
|
override fun render(attrsAndName: String) = "id".withAttrsAndName(attrsAndName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object ObjCInstanceType : ObjCNonNullReferenceType() {
|
||||||
|
override fun render(attrsAndName: String): String = "instancetype".withAttrsAndName(attrsAndName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCBlockPointerType(
|
||||||
|
val returnType: ObjCReferenceType, val parameterTypes: List<ObjCReferenceType>
|
||||||
|
) : ObjCNonNullReferenceType() {
|
||||||
|
|
||||||
|
override fun render(attrsAndName: String) = returnType.render(buildString {
|
||||||
|
append("(^")
|
||||||
|
append(attrsAndName)
|
||||||
|
append(")(")
|
||||||
|
if (parameterTypes.isEmpty()) append("void")
|
||||||
|
parameterTypes.joinTo(this) { it.render() }
|
||||||
|
append(')')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCPrimitiveType(val cName: String) : ObjCType() {
|
||||||
|
override fun render(attrsAndName: String) = cName.withAttrsAndName(attrsAndName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ObjCPointerType(val pointee: ObjCType, val nullable: Boolean = false) : ObjCType() {
|
||||||
|
override fun render(attrsAndName: String) =
|
||||||
|
pointee.render("*${if (nullable) {
|
||||||
|
" _Nullable".withAttrsAndName(attrsAndName)
|
||||||
|
} else {
|
||||||
|
attrsAndName
|
||||||
|
}}")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object ObjCVoidType : ObjCType() {
|
||||||
|
override fun render(attrsAndName: String) = "void".withAttrsAndName(attrsAndName)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum class ObjCValueType(
|
||||||
|
val kotlinValueType: ValueType, // It is here for simplicity.
|
||||||
|
val encoding: String
|
||||||
|
) {
|
||||||
|
|
||||||
|
BOOL(ValueType.BOOLEAN, "c"),
|
||||||
|
CHAR(ValueType.BYTE, "c"),
|
||||||
|
UNSIGNED_SHORT(ValueType.CHAR, "S"),
|
||||||
|
SHORT(ValueType.SHORT, "s"),
|
||||||
|
INT(ValueType.INT, "i"),
|
||||||
|
LONG_LONG(ValueType.LONG, "q"),
|
||||||
|
FLOAT(ValueType.FLOAT, "f"),
|
||||||
|
DOUBLE(ValueType.DOUBLE, "d")
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
// UNSIGNED_SHORT -> unsignedShort
|
||||||
|
val nsNumberName = this.name.split('_').mapIndexed { index, s ->
|
||||||
|
val lower = s.toLowerCase()
|
||||||
|
if (index > 0) lower.capitalize() else lower
|
||||||
|
}.joinToString("")
|
||||||
|
|
||||||
|
val nsNumberValueSelector get() = "${nsNumberName}Value"
|
||||||
|
val nsNumberFactorySelector get() = "numberWith${nsNumberName.capitalize()}:"
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
package org.jetbrains.kotlin.backend.konan.objcexport
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
|
|
||||||
|
open class Stub<out D : DeclarationDescriptor>(val name: String, val descriptor: D?)
|
||||||
|
|
||||||
|
abstract class ObjCClass<out D : DeclarationDescriptor>(name: String,
|
||||||
|
descriptor: D?,
|
||||||
|
val superProtocols: List<String>,
|
||||||
|
val members: List<Stub<*>>) : Stub<D>(name, descriptor)
|
||||||
|
|
||||||
|
class ObjCProtocol(name: String,
|
||||||
|
descriptor: ClassDescriptor,
|
||||||
|
superProtocols: List<String>,
|
||||||
|
members: List<Stub<*>>) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members)
|
||||||
|
|
||||||
|
class ObjCInterface(name: String,
|
||||||
|
val generics: List<String> = emptyList(),
|
||||||
|
descriptor: ClassDescriptor? = null,
|
||||||
|
val superClass: String? = null,
|
||||||
|
superProtocols: List<String> = emptyList(),
|
||||||
|
val categoryName: String? = null,
|
||||||
|
members: List<Stub<*>> = emptyList(),
|
||||||
|
val attributes: List<String> = emptyList()) : ObjCClass<ClassDescriptor>(name, descriptor, superProtocols, members)
|
||||||
|
|
||||||
|
class ObjCMethod(descriptor: DeclarationDescriptor?,
|
||||||
|
val isInstanceMethod: Boolean,
|
||||||
|
val returnType: ObjCType,
|
||||||
|
val selectors: List<String>,
|
||||||
|
val parameters: List<ObjCParameter>,
|
||||||
|
val attributes: List<String>) : Stub<DeclarationDescriptor>(buildMethodName(selectors, parameters), descriptor)
|
||||||
|
|
||||||
|
class ObjCParameter(name: String,
|
||||||
|
descriptor: ParameterDescriptor?,
|
||||||
|
val type: ObjCType) : Stub<ParameterDescriptor>(name, descriptor)
|
||||||
|
|
||||||
|
class ObjCProperty(name: String,
|
||||||
|
descriptor: PropertyDescriptor?,
|
||||||
|
val type: ObjCType,
|
||||||
|
val attributes: List<String>,
|
||||||
|
val setterName: String? = null,
|
||||||
|
val getterName: String? = null) : Stub<PropertyDescriptor>(name, descriptor)
|
||||||
|
|
||||||
|
private fun buildMethodName(selectors: List<String>, parameters: List<ObjCParameter>): String =
|
||||||
|
if (selectors.size == 1 && parameters.size == 0) {
|
||||||
|
selectors[0]
|
||||||
|
} else {
|
||||||
|
assert(selectors.size == parameters.size)
|
||||||
|
selectors.joinToString(separator = "")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user