1 /* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "ecma-boolean-object.h"
17 #include "ecma-builtins.h"
18 #include "ecma-exceptions.h"
19 #include "ecma-function-object.h"
20 #include "ecma-gc.h"
21 #include "ecma-globals.h"
22 #include "ecma-helpers.h"
23 #include "ecma-jobqueue.h"
24 #include "ecma-objects.h"
25 #include "ecma-objects-general.h"
26 #include "ecma-promise-object.h"
27 #include "jcontext.h"
28
29 #if ENABLED (JERRY_ES2015_BUILTIN_PROMISE)
30
31 /** \addtogroup ecma ECMA
32 * @{
33 *
34 * \addtogroup ecmapromiseobject ECMA Promise object related routines
35 * @{
36 */
37
38 /**
39 * Check if an object is promise.
40 *
41 * @return true - if the object is a promise.
42 * false - otherwise.
43 */
44 inline bool JERRY_ATTR_ALWAYS_INLINE
ecma_is_promise(ecma_object_t * obj_p)45 ecma_is_promise (ecma_object_t *obj_p) /**< points to object */
46 {
47 return ecma_object_class_is (obj_p, LIT_MAGIC_STRING_PROMISE_UL);
48 } /* ecma_is_promise */
49
50 /**
51 * Get the result of the promise.
52 *
53 * @return ecma value of the promise result.
54 * Returned value must be freed with ecma_free_value
55 */
56 ecma_value_t
ecma_promise_get_result(ecma_object_t * obj_p)57 ecma_promise_get_result (ecma_object_t *obj_p) /**< points to promise object */
58 {
59 JERRY_ASSERT (ecma_is_promise (obj_p));
60
61 ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p;
62
63 return ecma_copy_value (ext_object_p->u.class_prop.u.value);
64 } /* ecma_promise_get_result */
65
66 /**
67 * Set the PromiseResult of promise.
68 */
69 static inline void JERRY_ATTR_ALWAYS_INLINE
ecma_promise_set_result(ecma_object_t * obj_p,ecma_value_t result)70 ecma_promise_set_result (ecma_object_t *obj_p, /**< points to promise object */
71 ecma_value_t result) /**< the result value */
72 {
73 JERRY_ASSERT (ecma_is_promise (obj_p));
74
75 ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) obj_p;
76
77 JERRY_ASSERT (ext_object_p->u.class_prop.u.value == ECMA_VALUE_UNDEFINED);
78
79 ext_object_p->u.class_prop.u.value = result;
80 } /* ecma_promise_set_result */
81
82 /**
83 * Get the PromiseState of promise.
84 *
85 * @return the state's enum value
86 */
87 uint16_t
ecma_promise_get_flags(ecma_object_t * obj_p)88 ecma_promise_get_flags (ecma_object_t *obj_p) /**< points to promise object */
89 {
90 JERRY_ASSERT (ecma_is_promise (obj_p));
91
92 return ((ecma_extended_object_t *) obj_p)->u.class_prop.extra_info;
93 } /* ecma_promise_get_flags */
94
95 /**
96 * Set the PromiseState of promise.
97 */
98 static inline void JERRY_ATTR_ALWAYS_INLINE
ecma_promise_set_state(ecma_object_t * obj_p,bool is_fulfilled)99 ecma_promise_set_state (ecma_object_t *obj_p, /**< points to promise object */
100 bool is_fulfilled) /**< new flags */
101 {
102 JERRY_ASSERT (ecma_is_promise (obj_p));
103 JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
104
105 uint16_t flags_to_invert = (is_fulfilled ? (ECMA_PROMISE_IS_PENDING | ECMA_PROMISE_IS_FULFILLED)
106 : ECMA_PROMISE_IS_PENDING);
107
108 ((ecma_extended_object_t *) obj_p)->u.class_prop.extra_info ^= flags_to_invert;
109 } /* ecma_promise_set_state */
110
111 /**
112 * Take a collection of Reactions and enqueue a new PromiseReactionJob for each Reaction.
113 *
114 * See also: ES2015 25.4.1.8
115 */
116 static void
ecma_promise_trigger_reactions(ecma_collection_t * reactions,ecma_value_t value,bool is_reject)117 ecma_promise_trigger_reactions (ecma_collection_t *reactions, /**< lists of reactions */
118 ecma_value_t value, /**< value for resolve or reject */
119 bool is_reject) /**< true if promise is rejected, false otherwise */
120 {
121 ecma_value_t *buffer_p = reactions->buffer_p;
122 ecma_value_t *buffer_end_p = buffer_p + reactions->item_count;
123
124 while (buffer_p < buffer_end_p)
125 {
126 ecma_value_t capability_with_tag = *buffer_p++;
127 ecma_object_t *capability_obj_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, capability_with_tag);
128 ecma_value_t capability = ecma_make_object_value (capability_obj_p);
129
130 if (!is_reject)
131 {
132 ecma_value_t handler = ECMA_VALUE_TRUE;
133
134 if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag))
135 {
136 handler = *buffer_p++;
137 }
138
139 ecma_enqueue_promise_reaction_job (capability, handler, value);
140 }
141 else if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (capability_with_tag))
142 {
143 buffer_p++;
144 }
145
146 if (is_reject)
147 {
148 ecma_value_t handler = ECMA_VALUE_FALSE;
149
150 if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag))
151 {
152 handler = *buffer_p++;
153 }
154
155 ecma_enqueue_promise_reaction_job (capability, handler, value);
156 }
157 else if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (capability_with_tag))
158 {
159 buffer_p++;
160 }
161 }
162 } /* ecma_promise_trigger_reactions */
163
164 /**
165 * Checks whether a resolver is called before.
166 *
167 * @return true if it was called before, false otherwise
168 */
169 static bool
ecma_is_resolver_already_called(ecma_object_t * resolver_p,ecma_object_t * promise_obj_p)170 ecma_is_resolver_already_called (ecma_object_t *resolver_p, /**< resolver */
171 ecma_object_t *promise_obj_p) /**< promise */
172 {
173 ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
174 ecma_property_t *property_p = ecma_find_named_property (resolver_p, str_already_resolved_p);
175
176 if (property_p == NULL)
177 {
178 return (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED) != 0;
179 }
180
181 JERRY_ASSERT (ECMA_PROPERTY_GET_TYPE (*property_p) == ECMA_PROPERTY_TYPE_NAMEDDATA);
182
183 ecma_value_t already_resolved = ECMA_PROPERTY_VALUE_PTR (property_p)->value;
184 ecma_object_t *object_p = ecma_get_object_from_value (already_resolved);
185 JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS);
186
187 ecma_extended_object_t *already_resolved_p = (ecma_extended_object_t *) object_p;
188 JERRY_ASSERT (already_resolved_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL);
189
190 ecma_value_t current_value = already_resolved_p->u.class_prop.u.value;
191 already_resolved_p->u.class_prop.u.value = ECMA_VALUE_TRUE;
192
193 return current_value == ECMA_VALUE_TRUE;
194 } /* ecma_is_resolver_already_called */
195
196 /**
197 * Reject a Promise with a reason.
198 *
199 * See also: ES2015 25.4.1.7
200 */
201 static void
ecma_reject_promise(ecma_value_t promise,ecma_value_t reason)202 ecma_reject_promise (ecma_value_t promise, /**< promise */
203 ecma_value_t reason) /**< reason for reject */
204 {
205 ecma_object_t *obj_p = ecma_get_object_from_value (promise);
206
207 JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
208
209 ecma_promise_set_state (obj_p, false);
210 ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (reason));
211 ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p;
212
213 /* GC can be triggered by ecma_new_collection so freeing the collection
214 first and creating a new one might cause a heap after use event. */
215 ecma_collection_t *reactions = promise_p->reactions;
216
217 /* Fulfill reactions will never be triggered. */
218 ecma_promise_trigger_reactions (reactions, reason, true);
219
220 promise_p->reactions = ecma_new_collection ();
221
222 ecma_collection_destroy (reactions);
223 } /* ecma_reject_promise */
224
225 /**
226 * Fulfill a Promise with a value.
227 *
228 * See also: ES2015 25.4.1.4
229 */
230 static void
ecma_fulfill_promise(ecma_value_t promise,ecma_value_t value)231 ecma_fulfill_promise (ecma_value_t promise, /**< promise */
232 ecma_value_t value) /**< fulfilled value */
233 {
234 ecma_object_t *obj_p = ecma_get_object_from_value (promise);
235
236 JERRY_ASSERT (ecma_promise_get_flags (obj_p) & ECMA_PROMISE_IS_PENDING);
237
238 ecma_promise_set_state (obj_p, true);
239 ecma_promise_set_result (obj_p, ecma_copy_value_if_not_object (value));
240 ecma_promise_object_t *promise_p = (ecma_promise_object_t *) obj_p;
241
242 /* GC can be triggered by ecma_new_collection so freeing the collection
243 first and creating a new one might cause a heap after use event. */
244 ecma_collection_t *reactions = promise_p->reactions;
245
246 /* Reject reactions will never be triggered. */
247 ecma_promise_trigger_reactions (reactions, value, false);
248
249 promise_p->reactions = ecma_new_collection ();
250
251 ecma_collection_destroy (reactions);
252 } /* ecma_fulfill_promise */
253
254 /**
255 * Native handler for Promise Reject Function.
256 *
257 * See also: ES2015 25.4.1.3.1
258 *
259 * @return ecma value of undefined.
260 */
261 static ecma_value_t
ecma_promise_reject_handler(const ecma_value_t function,const ecma_value_t this,const ecma_value_t argv[],const ecma_length_t argc)262 ecma_promise_reject_handler (const ecma_value_t function, /**< the function itself */
263 const ecma_value_t this, /**< this_arg of the function */
264 const ecma_value_t argv[], /**< argument list */
265 const ecma_length_t argc) /**< argument number */
266 {
267 JERRY_UNUSED (this);
268
269 ecma_object_t *function_p = ecma_get_object_from_value (function);
270 /* 2. */
271 ecma_value_t promise = ecma_op_object_get_by_magic_id (function_p, LIT_INTERNAL_MAGIC_STRING_PROMISE);
272 /* 1. */
273 ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
274 JERRY_ASSERT (ecma_is_promise (promise_obj_p));
275
276 /* 3., 4. */
277 if (!ecma_is_resolver_already_called (function_p, promise_obj_p))
278 {
279 /* 5. */
280 ((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
281
282 /* 6. */
283 ecma_value_t reject_value = (argc == 0) ? ECMA_VALUE_UNDEFINED : argv[0];
284 ecma_reject_promise (promise, reject_value);
285 }
286
287 ecma_free_value (promise);
288 return ECMA_VALUE_UNDEFINED;
289 } /* ecma_promise_reject_handler */
290
291 /**
292 * Native handler for Promise Resolve Function.
293 *
294 * See also: ES2015 25.4.1.3.2
295 *
296 * @return ecma value of undefined.
297 */
298 static ecma_value_t
ecma_promise_resolve_handler(const ecma_value_t function,const ecma_value_t this,const ecma_value_t argv[],const ecma_length_t argc)299 ecma_promise_resolve_handler (const ecma_value_t function, /**< the function itself */
300 const ecma_value_t this, /**< this_arg of the function */
301 const ecma_value_t argv[], /**< argument list */
302 const ecma_length_t argc) /**< argument number */
303 {
304 JERRY_UNUSED (this);
305
306 ecma_object_t *function_p = ecma_get_object_from_value (function);
307 /* 2. */
308 ecma_value_t promise = ecma_op_object_get_by_magic_id (function_p, LIT_INTERNAL_MAGIC_STRING_PROMISE);
309 /* 1. */
310 ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
311 JERRY_ASSERT (ecma_is_promise (promise_obj_p));
312
313 /* 3., 4. */
314 if (ecma_is_resolver_already_called (function_p, promise_obj_p))
315 {
316 goto end_of_resolve_function;
317 }
318
319 /* 5. */
320 ((ecma_extended_object_t *) promise_obj_p)->u.class_prop.extra_info |= ECMA_PROMISE_ALREADY_RESOLVED;
321
322 /* If the argc is 0, then fulfill the `undefined`. */
323 if (argc == 0)
324 {
325 ecma_fulfill_promise (promise, ECMA_VALUE_UNDEFINED);
326 goto end_of_resolve_function;
327 }
328
329 /* 6. */
330 if (argv[0] == promise)
331 {
332 ecma_object_t *error_p = ecma_new_standard_error (ECMA_ERROR_TYPE);
333 ecma_reject_promise (promise, ecma_make_object_value (error_p));
334 ecma_deref_object (error_p);
335 goto end_of_resolve_function;
336 }
337
338 /* 7. */
339 if (!ecma_is_value_object (argv[0]))
340 {
341 ecma_fulfill_promise (promise, argv[0]);
342 goto end_of_resolve_function;
343 }
344
345 /* 8. */
346 ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (argv[0]),
347 LIT_MAGIC_STRING_THEN);
348
349 if (ECMA_IS_VALUE_ERROR (then))
350 {
351 /* 9. */
352 then = jcontext_take_exception ();
353 ecma_reject_promise (promise, then);
354 }
355 else if (!ecma_op_is_callable (then))
356 {
357 /* 11 .*/
358 ecma_fulfill_promise (promise, argv[0]);
359 }
360 else
361 {
362 /* 12 */
363 ecma_enqueue_promise_resolve_thenable_job (promise, argv[0], then);
364 }
365
366 ecma_free_value (then);
367
368 end_of_resolve_function:
369 ecma_free_value (promise);
370 return ECMA_VALUE_UNDEFINED;
371 } /* ecma_promise_resolve_handler */
372
373 /**
374 * CapabilitiesExecutor Function.
375 *
376 * See also: ES2015 25.4.1.5.1
377 *
378 * @return ecma value of undefined or typerror.
379 * Returned value must be freed with ecma_free_value
380 */
381 static ecma_value_t
ecma_call_builtin_executor(ecma_object_t * executor_p,ecma_value_t resolve_func,ecma_value_t reject_func)382 ecma_call_builtin_executor (ecma_object_t *executor_p, /**< the executor object */
383 ecma_value_t resolve_func, /**< the resolve function */
384 ecma_value_t reject_func) /**< the reject function */
385 {
386 ecma_string_t *capability_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY);
387 ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE);
388 ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT);
389
390 /* 2. */
391 ecma_value_t capability = ecma_op_object_get (executor_p, capability_str_p);
392 /* 3. */
393 ecma_value_t resolve = ecma_op_object_get (ecma_get_object_from_value (capability), resolve_str_p);
394
395 if (resolve != ECMA_VALUE_UNDEFINED)
396 {
397 ecma_free_value (resolve);
398 ecma_free_value (capability);
399
400 return ecma_raise_type_error (ECMA_ERR_MSG ("'resolve' function should be undefined."));
401 }
402
403 /* 4. */
404 ecma_value_t reject = ecma_op_object_get (ecma_get_object_from_value (capability), reject_str_p);
405
406 if (reject != ECMA_VALUE_UNDEFINED)
407 {
408 ecma_free_value (reject);
409 ecma_free_value (capability);
410
411 return ecma_raise_type_error (ECMA_ERR_MSG ("'reject' function should be undefined."));
412 }
413
414 /* 5. */
415 ecma_op_object_put (ecma_get_object_from_value (capability),
416 resolve_str_p,
417 resolve_func,
418 false);
419 /* 6. */
420 ecma_op_object_put (ecma_get_object_from_value (capability),
421 reject_str_p,
422 reject_func,
423 false);
424
425 ecma_free_value (capability);
426
427 return ECMA_VALUE_UNDEFINED;
428 } /* ecma_call_builtin_executor */
429
430 /**
431 * Helper function for PromiseCreateResovingFucntions.
432 *
433 * See also: ES2015 25.4.1.3 2. - 7.
434 *
435 * @return pointer to the resolving function
436 */
437 static ecma_value_t
ecma_promise_create_resolving_functions_helper(ecma_object_t * obj_p,ecma_external_handler_t handler_cb)438 ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promise Object */
439 ecma_external_handler_t handler_cb) /**< Callback handler */
440 {
441 ecma_string_t *str_promise_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE);
442 ecma_object_t *func_obj_p = ecma_op_create_external_function_object (handler_cb);
443
444 ecma_op_object_put (func_obj_p,
445 str_promise_p,
446 ecma_make_object_value (obj_p),
447 false);
448
449 return ecma_make_object_value (func_obj_p);
450 } /* ecma_promise_create_resolving_functions_helper */
451
452 /**
453 * Create a PromiseCreateResovingFucntions.
454 *
455 * See also: ES2015 25.4.1.3
456 *
457 * @return pointer to the resolving functions
458 */
459 void
ecma_promise_create_resolving_functions(ecma_object_t * object_p,ecma_promise_resolving_functions_t * funcs,bool create_already_resolved)460 ecma_promise_create_resolving_functions (ecma_object_t *object_p, /**< the promise object */
461 ecma_promise_resolving_functions_t *funcs, /**< [out] resolving functions */
462 bool create_already_resolved) /**< create already resolved flag */
463 {
464 /* 2. - 4. */
465 funcs->resolve = ecma_promise_create_resolving_functions_helper (object_p,
466 ecma_promise_resolve_handler);
467
468 /* 5. - 7. */
469 funcs->reject = ecma_promise_create_resolving_functions_helper (object_p,
470 ecma_promise_reject_handler);
471 if (!create_already_resolved)
472 {
473 return;
474 }
475
476 ecma_value_t already_resolved = ecma_op_create_boolean_object (ECMA_VALUE_FALSE);
477 ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED);
478 ecma_property_value_t *value_p;
479
480 value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->resolve),
481 str_already_resolved_p,
482 ECMA_PROPERTY_FIXED,
483 NULL);
484 value_p->value = already_resolved;
485
486 value_p = ecma_create_named_data_property (ecma_get_object_from_value (funcs->reject),
487 str_already_resolved_p,
488 ECMA_PROPERTY_FIXED,
489 NULL);
490 value_p->value = already_resolved;
491
492 ecma_free_value (already_resolved);
493 } /* ecma_promise_create_resolving_functions */
494
495 /**
496 * Free the heap and the member of the resolving functions.
497 */
498 void
ecma_promise_free_resolving_functions(ecma_promise_resolving_functions_t * funcs)499 ecma_promise_free_resolving_functions (ecma_promise_resolving_functions_t *funcs) /**< points to the functions */
500 {
501 ecma_free_value (funcs->resolve);
502 ecma_free_value (funcs->reject);
503 } /* ecma_promise_free_resolving_functions */
504
505 /**
506 * Create a promise object.
507 *
508 * See also: ES2015 25.4.3.1
509 *
510 * @return ecma value of the new promise object
511 * Returned value must be freed with ecma_free_value
512 */
513 ecma_value_t
ecma_op_create_promise_object(ecma_value_t executor,ecma_promise_executor_type_t type)514 ecma_op_create_promise_object (ecma_value_t executor, /**< the executor function or object */
515 ecma_promise_executor_type_t type) /**< indicates the type of executor */
516 {
517 JERRY_ASSERT (JERRY_CONTEXT (current_new_target) != NULL);
518 /* 3. */
519 ecma_object_t *proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_new_target),
520 ECMA_BUILTIN_ID_PROMISE_PROTOTYPE);
521
522 if (JERRY_UNLIKELY (proto_p == NULL))
523 {
524 return ECMA_VALUE_ERROR;
525 }
526
527 /* Calling ecma_new_collection might trigger a GC call, so this
528 * allocation is performed before the object is constructed. */
529 ecma_collection_t *reactions = ecma_new_collection ();
530
531 ecma_object_t *object_p = ecma_create_object (proto_p,
532 sizeof (ecma_promise_object_t),
533 ECMA_OBJECT_TYPE_CLASS);
534 ecma_deref_object (proto_p);
535 ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
536 ext_object_p->u.class_prop.class_id = LIT_MAGIC_STRING_PROMISE_UL;
537 /* 5 */
538 ext_object_p->u.class_prop.extra_info = ECMA_PROMISE_IS_PENDING;
539 ext_object_p->u.class_prop.u.value = ECMA_VALUE_UNDEFINED;
540 ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) object_p;
541
542 /* 6-7. */
543 promise_object_p->reactions = reactions;
544 /* 8. */
545 ecma_promise_resolving_functions_t funcs;
546 ecma_promise_create_resolving_functions (object_p, &funcs, false);
547
548 ecma_string_t *str_resolve_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_RESOLVE_FUNCTION);
549 ecma_string_t *str_reject_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_REJECT_FUNCTION);
550
551 ecma_op_object_put (object_p,
552 str_resolve_p,
553 funcs.resolve,
554 false);
555 ecma_op_object_put (object_p,
556 str_reject_p,
557 funcs.reject,
558 false);
559
560 /* 9. */
561 ecma_value_t completion = ECMA_VALUE_UNDEFINED;
562
563 if (type == ECMA_PROMISE_EXECUTOR_FUNCTION)
564 {
565 JERRY_ASSERT (ecma_op_is_callable (executor));
566
567 ecma_value_t argv[] = { funcs.resolve, funcs.reject };
568 completion = ecma_op_function_call (ecma_get_object_from_value (executor),
569 ECMA_VALUE_UNDEFINED,
570 argv,
571 2);
572 }
573 else if (type == ECMA_PROMISE_EXECUTOR_OBJECT)
574 {
575 JERRY_ASSERT (ecma_is_value_object (executor));
576
577 completion = ecma_call_builtin_executor (ecma_get_object_from_value (executor),
578 funcs.resolve,
579 funcs.reject);
580 }
581 else
582 {
583 JERRY_ASSERT (type == ECMA_PROMISE_EXECUTOR_EMPTY);
584 JERRY_UNUSED (executor);
585 }
586
587 ecma_value_t status = ECMA_VALUE_EMPTY;
588
589 if (ECMA_IS_VALUE_ERROR (completion))
590 {
591 /* 10.a. */
592 completion = jcontext_take_exception ();
593 status = ecma_op_function_call (ecma_get_object_from_value (funcs.reject),
594 ECMA_VALUE_UNDEFINED,
595 &completion,
596 1);
597 }
598
599 ecma_promise_free_resolving_functions (&funcs);
600 ecma_free_value (completion);
601
602 /* 10.b. */
603 if (ECMA_IS_VALUE_ERROR (status))
604 {
605 ecma_deref_object (object_p);
606 return status;
607 }
608
609 /* 11. */
610 ecma_free_value (status);
611
612 return ecma_make_object_value (object_p);
613 } /* ecma_op_create_promise_object */
614
615 /**
616 * 25.4.1.5.1 GetCapabilitiesExecutor Functions
617 *
618 * Checks and sets a promiseCapability's resolve and reject properties.
619 *
620 * @return ECMA_VALUE_UNDEFINED or TypeError
621 * returned value must be freed with ecma_free_value
622 */
623 static ecma_value_t
ecma_op_get_capabilities_executor_cb(const ecma_value_t function_obj,const ecma_value_t this_val,const ecma_value_t args_p[],const ecma_length_t args_count)624 ecma_op_get_capabilities_executor_cb (const ecma_value_t function_obj, /**< the function itself */
625 const ecma_value_t this_val, /**< this_arg of the function */
626 const ecma_value_t args_p[], /**< argument list */
627 const ecma_length_t args_count) /**< argument number */
628 {
629 JERRY_UNUSED (this_val);
630 /* 1. */
631 ecma_value_t capability = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (function_obj),
632 LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY);
633 JERRY_ASSERT (ecma_is_value_object (capability));
634
635 /* 2. */
636 ecma_object_t *capability_obj_p = ecma_get_object_from_value (capability);
637
638 /* 3. */
639 ecma_value_t resolve = ecma_op_object_get_by_magic_id (capability_obj_p,
640 LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE);
641
642 if (!ecma_is_value_undefined (resolve))
643 {
644 ecma_free_value (resolve);
645 ecma_deref_object (capability_obj_p);
646
647 return ecma_raise_type_error (ECMA_ERR_MSG ("Resolve must be undefined"));
648 }
649
650 /* 4. */
651 ecma_value_t reject = ecma_op_object_get_by_magic_id (capability_obj_p,
652 LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT);
653
654 if (!ecma_is_value_undefined (reject))
655 {
656 ecma_free_value (reject);
657 ecma_deref_object (capability_obj_p);
658
659 return ecma_raise_type_error (ECMA_ERR_MSG ("Reject must be undefined"));
660 }
661
662 /* 5. */
663 ecma_op_object_put (capability_obj_p,
664 ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE),
665 args_count > 0 ? args_p[0] : ECMA_VALUE_UNDEFINED,
666 false);
667
668 /* 6. */
669 ecma_op_object_put (capability_obj_p,
670 ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT),
671 args_count > 1 ? args_p[1] : ECMA_VALUE_UNDEFINED,
672 false);
673
674 ecma_deref_object (capability_obj_p);
675
676 /* 7. */
677 return ECMA_VALUE_UNDEFINED;
678 } /* ecma_op_get_capabilities_executor_cb */
679
680 /**
681 * Create a new PromiseCapability.
682 *
683 * See also: ES2015 25.4.1.5
684 *
685 * @return ecma value of the new PromiseCapability
686 * Returned value must be freed with ecma_free_value
687 */
688 ecma_value_t
ecma_promise_new_capability(ecma_value_t constructor)689 ecma_promise_new_capability (ecma_value_t constructor)
690 {
691 /* 1. */
692 if (!ecma_is_constructor (constructor))
693 {
694 return ecma_raise_type_error (ECMA_ERR_MSG ("Invalid capability"));
695 }
696
697 ecma_object_t *constructor_obj_p = ecma_get_object_from_value (constructor);
698 /* 3. */
699 ecma_object_t *capability_p = ecma_op_create_object_object_noarg ();
700
701 ecma_string_t *capability_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY);
702 ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE);
703 /* 4. */
704 ecma_object_t *executor_p = ecma_op_create_external_function_object (ecma_op_get_capabilities_executor_cb);
705 ecma_value_t executor = ecma_make_object_value (executor_p);
706 /* 5. */
707 ecma_op_object_put (executor_p,
708 capability_str_p,
709 ecma_make_object_value (capability_p),
710 false);
711
712 /* 6. */
713 ecma_value_t promise = ecma_op_function_construct (constructor_obj_p,
714 constructor_obj_p,
715 &executor,
716 1);
717 ecma_deref_object (executor_p);
718
719 /* 7. */
720 if (ECMA_IS_VALUE_ERROR (promise))
721 {
722 ecma_deref_object (capability_p);
723 return promise;
724 }
725
726 /* 10. */
727 ecma_op_object_put (capability_p,
728 promise_str_p,
729 promise,
730 false);
731
732 ecma_free_value (promise);
733
734 /* 8. */
735 ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE);
736 ecma_value_t resolve = ecma_op_object_get (capability_p, resolve_str_p);
737
738 if (!ecma_op_is_callable (resolve))
739 {
740 ecma_free_value (resolve);
741 ecma_deref_object (capability_p);
742 return ecma_raise_type_error (ECMA_ERR_MSG ("'resolve' parameter must be callable."));
743 }
744
745 ecma_free_value (resolve);
746 /* 9. */
747 ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT);
748 ecma_value_t reject = ecma_op_object_get (capability_p, reject_str_p);
749
750 if (!ecma_op_is_callable (reject))
751 {
752 ecma_free_value (reject);
753 ecma_deref_object (capability_p);
754 return ecma_raise_type_error (ECMA_ERR_MSG ("'reject' parameter must be callable."));
755 }
756
757 ecma_free_value (reject);
758 /* 11. */
759 return ecma_make_object_value (capability_p);
760 } /* ecma_promise_new_capability */
761
762 /**
763 * The common function for 'reject' and 'resolve'.
764 *
765 * @return ecma value
766 * Returned value must be freed with ecma_free_value.
767 */
768 ecma_value_t
ecma_promise_reject_or_resolve(ecma_value_t this_arg,ecma_value_t value,bool is_resolve)769 ecma_promise_reject_or_resolve (ecma_value_t this_arg, /**< "this" argument */
770 ecma_value_t value, /**< rejected or resolved value */
771 bool is_resolve) /**< the operation is resolve */
772 {
773 if (!ecma_is_value_object (this_arg))
774 {
775 return ecma_raise_type_error (ECMA_ERR_MSG ("'this' is not an object."));
776 }
777
778 if (is_resolve
779 && ecma_is_value_object (value)
780 && ecma_is_promise (ecma_get_object_from_value (value)))
781 {
782 ecma_object_t *object_p = ecma_get_object_from_value (value);
783 ecma_value_t constructor = ecma_op_object_get_by_magic_id (object_p, LIT_MAGIC_STRING_CONSTRUCTOR);
784
785 if (ECMA_IS_VALUE_ERROR (constructor))
786 {
787 return constructor;
788 }
789
790 /* The this_arg must be an object. */
791 bool is_same_value = (constructor == this_arg);
792 ecma_free_value (constructor);
793
794 if (is_same_value)
795 {
796 return ecma_copy_value (value);
797 }
798 }
799
800 ecma_value_t capability = ecma_promise_new_capability (this_arg);
801
802 if (ECMA_IS_VALUE_ERROR (capability))
803 {
804 return capability;
805 }
806
807 ecma_string_t *property_str_p;
808
809 if (is_resolve)
810 {
811 property_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE);
812 }
813 else
814 {
815 property_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT);
816 }
817
818 ecma_value_t func = ecma_op_object_get (ecma_get_object_from_value (capability), property_str_p);
819
820 ecma_value_t call_ret = ecma_op_function_call (ecma_get_object_from_value (func),
821 ECMA_VALUE_UNDEFINED,
822 &value,
823 1);
824
825 ecma_free_value (func);
826
827 if (ECMA_IS_VALUE_ERROR (call_ret))
828 {
829 return call_ret;
830 }
831
832 ecma_free_value (call_ret);
833
834 ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE);
835 ecma_value_t promise = ecma_op_object_get (ecma_get_object_from_value (capability), promise_str_p);
836 ecma_free_value (capability);
837
838 return promise;
839 } /* ecma_promise_reject_or_resolve */
840
841 /**
842 * It performs the "then" operation on promiFulfilled
843 * and onRejected as its settlement actions.
844 *
845 * See also: 25.4.5.3.1
846 *
847 * @return ecma value of the new promise object
848 * Returned value must be freed with ecma_free_value
849 */
850 static ecma_value_t
ecma_promise_do_then(ecma_value_t promise,ecma_value_t on_fulfilled,ecma_value_t on_rejected,ecma_value_t result_capability)851 ecma_promise_do_then (ecma_value_t promise, /**< the promise which call 'then' */
852 ecma_value_t on_fulfilled, /**< on_fulfilled function */
853 ecma_value_t on_rejected, /**< on_rejected function */
854 ecma_value_t result_capability) /**< promise capability */
855 {
856 ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE);
857
858 /* 3. boolean true indicates "indentity" */
859 if (!ecma_op_is_callable (on_fulfilled))
860 {
861 on_fulfilled = ECMA_VALUE_TRUE;
862 }
863
864 /* 4. boolean false indicates "thrower" */
865 if (!ecma_op_is_callable (on_rejected))
866 {
867 on_rejected = ECMA_VALUE_FALSE;
868 }
869
870 ecma_object_t *promise_obj_p = ecma_get_object_from_value (promise);
871 ecma_promise_object_t *promise_p = (ecma_promise_object_t *) promise_obj_p;
872
873 uint16_t flags = ecma_promise_get_flags (promise_obj_p);
874
875 if (flags & ECMA_PROMISE_IS_PENDING)
876 {
877 /* 7. */
878 ecma_value_t capability_with_tag;
879 ECMA_SET_NON_NULL_POINTER_TAG (capability_with_tag, ecma_get_object_from_value (result_capability), 0);
880
881 if (on_fulfilled != ECMA_VALUE_TRUE)
882 {
883 ECMA_SET_FIRST_BIT_TO_POINTER_TAG (capability_with_tag);
884 }
885
886 if (on_rejected != ECMA_VALUE_FALSE)
887 {
888 ECMA_SET_SECOND_BIT_TO_POINTER_TAG (capability_with_tag);
889 }
890
891 ecma_collection_push_back (promise_p->reactions, capability_with_tag);
892
893 if (on_fulfilled != ECMA_VALUE_TRUE)
894 {
895 ecma_collection_push_back (promise_p->reactions, on_fulfilled);
896 }
897
898 if (on_rejected != ECMA_VALUE_FALSE)
899 {
900 ecma_collection_push_back (promise_p->reactions, on_rejected);
901 }
902 }
903 else if (flags & ECMA_PROMISE_IS_FULFILLED)
904 {
905 /* 8. */
906 ecma_value_t value = ecma_promise_get_result (promise_obj_p);
907 ecma_enqueue_promise_reaction_job (result_capability, on_fulfilled, value);
908 ecma_free_value (value);
909 }
910 else
911 {
912 /* 9. */
913 ecma_value_t reason = ecma_promise_get_result (promise_obj_p);
914 ecma_enqueue_promise_reaction_job (result_capability, on_rejected, reason);
915 ecma_free_value (reason);
916 }
917
918 /* 10. */
919 return ecma_op_object_get (ecma_get_object_from_value (result_capability), promise_str_p);
920 } /* ecma_promise_do_then */
921
922 /**
923 * The common function for ecma_builtin_promise_prototype_then
924 * and ecma_builtin_promise_prototype_catch.
925 *
926 * @return ecma value of a new promise object.
927 * Returned value must be freed with ecma_free_value.
928 */
929 ecma_value_t
ecma_promise_then(ecma_value_t promise,ecma_value_t on_fulfilled,ecma_value_t on_rejected)930 ecma_promise_then (ecma_value_t promise, /**< the promise which call 'then' */
931 ecma_value_t on_fulfilled, /**< on_fulfilled function */
932 ecma_value_t on_rejected) /**< on_rejected function */
933 {
934 if (!ecma_is_value_object (promise))
935 {
936 return ecma_raise_type_error (ECMA_ERR_MSG ("'this' is not an object."));
937 }
938
939 ecma_object_t *obj = ecma_get_object_from_value (promise);
940
941 if (!ecma_is_promise (obj))
942 {
943 return ecma_raise_type_error (ECMA_ERR_MSG ("'this' is not a Promise."));
944 }
945
946 ecma_value_t species = ecma_op_species_constructor (obj, ECMA_BUILTIN_ID_PROMISE);
947 if (ECMA_IS_VALUE_ERROR (species))
948 {
949 return species;
950 }
951
952 ecma_value_t result_capability = ecma_promise_new_capability (species);
953 ecma_free_value (species);
954
955 if (ECMA_IS_VALUE_ERROR (result_capability))
956 {
957 return result_capability;
958 }
959
960 ecma_value_t ret = ecma_promise_do_then (promise, on_fulfilled, on_rejected, result_capability);
961 ecma_free_value (result_capability);
962
963 return ret;
964 } /* ecma_promise_then */
965
966 /**
967 * @}
968 * @}
969 */
970 #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */
971