Avoid requesting selectors in Objective-C interop stubs

Selector references aren't DCEed and thus can cause false positive
API usage report from AppStore online validation.
Generate selectors by compiler instead.
This commit is contained in:
Svyatoslav Scherbina
2019-03-14 15:39:33 +03:00
committed by SvyatoslavScherbina
parent 0ff1209d0a
commit f8393819fc
7 changed files with 31 additions and 9 deletions
@@ -125,6 +125,10 @@ annotation class InteropStubs()
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
internal annotation class ObjCMethodImp(val selector: String, val encoding: String) internal annotation class ObjCMethodImp(val selector: String, val encoding: String)
@PublishedApi
@TypedIntrinsic(IntrinsicType.OBJC_GET_SELECTOR)
internal external fun objCGetSelector(selector: String): COpaquePointer
@kotlin.native.internal.ExportForCppRuntime("Kotlin_Interop_getObjCClass") @kotlin.native.internal.ExportForCppRuntime("Kotlin_Interop_getObjCClass")
private fun getObjCClassByName(name: NativePtr): NativePtr { private fun getObjCClassByName(name: NativePtr): NativePtr {
val result = objc_lookUpClass(name) val result = objc_lookUpClass(name)
@@ -178,6 +178,7 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
val kniReceiverParameter = "kniR" val kniReceiverParameter = "kniR"
val kniSuperClassParameter = "kniSC" val kniSuperClassParameter = "kniSC"
val kniSelectorParameter = "kniSEL"
val voidPtr = PointerType(VoidType) val voidPtr = PointerType(VoidType)
@@ -199,6 +200,9 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
TypedKotlinValue(voidPtr, TypedKotlinValue(voidPtr,
"getReceiverOrSuper($kniReceiverParameter, $kniSuperClassParameter)")) "getReceiverOrSuper($kniReceiverParameter, $kniSuperClassParameter)"))
kotlinObjCBridgeParameters.add(KotlinParameter(kniSelectorParameter, KotlinTypes.cOpaquePointer))
nativeBridgeArguments.add(TypedKotlinValue(voidPtr, kniSelectorParameter))
val kotlinParameterNames = method.getKotlinParameterNames() val kotlinParameterNames = method.getKotlinParameterNames()
method.parameters.forEachIndexed { index, it -> method.parameters.forEachIndexed { index, it ->
@@ -224,7 +228,6 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
returnType, returnType,
nativeBridgeArguments nativeBridgeArguments
) { nativeValues -> ) { nativeValues ->
val selector = "@selector(${method.selector})"
val messengerParameterTypes = mutableListOf<String>() val messengerParameterTypes = mutableListOf<String>()
messengerParameterTypes.add("void*") messengerParameterTypes.add("void*")
messengerParameterTypes.add("SEL") messengerParameterTypes.add("SEL")
@@ -239,7 +242,7 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
val messenger = "(($messengerType) ${nativeValues.first()})" val messenger = "(($messengerType) ${nativeValues.first()})"
val messengerArguments = listOf(nativeValues[1]) + selector + nativeValues.drop(2) val messengerArguments = nativeValues.drop(1)
"$messenger(${messengerArguments.joinToString()})" "$messenger(${messengerArguments.joinToString()})"
} }
@@ -233,6 +233,8 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val interopCreateNSStringFromKString = val interopCreateNSStringFromKString =
symbolTable.referenceSimpleFunction(context.interopBuiltIns.CreateNSStringFromKString) symbolTable.referenceSimpleFunction(context.interopBuiltIns.CreateNSStringFromKString)
val interopObjCGetSelector = interopFunction("objCGetSelector")
val objCExportTrapOnUndeclaredException = val objCExportTrapOnUndeclaredException =
symbolTable.referenceSimpleFunction(context.builtIns.kotlinNativeInternal.getContributedFunctions( symbolTable.referenceSimpleFunction(context.builtIns.kotlinNativeInternal.getContributedFunctions(
Name.identifier("trapOnUndeclaredException"), Name.identifier("trapOnUndeclaredException"),
@@ -4,6 +4,7 @@ import kotlinx.cinterop.cValuesOf
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.descriptors.TypedIntrinsic import org.jetbrains.kotlin.backend.konan.descriptors.TypedIntrinsic
import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic import org.jetbrains.kotlin.backend.konan.descriptors.isTypedIntrinsic
import org.jetbrains.kotlin.backend.konan.llvm.objc.genObjCSelector
import org.jetbrains.kotlin.backend.konan.reportCompilationError import org.jetbrains.kotlin.backend.konan.reportCompilationError
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
@@ -51,6 +52,7 @@ internal enum class IntrinsicType {
OBJC_GET_OBJC_CLASS, OBJC_GET_OBJC_CLASS,
OBJC_GET_RECEIVER_OR_SUPER, OBJC_GET_RECEIVER_OR_SUPER,
OBJC_INIT_BY, OBJC_INIT_BY,
OBJC_GET_SELECTOR,
// Other // Other
GET_CLASS_TYPE_INFO, GET_CLASS_TYPE_INFO,
CREATE_UNINITIALIZED_INSTANCE, CREATE_UNINITIALIZED_INSTANCE,
@@ -148,6 +150,10 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
environment.evaluateCall(constructorDescriptor, args, Lifetime.IRRELEVANT) environment.evaluateCall(constructorDescriptor, args, Lifetime.IRRELEVANT)
receiver receiver
} }
IntrinsicType.OBJC_GET_SELECTOR -> {
val selector = (callSite.getValueArgument(0) as IrConst<*>).value as String
environment.functionGenerationContext.genObjCSelector(selector)
}
IntrinsicType.INIT_INSTANCE -> { IntrinsicType.INIT_INSTANCE -> {
val callee = callSite as IrCall val callee = callSite as IrCall
val initializer = callee.getValueArgument(1) as IrCall val initializer = callee.getValueArgument(1) as IrCall
@@ -232,6 +238,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
reportNonLoweredIntrinsic(intrinsicType) reportNonLoweredIntrinsic(intrinsicType)
IntrinsicType.INIT_INSTANCE, IntrinsicType.INIT_INSTANCE,
IntrinsicType.OBJC_INIT_BY, IntrinsicType.OBJC_INIT_BY,
IntrinsicType.OBJC_GET_SELECTOR,
IntrinsicType.IMMUTABLE_BLOB -> IntrinsicType.IMMUTABLE_BLOB ->
reportSpecialIntrinsic(intrinsicType) reportSpecialIntrinsic(intrinsicType)
} }
@@ -14,11 +14,7 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
val dataGenerator = codegen.objCDataGenerator!! val dataGenerator = codegen.objCDataGenerator!!
fun FunctionGenerationContext.genSelector(selector: String): LLVMValueRef { fun FunctionGenerationContext.genSelector(selector: String): LLVMValueRef = genObjCSelector(selector)
val selectorRef = dataGenerator.genSelectorRef(selector)
// TODO: clang emits it with `invariant.load` metadata.
return load(selectorRef.llvm)
}
fun FunctionGenerationContext.genGetLinkedClass(name: String): LLVMValueRef { fun FunctionGenerationContext.genGetLinkedClass(name: String): LLVMValueRef {
val classRef = dataGenerator.genClassRef(name) val classRef = dataGenerator.genClassRef(name)
@@ -43,3 +39,9 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
fun msgSender(functionType: LLVMTypeRef): LLVMValueRef = fun msgSender(functionType: LLVMTypeRef): LLVMValueRef =
objcMsgSend.bitcast(pointerType(functionType)).llvm objcMsgSend.bitcast(pointerType(functionType)).llvm
} }
internal fun FunctionGenerationContext.genObjCSelector(selector: String): LLVMValueRef {
val selectorRef = codegen.objCDataGenerator!!.genSelectorRef(selector)
// TODO: clang emits it with `invariant.load` metadata.
return load(selectorRef.llvm)
}
@@ -587,10 +587,13 @@ internal class InteropLoweringPart1(val context: Context) : IrBuildingTransforme
return irCall(bridge, symbolTable.translateErased(info.bridge.returnType!!)).apply { return irCall(bridge, symbolTable.translateErased(info.bridge.returnType!!)).apply {
putValueArgument(0, superClass) putValueArgument(0, superClass)
putValueArgument(1, receiver) putValueArgument(1, receiver)
putValueArgument(2, irCall(symbols.interopObjCGetSelector.owner).apply {
putValueArgument(0, irString(info.selector))
})
assert(arguments.size + 2 == info.bridge.valueParameters.size) assert(arguments.size + 3 == info.bridge.valueParameters.size)
arguments.forEachIndexed { index, argument -> arguments.forEachIndexed { index, argument ->
putValueArgument(index + 2, argument) putValueArgument(index + 3, argument)
} }
} }
} }
@@ -42,6 +42,7 @@ class IntrinsicType {
const val OBJC_GET_OBJC_CLASS = "OBJC_GET_OBJC_CLASS" const val OBJC_GET_OBJC_CLASS = "OBJC_GET_OBJC_CLASS"
const val OBJC_GET_RECEIVER_OR_SUPER = "OBJC_GET_RECEIVER_OR_SUPER" const val OBJC_GET_RECEIVER_OR_SUPER = "OBJC_GET_RECEIVER_OR_SUPER"
const val OBJC_INIT_BY = "OBJC_INIT_BY" const val OBJC_INIT_BY = "OBJC_INIT_BY"
const val OBJC_GET_SELECTOR = "OBJC_GET_SELECTOR"
// Other // Other
const val GET_CLASS_TYPE_INFO = "GET_CLASS_TYPE_INFO" const val GET_CLASS_TYPE_INFO = "GET_CLASS_TYPE_INFO"