mirror of
https://github.com/napi-rs/napi-rs.git
synced 2025-12-08 19:56:07 +00:00
feat: make generator an iterator (#2784)
* make the generator an iterator * add JSDoc for generators * prevent naming conflicts * add JSDoc struct * update snapshot * clippy * Update typedef * update fmt * Apply code review * test compatible --------- Co-authored-by: LongYinan <lynweklm@gmail.com>
This commit is contained in:
parent
1e30088198
commit
88fca29e3b
@ -197,8 +197,8 @@ Generated by [AVA](https://avajs.dev).
|
||||
␊
|
||||
export declare class Fib3 {␊
|
||||
current: number␊
|
||||
next: number␊
|
||||
constructor(current: number, next: number)␊
|
||||
nextNum: number␊
|
||||
constructor(current: number, nextNum: number)␊
|
||||
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||
}␊
|
||||
␊
|
||||
|
||||
@ -15,6 +15,7 @@ enum TypeDefKind {
|
||||
Type = 'type',
|
||||
Fn = 'fn',
|
||||
Struct = 'struct',
|
||||
Extends = 'extends',
|
||||
Impl = 'impl',
|
||||
}
|
||||
|
||||
@ -23,6 +24,7 @@ interface TypeDefLine {
|
||||
name: string
|
||||
original_name?: string
|
||||
def: string
|
||||
extends?: string
|
||||
js_doc?: string
|
||||
js_mod?: string
|
||||
}
|
||||
@ -57,7 +59,8 @@ function prettyPrint(
|
||||
break
|
||||
|
||||
case TypeDefKind.Struct:
|
||||
s += `${exportDeclare(ambient)} class ${line.name} {\n${line.def}\n}`
|
||||
const extendsDef = line.extends ? ` extends ${line.extends}` : ''
|
||||
s += `${exportDeclare(ambient)} class ${line.name}${extendsDef} {\n${line.def}\n}`
|
||||
if (line.original_name && line.original_name !== line.name) {
|
||||
s += `\nexport type ${line.original_name} = ${line.name}`
|
||||
}
|
||||
@ -185,6 +188,11 @@ function preprocessTypeDef(defs: TypeDefLine[]): Map<string, TypeDefLine[]> {
|
||||
if (def.kind === TypeDefKind.Struct) {
|
||||
group.push(def)
|
||||
classDefs.set(def.name, def)
|
||||
} else if (def.kind === TypeDefKind.Extends) {
|
||||
const classDef = classDefs.get(def.name)
|
||||
if (classDef) {
|
||||
classDef.extends = def.def
|
||||
}
|
||||
} else if (def.kind === TypeDefKind.Impl) {
|
||||
// merge `impl` into class definition
|
||||
const classDef = classDefs.get(def.name)
|
||||
|
||||
@ -87,6 +87,7 @@ pub struct NapiStruct {
|
||||
pub register_name: Ident,
|
||||
pub kind: NapiStructKind,
|
||||
pub has_lifetime: bool,
|
||||
pub is_generator: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
||||
@ -20,7 +20,7 @@ pub struct TypeDef {
|
||||
pub original_name: Option<String>,
|
||||
pub def: String,
|
||||
pub js_mod: Option<String>,
|
||||
pub js_doc: String,
|
||||
pub js_doc: JSDoc,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
@ -33,25 +33,6 @@ fn add_alias(name: String, alias: String) {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn js_doc_from_comments(comments: &[String]) -> String {
|
||||
if comments.is_empty() {
|
||||
return "".to_owned();
|
||||
}
|
||||
|
||||
if comments.len() == 1 {
|
||||
return format!("/**{} */\n", comments[0]);
|
||||
}
|
||||
|
||||
format!(
|
||||
"/**\n{} */\n",
|
||||
comments
|
||||
.iter()
|
||||
.map(|c| format!(" *{c}\n"))
|
||||
.collect::<Vec<String>>()
|
||||
.join("")
|
||||
)
|
||||
}
|
||||
|
||||
fn escape_json(src: &str) -> String {
|
||||
use std::fmt::Write;
|
||||
let mut escaped = String::with_capacity(src.len());
|
||||
@ -80,6 +61,83 @@ fn escape_json(src: &str) -> String {
|
||||
escaped
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct JSDoc {
|
||||
blocks: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
impl JSDoc {
|
||||
pub fn new<I, S>(initial_lines: I) -> JSDoc
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<String>,
|
||||
{
|
||||
let block = Self::cleanup_lines(initial_lines);
|
||||
if block.is_empty() {
|
||||
return Self { blocks: vec![] };
|
||||
}
|
||||
|
||||
Self {
|
||||
blocks: vec![block],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_block<I, S>(&mut self, lines: I)
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<String>,
|
||||
{
|
||||
let v: Vec<String> = Self::cleanup_lines(lines);
|
||||
|
||||
if !v.is_empty() {
|
||||
self.blocks.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
fn cleanup_lines<I, S>(lines: I) -> Vec<String>
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<String>,
|
||||
{
|
||||
let raw: Vec<String> = lines.into_iter().map(Into::into).collect();
|
||||
|
||||
if let (Some(first_non_blank), Some(last_non_blank)) = (
|
||||
raw.iter().position(|l| !l.trim().is_empty()),
|
||||
raw.iter().rposition(|l| !l.trim().is_empty()),
|
||||
) {
|
||||
raw[first_non_blank..=last_non_blank]
|
||||
.iter()
|
||||
.map(|l| l.trim().to_owned())
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for JSDoc {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
if self.blocks.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if self.blocks.len() == 1 && self.blocks[0].len() == 1 {
|
||||
return writeln!(f, "/** {} */", self.blocks[0][0]);
|
||||
}
|
||||
|
||||
writeln!(f, "/**")?;
|
||||
for (i, block) in self.blocks.iter().enumerate() {
|
||||
for line in block {
|
||||
writeln!(f, " * {line}")?;
|
||||
}
|
||||
if i + 1 != self.blocks.len() {
|
||||
writeln!(f, " *")?;
|
||||
}
|
||||
}
|
||||
writeln!(f, " */")
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TypeDef {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let js_mod = if let Some(js_mod) = &self.js_mod {
|
||||
@ -98,7 +156,7 @@ impl Display for TypeDef {
|
||||
r#"{{"kind": "{}", "name": "{}", "js_doc": "{}", "def": "{}"{}{}}}"#,
|
||||
self.kind,
|
||||
self.name,
|
||||
escape_json(&self.js_doc),
|
||||
escape_json(&self.js_doc.to_string()),
|
||||
escape_json(&self.def),
|
||||
original_name,
|
||||
js_mod,
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
use super::{ToTypeDef, TypeDef};
|
||||
|
||||
use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiConst};
|
||||
use crate::{
|
||||
ty_to_ts_type,
|
||||
typegen::{add_alias, JSDoc},
|
||||
NapiConst,
|
||||
};
|
||||
|
||||
impl ToTypeDef for NapiConst {
|
||||
fn to_type_def(&self) -> Option<TypeDef> {
|
||||
@ -20,7 +24,7 @@ impl ToTypeDef for NapiConst {
|
||||
ty_to_ts_type(&self.type_name, false, false, false).0
|
||||
),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: js_doc_from_comments(&self.comments),
|
||||
js_doc: JSDoc::new(&self.comments),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use super::{add_alias, ToTypeDef, TypeDef};
|
||||
use crate::{js_doc_from_comments, NapiEnum, NapiEnumValue};
|
||||
use crate::{typegen::JSDoc, NapiEnum, NapiEnumValue};
|
||||
|
||||
impl ToTypeDef for NapiEnum {
|
||||
fn to_type_def(&self) -> Option<TypeDef> {
|
||||
@ -18,7 +18,7 @@ impl ToTypeDef for NapiEnum {
|
||||
name: self.js_name.to_owned(),
|
||||
original_name: Some(self.name.to_string()),
|
||||
def: self.gen_ts_variants(),
|
||||
js_doc: js_doc_from_comments(&self.comments),
|
||||
js_doc: JSDoc::new(&self.comments),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
})
|
||||
}
|
||||
@ -34,7 +34,7 @@ impl NapiEnum {
|
||||
NapiEnumValue::Number(num) => format!("{num}"),
|
||||
NapiEnumValue::String(string) => format!("'{string}'"),
|
||||
};
|
||||
format!("{}{} = {}", js_doc_from_comments(&v.comments), v.name, val)
|
||||
format!("{}{} = {}", JSDoc::new(&v.comments), v.name, val)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n ")
|
||||
|
||||
@ -4,7 +4,7 @@ use std::fmt::{Display, Formatter};
|
||||
use syn::{Member, Pat, PathArguments, PathSegment};
|
||||
|
||||
use super::{r#struct::CLASS_STRUCTS, ty_to_ts_type, ToTypeDef, TypeDef};
|
||||
use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn};
|
||||
use crate::{typegen::JSDoc, CallbackArg, FnKind, NapiFn};
|
||||
|
||||
pub(crate) struct FnArg {
|
||||
pub(crate) arg: String,
|
||||
@ -100,7 +100,7 @@ impl ToTypeDef for NapiFn {
|
||||
original_name: None,
|
||||
def,
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: js_doc_from_comments(&self.comments),
|
||||
js_doc: JSDoc::new(&self.comments),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -329,7 +329,6 @@ impl NapiFn {
|
||||
} else {
|
||||
"void".to_owned()
|
||||
};
|
||||
|
||||
if self.is_async {
|
||||
format!(": Promise<{ret}>")
|
||||
} else {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::vec::Vec;
|
||||
use std::{cell::RefCell, iter};
|
||||
|
||||
use super::{add_alias, ToTypeDef, TypeDef};
|
||||
use crate::typegen::JSDoc;
|
||||
use crate::{
|
||||
format_js_property_name, js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct,
|
||||
NapiStructField, NapiStructKind,
|
||||
format_js_property_name, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructField, NapiStructKind,
|
||||
};
|
||||
|
||||
thread_local! {
|
||||
@ -20,6 +21,17 @@ impl ToTypeDef for NapiStruct {
|
||||
});
|
||||
add_alias(self.name.to_string(), self.js_name.to_string());
|
||||
|
||||
let mut js_doc = JSDoc::new(&self.comments);
|
||||
if self.is_generator {
|
||||
let generator_doc =[
|
||||
"This type extends JavaScript's `Iterator`, and so has the iterator helper",
|
||||
"methods. It may extend the upcoming TypeScript `Iterator` class in the future.",
|
||||
"",
|
||||
"@see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods",
|
||||
"@see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods", ];
|
||||
js_doc.add_block(generator_doc)
|
||||
}
|
||||
|
||||
Some(TypeDef {
|
||||
kind: String::from(match self.kind {
|
||||
NapiStructKind::Transparent(_) => "type",
|
||||
@ -32,7 +44,7 @@ impl ToTypeDef for NapiStruct {
|
||||
original_name: Some(self.name.to_string()),
|
||||
def: self.gen_ts_class(),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: js_doc_from_comments(&self.comments),
|
||||
js_doc,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -67,17 +79,17 @@ impl ToTypeDef for NapiImpl {
|
||||
"void".to_owned()
|
||||
};
|
||||
Some(TypeDef {
|
||||
kind: "impl".to_owned(),
|
||||
kind: "extends".to_owned(),
|
||||
name: self.js_name.to_owned(),
|
||||
original_name: None,
|
||||
def: format!(
|
||||
"[Symbol.iterator](): Iterator<{}, {}, {}>",
|
||||
"Iterator<{}, {}, {}>",
|
||||
ty_to_ts_type(output_type, false, true, false).0,
|
||||
return_type,
|
||||
next_type,
|
||||
),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: "".to_string(),
|
||||
js_doc: JSDoc::new::<Vec<String>, String>(Vec::default()),
|
||||
})
|
||||
} else {
|
||||
Some(TypeDef {
|
||||
@ -93,7 +105,7 @@ impl ToTypeDef for NapiImpl {
|
||||
} else {
|
||||
Some(format!(
|
||||
"{}{}",
|
||||
js_doc_from_comments(&f.comments),
|
||||
JSDoc::new(&f.comments),
|
||||
f.to_type_def()
|
||||
.map_or(String::default(), |type_def| type_def.def)
|
||||
))
|
||||
@ -102,7 +114,7 @@ impl ToTypeDef for NapiImpl {
|
||||
.collect::<Vec<_>>()
|
||||
.join("\\n"),
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: "".to_string(),
|
||||
js_doc: JSDoc::new::<Vec<String>, String>(Vec::default()),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -117,7 +129,7 @@ impl NapiStruct {
|
||||
let mut field_str = String::from("");
|
||||
|
||||
if !f.comments.is_empty() {
|
||||
field_str.push_str(&js_doc_from_comments(&f.comments))
|
||||
field_str.push_str(&format!("{}", JSDoc::new(&f.comments)))
|
||||
}
|
||||
|
||||
if !f.setter {
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
use super::{ToTypeDef, TypeDef};
|
||||
|
||||
use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiType};
|
||||
use crate::{
|
||||
ty_to_ts_type,
|
||||
typegen::{add_alias, JSDoc},
|
||||
NapiType,
|
||||
};
|
||||
|
||||
impl ToTypeDef for NapiType {
|
||||
fn to_type_def(&self) -> Option<TypeDef> {
|
||||
@ -16,7 +20,7 @@ impl ToTypeDef for NapiType {
|
||||
original_name: Some(self.name.to_string()),
|
||||
def: ty_to_ts_type(&self.value, false, false, false).0,
|
||||
js_mod: self.js_mod.to_owned(),
|
||||
js_doc: js_doc_from_comments(&self.comments),
|
||||
js_doc: JSDoc::new(&self.comments),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1270,6 +1270,22 @@ impl ConvertToAST for syn::ItemStruct {
|
||||
record_struct(&rust_struct_ident, final_js_name_for_struct.clone(), opts);
|
||||
let namespace = opts.namespace().map(|(m, _)| m.to_owned());
|
||||
let implement_iterator = opts.iterator().is_some();
|
||||
|
||||
if implement_iterator
|
||||
&& self
|
||||
.fields
|
||||
.iter()
|
||||
.filter(|f| matches!(f.vis, Visibility::Public(_)))
|
||||
.filter_map(|f| f.ident.clone())
|
||||
.map(|ident| ident.to_string())
|
||||
.any(|field_name| field_name == "next" || field_name == "throw" || field_name == "return")
|
||||
{
|
||||
bail_span!(
|
||||
self,
|
||||
"Generator structs cannot have public fields named `next`, `throw`, or `return`."
|
||||
);
|
||||
}
|
||||
|
||||
let generator_struct = GENERATOR_STRUCT.get_or_init(|| Mutex::new(HashMap::new()));
|
||||
let mut generator_struct = generator_struct
|
||||
.lock()
|
||||
@ -1393,6 +1409,7 @@ impl ConvertToAST for syn::ItemStruct {
|
||||
register_name: get_register_ident(format!("{rust_struct_ident}_struct").as_str()),
|
||||
comments: extract_doc_comments(&self.attrs),
|
||||
has_lifetime: lifetime.is_some(),
|
||||
is_generator: implement_iterator,
|
||||
}),
|
||||
})
|
||||
}
|
||||
@ -1556,6 +1573,7 @@ impl ConvertToAST for syn::ItemEnum {
|
||||
object_to_js: opts.object_to_js(),
|
||||
}),
|
||||
has_lifetime: false,
|
||||
is_generator: false,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -48,50 +48,193 @@ pub unsafe fn create_iterator<T: Generator>(
|
||||
let mut global = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe { sys::napi_get_global(env, &mut global) },
|
||||
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, c"Symbol".as_ptr().cast(), &mut symbol_object)
|
||||
},
|
||||
sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &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,
|
||||
c"iterator".as_ptr().cast(),
|
||||
&mut iterator_symbol,
|
||||
)
|
||||
},
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
symbol_object,
|
||||
c"iterator".as_ptr().cast(),
|
||||
&mut iterator_symbol,
|
||||
),
|
||||
"Get Symbol.iterator failed",
|
||||
);
|
||||
|
||||
let mut next_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"next".as_ptr().cast(),
|
||||
4,
|
||||
Some(generator_next::<T>),
|
||||
generator_ptr as *mut c_void,
|
||||
&mut next_function,
|
||||
),
|
||||
"Create next function failed"
|
||||
);
|
||||
|
||||
let mut return_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"return".as_ptr().cast(),
|
||||
6,
|
||||
Some(generator_return::<T>),
|
||||
generator_ptr as *mut c_void,
|
||||
&mut return_function,
|
||||
),
|
||||
"Create return function failed"
|
||||
);
|
||||
|
||||
let mut throw_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"throw".as_ptr().cast(),
|
||||
5,
|
||||
Some(generator_throw::<T>),
|
||||
generator_ptr as *mut c_void,
|
||||
&mut throw_function,
|
||||
),
|
||||
"Create throw function failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_set_named_property(env, instance, c"next".as_ptr().cast(), next_function,),
|
||||
"Set next function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_set_named_property(env, instance, c"return".as_ptr().cast(), return_function),
|
||||
"Set return function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_set_named_property(env, instance, c"throw".as_ptr().cast(), throw_function),
|
||||
"Set throw function on Generator object failed"
|
||||
);
|
||||
|
||||
let mut generator_state = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_get_boolean(env, false, &mut generator_state),
|
||||
"Create generator state failed"
|
||||
);
|
||||
|
||||
let properties = [sys::napi_property_descriptor {
|
||||
utf8name: GENERATOR_STATE_KEY.as_ptr().cast(),
|
||||
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,
|
||||
sys::napi_define_properties(env, instance, 1, properties.as_ptr()),
|
||||
"Define properties on Generator object failed"
|
||||
);
|
||||
|
||||
let mut generator_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"Iterator".as_ptr().cast(),
|
||||
8,
|
||||
Some(symbol_generator::<T>),
|
||||
generator_ptr as *mut c_void,
|
||||
&mut generator_function,
|
||||
)
|
||||
},
|
||||
sys::napi_create_function(
|
||||
env,
|
||||
c"Iterator".as_ptr().cast(),
|
||||
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) },
|
||||
sys::napi_set_property(env, instance, iterator_symbol, generator_function),
|
||||
"Failed to set Symbol.iterator on class instance",
|
||||
);
|
||||
|
||||
let mut iterator_ctor = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_get_named_property(env, global, c"Iterator".as_ptr().cast(), &mut iterator_ctor,),
|
||||
"Get Global.Iterator failed",
|
||||
);
|
||||
|
||||
let mut iterator_ctor_type = 0;
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_typeof(env, iterator_ctor, &mut iterator_ctor_type),
|
||||
"Get Global.Iterator type failed",
|
||||
);
|
||||
|
||||
if iterator_ctor_type == sys::ValueType::napi_function {
|
||||
let mut iterator_proto = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
iterator_ctor,
|
||||
c"prototype".as_ptr().cast(),
|
||||
&mut iterator_proto,
|
||||
),
|
||||
"Failed to get Iterator.prototype",
|
||||
);
|
||||
|
||||
let mut object_ctor = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_get_named_property(env, global, c"Object".as_ptr().cast(), &mut object_ctor),
|
||||
"Failed to get Object constructor"
|
||||
);
|
||||
|
||||
let mut set_prototype_function = ptr::null_mut();
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_get_named_property(
|
||||
env,
|
||||
object_ctor,
|
||||
c"setPrototypeOf".as_ptr().cast(),
|
||||
&mut set_prototype_function,
|
||||
),
|
||||
"Failed to get Object.setPrototypeOf"
|
||||
);
|
||||
|
||||
let mut argv = [instance, iterator_proto];
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
sys::napi_call_function(
|
||||
env,
|
||||
object_ctor,
|
||||
set_prototype_function,
|
||||
2,
|
||||
argv.as_mut_ptr(),
|
||||
ptr::null_mut(),
|
||||
),
|
||||
"Failed to set prototype on object"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -117,122 +260,8 @@ pub unsafe extern "C" fn symbol_generator<T: Generator>(
|
||||
},
|
||||
"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,
|
||||
c"next".as_ptr().cast(),
|
||||
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,
|
||||
c"return".as_ptr().cast(),
|
||||
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,
|
||||
c"throw".as_ptr().cast(),
|
||||
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,
|
||||
c"next".as_ptr().cast(),
|
||||
next_function,
|
||||
)
|
||||
},
|
||||
"Set next function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
c"return".as_ptr().cast(),
|
||||
return_function,
|
||||
)
|
||||
},
|
||||
"Set return function on Generator object failed"
|
||||
);
|
||||
|
||||
check_status_or_throw!(
|
||||
env,
|
||||
unsafe {
|
||||
sys::napi_set_named_property(
|
||||
env,
|
||||
generator_object,
|
||||
c"throw".as_ptr().cast(),
|
||||
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 = [sys::napi_property_descriptor {
|
||||
utf8name: GENERATOR_STATE_KEY.as_ptr().cast(),
|
||||
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
|
||||
this
|
||||
}
|
||||
|
||||
extern "C" fn generator_next<T: Generator>(
|
||||
|
||||
@ -214,21 +214,39 @@ Generated by [AVA](https://avajs.dev).
|
||||
constructor(name: string)␊
|
||||
}␊
|
||||
␊
|
||||
export declare class Fib {␊
|
||||
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||
/**␊
|
||||
* This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊
|
||||
* methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊
|
||||
*␊
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊
|
||||
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊
|
||||
*/␊
|
||||
export declare class Fib extends Iterator<number, void, number> {␊
|
||||
constructor()␊
|
||||
}␊
|
||||
␊
|
||||
export declare class Fib2 {␊
|
||||
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||
/**␊
|
||||
* This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊
|
||||
* methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊
|
||||
*␊
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊
|
||||
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊
|
||||
*/␊
|
||||
export declare class Fib2 extends Iterator<number, void, number> {␊
|
||||
static create(seed: number): Fib2␊
|
||||
}␊
|
||||
␊
|
||||
export declare class Fib3 {␊
|
||||
/**␊
|
||||
* This type extends JavaScript's \`Iterator\`, and so has the iterator helper␊
|
||||
* methods. It may extend the upcoming TypeScript \`Iterator\` class in the future.␊
|
||||
*␊
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods␊
|
||||
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods␊
|
||||
*/␊
|
||||
export declare class Fib3 extends Iterator<number, void, number> {␊
|
||||
current: number␊
|
||||
next: number␊
|
||||
constructor(current: number, next: number)␊
|
||||
[Symbol.iterator](): Iterator<number, void, number>␊
|
||||
nextNum: number␊
|
||||
constructor(current: number, nextNum: number)␊
|
||||
}␊
|
||||
␊
|
||||
export declare class GetterSetterWithClosures {␊
|
||||
|
||||
Binary file not shown.
@ -12,10 +12,10 @@ for (const [index, factory] of [
|
||||
() => new Fib3(0, 1),
|
||||
].entries()) {
|
||||
test(`should be able to stop a generator #${index}`, (t) => {
|
||||
const fib = factory()
|
||||
const gen = fib[Symbol.iterator]
|
||||
t.is(typeof gen, 'function')
|
||||
const iterator = gen()
|
||||
let iterator = factory()
|
||||
if (typeof Iterator === 'undefined') {
|
||||
iterator = iterator[Symbol.iterator]()
|
||||
}
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: false,
|
||||
value: 1,
|
||||
@ -37,10 +37,7 @@ for (const [index, factory] of [
|
||||
})
|
||||
|
||||
test(`should be able to throw to generator #${index}`, (t) => {
|
||||
const fib = factory()
|
||||
const gen = fib[Symbol.iterator]
|
||||
t.is(typeof gen, 'function')
|
||||
const iterator = gen()
|
||||
const iterator = factory()
|
||||
t.deepEqual(iterator.next(), {
|
||||
done: false,
|
||||
value: 1,
|
||||
@ -58,4 +55,21 @@ for (const [index, factory] of [
|
||||
done: true,
|
||||
})
|
||||
})
|
||||
|
||||
test(`should be an Iterator and have the Iterator Helper methods #${index}`, (t) => {
|
||||
if (typeof Iterator === 'undefined') {
|
||||
t.pass('Iterator is not existing, skipping test')
|
||||
return
|
||||
}
|
||||
const iterator = factory()
|
||||
|
||||
t.true(Object.getPrototypeOf(iterator) === Iterator.prototype)
|
||||
let arr = [
|
||||
...iterator
|
||||
.drop(3)
|
||||
.filter((x: number) => x % 2 == 0)
|
||||
.take(5),
|
||||
]
|
||||
t.deepEqual(arr, [8, 34, 144, 610, 2584])
|
||||
})
|
||||
}
|
||||
|
||||
@ -175,21 +175,39 @@ export declare class Dog {
|
||||
constructor(name: string)
|
||||
}
|
||||
|
||||
export declare class Fib {
|
||||
[Symbol.iterator](): Iterator<number, void, number>
|
||||
/**
|
||||
* This type extends JavaScript's `Iterator`, and so has the iterator helper
|
||||
* methods. It may extend the upcoming TypeScript `Iterator` class in the future.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
|
||||
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
|
||||
*/
|
||||
export declare class Fib extends Iterator<number, void, number> {
|
||||
constructor()
|
||||
}
|
||||
|
||||
export declare class Fib2 {
|
||||
[Symbol.iterator](): Iterator<number, void, number>
|
||||
/**
|
||||
* This type extends JavaScript's `Iterator`, and so has the iterator helper
|
||||
* methods. It may extend the upcoming TypeScript `Iterator` class in the future.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
|
||||
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
|
||||
*/
|
||||
export declare class Fib2 extends Iterator<number, void, number> {
|
||||
static create(seed: number): Fib2
|
||||
}
|
||||
|
||||
export declare class Fib3 {
|
||||
/**
|
||||
* This type extends JavaScript's `Iterator`, and so has the iterator helper
|
||||
* methods. It may extend the upcoming TypeScript `Iterator` class in the future.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
|
||||
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
|
||||
*/
|
||||
export declare class Fib3 extends Iterator<number, void, number> {
|
||||
current: number
|
||||
next: number
|
||||
constructor(current: number, next: number)
|
||||
[Symbol.iterator](): Iterator<number, void, number>
|
||||
nextNum: number
|
||||
constructor(current: number, nextNum: number)
|
||||
}
|
||||
|
||||
export declare class GetterSetterWithClosures {
|
||||
|
||||
@ -84,7 +84,7 @@ impl Fib2 {
|
||||
#[napi(iterator, constructor)]
|
||||
pub struct Fib3 {
|
||||
pub current: u32,
|
||||
pub next: u32,
|
||||
pub next_num: u32,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
@ -97,13 +97,13 @@ impl Generator for Fib3 {
|
||||
match value {
|
||||
Some(n) => {
|
||||
self.current = n as u32;
|
||||
self.next = n as u32 + 1;
|
||||
self.next_num = n as u32 + 1;
|
||||
}
|
||||
None => {
|
||||
let next = self.next;
|
||||
let next = self.next_num;
|
||||
let current = self.current;
|
||||
self.current = next;
|
||||
self.next = current + next;
|
||||
self.next_num = current + next;
|
||||
}
|
||||
};
|
||||
Some(self.current)
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"importsNotUsedAsValues": "remove",
|
||||
"outDir": "scripts",
|
||||
"lib": ["ES2022"]
|
||||
"lib": ["ESNext"]
|
||||
},
|
||||
"include": [],
|
||||
"references": [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user