neon/sys/call.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
use std::{mem::MaybeUninit, ptr::null_mut};
use smallvec::SmallVec;
use super::{
bindings as napi,
raw::{Env, FunctionCallbackInfo, Local},
};
// Number of arguments to allocate on the stack. This should be large enough
// to cover most use cases without being wasteful.
//
// * If the number is too large, too much space is allocated and then filled
// with `undefined`.
// * If the number is too small, getting arguments frequently takes two tries
// and requires heap allocation.
const ARGV_SIZE: usize = 4;
#[repr(transparent)]
/// List of JavaScript arguments to a function
// `Arguments` is intended to be a small abstraction to hide the usage of
// `SmallVec` allowing changes to `ARGV_SIZE` in a single location
pub struct Arguments(SmallVec<[Local; ARGV_SIZE]>);
impl Arguments {
#[inline]
/// Get an argument at a specific position
pub fn get(&self, i: usize) -> Option<Local> {
self.0.get(i).cloned()
}
}
pub unsafe fn is_construct(env: Env, info: FunctionCallbackInfo) -> bool {
let mut target: MaybeUninit<Local> = MaybeUninit::zeroed();
napi::get_new_target(env, info, target.as_mut_ptr()).unwrap();
// get_new_target is guaranteed to assign to target, so it's initialized.
let target: Local = target.assume_init();
// By the get_new_target contract, target will either be NULL if the current
// function was called without `new`, or a valid napi_value handle if the current
// function was called with `new`.
!target.is_null()
}
pub unsafe fn this(env: Env, info: FunctionCallbackInfo, out: &mut Local) {
napi::get_cb_info(env, info, null_mut(), null_mut(), out as *mut _, null_mut()).unwrap();
}
/// Gets the number of arguments passed to the function.
// TODO: Remove this when `FunctionContext` is refactored to get call info upfront.
pub unsafe fn len(env: Env, info: FunctionCallbackInfo) -> usize {
let mut argc = 0usize;
napi::get_cb_info(
env,
info,
&mut argc as *mut _,
null_mut(),
null_mut(),
null_mut(),
)
.unwrap();
argc
}
/// Returns the function arguments for a call
pub unsafe fn argv(env: Env, info: FunctionCallbackInfo) -> Arguments {
// Allocate space on the stack for up to `ARGV_SIZE` values
let mut argv = MaybeUninit::<[Local; ARGV_SIZE]>::uninit();
// Starts as the size allocated; after `get_cb_info` it is the number of arguments
let mut argc = ARGV_SIZE;
napi::get_cb_info(
env,
info,
&mut argc as *mut _,
argv.as_mut_ptr().cast(),
null_mut(),
null_mut(),
)
.unwrap();
// We did not allocate enough space; allocate on the heap and try again
let argv = if argc > ARGV_SIZE {
// We know exactly how much space to reserve
let mut argv = Vec::with_capacity(argc);
napi::get_cb_info(
env,
info,
&mut argc as *mut _,
argv.as_mut_ptr(),
null_mut(),
null_mut(),
)
.unwrap();
// Set the size of `argv` to the number of initialized elements
argv.set_len(argc);
SmallVec::from_vec(argv)
// There were `ARGV_SIZE` or fewer arguments, use the stack allocated space
} else {
SmallVec::from_buf_and_len(argv.assume_init(), argc)
};
Arguments(argv)
}