@ 261027.7 · fetching
current
//! Only original N-API functions are used here. No generated napi-rs module
//! initialization or newer path-discovery API is linked into the carrier.

use std::ffi::{c_char, c_void, CString};

use swc_native_addon::{Error, ErrorKind, Result};

pub type Env = *mut c_void;
pub type Value = *mut c_void;
pub type Register = unsafe extern "C" fn(Env, Value) -> Value;
type ThrowError = unsafe extern "C" fn(Env, *const c_char, *const c_char) -> i32;
type FatalError = unsafe extern "C" fn(*const c_char, usize, *const c_char, usize);

pub struct Api {
    throw_error: ThrowError,
    fatal_error: FatalError,
}

impl Api {
    pub unsafe fn load() -> Result<Self> {
        #[cfg(unix)]
        let process = libloading::os::unix::Library::this();
        #[cfg(windows)]
        let process = libloading::os::windows::Library::this()
            .map_err(|e| Error::new(ErrorKind::Load, format!("open Node process symbols: {e}")))?;
        let throw_error = *process
            .get::<ThrowError>(b"napi_throw_error\0")
            .map_err(|e| {
                Error::new(
                    ErrorKind::Load,
                    format!("Node must expose napi_throw_error: {e}"),
                )
            })?;
        let fatal_error = *process
            .get::<FatalError>(b"napi_fatal_error\0")
            .map_err(|e| {
                Error::new(
                    ErrorKind::Load,
                    format!("Node must expose napi_fatal_error: {e}"),
                )
            })?;
        Ok(Self {
            throw_error,
            fatal_error,
        })
    }

    pub unsafe fn throw(&self, env: Env, error: &Error) -> Value {
        let code = CString::new(error.kind.code()).unwrap();
        let message = format!(
            "SWC native addon: {error}. Reinstall the native package if its payload is damaged; \
             check SWC_NATIVE_BINDING_CACHE and directory permissions for materialization \
             failures."
        );
        let message = CString::new(message.replace('\0', "?")).unwrap();
        if (self.throw_error)(env, code.as_ptr(), message.as_ptr()) != 0 {
            // A functioning Node host always provides the original throw API.
            // If it cannot create an exception, returning exports would falsely
            // report success. N-API's original fatal API is the last resort.
            let location = b"swc native loader";
            (self.fatal_error)(
                location.as_ptr().cast(),
                location.len(),
                message.as_ptr(),
                message.as_bytes().len(),
            );
        }
        std::ptr::null_mut()
    }
}