#[cfg(feature = "napi-6")]
#[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
pub mod bigint;
pub(crate) mod boxed;
pub mod buffer;
#[cfg(feature = "napi-5")]
pub(crate) mod date;
pub(crate) mod error;
pub mod extract;
pub mod function;
pub(crate) mod promise;
pub(crate) mod private;
pub(crate) mod utf8;
use std::{
any,
fmt::{self, Debug},
};
use private::prepare_call;
use smallvec::smallvec;
use crate::{
context::{
internal::{ContextInternal, Env},
Context, Cx, FunctionContext,
},
handle::{
internal::{SuperType, TransparentNoCopyWrapper},
Handle,
},
object::Object,
result::{JsResult, NeonResult, ResultExt, Throw},
sys::{self, raw},
types::{
function::{BindOptions, CallOptions, ConstructOptions},
private::ValueInternal,
utf8::Utf8,
},
};
pub use self::{
boxed::{Finalize, JsBox},
buffer::types::{
JsArrayBuffer, JsBigInt64Array, JsBigUint64Array, JsBuffer, JsFloat32Array, JsFloat64Array,
JsInt16Array, JsInt32Array, JsInt8Array, JsTypedArray, JsUint16Array, JsUint32Array,
JsUint8Array,
},
error::JsError,
promise::{Deferred, JsPromise},
};
#[cfg(feature = "napi-5")]
pub use self::date::{DateError, DateErrorKind, JsDate};
#[cfg(all(feature = "napi-5", feature = "futures"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "napi-5", feature = "futures"))))]
pub use self::promise::JsFuture;
pub(crate) fn build<'a, T: Value, F: FnOnce(&mut raw::Local) -> bool>(
env: Env,
init: F,
) -> JsResult<'a, T> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
if init(&mut local) {
Ok(Handle::new_internal(T::from_local(env, local)))
} else {
Err(Throw::new())
}
}
}
impl<T: Value> SuperType<T> for JsValue {
fn upcast_internal(v: &T) -> JsValue {
JsValue(v.to_local())
}
}
impl<T: Object> SuperType<T> for JsObject {
fn upcast_internal(v: &T) -> JsObject {
JsObject(v.to_local())
}
}
pub trait Value: ValueInternal {
fn to_string<'cx, C: Context<'cx>>(&self, cx: &mut C) -> JsResult<'cx, JsString> {
let env = cx.env();
build(env, |out| unsafe {
sys::convert::to_string(out, env.to_raw(), self.to_local())
})
}
fn as_value<'cx, C: Context<'cx>>(&self, _: &mut C) -> Handle<'cx, JsValue> {
JsValue::new_internal(self.to_local())
}
#[cfg(feature = "sys")]
#[cfg_attr(docsrs, doc(cfg(feature = "sys")))]
fn to_raw(&self) -> sys::Value {
self.to_local()
}
#[cfg(feature = "sys")]
#[cfg_attr(docsrs, doc(cfg(feature = "sys")))]
unsafe fn from_raw<'cx, C: Context<'cx>>(cx: &C, value: sys::Value) -> Handle<'cx, Self> {
Handle::new_internal(Self::from_local(cx.env(), value))
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsValue(raw::Local);
impl Value for JsValue {}
unsafe impl TransparentNoCopyWrapper for JsValue {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsValue {
fn name() -> &'static str {
"any"
}
fn is_typeof<Other: Value>(_cx: &mut Cx, _other: &Other) -> bool {
true
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsValue(h)
}
}
impl JsValue {
pub(crate) fn new_internal<'a>(value: raw::Local) -> Handle<'a, JsValue> {
Handle::new_internal(JsValue(value))
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsUndefined(raw::Local);
impl JsUndefined {
pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsUndefined> {
JsUndefined::new_internal(cx.env())
}
pub(crate) fn new_internal<'a>(env: Env) -> Handle<'a, JsUndefined> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::primitive::undefined(&mut local, env.to_raw());
Handle::new_internal(JsUndefined(local))
}
}
}
impl Value for JsUndefined {}
unsafe impl TransparentNoCopyWrapper for JsUndefined {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsUndefined {
fn name() -> &'static str {
"undefined"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_undefined(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsUndefined(h)
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsNull(raw::Local);
impl JsNull {
pub fn new<'a, C: Context<'a>>(cx: &mut C) -> Handle<'a, JsNull> {
JsNull::new_internal(cx.env())
}
pub(crate) fn new_internal<'a>(env: Env) -> Handle<'a, JsNull> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::primitive::null(&mut local, env.to_raw());
Handle::new_internal(JsNull(local))
}
}
}
impl Value for JsNull {}
unsafe impl TransparentNoCopyWrapper for JsNull {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsNull {
fn name() -> &'static str {
"null"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_null(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsNull(h)
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsBoolean(raw::Local);
impl JsBoolean {
pub fn new<'a, C: Context<'a>>(cx: &mut C, b: bool) -> Handle<'a, JsBoolean> {
JsBoolean::new_internal(cx.env(), b)
}
pub(crate) fn new_internal<'a>(env: Env, b: bool) -> Handle<'a, JsBoolean> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::primitive::boolean(&mut local, env.to_raw(), b);
Handle::new_internal(JsBoolean(local))
}
}
pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> bool {
let env = cx.env().to_raw();
unsafe { sys::primitive::boolean_value(env, self.to_local()) }
}
}
impl Value for JsBoolean {}
unsafe impl TransparentNoCopyWrapper for JsBoolean {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsBoolean {
fn name() -> &'static str {
"boolean"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_boolean(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsBoolean(h)
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsString(raw::Local);
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub struct StringOverflow(usize);
impl fmt::Display for StringOverflow {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "string size out of range: {}", self.0)
}
}
pub type StringResult<'a> = Result<Handle<'a, JsString>, StringOverflow>;
impl<'a> ResultExt<Handle<'a, JsString>> for StringResult<'a> {
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, JsString> {
match self {
Ok(v) => Ok(v),
Err(e) => cx.throw_range_error(e.to_string()),
}
}
}
impl Value for JsString {}
unsafe impl TransparentNoCopyWrapper for JsString {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsString {
fn name() -> &'static str {
"string"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_string(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsString(h)
}
}
impl JsString {
pub fn size<'a, C: Context<'a>>(&self, cx: &mut C) -> usize {
let env = cx.env().to_raw();
unsafe { sys::string::utf8_len(env, self.to_local()) }
}
pub fn size_utf16<'a, C: Context<'a>>(&self, cx: &mut C) -> usize {
let env = cx.env().to_raw();
unsafe { sys::string::utf16_len(env, self.to_local()) }
}
pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> String {
let env = cx.env().to_raw();
unsafe {
let capacity = sys::string::utf8_len(env, self.to_local()) + 1;
let mut buffer: Vec<u8> = Vec::with_capacity(capacity);
let len = sys::string::data(env, buffer.as_mut_ptr(), capacity, self.to_local());
buffer.set_len(len);
String::from_utf8_unchecked(buffer)
}
}
pub fn to_utf16<'a, C: Context<'a>>(&self, cx: &mut C) -> Vec<u16> {
let env = cx.env().to_raw();
unsafe {
let capacity = sys::string::utf16_len(env, self.to_local()) + 1;
let mut buffer: Vec<u16> = Vec::with_capacity(capacity);
let len = sys::string::data_utf16(env, buffer.as_mut_ptr(), capacity, self.to_local());
buffer.set_len(len);
buffer
}
}
pub fn new<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, val: S) -> Handle<'a, JsString> {
JsString::try_new(cx, val).unwrap()
}
pub fn try_new<'a, C: Context<'a>, S: AsRef<str>>(cx: &mut C, val: S) -> StringResult<'a> {
let val = val.as_ref();
match JsString::new_internal(cx.env(), val) {
Some(s) => Ok(s),
None => Err(StringOverflow(val.len())),
}
}
pub(crate) fn new_internal<'a>(env: Env, val: &str) -> Option<Handle<'a, JsString>> {
let (ptr, len) = if let Some(small) = Utf8::from(val).into_small() {
small.lower()
} else {
return None;
};
unsafe {
let mut local: raw::Local = std::mem::zeroed();
if sys::string::new(&mut local, env.to_raw(), ptr, len) {
Some(Handle::new_internal(JsString(local)))
} else {
None
}
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsNumber(raw::Local);
impl JsNumber {
pub fn new<'a, C: Context<'a>, T: Into<f64>>(cx: &mut C, x: T) -> Handle<'a, JsNumber> {
JsNumber::new_internal(cx.env(), x.into())
}
pub(crate) fn new_internal<'a>(env: Env, v: f64) -> Handle<'a, JsNumber> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::primitive::number(&mut local, env.to_raw(), v);
Handle::new_internal(JsNumber(local))
}
}
pub fn value<'a, C: Context<'a>>(&self, cx: &mut C) -> f64 {
let env = cx.env().to_raw();
unsafe { sys::primitive::number_value(env, self.to_local()) }
}
}
impl Value for JsNumber {}
unsafe impl TransparentNoCopyWrapper for JsNumber {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsNumber {
fn name() -> &'static str {
"number"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_number(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsNumber(h)
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsObject(raw::Local);
impl Value for JsObject {}
unsafe impl TransparentNoCopyWrapper for JsObject {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsObject {
fn name() -> &'static str {
"object"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_object(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsObject(h)
}
}
impl Object for JsObject {}
impl JsObject {
pub fn new<'a, C: Context<'a>>(c: &mut C) -> Handle<'a, JsObject> {
JsObject::new_internal(c.env())
}
pub(crate) fn new_internal<'a>(env: Env) -> Handle<'a, JsObject> {
JsObject::build(|out| unsafe { sys::object::new(out, env.to_raw()) })
}
pub(crate) fn build<'a, F: FnOnce(&mut raw::Local)>(init: F) -> Handle<'a, JsObject> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
init(&mut local);
Handle::new_internal(JsObject(local))
}
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsArray(raw::Local);
impl JsArray {
pub fn new<'a, C: Context<'a>>(cx: &mut C, len: usize) -> Handle<'a, JsArray> {
JsArray::new_internal(cx.env(), len)
}
pub(crate) fn new_internal<'a>(env: Env, len: usize) -> Handle<'a, JsArray> {
unsafe {
let mut local: raw::Local = std::mem::zeroed();
sys::array::new(&mut local, env.to_raw(), len);
Handle::new_internal(JsArray(local))
}
}
pub fn to_vec<'a, C: Context<'a>>(&self, cx: &mut C) -> NeonResult<Vec<Handle<'a, JsValue>>> {
let mut result = Vec::with_capacity(self.len_inner(cx.env()) as usize);
let mut i = 0;
loop {
if i >= self.len_inner(cx.env()) {
return Ok(result);
}
result.push(self.get(cx, i)?);
i += 1;
}
}
fn len_inner(&self, env: Env) -> u32 {
unsafe { sys::array::len(env.to_raw(), self.to_local()) }
}
#[allow(clippy::len_without_is_empty)]
pub fn len<'a, C: Context<'a>>(&self, cx: &mut C) -> u32 {
self.len_inner(cx.env())
}
pub fn is_empty<'a, C: Context<'a>>(&self, cx: &mut C) -> bool {
self.len(cx) == 0
}
}
impl Value for JsArray {}
unsafe impl TransparentNoCopyWrapper for JsArray {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.0
}
}
impl ValueInternal for JsArray {
fn name() -> &'static str {
"Array"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_array(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.0
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsArray(h)
}
}
impl Object for JsArray {}
#[derive(Debug)]
#[repr(transparent)]
pub struct JsFunction {
raw: raw::Local,
}
impl Object for JsFunction {}
impl JsFunction {
#[cfg(not(feature = "napi-5"))]
pub fn new<'a, C, U>(
cx: &mut C,
f: fn(FunctionContext) -> JsResult<U>,
) -> JsResult<'a, JsFunction>
where
C: Context<'a>,
U: Value,
{
let name = any::type_name_of_val(&f);
Self::new_internal(cx, f, name)
}
#[cfg(feature = "napi-5")]
pub fn new<'a, C, F, V>(cx: &mut C, f: F) -> JsResult<'a, JsFunction>
where
C: Context<'a>,
F: Fn(FunctionContext) -> JsResult<V> + 'static,
V: Value,
{
let name = any::type_name::<F>();
Self::new_internal(cx, f, name)
}
#[cfg(not(feature = "napi-5"))]
pub fn with_name<'a, C, U>(
cx: &mut C,
name: &str,
f: fn(FunctionContext) -> JsResult<U>,
) -> JsResult<'a, JsFunction>
where
C: Context<'a>,
U: Value,
{
Self::new_internal(cx, f, name)
}
#[cfg(feature = "napi-5")]
pub fn with_name<'a, C, F, V>(cx: &mut C, name: &str, f: F) -> JsResult<'a, JsFunction>
where
C: Context<'a>,
F: Fn(FunctionContext) -> JsResult<V> + 'static,
V: Value,
{
Self::new_internal(cx, f, name)
}
fn new_internal<'a, C, F, V>(cx: &mut C, f: F, name: &str) -> JsResult<'a, JsFunction>
where
C: Context<'a>,
F: Fn(FunctionContext) -> JsResult<V> + 'static,
V: Value,
{
use std::panic::AssertUnwindSafe;
use std::ptr;
use crate::context::CallbackInfo;
use crate::types::error::convert_panics;
let f = move |env: raw::Env, info| {
let env = env.into();
let info = unsafe { CallbackInfo::new(info) };
FunctionContext::with(env, &info, |cx| {
convert_panics(env, AssertUnwindSafe(|| f(cx)))
.map(|v| v.to_local())
.unwrap_or_else(|_: Throw| ptr::null_mut())
})
};
unsafe {
if let Ok(raw) = sys::fun::new(cx.env().to_raw(), name, f) {
Ok(Handle::new_internal(JsFunction { raw }))
} else {
Err(Throw::new())
}
}
}
}
impl JsFunction {
pub fn call<'a, 'b, C: Context<'a>, T, AS>(
&self,
cx: &mut C,
this: Handle<'b, T>,
args: AS,
) -> JsResult<'a, JsValue>
where
T: Value,
AS: AsRef<[Handle<'b, JsValue>]>,
{
unsafe { self.try_call(cx, this, args) }
}
pub fn exec<'a, 'b, C: Context<'a>, T, AS>(
&self,
cx: &mut C,
this: Handle<'b, T>,
args: AS,
) -> NeonResult<()>
where
T: Value,
AS: AsRef<[Handle<'b, JsValue>]>,
{
self.call(cx, this, args)?;
Ok(())
}
pub fn construct<'a, 'b, C: Context<'a>, AS>(
&self,
cx: &mut C,
args: AS,
) -> JsResult<'a, JsObject>
where
AS: AsRef<[Handle<'b, JsValue>]>,
{
let (argc, argv) = unsafe { prepare_call(cx, args.as_ref()) }?;
let env = cx.env().to_raw();
build(cx.env(), |out| unsafe {
sys::fun::construct(out, env, self.to_local(), argc, argv)
})
}
}
impl JsFunction {
pub fn bind<'a, 'cx: 'a>(&self, cx: &'a mut Cx<'cx>) -> BindOptions<'a, 'cx> {
let callee = self.as_value(cx);
BindOptions {
cx,
callee,
this: None,
args: smallvec![],
}
}
}
impl JsFunction {
#[deprecated(since = "TBD", note = "use `JsFunction::bind` instead")]
pub fn call_with<'a, C: Context<'a>>(&self, _cx: &C) -> CallOptions<'a> {
CallOptions {
this: None,
callee: Handle::new_internal(unsafe { self.clone() }),
args: smallvec![],
}
}
#[deprecated(since = "TBD", note = "use `JsFunction::bind` instead")]
pub fn construct_with<'a, C: Context<'a>>(&self, _cx: &C) -> ConstructOptions<'a> {
ConstructOptions {
callee: Handle::new_internal(unsafe { self.clone() }),
args: smallvec![],
}
}
unsafe fn clone(&self) -> Self {
Self { raw: self.raw }
}
}
impl Value for JsFunction {}
unsafe impl TransparentNoCopyWrapper for JsFunction {
type Inner = raw::Local;
fn into_inner(self) -> Self::Inner {
self.raw
}
}
impl ValueInternal for JsFunction {
fn name() -> &'static str {
"function"
}
fn is_typeof<Other: Value>(cx: &mut Cx, other: &Other) -> bool {
unsafe { sys::tag::is_function(cx.env().to_raw(), other.to_local()) }
}
fn to_local(&self) -> raw::Local {
self.raw
}
unsafe fn from_local(_env: Env, h: raw::Local) -> Self {
JsFunction { raw: h }
}
}
#[cfg(feature = "napi-6")]
#[cfg_attr(docsrs, doc(cfg(feature = "napi-6")))]
#[derive(Debug)]
#[repr(transparent)]
pub struct JsBigInt(raw::Local);