napi/bindgen_runtime/
iterator.rs

1use std::ffi::{c_void, CStr};
2use std::ptr;
3
4use crate::Value;
5use crate::{bindgen_runtime::Unknown, check_status_or_throw, sys, Env};
6
7use super::{FromNapiValue, ToNapiValue};
8
9const GENERATOR_STATE_KEY: &CStr = c"[[GeneratorState]]";
10
11/// Implement a Iterator for the JavaScript Class.
12/// This feature is an experimental feature and is not yet stable.
13pub trait Generator {
14  type Yield: ToNapiValue;
15  type Next: FromNapiValue;
16  type Return: FromNapiValue;
17
18  /// Handle the `Generator.next()`
19  /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next>
20  fn next(&mut self, value: Option<Self::Next>) -> Option<Self::Yield>;
21
22  #[allow(unused_variables)]
23  /// Implement complete to handle the `Generator.return()`
24  /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return>
25  fn complete(&mut self, value: Option<Self::Return>) -> Option<Self::Yield> {
26    None
27  }
28
29  #[allow(unused_variables)]
30  /// Implement catch to handle the `Generator.throw()`
31  /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/throw>
32  fn catch<'env>(
33    &'env mut self,
34    env: Env,
35    value: Unknown<'env>,
36  ) -> Result<Option<Self::Yield>, Unknown<'env>> {
37    Err(value)
38  }
39}
40
41impl<'env, T: Generator + 'env> ScopedGenerator<'env> for T {
42  type Yield = T::Yield;
43  type Next = T::Next;
44  type Return = T::Return;
45
46  fn next(&mut self, _: &'env Env, value: Option<Self::Next>) -> Option<Self::Yield> {
47    T::next(self, value)
48  }
49
50  fn complete(&mut self, value: Option<Self::Return>) -> Option<Self::Yield> {
51    T::complete(self, value)
52  }
53
54  fn catch(
55    &'env mut self,
56    env: &'env Env,
57    value: Unknown<'env>,
58  ) -> Result<Option<Self::Yield>, Unknown<'env>> {
59    T::catch(self, Env::from_raw(env.0), value)
60  }
61}
62
63pub trait ScopedGenerator<'env> {
64  type Yield: ToNapiValue + 'env;
65  type Next: FromNapiValue;
66  type Return: FromNapiValue;
67
68  /// Handle the `Generator.next()`
69  /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/next>
70  fn next(&mut self, env: &'env Env, value: Option<Self::Next>) -> Option<Self::Yield>;
71
72  #[allow(unused_variables)]
73  /// Implement complete to handle the `Generator.return()`
74  /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/return>
75  fn complete(&mut self, value: Option<Self::Return>) -> Option<Self::Yield> {
76    None
77  }
78
79  #[allow(unused_variables)]
80  /// Implement catch to handle the `Generator.throw()`
81  /// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator/throw>
82  fn catch(
83    &'env mut self,
84    env: &'env Env,
85    value: Unknown<'env>,
86  ) -> Result<Option<Self::Yield>, Unknown<'env>> {
87    Err(value)
88  }
89}
90
91#[doc(hidden)]
92#[allow(clippy::not_unsafe_ptr_arg_deref)]
93pub unsafe fn create_iterator<'a, T: ScopedGenerator<'a> + 'a>(
94  env: sys::napi_env,
95  instance: sys::napi_value,
96  generator_ptr: *mut T,
97) {
98  let mut global = ptr::null_mut();
99  check_status_or_throw!(
100    env,
101    sys::napi_get_global(env, &mut global),
102    "Get global object failed",
103  );
104
105  let mut symbol_object = ptr::null_mut();
106  check_status_or_throw!(
107    env,
108    sys::napi_get_named_property(env, global, c"Symbol".as_ptr().cast(), &mut symbol_object),
109    "Get global object failed",
110  );
111
112  let mut iterator_symbol = ptr::null_mut();
113  check_status_or_throw!(
114    env,
115    sys::napi_get_named_property(
116      env,
117      symbol_object,
118      c"iterator".as_ptr().cast(),
119      &mut iterator_symbol,
120    ),
121    "Get Symbol.iterator failed",
122  );
123
124  let mut next_function = ptr::null_mut();
125  check_status_or_throw!(
126    env,
127    sys::napi_create_function(
128      env,
129      c"next".as_ptr().cast(),
130      4,
131      Some(generator_next::<T>),
132      generator_ptr as *mut c_void,
133      &mut next_function,
134    ),
135    "Create next function failed"
136  );
137
138  let mut return_function = ptr::null_mut();
139  check_status_or_throw!(
140    env,
141    sys::napi_create_function(
142      env,
143      c"return".as_ptr().cast(),
144      6,
145      Some(generator_return::<T>),
146      generator_ptr as *mut c_void,
147      &mut return_function,
148    ),
149    "Create return function failed"
150  );
151
152  let mut throw_function = ptr::null_mut();
153  check_status_or_throw!(
154    env,
155    sys::napi_create_function(
156      env,
157      c"throw".as_ptr().cast(),
158      5,
159      Some(generator_throw::<T>),
160      generator_ptr as *mut c_void,
161      &mut throw_function,
162    ),
163    "Create throw function failed"
164  );
165
166  check_status_or_throw!(
167    env,
168    sys::napi_set_named_property(env, instance, c"next".as_ptr().cast(), next_function,),
169    "Set next function on Generator object failed"
170  );
171
172  check_status_or_throw!(
173    env,
174    sys::napi_set_named_property(env, instance, c"return".as_ptr().cast(), return_function),
175    "Set return function on Generator object failed"
176  );
177
178  check_status_or_throw!(
179    env,
180    sys::napi_set_named_property(env, instance, c"throw".as_ptr().cast(), throw_function),
181    "Set throw function on Generator object failed"
182  );
183
184  let mut generator_state = ptr::null_mut();
185  check_status_or_throw!(
186    env,
187    sys::napi_get_boolean(env, false, &mut generator_state),
188    "Create generator state failed"
189  );
190
191  let properties = [sys::napi_property_descriptor {
192    utf8name: GENERATOR_STATE_KEY.as_ptr().cast(),
193    name: ptr::null_mut(),
194    method: None,
195    getter: None,
196    setter: None,
197    value: generator_state,
198    attributes: sys::PropertyAttributes::writable,
199    data: ptr::null_mut(),
200  }];
201
202  check_status_or_throw!(
203    env,
204    sys::napi_define_properties(env, instance, 1, properties.as_ptr()),
205    "Define properties on Generator object failed"
206  );
207
208  let mut generator_function = ptr::null_mut();
209  check_status_or_throw!(
210    env,
211    sys::napi_create_function(
212      env,
213      c"Iterator".as_ptr().cast(),
214      8,
215      Some(symbol_generator::<T>),
216      generator_ptr as *mut c_void,
217      &mut generator_function,
218    ),
219    "Create iterator function failed",
220  );
221
222  check_status_or_throw!(
223    env,
224    sys::napi_set_property(env, instance, iterator_symbol, generator_function),
225    "Failed to set Symbol.iterator on class instance",
226  );
227
228  let mut iterator_ctor = ptr::null_mut();
229  check_status_or_throw!(
230    env,
231    sys::napi_get_named_property(env, global, c"Iterator".as_ptr().cast(), &mut iterator_ctor,),
232    "Get Global.Iterator failed",
233  );
234
235  let mut iterator_ctor_type = 0;
236  check_status_or_throw!(
237    env,
238    sys::napi_typeof(env, iterator_ctor, &mut iterator_ctor_type),
239    "Get Global.Iterator type failed",
240  );
241
242  if iterator_ctor_type == sys::ValueType::napi_function {
243    let mut iterator_proto = ptr::null_mut();
244    check_status_or_throw!(
245      env,
246      sys::napi_get_named_property(
247        env,
248        iterator_ctor,
249        c"prototype".as_ptr().cast(),
250        &mut iterator_proto,
251      ),
252      "Failed to get Iterator.prototype",
253    );
254
255    let mut object_ctor = ptr::null_mut();
256    check_status_or_throw!(
257      env,
258      sys::napi_get_named_property(env, global, c"Object".as_ptr().cast(), &mut object_ctor),
259      "Failed to get Object constructor"
260    );
261
262    let mut set_prototype_function = ptr::null_mut();
263    check_status_or_throw!(
264      env,
265      sys::napi_get_named_property(
266        env,
267        object_ctor,
268        c"setPrototypeOf".as_ptr().cast(),
269        &mut set_prototype_function,
270      ),
271      "Failed to get Object.setPrototypeOf"
272    );
273
274    let mut argv = [instance, iterator_proto];
275    check_status_or_throw!(
276      env,
277      sys::napi_call_function(
278        env,
279        object_ctor,
280        set_prototype_function,
281        2,
282        argv.as_mut_ptr(),
283        ptr::null_mut(),
284      ),
285      "Failed to set prototype on object"
286    );
287  }
288}
289
290#[doc(hidden)]
291pub unsafe extern "C" fn symbol_generator<'a, T: ScopedGenerator<'a> + 'a>(
292  env: sys::napi_env,
293  info: sys::napi_callback_info,
294) -> sys::napi_value {
295  let mut this = ptr::null_mut();
296  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
297  let mut argc = 0;
298  let mut generator_ptr = ptr::null_mut();
299  check_status_or_throw!(
300    env,
301    unsafe {
302      sys::napi_get_cb_info(
303        env,
304        info,
305        &mut argc,
306        argv.as_mut_ptr(),
307        &mut this,
308        &mut generator_ptr,
309      )
310    },
311    "Get callback info from generator function failed"
312  );
313
314  this
315}
316
317extern "C" fn generator_next<'a, T: ScopedGenerator<'a> + 'a>(
318  env: sys::napi_env,
319  info: sys::napi_callback_info,
320) -> sys::napi_value {
321  let mut this = ptr::null_mut();
322  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
323  let mut argc = 1;
324  let mut generator_ptr = ptr::null_mut();
325  check_status_or_throw!(
326    env,
327    unsafe {
328      sys::napi_get_cb_info(
329        env,
330        info,
331        &mut argc,
332        argv.as_mut_ptr(),
333        &mut this,
334        &mut generator_ptr,
335      )
336    },
337    "Get callback info from generator function failed"
338  );
339  let mut generator_state = ptr::null_mut();
340  check_status_or_throw!(
341    env,
342    unsafe {
343      sys::napi_get_named_property(
344        env,
345        this,
346        GENERATOR_STATE_KEY.as_ptr().cast(),
347        &mut generator_state,
348      )
349    },
350    "Get generator state failed"
351  );
352  let mut completed = false;
353  check_status_or_throw!(
354    env,
355    unsafe { sys::napi_get_value_bool(env, generator_state, &mut completed) },
356    "Get generator state failed"
357  );
358  let mut result = std::ptr::null_mut();
359  check_status_or_throw!(
360    env,
361    unsafe { sys::napi_create_object(env, &mut result) },
362    "Failed to create iterator result object",
363  );
364  if !completed {
365    let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
366    let item = if argc == 0 {
367      g.next(
368        // SAFETY: `Env` is long lived
369        unsafe { std::mem::transmute::<&Env, &'a Env>(&Env::from_raw(env)) },
370        None,
371      )
372    } else {
373      g.next(
374        // SAFETY: `Env` is long lived
375        unsafe { std::mem::transmute::<&Env, &'a Env>(&Env::from_raw(env)) },
376        match unsafe { T::Next::from_napi_value(env, argv[0]) } {
377          Ok(input) => Some(input),
378          Err(e) => {
379            unsafe {
380              sys::napi_throw_error(
381                env,
382                format!("{}", e.status).as_ptr().cast(),
383                e.reason.as_ptr().cast(),
384              )
385            };
386            None
387          }
388        },
389      )
390    };
391
392    if let Some(value) = item {
393      set_generator_value(env, result, value);
394    } else {
395      completed = true;
396    }
397  }
398  let mut completed_value = ptr::null_mut();
399  check_status_or_throw!(
400    env,
401    unsafe { sys::napi_get_boolean(env, completed, &mut completed_value) },
402    "Failed to create completed value"
403  );
404  check_status_or_throw!(
405    env,
406    unsafe { sys::napi_set_named_property(env, result, c"done".as_ptr().cast(), completed_value,) },
407    "Failed to set iterator result done",
408  );
409
410  result
411}
412
413extern "C" fn generator_return<'a, T: ScopedGenerator<'a> + 'a>(
414  env: sys::napi_env,
415  info: sys::napi_callback_info,
416) -> sys::napi_value {
417  let mut this = ptr::null_mut();
418  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
419  let mut argc = 1;
420  let mut generator_ptr = ptr::null_mut();
421  check_status_or_throw!(
422    env,
423    unsafe {
424      sys::napi_get_cb_info(
425        env,
426        info,
427        &mut argc,
428        argv.as_mut_ptr(),
429        &mut this,
430        &mut generator_ptr,
431      )
432    },
433    "Get callback info from generator function failed"
434  );
435
436  let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
437  if argc == 0 {
438    g.complete(None);
439  } else {
440    g.complete(Some(
441      match unsafe { T::Return::from_napi_value(env, argv[0]) } {
442        Ok(input) => input,
443        Err(e) => {
444          unsafe {
445            sys::napi_throw_error(
446              env,
447              format!("{}", e.status).as_ptr().cast(),
448              e.reason.as_ptr().cast(),
449            )
450          };
451          return ptr::null_mut();
452        }
453      },
454    ));
455  }
456  let mut generator_state = ptr::null_mut();
457  check_status_or_throw!(
458    env,
459    unsafe { sys::napi_get_boolean(env, true, &mut generator_state) },
460    "Create generator state failed"
461  );
462  check_status_or_throw!(
463    env,
464    unsafe {
465      sys::napi_set_named_property(
466        env,
467        this,
468        GENERATOR_STATE_KEY.as_ptr().cast(),
469        generator_state,
470      )
471    },
472    "Get generator state failed"
473  );
474  let mut result = std::ptr::null_mut();
475  check_status_or_throw!(
476    env,
477    unsafe { sys::napi_create_object(env, &mut result) },
478    "Failed to create iterator result object",
479  );
480  if argc > 0 {
481    check_status_or_throw!(
482      env,
483      unsafe { sys::napi_set_named_property(env, result, c"value".as_ptr().cast(), argv[0],) },
484      "Failed to set iterator result value",
485    );
486  }
487  check_status_or_throw!(
488    env,
489    unsafe {
490      sys::napi_set_named_property(
491        env,
492        result,
493        c"done".as_ptr() as *const std::os::raw::c_char,
494        generator_state,
495      )
496    },
497    "Failed to set iterator result done",
498  );
499
500  result
501}
502
503extern "C" fn generator_throw<'a, T: ScopedGenerator<'a> + 'a>(
504  env: sys::napi_env,
505  info: sys::napi_callback_info,
506) -> sys::napi_value {
507  let mut this = ptr::null_mut();
508  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
509  let mut argc = 1;
510  let mut generator_ptr = ptr::null_mut();
511  check_status_or_throw!(
512    env,
513    unsafe {
514      sys::napi_get_cb_info(
515        env,
516        info,
517        &mut argc,
518        argv.as_mut_ptr(),
519        &mut this,
520        &mut generator_ptr,
521      )
522    },
523    "Get callback info from generator function failed"
524  );
525
526  let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
527  let catch_result = if argc == 0 {
528    let mut undefined = ptr::null_mut();
529    check_status_or_throw!(
530      env,
531      unsafe { sys::napi_get_undefined(env, &mut undefined) },
532      "Get undefined failed"
533    );
534    g.catch(
535      // SAFETY: `Env` is long lived
536      unsafe { std::mem::transmute::<&Env, &'a Env>(&Env::from_raw(env)) },
537      Unknown(
538        Value {
539          env,
540          value: undefined,
541          value_type: crate::ValueType::Unknown,
542        },
543        std::marker::PhantomData,
544      ),
545    )
546  } else {
547    g.catch(
548      // SAFETY: `Env` is long lived
549      unsafe { std::mem::transmute::<&Env, &'a Env>(&Env::from_raw(env)) },
550      Unknown(
551        Value {
552          env,
553          value: argv[0],
554          value_type: crate::ValueType::Unknown,
555        },
556        std::marker::PhantomData,
557      ),
558    )
559  };
560  let mut result = ptr::null_mut();
561  check_status_or_throw!(
562    env,
563    unsafe { sys::napi_create_object(env, &mut result) },
564    "Failed to create iterator result object",
565  );
566  let mut generator_state = ptr::null_mut();
567  let mut generator_state_value = false;
568  match catch_result {
569    Err(e) => {
570      generator_state_value = true;
571      check_status_or_throw!(
572        env,
573        unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
574        "Create generator state failed"
575      );
576      check_status_or_throw!(
577        env,
578        unsafe {
579          sys::napi_set_named_property(
580            env,
581            this,
582            GENERATOR_STATE_KEY.as_ptr().cast(),
583            generator_state,
584          )
585        },
586        "Get generator state failed"
587      );
588      let throw_status = unsafe { sys::napi_throw(env, e.0.value) };
589      debug_assert!(
590        throw_status == sys::Status::napi_ok,
591        "Failed to throw error {}",
592        crate::Status::from(throw_status)
593      );
594      return ptr::null_mut();
595    }
596    Ok(Some(v)) => {
597      set_generator_value(env, result, v);
598    }
599    Ok(None) => {
600      generator_state_value = true;
601    }
602  }
603  check_status_or_throw!(
604    env,
605    unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
606    "Create generator state failed"
607  );
608  check_status_or_throw!(
609    env,
610    unsafe {
611      sys::napi_set_named_property(
612        env,
613        this,
614        GENERATOR_STATE_KEY.as_ptr().cast(),
615        generator_state,
616      )
617    },
618    "Get generator state failed"
619  );
620  check_status_or_throw!(
621    env,
622    unsafe { sys::napi_set_named_property(env, result, c"done".as_ptr().cast(), generator_state) },
623    "Get generator state failed"
624  );
625
626  result
627}
628
629fn set_generator_value<V: ToNapiValue>(env: sys::napi_env, result: sys::napi_value, value: V) {
630  match unsafe { ToNapiValue::to_napi_value(env, value) } {
631    Ok(val) => {
632      check_status_or_throw!(
633        env,
634        unsafe { sys::napi_set_named_property(env, result, c"value".as_ptr().cast(), val,) },
635        "Failed to set iterator result value",
636      );
637    }
638    Err(e) => {
639      unsafe {
640        sys::napi_throw_error(
641          env,
642          format!("{}", e.status).as_ptr().cast(),
643          e.reason.as_ptr().cast(),
644        )
645      };
646    }
647  }
648}