Move unhandledExceptionHook to kotlin code (#4704)
This commit is contained in:
committed by
Vasily Levchenko
parent
3ee0dc112b
commit
d5ca285a79
@@ -2531,12 +2531,20 @@ standaloneTest("kt-37572") {
|
||||
}
|
||||
|
||||
standaloneTest("custom_hook") {
|
||||
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
|
||||
enabled = (project.testTarget != 'wasm32') && // Uses exceptions.
|
||||
!isExperimentalMM // Experimental MM does not support freezing yet.
|
||||
goldValue = "value 42: Error\n"
|
||||
expectedExitStatus = 1
|
||||
source = "runtime/exceptions/custom_hook.kt"
|
||||
}
|
||||
|
||||
standaloneTest("exception_in_global_init") {
|
||||
enabled = (project.testTarget != 'wasm32') // Uses exceptions.
|
||||
source = "runtime/exceptions/exception_in_global_init.kt"
|
||||
expectedExitStatusChecker = { it != 0 }
|
||||
outputChecker = { s -> s.contains("Uncaught Kotlin exception: kotlin.IllegalStateException: FAIL") && !s.contains("in kotlin main") }
|
||||
}
|
||||
|
||||
standaloneTest("runtime_math_exceptions") {
|
||||
enabled = (project.testTarget != 'wasm32')
|
||||
source = "stdlib_external/numbers/MathExceptionTest.kt"
|
||||
|
||||
@@ -6,31 +6,17 @@ import kotlin.test.*
|
||||
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
fun setHookLegacyMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
|
||||
fun main(args : Array<String>) {
|
||||
assertFailsWith<InvalidMutabilityException> {
|
||||
setUnhandledExceptionHook { _ -> println("wrong") }
|
||||
}
|
||||
|
||||
return setUnhandledExceptionHook(hook.freeze())
|
||||
}
|
||||
|
||||
fun setHookNewMM(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
|
||||
return setUnhandledExceptionHook(hook)
|
||||
}
|
||||
|
||||
fun setHook(hook: ReportUnhandledExceptionHook) : ReportUnhandledExceptionHook? {
|
||||
return when (kotlin.native.Platform.memoryModel) {
|
||||
kotlin.native.MemoryModel.EXPERIMENTAL -> setHookNewMM(hook)
|
||||
else -> setHookLegacyMM(hook)
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val x = 42
|
||||
val old = setHook {
|
||||
val old = setUnhandledExceptionHook({
|
||||
throwable: Throwable -> println("value $x: ${throwable::class.simpleName}")
|
||||
}
|
||||
}.freeze())
|
||||
|
||||
assertNull(old)
|
||||
|
||||
throw Error("an error")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.*
|
||||
|
||||
val p: Nothing = error("FAIL")
|
||||
|
||||
fun main() {
|
||||
println("in kotlin main")
|
||||
}
|
||||
@@ -48,14 +48,6 @@
|
||||
|
||||
namespace {
|
||||
|
||||
// RuntimeUtils.kt
|
||||
extern "C" void ReportUnhandledException(KRef throwable);
|
||||
extern "C" void ExceptionReporterLaunchpad(KRef reporter, KRef throwable);
|
||||
|
||||
KRef currentUnhandledExceptionHook = nullptr;
|
||||
int32_t currentUnhandledExceptionHookLock = 0;
|
||||
int32_t currentUnhandledExceptionHookCookie = 0;
|
||||
|
||||
#if USE_GCC_UNWIND
|
||||
struct Backtrace {
|
||||
Backtrace(int count, int skip) : index(0), skipCount(skip) {
|
||||
@@ -220,23 +212,6 @@ void ThrowException(KRef exception) {
|
||||
#endif
|
||||
}
|
||||
|
||||
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook) {
|
||||
RETURN_RESULT_OF(SwapHeapRefLocked,
|
||||
¤tUnhandledExceptionHook, currentUnhandledExceptionHook, hook, ¤tUnhandledExceptionHookLock,
|
||||
¤tUnhandledExceptionHookCookie);
|
||||
}
|
||||
|
||||
void OnUnhandledException(KRef throwable) {
|
||||
ObjHolder handlerHolder;
|
||||
auto* handler = SwapHeapRefLocked(¤tUnhandledExceptionHook, currentUnhandledExceptionHook, nullptr,
|
||||
¤tUnhandledExceptionHookLock, ¤tUnhandledExceptionHookCookie, handlerHolder.slot());
|
||||
if (handler == nullptr) {
|
||||
ReportUnhandledException(throwable);
|
||||
} else {
|
||||
ExceptionReporterLaunchpad(handler, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class {
|
||||
|
||||
@@ -28,11 +28,10 @@ OBJ_GETTER0(Kotlin_getCurrentStackTrace);
|
||||
|
||||
OBJ_GETTER(GetStackTraceStrings, KConstRef stackTrace);
|
||||
|
||||
OBJ_GETTER(Kotlin_setUnhandledExceptionHook, KRef hook);
|
||||
|
||||
// Throws arbitrary exception.
|
||||
void ThrowException(KRef exception);
|
||||
|
||||
// RuntimeUtils.kt
|
||||
void OnUnhandledException(KRef throwable);
|
||||
|
||||
RUNTIME_NORETURN void TerminateWithUnhandledException(KRef exception);
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
*/
|
||||
package kotlin.native
|
||||
|
||||
import kotlin.native.concurrent.isFrozen
|
||||
import kotlin.native.concurrent.InvalidMutabilityException
|
||||
import kotlin.native.internal.Escapes
|
||||
import kotlin.native.internal.UnhandledExceptionHookHolder
|
||||
|
||||
/**
|
||||
* Initializes Kotlin runtime for the current thread, if not inited already.
|
||||
@@ -45,19 +44,16 @@ public typealias ReportUnhandledExceptionHook = Function1<Throwable, Unit>
|
||||
* with custom exception hooks.
|
||||
*/
|
||||
public fun setUnhandledExceptionHook(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook? {
|
||||
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL && !hook.isFrozen) {
|
||||
try {
|
||||
return UnhandledExceptionHookHolder.hook.swap(hook)
|
||||
} catch (e: InvalidMutabilityException) {
|
||||
throw InvalidMutabilityException("Unhandled exception hook must be frozen")
|
||||
}
|
||||
return setUnhandledExceptionHook0(hook)
|
||||
}
|
||||
|
||||
@SymbolName("Kotlin_setUnhandledExceptionHook")
|
||||
@Escapes(0b01) // <hook> escapes
|
||||
external private fun setUnhandledExceptionHook0(hook: ReportUnhandledExceptionHook): ReportUnhandledExceptionHook?
|
||||
|
||||
/**
|
||||
* Compute stable wrt potential object relocations by the memory manager identity hash code.
|
||||
* @return 0 for `null` object, identity hash code otherwise.
|
||||
*/
|
||||
@SymbolName("Kotlin_Any_hashCode")
|
||||
public external fun Any?.identityHashCode(): Int
|
||||
public external fun Any?.identityHashCode(): Int
|
||||
|
||||
@@ -283,6 +283,19 @@ public class AtomicReference<T> {
|
||||
public override fun toString(): String =
|
||||
"${debugString(this)} -> ${debugString(value)}"
|
||||
|
||||
// TODO: Consider making this public.
|
||||
internal fun swap(new: T): T {
|
||||
while (true) {
|
||||
val old = value
|
||||
if (old === new) {
|
||||
return old
|
||||
}
|
||||
if (compareAndSet(old, new)) {
|
||||
return old
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation details.
|
||||
@SymbolName("Kotlin_AtomicReference_set")
|
||||
private external fun setImpl(new: Any?): Unit
|
||||
|
||||
@@ -7,6 +7,7 @@ package kotlin.native.internal
|
||||
|
||||
import kotlin.internal.getProgressionLastElement
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.native.concurrent.AtomicReference
|
||||
|
||||
@ExportForCppRuntime
|
||||
fun ThrowNullPointerException(): Nothing {
|
||||
@@ -118,10 +119,23 @@ internal fun ReportUnhandledException(throwable: Throwable) {
|
||||
@SymbolName("TerminateWithUnhandledException")
|
||||
internal external fun TerminateWithUnhandledException(throwable: Throwable)
|
||||
|
||||
// Using object to make sure that `hook` is initialized when it's needed instead of
|
||||
// in a normal global initialization flow. This is important if some global happens
|
||||
// to throw an exception during it's initialization before this hook would've been initialized.
|
||||
internal object UnhandledExceptionHookHolder {
|
||||
internal val hook: AtomicReference<ReportUnhandledExceptionHook?> = AtomicReference(null)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
@ExportForCppRuntime
|
||||
internal fun ExceptionReporterLaunchpad(reporter: (Throwable) -> Unit, throwable: Throwable) {
|
||||
internal fun OnUnhandledException(throwable: Throwable) {
|
||||
val handler = UnhandledExceptionHookHolder.hook.swap(null)
|
||||
if (handler == null) {
|
||||
ReportUnhandledException(throwable);
|
||||
return
|
||||
}
|
||||
try {
|
||||
reporter(throwable)
|
||||
handler(throwable)
|
||||
} catch (t: Throwable) {
|
||||
ReportUnhandledException(t)
|
||||
}
|
||||
@@ -210,8 +224,3 @@ internal fun <T> listOfInternal(vararg elements: T): List<T> {
|
||||
result.add(elements[i])
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@PublishedApi
|
||||
@SymbolName("OnUnhandledException")
|
||||
external internal fun OnUnhandledException(throwable: Throwable)
|
||||
|
||||
@@ -170,7 +170,7 @@ RUNTIME_NORETURN OBJ_GETTER(DescribeObjectForDebugging, KConstNativePtr typeInfo
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
void ExceptionReporterLaunchpad(KRef reporter, KRef throwable) {
|
||||
void OnUnhandledException(KRef throwable) {
|
||||
throw std::runtime_error("Not implemented for tests");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user