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:
Max Medvedev
2018-05-24 00:09:36 +03:00
committed by SvyatoslavScherbina
parent 40a58a9a61
commit 9b9f0412ce
11 changed files with 969 additions and 653 deletions
@@ -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) }
)
}
}
}
@@ -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()) }
}
@@ -46,7 +46,7 @@ internal class ObjCExport(val codegen: CodeGenerator) {
val topLevelDeclarations: Map<FqName, List<CallableMemberDescriptor>>
if (context.config.produce == CompilerOutputKind.FRAMEWORK) {
val headerGenerator = ObjCExportHeaderGenerator(context)
val headerGenerator = ObjCExportHeaderGeneratorImpl(context)
produceFrameworkSpecific(headerGenerator)
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)
generatedClasses = emptySet()
@@ -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
)
}
}
@@ -36,7 +36,7 @@ internal abstract class ObjCExportMapper {
private val methodBridgeCache = mutableMapOf<FunctionDescriptor, MethodBridge>()
fun bridgeMethod(descriptor: FunctionDescriptor) = methodBridgeCache.getOrPut(descriptor) {
fun bridgeMethod(descriptor: FunctionDescriptor): MethodBridge = methodBridgeCache.getOrPut(descriptor) {
bridgeMethodImpl(descriptor)
}
}
@@ -110,105 +110,6 @@ internal fun ObjCExportMapper.doesThrow(method: FunctionDescriptor): Boolean = m
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 {
val valueType = kotlinType.correspondingValueType
?: return ReferenceBridge
@@ -305,30 +206,4 @@ internal fun ObjCExportMapper.bridgePropertyType(descriptor: PropertyDescriptor)
assert(isBaseProperty(descriptor))
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()}:"
}
}
@@ -16,9 +16,9 @@
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.isInterface
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
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.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"
private val commonPackageSegments = context.moduleDescriptor.guessMainPackage().pathSegments()
private val topLevelNamePrefix = context.moduleDescriptor.namePrefix
private val commonPackageSegments = moduleDescriptor.guessMainPackage().pathSegments()
val mutableSetName = "${topLevelNamePrefix}MutableSet"
val mutableMapName = "${topLevelNamePrefix}MutableDictionary"
@@ -116,7 +119,7 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
StringBuilder().apply {
append(topLevelNamePrefix)
if (descriptor.module != context.moduleDescriptor) {
if (descriptor.module != moduleDescriptor) {
append(descriptor.module.namePrefix)
}
@@ -246,11 +249,11 @@ internal class ObjCExportNamer(val context: Context, val mapper: ObjCExportMappe
}
init {
val any = context.builtIns.any
val any = builtIns.any
classNames.forceAssign(any, kotlinAnyName)
classNames.forceAssign(context.builtIns.mutableSet, mutableSetName)
classNames.forceAssign(context.builtIns.mutableMap, mutableMapName)
classNames.forceAssign(builtIns.mutableSet, mutableSetName)
classNames.forceAssign(builtIns.mutableMap, mutableMapName)
fun ClassDescriptor.method(name: String) =
this.unsubstitutedMemberScope.getContributedFunctions(
@@ -439,7 +442,7 @@ private fun ObjCExportMapper.canHaveSameName(first: PropertyDescriptor, second:
return bridgePropertyType(first) == bridgePropertyType(second)
}
private val ModuleDescriptor.namePrefix: String get() {
internal val ModuleDescriptor.namePrefix: String get() {
// <fooBar> -> FooBar
val moduleName = this.name.asString().let { it.substring(1, it.lastIndex) }.capitalize()
@@ -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()
}
@@ -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
}
}
}
@@ -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()}:"
}
@@ -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 = "")
}