wolfram_export/native/macro_utils.rs
1//! Native-mode runtime: the C-ABI dispatcher the `#[export]` macro calls into
2//! for `MArgument`-based functions, plus the `#[init]` helper.
3//!
4//! Types and helpers (`NativeFunction`, `initialize`, `call_and_catch_panic`)
5//! are imported from `wolfram-library-link`; the dispatcher logic itself is
6//! owned here so the macro emission paths under `wolfram_export::macro_utils::*`
7//! resolve without going back through `wolfram-library-link`.
8
9use std::os::raw::c_int;
10use std::panic::AssertUnwindSafe;
11
12use wolfram_library_link::call_and_catch_panic;
13use wolfram_library_link::sys::{self, MArgument};
14use wolfram_library_link::NativeFunction;
15
16/// Returned when [`wolfram_library_link::initialize`] fails on entry.
17/// Returned when the wrapped Rust code panicked.
18
19/// Bridge a native `#[export]`-marked function across the LibraryLink C ABI.
20///
21/// 1. Calls `wolfram_library_link::initialize(lib_data)`.
22/// 2. Slices `argc` raw `MArgument`s, hands them to the user's
23/// `NativeFunction` impl (which performs `FromArg`/`IntoArg` conversions),
24/// catching any panic.
25pub unsafe fn call_native_wolfram_library_function<'a, F: NativeFunction<'a>>(
26 lib_data: sys::WolframLibraryData,
27 args: *mut MArgument,
28 argc: sys::mint,
29 res: MArgument,
30 func: F,
31) -> c_int {
32 if wolfram_library_link::initialize(lib_data).is_err() {
33 return wolfram_library_link::FAILED_TO_INIT;
34 }
35
36 let argc = match usize::try_from(argc) {
37 Ok(argc) => argc,
38 Err(_) => return wolfram_library_link::sys::LIBRARY_FUNCTION_ERROR as c_int,
39 };
40
41 // FIXME: This isn't safe! 'a could be 'static, and then the user could store the
42 // `&mut Link` reference beyond the lifetime of this function.
43 // E.g. `fn foo(link: &'static mut str) { ... }`
44 let args: &[MArgument] = std::slice::from_raw_parts(args, argc);
45
46 if call_and_catch_panic(AssertUnwindSafe(move || func.call(args, res))).is_err() {
47 return wolfram_library_link::FAILED_WITH_PANIC;
48 }
49
50 sys::LIBRARY_NO_ERROR as c_int
51}
52
53/// Bridge an `#[init]`-marked function: runs `initialize` then the user's
54/// init body inside a panic guard.
55pub unsafe fn init_with_user_function(
56 lib: sys::WolframLibraryData,
57 user_init_func: fn(),
58) -> c_int {
59 if wolfram_library_link::initialize(lib).is_err() {
60 return wolfram_library_link::FAILED_TO_INIT;
61 }
62
63 if call_and_catch_panic(user_init_func).is_err() {
64 wolfram_library_link::FAILED_WITH_PANIC
65 } else {
66 sys::LIBRARY_NO_ERROR as c_int
67 }
68}