ObjCExport: prohibit Kotlin exceptions leaking through native code back

If a Kotlin exceptions is thrown out of a native code in ObjCExport
(when calling Swift/Obj-C method overriding Kotlin method, or calling
Swift/Obj-C block as Kotlin function type), treat this exception as
fatal and terminate the process.
This could happen if the Kotlin exception leaked from Kotlin to native
code before.
In other words, when calling Kotlin -> Swift/Obj-C -> Kotlin, if Kotlin
exception passes from the last to the first part, terminate.

^KT-50830
This commit is contained in:
Svyatoslav Scherbina
2022-01-13 13:57:15 +03:00
committed by Space
parent f7a1fa86a9
commit 776154881b
7 changed files with 16 additions and 21 deletions
@@ -51,7 +51,11 @@ internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
) )
val invoke = loadBlockInvoke(blockPtr, bridge) val invoke = loadBlockInvoke(blockPtr, bridge)
val result = callFromBridge(invoke, listOf(blockPtr) + args, toNative = true) switchThreadStateIfExperimentalMM(ThreadState.Native)
// Using terminatingExceptionHandler, so any exception thrown by `invoke` will lead to the termination,
// and switching the thread state back to `Runnable` on exceptional path is not required.
val result = call(invoke, listOf(blockPtr) + args, exceptionHandler = terminatingExceptionHandler)
switchThreadStateIfExperimentalMM(ThreadState.Runnable)
val kotlinResult = if (bridge.returnsVoid) { val kotlinResult = if (bridge.returnsVoid) {
theUnitInstanceRef.llvm theUnitInstanceRef.llvm
@@ -1259,7 +1259,11 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
} }
} }
val targetResult = callFromBridge(objcMsgSend, objCArgs, toNative = true) switchThreadStateIfExperimentalMM(ThreadState.Native)
// Using terminatingExceptionHandler, so any exception thrown by the method will lead to the termination,
// and switching the thread state back to `Runnable` on exceptional path is not required.
val targetResult = call(objcMsgSend, objCArgs, exceptionHandler = terminatingExceptionHandler)
switchThreadStateIfExperimentalMM(ThreadState.Runnable)
assert(baseMethod.symbol !is IrConstructorSymbol) assert(baseMethod.symbol !is IrConstructorSymbol)
@@ -2675,8 +2675,7 @@ __attribute__((swift_name("ValuesKt")))
+ (id<KtTransform>)createTransformDecimalStringToInt __attribute__((swift_name("createTransformDecimalStringToInt()"))); + (id<KtTransform>)createTransformDecimalStringToInt __attribute__((swift_name("createTransformDecimalStringToInt()")));
+ (BOOL)runUnitBlockBlock:(void (^)(void))block __attribute__((swift_name("runUnitBlock(block:)"))); + (BOOL)runUnitBlockBlock:(void (^)(void))block __attribute__((swift_name("runUnitBlock(block:)")));
+ (void (^)(void))asUnitBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asUnitBlock(block:)"))); + (void (^)(void))asUnitBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asUnitBlock(block:)")));
+ (BOOL)runNothingBlockBlock:(void (^)(void))block __attribute__((swift_name("runNothingBlock(block:)"))); + (void)runNothingBlockBlock:(void (^)(void))block __attribute__((swift_name("runNothingBlock(block:)")));
+ (void (^)(void))asNothingBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asNothingBlock(block:)")));
+ (void (^ _Nullable)(void))getNullBlock __attribute__((swift_name("getNullBlock()"))); + (void (^ _Nullable)(void))getNullBlock __attribute__((swift_name("getNullBlock()")));
+ (BOOL)isBlockNullBlock:(void (^ _Nullable)(void))block __attribute__((swift_name("isBlockNull(block:)"))); + (BOOL)isBlockNullBlock:(void (^ _Nullable)(void))block __attribute__((swift_name("isBlockNull(block:)")));
+ (BOOL)isFunctionObj:(id _Nullable)obj __attribute__((swift_name("isFunction(obj:)"))); + (BOOL)isFunctionObj:(id _Nullable)obj __attribute__((swift_name("isFunction(obj:)")));
@@ -2617,8 +2617,7 @@ __attribute__((swift_name("ValuesKt")))
+ (id<KtTransform>)createTransformDecimalStringToInt __attribute__((swift_name("createTransformDecimalStringToInt()"))); + (id<KtTransform>)createTransformDecimalStringToInt __attribute__((swift_name("createTransformDecimalStringToInt()")));
+ (BOOL)runUnitBlockBlock:(void (^)(void))block __attribute__((swift_name("runUnitBlock(block:)"))); + (BOOL)runUnitBlockBlock:(void (^)(void))block __attribute__((swift_name("runUnitBlock(block:)")));
+ (void (^)(void))asUnitBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asUnitBlock(block:)"))); + (void (^)(void))asUnitBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asUnitBlock(block:)")));
+ (BOOL)runNothingBlockBlock:(void (^)(void))block __attribute__((swift_name("runNothingBlock(block:)"))); + (void)runNothingBlockBlock:(void (^)(void))block __attribute__((swift_name("runNothingBlock(block:)")));
+ (void (^)(void))asNothingBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asNothingBlock(block:)")));
+ (void (^ _Nullable)(void))getNullBlock __attribute__((swift_name("getNullBlock()"))); + (void (^ _Nullable)(void))getNullBlock __attribute__((swift_name("getNullBlock()")));
+ (BOOL)isBlockNullBlock:(void (^ _Nullable)(void))block __attribute__((swift_name("isBlockNull(block:)"))); + (BOOL)isBlockNullBlock:(void (^ _Nullable)(void))block __attribute__((swift_name("isBlockNull(block:)")));
+ (BOOL)isFunctionObj:(id _Nullable)obj __attribute__((swift_name("isFunction(obj:)"))); + (BOOL)isFunctionObj:(id _Nullable)obj __attribute__((swift_name("isFunction(obj:)")));
@@ -2617,8 +2617,7 @@ __attribute__((swift_name("ValuesKt")))
+ (id<KtTransform>)createTransformDecimalStringToInt __attribute__((swift_name("createTransformDecimalStringToInt()"))); + (id<KtTransform>)createTransformDecimalStringToInt __attribute__((swift_name("createTransformDecimalStringToInt()")));
+ (BOOL)runUnitBlockBlock:(void (^)(void))block __attribute__((swift_name("runUnitBlock(block:)"))); + (BOOL)runUnitBlockBlock:(void (^)(void))block __attribute__((swift_name("runUnitBlock(block:)")));
+ (void (^)(void))asUnitBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asUnitBlock(block:)"))); + (void (^)(void))asUnitBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asUnitBlock(block:)")));
+ (BOOL)runNothingBlockBlock:(void (^)(void))block __attribute__((swift_name("runNothingBlock(block:)"))); + (void)runNothingBlockBlock:(void (^)(void))block __attribute__((swift_name("runNothingBlock(block:)")));
+ (void (^)(void))asNothingBlockBlock:(id _Nullable (^)(void))block __attribute__((swift_name("asNothingBlock(block:)")));
+ (void (^ _Nullable)(void))getNullBlock __attribute__((swift_name("getNullBlock()"))); + (void (^ _Nullable)(void))getNullBlock __attribute__((swift_name("getNullBlock()")));
+ (BOOL)isBlockNullBlock:(void (^ _Nullable)(void))block __attribute__((swift_name("isBlockNull(block:)"))); + (BOOL)isBlockNullBlock:(void (^ _Nullable)(void))block __attribute__((swift_name("isBlockNull(block:)")));
+ (BOOL)isFunctionObj:(id _Nullable)obj __attribute__((swift_name("isFunction(obj:)"))); + (BOOL)isFunctionObj:(id _Nullable)obj __attribute__((swift_name("isFunction(obj:)")));
@@ -504,17 +504,7 @@ fun runUnitBlock(block: () -> Unit): Boolean {
fun asUnitBlock(block: () -> Any?): () -> Unit = { block() } fun asUnitBlock(block: () -> Any?): () -> Unit = { block() }
fun runNothingBlock(block: () -> Nothing) = try { fun runNothingBlock(block: () -> Nothing) { (block as () -> Any?)() }
block()
false
} catch (e: Throwable) {
true
}
fun asNothingBlock(block: () -> Any?): () -> Nothing = {
block()
TODO()
}
fun getNullBlock(): (() -> Unit)? = null fun getNullBlock(): (() -> Unit)? = null
fun isBlockNull(block: (() -> Unit)?): Boolean = block == null fun isBlockNull(block: (() -> Unit)?): Boolean = block == null
@@ -435,8 +435,8 @@ func testLambda() throws {
try assertTrue(unitBlock() == Void()) try assertTrue(unitBlock() == Void())
try assertEquals(actual: blockRuns, expected: 2) try assertEquals(actual: blockRuns, expected: 2)
let nothingBlock: () -> Void = ValuesKt.asNothingBlock { blockRuns += 1 } let nothingBlock: () -> Void = { blockRuns += 1 }
try assertTrue(ValuesKt.runNothingBlock(block: nothingBlock)) ValuesKt.runNothingBlock(block: nothingBlock)
try assertEquals(actual: blockRuns, expected: 3) try assertEquals(actual: blockRuns, expected: 3)
try assertTrue(ValuesKt.getNullBlock() == nil) try assertTrue(ValuesKt.getNullBlock() == nil)