From a3356264f27489dabe0abcad6c34651de554c111 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Fri, 6 May 2022 17:40:46 +0800 Subject: [PATCH] feat(napi): experimental iterator support --- crates/backend/src/ast.rs | 5 + crates/backend/src/codegen/fn.rs | 10 +- crates/backend/src/codegen/struct.rs | 55 +- crates/backend/src/lib.rs | 1 + crates/backend/src/typegen.rs | 17 +- crates/backend/src/typegen/fn.rs | 16 +- crates/backend/src/typegen/struct.rs | 74 ++- crates/macro/src/parser/attrs.rs | 1 + crates/macro/src/parser/mod.rs | 66 +- .../napi/src/bindgen_runtime/callback_info.rs | 27 +- crates/napi/src/bindgen_runtime/error.rs | 1 - crates/napi/src/bindgen_runtime/iterator.rs | 581 ++++++++++++++++++ crates/napi/src/bindgen_runtime/js_values.rs | 3 +- .../bindgen_runtime/js_values/value_ref.rs | 4 + crates/napi/src/bindgen_runtime/mod.rs | 4 + .../src/bindgen_runtime/module_register.rs | 6 +- crates/napi/src/lib.rs | 73 ++- examples/napi/__test__/generator.spec.ts | 51 ++ examples/napi/__test__/typegen.spec.ts.md | 4 + examples/napi/__test__/typegen.spec.ts.snap | Bin 3018 -> 3048 bytes examples/napi/index.d.ts | 4 + examples/napi/src/generator.rs | 42 ++ examples/napi/src/lib.rs | 1 + examples/napi/src/task.rs | 3 +- 24 files changed, 947 insertions(+), 102 deletions(-) create mode 100644 crates/napi/src/bindgen_runtime/iterator.rs create mode 100644 examples/napi/__test__/generator.spec.ts create mode 100644 examples/napi/src/generator.rs diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index c832ce3f..18d17f1d 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -21,6 +21,7 @@ pub struct NapiFn { pub ts_return_type: Option, pub skip_typescript: bool, pub comments: Vec, + pub parent_is_generator: bool, } #[derive(Debug, Clone)] @@ -62,6 +63,7 @@ pub struct NapiStruct { pub kind: NapiStructKind, pub js_mod: Option, pub comments: Vec, + pub implement_iterator: bool, } #[derive(Debug, Clone, PartialEq)] @@ -89,6 +91,9 @@ pub struct NapiImpl { pub js_name: String, pub items: Vec, pub task_output_type: Option, + pub iterator_yield_type: Option, + pub iterator_next_type: Option, + pub iterator_return_type: Option, pub js_mod: Option, pub comments: Vec, } diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index ee997c8a..352aa7ab 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -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) } } diff --git a/crates/backend/src/codegen/struct.rs b/crates/backend/src/codegen/struct.rs index 9fd954d7..8a8d6b85 100644 --- a/crates/backend/src/codegen/struct.rs +++ b/crates/backend/src/codegen/struct.rs @@ -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::() @@ -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 { - 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> { 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 { 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),*]); } } }) diff --git a/crates/backend/src/lib.rs b/crates/backend/src/lib.rs index 4e4c0217..332ab69a 100644 --- a/crates/backend/src/lib.rs +++ b/crates/backend/src/lib.rs @@ -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)),* } diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index ee05d7cc..21b80a52 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -179,7 +179,6 @@ static KNOWN_TYPES: Lazy> = Lazy::new(|| { ("Either3", "{} | {} | {}"), ("Either4", "{} | {} | {} | {}"), ("Either5", "{} | {} | {} | {} | {}"), - ("unknown", "unknown"), ("Null", "null"), ("JsNull", "null"), ("null", "null"), @@ -190,6 +189,8 @@ static KNOWN_TYPES: Lazy> = 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 + 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()) { diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 3cae752c..3bf6feab 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -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() { diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index 36968760..16049349 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -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::>() - .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::>() + .join("\\n"), + js_mod: self.js_mod.to_owned(), + js_doc: "".to_string(), + }) + } } } diff --git a/crates/macro/src/parser/attrs.rs b/crates/macro/src/parser/attrs.rs index aaa5cd20..c317a87a 100644 --- a/crates/macro/src/parser/attrs.rs +++ b/crates/macro/src/parser/attrs.rs @@ -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)), diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index a331ea41..2f898072 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -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> = 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), }), }) diff --git a/crates/napi/src/bindgen_runtime/callback_info.rs b/crates/napi/src/bindgen_runtime/callback_info.rs index cd31e566..7f3d33a2 100644 --- a/crates/napi/src/bindgen_runtime/callback_info.rs +++ b/crates/napi/src/bindgen_runtime/callback_info.rs @@ -66,10 +66,10 @@ impl CallbackInfo { self.this } - pub fn construct(&self, js_name: &str, obj: T) -> Result { + fn _construct(&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 = Box::new(|| {}); let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize)))); @@ -78,7 +78,7 @@ impl CallbackInfo { sys::napi_wrap( self.env, this, - value_ref, + value_ref as *mut c_void, Some(raw_finalize_unchecked::), ptr::null_mut(), &mut object_ref @@ -88,8 +88,25 @@ impl CallbackInfo { )?; }; - Reference::::add_ref(value_ref, (value_ref, object_ref, finalize_callbacks_ptr)); - Ok(this) + Reference::::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(&self, js_name: &str, obj: T) -> Result { + self._construct(js_name, obj).map(|(v, _)| v) + } + + pub fn construct_generator( + &self, + js_name: &str, + obj: T, + ) -> Result { + let (instance, generator_ptr) = self._construct(js_name, obj)?; + crate::__private::create_iterator(self.env, instance, generator_ptr); + Ok(instance) } pub fn factory(&self, js_name: &str, obj: T) -> Result { diff --git a/crates/napi/src/bindgen_runtime/error.rs b/crates/napi/src/bindgen_runtime/error.rs index 8091710d..dd5a28ed 100644 --- a/crates/napi/src/bindgen_runtime/error.rs +++ b/crates/napi/src/bindgen_runtime/error.rs @@ -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; } }; } diff --git a/crates/napi/src/bindgen_runtime/iterator.rs b/crates/napi/src/bindgen_runtime/iterator.rs new file mode 100644 index 00000000..7d4a8dbe --- /dev/null +++ b/crates/napi/src/bindgen_runtime/iterator.rs @@ -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) -> Option; + + #[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) -> Option { + 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, Unknown> { + Err(value) + } +} + +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn create_iterator( + 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::), + 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( + 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::), + 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::), + 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::), + 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( + 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( + 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( + 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(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, + ) + }; + } + } +} diff --git a/crates/napi/src/bindgen_runtime/js_values.rs b/crates/napi/src/bindgen_runtime/js_values.rs index 54ebcd88..a869527a 100644 --- a/crates/napi/src/bindgen_runtime/js_values.rs +++ b/crates/napi/src/bindgen_runtime/js_values.rs @@ -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 TypeName for Option { fn type_name() -> &'static str { - "Option" + T::type_name() } fn value_type() -> ValueType { diff --git a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs index a6bb39b8..981996ff 100644 --- a/crates/napi/src/bindgen_runtime/js_values/value_ref.rs +++ b/crates/napi/src/bindgen_runtime/js_values/value_ref.rs @@ -170,6 +170,10 @@ impl SharedReference { }) } + pub fn clone_owner(&self, env: Env) -> Result> { + self.owner.clone(env) + } + /// Safety to share because caller can provide `Env` pub fn share_with Result>( self, diff --git a/crates/napi/src/bindgen_runtime/mod.rs b/crates/napi/src/bindgen_runtime/mod.rs index 2e10e27a..ba4c2c4b 100644 --- a/crates/napi/src/bindgen_runtime/mod.rs +++ b/crates/napi/src/bindgen_runtime/mod.rs @@ -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( env: sys::napi_env, finalize_data: *mut c_void, @@ -63,6 +66,7 @@ pub unsafe extern "C" fn raw_finalize_unchecked( /// # 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, diff --git a/crates/napi/src/bindgen_runtime/module_register.rs b/crates/napi/src/bindgen_runtime/module_register.rs index 1b500831..dad3da85 100644 --- a/crates/napi/src/bindgen_runtime/module_register.rs +++ b/crates/napi/src/bindgen_runtime/module_register.rs @@ -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(); diff --git a/crates/napi/src/lib.rs b/crates/napi/src/lib.rs index 69b30e99..4424e987 100644 --- a/crates/napi/src/lib.rs +++ b/crates/napi/src/lib.rs @@ -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>( - // `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>( + // `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; diff --git a/examples/napi/__test__/generator.spec.ts b/examples/napi/__test__/generator.spec.ts new file mode 100644 index 00000000..c636fe2d --- /dev/null +++ b/examples/napi/__test__/generator.spec.ts @@ -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, + }) +}) diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 9700e0f1..2c845b38 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -264,6 +264,10 @@ Generated by [AVA](https://avajs.dev). export class JsClassForEither {␊ constructor()␊ }␊ + export class Fib {␊ + [Symbol.iterator](): Iterator␊ + constructor()␊ + }␊ export class JsRepo {␊ constructor(dir: string)␊ remote(): JsRemote␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 7743c80efa3ef74f44de036a0973db5c0ae8ad82..a3b5cf475ca1f65f6e538873687af4c4dcf8e365 100644 GIT binary patch delta 3018 zcmV;*3pMo07w8vSK~_N^Q*L2!b7*gLAa*kf0|4j>=W)G0oWi}_rTBL#mps!f*$Mik zm**4JVS>!f-rFCG2mk;800003ts2{I8@V-YQ55jo{sEh!*iqm(z63!kI1Xgl&f1Y$ z9DgtDC-fY0IK#O~@^%9U9&*l|=gRTV!$BzgYx&bJOmP*myT8h@NWy@OSj1wg81Y3M zGhY!kn=%rxAsOI>{rd8KJ6&G2NguWzRvtiCZ6CPeB&Te1Ry z_B(*X?#Nl>dsC{$U@)x&;GD%`!X?|@O@_ldnJ6M1C!yl~Tj9}oWCd|>oAep*5x7Vv z2t3+O7pr7oA6xdJg6D(8itX~zVSl8&<-iVKy_^GSw&AcYUcKzdp4QzPa7PkjkGiAl z-Ugme!*D?T>o!3PtY_vUZ+6Iy-~kZ8UlnvQ#b5hTBZXB0tBtpINqz+XhS=m0#Lkak z=rCN8M^DjMHF2pSYZyOL5zS%s{s2NFJmc==0v% z+8Pjsj6tK><&~xZPt|Zq1w7X@Z0z=TJPUCcO%nz5G=)Bhl*6nNM?DtDxpwA|!CsIe zApN=;6iHO%fpWw8;zZ{TkAEB9d$8A>>ilsmBC)6Oj(Hez`B*&2MH|OFN)#($;xCH_ z8G*$^>NCA7brq86`Y9M8EDGMHi-!s~#?_d`Cdr(}Mg6jq&vIyk$mXE;H7Sdn;gqN} zNU+tw6dndbgp5WN_PORzQMBGSRc=ihzg)-dv`(mPJnuO^9k0%DYccf+YC6LLet1bl+V*il6ad`Uo)v9&3Ui;y8WJXx>w8XEUK zj{FbvbAa{;;Qwcc5RpYHlB=Av#i!!k+C!?rQYpXpO064nSWSX7`~=sur3 z0wX`o$NPh&k$*K4{=ioMMdit&enaK>m`<%J&lyaa!M1JomX_cInh3P3?tvB?^}@DI zh?DhDOCZ`}++JG2w14rYm1$>*`NtAT!(Tdq-!N}v7(s-RYAME8^bIVa+>u}S?o2$0VE+f>M3f{YJWQFx{=l*nW^@%E;6ChQ{D1= zV4Js2&93y$&O2N;v{c&FTJ3otxqCIGJZeT-!%vU2i~MQG2O^@r&%J0dFf_Ibg7`g~ zNe^avJJ?rlAnJ?G{L271#do0?P;CUf3lc*z(i?T4i%p>~H>xJhJIPKMngg_nNp}k9 zvDqX9YkxXo+2Pid`m~d)&_eS7UX@3!f4B-beN5-WVW+31>ex1H7kiFe4p$q97#2W8 zC#fdj2busp4(4bujLwY?K}rIHyFhJ~u#{9u)SNpe5?__fm|3yWY%ZE7okhDqan2C~ z*a0NFltiO^eTjv+JEqZynNAly>=fqfmGqt)j&EAH?$Vv{0pLKvKoL=6KY1U}s?>H`dpes|1DX$H=h|cG*)HqruKu&*j?`#NXIW=>=k*CzuW7z6;5OGv7|m4=?`O^h#5%xCJ-orz*C4HS{S^o_ z;FL$wlQ4z&EWeh5(G6sB>T^@I1qwdJa9F0A8{i8y+<3J-_y-EJ*TypNvDJG?aU{gC z?j{Z+^mo%)I2}eq#W1m#b|fteUje&?$bS@x>I`7F3wDl07()Ctl*vV{6H;4-)GCpD zq=y&{-P4OD=;L)3&ZRVvbm(-z`7Kxdty)u#R25qNeA3i?_5Vxn0DL<<76JQ>$^B{( zAz;$Mt!10XYUZ3`GO_brGIYAk1`Hf^@!nz~X^0j`a_VAhvoZ&!TMNIp6o+(YrhjY| z=rjz{)fx046x3l}YqfyIY?u$L=+ya2JGNeSLprhKn+wxGo92v4iQbls#w8ms^>OE% z1&PmU�!Xt&nHrlWv}{XA$wI^hRD+wohjF6V!t*3CBxxh$rZMSr|y71Q|e;N(LeK zSCNdstF~L959xPA_+$#8C=+qWBSzd`F7c+`b9P%-EAEw){+!r}_=JYp^_sqGgUU$F zwl0d!`!Wpg9{}j=5SW%dHY2N}!Sh!i-E9ldcFRe+0kTJrD6J z%@>mii?iDS);3_@UtZlm zy!gKT^l7Goncz;*v-FiNf64^n)x5z@(p(yLo%6+f!{flYPu0$aH?%dKnCltEb1C5? z&BqbYZqS7aubjp-qpCmR?}qf6Z4U4bhT)y6S?`*)Zb5q!agA5;*edA&)p+UWd0yx~ zQ#oA{_QOZ!vTWvEvsv3RhIOl5;z~#|4fLItZpE@pqH|%b_&_C>e;pRJ0wK^nDH#=8 z19oQyp$VN0*a>6!UB>oqMf0|b%ItxBsgPOUloCX8I%u6pc&JM@=t6tu=A+!rQ!b!k^Zk=3V|E3+jBY{qmm9` z<9NqZUUx-k0wQC8f3touFBMPpNOq*ezleB4%tOg&=H-3O!2o5C2Tp&OlCpO>wR|j_ z8ZJ#t^dG&PQ8lg$DuZvwQP=zP@Azq93-S{a`S{xWUDr+IE=lv*-jbO69lgwXK24O> zlHGU|-CU#jAL^Uf>reBKGfFwGZF)XPOK6R+JCS@+sfz6ie+g4S>mU^rx3U1`&w(}G zRKhsm0}XU(#-uf_FvE)Y3jORbU2NNt1(RWZWx9a$kvwNpQ4t~Fv0E%+DCbH?Cc@)$ z9Ua`ytl1dG-CplJ3E2lOF~qu3+EwY8@)w#Wgcq)B{!*$!zNcm(>x~&p8RY8<3kS4p zf*84#c-c6Qf2}00_yoER-EX$B&T>Kbayx2 z(ny8t%g%?xcPGdDCw-^3)Lis${qX8#(=ER#8vwMi`8r3^b)tq60L>@#5#=;`yEM$s zN2^3NHydozI~dHYlPL4ktpvUg_z3zM&lTaJ!FHRj9PM0%Ht||-!Cij6O+PODfby{N MKS67@dzL8x0BIH6>Hq)$ delta 2987 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 diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 96b1ae39..71da4b96 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -254,6 +254,10 @@ export class ClassWithFactory { export class JsClassForEither { constructor() } +export class Fib { + [Symbol.iterator](): Iterator + constructor() +} export class JsRepo { constructor(dir: string) remote(): JsRemote diff --git a/examples/napi/src/generator.rs b/examples/napi/src/generator.rs new file mode 100644 index 00000000..48f24658 --- /dev/null +++ b/examples/napi/src/generator.rs @@ -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) -> Option { + 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, + } + } +} diff --git a/examples/napi/src/lib.rs b/examples/napi/src/lib.rs index 611ce6c8..3cf73749 100644 --- a/examples/napi/src/lib.rs +++ b/examples/napi/src/lib.rs @@ -26,6 +26,7 @@ mod error; mod external; mod fn_strict; mod fn_ts_override; +mod generator; mod js_mod; mod map; mod nullable; diff --git a/examples/napi/src/task.rs b/examples/napi/src/task.rs index fbbd2492..b2f1c4f1 100644 --- a/examples/napi/src/task.rs +++ b/examples/napi/src/task.rs @@ -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;