Skip to content

Commit

Permalink
Add Rust panic to exception support
Browse files Browse the repository at this point in the history
  • Loading branch information
HKalbasi committed Sep 23, 2023
1 parent 73b8de8 commit 0bc780a
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 37 deletions.
7 changes: 7 additions & 0 deletions examples/simple/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ int main() {
Vec<int32_t>::push(s, 3);
// You can call Rust functions just like normal Rust.
std::cout << s.clone().into_iter().sum() << std::endl;
// You can catch Rust panics as C++ exceptions
try {
std::cout << "s[2] = " << *s.get(2).unwrap() << std::endl;
std::cout << "s[4] = " << *s.get(4).unwrap() << std::endl;
} catch (rust::Panic e) {
std::cout << "Rust panic happened" << std::endl;
}
int state = 0;
// You can convert a C++ lambda into a `Box<dyn Fn>` and friends.
auto f = BoxDyn<::rust::Fn<int32_t, int32_t>>::build([&](int32_t x) {
Expand Down
10 changes: 10 additions & 0 deletions examples/simple/main.zng
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#convert_panic_to_exception

type () {
layout(size = 0, align = 1);
wellknown_traits(Copy);
Expand All @@ -18,6 +20,13 @@ mod ::std {
fn unwrap(self) -> i32;
}

type option::Option<&i32> {
layout(size = 8, align = 8);
wellknown_traits(Copy);

fn unwrap(self) -> &i32;
}

type iter::Map<::std::vec::IntoIter<i32>, Box<dyn Fn(i32) -> i32>> {
layout(size = 48, align = 8);

Expand All @@ -40,6 +49,7 @@ mod ::std {
fn new() -> Vec<i32>;
fn push(&mut self, i32);
fn clone(&self) -> Vec<i32>;
fn get(&self, usize) -> ::std::option::Option<&i32> deref [i32];
fn into_iter(self) -> ::std::vec::IntoIter<i32>;
}
}
Expand Down
9 changes: 8 additions & 1 deletion zngur-def/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,17 @@ pub enum LayoutPolicy {
OnlyByRef,
}

pub struct ZngurMethodDetails {
pub data: ZngurMethod,
pub use_path: Option<Vec<String>>,
pub deref: Option<RustType>,
}

pub struct ZngurType {
pub ty: RustType,
pub layout: LayoutPolicy,
pub wellknown_traits: Vec<ZngurWellknownTrait>,
pub methods: Vec<(ZngurMethod, Option<Vec<String>>)>,
pub methods: Vec<ZngurMethodDetails>,
pub constructors: Vec<ZngurConstructor>,
pub cpp_value: Option<(String, String)>,
pub cpp_ref: Option<String>,
Expand All @@ -100,6 +106,7 @@ pub struct ZngurFile {
pub extern_cpp_funcs: Vec<ZngurExternCppFn>,
pub extern_cpp_impls: Vec<ZngurExternCppImpl>,
pub additional_includes: String,
pub convert_panic_to_exception: bool,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Expand Down
44 changes: 42 additions & 2 deletions zngur-generator/src/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,23 @@ impl From<&str> for CppType {

struct State {
text: String,
panic_to_exception: bool,
}

impl State {
fn panic_handler(&self) -> String {
if self.panic_to_exception {
r#"
if (__zngur_detect_panic()) {
__zngur_take_panic();
throw ::rust::Panic{};
}
"#
.to_owned()
} else {
"".to_owned()
}
}
}

impl Write for State {
Expand Down Expand Up @@ -242,6 +259,7 @@ impl CppFnSig {
{output} o;
::rust::__zngur_internal_assume_init(o);
{rust_link_name}({input_args}::rust::__zngur_internal_data_ptr(o));
{panic_handler}
{deinits}
return o;
}}",
Expand All @@ -253,6 +271,7 @@ impl CppFnSig {
input_args = (0..inputs.len())
.map(|n| format!("::rust::__zngur_internal_data_ptr(i{n}), "))
.join(""),
panic_handler = state.panic_handler(),
deinits = (0..inputs.len())
.map(|n| format!("::rust::__zngur_internal_assume_deinit(i{n});"))
.join("\n"),
Expand Down Expand Up @@ -403,7 +422,7 @@ impl CppTraitDefinition {
link_name: _,
link_name_ref: _,
} => {
for method in dbg!(methods) {
for method in methods {
write!(state, "void {}(uint8_t* data", method.rust_link_name)?;
for arg in 0..method.inputs.len() {
write!(state, ", uint8_t* i{arg}")?;
Expand Down Expand Up @@ -1178,6 +1197,7 @@ pub struct CppFile {
pub exported_fn_defs: Vec<CppExportedFnDefinition>,
pub exported_impls: Vec<CppExportedImplDefinition>,
pub additional_includes: String,
pub panic_to_exception: bool,
}

impl CppFile {
Expand All @@ -1192,7 +1212,19 @@ impl CppFile {
#include <iostream>
#include <functional>
#include <math.h>
"#;
if self.panic_to_exception {
state.text += r#"
namespace rust {
class Panic {};
}
extern "C" {
uint8_t __zngur_detect_panic();
void __zngur_take_panic();
}
"#;
}
state.text += r#"
#define zngur_dbg(x) \
{ \
::std::cerr << "[" << __FILE__ << ":" << __LINE__ << "] " << #x << " = "; \
Expand Down Expand Up @@ -1270,6 +1302,12 @@ namespace rust {
for ty in [8, 16, 32, 64]
.into_iter()
.flat_map(|x| [format!("int{x}_t"), format!("uint{x}_t")])
.chain([8, 16, 32, 64].into_iter().flat_map(|x| {
[
format!("::rust::Ref<int{x}_t>"),
format!("::rust::Ref<uint{x}_t>"),
]
}))
.chain([
format!("::rust::ZngurCppOpaqueOwnedObject"),
format!("::double_t"),
Expand Down Expand Up @@ -1455,9 +1493,11 @@ namespace rust {
pub fn render(self) -> (String, Option<String>) {
let mut h_file = State {
text: "".to_owned(),
panic_to_exception: self.panic_to_exception,
};
let mut cpp_file = State {
text: "".to_owned(),
panic_to_exception: self.panic_to_exception,
};
self.emit_h_file(&mut h_file).unwrap();
let mut is_cpp_needed = false;
Expand Down
25 changes: 20 additions & 5 deletions zngur-generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ impl ZngurGenerator {
.iter()
.map(|(key, value)| (key.clone(), rust_file.add_builder_for_dyn_trait(&value)))
.collect();
if zng.convert_panic_to_exception {
rust_file.enable_panic_to_exception();
cpp_file.panic_to_exception = true;
}
for ty_def in zng.types {
let is_copy = ty_def.wellknown_traits.contains(&ZngurWellknownTrait::Copy);
match ty_def.layout {
Expand Down Expand Up @@ -98,18 +102,24 @@ impl ZngurGenerator {
let data = rust_file.add_wellknown_trait(&ty_def.ty, wellknown_trait);
wellknown_traits.push(data);
}
for (method, use_path) in ty_def.methods {
for method_details in ty_def.methods {
let ZngurMethodDetails {
data: method,
use_path,
deref,
} = method_details;
let (rusty_inputs, inputs) = real_inputs_of_method(&method, &ty_def.ty);
let rust_link_name = rust_file.add_function(
&format!(
"<{}>::{}::<{}>",
ty_def.ty,
deref.as_ref().unwrap_or(&ty_def.ty),
method.name,
method.generics.iter().join(", "),
),
&rusty_inputs,
&method.output,
use_path,
deref.is_some(),
);
cpp_methods.push(CppMethod {
name: cpp_handle_keyword(&method.name).to_owned(),
Expand Down Expand Up @@ -171,8 +181,13 @@ impl ZngurGenerator {
});
}
for func in zng.funcs {
let rust_link_name =
rust_file.add_function(&func.path.to_string(), &func.inputs, &func.output, None);
let rust_link_name = rust_file.add_function(
&func.path.to_string(),
&func.inputs,
&func.output,
None,
false,
);
cpp_file.fn_defs.push(CppFnDefinition {
name: CppPath(func.path.path),
sig: CppFnSig {
Expand Down Expand Up @@ -222,7 +237,7 @@ impl ZngurGenerator {
});
}
let (h, cpp) = cpp_file.render();
(rust_file.0, h, cpp)
(rust_file.text, h, cpp)
}
}

Expand Down
90 changes: 71 additions & 19 deletions zngur-generator/src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,15 @@ impl IntoCpp for RustType {
}
}

pub struct RustFile(pub String);
pub struct RustFile {
pub text: String,
pub panic_to_exception: bool,
}

impl Default for RustFile {
fn default() -> Self {
Self(
r#"
Self {
text: r#"
#[allow(dead_code)]
mod zngur_types {
pub struct ZngurCppOpaqueBorrowedObject(());
Expand Down Expand Up @@ -168,13 +171,14 @@ pub use zngur_types::ZngurCppOpaqueOwnedObject;
pub use zngur_types::ZngurCppOpaqueBorrowedObject;
"#
.to_owned(),
)
panic_to_exception: false,
}
}
}

impl Write for RustFile {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.0.write_str(s)
self.text.write_str(s)
}
}

Expand Down Expand Up @@ -610,6 +614,7 @@ pub extern "C" fn {mangled_name}(d: *mut u8) -> *mut ZngurCppOpaqueOwnedObject {
inputs: &[RustType],
output: &RustType,
use_path: Option<Vec<String>>,
deref: bool,
) -> String {
let mangled_name = mangle_name(rust_name);
w!(
Expand All @@ -622,21 +627,27 @@ pub extern "C" fn {mangled_name}("#
w!(self, "i{n}: *mut u8, ");
}
wln!(self, "o: *mut u8) {{ unsafe {{");
if let Some(use_path) = use_path {
if use_path.first().is_some_and(|x| x == "crate") {
wln!(self, " use {};", use_path.iter().join("::"));
} else {
wln!(self, " use ::{};", use_path.iter().join("::"));
self.wrap_in_catch_unwind(|this| {
if let Some(use_path) = use_path {
if use_path.first().is_some_and(|x| x == "crate") {
wln!(this, " use {};", use_path.iter().join("::"));
} else {
wln!(this, " use ::{};", use_path.iter().join("::"));
}
}
}
w!(
self,
" ::std::ptr::write(o as *mut {output}, {rust_name}("
);
for (n, ty) in inputs.iter().enumerate() {
w!(self, "::std::ptr::read(i{n} as *mut {ty}), ");
}
wln!(self, ")) }} }}");
w!(
this,
" ::std::ptr::write(o as *mut {output}, {rust_name}("
);
if deref {
w!(this, "&");
}
for (n, ty) in inputs.iter().enumerate() {
w!(this, "::std::ptr::read(i{n} as *mut {ty}), ");
}
wln!(this, "));");
});
wln!(self, " }} }}");
mangled_name
}

Expand Down Expand Up @@ -686,4 +697,45 @@ pub extern "C" fn {debug_print}(v: *mut u8) {{
}
}
}

pub(crate) fn enable_panic_to_exception(&mut self) {
wln!(
self,
r#"thread_local! {{
pub static PANIC_PAYLOAD: ::std::cell::Cell<Option<()>> = ::std::cell::Cell::new(None);
}}
#[no_mangle]
pub fn __zngur_detect_panic() -> u8 {{
PANIC_PAYLOAD.with(|p| {{
let pp = p.take();
let r = if pp.is_some() {{ 1 }} else {{ 0 }};
p.set(pp);
r
}})
}}
#[no_mangle]
pub fn __zngur_take_panic() {{
PANIC_PAYLOAD.with(|p| {{
p.take();
}})
}}
"#
);
self.panic_to_exception = true;
}

fn wrap_in_catch_unwind(&mut self, f: impl FnOnce(&mut RustFile)) {
if !self.panic_to_exception {
f(self);
} else {
wln!(self, "let e = ::std::panic::catch_unwind(|| {{");
f(self);
wln!(self, "}});");
wln!(
self,
"if let Err(_) = e {{ PANIC_PAYLOAD.with(|p| p.set(Some(()))) }}"
);
}
}
}
Loading

0 comments on commit 0bc780a

Please sign in to comment.