feat(napi): experimental iterator support
This commit is contained in:
parent
b074608582
commit
a3356264f2
24 changed files with 947 additions and 102 deletions
|
@ -21,6 +21,7 @@ pub struct NapiFn {
|
|||
pub ts_return_type: Option<String>,
|
||||
pub skip_typescript: bool,
|
||||
pub comments: Vec<String>,
|
||||
pub parent_is_generator: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -62,6 +63,7 @@ pub struct NapiStruct {
|
|||
pub kind: NapiStructKind,
|
||||
pub js_mod: Option<String>,
|
||||
pub comments: Vec<String>,
|
||||
pub implement_iterator: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
@ -89,6 +91,9 @@ pub struct NapiImpl {
|
|||
pub js_name: String,
|
||||
pub items: Vec<NapiFn>,
|
||||
pub task_output_type: Option<Type>,
|
||||
pub iterator_yield_type: Option<Type>,
|
||||
pub iterator_next_type: Option<Type>,
|
||||
pub iterator_return_type: Option<Type>,
|
||||
pub js_mod: Option<String>,
|
||||
pub comments: Vec<String>,
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ impl NapiFn {
|
|||
} else {
|
||||
if self.parent.is_some() {
|
||||
if let syn::Type::Path(path) = path.ty.as_ref() {
|
||||
if let Some(p) = path.path.segments.first() {
|
||||
if let Some(p) = path.path.segments.last() {
|
||||
if p.ident == "Reference" {
|
||||
if let syn::PathArguments::AngleBracketed(
|
||||
syn::AngleBracketedGenericArguments { args: angle_bracketed_args, .. },
|
||||
|
@ -281,7 +281,13 @@ impl NapiFn {
|
|||
let is_return_self = ty_string == "& Self" || ty_string == "&mut Self";
|
||||
if self.kind == FnKind::Constructor {
|
||||
if self.is_ret_result {
|
||||
quote! { cb.construct(#js_name, #ret?) }
|
||||
if self.parent_is_generator {
|
||||
quote! { cb.construct_generator(#js_name, #ret?) }
|
||||
} else {
|
||||
quote! { cb.construct(#js_name, #ret?) }
|
||||
}
|
||||
} else if self.parent_is_generator {
|
||||
quote! { cb.construct_generator(#js_name, #ret) }
|
||||
} else {
|
||||
quote! { cb.construct(#js_name, #ret) }
|
||||
}
|
||||
|
|
|
@ -186,13 +186,19 @@ impl NapiStruct {
|
|||
quote! { #name {#(#fields),*} }
|
||||
};
|
||||
|
||||
let constructor = if self.implement_iterator {
|
||||
quote! { unsafe { cb.construct_generator(#js_name_str, #construct) } }
|
||||
} else {
|
||||
quote! { unsafe { cb.construct(#js_name_str, #construct) } }
|
||||
};
|
||||
|
||||
quote! {
|
||||
extern "C" fn constructor(
|
||||
env: napi::bindgen_prelude::sys::napi_env,
|
||||
cb: napi::bindgen_prelude::sys::napi_callback_info
|
||||
) -> napi::bindgen_prelude::sys::napi_value {
|
||||
napi::bindgen_prelude::CallbackInfo::<#fields_len>::new(env, cb, None)
|
||||
.and_then(|cb| unsafe { cb.construct(#js_name_str, #construct) })
|
||||
.and_then(|cb| #constructor)
|
||||
.unwrap_or_else(|e| {
|
||||
unsafe { napi::bindgen_prelude::JsError::from(e).throw_into(env) };
|
||||
std::ptr::null_mut::<napi::bindgen_prelude::sys::napi_value__>()
|
||||
|
@ -218,18 +224,21 @@ impl NapiStruct {
|
|||
let name = &self.name;
|
||||
let js_name_raw = &self.js_name;
|
||||
let js_name_str = format!("{}\0", js_name_raw);
|
||||
let iterator_implementation = self.gen_iterator_property(name);
|
||||
quote! {
|
||||
impl napi::bindgen_prelude::ToNapiValue for #name {
|
||||
unsafe fn to_napi_value(
|
||||
env: napi::sys::napi_env,
|
||||
val: #name
|
||||
) -> napi::Result<napi::bindgen_prelude::sys::napi_value> {
|
||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
||||
let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void;
|
||||
#name::new_instance(env, wrapped_value, ctor_ref)
|
||||
if let Some(ctor_ref) = napi::__private::get_class_constructor(#js_name_str) {
|
||||
let wrapped_value = Box::into_raw(Box::new(val));
|
||||
let instance_value = #name::new_instance(env, wrapped_value as *mut std::ffi::c_void, ctor_ref)?;
|
||||
#iterator_implementation
|
||||
Ok(instance_value)
|
||||
} else {
|
||||
Err(napi::bindgen_prelude::Error::new(
|
||||
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_raw))
|
||||
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}` in `ToNapiValue`", #js_name_raw))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -239,9 +248,13 @@ impl NapiStruct {
|
|||
pub fn into_reference(val: #name, env: napi::Env) -> napi::Result<napi::bindgen_prelude::Reference<#name>> {
|
||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
||||
unsafe {
|
||||
let wrapped_value = Box::into_raw(Box::new(val)) as *mut std::ffi::c_void;
|
||||
#name::new_instance(env.raw(), wrapped_value, ctor_ref)?;
|
||||
napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value, env.raw())
|
||||
let wrapped_value = Box::into_raw(Box::new(val));
|
||||
let instance_value = #name::new_instance(env.raw(), wrapped_value as *mut std::ffi::c_void, ctor_ref)?;
|
||||
{
|
||||
let env = env.raw();
|
||||
#iterator_implementation
|
||||
}
|
||||
napi::bindgen_prelude::Reference::<#name>::from_value_ptr(wrapped_value as *mut std::ffi::c_void, env.raw())
|
||||
}
|
||||
} else {
|
||||
Err(napi::bindgen_prelude::Error::new(
|
||||
|
@ -258,7 +271,7 @@ impl NapiStruct {
|
|||
let mut ctor = std::ptr::null_mut();
|
||||
napi::check_status!(
|
||||
napi::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
|
||||
"Failed to get constructor of class `{}`",
|
||||
"Failed to get constructor reference of class `{}`",
|
||||
#js_name_raw
|
||||
)?;
|
||||
|
||||
|
@ -292,6 +305,15 @@ impl NapiStruct {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_iterator_property(&self, name: &Ident) -> TokenStream {
|
||||
if !self.implement_iterator {
|
||||
return quote! {};
|
||||
}
|
||||
quote! {
|
||||
napi::__private::create_iterator::<#name>(env, instance_value, wrapped_value);
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_to_napi_value_ctor_impl(&self) -> TokenStream {
|
||||
let name = &self.name;
|
||||
let js_name_str = format!("{}\0", &self.js_name);
|
||||
|
@ -331,28 +353,29 @@ impl NapiStruct {
|
|||
quote! {
|
||||
impl napi::bindgen_prelude::ToNapiValue for #name {
|
||||
unsafe fn to_napi_value(
|
||||
env: napi::bindgen_prelude::sys::napi_env, val: #name
|
||||
env: napi::bindgen_prelude::sys::napi_env,
|
||||
val: #name,
|
||||
) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
|
||||
if let Some(ctor_ref) = napi::bindgen_prelude::get_class_constructor(#js_name_str) {
|
||||
let mut ctor = std::ptr::null_mut();
|
||||
|
||||
napi::bindgen_prelude::check_status!(
|
||||
napi::bindgen_prelude::sys::napi_get_reference_value(env, ctor_ref, &mut ctor),
|
||||
"Failed to get constructor of class `{}`",
|
||||
"Failed to get constructor reference of class `{}`",
|
||||
#js_name_str
|
||||
)?;
|
||||
|
||||
let mut result = std::ptr::null_mut();
|
||||
let mut instance_value = std::ptr::null_mut();
|
||||
let #destructed_fields = val;
|
||||
let args = vec![#(#field_conversions),*];
|
||||
|
||||
napi::bindgen_prelude::check_status!(
|
||||
napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut result),
|
||||
napi::bindgen_prelude::sys::napi_new_instance(env, ctor, args.len(), args.as_ptr(), &mut instance_value),
|
||||
"Failed to construct class `{}`",
|
||||
#js_name_str
|
||||
)?;
|
||||
|
||||
Ok(result)
|
||||
Ok(instance_value)
|
||||
} else {
|
||||
Err(napi::bindgen_prelude::Error::new(
|
||||
napi::bindgen_prelude::Status::InvalidArg, format!("Failed to get constructor of class `{}`", #js_name_str))
|
||||
|
@ -601,7 +624,7 @@ impl NapiStruct {
|
|||
#[cfg(all(not(test), not(feature = "noop")))]
|
||||
#[napi::bindgen_prelude::ctor]
|
||||
fn #struct_register_name() {
|
||||
napi::bindgen_prelude::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -675,7 +698,7 @@ impl NapiImpl {
|
|||
#[cfg(all(not(test), not(feature = "noop")))]
|
||||
#[napi::bindgen_prelude::ctor]
|
||||
fn #register_name() {
|
||||
napi::bindgen_prelude::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||
napi::__private::register_class(#name_str, #js_mod_ident, #js_name, vec![#(#props),*]);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -24,6 +24,7 @@ pub struct Napi {
|
|||
macro_rules! napi_ast_impl {
|
||||
( $( ($v:ident, $ast:ident), )* ) => {
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum NapiItem {
|
||||
$($v($ast)),*
|
||||
}
|
||||
|
|
|
@ -179,7 +179,6 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("Either3", "{} | {} | {}"),
|
||||
("Either4", "{} | {} | {} | {}"),
|
||||
("Either5", "{} | {} | {} | {} | {}"),
|
||||
("unknown", "unknown"),
|
||||
("Null", "null"),
|
||||
("JsNull", "null"),
|
||||
("null", "null"),
|
||||
|
@ -190,6 +189,8 @@ static KNOWN_TYPES: Lazy<HashMap<&'static str, &'static str>> = Lazy::new(|| {
|
|||
("JsFunction", "(...args: any[]) => any"),
|
||||
("JsGlobal", "typeof global"),
|
||||
("External", "ExternalObject<{}>"),
|
||||
("unknown", "unknown"),
|
||||
("Unknown", "unknown"),
|
||||
("JsUnknown", "unknown"),
|
||||
]);
|
||||
|
||||
|
@ -281,11 +282,17 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S
|
|||
});
|
||||
} else if rust_ty == "Reference" {
|
||||
ts_ty = r#struct::TASK_STRUCTS.with(|t| {
|
||||
let (output_type, _) = args.first().unwrap().to_owned();
|
||||
if let Some(o) = t.borrow().get(&output_type) {
|
||||
Some((o.to_owned(), false))
|
||||
// Reference<T> => T
|
||||
if let Some(arg) = args.first() {
|
||||
let (output_type, _) = arg.to_owned();
|
||||
if let Some(o) = t.borrow().get(&output_type) {
|
||||
Some((o.to_owned(), false))
|
||||
} else {
|
||||
Some((output_type, false))
|
||||
}
|
||||
} else {
|
||||
Some((output_type, false))
|
||||
// Not NAPI-RS `Reference`
|
||||
Some((rust_ty, false))
|
||||
}
|
||||
});
|
||||
} else if let Some(&known_ty) = KNOWN_TYPES.get(rust_ty.as_str()) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use convert_case::{Case, Casing};
|
||||
use quote::ToTokens;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use syn::Pat;
|
||||
use syn::{Pat, PathArguments, PathSegment};
|
||||
|
||||
use super::{ty_to_ts_type, ToTypeDef, TypeDef};
|
||||
use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
|
||||
|
@ -122,9 +122,21 @@ impl NapiFn {
|
|||
.filter_map(|arg| match arg {
|
||||
crate::NapiFnArgKind::PatType(path) => {
|
||||
let ty_string = path.ty.to_token_stream().to_string();
|
||||
if ty_string == "Env" || ty_string.replace(' ', "").starts_with("Reference<") {
|
||||
if ty_string == "Env" {
|
||||
return None;
|
||||
}
|
||||
if let syn::Type::Path(path) = path.ty.as_ref() {
|
||||
if let Some(PathSegment {
|
||||
ident,
|
||||
arguments: PathArguments::AngleBracketed(_),
|
||||
}) = path.path.segments.last()
|
||||
{
|
||||
if ident == "Reference" {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut path = path.clone();
|
||||
// remove mutability from PatIdent
|
||||
if let Pat::Ident(i) = path.pat.as_mut() {
|
||||
|
|
|
@ -43,30 +43,56 @@ impl ToTypeDef for NapiImpl {
|
|||
});
|
||||
}
|
||||
|
||||
Some(TypeDef {
|
||||
kind: "impl".to_owned(),
|
||||
name: self.js_name.to_owned(),
|
||||
original_name: None,
|
||||
def: self
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
if f.skip_typescript {
|
||||
None
|
||||
} else {
|
||||
Some(format!(
|
||||
"{}{}",
|
||||
js_doc_from_comments(&f.comments),
|
||||
f.to_type_def()
|
||||
.map_or(String::default(), |type_def| type_def.def)
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\\n"),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: "".to_string(),
|
||||
})
|
||||
if let Some(output_type) = &self.iterator_yield_type {
|
||||
let next_type = if let Some(ref ty) = self.iterator_next_type {
|
||||
ty_to_ts_type(ty, false, false).0
|
||||
} else {
|
||||
"void".to_owned()
|
||||
};
|
||||
let return_type = if let Some(ref ty) = self.iterator_return_type {
|
||||
ty_to_ts_type(ty, false, false).0
|
||||
} else {
|
||||
"void".to_owned()
|
||||
};
|
||||
Some(TypeDef {
|
||||
kind: "impl".to_owned(),
|
||||
name: self.js_name.to_owned(),
|
||||
original_name: None,
|
||||
def: format!(
|
||||
"[Symbol.iterator](): Iterator<{}, {}, {}>",
|
||||
ty_to_ts_type(output_type, false, true).0,
|
||||
return_type,
|
||||
next_type,
|
||||
),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: "".to_string(),
|
||||
})
|
||||
} else {
|
||||
Some(TypeDef {
|
||||
kind: "impl".to_owned(),
|
||||
name: self.js_name.to_owned(),
|
||||
original_name: None,
|
||||
def: self
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|f| {
|
||||
if f.skip_typescript {
|
||||
None
|
||||
} else {
|
||||
Some(format!(
|
||||
"{}{}",
|
||||
js_doc_from_comments(&f.comments),
|
||||
f.to_type_def()
|
||||
.map_or(String::default(), |type_def| type_def.def)
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\\n"),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: "".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@ macro_rules! attrgen {
|
|||
(strict, Strict(Span)),
|
||||
(object, Object(Span)),
|
||||
(namespace, Namespace(Span, String, Span)),
|
||||
(iterator, Iterator(Span)),
|
||||
(ts_args_type, TsArgsType(Span, String, Span)),
|
||||
(ts_return_type, TsReturnType(Span, String, Span)),
|
||||
(ts_type, TsType(Span, String, Span)),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#[macro_use]
|
||||
pub mod attrs;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::str::Chars;
|
||||
|
||||
|
@ -17,10 +17,14 @@ use proc_macro2::{Ident, TokenStream, TokenTree};
|
|||
use quote::ToTokens;
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::{Parse, ParseStream, Result as SynResult};
|
||||
use syn::{Attribute, Signature, Type, Visibility};
|
||||
use syn::{Attribute, PathSegment, Signature, Type, Visibility};
|
||||
|
||||
use crate::parser::attrs::{check_recorded_struct_for_impl, record_struct};
|
||||
|
||||
thread_local! {
|
||||
static GENERATOR_STRUCT: RefCell<HashMap<String, bool>> = Default::default();
|
||||
}
|
||||
|
||||
struct AnyIdent(Ident);
|
||||
|
||||
impl Parse for AnyIdent {
|
||||
|
@ -562,6 +566,20 @@ fn napi_fn_from_decl(
|
|||
)
|
||||
};
|
||||
|
||||
let namespace = opts.namespace().map(|(m, _)| m.to_owned());
|
||||
let parent_is_generator = if let Some(p) = parent {
|
||||
GENERATOR_STRUCT.with(|inner| {
|
||||
let inner = inner.borrow();
|
||||
let key = namespace
|
||||
.as_ref()
|
||||
.map(|n| format!("{}::{}", n, p))
|
||||
.unwrap_or_else(|| p.to_string());
|
||||
*inner.get(&key).unwrap_or(&false)
|
||||
})
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
NapiFn {
|
||||
name: ident,
|
||||
js_name,
|
||||
|
@ -581,6 +599,7 @@ fn napi_fn_from_decl(
|
|||
ts_args_type: opts.ts_args_type().map(|(m, _)| m.to_owned()),
|
||||
ts_return_type: opts.ts_return_type().map(|(m, _)| m.to_owned()),
|
||||
skip_typescript: opts.skip_typescript().is_some(),
|
||||
parent_is_generator,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -786,6 +805,16 @@ impl ConvertToAST for syn::ItemStruct {
|
|||
}
|
||||
|
||||
record_struct(&struct_name, js_name.clone(), &opts);
|
||||
let namespace = opts.namespace().map(|(m, _)| m.to_owned());
|
||||
let implement_iterator = opts.iterator().is_some();
|
||||
GENERATOR_STRUCT.with(|inner| {
|
||||
let mut inner = inner.borrow_mut();
|
||||
let key = namespace
|
||||
.as_ref()
|
||||
.map(|n| format!("{}::{}", n, struct_name))
|
||||
.unwrap_or_else(|| struct_name.to_string());
|
||||
inner.insert(key, implement_iterator);
|
||||
});
|
||||
|
||||
Diagnostic::from_vec(errors).map(|()| Napi {
|
||||
item: NapiItem::Struct(NapiStruct {
|
||||
|
@ -795,8 +824,9 @@ impl ConvertToAST for syn::ItemStruct {
|
|||
fields,
|
||||
is_tuple,
|
||||
kind: struct_kind,
|
||||
js_mod: opts.namespace().map(|(m, _)| m.to_owned()),
|
||||
js_mod: namespace,
|
||||
comments: extract_doc_comments(&self.attrs),
|
||||
implement_iterator,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
@ -819,13 +849,30 @@ impl ConvertToAST for syn::ItemImpl {
|
|||
let mut struct_js_name = struct_name.to_string().to_case(Case::UpperCamel);
|
||||
let mut items = vec![];
|
||||
let mut task_output_type = None;
|
||||
let mut iterator_yield_type = None;
|
||||
let mut iterator_next_type = None;
|
||||
let mut iterator_return_type = None;
|
||||
for item in self.items.iter_mut() {
|
||||
if let Some(method) = match item {
|
||||
syn::ImplItem::Method(m) => Some(m),
|
||||
syn::ImplItem::Type(m) => {
|
||||
if m.ident == *"JsValue" {
|
||||
if let Type::Path(_) = &m.ty {
|
||||
task_output_type = Some(m.ty.clone());
|
||||
if let Some((_, t, _)) = &self.trait_ {
|
||||
if let Some(PathSegment { ident, .. }) = t.segments.last() {
|
||||
if ident == "Task" && m.ident == "JsValue" {
|
||||
if let Type::Path(_) = &m.ty {
|
||||
task_output_type = Some(m.ty.clone());
|
||||
}
|
||||
} else if ident == "Generator" {
|
||||
if let Type::Path(_) = &m.ty {
|
||||
if m.ident == "Yield" {
|
||||
iterator_yield_type = Some(m.ty.clone());
|
||||
} else if m.ident == "Next" {
|
||||
iterator_next_type = Some(m.ty.clone());
|
||||
} else if m.ident == "Return" {
|
||||
iterator_return_type = Some(m.ty.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -866,13 +913,18 @@ impl ConvertToAST for syn::ItemImpl {
|
|||
}
|
||||
}
|
||||
|
||||
let namespace = impl_opts.namespace().map(|(m, _)| m.to_owned());
|
||||
|
||||
Ok(Napi {
|
||||
item: NapiItem::Impl(NapiImpl {
|
||||
name: struct_name,
|
||||
js_name: struct_js_name,
|
||||
items,
|
||||
task_output_type,
|
||||
js_mod: impl_opts.namespace().map(|(m, _)| m.to_owned()),
|
||||
iterator_yield_type,
|
||||
iterator_next_type,
|
||||
iterator_return_type,
|
||||
js_mod: namespace,
|
||||
comments: extract_doc_comments(&self.attrs),
|
||||
}),
|
||||
})
|
||||
|
|
|
@ -66,10 +66,10 @@ impl<const N: usize> CallbackInfo<N> {
|
|||
self.this
|
||||
}
|
||||
|
||||
pub fn construct<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
||||
fn _construct<T: 'static>(&self, js_name: &str, obj: T) -> Result<(sys::napi_value, *mut T)> {
|
||||
let obj = Box::new(obj);
|
||||
let this = self.this();
|
||||
let value_ref = Box::into_raw(obj) as *mut c_void;
|
||||
let value_ref = Box::into_raw(obj);
|
||||
let mut object_ref = ptr::null_mut();
|
||||
let initial_finalize: Box<dyn FnOnce()> = Box::new(|| {});
|
||||
let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize))));
|
||||
|
@ -78,7 +78,7 @@ impl<const N: usize> CallbackInfo<N> {
|
|||
sys::napi_wrap(
|
||||
self.env,
|
||||
this,
|
||||
value_ref,
|
||||
value_ref as *mut c_void,
|
||||
Some(raw_finalize_unchecked::<T>),
|
||||
ptr::null_mut(),
|
||||
&mut object_ref
|
||||
|
@ -88,8 +88,25 @@ impl<const N: usize> CallbackInfo<N> {
|
|||
)?;
|
||||
};
|
||||
|
||||
Reference::<T>::add_ref(value_ref, (value_ref, object_ref, finalize_callbacks_ptr));
|
||||
Ok(this)
|
||||
Reference::<T>::add_ref(
|
||||
value_ref as *mut c_void,
|
||||
(value_ref as *mut c_void, object_ref, finalize_callbacks_ptr),
|
||||
);
|
||||
Ok((this, value_ref))
|
||||
}
|
||||
|
||||
pub fn construct<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
||||
self._construct(js_name, obj).map(|(v, _)| v)
|
||||
}
|
||||
|
||||
pub fn construct_generator<T: Generator + 'static>(
|
||||
&self,
|
||||
js_name: &str,
|
||||
obj: T,
|
||||
) -> Result<sys::napi_value> {
|
||||
let (instance, generator_ptr) = self._construct(js_name, obj)?;
|
||||
crate::__private::create_iterator(self.env, instance, generator_ptr);
|
||||
Ok(instance)
|
||||
}
|
||||
|
||||
pub fn factory<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
|
||||
|
|
|
@ -5,7 +5,6 @@ macro_rules! check_status_or_throw {
|
|||
if let Err(e) = $crate::check_status!($code, $($msg)*) {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe { $crate::JsError::from(e).throw_into($env) };
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
581
crates/napi/src/bindgen_runtime/iterator.rs
Normal file
581
crates/napi/src/bindgen_runtime/iterator.rs
Normal file
|
@ -0,0 +1,581 @@
|
|||
use std::ptr;
|
||||
use std::{ffi::c_void, os::raw::c_char};
|
||||
|
||||
use crate::Value;
|
||||
use crate::{bindgen_runtime::Unknown, check_status_or_throw, sys, Env};
|
||||
|
||||
use super::{FromNapiValue, ToNapiValue};
|
||||
|
||||
const GENERATOR_STATE_KEY: &str = "[[GeneratorState]]\0";
|
||||
|
||||
/// Implement a Iterator for the JavaScript Class.
|
||||
/// This feature is an experimental feature and is not yet stable.
|
||||
pub trait Generator {
|
||||
type Yield: ToNapiValue;
|
||||
type Next: FromNapiValue;
|
||||
type Return: FromNapiValue;
|
||||
|
||||
/// Handle the `Generator.next()`
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next
|
||||
fn next(&mut self, value: Option<Self::Next>) -> Option<Self::Yield>;
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// Implement complete to handle the `Generator.return()`
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return
|
||||
fn complete(&mut self, value: Option<Self::Return>) -> Option<Self::Yield> {
|
||||
None
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
/// Implement catch to handle the `Generator.throw()`
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/throw
|
||||
fn catch(&mut self, env: Env, value: Unknown) -> Result<Option<Self::Yield>, Unknown> {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
pub fn create_iterator<T: Generator>(
|
||||
env: sys::napi_env,
|
||||
instance: sys::napi_value,
|
||||
generator_ptr: *mut T,
|
||||
) {
|
||||
let mut global = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_global(env, &mut global) },
|
||||
"Get global object failed",
|
||||
);
|
||||
let mut symbol_object = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
global,
|
||||
"Symbol\0".as_ptr() as *const c_char,
|
||||
&mut symbol_object,
|
||||
)
|
||||
},
|
||||
"Get global object failed",
|
||||
);
|
||||
let mut iterator_symbol = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
symbol_object,
|
||||
"iterator\0".as_ptr() as *const c_char,
|
||||
&mut iterator_symbol,
|
||||
)
|
||||
},
|
||||
"Get Symbol.iterator failed",
|
||||
);
|
||||
let mut generator_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
"Iterator\0".as_ptr() as *const c_char,
|
||||
8,
|
||||
Some(symbol_generator::<T>),
|
||||
generator_ptr as *mut c_void,
|
||||
&mut generator_function,
|
||||
)
|
||||
},
|
||||
"Create iterator function failed",
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_set_property(env, instance, iterator_symbol, generator_function) },
|
||||
"Failed to set Symbol.iterator on class instance",
|
||||
);
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe extern "C" fn symbol_generator<T: Generator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 0;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
let mut generator_object = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_create_object(env, &mut generator_object) },
|
||||
"Create Generator object failed"
|
||||
);
|
||||
let mut next_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
"next\0".as_ptr() as *const c_char,
|
||||
4,
|
||||
Some(generator_next::<T>),
|
||||
generator_ptr,
|
||||
&mut next_function,
|
||||
)
|
||||
},
|
||||
"Create next function failed"
|
||||
);
|
||||
let mut return_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
"return\0".as_ptr() as *const c_char,
|
||||
6,
|
||||
Some(generator_return::<T>),
|
||||
generator_ptr,
|
||||
&mut return_function,
|
||||
)
|
||||
},
|
||||
"Create next function failed"
|
||||
);
|
||||
let mut throw_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
"throw\0".as_ptr() as *const c_char,
|
||||
5,
|
||||
Some(generator_throw::<T>),
|
||||
generator_ptr,
|
||||
&mut throw_function,
|
||||
)
|
||||
},
|
||||
"Create next function failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
"next\0".as_ptr() as *const c_char,
|
||||
next_function,
|
||||
)
|
||||
},
|
||||
"Set next function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
"return\0".as_ptr() as *const c_char,
|
||||
return_function,
|
||||
)
|
||||
},
|
||||
"Set return function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
"throw\0".as_ptr() as *const c_char,
|
||||
throw_function,
|
||||
)
|
||||
},
|
||||
"Set throw function on Generator object failed"
|
||||
);
|
||||
|
||||
let mut generator_state = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_boolean(env, false, &mut generator_state) },
|
||||
"Create generator state failed"
|
||||
);
|
||||
|
||||
let properties = vec![sys::napi_property_descriptor {
|
||||
utf8name: GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||
name: ptr::null_mut(),
|
||||
method: None,
|
||||
getter: None,
|
||||
setter: None,
|
||||
value: generator_state,
|
||||
attributes: sys::PropertyAttributes::writable,
|
||||
data: ptr::null_mut(),
|
||||
}];
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_define_properties(env, generator_object, 1, properties.as_ptr()) },
|
||||
"Define properties on Generator object failed"
|
||||
);
|
||||
|
||||
generator_object
|
||||
}
|
||||
|
||||
extern "C" fn generator_next<T: Generator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 1;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
let mut generator_state = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
this,
|
||||
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||
&mut generator_state,
|
||||
)
|
||||
},
|
||||
"Get generator state failed"
|
||||
);
|
||||
let mut completed = false;
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_value_bool(env, generator_state, &mut completed) },
|
||||
"Get generator state failed"
|
||||
);
|
||||
let mut result = std::ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_create_object(env, &mut result) },
|
||||
"Failed to create iterator result object",
|
||||
);
|
||||
if !completed {
|
||||
let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
|
||||
let item = if argc == 0 {
|
||||
g.next(None)
|
||||
} else {
|
||||
g.next(match unsafe { T::Next::from_napi_value(env, argv[0]) } {
|
||||
Ok(input) => Some(input),
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||
e.reason.as_ptr() as *mut c_char,
|
||||
)
|
||||
};
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
if let Some(value) = item {
|
||||
set_generator_value(env, result, value);
|
||||
} else {
|
||||
completed = true;
|
||||
}
|
||||
}
|
||||
let mut completed_value = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_boolean(env, completed, &mut completed_value) },
|
||||
"Failed to create completed value"
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
"done\0".as_ptr() as *const std::os::raw::c_char,
|
||||
completed_value,
|
||||
)
|
||||
},
|
||||
"Failed to set iterator result done",
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
extern "C" fn generator_return<T: Generator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 1;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
|
||||
let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
|
||||
if argc == 0 {
|
||||
g.complete(None);
|
||||
} else {
|
||||
g.complete(Some(
|
||||
match unsafe { T::Return::from_napi_value(env, argv[0]) } {
|
||||
Ok(input) => input,
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||
e.reason.as_ptr() as *mut c_char,
|
||||
)
|
||||
};
|
||||
return ptr::null_mut();
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
let mut generator_state = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_boolean(env, true, &mut generator_state) },
|
||||
"Create generator state failed"
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
this,
|
||||
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||
generator_state,
|
||||
)
|
||||
},
|
||||
"Get generator state failed"
|
||||
);
|
||||
let mut result = std::ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_create_object(env, &mut result) },
|
||||
"Failed to create iterator result object",
|
||||
);
|
||||
if argc > 0 {
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
"value\0".as_ptr() as *const std::os::raw::c_char,
|
||||
argv[0],
|
||||
)
|
||||
},
|
||||
"Failed to set iterator result value",
|
||||
);
|
||||
}
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
"done\0".as_ptr() as *const std::os::raw::c_char,
|
||||
generator_state,
|
||||
)
|
||||
},
|
||||
"Failed to set iterator result done",
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
extern "C" fn generator_throw<T: Generator>(
|
||||
env: sys::napi_env,
|
||||
info: sys::napi_callback_info,
|
||||
) -> sys::napi_value {
|
||||
let mut this = ptr::null_mut();
|
||||
let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
|
||||
let mut argc = 1;
|
||||
let mut generator_ptr = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_get_cb_info(
|
||||
env,
|
||||
info,
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
&mut this,
|
||||
&mut generator_ptr,
|
||||
)
|
||||
},
|
||||
"Get callback info from generator function failed"
|
||||
);
|
||||
|
||||
let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
|
||||
let catch_result = if argc == 0 {
|
||||
let mut undefined = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_undefined(env, &mut undefined) },
|
||||
"Get undefined failed"
|
||||
);
|
||||
g.catch(
|
||||
Env(env),
|
||||
Unknown(Value {
|
||||
env,
|
||||
value: undefined,
|
||||
value_type: crate::ValueType::Undefined,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
g.catch(
|
||||
Env(env),
|
||||
Unknown(Value {
|
||||
env,
|
||||
value: argv[0],
|
||||
value_type: crate::ValueType::Unknown,
|
||||
}),
|
||||
)
|
||||
};
|
||||
let mut result = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_create_object(env, &mut result) },
|
||||
"Failed to create iterator result object",
|
||||
);
|
||||
let mut generator_state = ptr::null_mut();
|
||||
let mut generator_state_value = false;
|
||||
match catch_result {
|
||||
Err(e) => {
|
||||
generator_state_value = true;
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
|
||||
"Create generator state failed"
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
this,
|
||||
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||
generator_state,
|
||||
)
|
||||
},
|
||||
"Get generator state failed"
|
||||
);
|
||||
let throw_status = unsafe { sys::napi_throw(env, e.0.value) };
|
||||
debug_assert!(
|
||||
throw_status == sys::Status::napi_ok,
|
||||
"Failed to throw error {}",
|
||||
crate::Status::from(throw_status)
|
||||
);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
Ok(Some(v)) => {
|
||||
set_generator_value(env, result, v);
|
||||
}
|
||||
Ok(None) => {
|
||||
generator_state_value = true;
|
||||
}
|
||||
}
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
|
||||
"Create generator state failed"
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
this,
|
||||
GENERATOR_STATE_KEY.as_ptr() as *const c_char,
|
||||
generator_state,
|
||||
)
|
||||
},
|
||||
"Get generator state failed"
|
||||
);
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
"done\0".as_ptr() as *const c_char,
|
||||
generator_state,
|
||||
)
|
||||
},
|
||||
"Get generator state failed"
|
||||
);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn set_generator_value<V: ToNapiValue>(env: sys::napi_env, result: sys::napi_value, value: V) {
|
||||
match unsafe { ToNapiValue::to_napi_value(env, value) } {
|
||||
Ok(val) => {
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
result,
|
||||
"value\0".as_ptr() as *const std::os::raw::c_char,
|
||||
val,
|
||||
)
|
||||
},
|
||||
"Failed to set iterator result value",
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
unsafe {
|
||||
sys::napi_throw_error(
|
||||
env,
|
||||
format!("{}", e.status).as_ptr() as *mut c_char,
|
||||
e.reason.as_ptr() as *mut c_char,
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ mod symbol;
|
|||
mod task;
|
||||
mod value_ref;
|
||||
|
||||
pub use crate::js_values::JsUnknown as Unknown;
|
||||
#[cfg(feature = "napi5")]
|
||||
pub use crate::JsDate as Date;
|
||||
pub use array::*;
|
||||
|
@ -159,7 +160,7 @@ pub trait ValidateNapiValue: FromNapiValue + TypeName {
|
|||
|
||||
impl<T: TypeName> TypeName for Option<T> {
|
||||
fn type_name() -> &'static str {
|
||||
"Option"
|
||||
T::type_name()
|
||||
}
|
||||
|
||||
fn value_type() -> ValueType {
|
||||
|
|
|
@ -170,6 +170,10 @@ impl<T: 'static, S: 'static> SharedReference<T, S> {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn clone_owner(&self, env: Env) -> Result<Reference<T>> {
|
||||
self.owner.clone(env)
|
||||
}
|
||||
|
||||
/// Safety to share because caller can provide `Env`
|
||||
pub fn share_with<U: 'static, F: FnOnce(&'static mut S) -> Result<U>>(
|
||||
self,
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::rc::Rc;
|
|||
pub use callback_info::*;
|
||||
pub use ctor::ctor;
|
||||
pub use env::*;
|
||||
pub use iterator::Generator;
|
||||
pub use js_values::*;
|
||||
pub use module_register::*;
|
||||
|
||||
|
@ -14,12 +15,14 @@ use crate::Status;
|
|||
mod callback_info;
|
||||
mod env;
|
||||
mod error;
|
||||
pub mod iterator;
|
||||
mod js_values;
|
||||
mod module_register;
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// called when node wrapper objects destroyed
|
||||
#[doc(hidden)]
|
||||
pub unsafe extern "C" fn raw_finalize_unchecked<T>(
|
||||
env: sys::napi_env,
|
||||
finalize_data: *mut c_void,
|
||||
|
@ -63,6 +66,7 @@ pub unsafe extern "C" fn raw_finalize_unchecked<T>(
|
|||
/// # Safety
|
||||
///
|
||||
/// called when node buffer is ready for gc
|
||||
#[doc(hidden)]
|
||||
pub unsafe extern "C" fn drop_buffer(
|
||||
_env: sys::napi_env,
|
||||
finalize_data: *mut c_void,
|
||||
|
|
|
@ -376,11 +376,7 @@ unsafe extern "C" fn napi_register_module_v1(
|
|||
}
|
||||
}
|
||||
let (ctor, props): (Vec<_>, Vec<_>) = props.iter().partition(|prop| prop.is_ctor);
|
||||
// one or more or zero?
|
||||
// zero is for `#[napi(task)]`
|
||||
if ctor.is_empty() && props.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let ctor = ctor.get(0).map(|c| c.raw().method.unwrap()).unwrap_or(noop);
|
||||
let raw_props: Vec<_> = props.iter().map(|prop| prop.raw()).collect();
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ pub use napi_sys as sys;
|
|||
pub use async_work::AsyncWorkPromise;
|
||||
pub use call_context::CallContext;
|
||||
|
||||
pub use bindgen_runtime::iterator;
|
||||
pub use env::*;
|
||||
pub use error::*;
|
||||
pub use js_values::*;
|
||||
|
@ -154,38 +155,6 @@ macro_rules! assert_type_of {
|
|||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) unsafe fn log_js_value<V: AsRef<[sys::napi_value]>>(
|
||||
// `info`, `log`, `warning` or `error`
|
||||
method: &str,
|
||||
env: sys::napi_env,
|
||||
values: V,
|
||||
) {
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
let mut g = ptr::null_mut();
|
||||
unsafe { sys::napi_get_global(env, &mut g) };
|
||||
let mut console = ptr::null_mut();
|
||||
let console_c_string = CString::new("console").unwrap();
|
||||
let method_c_string = CString::new(method).unwrap();
|
||||
unsafe { sys::napi_get_named_property(env, g, console_c_string.as_ptr(), &mut console) };
|
||||
let mut method_js_fn = ptr::null_mut();
|
||||
unsafe {
|
||||
sys::napi_get_named_property(env, console, method_c_string.as_ptr(), &mut method_js_fn)
|
||||
};
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
env,
|
||||
console,
|
||||
method_js_fn,
|
||||
values.as_ref().len(),
|
||||
values.as_ref().as_ptr(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
pub use crate::bindgen_runtime::ctor as module_init;
|
||||
|
||||
pub mod bindgen_prelude {
|
||||
|
@ -199,5 +168,45 @@ pub mod bindgen_prelude {
|
|||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
pub use crate::bindgen_runtime::{
|
||||
get_class_constructor, iterator::create_iterator, register_class,
|
||||
};
|
||||
|
||||
use crate::sys;
|
||||
|
||||
pub unsafe fn log_js_value<V: AsRef<[sys::napi_value]>>(
|
||||
// `info`, `log`, `warning` or `error`
|
||||
method: &str,
|
||||
env: sys::napi_env,
|
||||
values: V,
|
||||
) {
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
let mut g = ptr::null_mut();
|
||||
unsafe { sys::napi_get_global(env, &mut g) };
|
||||
let mut console = ptr::null_mut();
|
||||
let console_c_string = CString::new("console").unwrap();
|
||||
let method_c_string = CString::new(method).unwrap();
|
||||
unsafe { sys::napi_get_named_property(env, g, console_c_string.as_ptr(), &mut console) };
|
||||
let mut method_js_fn = ptr::null_mut();
|
||||
unsafe {
|
||||
sys::napi_get_named_property(env, console, method_c_string.as_ptr(), &mut method_js_fn)
|
||||
};
|
||||
unsafe {
|
||||
sys::napi_call_function(
|
||||
env,
|
||||
console,
|
||||
method_js_fn,
|
||||
values.as_ref().len(),
|
||||
values.as_ref().as_ptr(),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "tokio_rt")]
|
||||
pub extern crate tokio;
|
||||
|
|
51
examples/napi/__test__/generator.spec.ts
Normal file
51
examples/napi/__test__/generator.spec.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import test from 'ava'
|
||||
|
||||
import { Fib } from '../index'
|
||||
|
||||
test('should be able to stop a generator', (t) => {
|
||||
const fib = new Fib()
|
||||
const gen = fib[Symbol.iterator]
|
||||
t.is(typeof gen, 'function')
|
||||
const iterator = gen()
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: false,
|
||||
value: 1,
|
||||
})
|
||||
iterator.next()
|
||||
iterator.next()
|
||||
iterator.next()
|
||||
iterator.next()
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: false,
|
||||
value: 8,
|
||||
})
|
||||
t.deepEqual(iterator.return?.(), {
|
||||
done: true,
|
||||
})
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('should be able to throw to generator', (t) => {
|
||||
const fib = new Fib()
|
||||
const gen = fib[Symbol.iterator]
|
||||
t.is(typeof gen, 'function')
|
||||
const iterator = gen()
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: false,
|
||||
value: 1,
|
||||
})
|
||||
iterator.next()
|
||||
iterator.next()
|
||||
iterator.next()
|
||||
iterator.next()
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: false,
|
||||
value: 8,
|
||||
})
|
||||
t.throws(() => iterator.throw!(new Error()))
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: true,
|
||||
})
|
||||
})
|
|
@ -264,6 +264,10 @@ Generated by [AVA](https://avajs.dev).
|
|||
export class JsClassForEither {␊
|
||||
constructor()␊
|
||||
}␊
|
||||
export class Fib {␊
|
||||
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||
constructor()␊
|
||||
}␊
|
||||
export class JsRepo {␊
|
||||
constructor(dir: string)␊
|
||||
remote(): JsRemote␊
|
||||
|
|
Binary file not shown.
4
examples/napi/index.d.ts
vendored
4
examples/napi/index.d.ts
vendored
|
@ -254,6 +254,10 @@ export class ClassWithFactory {
|
|||
export class JsClassForEither {
|
||||
constructor()
|
||||
}
|
||||
export class Fib {
|
||||
[Symbol.iterator](): Iterator<number, void, number>
|
||||
constructor()
|
||||
}
|
||||
export class JsRepo {
|
||||
constructor(dir: string)
|
||||
remote(): JsRemote
|
||||
|
|
42
examples/napi/src/generator.rs
Normal file
42
examples/napi/src/generator.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use napi::bindgen_prelude::*;
|
||||
|
||||
#[napi(iterator)]
|
||||
pub struct Fib {
|
||||
current: u32,
|
||||
next: u32,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
impl Generator for Fib {
|
||||
type Yield = u32;
|
||||
type Next = i32;
|
||||
type Return = ();
|
||||
|
||||
fn next(&mut self, value: Option<Self::Next>) -> Option<Self::Yield> {
|
||||
match value {
|
||||
Some(n) => {
|
||||
self.current = n as u32;
|
||||
self.next = n as u32 + 1;
|
||||
}
|
||||
None => {
|
||||
let next = self.next;
|
||||
let current = self.current;
|
||||
self.current = next;
|
||||
self.next = current + next;
|
||||
}
|
||||
};
|
||||
Some(self.current)
|
||||
}
|
||||
}
|
||||
|
||||
#[napi]
|
||||
#[allow(clippy::new_without_default)]
|
||||
impl Fib {
|
||||
#[napi(constructor)]
|
||||
pub fn new() -> Self {
|
||||
Fib {
|
||||
current: 0,
|
||||
next: 1,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,6 +26,7 @@ mod error;
|
|||
mod external;
|
||||
mod fn_strict;
|
||||
mod fn_ts_override;
|
||||
mod generator;
|
||||
mod js_mod;
|
||||
mod map;
|
||||
mod nullable;
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use std::thread::sleep;
|
||||
|
||||
use napi::bindgen_prelude::*;
|
||||
use napi::Task;
|
||||
|
||||
struct DelaySum(u32, u32);
|
||||
|
||||
#[napi]
|
||||
impl Task for DelaySum {
|
||||
impl napi::Task for DelaySum {
|
||||
type Output = u32;
|
||||
type JsValue = u32;
|
||||
|
||||
|
|
Loading…
Reference in a new issue