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 70853898..d3ff3d42 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ 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) + } +}