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}
570