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>>
|
||||
|
||||
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()
|
||||
|
||||
+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>()
|
||||
|
||||
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()}:"
|
||||
}
|
||||
}
|
||||
+12
-9
@@ -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()
|
||||
|
||||
|
||||
+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