Skip to main content

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}