Skip to main content

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