pub(crate) mod internal;
use std::{
convert::Into,
marker::PhantomData,
ops::{Deref, DerefMut},
panic::UnwindSafe,
};
pub use crate::types::buffer::lock::Lock;
use crate::{
event::TaskBuilder,
handle::Handle,
object::Object,
result::{JsResult, NeonResult, Throw},
sys::{
self, raw,
scope::{EscapableHandleScope, HandleScope},
},
types::{
boxed::{Finalize, JsBox},
error::JsError,
extract::{FromArgs, TryFromJs},
private::ValueInternal,
Deferred, JsArray, JsArrayBuffer, JsBoolean, JsBuffer, JsFunction, JsNull, JsNumber,
JsObject, JsPromise, JsString, JsUndefined, JsValue, StringResult, Value,
},
};
use self::internal::{ContextInternal, Env};
#[cfg(feature = "napi-4")]
use crate::event::Channel;
#[cfg(feature = "napi-5")]
use crate::types::date::{DateError, JsDate};
#[cfg(feature = "napi-6")]
use crate::lifecycle::InstanceData;
#[doc(hidden)]
pub type TaskContext<'cx> = Cx<'cx>;
#[doc(hidden)]
pub type ExecuteContext<'cx> = Cx<'cx>;
#[doc(hidden)]
pub type ComputeContext<'cx> = Cx<'cx>;
#[doc(hidden)]
pub type FinalizeContext<'cx> = Cx<'cx>;
#[cfg(feature = "sys")]
#[cfg_attr(docsrs, doc(cfg(feature = "sys")))]
#[doc(hidden)]
pub type SysContext<'cx> = Cx<'cx>;
pub struct Cx<'cx> {
env: Env,
_phantom_inner: PhantomData<&'cx ()>,
}
impl<'cx> Cx<'cx> {
#[cfg(feature = "sys")]
#[cfg_attr(docsrs, doc(cfg(feature = "sys")))]
pub unsafe fn from_raw(env: sys::Env) -> Self {
Self {
env: env.into(),
_phantom_inner: PhantomData,
}
}
fn new(env: Env) -> Self {
Self {
env,
_phantom_inner: PhantomData,
}
}
pub(crate) fn with_context<T, F: for<'b> FnOnce(Cx<'b>) -> T>(env: Env, f: F) -> T {
f(Self {
env,
_phantom_inner: PhantomData,
})
}
}
impl<'cx> ContextInternal<'cx> for Cx<'cx> {
fn cx(&self) -> &Cx<'cx> {
self
}
fn cx_mut(&mut self) -> &mut Cx<'cx> {
self
}
}
impl<'cx> Context<'cx> for Cx<'cx> {}
impl<'cx> From<FunctionContext<'cx>> for Cx<'cx> {
fn from(cx: FunctionContext<'cx>) -> Self {
cx.cx
}
}
impl<'cx> From<ModuleContext<'cx>> for Cx<'cx> {
fn from(cx: ModuleContext<'cx>) -> Self {
cx.cx
}
}
#[repr(C)]
pub(crate) struct CallbackInfo<'cx> {
info: raw::FunctionCallbackInfo,
_lifetime: PhantomData<&'cx raw::FunctionCallbackInfo>,
}
impl CallbackInfo<'_> {
pub unsafe fn new(info: raw::FunctionCallbackInfo) -> Self {
Self {
info,
_lifetime: PhantomData,
}
}
fn kind<'b, C: Context<'b>>(&self, cx: &C) -> CallKind {
if unsafe { sys::call::is_construct(cx.env().to_raw(), self.info) } {
CallKind::Construct
} else {
CallKind::Call
}
}
pub fn len<'b, C: Context<'b>>(&self, cx: &C) -> usize {
unsafe { sys::call::len(cx.env().to_raw(), self.info) }
}
pub fn argv<'b, C: Context<'b>>(&self, cx: &mut C) -> sys::call::Arguments {
unsafe { sys::call::argv(cx.env().to_raw(), self.info) }
}
pub fn this<'b, C: Context<'b>>(&self, cx: &mut C) -> raw::Local {
let env = cx.env();
unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::call::this(env.to_raw(), self.info, &mut local);
local
}
}
pub(crate) fn argv_exact<'b, C: Context<'b>, const N: usize>(
&self,
cx: &mut C,
) -> [Handle<'b, JsValue>; N] {
use std::ptr;
let mut argv = [JsValue::new_internal(ptr::null_mut()); N];
let mut argc = argv.len();
unsafe {
sys::get_cb_info(
cx.env().to_raw(),
self.info,
&mut argc,
argv.as_mut_ptr().cast(),
ptr::null_mut(),
ptr::null_mut(),
)
.unwrap();
}
argv
}
}
#[derive(Clone, Copy, Debug)]
pub enum CallKind {
Construct,
Call,
}
pub trait Context<'a>: ContextInternal<'a> {
fn lock<'b>(&'b mut self) -> Lock<'b, Self>
where
'a: 'b,
{
Lock::new(self)
}
fn execute_scoped<'b, T, F>(&mut self, f: F) -> T
where
'a: 'b,
F: FnOnce(Cx<'b>) -> T,
{
let env = self.env();
let scope = unsafe { HandleScope::new(env.to_raw()) };
let result = f(Cx::new(env));
drop(scope);
result
}
fn compute_scoped<'b, V, F>(&mut self, f: F) -> JsResult<'a, V>
where
'a: 'b,
V: Value,
F: FnOnce(Cx<'b>) -> JsResult<'b, V>,
{
let env = self.env();
let scope = unsafe { EscapableHandleScope::new(env.to_raw()) };
let cx = Cx::new(env);
let escapee = unsafe { scope.escape(f(cx)?.to_local()) };
Ok(Handle::new_internal(unsafe {
V::from_local(self.env(), escapee)
}))
}
fn try_catch<T, F>(&mut self, f: F) -> Result<T, Handle<'a, JsValue>>
where
F: FnOnce(&mut Self) -> NeonResult<T>,
{
unsafe {
self.env()
.try_catch(move || f(self))
.map_err(JsValue::new_internal)
}
}
fn boolean(&mut self, b: bool) -> Handle<'a, JsBoolean> {
JsBoolean::new(self, b)
}
fn number<T: Into<f64>>(&mut self, x: T) -> Handle<'a, JsNumber> {
JsNumber::new(self, x.into())
}
fn string<S: AsRef<str>>(&mut self, s: S) -> Handle<'a, JsString> {
JsString::new(self, s)
}
fn try_string<S: AsRef<str>>(&mut self, s: S) -> StringResult<'a> {
JsString::try_new(self, s)
}
fn null(&mut self) -> Handle<'a, JsNull> {
JsNull::new(self)
}
fn undefined(&mut self) -> Handle<'a, JsUndefined> {
JsUndefined::new(self)
}
fn empty_object(&mut self) -> Handle<'a, JsObject> {
JsObject::new(self)
}
fn empty_array(&mut self) -> Handle<'a, JsArray> {
JsArray::new(self, 0)
}
fn array_buffer(&mut self, size: usize) -> JsResult<'a, JsArrayBuffer> {
JsArrayBuffer::new(self, size)
}
fn buffer(&mut self, size: usize) -> JsResult<'a, JsBuffer> {
JsBuffer::new(self, size)
}
#[cfg(feature = "napi-5")]
#[cfg_attr(docsrs, doc(cfg(feature = "napi-5")))]
fn date(&mut self, value: impl Into<f64>) -> Result<Handle<'a, JsDate>, DateError> {
JsDate::new(self, value)
}
fn global<T: Value>(&mut self, name: &str) -> JsResult<'a, T> {
let global = self.global_object();
global.get(self, name)
}
fn global_object(&mut self) -> Handle<'a, JsObject> {
JsObject::build(|out| unsafe {
sys::scope::get_global(self.env().to_raw(), out);
})
}
fn throw<T: Value, U>(&mut self, v: Handle<T>) -> NeonResult<U> {
unsafe {
sys::error::throw(self.env().to_raw(), v.to_local());
Err(Throw::new())
}
}
fn error<S: AsRef<str>>(&mut self, msg: S) -> JsResult<'a, JsError> {
JsError::error(self, msg)
}
fn type_error<S: AsRef<str>>(&mut self, msg: S) -> JsResult<'a, JsError> {
JsError::type_error(self, msg)
}
fn range_error<S: AsRef<str>>(&mut self, msg: S) -> JsResult<'a, JsError> {
JsError::range_error(self, msg)
}
fn throw_error<S: AsRef<str>, T>(&mut self, msg: S) -> NeonResult<T> {
let err = JsError::error(self, msg)?;
self.throw(err)
}
fn throw_type_error<S: AsRef<str>, T>(&mut self, msg: S) -> NeonResult<T> {
let err = JsError::type_error(self, msg)?;
self.throw(err)
}
fn throw_range_error<S: AsRef<str>, T>(&mut self, msg: S) -> NeonResult<T> {
let err = JsError::range_error(self, msg)?;
self.throw(err)
}
fn boxed<U: Finalize + 'static>(&mut self, v: U) -> Handle<'a, JsBox<U>> {
JsBox::new(self, v)
}
#[cfg(feature = "napi-4")]
#[deprecated(since = "0.9.0", note = "Please use the channel() method instead")]
#[doc(hidden)]
fn queue(&mut self) -> Channel {
self.channel()
}
#[cfg(feature = "napi-4")]
#[cfg_attr(docsrs, doc(cfg(feature = "napi-4")))]
fn channel(&mut self) -> Channel {
#[cfg(feature = "napi-6")]
let channel = InstanceData::channel(self);
#[cfg(not(feature = "napi-6"))]
let channel = Channel::new(self);
channel
}
fn promise(&mut self) -> (Deferred, Handle<'a, JsPromise>) {
JsPromise::new(self)
}
fn task<'cx, O, E>(&'cx mut self, execute: E) -> TaskBuilder<'cx, Self, E>
where
'a: 'cx,
O: Send + 'static,
E: FnOnce() -> O + Send + 'static,
{
TaskBuilder::new(self, execute)
}
#[cfg(feature = "sys")]
#[cfg_attr(docsrs, doc(cfg(feature = "sys")))]
fn to_raw(&self) -> sys::Env {
self.env().to_raw()
}
}
pub struct ModuleContext<'cx> {
cx: Cx<'cx>,
exports: Handle<'cx, JsObject>,
}
impl<'cx> Deref for ModuleContext<'cx> {
type Target = Cx<'cx>;
fn deref(&self) -> &Self::Target {
self.cx()
}
}
impl<'cx> DerefMut for ModuleContext<'cx> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.cx_mut()
}
}
impl<'cx> UnwindSafe for ModuleContext<'cx> {}
impl<'cx> ModuleContext<'cx> {
pub(crate) fn with<T, F: for<'b> FnOnce(ModuleContext<'b>) -> T>(
env: Env,
exports: Handle<'cx, JsObject>,
f: F,
) -> T {
f(ModuleContext {
cx: Cx::new(env),
exports,
})
}
#[cfg(not(feature = "napi-5"))]
pub fn export_function<T: Value>(
&mut self,
key: &str,
f: fn(FunctionContext) -> JsResult<T>,
) -> NeonResult<()> {
let value = JsFunction::new(self, f)?.upcast::<JsValue>();
self.exports.clone().set(self, key, value)?;
Ok(())
}
#[cfg(feature = "napi-5")]
pub fn export_function<F, V>(&mut self, key: &str, f: F) -> NeonResult<()>
where
F: Fn(FunctionContext) -> JsResult<V> + 'static,
V: Value,
{
let value = JsFunction::new(self, f)?.upcast::<JsValue>();
self.exports.clone().set(self, key, value)?;
Ok(())
}
pub fn export_value<T: Value>(&mut self, key: &str, val: Handle<T>) -> NeonResult<()> {
self.exports.clone().set(self, key, val)?;
Ok(())
}
pub fn exports_object(&mut self) -> JsResult<'cx, JsObject> {
Ok(self.exports)
}
}
impl<'cx> ContextInternal<'cx> for ModuleContext<'cx> {
fn cx(&self) -> &Cx<'cx> {
&self.cx
}
fn cx_mut(&mut self) -> &mut Cx<'cx> {
&mut self.cx
}
}
impl<'cx> Context<'cx> for ModuleContext<'cx> {}
pub struct FunctionContext<'cx> {
cx: Cx<'cx>,
info: &'cx CallbackInfo<'cx>,
arguments: Option<sys::call::Arguments>,
}
impl<'cx> Deref for FunctionContext<'cx> {
type Target = Cx<'cx>;
fn deref(&self) -> &Self::Target {
&self.cx
}
}
impl<'cx> DerefMut for FunctionContext<'cx> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cx
}
}
impl<'cx> UnwindSafe for FunctionContext<'cx> {}
impl<'cx> FunctionContext<'cx> {
pub fn kind(&self) -> CallKind {
self.info.kind(self)
}
pub(crate) fn with<U, F: for<'b> FnOnce(FunctionContext<'b>) -> U>(
env: Env,
info: &'cx CallbackInfo<'cx>,
f: F,
) -> U {
f(FunctionContext {
cx: Cx::new(env),
info,
arguments: None,
})
}
pub fn len(&self) -> usize {
self.info.len(self)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn argument_opt(&mut self, i: usize) -> Option<Handle<'cx, JsValue>> {
let argv = if let Some(argv) = self.arguments.as_ref() {
argv
} else {
let argv = self.info.argv(self);
self.arguments.insert(argv)
};
argv.get(i)
.map(|v| Handle::new_internal(unsafe { JsValue::from_local(self.env(), v) }))
}
pub fn argument<V: Value>(&mut self, i: usize) -> JsResult<'cx, V> {
match self.argument_opt(i) {
Some(v) => v.downcast_or_throw(self),
None => self.throw_type_error("not enough arguments"),
}
}
pub fn this<T: Value>(&mut self) -> JsResult<'cx, T> {
self.this_value().downcast_or_throw(self)
}
pub fn this_value(&mut self) -> Handle<'cx, JsValue> {
JsValue::new_internal(self.info.this(self))
}
pub fn args<T>(&mut self) -> NeonResult<T>
where
T: FromArgs<'cx>,
{
T::from_args(self)
}
pub fn arg<T>(&mut self) -> NeonResult<T>
where
T: TryFromJs<'cx>,
{
self.args::<(T,)>().map(|(v,)| v)
}
pub fn args_opt<T>(&mut self) -> NeonResult<Option<T>>
where
T: FromArgs<'cx>,
{
T::from_args_opt(self)
}
pub fn arg_opt<T>(&mut self) -> NeonResult<Option<T>>
where
T: TryFromJs<'cx>,
{
self.args_opt::<(T,)>().map(|v| v.map(|(v,)| v))
}
pub(crate) fn argv<const N: usize>(&mut self) -> [Handle<'cx, JsValue>; N] {
self.info.argv_exact(self)
}
}
impl<'cx> ContextInternal<'cx> for FunctionContext<'cx> {
fn cx(&self) -> &Cx<'cx> {
&self.cx
}
fn cx_mut(&mut self) -> &mut Cx<'cx> {
&mut self.cx
}
}
impl<'cx> Context<'cx> for FunctionContext<'cx> {}