1 | use std::ptr; |
2 | use std::{ffi::c_void, os::raw::c_char}; |
3 | |
4 | use crate::Value; |
5 | use crate::{bindgen_runtime::Unknown, check_status_or_throw, sys, Env}; |
6 | |
7 | use super::{FromNapiValue, ToNapiValue}; |
8 | |
9 | const 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. |
13 | pub 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)] |
38 | pub 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)] |
93 | pub 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 | |
233 | extern "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 | |
328 | extern "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 | |
425 | extern "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 | |
543 | fn 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 | |