Workaround false positive in AppStore private API usage detector
Fix #2751
This commit is contained in:
committed by
SvyatoslavScherbina
parent
87ae1eaa3b
commit
581aaea0c9
+36
-1
@@ -89,10 +89,17 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
|
||||
)
|
||||
|
||||
fun emitEmptyClass(name: String, superName: String) {
|
||||
emitClass(name, superName, instanceMethods = emptyList())
|
||||
}
|
||||
|
||||
class Method(val selector: String, val encoding: String, val imp: ConstPointer)
|
||||
|
||||
fun emitClass(name: String, superName: String, instanceMethods: List<Method>) {
|
||||
val runtime = context.llvm.runtime
|
||||
fun struct(name: String) = runtime.getStructType(name)
|
||||
|
||||
val classRoType = struct("_class_ro_t")
|
||||
val methodType = struct("_objc_method")
|
||||
val methodListType = struct("__method_list_t")
|
||||
val protocolListType = struct("_objc_protocol_list")
|
||||
val ivarListType = struct("_ivar_list_t")
|
||||
@@ -100,6 +107,31 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
|
||||
|
||||
val classNameLiteral = classNames.get(name)
|
||||
|
||||
fun emitInstanceMethodList(): ConstPointer {
|
||||
if (instanceMethods.isEmpty()) return NullPointer(methodListType)
|
||||
|
||||
val methodStructs = instanceMethods.map {
|
||||
Struct(methodType, selectors.get(it.selector), encodings.get(it.encoding), it.imp.bitcast(int8TypePtr))
|
||||
}
|
||||
|
||||
val methodList = Struct(
|
||||
Int32(LLVMABISizeOfType(codegen.llvmTargetData, methodType).toInt()),
|
||||
Int32(instanceMethods.size),
|
||||
ConstArray(methodType, methodStructs)
|
||||
)
|
||||
|
||||
val globalName = "\u0001l_OBJC_\$_INSTANCE_METHODS_$name"
|
||||
val global = context.llvm.staticData.placeGlobal(globalName, methodList).also {
|
||||
it.setLinkage(LLVMLinkage.LLVMPrivateLinkage)
|
||||
it.setAlignment(runtime.pointerAlignment)
|
||||
it.setSection("__DATA, __objc_const")
|
||||
}
|
||||
|
||||
context.llvm.compilerUsedGlobals += global.llvmGlobal
|
||||
|
||||
return global.pointer.bitcast(pointerType(methodListType))
|
||||
}
|
||||
|
||||
fun buildClassRo(isMetaclass: Boolean): ConstPointer {
|
||||
// TODO: add NonFragileABI_Class_CompiledByARC flag?
|
||||
|
||||
@@ -124,7 +156,7 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
|
||||
fields += Int32(size)
|
||||
fields += NullPointer(int8Type) // ivar layout name
|
||||
fields += classNameLiteral
|
||||
fields += NullPointer(methodListType)
|
||||
fields += if (isMetaclass) NullPointer(methodListType) else emitInstanceMethodList()
|
||||
fields += NullPointer(protocolListType)
|
||||
fields += NullPointer(ivarListType)
|
||||
fields += NullPointer(int8Type) // ivar layout
|
||||
@@ -220,6 +252,9 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
|
||||
private val selectors =
|
||||
CStringLiteralsTable("OBJC_METH_VAR_NAME_", "__TEXT,__objc_methname,cstring_literals")
|
||||
|
||||
private val encodings =
|
||||
CStringLiteralsTable("OBJC_METH_VAR_TYPE_", "__TEXT,__objc_methtype,cstring_literals")
|
||||
|
||||
private inner class CStringLiteralsTable(val label: String, val section: String) {
|
||||
|
||||
private val literals = mutableMapOf<String, ConstPointer>()
|
||||
|
||||
+22
-1
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||
import org.jetbrains.kotlin.backend.konan.ir.constructedClass
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.*
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCDataGenerator
|
||||
import org.jetbrains.kotlin.backend.konan.objcexport.*
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.konan.CurrentKonanModuleOrigin
|
||||
@@ -52,6 +53,8 @@ internal class ObjCExportCodeGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
val referencedSelectors = mutableMapOf<String, MethodBridge>()
|
||||
|
||||
// TODO: currently bridges don't have any custom `landingpad`s,
|
||||
// so it is correct to use [callAtFunctionScope] here.
|
||||
// However, exception handling probably should be refactored
|
||||
@@ -231,6 +234,20 @@ internal class ObjCExportCodeGenerator(
|
||||
|
||||
emitSortedAdapters(placedClassAdapters, "Kotlin_ObjCExport_sortedClassAdapters")
|
||||
emitSortedAdapters(placedInterfaceAdapters, "Kotlin_ObjCExport_sortedProtocolAdapters")
|
||||
emitSelectorsHolder()
|
||||
}
|
||||
|
||||
private fun emitSelectorsHolder() {
|
||||
val impType = functionType(voidType, false, int8TypePtr, int8TypePtr)
|
||||
val imp = generateFunction(codegen, impType, "") {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
val methods = referencedSelectors.map { (selector, bridge) ->
|
||||
ObjCDataGenerator.Method(selector, getEncoding(bridge), constPointer(imp))
|
||||
}
|
||||
|
||||
dataGenerator.emitClass("KotlinSelectorsHolder", "NSObject", instanceMethods = methods)
|
||||
}
|
||||
|
||||
private val impType = pointerType(functionType(int8TypePtr, true, int8TypePtr, int8TypePtr))
|
||||
@@ -674,7 +691,11 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
|
||||
}
|
||||
|
||||
MethodBridgeReceiver.Instance -> kotlinReferenceToObjC(parameters[parameter]!!)
|
||||
MethodBridgeSelector -> genSelector(namer.getSelector(baseMethod))
|
||||
MethodBridgeSelector -> {
|
||||
val selector = namer.getSelector(baseMethod)
|
||||
referencedSelectors.getOrPut(selector) { methodBridge }
|
||||
genSelector(selector)
|
||||
}
|
||||
|
||||
MethodBridgeReceiver.Static,
|
||||
MethodBridgeReceiver.Factory ->
|
||||
|
||||
Reference in New Issue
Block a user