Skip to main content

deno_napi/
util.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2use libc::INT_MAX;
3
4use crate::*;
5
6#[repr(transparent)]
7pub(crate) struct SendPtr<T>(pub *const T);
8
9impl<T> SendPtr<T> {
10  // silly function to get around `clippy::redundant_locals`
11  pub fn take(self) -> *const T {
12    self.0
13  }
14}
15
16unsafe impl<T> Send for SendPtr<T> {}
17unsafe impl<T> Sync for SendPtr<T> {}
18
19pub fn get_array_buffer_ptr(ab: v8::Local<v8::ArrayBuffer>) -> *mut c_void {
20  match ab.data() {
21    Some(p) => p.as_ptr(),
22    None => std::ptr::null_mut(),
23  }
24}
25
26struct BufferFinalizer {
27  env: *mut Env,
28  finalize_cb: Option<napi_finalize>,
29  finalize_data: *mut c_void,
30  finalize_hint: *mut c_void,
31}
32
33impl Drop for BufferFinalizer {
34  fn drop(&mut self) {
35    if let Some(finalize_cb) = self.finalize_cb {
36      unsafe {
37        finalize_cb(self.env as _, self.finalize_data, self.finalize_hint);
38      }
39    }
40  }
41}
42
43pub(crate) extern "C" fn backing_store_deleter_callback(
44  data: *mut c_void,
45  _byte_length: usize,
46  deleter_data: *mut c_void,
47) {
48  let mut finalizer =
49    unsafe { Box::<BufferFinalizer>::from_raw(deleter_data as _) };
50
51  finalizer.finalize_data = data;
52
53  drop(finalizer);
54}
55
56pub(crate) fn make_external_backing_store(
57  env: *mut Env,
58  data: *mut c_void,
59  byte_length: usize,
60  finalize_data: *mut c_void,
61  finalize_cb: Option<napi_finalize>,
62  finalize_hint: *mut c_void,
63) -> v8::UniqueRef<v8::BackingStore> {
64  let finalizer = Box::new(BufferFinalizer {
65    env,
66    finalize_data,
67    finalize_cb,
68    finalize_hint,
69  });
70
71  unsafe {
72    v8::ArrayBuffer::new_backing_store_from_ptr(
73      data,
74      byte_length,
75      backing_store_deleter_callback,
76      Box::into_raw(finalizer) as _,
77    )
78  }
79}
80
81#[macro_export]
82macro_rules! check_env {
83  ($env: expr) => {{
84    let env = $env;
85    if env.is_null() {
86      return napi_invalid_arg;
87    }
88    unsafe { &mut *env }
89  }};
90}
91
92#[macro_export]
93macro_rules! return_error_status_if_false {
94  ($env: expr, $condition: expr, $status: ident) => {
95    if !$condition {
96      return Err($crate::util::napi_set_last_error($env, $status).into());
97    }
98  };
99}
100
101#[macro_export]
102macro_rules! return_status_if_false {
103  ($env: expr, $condition: expr, $status: ident) => {
104    if !$condition {
105      return $crate::util::napi_set_last_error($env, $status);
106    }
107  };
108}
109
110pub(crate) unsafe fn check_new_from_utf8_len<'s>(
111  env: *mut Env,
112  str_: *const c_char,
113  len: usize,
114) -> Result<v8::Local<'s, v8::String>, napi_status> {
115  let env = unsafe { &mut *env };
116  return_error_status_if_false!(
117    env,
118    (len == NAPI_AUTO_LENGTH) || len <= INT_MAX as _,
119    napi_invalid_arg
120  );
121  return_error_status_if_false!(env, !str_.is_null(), napi_invalid_arg);
122  let string = if len == NAPI_AUTO_LENGTH {
123    unsafe { std::ffi::CStr::from_ptr(str_ as *const _) }.to_bytes()
124  } else {
125    unsafe { std::slice::from_raw_parts(str_ as *const u8, len) }
126  };
127  let result = {
128    let env = unsafe { &mut *(env as *mut Env) };
129    v8::callback_scope!(unsafe scope, env.context());
130    v8::String::new_from_utf8(scope, string, v8::NewStringType::Internalized)
131  };
132  return_error_status_if_false!(env, result.is_some(), napi_generic_failure);
133  Ok(result.unwrap())
134}
135
136#[inline]
137pub(crate) unsafe fn check_new_from_utf8<'s>(
138  env: *mut Env,
139  str_: *const c_char,
140) -> Result<v8::Local<'s, v8::String>, napi_status> {
141  unsafe { check_new_from_utf8_len(env, str_, NAPI_AUTO_LENGTH) }
142}
143
144pub(crate) unsafe fn v8_name_from_property_descriptor<'s>(
145  env: *mut Env,
146  p: &'s napi_property_descriptor,
147) -> Result<v8::Local<'s, v8::Name>, napi_status> {
148  if !p.utf8name.is_null() {
149    unsafe { check_new_from_utf8(env, p.utf8name).map(|v| v.into()) }
150  } else {
151    match *p.name {
152      Some(v) => match v.try_into() {
153        Ok(name) => Ok(name),
154        Err(_) => Err(napi_name_expected),
155      },
156      None => Err(napi_name_expected),
157    }
158  }
159}
160
161pub(crate) fn napi_clear_last_error(env: *mut Env) -> napi_status {
162  let env = unsafe { &mut *env };
163  env.last_error.error_code.set(napi_ok);
164  env.last_error.engine_error_code = 0;
165  env.last_error.engine_reserved = std::ptr::null_mut();
166  env.last_error.error_message = std::ptr::null_mut();
167  napi_ok
168}
169
170pub(crate) fn napi_set_last_error(
171  env: *const Env,
172  error_code: napi_status,
173) -> napi_status {
174  let env = unsafe { &*env };
175  env.last_error.error_code.set(error_code);
176  error_code
177}
178
179#[macro_export]
180macro_rules! status_call {
181  ($call: expr) => {
182    let status = $call;
183    if status != napi_ok {
184      return status;
185    }
186  };
187}
188
189pub trait Nullable {
190  fn is_null(&self) -> bool;
191}
192
193impl<T> Nullable for *mut T {
194  fn is_null(&self) -> bool {
195    (*self).is_null()
196  }
197}
198
199impl<T> Nullable for *const T {
200  fn is_null(&self) -> bool {
201    (*self).is_null()
202  }
203}
204
205impl<T> Nullable for Option<T> {
206  fn is_null(&self) -> bool {
207    self.is_none()
208  }
209}
210
211impl Nullable for napi_value<'_> {
212  fn is_null(&self) -> bool {
213    self.is_none()
214  }
215}
216
217#[macro_export]
218macro_rules! check_arg {
219  ($env: expr, $ptr: expr) => {
220    $crate::return_status_if_false!(
221      $env,
222      !$crate::util::Nullable::is_null(&$ptr),
223      napi_invalid_arg
224    );
225  };
226}
227
228#[macro_export]
229macro_rules! napi_wrap {
230  ( $( # [ $attr:meta ] )* $vis:vis fn $name:ident $( < $( $x:lifetime ),* > )? ( $env:ident : & $( $lt:lifetime )? mut Env $( , $ident:ident : $ty:ty )* $(,)? ) -> napi_status $body:block ) => {
231    $( # [ $attr ] )*
232    #[unsafe(no_mangle)]
233    $vis unsafe extern "C" fn $name $( < $( $x ),* > )? ( env_ptr : *mut Env , $( $ident : $ty ),* ) -> napi_status {
234      let env: & $( $lt )? mut Env = $crate::check_env!(env_ptr);
235
236      if env.last_exception.is_some() {
237        return napi_pending_exception;
238      }
239
240      $crate::util::napi_clear_last_error(env);
241
242      let scope_env = unsafe { &mut *env_ptr };
243      deno_core::v8::callback_scope!(unsafe scope, scope_env.context());
244      deno_core::v8::tc_scope!(try_catch, scope);
245
246      #[inline(always)]
247      fn inner $( < $( $x ),* > )? ( $env: & $( $lt )? mut Env , $( $ident : $ty ),* ) -> napi_status $body
248
249      #[cfg(debug_assertions)]
250      log::trace!("NAPI ENTER: {}", stringify!($name));
251
252      let result = inner( env, $( $ident ),* );
253
254      #[cfg(debug_assertions)]
255      log::trace!("NAPI EXIT: {} {}", stringify!($name), result);
256
257      if let Some(exception) = try_catch.exception() {
258        let env = unsafe { &mut *env_ptr };
259        let global = v8::Global::new(env.isolate(), exception);
260        env.last_exception = Some(global);
261        return $crate::util::napi_set_last_error(env_ptr, napi_pending_exception);
262      }
263
264      if result != napi_ok {
265        return $crate::util::napi_set_last_error(env_ptr, result);
266      }
267
268      return result;
269    }
270  };
271
272  ( $( # [ $attr:meta ] )* $vis:vis fn $name:ident $( < $( $x:lifetime ),* > )? ( $( $ident:ident : $ty:ty ),* $(,)? ) -> napi_status $body:block ) => {
273    $( # [ $attr ] )*
274    #[unsafe(no_mangle)]
275    $vis unsafe extern "C" fn $name $( < $( $x ),* > )? ( $( $ident : $ty ),* ) -> napi_status {
276      #[inline(always)]
277      fn inner $( < $( $x ),* > )? ( $( $ident : $ty ),* ) -> napi_status $body
278
279      #[cfg(debug_assertions)]
280      log::trace!("NAPI ENTER: {}", stringify!($name));
281
282      let result = inner( $( $ident ),* );
283
284      #[cfg(debug_assertions)]
285      log::trace!("NAPI EXIT: {} {}", stringify!($name), result);
286
287      result
288    }
289  };
290}