Merge pull request #1177 from napi-rs/iterator-default-constructor

fix(napi): missing iterator implementation from class factory
This commit is contained in:
LongYinan 2022-05-10 22:23:45 +08:00 committed by GitHub
commit 3d4c421585
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 192 additions and 75 deletions

View file

@ -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) }
}

View file

@ -113,43 +113,59 @@ impl<const N: usize> CallbackInfo<N> {
}
pub fn factory<T: 'static>(&self, js_name: &str, obj: T) -> Result<sys::napi_value> {
self._factory(js_name, obj).map(|(value, _)| value)
}
pub fn generator_factory<T: Generator + 'static>(
&self,
js_name: &str,
obj: T,
) -> Result<sys::napi_value> {
let (instance, generator_ptr) = self._factory(js_name, obj)?;
crate::__private::create_iterator(self.env, instance, generator_ptr);
Ok(instance)
}
fn _factory<T: 'static>(&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<dyn FnOnce()> = 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<dyn FnOnce()> = 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::<T>),
ptr::null_mut(),
&mut object_ref
),
"Failed to initialize class `{}`",
js_name,
)?;
&mut object_ref,
)
},
"Failed to initialize class `{}`",
js_name,
)?;
Reference::<T>::add_ref(value_ref, (value_ref, object_ref, finalize_callbacks_ptr));
};
Ok(instance)
Reference::<T>::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<T>(&mut self) -> Result<&'static mut T>

View file

@ -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,
})
})
}

View file

@ -268,6 +268,16 @@ Generated by [AVA](https://avajs.dev).
[Symbol.iterator](): Iterator<number, void, number>
constructor()␊
}␊
export class Fib2 {␊
[Symbol.iterator](): Iterator<number, void, number>
static create(seed: number): Fib2␊
}␊
export class Fib3 {␊
current: number␊
next: number␊
constructor(current: number, next: number)␊
[Symbol.iterator](): Iterator<number, void, number>
}␊
export class JsRepo {␊
constructor(dir: string)␊
remote(): JsRemote␊

View file

@ -258,6 +258,16 @@ export class Fib {
[Symbol.iterator](): Iterator<number, void, number>
constructor()
}
export class Fib2 {
[Symbol.iterator](): Iterator<number, void, number>
static create(seed: number): Fib2
}
export class Fib3 {
current: number
next: number
constructor(current: number, next: number)
[Symbol.iterator](): Iterator<number, void, number>
}
export class JsRepo {
constructor(dir: string)
remote(): JsRemote

View file

@ -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<Self::Next>) -> Option<Self::Yield> {
match value {
Some(n) => {
self.current = n as u32;
self.next = n as u32 + 1;
}
None => {
let next = self.next;
let current = self.current;
self.current = next;
self.next = current + next;
}
};
Some(self.current)
}
}
#[napi]
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<Self::Next>) -> Option<Self::Yield> {
match value {
Some(n) => {
self.current = n as u32;
self.next = n as u32 + 1;
}
None => {
let next = self.next;
let current = self.current;
self.current = next;
self.next = current + next;
}
};
Some(self.current)
}
}