Add selector and method bridge to relevant ObjCMethodSpec types

This helps to drop usages of obsolete "IrDeclaration.descriptor"
from ObjCExportCodeGenerator
This commit is contained in:
Svyatoslav Scherbina
2021-01-28 12:46:32 +03:00
committed by Vasily Levchenko
parent ccf8b570ca
commit 4080a852e1
2 changed files with 85 additions and 54 deletions
@@ -24,6 +24,8 @@ import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrVararg import org.jetbrains.kotlin.ir.expressions.IrVararg
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.Family
@@ -373,8 +375,8 @@ internal class ObjCExportCodeGenerator(
methods = listOf("equals", "hashCode", "toString").map { name -> methods = listOf("equals", "hashCode", "toString").map { name ->
symbols.any.owner.simpleFunctions().single { it.name == Name.identifier(name) } symbols.any.owner.simpleFunctions().single { it.name == Name.identifier(name) }
}.map { }.map {
require(mapper.shouldBeExposed(it.descriptor)) val baseMethod = createObjCMethodSpecBaseMethod(mapper, namer, it.symbol, it.descriptor)
ObjCMethodForKotlinMethod(it.symbol) ObjCMethodForKotlinMethod(baseMethod)
}, },
categoryMethods = emptyList(), categoryMethods = emptyList(),
superClassNotAny = null superClassNotAny = null
@@ -957,11 +959,11 @@ private fun ObjCExportCodeGenerator.generateObjCImpForArrayConstructor(
// TODO: cache bridges. // TODO: cache bridges.
private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge( private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
irFunction: IrFunction, irFunction: IrFunction,
baseIrFunction: IrFunction baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>
): ConstPointer { ): ConstPointer {
val baseMethod = baseIrFunction.descriptor val baseIrFunction = baseMethod.symbol.owner
val methodBridge = mapper.bridgeMethod(baseMethod) val methodBridge = baseMethod.bridge
val parameterToBase = irFunction.allParameters.zip(baseIrFunction.allParameters).toMap() val parameterToBase = irFunction.allParameters.zip(baseIrFunction.allParameters).toMap()
@@ -991,7 +993,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
MethodBridgeReceiver.Instance -> kotlinReferenceToObjC(parameters[parameter]!!) MethodBridgeReceiver.Instance -> kotlinReferenceToObjC(parameters[parameter]!!)
MethodBridgeSelector -> { MethodBridgeSelector -> {
val selector = namer.getSelector(baseMethod) val selector = baseMethod.selector
// Selector is referenced thus should be defined to avoid false positive non-public API rejection: // Selector is referenced thus should be defined to avoid false positive non-public API rejection:
selectorsToDefine[selector] = methodBridge selectorsToDefine[selector] = methodBridge
genSelector(selector) genSelector(selector)
@@ -1022,7 +1024,7 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
val targetResult = callFromBridge(objcMsgSend, objCArgs) val targetResult = callFromBridge(objcMsgSend, objCArgs)
assert(baseMethod !is ConstructorDescriptor) assert(baseMethod.symbol !is IrConstructorSymbol)
fun rethrow() { fun rethrow() {
val error = load(errorOutPtr!!) val error = load(errorOutPtr!!)
@@ -1120,14 +1122,14 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
private fun ObjCExportCodeGenerator.createReverseAdapter( private fun ObjCExportCodeGenerator.createReverseAdapter(
irFunction: IrFunction, irFunction: IrFunction,
baseMethod: IrFunction, baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>,
functionName: String, functionName: String,
vtableIndex: Int?, vtableIndex: Int?,
itablePlace: ClassLayoutBuilder.InterfaceTablePlace? itablePlace: ClassLayoutBuilder.InterfaceTablePlace?
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter { ): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter {
val nameSignature = functionName.localHash.value val nameSignature = functionName.localHash.value
val selector = namer.getSelector(baseMethod.descriptor) val selector = baseMethod.selector
val kotlinToObjC = generateKotlinToObjCBridge( val kotlinToObjC = generateKotlinToObjCBridge(
irFunction, irFunction,
@@ -1141,51 +1143,51 @@ private fun ObjCExportCodeGenerator.createReverseAdapter(
} }
private fun ObjCExportCodeGenerator.createMethodVirtualAdapter( private fun ObjCExportCodeGenerator.createMethodVirtualAdapter(
baseMethod: IrFunction baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
assert(mapper.isBaseMethod(baseMethod.descriptor)) val selector = baseMethod.selector
val methodBridge = baseMethod.bridge
val selector = namer.getSelector(baseMethod.descriptor) val irFunction = baseMethod.symbol.owner
val imp = generateObjCImp(irFunction, irFunction, methodBridge, isVirtual = true)
val methodBridge = mapper.bridgeMethod(baseMethod.descriptor)
val imp = generateObjCImp(baseMethod, baseMethod, methodBridge, isVirtual = true)
return objCToKotlinMethodAdapter(selector, methodBridge, imp) return objCToKotlinMethodAdapter(selector, methodBridge, imp)
} }
private fun ObjCExportCodeGenerator.createMethodAdapter( private fun ObjCExportCodeGenerator.createMethodAdapter(
implementation: IrFunction?, implementation: IrFunction?,
baseMethod: IrFunction baseMethod: ObjCMethodSpec.BaseMethod<*>
) = createMethodAdapter(DirectAdapterRequest(implementation, baseMethod)) ) = createMethodAdapter(DirectAdapterRequest(implementation, baseMethod))
private fun ObjCExportCodeGenerator.createFinalMethodAdapter( private fun ObjCExportCodeGenerator.createFinalMethodAdapter(
irFunction: IrSimpleFunction baseMethod: ObjCMethodSpec.BaseMethod<IrSimpleFunctionSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val irFunction = baseMethod.symbol.owner
require(irFunction.modality == Modality.FINAL) require(irFunction.modality == Modality.FINAL)
return createMethodAdapter(irFunction, irFunction) return createMethodAdapter(irFunction, baseMethod)
} }
private fun ObjCExportCodeGenerator.createMethodAdapter( private fun ObjCExportCodeGenerator.createMethodAdapter(
request: DirectAdapterRequest request: DirectAdapterRequest
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = this.directMethodAdapters.getOrPut(request) { ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = this.directMethodAdapters.getOrPut(request) {
val selectorName = namer.getSelector(request.base.descriptor) val selectorName = request.base.selector
val methodBridge = mapper.bridgeMethod(request.base.descriptor) val methodBridge = request.base.bridge
val imp = generateObjCImp(request.implementation, request.base, methodBridge) val imp = generateObjCImp(request.implementation, request.base.symbol.owner, methodBridge)
objCToKotlinMethodAdapter(selectorName, methodBridge, imp) objCToKotlinMethodAdapter(selectorName, methodBridge, imp)
} }
private fun ObjCExportCodeGenerator.createConstructorAdapter( private fun ObjCExportCodeGenerator.createConstructorAdapter(
irConstructor: IrConstructor baseMethod: ObjCMethodSpec.BaseMethod<IrConstructorSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = createMethodAdapter(irConstructor, irConstructor) ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter = createMethodAdapter(baseMethod.symbol.owner, baseMethod)
private fun ObjCExportCodeGenerator.createArrayConstructorAdapter( private fun ObjCExportCodeGenerator.createArrayConstructorAdapter(
irConstructor: IrConstructor baseMethod: ObjCMethodSpec.BaseMethod<IrConstructorSymbol>
): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter { ): ObjCExportCodeGenerator.ObjCToKotlinMethodAdapter {
val selectorName = namer.getSelector(irConstructor.descriptor) val selectorName = baseMethod.selector
val methodBridge = mapper.bridgeMethod(irConstructor.descriptor) val methodBridge = baseMethod.bridge
val irConstructor = baseMethod.symbol.owner
val imp = generateObjCImpForArrayConstructor(irConstructor, methodBridge) val imp = generateObjCImpForArrayConstructor(irConstructor, methodBridge)
return objCToKotlinMethodAdapter(selectorName, methodBridge, imp) return objCToKotlinMethodAdapter(selectorName, methodBridge, imp)
@@ -1217,7 +1219,7 @@ private fun ObjCExportCodeGenerator.createTypeAdapterForFileClass(
): ObjCExportCodeGenerator.ObjCTypeAdapter { ): ObjCExportCodeGenerator.ObjCTypeAdapter {
val name = fileClass.binaryName val name = fileClass.binaryName
val adapters = fileClass.methods.map { createFinalMethodAdapter(it.baseMethod.owner) } val adapters = fileClass.methods.map { createFinalMethodAdapter(it.baseMethod) }
return ObjCTypeAdapter( return ObjCTypeAdapter(
irClass = null, irClass = null,
@@ -1246,10 +1248,10 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
type.methods.forEach { type.methods.forEach {
when (it) { when (it) {
is ObjCInitMethodForKotlinConstructor -> { is ObjCInitMethodForKotlinConstructor -> {
adapters += createConstructorAdapter(it.irConstructorSymbol.owner) adapters += createConstructorAdapter(it.baseMethod)
} }
is ObjCFactoryMethodForKotlinArrayConstructor -> { is ObjCFactoryMethodForKotlinArrayConstructor -> {
classAdapters += createArrayConstructorAdapter(it.irConstructorSymbol.owner) classAdapters += createArrayConstructorAdapter(it.baseMethod)
} }
is ObjCGetterForKotlinEnumEntry -> { is ObjCGetterForKotlinEnumEntry -> {
classAdapters += createEnumEntryAdapter(it.irEnumEntrySymbol.owner, it.selector) classAdapters += createEnumEntryAdapter(it.irEnumEntrySymbol.owner, it.selector)
@@ -1273,9 +1275,8 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
if (type is ObjCClassForKotlinClass) { if (type is ObjCClassForKotlinClass) {
type.categoryMethods.forEach { type.categoryMethods.forEach {
val irFunction = it.baseMethod.owner adapters += createFinalMethodAdapter(it.baseMethod)
adapters += createFinalMethodAdapter(irFunction) reverseAdapters += nonOverridableAdapter(it.baseMethod.selector, hasSelectorAmbiguity = false)
reverseAdapters += nonOverridableAdapter(irFunction.descriptor, hasSelectorAmbiguity = false)
} }
adapters += createDirectAdapters(type, superClass) adapters += createDirectAdapters(type, superClass)
@@ -1283,9 +1284,11 @@ private fun ObjCExportCodeGenerator.createTypeAdapter(
reverseAdapters += createReverseAdapters(type) reverseAdapters += createReverseAdapters(type)
val virtualAdapters = type.kotlinMethods.map { it.baseMethod.owner } val virtualAdapters = type.kotlinMethods
.filter { it.parentAsClass == irClass && it.isOverridable } .filter {
.map { createMethodVirtualAdapter(it) } val irFunction = it.baseMethod.symbol.owner
irFunction.parentAsClass == irClass && irFunction.isOverridable
}.map { createMethodVirtualAdapter(it.baseMethod) }
val typeInfo = constPointer(codegen.typeInfoValue(irClass)) val typeInfo = constPointer(codegen.typeInfoValue(irClass))
val objCName = type.binaryName val objCName = type.binaryName
@@ -1336,13 +1339,13 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
type: ObjCTypeForKotlinType type: ObjCTypeForKotlinType
): List<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter> { ): List<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter> {
val result = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>() val result = mutableListOf<ObjCExportCodeGenerator.KotlinToObjCMethodAdapter>()
val allBaseMethods = type.kotlinMethods.map { it.baseMethod.owner }.toSet() val allBaseMethodsByIr = type.kotlinMethods.map { it.baseMethod }.associateBy { it.symbol.owner }
for (method in type.irClassSymbol.owner.simpleFunctions()) { for (method in type.irClassSymbol.owner.simpleFunctions()) {
val baseMethods = method.allOverriddenFunctions.filter { it in allBaseMethods } val baseMethods = method.allOverriddenFunctions.mapNotNull { allBaseMethodsByIr[it] }
if (baseMethods.isEmpty()) continue if (baseMethods.isEmpty()) continue
val hasSelectorAmbiguity = baseMethods.map { namer.getSelector(it.descriptor) }.distinct().size > 1 val hasSelectorAmbiguity = baseMethods.map { it.selector }.distinct().size > 1
if (method.isOverridable && !hasSelectorAmbiguity) { if (method.isOverridable && !hasSelectorAmbiguity) {
val baseMethod = baseMethods.first() val baseMethod = baseMethods.first()
@@ -1379,8 +1382,8 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
} else { } else {
// Mark it as non-overridable: // Mark it as non-overridable:
baseMethods.distinctBy { namer.getSelector(it.descriptor) }.forEach { baseMethod -> baseMethods.map { it.selector }.distinct().forEach {
result += nonOverridableAdapter(baseMethod.descriptor, hasSelectorAmbiguity) result += nonOverridableAdapter(it, hasSelectorAmbiguity)
} }
} }
} }
@@ -1389,10 +1392,10 @@ private fun ObjCExportCodeGenerator.createReverseAdapters(
} }
private fun ObjCExportCodeGenerator.nonOverridableAdapter( private fun ObjCExportCodeGenerator.nonOverridableAdapter(
baseMethod: FunctionDescriptor, selector: String,
hasSelectorAmbiguity: Boolean hasSelectorAmbiguity: Boolean
): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter = KotlinToObjCMethodAdapter( ): ObjCExportCodeGenerator.KotlinToObjCMethodAdapter = KotlinToObjCMethodAdapter(
namer.getSelector(baseMethod), selector,
-1, -1,
vtableIndex = if (hasSelectorAmbiguity) -2 else -1, // Describes the reason. vtableIndex = if (hasSelectorAmbiguity) -2 else -1, // Describes the reason.
kotlinImpl = NullPointer(int8Type), kotlinImpl = NullPointer(int8Type),
@@ -1402,7 +1405,7 @@ private fun ObjCExportCodeGenerator.nonOverridableAdapter(
private val ObjCTypeForKotlinType.kotlinMethods: List<ObjCMethodForKotlinMethod> private val ObjCTypeForKotlinType.kotlinMethods: List<ObjCMethodForKotlinMethod>
get() = this.methods.filterIsInstance<ObjCMethodForKotlinMethod>() get() = this.methods.filterIsInstance<ObjCMethodForKotlinMethod>()
internal data class DirectAdapterRequest(val implementation: IrFunction?, val base: IrFunction) internal data class DirectAdapterRequest(val implementation: IrFunction?, val base: ObjCMethodSpec.BaseMethod<*>)
private fun ObjCExportCodeGenerator.createDirectAdapters( private fun ObjCExportCodeGenerator.createDirectAdapters(
typeDeclaration: ObjCClassForKotlinClass, typeDeclaration: ObjCClassForKotlinClass,
@@ -1411,15 +1414,15 @@ private fun ObjCExportCodeGenerator.createDirectAdapters(
fun ObjCClassForKotlinClass.getAllRequiredDirectAdapters() = this.kotlinMethods.map { method -> fun ObjCClassForKotlinClass.getAllRequiredDirectAdapters() = this.kotlinMethods.map { method ->
DirectAdapterRequest( DirectAdapterRequest(
findImplementation(irClassSymbol.owner, method.baseMethod.owner, context), findImplementation(irClassSymbol.owner, method.baseMethod.symbol.owner, context),
method.baseMethod.owner method.baseMethod
) )
} }
val inheritedAdapters = superClass?.getAllRequiredDirectAdapters().orEmpty() val inheritedAdapters = superClass?.getAllRequiredDirectAdapters().orEmpty()
val requiredAdapters = typeDeclaration.getAllRequiredDirectAdapters() - inheritedAdapters val requiredAdapters = typeDeclaration.getAllRequiredDirectAdapters() - inheritedAdapters
return requiredAdapters.distinctBy { namer.getSelector(it.base.descriptor) }.map { createMethodAdapter(it) } return requiredAdapters.distinctBy { it.base.selector }.map { createMethodAdapter(it) }
} }
private fun findImplementation(irClass: IrClass, method: IrSimpleFunction, context: Context): IrSimpleFunction? { private fun findImplementation(irClass: IrClass, method: IrSimpleFunction, context: Context): IrSimpleFunction? {
@@ -17,7 +17,14 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): ObjCExportCodeSpec { internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): ObjCExportCodeSpec {
fun createObjCMethods(methods: List<FunctionDescriptor>) = methods.map { fun createObjCMethods(methods: List<FunctionDescriptor>) = methods.map {
ObjCMethodForKotlinMethod(symbolTable.referenceSimpleFunction(it)) ObjCMethodForKotlinMethod(
createObjCMethodSpecBaseMethod(
mapper,
namer,
symbolTable.referenceSimpleFunction(it),
it
)
)
} }
fun List<CallableMemberDescriptor>.toObjCMethods() = createObjCMethods(this.flatMap { fun List<CallableMemberDescriptor>.toObjCMethods() = createObjCMethods(this.flatMap {
@@ -55,11 +62,12 @@ internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): Obj
} else { } else {
descriptor.constructors.filter { mapper.shouldBeExposed(it) }.mapTo(methods) { descriptor.constructors.filter { mapper.shouldBeExposed(it) }.mapTo(methods) {
val irConstructorSymbol = symbolTable.referenceConstructor(it) val irConstructorSymbol = symbolTable.referenceConstructor(it)
val baseMethod = createObjCMethodSpecBaseMethod(mapper, namer, irConstructorSymbol, it)
if (descriptor.isArray) { if (descriptor.isArray) {
ObjCFactoryMethodForKotlinArrayConstructor(irConstructorSymbol) ObjCFactoryMethodForKotlinArrayConstructor(baseMethod)
} else { } else {
ObjCInitMethodForKotlinConstructor(irConstructorSymbol) ObjCInitMethodForKotlinConstructor(baseMethod)
} }
} }
@@ -94,19 +102,39 @@ internal fun ObjCExportedInterface.createCodeSpec(symbolTable: SymbolTable): Obj
return ObjCExportCodeSpec(files, types) return ObjCExportCodeSpec(files, types)
} }
internal fun <S : IrFunctionSymbol> createObjCMethodSpecBaseMethod(
mapper: ObjCExportMapper,
namer: ObjCExportNamer,
symbol: S,
descriptor: FunctionDescriptor
): ObjCMethodSpec.BaseMethod<S> {
require(mapper.isBaseMethod(descriptor))
val selector = namer.getSelector(descriptor)
val bridge = mapper.bridgeMethod(descriptor)
return ObjCMethodSpec.BaseMethod(symbol, bridge, selector)
}
internal class ObjCExportCodeSpec( internal class ObjCExportCodeSpec(
val files: List<ObjCClassForKotlinFile>, val files: List<ObjCClassForKotlinFile>,
val types: List<ObjCTypeForKotlinType> val types: List<ObjCTypeForKotlinType>
) )
internal sealed class ObjCMethodSpec internal sealed class ObjCMethodSpec {
/**
* Aggregates base method (as defined by [ObjCExportMapper.isBaseMethod])
* and details required to generate code for bridges between Kotlin and Obj-C methods.
*/
data class BaseMethod<out S : IrFunctionSymbol>(val symbol: S, val bridge: MethodBridge, val selector: String)
}
internal class ObjCMethodForKotlinMethod(val baseMethod: IrSimpleFunctionSymbol) : ObjCMethodSpec() internal class ObjCMethodForKotlinMethod(val baseMethod: BaseMethod<IrSimpleFunctionSymbol>) : ObjCMethodSpec()
internal class ObjCInitMethodForKotlinConstructor(val irConstructorSymbol: IrConstructorSymbol) : ObjCMethodSpec() internal class ObjCInitMethodForKotlinConstructor(val baseMethod: BaseMethod<IrConstructorSymbol>) : ObjCMethodSpec()
internal class ObjCFactoryMethodForKotlinArrayConstructor( internal class ObjCFactoryMethodForKotlinArrayConstructor(
val irConstructorSymbol: IrConstructorSymbol val baseMethod: BaseMethod<IrConstructorSymbol>
) : ObjCMethodSpec() ) : ObjCMethodSpec()
internal class ObjCGetterForKotlinEnumEntry( internal class ObjCGetterForKotlinEnumEntry(