1
2 /*
3 * Copyright (C) Alexander Borisov
4 * Copyright (C) Nginx, Inc.
5 */
6
7 #include <njs_main.h>
8
9
10 typedef enum {
11 NJS_PROMISE_HANDLE = 0,
12 NJS_PROMISE_REJECT
13 } njs_promise_rejection_type_t;
14
15 typedef enum {
16 NJS_PROMISE_ALL = 0,
17 NJS_PROMISE_ALL_SETTLED,
18 NJS_PROMISE_ANY
19 } njs_promise_function_type_t;
20
21 typedef struct {
22 njs_promise_capability_t *capability;
23 njs_promise_type_t type;
24 njs_queue_link_t link;
25 njs_value_t handler;
26 } njs_promise_reaction_t;
27
28 typedef struct {
29 njs_value_t promise;
30 njs_value_t finally;
31 njs_value_t constructor;
32 njs_bool_t resolved;
33 njs_bool_t *resolved_ref;
34 njs_promise_capability_t *capability;
35 njs_function_native_t handler;
36 } njs_promise_context_t;
37
38 typedef struct {
39 njs_bool_t already_called;
40 uint32_t index;
41 uint32_t *remaining_elements;
42 njs_array_t *values;
43 njs_promise_capability_t *capability;
44 } njs_promise_all_context_t;
45
46 typedef struct {
47 njs_iterator_args_t args;
48 uint32_t *remaining;
49 njs_value_t *constructor;
50 njs_function_t *function;
51 njs_promise_capability_t *capability;
52 } njs_promise_iterator_args_t;
53
54
55 static njs_promise_t *njs_promise_constructor_call(njs_vm_t *vm,
56 njs_function_t *function);
57 static njs_int_t njs_promise_create_resolving_functions(njs_vm_t *vm,
58 njs_promise_t *promise, njs_value_t *dst);
59 static njs_int_t njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value,
60 njs_value_t *dst);
61 static njs_int_t njs_promise_capability_executor(njs_vm_t *vm,
62 njs_value_t *args, njs_uint_t nargs, njs_index_t retval);
63 static njs_int_t njs_promise_host_rejection_tracker(njs_vm_t *vm,
64 njs_promise_t *promise, njs_promise_rejection_type_t operation);
65 static njs_int_t njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args,
66 njs_uint_t nargs, njs_index_t retval);
67 static njs_int_t njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args,
68 njs_uint_t nargs, njs_index_t retval);
69 static njs_int_t njs_promise_then_finally_function(njs_vm_t *vm,
70 njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
71 static njs_int_t njs_promise_then_finally_return(njs_vm_t *vm,
72 njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
73 static njs_int_t njs_promise_catch_finally_return(njs_vm_t *vm,
74 njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
75 static njs_int_t njs_promise_reaction_job(njs_vm_t *vm, njs_value_t *args,
76 njs_uint_t nargs, njs_index_t unused);
77 static njs_int_t njs_promise_resolve_thenable_job(njs_vm_t *vm,
78 njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
79 static njs_int_t njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator,
80 njs_promise_iterator_args_t *pargs, njs_iterator_handler_t handler,
81 njs_value_t *retval);
82 static njs_int_t njs_promise_perform_all_handler(njs_vm_t *vm,
83 njs_iterator_args_t *args, njs_value_t *value, int64_t index);
84 static njs_int_t njs_promise_all_resolve_element_functions(njs_vm_t *vm,
85 njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
86 static njs_int_t njs_promise_perform_all_settled_handler(njs_vm_t *vm,
87 njs_iterator_args_t *args, njs_value_t *value, int64_t index);
88 static njs_int_t njs_promise_all_settled_element_functions(njs_vm_t *vm,
89 njs_value_t *args, njs_uint_t nargs, njs_index_t rejected);
90 static njs_int_t njs_promise_perform_any_handler(njs_vm_t *vm,
91 njs_iterator_args_t *args, njs_value_t *value, int64_t index);
92 static njs_int_t njs_promise_any_reject_element_functions(njs_vm_t *vm,
93 njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
94 static njs_int_t njs_promise_perform_race_handler(njs_vm_t *vm,
95 njs_iterator_args_t *args, njs_value_t *value, int64_t index);
96
97
98 static const njs_value_t string_resolve = njs_string("resolve");
99 static const njs_value_t string_any_rejected =
100 njs_long_string("All promises were rejected");
101
102
103 static njs_promise_t *
njs_promise_alloc(njs_vm_t * vm)104 njs_promise_alloc(njs_vm_t *vm)
105 {
106 njs_promise_t *promise;
107 njs_promise_data_t *data;
108
109 promise = njs_mp_alloc(vm->mem_pool, sizeof(njs_promise_t)
110 + sizeof(njs_promise_data_t));
111 if (njs_slow_path(promise == NULL)) {
112 goto memory_error;
113 }
114
115 njs_lvlhsh_init(&promise->object.hash);
116 njs_lvlhsh_init(&promise->object.shared_hash);
117 promise->object.type = NJS_PROMISE;
118 promise->object.shared = 0;
119 promise->object.extensible = 1;
120 promise->object.error_data = 0;
121 promise->object.fast_array = 0;
122 promise->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_PROMISE].object;
123 promise->object.slots = NULL;
124
125 data = (njs_promise_data_t *) ((uint8_t *) promise + sizeof(njs_promise_t));
126
127 data->state = NJS_PROMISE_PENDING;
128 data->is_handled = 0;
129
130 njs_queue_init(&data->fulfill_queue);
131 njs_queue_init(&data->reject_queue);
132
133 njs_set_data(&promise->value, data, 0);
134
135 return promise;
136
137 memory_error:
138
139 njs_memory_error(vm);
140
141 return NULL;
142 }
143
144
145 njs_int_t
njs_promise_constructor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)146 njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
147 njs_index_t unused)
148 {
149 njs_promise_t *promise;
150 njs_function_t *function;
151
152 if (njs_slow_path(!vm->top_frame->ctor)) {
153 njs_type_error(vm, "the Promise constructor must be called with new");
154 return NJS_ERROR;
155 }
156
157 if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) {
158 njs_type_error(vm, "unexpected arguments");
159 return NJS_ERROR;
160 }
161
162 function = njs_function(njs_argument(args, 1));
163
164 promise = njs_promise_constructor_call(vm, function);
165 if (njs_slow_path(promise == NULL)) {
166 return NJS_ERROR;
167 }
168
169 njs_set_promise(&vm->retval, promise);
170
171 return NJS_OK;
172 }
173
174
175 njs_int_t
njs_vm_promise_create(njs_vm_t * vm,njs_value_t * retval,njs_value_t * callbacks)176 njs_vm_promise_create(njs_vm_t *vm, njs_value_t *retval, njs_value_t *callbacks)
177 {
178 njs_int_t ret;
179 njs_promise_t *promise;
180
181 promise = njs_promise_alloc(vm);
182 if (njs_slow_path(promise == NULL)) {
183 return NJS_ERROR;
184 }
185
186 ret = njs_promise_create_resolving_functions(vm, promise, callbacks);
187 if (njs_slow_path(ret != NJS_OK)) {
188 return NJS_ERROR;
189 }
190
191 njs_set_promise(retval, promise);
192
193 return NJS_OK;
194 }
195
196
197 static njs_promise_t *
njs_promise_constructor_call(njs_vm_t * vm,njs_function_t * function)198 njs_promise_constructor_call(njs_vm_t *vm, njs_function_t *function)
199 {
200 njs_int_t ret;
201 njs_value_t retval, arguments[2];
202 njs_promise_t *promise;
203
204 promise = njs_promise_alloc(vm);
205 if (njs_slow_path(promise == NULL)) {
206 return NULL;
207 }
208
209 ret = njs_promise_create_resolving_functions(vm, promise, arguments);
210 if (njs_slow_path(ret != NJS_OK)) {
211 return NULL;
212 }
213
214 ret = njs_function_call(vm, function, &njs_value_undefined, arguments, 2,
215 &retval);
216 if (njs_slow_path(ret != NJS_OK)) {
217 if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
218 return NULL;
219 }
220
221 ret = njs_function_call(vm, njs_function(&arguments[1]),
222 &njs_value_undefined, &vm->retval, 1, &retval);
223 if (njs_slow_path(ret != NJS_OK)) {
224 return NULL;
225 }
226 }
227
228 return promise;
229 }
230
231
232 njs_function_t *
njs_promise_create_function(njs_vm_t * vm,size_t context_size)233 njs_promise_create_function(njs_vm_t *vm, size_t context_size)
234 {
235 njs_function_t *function;
236 njs_promise_context_t *context;
237
238 function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t));
239 if (njs_slow_path(function == NULL)) {
240 goto memory_error;
241 }
242
243 if (context_size > 0) {
244 context = njs_mp_zalloc(vm->mem_pool, context_size);
245 if (njs_slow_path(context == NULL)) {
246 njs_mp_free(vm->mem_pool, function);
247 goto memory_error;
248 }
249
250 } else {
251 context = NULL;
252 }
253
254 function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
255 function->object.shared_hash = vm->shared->arrow_instance_hash;
256 function->object.type = NJS_FUNCTION;
257 function->object.extensible = 1;
258 function->args_offset = 1;
259 function->native = 1;
260 function->context = context;
261
262 return function;
263
264 memory_error:
265
266 njs_memory_error(vm);
267
268 return NULL;
269 }
270
271
272 static njs_int_t
njs_promise_create_resolving_functions(njs_vm_t * vm,njs_promise_t * promise,njs_value_t * dst)273 njs_promise_create_resolving_functions(njs_vm_t *vm, njs_promise_t *promise,
274 njs_value_t *dst)
275 {
276 unsigned i;
277 njs_function_t *function;
278 njs_promise_context_t *context, *resolve_context;
279
280 i = 0;
281
282 /* Some compilers give at error an uninitialized context if using for. */
283 do {
284 function = njs_promise_create_function(vm,
285 sizeof(njs_promise_context_t));
286 if (njs_slow_path(function == NULL)) {
287 return NJS_ERROR;
288 }
289
290 function->args_count = 1;
291
292 context = function->context;
293 context->resolved_ref = &context->resolved;
294
295 njs_set_promise(&context->promise, promise);
296 njs_set_function(&dst[i], function);
297
298 } while (++i < 2);
299
300 njs_function(&dst[0])->u.native = njs_promise_resolve_function;
301 njs_function(&dst[1])->u.native = njs_promise_reject_function;
302
303 resolve_context = njs_function(&dst[0])->context;
304 resolve_context->resolved_ref = &context->resolved;
305
306 return NJS_OK;
307 }
308
309
310 njs_promise_capability_t *
njs_promise_new_capability(njs_vm_t * vm,njs_value_t * constructor)311 njs_promise_new_capability(njs_vm_t *vm, njs_value_t *constructor)
312 {
313 njs_int_t ret;
314 njs_value_t argument, this;
315 njs_object_t *object;
316 njs_function_t *function;
317 njs_promise_context_t *context;
318 njs_promise_capability_t *capability;
319
320 object = NULL;
321 function = NULL;
322
323 ret = njs_promise_value_constructor(vm, constructor, constructor);
324 if (njs_slow_path(ret != NJS_OK)) {
325 return NULL;
326 }
327
328 capability = njs_mp_zalloc(vm->mem_pool, sizeof(njs_promise_capability_t));
329 if (njs_slow_path(capability == NULL)) {
330 njs_memory_error(vm);
331 return NULL;
332 }
333
334 function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
335 if (njs_slow_path(function == NULL)) {
336 return NULL;
337 }
338
339 njs_set_undefined(&capability->resolve);
340 njs_set_undefined(&capability->reject);
341
342 function->u.native = njs_promise_capability_executor;
343 function->args_count = 2;
344
345 context = function->context;
346 context->capability = capability;
347
348 njs_set_function(&argument, function);
349
350 object = njs_function_new_object(vm, constructor);
351 if (njs_slow_path(object == NULL)) {
352 return NULL;
353 }
354
355 njs_set_object(&this, object);
356
357 ret = njs_function_call2(vm, njs_function(constructor), &this,
358 &argument, 1, &capability->promise, 1);
359 if (njs_slow_path(ret != NJS_OK)) {
360 return NULL;
361 }
362
363 if (njs_slow_path(!njs_is_function(&capability->resolve))) {
364 njs_type_error(vm, "capability resolve slot is not callable");
365 return NULL;
366 }
367
368 if (njs_slow_path(!njs_is_function(&capability->reject))) {
369 njs_type_error(vm, "capability reject slot is not callable");
370 return NULL;
371 }
372
373 return capability;
374 }
375
376
377 static njs_int_t
njs_promise_value_constructor(njs_vm_t * vm,njs_value_t * value,njs_value_t * dst)378 njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value,
379 njs_value_t *dst)
380 {
381 njs_int_t ret;
382
383 static const njs_value_t string_constructor = njs_string("constructor");
384
385 if (njs_is_function(value)) {
386 *dst = *value;
387 return NJS_OK;
388 }
389
390 ret = njs_value_property(vm, value, njs_value_arg(&string_constructor),
391 dst);
392 if (njs_slow_path(ret != NJS_OK)) {
393 return ret;
394 }
395
396 if (!njs_is_function(dst)) {
397 njs_type_error(vm, "the object does not contain a constructor");
398 return NJS_ERROR;
399 }
400
401 return NJS_OK;
402 }
403
404
405 static njs_int_t
njs_promise_capability_executor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)406 njs_promise_capability_executor(njs_vm_t *vm, njs_value_t *args,
407 njs_uint_t nargs, njs_index_t unused)
408 {
409 njs_promise_context_t *context;
410 njs_promise_capability_t *capability;
411
412 context = vm->top_frame->function->context;
413 capability = context->capability;
414
415 if (njs_slow_path(capability == NULL)) {
416 njs_type_error(vm, "failed to get function capability");
417 return NJS_ERROR;
418 }
419
420 if (!njs_is_undefined(&capability->resolve)) {
421 njs_type_error(vm, "capability resolve slot is not undefined");
422 return NJS_ERROR;
423 }
424
425 if (!njs_is_undefined(&capability->reject)) {
426 njs_type_error(vm, "capability reject slot is not undefined");
427 return NJS_ERROR;
428 }
429
430 capability->resolve = *njs_arg(args, nargs, 1);
431 capability->reject = *njs_arg(args, nargs, 2);
432
433 njs_vm_retval_set(vm, &njs_value_undefined);
434
435 return NJS_OK;
436 }
437
438
439 njs_inline njs_int_t
njs_promise_add_event(njs_vm_t * vm,njs_function_t * function,njs_value_t * args,njs_uint_t nargs)440 njs_promise_add_event(njs_vm_t *vm, njs_function_t *function, njs_value_t *args,
441 njs_uint_t nargs)
442 {
443 njs_event_t *event;
444
445 event = njs_mp_zalloc(vm->mem_pool, sizeof(njs_event_t));
446 if (njs_slow_path(event == NULL)) {
447 njs_memory_error(vm);
448 return NJS_ERROR;
449 }
450
451 event->function = function;
452 event->once = 1;
453
454 if (nargs != 0) {
455 event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
456 if (njs_slow_path(event->args == NULL)) {
457 njs_memory_error(vm);
458 return NJS_ERROR;
459 }
460
461 memcpy(event->args, args, sizeof(njs_value_t) * nargs);
462
463 event->nargs = nargs;
464 }
465
466 njs_queue_insert_tail(&vm->promise_events, &event->link);
467
468 return NJS_OK;
469 }
470
471
472 njs_inline njs_value_t *
njs_promise_trigger_reactions(njs_vm_t * vm,njs_value_t * value,njs_queue_t * queue)473 njs_promise_trigger_reactions(njs_vm_t *vm, njs_value_t *value,
474 njs_queue_t *queue)
475 {
476 njs_int_t ret;
477 njs_value_t arguments[2];
478 njs_function_t *function;
479 njs_queue_link_t *link;
480 njs_promise_reaction_t *reaction;
481
482 for (link = njs_queue_first(queue);
483 link != njs_queue_tail(queue);
484 link = njs_queue_next(link))
485 {
486 reaction = njs_queue_link_data(link, njs_promise_reaction_t, link);
487
488 function = njs_promise_create_function(vm,
489 sizeof(njs_promise_context_t));
490 function->u.native = njs_promise_reaction_job;
491
492 njs_set_data(&arguments[0], reaction, 0);
493 arguments[1] = *value;
494
495 ret = njs_promise_add_event(vm, function, arguments, 2);
496 if (njs_slow_path(ret != NJS_OK)) {
497 return njs_value_arg(&njs_value_null);
498 }
499 }
500
501 return njs_value_arg(&njs_value_undefined);
502 }
503
504
505 njs_inline njs_value_t *
njs_promise_fulfill(njs_vm_t * vm,njs_promise_t * promise,njs_value_t * value)506 njs_promise_fulfill(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *value)
507 {
508 njs_queue_t queue;
509 njs_promise_data_t *data;
510
511 data = njs_data(&promise->value);
512
513 data->result = *value;
514 data->state = NJS_PROMISE_FULFILL;
515
516 if (njs_queue_is_empty(&data->fulfill_queue)) {
517 return njs_value_arg(&njs_value_undefined);
518
519 } else {
520 queue = data->fulfill_queue;
521
522 queue.head.prev->next = &queue.head;
523 queue.head.next->prev = &queue.head;
524 }
525
526 njs_queue_init(&data->fulfill_queue);
527 njs_queue_init(&data->reject_queue);
528
529 return njs_promise_trigger_reactions(vm, value, &queue);
530 }
531
532
533 njs_inline njs_value_t *
njs_promise_reject(njs_vm_t * vm,njs_promise_t * promise,njs_value_t * reason)534 njs_promise_reject(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *reason)
535 {
536 njs_int_t ret;
537 njs_queue_t queue;
538 njs_promise_data_t *data;
539
540 data = njs_data(&promise->value);
541
542 data->result = *reason;
543 data->state = NJS_PROMISE_REJECTED;
544
545 if (!data->is_handled) {
546 ret = njs_promise_host_rejection_tracker(vm, promise,
547 NJS_PROMISE_REJECT);
548 if (njs_slow_path(ret != NJS_OK)) {
549 return njs_value_arg(&njs_value_null);
550 }
551 }
552
553 if (njs_queue_is_empty(&data->reject_queue)) {
554 return njs_value_arg(&njs_value_undefined);
555
556 } else {
557 queue = data->reject_queue;
558
559 queue.head.prev->next = &queue.head;
560 queue.head.next->prev = &queue.head;
561 }
562
563 njs_queue_init(&data->fulfill_queue);
564 njs_queue_init(&data->reject_queue);
565
566 return njs_promise_trigger_reactions(vm, reason, &queue);
567 }
568
569
570 static njs_int_t
njs_promise_host_rejection_tracker(njs_vm_t * vm,njs_promise_t * promise,njs_promise_rejection_type_t operation)571 njs_promise_host_rejection_tracker(njs_vm_t *vm, njs_promise_t *promise,
572 njs_promise_rejection_type_t operation)
573 {
574 uint32_t i, length;
575 njs_value_t *value;
576 njs_promise_data_t *data;
577
578 if (vm->options.unhandled_rejection
579 == NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE)
580 {
581 return NJS_OK;
582 }
583
584 if (vm->promise_reason == NULL) {
585 vm->promise_reason = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
586 if (njs_slow_path(vm->promise_reason == NULL)) {
587 return NJS_ERROR;
588 }
589 }
590
591 data = njs_data(&promise->value);
592
593 if (operation == NJS_PROMISE_REJECT) {
594 if (vm->promise_reason != NULL) {
595 return njs_array_add(vm, vm->promise_reason, &data->result);
596 }
597
598 } else {
599 value = vm->promise_reason->start;
600 length = vm->promise_reason->length;
601
602 for (i = 0; i < length; i++) {
603 if (njs_values_same(&value[i], &data->result)) {
604 length--;
605
606 if (i < length) {
607 memmove(&value[i], &value[i + 1],
608 sizeof(njs_value_t) * (length - i));
609 }
610
611 break;
612 }
613 }
614
615 vm->promise_reason->length = length;
616 }
617
618 return NJS_OK;
619 }
620
621
622 static njs_int_t
njs_promise_invoke_then(njs_vm_t * vm,njs_value_t * promise,njs_value_t * args,njs_int_t nargs)623 njs_promise_invoke_then(njs_vm_t *vm, njs_value_t *promise, njs_value_t *args,
624 njs_int_t nargs)
625 {
626 njs_int_t ret;
627 njs_value_t function;
628
629 static const njs_value_t string_then = njs_string("then");
630
631 ret = njs_value_property(vm, promise, njs_value_arg(&string_then),
632 &function);
633 if (njs_slow_path(ret != NJS_OK)) {
634 if (ret == NJS_DECLINED) {
635 goto failed;
636 }
637
638 return NJS_ERROR;
639 }
640
641 if (njs_fast_path(njs_is_function(&function))) {
642 return njs_function_call(vm, njs_function(&function), promise, args,
643 nargs, &vm->retval);
644 }
645
646 failed:
647
648 njs_type_error(vm, "is not a function");
649
650 return NJS_ERROR;
651 }
652
653
654 static njs_int_t
njs_promise_resolve_function(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)655 njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
656 njs_index_t unused)
657 {
658 njs_int_t ret;
659 njs_value_t *resolution, error, then, arguments[3];
660 njs_promise_t *promise;
661 njs_function_t *function;
662 njs_native_frame_t *active_frame;
663 njs_promise_context_t *context;
664
665 static const njs_value_t string_then = njs_string("then");
666
667 active_frame = vm->top_frame;
668 context = active_frame->function->context;
669 promise = njs_promise(&context->promise);
670
671 if (*context->resolved_ref) {
672 njs_vm_retval_set(vm, &njs_value_undefined);
673 return NJS_OK;
674 }
675
676 *context->resolved_ref = 1;
677
678 resolution = njs_arg(args, nargs, 1);
679
680 if (njs_values_same(resolution, &context->promise)) {
681 njs_error_fmt_new(vm, &error, NJS_OBJ_TYPE_TYPE_ERROR,
682 "promise self resolution");
683 if (njs_slow_path(!njs_is_error(&error))) {
684 return NJS_ERROR;
685 }
686
687 njs_vm_retval_set(vm, njs_promise_reject(vm, promise, &error));
688
689 return NJS_OK;
690 }
691
692 if (!njs_is_object(resolution)) {
693 goto fulfill;
694 }
695
696 ret = njs_value_property(vm, resolution, njs_value_arg(&string_then),
697 &then);
698 if (njs_slow_path(ret == NJS_ERROR)) {
699 if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
700 return NJS_ERROR;
701 }
702
703 njs_vm_retval_set(vm, njs_promise_reject(vm, promise, &vm->retval));
704 if (njs_slow_path(njs_vm_retval(vm)->type == NJS_NULL)) {
705 return NJS_ERROR;
706 }
707
708 return NJS_OK;
709 }
710
711 if (!njs_is_function(&then)) {
712 goto fulfill;
713 }
714
715 arguments[0] = context->promise;
716 arguments[1] = *resolution;
717 arguments[2] = then;
718
719 function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
720 if (njs_slow_path(function == NULL)) {
721 return NJS_ERROR;
722 }
723
724 function->u.native = njs_promise_resolve_thenable_job;
725
726 ret = njs_promise_add_event(vm, function, arguments, 3);
727 if (njs_slow_path(ret != NJS_OK)) {
728 return ret;
729 }
730
731 njs_vm_retval_set(vm, &njs_value_undefined);
732
733 return NJS_OK;
734
735 fulfill:
736
737 njs_vm_retval_set(vm, njs_promise_fulfill(vm, promise, resolution));
738 if (njs_slow_path(njs_vm_retval(vm)->type == NJS_NULL)) {
739 return NJS_ERROR;
740 }
741
742 return NJS_OK;
743 }
744
745
746 static njs_int_t
njs_promise_object_resolve(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)747 njs_promise_object_resolve(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
748 njs_index_t unused)
749 {
750 njs_promise_t *promise;
751
752 if (njs_slow_path(!njs_is_object(njs_arg(args, nargs, 0)))) {
753 njs_type_error(vm, "this value is not an object");
754 return NJS_ERROR;
755 }
756
757 promise = njs_promise_resolve(vm, njs_argument(args, 0),
758 njs_arg(args, nargs, 1));
759 if (njs_slow_path(promise == NULL)) {
760 return NJS_ERROR;
761 }
762
763 njs_set_promise(&vm->retval, promise);
764
765 return NJS_OK;
766 }
767
768
769 njs_promise_t *
njs_promise_resolve(njs_vm_t * vm,njs_value_t * constructor,njs_value_t * x)770 njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x)
771 {
772 njs_int_t ret;
773 njs_value_t value;
774 njs_object_t *object;
775 njs_promise_capability_t *capability;
776
777 static const njs_value_t string_constructor = njs_string("constructor");
778
779 if (njs_is_object(x)) {
780 object = njs_object_proto_lookup(njs_object(x), NJS_PROMISE,
781 njs_object_t);
782
783 if (object != NULL) {
784 ret = njs_value_property(vm, x, njs_value_arg(&string_constructor),
785 &value);
786 if (njs_slow_path(ret == NJS_ERROR)) {
787 return NULL;
788 }
789
790 if (njs_values_same(&value, constructor)) {
791 return njs_promise(x);
792 }
793 }
794 }
795
796 capability = njs_promise_new_capability(vm, constructor);
797 if (njs_slow_path(capability == NULL)) {
798 return NULL;
799 }
800
801 ret = njs_function_call(vm, njs_function(&capability->resolve),
802 &njs_value_undefined, x, 1, &value);
803 if (njs_slow_path(ret != NJS_OK)) {
804 return NULL;
805 }
806
807 return njs_promise(&capability->promise);
808 }
809
810
811 static njs_int_t
njs_promise_reject_function(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)812 njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
813 njs_index_t unused)
814 {
815 njs_value_t *value;
816 njs_native_frame_t *active_frame;
817 njs_promise_context_t *context;
818
819 active_frame = vm->top_frame;
820 context = active_frame->function->context;
821
822 if (*context->resolved_ref) {
823 njs_vm_retval_set(vm, &njs_value_undefined);
824 return NJS_OK;
825 }
826
827 *context->resolved_ref = 1;
828
829 value = njs_promise_reject(vm, njs_promise(&context->promise),
830 njs_arg(args, nargs, 1));
831 if (njs_slow_path(value->type == NJS_NULL)) {
832 return NJS_ERROR;
833 }
834
835 njs_vm_retval_set(vm, value);
836
837 return NJS_OK;
838 }
839
840
841 static njs_int_t
njs_promise_object_reject(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)842 njs_promise_object_reject(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
843 njs_index_t unused)
844 {
845 njs_int_t ret;
846 njs_value_t value;
847 njs_promise_capability_t *capability;
848
849 if (njs_slow_path(!njs_is_object(njs_arg(args, nargs, 0)))) {
850 njs_type_error(vm, "this value is not an object");
851 return NJS_ERROR;
852 }
853
854 capability = njs_promise_new_capability(vm, njs_argument(args, 0));
855 if (njs_slow_path(capability == NULL)) {
856 return NJS_ERROR;
857 }
858
859 ret = njs_function_call(vm, njs_function(&capability->reject),
860 &njs_value_undefined, njs_arg(args, nargs, 1),
861 1, &value);
862 if (njs_slow_path(ret != NJS_OK)) {
863 return ret;
864 }
865
866 njs_vm_retval_set(vm, &capability->promise);
867
868 return NJS_OK;
869 }
870
871
872 static njs_int_t
njs_promise_prototype_then(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)873 njs_promise_prototype_then(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
874 njs_index_t unused)
875 {
876 njs_int_t ret;
877 njs_value_t *promise, *fulfilled, *rejected, constructor;
878 njs_object_t *object;
879 njs_function_t *function;
880 njs_promise_capability_t *capability;
881
882 promise = njs_arg(args, nargs, 0);
883
884 if (njs_slow_path(!njs_is_object(promise))) {
885 goto failed;
886 }
887
888 object = njs_object_proto_lookup(njs_object(promise), NJS_PROMISE,
889 njs_object_t);
890 if (njs_slow_path(object == NULL)) {
891 goto failed;
892 }
893
894 function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
895 function->u.native = njs_promise_constructor;
896
897 njs_set_function(&constructor, function);
898
899 ret = njs_value_species_constructor(vm, promise, &constructor,
900 &constructor);
901 if (njs_slow_path(ret != NJS_OK)) {
902 return ret;
903 }
904
905 capability = njs_promise_new_capability(vm, &constructor);
906 if (njs_slow_path(capability == NULL)) {
907 return NJS_ERROR;
908 }
909
910 fulfilled = njs_arg(args, nargs, 1);
911 rejected = njs_arg(args, nargs, 2);
912
913 return njs_promise_perform_then(vm, promise, fulfilled, rejected,
914 capability);
915
916 failed:
917
918 njs_type_error(vm, "required a promise object");
919
920 return NJS_ERROR;
921 }
922
923
924 njs_int_t
njs_promise_perform_then(njs_vm_t * vm,njs_value_t * value,njs_value_t * fulfilled,njs_value_t * rejected,njs_promise_capability_t * capability)925 njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
926 njs_value_t *fulfilled, njs_value_t *rejected,
927 njs_promise_capability_t *capability)
928 {
929 njs_int_t ret;
930 njs_value_t arguments[2];
931 njs_promise_t *promise;
932 njs_function_t *function;
933 njs_promise_data_t *data;
934 njs_promise_reaction_t *fulfilled_reaction, *rejected_reaction;
935
936 if (!njs_is_function(fulfilled)) {
937 fulfilled = njs_value_arg(&njs_value_undefined);
938 }
939
940 if (!njs_is_function(rejected)) {
941 rejected = njs_value_arg(&njs_value_undefined);
942 }
943
944 promise = njs_promise(value);
945 data = njs_data(&promise->value);
946
947 fulfilled_reaction = njs_mp_alloc(vm->mem_pool,
948 sizeof(njs_promise_reaction_t));
949 if (njs_slow_path(fulfilled_reaction == NULL)) {
950 njs_memory_error(vm);
951 return NJS_ERROR;
952 }
953
954 fulfilled_reaction->capability = capability;
955 fulfilled_reaction->handler = *fulfilled;
956 fulfilled_reaction->type = NJS_PROMISE_FULFILL;
957
958 rejected_reaction = njs_mp_alloc(vm->mem_pool,
959 sizeof(njs_promise_reaction_t));
960 if (njs_slow_path(rejected_reaction == NULL)) {
961 njs_memory_error(vm);
962 return NJS_ERROR;
963 }
964
965 rejected_reaction->capability = capability;
966 rejected_reaction->handler = *rejected;
967 rejected_reaction->type = NJS_PROMISE_REJECTED;
968
969 if (data->state == NJS_PROMISE_PENDING) {
970 njs_queue_insert_tail(&data->fulfill_queue, &fulfilled_reaction->link);
971 njs_queue_insert_tail(&data->reject_queue, &rejected_reaction->link);
972
973 } else {
974 function = njs_promise_create_function(vm,
975 sizeof(njs_promise_context_t));
976 function->u.native = njs_promise_reaction_job;
977
978 if (data->state == NJS_PROMISE_REJECTED) {
979 njs_set_data(&arguments[0], rejected_reaction, 0);
980
981 ret = njs_promise_host_rejection_tracker(vm, promise,
982 NJS_PROMISE_HANDLE);
983 if (njs_slow_path(ret != NJS_OK)) {
984 return ret;
985 }
986
987 } else {
988 njs_set_data(&arguments[0], fulfilled_reaction, 0);
989 }
990
991 arguments[1] = data->result;
992
993 ret = njs_promise_add_event(vm, function, arguments, 2);
994 if (njs_slow_path(ret != NJS_OK)) {
995 return ret;
996 }
997 }
998
999 data->is_handled = 1;
1000
1001 if (capability == NULL) {
1002 njs_vm_retval_set(vm, &njs_value_undefined);
1003
1004 } else {
1005 njs_vm_retval_set(vm, &capability->promise);
1006 }
1007
1008 return NJS_OK;
1009 }
1010
1011
1012 static njs_int_t
njs_promise_prototype_catch(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1013 njs_promise_prototype_catch(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1014 njs_index_t unused)
1015 {
1016 njs_value_t arguments[2];
1017
1018 arguments[0] = njs_value_undefined;
1019 arguments[1] = *njs_arg(args, nargs, 1);
1020
1021 return njs_promise_invoke_then(vm, njs_arg(args, nargs, 0), arguments, 2);
1022 }
1023
1024
1025 static njs_int_t
njs_promise_prototype_finally(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1026 njs_promise_prototype_finally(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1027 njs_index_t unused)
1028 {
1029 njs_int_t ret;
1030 njs_value_t *promise, *finally, constructor, arguments[2];
1031 njs_function_t *function;
1032 njs_promise_context_t *context;
1033
1034 promise = njs_arg(args, nargs, 0);
1035
1036 if (njs_slow_path(!njs_is_object(promise))) {
1037 njs_type_error(vm, "required a object");
1038 return NJS_ERROR;
1039 }
1040
1041 finally = njs_arg(args, nargs, 1);
1042
1043 function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
1044 function->u.native = njs_promise_constructor;
1045
1046 njs_set_function(&constructor, function);
1047
1048 ret = njs_value_species_constructor(vm, promise, &constructor,
1049 &constructor);
1050 if (njs_slow_path(ret != NJS_OK)) {
1051 return ret;
1052 }
1053
1054 if (!njs_is_function(finally)) {
1055 arguments[0] = *finally;
1056 arguments[1] = *finally;
1057
1058 return njs_promise_invoke_then(vm, promise, arguments, 2);
1059 }
1060
1061 function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
1062 if (njs_slow_path(function == NULL)) {
1063 return NJS_ERROR;
1064 }
1065
1066 function->u.native = njs_promise_then_finally_function;
1067 function->args_count = 1;
1068
1069 context = function->context;
1070 context->constructor = constructor;
1071 context->finally = *finally;
1072 context->handler = njs_promise_then_finally_return;
1073
1074 njs_set_function(&arguments[0], function);
1075
1076 function = njs_promise_create_function(vm, sizeof(njs_promise_context_t));
1077 if (njs_slow_path(function == NULL)) {
1078 njs_mp_free(vm->mem_pool, njs_function(&arguments[0]));
1079 return NJS_ERROR;
1080 }
1081
1082 function->u.native = njs_promise_then_finally_function;
1083 function->args_count = 1;
1084
1085 context = function->context;
1086 context->constructor = constructor;
1087 context->finally = *finally;
1088 context->handler = njs_promise_catch_finally_return;
1089
1090 njs_set_function(&arguments[1], function);
1091
1092 return njs_promise_invoke_then(vm, promise, arguments, 2);
1093 }
1094
1095
1096 static njs_int_t
njs_promise_then_finally_function(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1097 njs_promise_then_finally_function(njs_vm_t *vm, njs_value_t *args,
1098 njs_uint_t nargs, njs_index_t unused)
1099 {
1100 njs_int_t ret;
1101 njs_value_t value, retval, argument;
1102 njs_promise_t *promise;
1103 njs_function_t *function;
1104 njs_native_frame_t *frame;
1105 njs_promise_context_t *context;
1106
1107 frame = vm->top_frame;
1108 context = frame->function->context;
1109
1110 ret = njs_function_call(vm, njs_function(&context->finally),
1111 &njs_value_undefined, args, 0, &retval);
1112 if (njs_slow_path(ret != NJS_OK)) {
1113 return ret;
1114 }
1115
1116 promise = njs_promise_resolve(vm, &context->constructor, &retval);
1117 if (njs_slow_path(promise == NULL)) {
1118 return NJS_ERROR;
1119 }
1120
1121 njs_set_promise(&value, promise);
1122
1123 function = njs_promise_create_function(vm, sizeof(njs_value_t));
1124 if (njs_slow_path(function == NULL)) {
1125 return NJS_ERROR;
1126 }
1127
1128 function->u.native = context->handler;
1129
1130 *((njs_value_t *) function->context) = *njs_arg(args, nargs, 1);
1131
1132 njs_set_function(&argument, function);
1133
1134 return njs_promise_invoke_then(vm, &value, &argument, 1);
1135 }
1136
1137
1138 static njs_int_t
njs_promise_then_finally_return(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1139 njs_promise_then_finally_return(njs_vm_t *vm, njs_value_t *args,
1140 njs_uint_t nargs, njs_index_t unused)
1141 {
1142 njs_vm_retval_set(vm, vm->top_frame->function->context);
1143 return NJS_OK;
1144 }
1145
1146
1147 static njs_int_t
njs_promise_catch_finally_return(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1148 njs_promise_catch_finally_return(njs_vm_t *vm, njs_value_t *args,
1149 njs_uint_t nargs, njs_index_t unused)
1150 {
1151 njs_vm_retval_set(vm, vm->top_frame->function->context);
1152 return NJS_ERROR;
1153 }
1154
1155
1156 static njs_int_t
njs_promise_reaction_job(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1157 njs_promise_reaction_job(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1158 njs_index_t unused)
1159 {
1160 njs_int_t ret;
1161 njs_bool_t is_error;
1162 njs_value_t *value, *argument, retval;
1163 njs_promise_reaction_t *reaction;
1164 njs_promise_capability_t *capability;
1165
1166 value = njs_arg(args, nargs, 1);
1167 argument = njs_arg(args, nargs, 2);
1168
1169 reaction = njs_data(value);
1170 capability = reaction->capability;
1171
1172 is_error = 0;
1173
1174 if (njs_is_undefined(&reaction->handler)) {
1175 if (reaction->type == NJS_PROMISE_REJECTED) {
1176 is_error = 1;
1177 }
1178
1179 retval = *argument;
1180
1181 } else {
1182 ret = njs_function_call(vm, njs_function(&reaction->handler),
1183 &njs_value_undefined, argument, 1, &retval);
1184 if (njs_slow_path(ret != NJS_OK)) {
1185 if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
1186 return NJS_ERROR;
1187 }
1188
1189 retval = vm->retval;
1190 is_error = 1;
1191 }
1192 }
1193
1194 if (capability == NULL) {
1195 njs_vm_retval_set(vm, &retval);
1196 return NJS_OK;
1197 }
1198
1199 if (is_error) {
1200 ret = njs_function_call(vm, njs_function(&capability->reject),
1201 &njs_value_undefined, &retval, 1, &vm->retval);
1202
1203 } else {
1204 ret = njs_function_call(vm, njs_function(&capability->resolve),
1205 &njs_value_undefined, &retval, 1, &vm->retval);
1206 }
1207
1208 if (njs_slow_path(ret != NJS_OK)) {
1209 return NJS_ERROR;
1210 }
1211
1212 return NJS_OK;
1213 }
1214
1215
1216 static njs_int_t
njs_promise_resolve_thenable_job(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1217 njs_promise_resolve_thenable_job(njs_vm_t *vm, njs_value_t *args,
1218 njs_uint_t nargs, njs_index_t unused)
1219 {
1220 njs_int_t ret;
1221 njs_value_t *promise, retval, arguments[2];
1222
1223 promise = njs_arg(args, nargs, 1);
1224
1225 ret = njs_promise_create_resolving_functions(vm, njs_promise(promise),
1226 arguments);
1227 if (njs_slow_path(ret != NJS_OK)) {
1228 return ret;
1229 }
1230
1231 ret = njs_function_call(vm, njs_function(njs_arg(args, nargs, 3)),
1232 njs_arg(args, nargs, 2), arguments, 2, &retval);
1233 if (njs_slow_path(ret != NJS_OK)) {
1234
1235 if (njs_slow_path(njs_is_memory_error(vm, &vm->retval))) {
1236 return NJS_ERROR;
1237 }
1238
1239 ret = njs_function_call(vm, njs_function(&arguments[1]),
1240 &njs_value_undefined, &vm->retval, 1,
1241 &vm->retval);
1242 if (njs_slow_path(ret != NJS_OK)) {
1243 return NJS_ERROR;
1244 }
1245 }
1246
1247 return NJS_OK;
1248 }
1249
1250
1251 static njs_int_t
njs_promise_all(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t function_type)1252 njs_promise_all(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1253 njs_index_t function_type)
1254 {
1255 njs_int_t ret;
1256 njs_value_t *promise, resolve;
1257 njs_iterator_handler_t handler;
1258 njs_promise_iterator_args_t pargs;
1259
1260 promise = njs_argument(args, 0);
1261
1262 pargs.capability = njs_promise_new_capability(vm, promise);
1263 if (njs_slow_path(pargs.capability == NULL)) {
1264 return NJS_ERROR;
1265 }
1266
1267 ret = njs_value_property(vm, promise, njs_value_arg(&string_resolve),
1268 &resolve);
1269 if (njs_slow_path(ret != NJS_OK)) {
1270 return ret;
1271 }
1272
1273 if (njs_slow_path(!njs_is_function(&resolve))) {
1274 njs_type_error(vm, "resolve is not callable");
1275 return NJS_ERROR;
1276 }
1277
1278 pargs.function = njs_function(&resolve);
1279 pargs.constructor = promise;
1280
1281 switch (function_type) {
1282 case NJS_PROMISE_ALL_SETTLED:
1283 handler = njs_promise_perform_all_settled_handler;
1284 break;
1285
1286 case NJS_PROMISE_ANY:
1287 handler = njs_promise_perform_any_handler;
1288 break;
1289
1290 default:
1291 handler = njs_promise_perform_all_handler;
1292 break;
1293 }
1294
1295 return njs_promise_perform_all(vm, njs_arg(args, nargs, 1), &pargs,
1296 handler, &vm->retval);
1297 }
1298
1299
1300 static njs_int_t
njs_promise_perform_all(njs_vm_t * vm,njs_value_t * iterator,njs_promise_iterator_args_t * pargs,njs_iterator_handler_t handler,njs_value_t * retval)1301 njs_promise_perform_all(njs_vm_t *vm, njs_value_t *iterator,
1302 njs_promise_iterator_args_t *pargs, njs_iterator_handler_t handler,
1303 njs_value_t *retval)
1304 {
1305 int64_t length;
1306 njs_int_t ret;
1307 njs_value_t argument;
1308 njs_object_t *error;
1309
1310 if (njs_slow_path(!njs_is_object(pargs->constructor))) {
1311 njs_type_error(vm, "constructor is not object");
1312 return NJS_ERROR;
1313 }
1314
1315 njs_memzero(&pargs->args, sizeof(njs_iterator_args_t));
1316
1317 ret = njs_object_length(vm, iterator, &length);
1318 if (njs_slow_path(ret != NJS_OK)) {
1319 return ret;
1320 }
1321
1322 pargs->args.data = njs_array_alloc(vm, 1, length, 0);
1323 if (njs_slow_path(pargs->args.data == NULL)) {
1324 return NJS_ERROR;
1325 }
1326
1327 pargs->remaining = njs_mp_alloc(vm->mem_pool, sizeof(uint32_t));
1328 if (njs_slow_path(pargs->remaining == NULL)) {
1329 njs_memory_error(vm);
1330 return NJS_ERROR;
1331 }
1332
1333 (*pargs->remaining) = 1;
1334
1335 pargs->args.value = iterator;
1336 pargs->args.to = length;
1337
1338 ret = njs_object_iterate(vm, &pargs->args, handler);
1339 if (njs_slow_path(ret == NJS_ERROR)) {
1340 return ret;
1341 }
1342
1343 if (--(*pargs->remaining) == 0) {
1344 njs_mp_free(vm->mem_pool, pargs->remaining);
1345
1346 njs_set_array(&argument, pargs->args.data);
1347
1348 if (handler == njs_promise_perform_any_handler) {
1349 error = njs_error_alloc(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR,
1350 NULL, &string_any_rejected, &argument);
1351 if (njs_slow_path(error == NULL)) {
1352 return NJS_ERROR;
1353 }
1354
1355 njs_set_object(&argument, error);
1356 }
1357
1358 ret = njs_function_call(vm, njs_function(&pargs->capability->resolve),
1359 &njs_value_undefined, &argument, 1, retval);
1360 if (njs_slow_path(ret == NJS_ERROR)) {
1361 return ret;
1362 }
1363 }
1364
1365 *retval = pargs->capability->promise;
1366
1367 return NJS_OK;
1368 }
1369
1370
1371 static njs_int_t
njs_promise_perform_all_handler(njs_vm_t * vm,njs_iterator_args_t * args,njs_value_t * value,int64_t index)1372 njs_promise_perform_all_handler(njs_vm_t *vm, njs_iterator_args_t *args,
1373 njs_value_t *value, int64_t index)
1374 {
1375 njs_int_t ret;
1376 njs_array_t *array;
1377 njs_value_t arguments[2], next;
1378 njs_function_t *on_fulfilled;
1379 njs_promise_capability_t *capability;
1380 njs_promise_all_context_t *context;
1381 njs_promise_iterator_args_t *pargs;
1382
1383 pargs = (njs_promise_iterator_args_t *) args;
1384
1385 capability = pargs->capability;
1386
1387 array = args->data;
1388 njs_set_undefined(&array->start[index]);
1389
1390 ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
1391 1, &next);
1392 if (njs_slow_path(ret == NJS_ERROR)) {
1393 return ret;
1394 }
1395
1396 on_fulfilled = njs_promise_create_function(vm,
1397 sizeof(njs_promise_all_context_t));
1398 if (njs_slow_path(on_fulfilled == NULL)) {
1399 return NJS_ERROR;
1400 }
1401
1402 on_fulfilled->u.native = njs_promise_all_resolve_element_functions;
1403 on_fulfilled->args_count = 1;
1404
1405 context = on_fulfilled->context;
1406
1407 context->already_called = 0;
1408 context->index = (uint32_t) index;
1409 context->values = pargs->args.data;
1410 context->capability = capability;
1411 context->remaining_elements = pargs->remaining;
1412
1413 (*pargs->remaining)++;
1414
1415 njs_set_function(&arguments[0], on_fulfilled);
1416 arguments[1] = capability->reject;
1417
1418 ret = njs_promise_invoke_then(vm, &next, arguments, 2);
1419 if (njs_slow_path(ret == NJS_ERROR)) {
1420 return ret;
1421 }
1422
1423 return NJS_OK;
1424 }
1425
1426
1427 static njs_int_t
njs_promise_all_resolve_element_functions(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1428 njs_promise_all_resolve_element_functions(njs_vm_t *vm, njs_value_t *args,
1429 njs_uint_t nargs, njs_index_t unused)
1430 {
1431 njs_value_t arguments;
1432 njs_promise_all_context_t *context;
1433
1434 context = vm->top_frame->function->context;
1435
1436 if (context->already_called) {
1437 njs_vm_retval_set(vm, &njs_value_undefined);
1438 return NJS_OK;
1439 }
1440
1441 context->already_called = 1;
1442 context->values->start[context->index] = *njs_arg(args, nargs, 1);
1443
1444 if (--(*context->remaining_elements) == 0) {
1445 njs_mp_free(vm->mem_pool, context->remaining_elements);
1446
1447 njs_set_array(&arguments, context->values);
1448
1449 return njs_function_call(vm,
1450 njs_function(&context->capability->resolve),
1451 &njs_value_undefined, &arguments, 1,
1452 &vm->retval);
1453 }
1454
1455 njs_vm_retval_set(vm, &njs_value_undefined);
1456
1457 return NJS_OK;
1458 }
1459
1460
1461 static njs_int_t
njs_promise_perform_all_settled_handler(njs_vm_t * vm,njs_iterator_args_t * args,njs_value_t * value,int64_t index)1462 njs_promise_perform_all_settled_handler(njs_vm_t *vm, njs_iterator_args_t *args,
1463 njs_value_t *value, int64_t index)
1464 {
1465 njs_int_t ret;
1466 njs_array_t *array;
1467 njs_value_t arguments[2], next;
1468 njs_function_t *on_fulfilled, *on_rejected;
1469 njs_promise_capability_t *capability;
1470 njs_promise_all_context_t *context;
1471 njs_promise_iterator_args_t *pargs;
1472
1473 pargs = (njs_promise_iterator_args_t *) args;
1474
1475 capability = pargs->capability;
1476
1477 array = args->data;
1478 njs_set_undefined(&array->start[index]);
1479
1480 ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
1481 1, &next);
1482 if (njs_slow_path(ret == NJS_ERROR)) {
1483 return ret;
1484 }
1485
1486 on_fulfilled = njs_promise_create_function(vm,
1487 sizeof(njs_promise_all_context_t));
1488 if (njs_slow_path(on_fulfilled == NULL)) {
1489 return NJS_ERROR;
1490 }
1491
1492 context = on_fulfilled->context;
1493
1494 context->already_called = 0;
1495 context->index = (uint32_t) index;
1496 context->values = pargs->args.data;
1497 context->capability = capability;
1498 context->remaining_elements = pargs->remaining;
1499
1500 on_rejected = njs_promise_create_function(vm, 0);
1501 if (njs_slow_path(on_rejected == NULL)) {
1502 return NJS_ERROR;
1503 }
1504
1505 on_fulfilled->u.native = njs_promise_all_settled_element_functions;
1506 on_rejected->u.native = njs_promise_all_settled_element_functions;
1507 on_rejected->magic8 = 1; /* rejected. */
1508
1509 on_fulfilled->args_count = 1;
1510 on_rejected->args_count = 1;
1511
1512 on_rejected->context = context;
1513
1514 (*pargs->remaining)++;
1515
1516 njs_set_function(&arguments[0], on_fulfilled);
1517 njs_set_function(&arguments[1], on_rejected);
1518
1519 ret = njs_promise_invoke_then(vm, &next, arguments, 2);
1520 if (njs_slow_path(ret == NJS_ERROR)) {
1521 return ret;
1522 }
1523
1524 return NJS_OK;
1525 }
1526
1527
1528 static njs_int_t
njs_promise_all_settled_element_functions(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t rejected)1529 njs_promise_all_settled_element_functions(njs_vm_t *vm,
1530 njs_value_t *args, njs_uint_t nargs, njs_index_t rejected)
1531 {
1532 njs_int_t ret;
1533 njs_value_t arguments, *value;
1534 njs_object_t *obj;
1535 const njs_value_t *status, *set;
1536 njs_promise_all_context_t *context;
1537
1538 static const njs_value_t string_status = njs_string("status");
1539 static const njs_value_t string_fulfilled = njs_string("fulfilled");
1540 static const njs_value_t string_value = njs_string("value");
1541 static const njs_value_t string_rejected = njs_string("rejected");
1542 static const njs_value_t string_reason = njs_string("reason");
1543
1544 context = vm->top_frame->function->context;
1545
1546 if (context->already_called) {
1547 njs_vm_retval_set(vm, &njs_value_undefined);
1548 return NJS_OK;
1549 }
1550
1551 context->already_called = 1;
1552
1553 obj = njs_object_alloc(vm);
1554 if (njs_slow_path(obj == NULL)) {
1555 return NJS_ERROR;
1556 }
1557
1558 value = &context->values->start[context->index];
1559
1560 njs_set_object(value, obj);
1561
1562 if (rejected) {
1563 status = &string_rejected;
1564 set = &string_reason;
1565
1566 } else {
1567 status = &string_fulfilled;
1568 set = &string_value;
1569 }
1570
1571 ret = njs_value_property_set(vm, value, njs_value_arg(&string_status),
1572 njs_value_arg(status));
1573 if (njs_slow_path(ret == NJS_ERROR)) {
1574 return ret;
1575 }
1576
1577 ret = njs_value_property_set(vm, value, njs_value_arg(set),
1578 njs_arg(args, nargs, 1));
1579 if (njs_slow_path(ret == NJS_ERROR)) {
1580 return ret;
1581 }
1582
1583 if (--(*context->remaining_elements) == 0) {
1584 njs_mp_free(vm->mem_pool, context->remaining_elements);
1585
1586 njs_set_array(&arguments, context->values);
1587
1588 return njs_function_call(vm,
1589 njs_function(&context->capability->resolve),
1590 &njs_value_undefined, &arguments, 1,
1591 &vm->retval);
1592 }
1593
1594 njs_vm_retval_set(vm, &njs_value_undefined);
1595
1596 return NJS_OK;
1597 }
1598
1599
1600 static njs_int_t
njs_promise_perform_any_handler(njs_vm_t * vm,njs_iterator_args_t * args,njs_value_t * value,int64_t index)1601 njs_promise_perform_any_handler(njs_vm_t *vm, njs_iterator_args_t *args,
1602 njs_value_t *value, int64_t index)
1603 {
1604 njs_int_t ret;
1605 njs_array_t *array;
1606 njs_value_t arguments[2], next;
1607 njs_function_t *on_rejected;
1608 njs_promise_capability_t *capability;
1609 njs_promise_all_context_t *context;
1610 njs_promise_iterator_args_t *pargs;
1611
1612 pargs = (njs_promise_iterator_args_t *) args;
1613
1614 capability = pargs->capability;
1615
1616 array = pargs->args.data;
1617 njs_set_undefined(&array->start[index]);
1618
1619 ret = njs_function_call(vm, pargs->function, pargs->constructor, value, 1,
1620 &next);
1621 if (njs_slow_path(ret == NJS_ERROR)) {
1622 return ret;
1623 }
1624
1625 on_rejected = njs_promise_create_function(vm,
1626 sizeof(njs_promise_all_context_t));
1627 if (njs_slow_path(on_rejected == NULL)) {
1628 return NJS_ERROR;
1629 }
1630
1631 on_rejected->u.native = njs_promise_any_reject_element_functions;
1632 on_rejected->args_count = 1;
1633
1634 context = on_rejected->context;
1635
1636 context->already_called = 0;
1637 context->index = (uint32_t) index;
1638 context->values = pargs->args.data;
1639 context->capability = capability;
1640 context->remaining_elements = pargs->remaining;
1641
1642 (*pargs->remaining)++;
1643
1644 arguments[0] = capability->resolve;
1645 njs_set_function(&arguments[1], on_rejected);
1646
1647 ret = njs_promise_invoke_then(vm, &next, arguments, 2);
1648 if (njs_slow_path(ret == NJS_ERROR)) {
1649 return ret;
1650 }
1651
1652 return NJS_OK;
1653 }
1654
1655
1656 static njs_int_t
njs_promise_any_reject_element_functions(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1657 njs_promise_any_reject_element_functions(njs_vm_t *vm, njs_value_t *args,
1658 njs_uint_t nargs, njs_index_t unused)
1659 {
1660 njs_value_t argument;
1661 njs_object_t *error;
1662 njs_promise_all_context_t *context;
1663
1664 context = vm->top_frame->function->context;
1665
1666 if (context->already_called) {
1667 njs_vm_retval_set(vm, &njs_value_undefined);
1668 return NJS_OK;
1669 }
1670
1671 context->already_called = 1;
1672 context->values->start[context->index] = *njs_arg(args, nargs, 1);
1673
1674 if (--(*context->remaining_elements) == 0) {
1675 njs_mp_free(vm->mem_pool, context->remaining_elements);
1676
1677 njs_set_array(&argument, context->values);
1678
1679 error = njs_error_alloc(vm, NJS_OBJ_TYPE_AGGREGATE_ERROR,
1680 NULL, &string_any_rejected, &argument);
1681 if (njs_slow_path(error == NULL)) {
1682 return NJS_ERROR;
1683 }
1684
1685 njs_set_object(&argument, error);
1686
1687 return njs_function_call(vm, njs_function(&context->capability->reject),
1688 &njs_value_undefined, &argument, 1,
1689 &vm->retval);
1690 }
1691
1692 njs_vm_retval_set(vm, &njs_value_undefined);
1693
1694 return NJS_OK;
1695 }
1696
1697
1698 static njs_int_t
njs_promise_race(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1699 njs_promise_race(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1700 njs_index_t unused)
1701 {
1702 int64_t length;
1703 njs_int_t ret;
1704 njs_value_t *promise, *iterator, resolve;
1705 njs_promise_iterator_args_t pargs;
1706
1707 promise = njs_argument(args, 0);
1708 iterator = njs_arg(args, nargs, 1);
1709
1710 pargs.capability = njs_promise_new_capability(vm, promise);
1711 if (njs_slow_path(pargs.capability == NULL)) {
1712 return NJS_ERROR;
1713 }
1714
1715 ret = njs_value_property(vm, promise, njs_value_arg(&string_resolve),
1716 &resolve);
1717 if (njs_slow_path(ret != NJS_OK)) {
1718 return ret;
1719 }
1720
1721 if (njs_slow_path(!njs_is_function(&resolve))) {
1722 njs_type_error(vm, "resolve is not callable");
1723 return NJS_ERROR;
1724 }
1725
1726 ret = njs_object_length(vm, iterator, &length);
1727 if (njs_slow_path(ret != NJS_OK)) {
1728 return ret;
1729 }
1730
1731 njs_memzero(&pargs.args, sizeof(njs_iterator_args_t));
1732
1733 pargs.function = njs_function(&resolve);
1734 pargs.constructor = promise;
1735
1736 pargs.args.value = iterator;
1737 pargs.args.to = length;
1738
1739 ret = njs_object_iterate(vm, &pargs.args, njs_promise_perform_race_handler);
1740 if (njs_slow_path(ret == NJS_ERROR)) {
1741 return ret;
1742 }
1743
1744 vm->retval = pargs.capability->promise;
1745
1746 return NJS_OK;
1747 }
1748
1749
1750 static njs_int_t
njs_promise_perform_race_handler(njs_vm_t * vm,njs_iterator_args_t * args,njs_value_t * value,int64_t index)1751 njs_promise_perform_race_handler(njs_vm_t *vm, njs_iterator_args_t *args,
1752 njs_value_t *value, int64_t index)
1753 {
1754 njs_int_t ret;
1755 njs_value_t arguments[2], next;
1756 njs_promise_capability_t *capability;
1757 njs_promise_iterator_args_t *pargs;
1758
1759 pargs = (njs_promise_iterator_args_t *) args;
1760
1761 ret = njs_function_call(vm, pargs->function, pargs->constructor, value,
1762 1, &next);
1763 if (njs_slow_path(ret == NJS_ERROR)) {
1764 return ret;
1765 }
1766
1767 capability = pargs->capability;
1768
1769 arguments[0] = capability->resolve;
1770 arguments[1] = capability->reject;
1771
1772 (void) njs_promise_invoke_then(vm, &next, arguments, 2);
1773
1774 return NJS_OK;
1775 }
1776
1777
1778 static njs_int_t
njs_promise_species(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1779 njs_promise_species(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1780 njs_index_t unused)
1781 {
1782 njs_vm_retval_set(vm, njs_arg(args, nargs, 0));
1783
1784 return NJS_OK;
1785 }
1786
1787
1788 static const njs_object_prop_t njs_promise_constructor_properties[] =
1789 {
1790 {
1791 .type = NJS_PROPERTY,
1792 .name = njs_string("name"),
1793 .value = njs_string("Promise"),
1794 .configurable = 1,
1795 },
1796
1797 {
1798 .type = NJS_PROPERTY,
1799 .name = njs_string("length"),
1800 .value = njs_value(NJS_NUMBER, 1, 1.0),
1801 .configurable = 1,
1802 },
1803
1804 {
1805 .type = NJS_PROPERTY_HANDLER,
1806 .name = njs_string("prototype"),
1807 .value = njs_prop_handler(njs_object_prototype_create),
1808 },
1809
1810 {
1811 .type = NJS_PROPERTY,
1812 .name = njs_string("resolve"),
1813 .value = njs_native_function(njs_promise_object_resolve, 1),
1814 .writable = 1,
1815 .configurable = 1,
1816 },
1817
1818 {
1819 .type = NJS_PROPERTY,
1820 .name = njs_string("reject"),
1821 .value = njs_native_function(njs_promise_object_reject, 1),
1822 .writable = 1,
1823 .configurable = 1,
1824 },
1825
1826 {
1827 .type = NJS_PROPERTY,
1828 .name = njs_string("all"),
1829 .value = njs_native_function2(njs_promise_all, 1, NJS_PROMISE_ALL),
1830 .writable = 1,
1831 .configurable = 1,
1832 },
1833
1834 {
1835 .type = NJS_PROPERTY,
1836 .name = njs_string("allSettled"),
1837 .value = njs_native_function2(njs_promise_all, 1,
1838 NJS_PROMISE_ALL_SETTLED),
1839 .writable = 1,
1840 .configurable = 1,
1841 },
1842
1843 {
1844 .type = NJS_PROPERTY,
1845 .name = njs_string("any"),
1846 .value = njs_native_function2(njs_promise_all, 1, NJS_PROMISE_ANY),
1847 .writable = 1,
1848 .configurable = 1,
1849 },
1850
1851 {
1852 .type = NJS_PROPERTY,
1853 .name = njs_string("race"),
1854 .value = njs_native_function(njs_promise_race, 1),
1855 .writable = 1,
1856 .configurable = 1,
1857 },
1858
1859 {
1860 .type = NJS_PROPERTY,
1861 .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES),
1862 .value = njs_value(NJS_INVALID, 1, NAN),
1863 .getter = njs_native_function(njs_promise_species, 0),
1864 .setter = njs_value(NJS_UNDEFINED, 0, NAN),
1865 .writable = NJS_ATTRIBUTE_UNSET,
1866 .configurable = 1,
1867 .enumerable = 0,
1868 },
1869 };
1870
1871
1872 const njs_object_init_t njs_promise_constructor_init = {
1873 njs_promise_constructor_properties,
1874 njs_nitems(njs_promise_constructor_properties),
1875 };
1876
1877
1878 static const njs_object_prop_t njs_promise_prototype_properties[] =
1879 {
1880 {
1881 .type = NJS_PROPERTY_HANDLER,
1882 .name = njs_string("constructor"),
1883 .value = njs_prop_handler(njs_object_prototype_create_constructor),
1884 .writable = 1,
1885 .configurable = 1,
1886 },
1887
1888 {
1889 .type = NJS_PROPERTY,
1890 .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
1891 .value = njs_string("Promise"),
1892 .configurable = 1,
1893 },
1894
1895 {
1896 .type = NJS_PROPERTY,
1897 .name = njs_string("then"),
1898 .value = njs_native_function(njs_promise_prototype_then, 2),
1899 .writable = 1,
1900 .configurable = 1,
1901 },
1902
1903 {
1904 .type = NJS_PROPERTY,
1905 .name = njs_string("catch"),
1906 .value = njs_native_function(njs_promise_prototype_catch, 1),
1907 .writable = 1,
1908 .configurable = 1,
1909 },
1910
1911 {
1912 .type = NJS_PROPERTY,
1913 .name = njs_string("finally"),
1914 .value = njs_native_function(njs_promise_prototype_finally, 1),
1915 .writable = 1,
1916 .configurable = 1,
1917 },
1918 };
1919
1920
1921 const njs_object_init_t njs_promise_prototype_init = {
1922 njs_promise_prototype_properties,
1923 njs_nitems(njs_promise_prototype_properties),
1924 };
1925
1926 const njs_object_type_init_t njs_promise_type_init = {
1927 .constructor = njs_native_ctor(njs_promise_constructor, 1, 0),
1928 .prototype_props = &njs_promise_prototype_init,
1929 .constructor_props = &njs_promise_constructor_init,
1930 .prototype_value = { .object = { .type = NJS_OBJECT } },
1931 };
1932