Custom exception reporter. (#2604)
This commit is contained in:
@@ -2036,6 +2036,13 @@ task extend_exception(type: RunKonanTest) {
|
||||
source = "runtime/exceptions/extend0.kt"
|
||||
}
|
||||
|
||||
task custom_hook(type: RunStandaloneKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32') // Uses exceptions.
|
||||
goldValue = "value 42: Error\n"
|
||||
expectedExitStatus = 1
|
||||
source = "runtime/exceptions/custom_hook.kt"
|
||||
}
|
||||
|
||||
task runtime_math_exceptions(type: RunKonanTest) {
|
||||
disabled = (project.testTarget == 'wasm32')
|
||||
source = "stdlib_external/numbers/MathExceptionTest.kt"
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the LICENSE file.
|
||||
*/
|
||||
import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
fun main(args : Array<String>) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
setUnhandledExceptionHook { _ -> println("wrong") }
|
||||
}
|
||||
|
||||
val x = 42
|
||||
val old = setUnhandledExceptionHook({
|
||||
throwable: Throwable -> println("value $x: ${throwable::class.simpleName}")
|
||||
}.freeze())
|
||||
|
||||
assertNull(old)
|
||||
|
||||
throw Error("an error")
|
||||
}
|
||||
@@ -12,18 +12,17 @@ import kotlin.native.internal.ExportForCppRuntime
|
||||
@SymbolName("EntryPointSelector")
|
||||
external fun EntryPointSelector(args: Array<String>)
|
||||
|
||||
@SymbolName("OnUnhandledException")
|
||||
external private fun OnUnhandledException(throwable: Throwable)
|
||||
|
||||
@ExportForCppRuntime
|
||||
private fun Konan_start(args: Array<String>): Int {
|
||||
try {
|
||||
EntryPointSelector(args)
|
||||
|
||||
// Successfully finished:
|
||||
return 0
|
||||
|
||||
} catch (e: Throwable) {
|
||||
// TODO: may be add some more info.
|
||||
print("Uncaught exception from Kotlin's main: ")
|
||||
e.printStackTrace()
|
||||
OnUnhandledException(e)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,13 @@ class AutoFree {
|
||||
}
|
||||
};
|
||||
|
||||
// RuntimeUtiks.kt
|
||||
extern "C" void ReportUnhandledException(KRef throwable);
|
||||
extern "C" void ExceptionReporterLaunchpad(KRef reporter, KRef throwable);
|
||||
|
||||
KRef currentUnhandledExceptionHook = nullptr;
|
||||
int32_t currentUnhandledExceptionHookLock = 0;
|
||||
|
||||
#if USE_GCC_UNWIND
|
||||
struct Backtrace {
|
||||
Backtrace(int count, int skip) : index(0), skipCount(skip) {
|
||||
@@ -206,10 +213,24 @@ void ThrowException(KRef exception) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void ReportUnhandledException(KRef e);
|
||||
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
|
||||
RETURN_RESULT_OF(SwapRefLocked,
|
||||
¤tUnhandledExceptionHook, currentUnhandledExceptionHook, hook, ¤tUnhandledExceptionHookLock);
|
||||
}
|
||||
|
||||
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef e) {
|
||||
ReportUnhandledException(e);
|
||||
void OnUnhandledException(KRef throwable) {
|
||||
ObjHolder handlerHolder;
|
||||
auto* handler = SwapRefLocked(¤tUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
|
||||
¤tUnhandledExceptionHookLock, handlerHolder.slot());
|
||||
if (handler == nullptr) {
|
||||
ReportUnhandledException(throwable);
|
||||
} else {
|
||||
ExceptionReporterLaunchpad(handler, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef throwable) {
|
||||
OnUnhandledException(throwable);
|
||||
konan::abort();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
*/
|
||||
package kotlin.native
|
||||
|
||||
import kotlin.native.concurrent.isFrozen
|
||||
import kotlin.native.concurrent.InvalidMutabilityException
|
||||
|
||||
/**
|
||||
* Initializes Kotlin runtime for the current thread, if not inited already.
|
||||
*/
|
||||
@@ -23,4 +26,27 @@ public class IncorrectDereferenceException : RuntimeException {
|
||||
constructor() : super()
|
||||
|
||||
constructor(message: String) : super(message)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Typealias describing custom exception reporting hook.
|
||||
*/
|
||||
public typealias ReportUnhandledExceptionHook = Function1<Throwable, Unit>
|
||||
|
||||
/**
|
||||
* Install custom unhandled exception hook. Returns old hook, or null if it was not specified.
|
||||
* Hook is invoked whenever there's uncaught exception reaching boundaries of the Kotlin world,
|
||||
* i.e. top level main(), or when Objective-C to Kotlin call not marked with @Throws throws an exception.
|
||||
* Hook must be a frozen lambda, so that it could be called from any thread/worker.
|
||||
* Hook is invoked once, and is cleared afterwards, so that memory leak detection works as expected even
|
||||
* with custom exception hooks.
|
||||
*/
|
||||
public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook? {
|
||||
if (!hook.isFrozen) {
|
||||
throw InvalidMutabilityException("Unhandled exception hook must be frozen")
|
||||
}
|
||||
return setUnhandledExceptionHook0(hook)
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_setUnhandledExceptionHook")
|
||||
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
|
||||
@@ -86,9 +86,18 @@ internal fun PrintThrowable(throwable: Throwable) {
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun ReportUnhandledException(e: Throwable) {
|
||||
internal fun ReportUnhandledException(throwable: Throwable) {
|
||||
print("Uncaught Kotlin exception: ")
|
||||
e.printStackTrace()
|
||||
throwable.printStackTrace()
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
internal fun ExceptionReporterLaunchpad(reporter: (Throwable) -> Unit, throwable: Throwable) {
|
||||
try {
|
||||
reporter(throwable)
|
||||
} catch (t: Throwable) {
|
||||
ReportUnhandledException(t)
|
||||
}
|
||||
}
|
||||
|
||||
@ExportForCppRuntime
|
||||
|
||||
Reference in New Issue
Block a user