Merge pull request #1177 from napi-rs/iterator-default-constructor
fix(napi): missing iterator implementation from class factory
This commit is contained in:
commit
3d4c421585
7 changed files with 192 additions and 75 deletions
|
@ -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) }
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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␊
|
||||
|
|
Binary file not shown.
10
examples/napi/index.d.ts
vendored
10
examples/napi/index.d.ts
vendored
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue