napi_h/bindgen_runtime/
iterator.rs

1use std::ptr;
2use std::{ffi::c_void, os::raw::c_char};
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: &str = "[[GeneratorState]]\0";
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(&mut self, env: Env, value: Unknown) -> Result<Option<Self::Yield>, Unknown> {
33    Err(value)
34  }
35}
36
37#[allow(clippy::not_unsafe_ptr_arg_deref)]
38pub fn create_iterator<T: Generator>(
39  env: sys::napi_env,
40  instance: sys::napi_value,
41  generator_ptr: *mut T,
42) {
43  let mut global = ptr::null_mut();
44  check_status_or_throw!(
45    env,
46    unsafe { sys::napi_get_global(env, &mut global) },
47    "Get global object failed",
48  );
49  let mut symbol_object = ptr::null_mut();
50  check_status_or_throw!(
51    env,
52    unsafe {
53      sys::napi_get_named_property(env, global, "Symbol\0".as_ptr().cast(), &mut symbol_object)
54    },
55    "Get global object failed",
56  );
57  let mut iterator_symbol = ptr::null_mut();
58  check_status_or_throw!(
59    env,
60    unsafe {
61      sys::napi_get_named_property(
62        env,
63        symbol_object,
64        "iterator\0".as_ptr().cast(),
65        &mut iterator_symbol,
66      )
67    },
68    "Get Symbol.iterator failed",
69  );
70  let mut generator_function = ptr::null_mut();
71  check_status_or_throw!(
72    env,
73    unsafe {
74      sys::napi_create_function(
75        env,
76        "Iterator\0".as_ptr().cast(),
77        8,
78        Some(symbol_generator::<T>),
79        generator_ptr as *mut c_void,
80        &mut generator_function,
81      )
82    },
83    "Create iterator function failed",
84  );
85  check_status_or_throw!(
86    env,
87    unsafe { sys::napi_set_property(env, instance, iterator_symbol, generator_function) },
88    "Failed to set Symbol.iterator on class instance",
89  );
90}
91
92#[doc(hidden)]
93pub unsafe extern "C" fn symbol_generator<T: Generator>(
94  env: sys::napi_env,
95  info: sys::napi_callback_info,
96) -> sys::napi_value {
97  let mut this = ptr::null_mut();
98  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
99  let mut argc = 0;
100  let mut generator_ptr = ptr::null_mut();
101  check_status_or_throw!(
102    env,
103    unsafe {
104      sys::napi_get_cb_info(
105        env,
106        info,
107        &mut argc,
108        argv.as_mut_ptr(),
109        &mut this,
110        &mut generator_ptr,
111      )
112    },
113    "Get callback info from generator function failed"
114  );
115  let mut generator_object = ptr::null_mut();
116  check_status_or_throw!(
117    env,
118    unsafe { sys::napi_create_object(env, &mut generator_object) },
119    "Create Generator object failed"
120  );
121  let mut next_function = ptr::null_mut();
122  check_status_or_throw!(
123    env,
124    unsafe {
125      sys::napi_create_function(
126        env,
127        "next\0".as_ptr().cast(),
128        4,
129        Some(generator_next::<T>),
130        generator_ptr,
131        &mut next_function,
132      )
133    },
134    "Create next function failed"
135  );
136  let mut return_function = ptr::null_mut();
137  check_status_or_throw!(
138    env,
139    unsafe {
140      sys::napi_create_function(
141        env,
142        "return\0".as_ptr().cast(),
143        6,
144        Some(generator_return::<T>),
145        generator_ptr,
146        &mut return_function,
147      )
148    },
149    "Create next function failed"
150  );
151  let mut throw_function = ptr::null_mut();
152  check_status_or_throw!(
153    env,
154    unsafe {
155      sys::napi_create_function(
156        env,
157        "throw\0".as_ptr().cast(),
158        5,
159        Some(generator_throw::<T>),
160        generator_ptr,
161        &mut throw_function,
162      )
163    },
164    "Create next function failed"
165  );
166
167  check_status_or_throw!(
168    env,
169    unsafe {
170      sys::napi_set_named_property(
171        env,
172        generator_object,
173        "next\0".as_ptr().cast(),
174        next_function,
175      )
176    },
177    "Set next function on Generator object failed"
178  );
179
180  check_status_or_throw!(
181    env,
182    unsafe {
183      sys::napi_set_named_property(
184        env,
185        generator_object,
186        "return\0".as_ptr().cast(),
187        return_function,
188      )
189    },
190    "Set return function on Generator object failed"
191  );
192
193  check_status_or_throw!(
194    env,
195    unsafe {
196      sys::napi_set_named_property(
197        env,
198        generator_object,
199        "throw\0".as_ptr().cast(),
200        throw_function,
201      )
202    },
203    "Set throw function on Generator object failed"
204  );
205
206  let mut generator_state = ptr::null_mut();
207  check_status_or_throw!(
208    env,
209    unsafe { sys::napi_get_boolean(env, false, &mut generator_state) },
210    "Create generator state failed"
211  );
212
213  let properties = [sys::napi_property_descriptor {
214    utf8name: GENERATOR_STATE_KEY.as_ptr().cast(),
215    name: ptr::null_mut(),
216    method: None,
217    getter: None,
218    setter: None,
219    value: generator_state,
220    attributes: sys::PropertyAttributes::writable,
221    data: ptr::null_mut(),
222  }];
223
224  check_status_or_throw!(
225    env,
226    unsafe { sys::napi_define_properties(env, generator_object, 1, properties.as_ptr()) },
227    "Define properties on Generator object failed"
228  );
229
230  generator_object
231}
232
233extern "C" fn generator_next<T: Generator>(
234  env: sys::napi_env,
235  info: sys::napi_callback_info,
236) -> sys::napi_value {
237  let mut this = ptr::null_mut();
238  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
239  let mut argc = 1;
240  let mut generator_ptr = ptr::null_mut();
241  check_status_or_throw!(
242    env,
243    unsafe {
244      sys::napi_get_cb_info(
245        env,
246        info,
247        &mut argc,
248        argv.as_mut_ptr(),
249        &mut this,
250        &mut generator_ptr,
251      )
252    },
253    "Get callback info from generator function failed"
254  );
255  let mut generator_state = ptr::null_mut();
256  check_status_or_throw!(
257    env,
258    unsafe {
259      sys::napi_get_named_property(
260        env,
261        this,
262        GENERATOR_STATE_KEY.as_ptr().cast(),
263        &mut generator_state,
264      )
265    },
266    "Get generator state failed"
267  );
268  let mut completed = false;
269  check_status_or_throw!(
270    env,
271    unsafe { sys::napi_get_value_bool(env, generator_state, &mut completed) },
272    "Get generator state failed"
273  );
274  let mut result = std::ptr::null_mut();
275  check_status_or_throw!(
276    env,
277    unsafe { sys::napi_create_object(env, &mut result) },
278    "Failed to create iterator result object",
279  );
280  if !completed {
281    let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
282    let item = if argc == 0 {
283      g.next(None)
284    } else {
285      g.next(match unsafe { T::Next::from_napi_value(env, argv[0]) } {
286        Ok(input) => Some(input),
287        Err(e) => {
288          unsafe {
289            sys::napi_throw_error(
290              env,
291              format!("{}", e.status).as_ptr() as *mut c_char,
292              e.reason.as_ptr() as *mut c_char,
293            )
294          };
295          None
296        }
297      })
298    };
299
300    if let Some(value) = item {
301      set_generator_value(env, result, value);
302    } else {
303      completed = true;
304    }
305  }
306  let mut completed_value = ptr::null_mut();
307  check_status_or_throw!(
308    env,
309    unsafe { sys::napi_get_boolean(env, completed, &mut completed_value) },
310    "Failed to create completed value"
311  );
312  check_status_or_throw!(
313    env,
314    unsafe {
315      sys::napi_set_named_property(
316        env,
317        result,
318        "done\0".as_ptr() as *const std::os::raw::c_char,
319        completed_value,
320      )
321    },
322    "Failed to set iterator result done",
323  );
324
325  result
326}
327
328extern "C" fn generator_return<T: Generator>(
329  env: sys::napi_env,
330  info: sys::napi_callback_info,
331) -> sys::napi_value {
332  let mut this = ptr::null_mut();
333  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
334  let mut argc = 1;
335  let mut generator_ptr = ptr::null_mut();
336  check_status_or_throw!(
337    env,
338    unsafe {
339      sys::napi_get_cb_info(
340        env,
341        info,
342        &mut argc,
343        argv.as_mut_ptr(),
344        &mut this,
345        &mut generator_ptr,
346      )
347    },
348    "Get callback info from generator function failed"
349  );
350
351  let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
352  if argc == 0 {
353    g.complete(None);
354  } else {
355    g.complete(Some(
356      match unsafe { T::Return::from_napi_value(env, argv[0]) } {
357        Ok(input) => input,
358        Err(e) => {
359          unsafe {
360            sys::napi_throw_error(
361              env,
362              format!("{}", e.status).as_ptr() as *mut c_char,
363              e.reason.as_ptr() as *mut c_char,
364            )
365          };
366          return ptr::null_mut();
367        }
368      },
369    ));
370  }
371  let mut generator_state = ptr::null_mut();
372  check_status_or_throw!(
373    env,
374    unsafe { sys::napi_get_boolean(env, true, &mut generator_state) },
375    "Create generator state failed"
376  );
377  check_status_or_throw!(
378    env,
379    unsafe {
380      sys::napi_set_named_property(
381        env,
382        this,
383        GENERATOR_STATE_KEY.as_ptr().cast(),
384        generator_state,
385      )
386    },
387    "Get generator state failed"
388  );
389  let mut result = std::ptr::null_mut();
390  check_status_or_throw!(
391    env,
392    unsafe { sys::napi_create_object(env, &mut result) },
393    "Failed to create iterator result object",
394  );
395  if argc > 0 {
396    check_status_or_throw!(
397      env,
398      unsafe {
399        sys::napi_set_named_property(
400          env,
401          result,
402          "value\0".as_ptr() as *const std::os::raw::c_char,
403          argv[0],
404        )
405      },
406      "Failed to set iterator result value",
407    );
408  }
409  check_status_or_throw!(
410    env,
411    unsafe {
412      sys::napi_set_named_property(
413        env,
414        result,
415        "done\0".as_ptr() as *const std::os::raw::c_char,
416        generator_state,
417      )
418    },
419    "Failed to set iterator result done",
420  );
421
422  result
423}
424
425extern "C" fn generator_throw<T: Generator>(
426  env: sys::napi_env,
427  info: sys::napi_callback_info,
428) -> sys::napi_value {
429  let mut this = ptr::null_mut();
430  let mut argv: [sys::napi_value; 1] = [ptr::null_mut()];
431  let mut argc = 1;
432  let mut generator_ptr = ptr::null_mut();
433  check_status_or_throw!(
434    env,
435    unsafe {
436      sys::napi_get_cb_info(
437        env,
438        info,
439        &mut argc,
440        argv.as_mut_ptr(),
441        &mut this,
442        &mut generator_ptr,
443      )
444    },
445    "Get callback info from generator function failed"
446  );
447
448  let g = unsafe { Box::leak(Box::from_raw(generator_ptr as *mut T)) };
449  let catch_result = if argc == 0 {
450    let mut undefined = ptr::null_mut();
451    check_status_or_throw!(
452      env,
453      unsafe { sys::napi_get_undefined(env, &mut undefined) },
454      "Get undefined failed"
455    );
456    g.catch(
457      Env(env),
458      Unknown(Value {
459        env,
460        value: undefined,
461        value_type: crate::ValueType::Undefined,
462      }),
463    )
464  } else {
465    g.catch(
466      Env(env),
467      Unknown(Value {
468        env,
469        value: argv[0],
470        value_type: crate::ValueType::Unknown,
471      }),
472    )
473  };
474  let mut result = 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  let mut generator_state = ptr::null_mut();
481  let mut generator_state_value = false;
482  match catch_result {
483    Err(e) => {
484      generator_state_value = true;
485      check_status_or_throw!(
486        env,
487        unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
488        "Create generator state failed"
489      );
490      check_status_or_throw!(
491        env,
492        unsafe {
493          sys::napi_set_named_property(
494            env,
495            this,
496            GENERATOR_STATE_KEY.as_ptr().cast(),
497            generator_state,
498          )
499        },
500        "Get generator state failed"
501      );
502      let throw_status = unsafe { sys::napi_throw(env, e.0.value) };
503      debug_assert!(
504        throw_status == sys::Status::napi_ok,
505        "Failed to throw error {}",
506        crate::Status::from(throw_status)
507      );
508      return ptr::null_mut();
509    }
510    Ok(Some(v)) => {
511      set_generator_value(env, result, v);
512    }
513    Ok(None) => {
514      generator_state_value = true;
515    }
516  }
517  check_status_or_throw!(
518    env,
519    unsafe { sys::napi_get_boolean(env, generator_state_value, &mut generator_state) },
520    "Create generator state failed"
521  );
522  check_status_or_throw!(
523    env,
524    unsafe {
525      sys::napi_set_named_property(
526        env,
527        this,
528        GENERATOR_STATE_KEY.as_ptr().cast(),
529        generator_state,
530      )
531    },
532    "Get generator state failed"
533  );
534  check_status_or_throw!(
535    env,
536    unsafe { sys::napi_set_named_property(env, result, "done\0".as_ptr().cast(), generator_state) },
537    "Get generator state failed"
538  );
539
540  result
541}
542
543fn set_generator_value<V: ToNapiValue>(env: sys::napi_env, result: sys::napi_value, value: V) {
544  match unsafe { ToNapiValue::to_napi_value(env, value) } {
545    Ok(val) => {
546      check_status_or_throw!(
547        env,
548        unsafe {
549          sys::napi_set_named_property(
550            env,
551            result,
552            "value\0".as_ptr() as *const std::os::raw::c_char,
553            val,
554          )
555        },
556        "Failed to set iterator result value",
557      );
558    }
559    Err(e) => {
560      unsafe {
561        sys::napi_throw_error(
562          env,
563          format!("{}", e.status).as_ptr() as *mut c_char,
564          e.reason.as_ptr() as *mut c_char,
565        )
566      };
567    }
568  }
569}