summaryrefslogtreecommitdiff
path: root/cli/napi/util.rs
diff options
context:
space:
mode:
authorsnek <the@snek.dev>2024-06-10 09:20:44 -0700
committerGitHub <noreply@github.com>2024-06-10 16:20:44 +0000
commite3b2ee183bc7497ec0432bc764678f5eda6495a7 (patch)
tree7a5fa0208ef56cb83fa6bae9bad0bc89334ed533 /cli/napi/util.rs
parent7c5dbd5d54770dba5e56442b633e9597403ef5da (diff)
fix: Rewrite Node-API (#24101)
Phase 1 node-api rewrite
Diffstat (limited to 'cli/napi/util.rs')
-rw-r--r--cli/napi/util.rs284
1 files changed, 284 insertions, 0 deletions
diff --git a/cli/napi/util.rs b/cli/napi/util.rs
index edf109460..cd3ef14a2 100644
--- a/cli/napi/util.rs
+++ b/cli/napi/util.rs
@@ -1,8 +1,292 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_runtime::deno_napi::*;
+use libc::INT_MAX;
+
+#[repr(transparent)]
+pub struct SendPtr<T>(pub *const T);
+
+impl<T> SendPtr<T> {
+ // silly function to get around `clippy::redundant_locals`
+ pub fn take(self) -> *const T {
+ self.0
+ }
+}
+
+unsafe impl<T> Send for SendPtr<T> {}
+unsafe impl<T> Sync for SendPtr<T> {}
pub fn get_array_buffer_ptr(ab: v8::Local<v8::ArrayBuffer>) -> *mut u8 {
// SAFETY: Thanks to the null pointer optimization, NonNull<T> and Option<NonNull<T>> are guaranteed
// to have the same size and alignment.
unsafe { std::mem::transmute(ab.data()) }
}
+
+struct BufferFinalizer {
+ env: *mut Env,
+ finalize_cb: napi_finalize,
+ finalize_data: *mut c_void,
+ finalize_hint: *mut c_void,
+}
+
+impl Drop for BufferFinalizer {
+ fn drop(&mut self) {
+ unsafe {
+ (self.finalize_cb)(self.env as _, self.finalize_data, self.finalize_hint);
+ }
+ }
+}
+
+pub extern "C" fn backing_store_deleter_callback(
+ data: *mut c_void,
+ _byte_length: usize,
+ deleter_data: *mut c_void,
+) {
+ let mut finalizer =
+ unsafe { Box::<BufferFinalizer>::from_raw(deleter_data as _) };
+
+ finalizer.finalize_data = data;
+
+ drop(finalizer);
+}
+
+pub fn make_external_backing_store(
+ env: *mut Env,
+ data: *mut c_void,
+ byte_length: usize,
+ finalize_data: *mut c_void,
+ finalize_cb: napi_finalize,
+ finalize_hint: *mut c_void,
+) -> v8::UniqueRef<v8::BackingStore> {
+ let finalizer = Box::new(BufferFinalizer {
+ env,
+ finalize_data,
+ finalize_cb,
+ finalize_hint,
+ });
+
+ unsafe {
+ v8::ArrayBuffer::new_backing_store_from_ptr(
+ data,
+ byte_length,
+ backing_store_deleter_callback,
+ Box::into_raw(finalizer) as _,
+ )
+ }
+}
+
+#[macro_export]
+macro_rules! check_env {
+ ($env: expr) => {{
+ let env = $env;
+ if env.is_null() {
+ return napi_invalid_arg;
+ }
+ unsafe { &mut *env }
+ }};
+}
+
+#[macro_export]
+macro_rules! return_error_status_if_false {
+ ($env: expr, $condition: expr, $status: ident) => {
+ if !$condition {
+ return Err(
+ $crate::napi::util::napi_set_last_error($env, $status).into(),
+ );
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! return_status_if_false {
+ ($env: expr, $condition: expr, $status: ident) => {
+ if !$condition {
+ return $crate::napi::util::napi_set_last_error($env, $status);
+ }
+ };
+}
+
+pub(crate) unsafe fn check_new_from_utf8_len<'s>(
+ env: *mut Env,
+ str_: *const c_char,
+ len: usize,
+) -> Result<v8::Local<'s, v8::String>, napi_status> {
+ let env = unsafe { &mut *env };
+ return_error_status_if_false!(
+ env,
+ (len == NAPI_AUTO_LENGTH) || len <= INT_MAX as _,
+ napi_invalid_arg
+ );
+ return_error_status_if_false!(env, !str_.is_null(), napi_invalid_arg);
+ let string = if len == NAPI_AUTO_LENGTH {
+ let result = unsafe { std::ffi::CStr::from_ptr(str_ as *const _) }.to_str();
+ return_error_status_if_false!(env, result.is_ok(), napi_generic_failure);
+ result.unwrap()
+ } else {
+ let string = unsafe { std::slice::from_raw_parts(str_ as *const u8, len) };
+ let result = std::str::from_utf8(string);
+ return_error_status_if_false!(env, result.is_ok(), napi_generic_failure);
+ result.unwrap()
+ };
+ let result = {
+ let env = unsafe { &mut *(env as *mut Env) };
+ v8::String::new(&mut env.scope(), string)
+ };
+ return_error_status_if_false!(env, result.is_some(), napi_generic_failure);
+ Ok(result.unwrap())
+}
+
+#[inline]
+pub(crate) unsafe fn check_new_from_utf8<'s>(
+ env: *mut Env,
+ str_: *const c_char,
+) -> Result<v8::Local<'s, v8::String>, napi_status> {
+ unsafe { check_new_from_utf8_len(env, str_, NAPI_AUTO_LENGTH) }
+}
+
+pub(crate) unsafe fn v8_name_from_property_descriptor<'s>(
+ env: *mut Env,
+ p: &'s napi_property_descriptor,
+) -> Result<v8::Local<'s, v8::Name>, napi_status> {
+ if !p.utf8name.is_null() {
+ unsafe { check_new_from_utf8(env, p.utf8name).map(|v| v.into()) }
+ } else {
+ match *p.name {
+ Some(v) => match v.try_into() {
+ Ok(name) => Ok(name),
+ Err(_) => Err(napi_name_expected),
+ },
+ None => Err(napi_name_expected),
+ }
+ }
+}
+
+pub(crate) fn napi_clear_last_error(env: *mut Env) -> napi_status {
+ let env = unsafe { &mut *env };
+ env.last_error.error_code = napi_ok;
+ env.last_error.engine_error_code = 0;
+ env.last_error.engine_reserved = std::ptr::null_mut();
+ env.last_error.error_message = std::ptr::null_mut();
+ napi_ok
+}
+
+pub(crate) fn napi_set_last_error(
+ env: *mut Env,
+ error_code: napi_status,
+) -> napi_status {
+ let env = unsafe { &mut *env };
+ env.last_error.error_code = error_code;
+ error_code
+}
+
+#[macro_export]
+macro_rules! status_call {
+ ($call: expr) => {
+ let status = $call;
+ if status != napi_ok {
+ return status;
+ }
+ };
+}
+
+pub trait Nullable {
+ fn is_null(&self) -> bool;
+}
+
+impl<T> Nullable for *mut T {
+ fn is_null(&self) -> bool {
+ (*self).is_null()
+ }
+}
+
+impl<T> Nullable for *const T {
+ fn is_null(&self) -> bool {
+ (*self).is_null()
+ }
+}
+
+impl<T> Nullable for Option<T> {
+ fn is_null(&self) -> bool {
+ self.is_none()
+ }
+}
+
+impl<'s> Nullable for napi_value<'s> {
+ fn is_null(&self) -> bool {
+ self.is_none()
+ }
+}
+
+// TODO: replace Nullable with some sort of "CheckedUnwrap" trait
+// *mut T -> &mut MaybeUninit<T>
+// Option<T> -> T
+// napi_value -> Local<Value>
+#[macro_export]
+macro_rules! check_arg {
+ ($env: expr, $ptr: expr) => {
+ $crate::return_status_if_false!(
+ $env,
+ !$crate::napi::util::Nullable::is_null(&$ptr),
+ napi_invalid_arg
+ );
+ };
+}
+
+#[macro_export]
+macro_rules! napi_wrap {
+ ( $( # $attr:tt )* fn $name:ident $( < $( $x:lifetime ),* > )? ( $env:ident : & $( $lt:lifetime )? mut Env $( , $ident:ident : $ty:ty )* $(,)? ) -> napi_status $body:block ) => {
+ $( # $attr )*
+ #[no_mangle]
+ pub unsafe extern "C" fn $name $( < $( $x ),* > )? ( env_ptr : *mut Env , $( $ident : $ty ),* ) -> napi_status {
+ let env: & $( $lt )? mut Env = $crate::check_env!(env_ptr);
+
+ if env.last_exception.is_some() {
+ return napi_pending_exception;
+ }
+
+ $crate::napi::util::napi_clear_last_error(env);
+
+ let scope_env = unsafe { &mut *env_ptr };
+ let scope = &mut scope_env.scope();
+ let try_catch = &mut v8::TryCatch::new(scope);
+
+ #[inline(always)]
+ fn inner $( < $( $x ),* > )? ( $env: & $( $lt )? mut Env , $( $ident : $ty ),* ) -> napi_status $body
+
+ log::trace!("NAPI ENTER: {}", stringify!($name));
+
+ let result = inner( env, $( $ident ),* );
+
+ log::trace!("NAPI EXIT: {} {}", stringify!($name), result);
+
+ if let Some(exception) = try_catch.exception() {
+ let env = unsafe { &mut *env_ptr };
+ let global = v8::Global::new(env.isolate(), exception);
+ env.last_exception = Some(global);
+ return $crate::napi::util::napi_set_last_error(env_ptr, napi_pending_exception);
+ }
+
+ if result != napi_ok {
+ return $crate::napi::util::napi_set_last_error(env_ptr, result);
+ }
+
+ return result;
+ }
+ };
+
+ ( $( # $attr:tt )* fn $name:ident $( < $( $x:lifetime ),* > )? ( $( $ident:ident : $ty:ty ),* $(,)? ) -> napi_status $body:block ) => {
+ $( # $attr )*
+ #[no_mangle]
+ pub unsafe extern "C" fn $name $( < $( $x ),* > )? ( $( $ident : $ty ),* ) -> napi_status {
+ #[inline(always)]
+ fn inner $( < $( $x ),* > )? ( $( $ident : $ty ),* ) -> napi_status $body
+
+ log::trace!("NAPI ENTER: {}", stringify!($name));
+
+ let result = inner( $( $ident ),* );
+
+ log::trace!("NAPI EXIT: {} {}", stringify!($name), result);
+
+ result
+ }
+ };
+}