From 47fcc8501ab8864e70e72a1a68e2c71807d3744a Mon Sep 17 00:00:00 2001 From: LongYinan Date: Tue, 10 May 2022 21:46:16 +0800 Subject: [PATCH] fix(napi): missing iterator implementation from class factory --- crates/backend/src/codegen/fn.rs | 8 +- .../napi/src/bindgen_runtime/callback_info.rs | 74 ++++++++------ examples/napi/__test__/generator.spec.ts | 96 ++++++++++-------- examples/napi/__test__/typegen.spec.ts.md | 10 ++ examples/napi/__test__/typegen.spec.ts.snap | Bin 3074 -> 3114 bytes examples/napi/index.d.ts | 10 ++ examples/napi/src/generator.rs | 69 +++++++++++++ 7 files changed, 192 insertions(+), 75 deletions(-) diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index c50c1b25..31d0e1c1 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -294,7 +294,13 @@ impl NapiFn { } } else if self.kind == FnKind::Factory { if self.is_ret_result { - quote! { cb.factory(#js_name, #ret?) } + if self.parent_is_generator { + quote! { cb.generator_factory(#js_name, #ret?) } + } else { + quote! { cb.factory(#js_name, #ret?) } + } + } else if self.parent_is_generator { + quote! { cb.generator_factory(#js_name, #ret) } } else { quote! { cb.factory(#js_name, #ret) } } diff --git a/crates/napi/src/bindgen_runtime/callback_info.rs b/crates/napi/src/bindgen_runtime/callback_info.rs index 10ec5770..6f0a2912 100644 --- a/crates/napi/src/bindgen_runtime/callback_info.rs +++ b/crates/napi/src/bindgen_runtime/callback_info.rs @@ -113,43 +113,59 @@ impl CallbackInfo { } pub fn factory(&self, js_name: &str, obj: T) -> Result { + self._factory(js_name, obj).map(|(value, _)| value) + } + + pub fn generator_factory( + &self, + js_name: &str, + obj: T, + ) -> Result { + let (instance, generator_ptr) = self._factory(js_name, obj)?; + crate::__private::create_iterator(self.env, instance, generator_ptr); + Ok(instance) + } + + fn _factory(&self, js_name: &str, obj: T) -> Result<(sys::napi_value, *mut T)> { let this = self.this(); let mut instance = ptr::null_mut(); - unsafe { - let inner = ___CALL_FROM_FACTORY.get_or_default(); - inner.store(true, Ordering::Relaxed); - let status = sys::napi_new_instance(self.env, this, 0, ptr::null_mut(), &mut instance); - inner.store(false, Ordering::Relaxed); - // Error thrown in `constructor` - if status == sys::Status::napi_pending_exception { - let mut exception = ptr::null_mut(); - sys::napi_get_and_clear_last_exception(self.env, &mut exception); - sys::napi_throw(self.env, exception); - return Ok(ptr::null_mut()); - } - let obj = Box::new(obj); - let initial_finalize: Box = Box::new(|| {}); - let finalize_callbacks_ptr = - Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize)))); - let mut object_ref = ptr::null_mut(); - let value_ref = Box::into_raw(obj) as *mut c_void; - check_status!( + let inner = ___CALL_FROM_FACTORY.get_or_default(); + inner.store(true, Ordering::Relaxed); + let status = + unsafe { sys::napi_new_instance(self.env, this, 0, ptr::null_mut(), &mut instance) }; + inner.store(false, Ordering::Relaxed); + // Error thrown in `constructor` + if status == sys::Status::napi_pending_exception { + let mut exception = ptr::null_mut(); + unsafe { sys::napi_get_and_clear_last_exception(self.env, &mut exception) }; + unsafe { sys::napi_throw(self.env, exception) }; + return Ok((ptr::null_mut(), ptr::null_mut())); + } + let obj = Box::new(obj); + let initial_finalize: Box = Box::new(|| {}); + let finalize_callbacks_ptr = Rc::into_raw(Rc::new(Cell::new(Box::into_raw(initial_finalize)))); + let mut object_ref = ptr::null_mut(); + let value_ref = Box::into_raw(obj); + check_status!( + unsafe { sys::napi_wrap( self.env, instance, - value_ref, + value_ref as *mut c_void, Some(raw_finalize_unchecked::), ptr::null_mut(), - &mut object_ref - ), - "Failed to initialize class `{}`", - js_name, - )?; + &mut object_ref, + ) + }, + "Failed to initialize class `{}`", + js_name, + )?; - Reference::::add_ref(value_ref, (value_ref, object_ref, finalize_callbacks_ptr)); - }; - - Ok(instance) + Reference::::add_ref( + value_ref as *mut c_void, + (value_ref as *mut c_void, object_ref, finalize_callbacks_ptr), + ); + Ok((instance, value_ref)) } pub fn unwrap_borrow_mut(&mut self) -> Result<&'static mut T> diff --git a/examples/napi/__test__/generator.spec.ts b/examples/napi/__test__/generator.spec.ts index c636fe2d..2dde4497 100644 --- a/examples/napi/__test__/generator.spec.ts +++ b/examples/napi/__test__/generator.spec.ts @@ -1,51 +1,57 @@ import test from 'ava' -import { Fib } from '../index' +import { Fib, Fib2, Fib3 } 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, +for (const [index, factory] of [ + () => new Fib(), + () => Fib2.create(0), + () => 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() + 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, + }) }) - 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, + 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() + 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, + }) }) - 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 3816e402..700fd349 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -268,6 +268,16 @@ Generated by [AVA](https://avajs.dev). [Symbol.iterator](): Iterator␊ constructor()␊ }␊ + export class Fib2 {␊ + [Symbol.iterator](): Iterator␊ + static create(seed: number): Fib2␊ + }␊ + export class Fib3 {␊ + current: number␊ + next: number␊ + constructor(current: number, next: number)␊ + [Symbol.iterator](): Iterator␊ + }␊ 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 708538988c9b262ceec7b59ec7eeae2ef28a7804..d3ff3d429dcc50e953f473a1a31edb899f7eea8e 100644 GIT binary patch literal 3114 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 QnfsOh0ap)4hv+E)0B%Iz#Q*>R diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 11f42f2a..b50df571 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -258,6 +258,16 @@ export class Fib { [Symbol.iterator](): Iterator constructor() } +export class Fib2 { + [Symbol.iterator](): Iterator + static create(seed: number): Fib2 +} +export class Fib3 { + current: number + next: number + constructor(current: number, next: number) + [Symbol.iterator](): Iterator +} export class JsRepo { constructor(dir: string) remote(): JsRemote diff --git a/examples/napi/src/generator.rs b/examples/napi/src/generator.rs index 48f24658..b3b0ca0e 100644 --- a/examples/napi/src/generator.rs +++ b/examples/napi/src/generator.rs @@ -40,3 +40,72 @@ impl Fib { } } } + +#[napi(iterator)] +pub struct Fib2 { + current: u32, + next: u32, +} + +#[napi] +impl Generator for Fib2 { + 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] +impl Fib2 { + #[napi(factory)] + pub fn create(seed: u32) -> Self { + Self { + current: seed, + next: seed + 1, + } + } +} + +#[napi(iterator, constructor)] +pub struct Fib3 { + pub current: u32, + pub next: u32, +} + +#[napi] +impl Generator for Fib3 { + 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) + } +}