From 019e7b1cc8d3b91aa0fe9396b09c0af08cb4a0b5 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Tue, 14 May 2019 14:32:29 +0300 Subject: [PATCH] Custom exception hook in reverse C interop (#2946) --- .../kotlin/backend/konan/CAdapterGenerator.kt | 19 +++++++++++++++++++ backend.native/tests/build.gradle | 3 ++- .../tests/produce_dynamic/simple/hello.kt | 15 +++++++++++++++ .../tests/produce_dynamic/simple/main.c | 7 +++++++ runtime/src/main/cpp/Memory.h | 1 - 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt index 20f19f3d7e2..24f192c6187 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CAdapterGenerator.kt @@ -432,6 +432,7 @@ private class ExportedElement(val kind: ElementKind, val isStringReturned = owner.isMappedToString(cfunction[0].second) // TODO: do we really need that in every function? builder.append(" Kotlin_initRuntimeIfNeeded();\n") + builder.append(" try {\n") if (isObjectReturned || isStringReturned) { builder.append(" KObjHolder result_holder;\n") args += "result_holder.slot()" @@ -455,6 +456,8 @@ private class ExportedElement(val kind: ElementKind, "result", cfunction[0].second, Direction.KOTLIN_TO_C, builder) builder.append(" return $result;\n") } + builder.append(" } catch (ExceptionObjHolder& e) { TerminateWithUnhandledException(e.obj()); } \n") + builder.append("}\n") return builder.toString() @@ -885,6 +888,7 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi | |#define RUNTIME_NOTHROW __attribute__((nothrow)) |#define RUNTIME_USED __attribute__((used)) + |#define RUNTIME_NORETURN __attribute__((noreturn)) | |extern "C" { |void UpdateHeapRef(KObjHeader**, const KObjHeader*) RUNTIME_NOTHROW; @@ -897,6 +901,7 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi |void EnterFrame(KObjHeader** start, int parameters, int count) RUNTIME_NOTHROW; |void LeaveFrame(KObjHeader** start, int parameters, int count) RUNTIME_NOTHROW; |void Kotlin_initRuntimeIfNeeded(); + |void TerminateWithUnhandledException(KObjHeader*) RUNTIME_NORETURN; | |KObjHeader* CreateStringFromCString(const char*, KObjHeader**); |char* CreateCStringFromString(const KObjHeader*); @@ -930,6 +935,20 @@ internal class CAdapterGenerator(val context: Context) : DeclarationDescriptorVi | | KObjHeader** frame() { return reinterpret_cast(&frame_); } |}; + | + |class ExceptionObjHolder { + | public: + | explicit ExceptionObjHolder(const KObjHeader* obj): obj_(nullptr) { + | ::UpdateHeapRef(&obj_, obj); + | } + | ~ExceptionObjHolder() { + | UpdateHeapRef(&obj_, nullptr); + | } + | KObjHeader* obj() { return obj_; } + | private: + | KObjHeader* obj_; + |}; + | |static void DisposeStablePointerImpl(${prefix}_KNativePtr ptr) { | DisposeStablePointer(ptr); |} diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index e8f1d151297..1c10a9f7eea 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -3297,7 +3297,8 @@ task produce_dynamic(type: DynamicKonanTest) { "object = 42\n" + "singleton = I am single\n" + "mutable = foo\n" + - "topLevel = 777 3\n" + "topLevel = 777 3\n" + + "Error handler: kotlin.Error: Expected error\n" } task library_mismatch(type: RunStandaloneKonanTest) { diff --git a/backend.native/tests/produce_dynamic/simple/hello.kt b/backend.native/tests/produce_dynamic/simple/hello.kt index f21a508c398..acb33e61222 100644 --- a/backend.native/tests/produce_dynamic/simple/hello.kt +++ b/backend.native/tests/produce_dynamic/simple/hello.kt @@ -6,6 +6,7 @@ import kotlinx.cinterop.* import kotlin.native.CName +import kotlin.native.concurrent.freeze // Top level functions. fun hello() { @@ -96,3 +97,17 @@ fun useInlineClasses(ic1: IC1, ic2: IC2, ic3: IC3) { assert(ic2.value == "bar") assert(ic3.value is Base) } + +fun setCErrorHandler(callback: CPointer) -> Unit>>?) { + setUnhandledExceptionHook({ + throwable: Throwable -> + memScoped { + callback!!(throwable.toString().cstr.ptr) + } + kotlin.system.exitProcess(0) + }.freeze()) +} + +fun throwException() { + throw Error("Expected error") +} diff --git a/backend.native/tests/produce_dynamic/simple/main.c b/backend.native/tests/produce_dynamic/simple/main.c index c89c6293417..f060aa88013 100644 --- a/backend.native/tests/produce_dynamic/simple/main.c +++ b/backend.native/tests/produce_dynamic/simple/main.c @@ -5,6 +5,10 @@ #define T_(x) testlib_kref_ ## x #define CAST(T, v) testlib_kref_ ## T { .pinned = v } +void errorHandler(const char* str) { + printf("Error handler: %s\n", str); +} + int main(void) { T_(Singleton) singleton = __ kotlin.root.Singleton._instance(); T_(Base) base = __ kotlin.root.Base.Base(); @@ -61,6 +65,9 @@ int main(void) { __ DisposeStablePointer(enum1.pinned); __ DisposeStablePointer(object1.pinned); + __ kotlin.root.setCErrorHandler(&errorHandler); + __ kotlin.root.throwException(); + return 0; } diff --git a/runtime/src/main/cpp/Memory.h b/runtime/src/main/cpp/Memory.h index b1f50628f4a..1ca61e5904e 100644 --- a/runtime/src/main/cpp/Memory.h +++ b/runtime/src/main/cpp/Memory.h @@ -569,7 +569,6 @@ class ExceptionObjHolder { ObjHeader* obj_; }; - class KRefSharedHolder { public: inline ObjHeader** slotToInit() {