From f83e73ca204fb6648334275ccce7827b8020b1eb Mon Sep 17 00:00:00 2001 From: Mike Aizatsky Date: Mon, 16 Sep 2024 10:54:48 -0700 Subject: [PATCH] rust error callback Rather then throw things directly, introduce a global callback. We will override the callback in workerd to integrate with kj::Exception --- gen/src/builtin.rs | 17 +++++++++++++---- gen/src/write.rs | 5 ++++- include/cxx.h | 4 ++++ tests/ffi/tests.cc | 26 ++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/gen/src/builtin.rs b/gen/src/builtin.rs index a7dbc2da9..e4c29c311 100644 --- a/gen/src/builtin.rs +++ b/gen/src/builtin.rs @@ -352,11 +352,11 @@ pub(super) fn write(out: &mut OutFile) { writeln!(out, "template <>"); writeln!(out, "class impl final {{"); writeln!(out, "public:"); - writeln!(out, " static Error error(repr::PtrLen repr) noexcept {{"); + writeln!(out, " static void error(const char* msg, size_t len) {{"); writeln!(out, " Error error;"); - writeln!(out, " error.msg = static_cast(repr.ptr);"); - writeln!(out, " error.len = repr.len;"); - writeln!(out, " return error;"); + writeln!(out, " error.msg = msg;"); + writeln!(out, " error.len = len;"); + writeln!(out, " throw error;"); writeln!(out, " }}"); writeln!(out, "}};"); } @@ -395,6 +395,15 @@ pub(super) fn write(out: &mut OutFile) { } out.end_block(Block::AnonymousNamespace); + + if builtin.rust_error { + out.next_section(); + writeln!( + out, + "inline void (*throw_rust_error)(const char*, size_t) = impl::error;" + ); + } + out.end_block(Block::InlineNamespace("cxxbridge1")); if builtin.trycatch { diff --git a/gen/src/write.rs b/gen/src/write.rs index f7dcf9c0e..9619f5387 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1103,7 +1103,10 @@ fn write_rust_function_shim_impl( writeln!(out, ";"); out.builtin.rust_error = true; writeln!(out, " if (error$.ptr) {{"); - writeln!(out, " throw ::rust::impl<::rust::Error>::error(error$);"); + writeln!( + out, + " ::rust::throw_rust_error(static_cast(error$.ptr), error$.len);" + ); writeln!(out, " }}"); if indirect_return { diff --git a/include/cxx.h b/include/cxx.h index 739823cec..f0881b816 100644 --- a/include/cxx.h +++ b/include/cxx.h @@ -42,6 +42,10 @@ template class impl; } +// msg is 0-terminated, size counts the 0. +// msg was new char[] allocated, ownership is transferred to throw_rust_error. +extern void (*throw_rust_error)(const char* msg, size_t size); + #ifndef CXXBRIDGE1_RUST_STRING #define CXXBRIDGE1_RUST_STRING // https://cxx.rs/binding/string.html diff --git a/tests/ffi/tests.cc b/tests/ffi/tests.cc index a94a52e9c..1cc2ba6df 100644 --- a/tests/ffi/tests.cc +++ b/tests/ffi/tests.cc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -761,6 +762,20 @@ std::unique_ptr<::F::F> c_return_ns_opaque_ptr() { return f; } +struct TestException : public std::exception { + TestException(std::string&& msg) : message(std::move(msg)) {} + + static void do_throw(const char* msg, size_t len) { + throw TestException(std::string(msg, len)); + } + + const char* what() const noexcept override { + return message.c_str(); + } + + std::string message; +}; + extern "C" const char *cxx_run_test() noexcept { #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) @@ -987,6 +1002,17 @@ extern "C" const char *cxx_run_test() noexcept { ASSERT(std::strcmp(e.what(), "panic in cxx_test_suite::ffi::r_panic: foobar") == 0); } + // Test custom exception handler + auto prev_handler = ::rust::throw_rust_error; + ::rust::throw_rust_error = TestException::do_throw; + try { + r_fail_return_primitive(); + ASSERT(false); + } catch (const TestException &e) { + ASSERT(std::strcmp(e.what(), "rust error") == 0); + } + ::rust::throw_rust_error = prev_handler; + cxx_test_suite_set_correct(); return nullptr; }