ObjCExport: use terminatingExceptionHandler by default

If something called from an ObjCExport bridge throws a Kotlin exception,
treat it as fatal by default and terminate the process.

This affects
* Runtime calls (managing and converting Kotlin and Obj-C references and
  other types), that shouldn't throw such exceptions anyway
* User code (Kotlin function object throws Kotlin exception when
  called from Swift/Obj-C).

If an exception should be handled non-fatally (e.g. forwarded to
the caller), this intention should be expressed in the code generator
explicitly.

^KT-50830

This change makes the code more simple and efficient,
and the control flow -- more explicit.
This commit is contained in:
Svyatoslav Scherbina
2022-01-13 15:43:50 +03:00
committed by Space
parent bc2cc026db
commit 35cb897bdd
3 changed files with 9 additions and 17 deletions
@@ -500,7 +500,7 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) : Runti
val Kotlin_ObjCExport_AbstractClassConstructorCalled by lazyRtFunction
val Kotlin_ObjCExport_RethrowExceptionAsNSError by lazyRtFunction
val Kotlin_ObjCExport_WrapExceptionToNSError by lazyRtFunction
val Kotlin_ObjCExport_RethrowNSErrorAsException by lazyRtFunction
val Kotlin_ObjCExport_NSErrorAsException by lazyRtFunction
val Kotlin_ObjCExport_AllocInstanceWithAssociatedObject by lazyRtFunction
val Kotlin_ObjCExport_createContinuationArgument by lazyRtFunction
val Kotlin_ObjCExport_createUnitContinuationArgument by lazyRtFunction
@@ -169,19 +169,13 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
return callFromBridge(llvmDeclarations, args, resultLifetime)
}
// 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
// (e.g. moved from `IrToBitcode.kt` to [FunctionGenerationContext]).
fun ObjCExportFunctionGenerationContext.callFromBridge(
function: LlvmCallable,
args: List<LLVMValueRef>,
resultLifetime: Lifetime = Lifetime.IRRELEVANT,
): LLVMValueRef {
// TODO: it is required only for Kotlin-to-Objective-C bridges.
this.forwardingForeignExceptionsTerminatedWith = objcTerminate
val exceptionHandler = ExceptionHandler.Caller
// All calls that actually do need to forward exceptions to callers should express this explicitly.
val exceptionHandler = terminatingExceptionHandler
return call(function, args, resultLifetime, exceptionHandler)
}
@@ -1251,8 +1245,12 @@ private fun ObjCExportCodeGenerator.generateKotlinToObjCBridge(
fun rethrow() {
val error = load(errorOutPtr!!)
callFromBridge(context.llvm.Kotlin_ObjCExport_RethrowNSErrorAsException, listOf(error))
unreachable()
val exception = callFromBridge(
context.llvm.Kotlin_ObjCExport_NSErrorAsException,
listOf(error),
resultLifetime = Lifetime.THROW
)
ExceptionHandler.Caller.genThrow(this, exception)
}
fun genKotlinBaseMethodResult(
@@ -138,12 +138,6 @@ extern "C" OBJ_GETTER(Kotlin_ObjCExport_NSErrorAsException, id error) {
RETURN_RESULT_OF(Kotlin_ObjCExport_NSErrorAsExceptionImpl, message, kotlinError);
}
extern "C" void Kotlin_ObjCExport_RethrowNSErrorAsException(id error) {
ObjHolder holder;
KRef exception = Kotlin_ObjCExport_NSErrorAsException(error, holder.slot());
ThrowException(exception);
}
@interface NSError (NSErrorKotlinException)
@end;