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 */
44inline bool JERRY_ATTR_ALWAYS_INLINE
45ecma_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 */
56ecma_value_t
57ecma_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 */
69static inline void JERRY_ATTR_ALWAYS_INLINE
70ecma_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 */
87uint16_t
88ecma_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 */
98static inline void JERRY_ATTR_ALWAYS_INLINE
99ecma_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 */
116static void
117ecma_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 */
169static bool
170ecma_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 */
201static void
202ecma_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 */
230static void
231ecma_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 */
261static ecma_value_t
262ecma_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 */
298static ecma_value_t
299ecma_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
368end_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 */
381static ecma_value_t
382ecma_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 */
437static ecma_value_t
438ecma_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 */
459void
460ecma_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 */
498void
499ecma_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 */
513ecma_value_t
514ecma_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 */
623static ecma_value_t
624ecma_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 */
688ecma_value_t
689ecma_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 */
768ecma_value_t
769ecma_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 */
850static ecma_value_t
851ecma_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 */
929ecma_value_t
930ecma_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