From 1725e014b8b8ccbf3e64c3bce2ac8aed892b9753 Mon Sep 17 00:00:00 2001 From: Daniel Balla Date: Fri, 10 Jan 2020 15:44:52 +0100 Subject: [PATCH] Support iterable objects in Promise.all and Promise.race functions (#3496) JerryScript-DCO-1.0-Signed-off-by: Daniel Balla dballa@inf.u-szeged.hu --- .../builtin-objects/ecma-builtin-promise.c | 331 +++++++++--------- jerry-core/ecma/operations/ecma-objects.c | 59 ++++ jerry-core/ecma/operations/ecma-objects.h | 4 + .../ecma/operations/ecma-promise-object.c | 71 ++-- tests/jerry/es2015/promise-all-iterator.js | 76 ++++ tests/jerry/es2015/promise-race-iterator.js | 64 ++++ .../regression-test-issue-2544.js | 6 +- 7 files changed, 416 insertions(+), 195 deletions(-) create mode 100644 tests/jerry/es2015/promise-all-iterator.js create mode 100644 tests/jerry/es2015/promise-race-iterator.js rename tests/jerry/{fail => es2015}/regression-test-issue-2544.js (75%) diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-promise.c b/jerry-core/ecma/builtin-objects/ecma-builtin-promise.c index acf29fba4..875d94794 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-promise.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-promise.c @@ -19,6 +19,7 @@ #include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" +#include "ecma-iterator-object.h" #include "ecma-number-object.h" #include "ecma-promise-object.h" #include "jcontext.h" @@ -52,12 +53,17 @@ * Returned value must be freed with ecma_free_value. */ inline static ecma_value_t -ecma_builtin_promise_reject_abrupt (ecma_value_t capability) /**< reject description */ +ecma_builtin_promise_reject_abrupt (ecma_value_t value, /**< value */ + ecma_value_t capability) /**< capability */ { - ecma_raise_type_error (ECMA_ERR_MSG ("Second argument is not an array.")); + if (!ECMA_IS_VALUE_ERROR (value)) + { + return value; + } + ecma_value_t reason = jcontext_take_exception (); - ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT); - ecma_value_t reject = ecma_op_object_get (ecma_get_object_from_value (capability), reject_str_p); + ecma_value_t reject = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (capability), + LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT); ecma_value_t call_ret = ecma_op_function_call (ecma_get_object_from_value (reject), ECMA_VALUE_UNDEFINED, @@ -73,10 +79,8 @@ ecma_builtin_promise_reject_abrupt (ecma_value_t capability) /**< reject descrip ecma_free_value (call_ret); - ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE); - ecma_value_t promise_new = ecma_op_object_get (ecma_get_object_from_value (capability), promise_str_p); - - return promise_new; + return ecma_op_object_get_by_magic_id (ecma_get_object_from_value (capability), + LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE); } /* ecma_builtin_promise_reject_abrupt */ /** @@ -121,82 +125,81 @@ ecma_builtin_promise_resolve (ecma_value_t this_arg, /**< 'this' argument */ * Returned value must be freed with ecma_free_value. */ inline static ecma_value_t -ecma_builtin_promise_do_race (ecma_value_t array, /**< the array for race */ - ecma_value_t capability, /**< PromiseCapability record */ - ecma_value_t ctor) /**< the caller of Promise.race */ +ecma_builtin_promise_perform_race (ecma_value_t iterator, /**< the iterator for race */ + ecma_value_t capability, /**< PromiseCapability record */ + ecma_value_t ctor, /**< Constructor value */ + bool *done_p) /**< [out] iteratorRecord[[done]] */ { - JERRY_ASSERT (ecma_is_value_object (capability) - && ecma_is_value_object (array) - && ecma_is_value_object (ctor)); - JERRY_ASSERT (ecma_get_object_builtin_id (ecma_get_object_from_value (ctor)) == ECMA_BUILTIN_ID_PROMISE); - JERRY_ASSERT (ecma_get_object_type (ecma_get_object_from_value (array)) == ECMA_OBJECT_TYPE_ARRAY); + JERRY_ASSERT (ecma_is_value_object (iterator) + && ecma_is_value_object (capability)); - ecma_value_t ret = ECMA_VALUE_EMPTY; - ecma_object_t *array_p = ecma_get_object_from_value (array); - ecma_extended_object_t *ext_array_p = (ecma_extended_object_t *) array_p; - ecma_length_t len = ext_array_p->u.array.length; - - ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE); - ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE); - ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT); - - ecma_value_t resolve = ecma_op_object_get (ecma_get_object_from_value (capability), - resolve_str_p); - ecma_value_t reject = ecma_op_object_get (ecma_get_object_from_value (capability), - reject_str_p); - - for (ecma_length_t index = 0; index <= len; index++) + ecma_object_t *capability_obj_p = ecma_get_object_from_value (capability); + /* 1. */ + while (true) { - /* b-d. */ - if (index == len) + /* a. */ + ecma_value_t next = ecma_op_iterator_step (iterator); + /* b, c. */ + if (ECMA_IS_VALUE_ERROR (next)) { - ret = ecma_op_object_get (ecma_get_object_from_value (capability), promise_str_p); - break; + *done_p = true; + return next; + } + + /* d. */ + if (ecma_is_value_false (next)) + { + /* i. */ + *done_p = true; + /* ii. */ + return ecma_op_object_get_by_magic_id (capability_obj_p, LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE); } /* e. */ - ecma_value_t array_item = ecma_op_object_get_by_uint32_index (array_p, index); + ecma_value_t next_val = ecma_op_iterator_value (next); + ecma_free_value (next); - /* f. */ - if (ECMA_IS_VALUE_ERROR (array_item)) + /* f, g. */ + if (ECMA_IS_VALUE_ERROR (next_val)) { - ret = array_item; - break; + *done_p = true; + return next_val; } /* h. */ - ecma_value_t next_promise = ecma_builtin_promise_resolve (ctor, array_item); - ecma_free_value (array_item); + ecma_value_t next_promise = ecma_op_invoke_by_magic_id (ctor, LIT_MAGIC_STRING_RESOLVE, &next_val, 1); + ecma_free_value (next_val); /* i. */ if (ECMA_IS_VALUE_ERROR (next_promise)) { - ret = next_promise; - break; + return next_promise; } /* j. */ - ecma_value_t then_result = ecma_promise_then (next_promise, resolve, reject); + ecma_value_t args[2]; + args[0] = ecma_op_object_get_by_magic_id (capability_obj_p, LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE); + args[1] = ecma_op_object_get_by_magic_id (capability_obj_p, LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT); + ecma_value_t result = ecma_op_invoke_by_magic_id (next_promise, LIT_MAGIC_STRING_THEN, args, 2); ecma_free_value (next_promise); - /* k. */ - if (ECMA_IS_VALUE_ERROR (then_result)) + for (uint8_t i = 0; i < 2; i++) { - ret = then_result; - break; + ecma_free_value (args[i]); } - ecma_free_value (then_result); + /* k. */ + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + ecma_free_value (result); } - ecma_free_value (reject); - ecma_free_value (resolve); - - JERRY_ASSERT (!ecma_is_value_empty (ret)); - - return ret; -} /* ecma_builtin_promise_do_race */ + JERRY_UNREACHABLE (); +} /* ecma_builtin_promise_perform_race */ /** * Helper function for increase or decrease the remaining count. @@ -226,7 +229,6 @@ ecma_builtin_promise_remaining_inc_or_dec (ecma_value_t remaining, /**< the rema { current--; } - ext_object_p->u.class_prop.u.value = ecma_make_uint32_value (current); return current; @@ -249,7 +251,6 @@ ecma_builtin_promise_all_handler (const ecma_value_t function, /**< the function JERRY_UNUSED (this); JERRY_UNUSED (argc); - ecma_value_t ret = ECMA_VALUE_UNDEFINED; /* 1. */ ecma_object_t *function_p = ecma_get_object_from_value (function); ecma_string_t *already_called_str_p; @@ -261,8 +262,7 @@ ecma_builtin_promise_all_handler (const ecma_value_t function, /**< the function /* 2. */ if (ecma_is_value_true (already_called)) { - ecma_fast_free_value (already_called); - return ret; + return ECMA_VALUE_UNDEFINED; } /* 3. */ @@ -278,37 +278,36 @@ ecma_builtin_promise_all_handler (const ecma_value_t function, /**< the function /* 4-7. */ ecma_value_t index_val = ecma_op_object_get (function_p, str_index_p); - ecma_value_t value_array = ecma_op_object_get (function_p, str_value_p); + ecma_value_t values_array = ecma_op_object_get (function_p, str_value_p); ecma_value_t capability = ecma_op_object_get (function_p, str_capability_p); ecma_value_t remaining = ecma_op_object_get (function_p, str_remaining_p); JERRY_ASSERT (ecma_is_value_integer_number (index_val)); /* 8. */ - ecma_op_object_put_by_uint32_index (ecma_get_object_from_value (value_array), + ecma_op_object_put_by_uint32_index (ecma_get_object_from_value (values_array), (uint32_t) ecma_get_integer_from_value (index_val), argv[0], false); /* 9-10. */ + ecma_value_t ret = ECMA_VALUE_UNDEFINED; if (ecma_builtin_promise_remaining_inc_or_dec (remaining, false) == 0) { - ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE); - ecma_value_t resolve = ecma_op_object_get (ecma_get_object_from_value (capability), resolve_str_p); - + ecma_value_t resolve = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (capability), + LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE); ret = ecma_op_function_call (ecma_get_object_from_value (resolve), ECMA_VALUE_UNDEFINED, - &value_array, + &values_array, 1); ecma_free_value (resolve); } ecma_free_value (remaining); ecma_free_value (capability); - ecma_free_value (value_array); - ecma_fast_free_value (index_val); - ecma_fast_free_value (already_called); + ecma_free_value (values_array); + ecma_free_value (index_val); return ret; } /* ecma_builtin_promise_all_handler */ @@ -323,25 +322,25 @@ ecma_builtin_promise_all_handler (const ecma_value_t function, /**< the function * Returned value must be freed with ecma_free_value. */ inline static ecma_value_t -ecma_builtin_promise_do_all (ecma_value_t array, /**< the array for all */ - ecma_value_t capability, /**< PromiseCapability record */ - ecma_value_t ctor) /**< the caller of Promise.race */ +ecma_builtin_promise_perform_all (ecma_value_t iterator, /**< iteratorRecord */ + ecma_value_t ctor, /**< the caller of Promise.race */ + ecma_value_t capability, /**< PromiseCapability record */ + bool *done_p) /**< [out] iteratorRecord[[done]] */ { - JERRY_ASSERT (ecma_is_value_object (capability) - && ecma_is_value_object (array) - && ecma_is_value_object (ctor)); - JERRY_ASSERT (ecma_get_object_builtin_id (ecma_get_object_from_value (ctor)) == ECMA_BUILTIN_ID_PROMISE); - JERRY_ASSERT (ecma_get_object_type (ecma_get_object_from_value (array)) == ECMA_OBJECT_TYPE_ARRAY); + /* 1. - 2. */ + JERRY_ASSERT (ecma_is_value_object (capability) && ecma_is_constructor (ctor)); - ecma_value_t ret = ECMA_VALUE_EMPTY; - ecma_object_t *array_p = ecma_get_object_from_value (array); - ecma_extended_object_t *ext_array_p = (ecma_extended_object_t *) array_p; + /* 3. */ + ecma_object_t *values_array_obj_p = ecma_op_new_fast_array_object (0); + ecma_value_t values_array = ecma_make_object_value (values_array_obj_p); + /* 4. */ + ecma_value_t remaining = ecma_op_create_number_object (ecma_make_integer_value (1)); + /* 5. */ + uint32_t idx = 0; - ecma_length_t len = ext_array_p->u.array.length; + ecma_value_t ret_value = ECMA_VALUE_ERROR; + ecma_object_t *capability_obj_p = ecma_get_object_from_value (capability); - ecma_string_t *promise_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE); - ecma_string_t *resolve_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE); - ecma_string_t *reject_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT); ecma_string_t *already_called_str_p; already_called_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_ALREADY_CALLED); ecma_string_t *index_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_INDEX); @@ -349,81 +348,75 @@ ecma_builtin_promise_do_all (ecma_value_t array, /**< the array for all */ ecma_string_t *capability_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_CAPABILITY); ecma_string_t *remaining_str_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REMAINING_ELEMENT); - ecma_value_t undefined_val = ECMA_VALUE_UNDEFINED; - /* String '1' indicates [[Resolve]] and '2' indicates [[Reject]]. */ - ecma_value_t resolve = ecma_op_object_get (ecma_get_object_from_value (capability), - resolve_str_p); - ecma_value_t reject = ecma_op_object_get (ecma_get_object_from_value (capability), - reject_str_p); - /* 3. */ - ecma_value_t result_array_length_val = ecma_make_uint32_value (0); - ecma_value_t value_array = ecma_op_create_array_object (&result_array_length_val, 1, true); - ecma_free_value (result_array_length_val); - /* 4. */ - ecma_value_t remaining = ecma_op_create_number_object (ecma_make_integer_value (1)); - /* 5. */ - ecma_length_t index = 0; - - /* 6 */ + /* 6. */ while (true) { - JERRY_ASSERT (index <= len); - /* d. */ - if (index == len) + /* a. */ + ecma_value_t next = ecma_op_iterator_step (iterator); + /* b. - c. */ + if (ECMA_IS_VALUE_ERROR (next)) { - /* ii. */ + *done_p = true; + break; + } + + /* d. */ + if (ecma_is_value_false (next)) + { + /* i. */ + *done_p = true; + + /* ii. - iii. */ if (ecma_builtin_promise_remaining_inc_or_dec (remaining, false) == 0) { - /* iii. */ - ecma_value_t resolve_ret = ecma_op_function_call (ecma_get_object_from_value (resolve), - ECMA_VALUE_UNDEFINED, - &value_array, - 1); + /* 2. */ + ecma_value_t resolve = ecma_op_object_get_by_magic_id (capability_obj_p, + LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_RESOLVE); + ecma_value_t resolve_result = ecma_op_function_call (ecma_get_object_from_value (resolve), + ECMA_VALUE_UNDEFINED, + &values_array, + 1); + ecma_free_value (resolve); - if (ECMA_IS_VALUE_ERROR (resolve_ret)) + /* 3. */ + if (ECMA_IS_VALUE_ERROR (resolve_result)) { - ret = resolve_ret; break; } + + ecma_free_value (resolve_result); } /* iv. */ - ret = ecma_op_object_get (ecma_get_object_from_value (capability), promise_str_p); + ret_value = ecma_op_object_get_by_magic_id (capability_obj_p, + LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_PROMISE); break; } - /* e. h. */ - ecma_string_t *index_to_str_p = ecma_new_ecma_string_from_uint32 (index); - ecma_value_t array_item = ecma_op_object_get (array_p, index_to_str_p); + /* e. */ + ecma_value_t next_value = ecma_op_iterator_value (next); + ecma_free_value (next); - if (ECMA_IS_VALUE_ERROR (array_item)) + /* f. - g. */ + if (ECMA_IS_VALUE_ERROR (next_value)) { - ecma_deref_ecma_string (index_to_str_p); - ret = array_item; + *done_p = true; break; } - ecma_value_t put_ret = ecma_builtin_helper_def_prop (ecma_get_object_from_value (value_array), - index_to_str_p, - undefined_val, - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); - ecma_deref_ecma_string (index_to_str_p); - - if (ECMA_IS_VALUE_ERROR (put_ret)) - { - ecma_free_value (array_item); - ret = put_ret; - break; - } + /* h. */ + ecma_builtin_helper_def_prop_by_index (values_array_obj_p, + idx, + ECMA_VALUE_UNDEFINED, + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE); /* i. */ - ecma_value_t next_promise = ecma_builtin_promise_resolve (ctor, array_item); - ecma_free_value (array_item); + ecma_value_t next_promise = ecma_op_invoke_by_magic_id (ctor, LIT_MAGIC_STRING_RESOLVE, &next_value, 1); + ecma_free_value (next_value); /* j. */ if (ECMA_IS_VALUE_ERROR (next_promise)) { - ret = next_promise; break; } @@ -436,15 +429,19 @@ ecma_builtin_promise_do_all (ecma_value_t array, /**< the array for all */ ECMA_VALUE_FALSE, false); /* m. */ + ecma_value_t idx_value = ecma_make_uint32_value (idx); ecma_op_object_put (res_ele_p, index_str_p, - ecma_make_uint32_value (index), + idx_value, false); + ecma_free_value (idx_value); + /* n. */ ecma_op_object_put (res_ele_p, value_str_p, - value_array, + values_array, false); + /* o. */ ecma_op_object_put (res_ele_p, capability_str_p, @@ -458,33 +455,35 @@ ecma_builtin_promise_do_all (ecma_value_t array, /**< the array for all */ /* q. */ ecma_builtin_promise_remaining_inc_or_dec (remaining, true); + /* r. */ - ecma_value_t then_result = ecma_promise_then (next_promise, - ecma_make_object_value (res_ele_p), - reject); - ecma_deref_object (res_ele_p); + ecma_value_t args[2]; + args[0] = ecma_make_object_value (res_ele_p); + args[1] = ecma_op_object_get_by_magic_id (capability_obj_p, LIT_INTERNAL_MAGIC_STRING_PROMISE_PROPERTY_REJECT); + ecma_value_t result = ecma_op_invoke_by_magic_id (next_promise, LIT_MAGIC_STRING_THEN, args, 2); ecma_free_value (next_promise); - /* s. */ - if (ECMA_IS_VALUE_ERROR (then_result)) + for (uint8_t i = 0; i < 2; i++) + { + ecma_free_value (args[i]); + } + + /* s. */ + if (ECMA_IS_VALUE_ERROR (result)) { - ret = then_result; break; } - ecma_free_value (then_result); - index ++; + ecma_free_value (result); + + /* t. */ + idx++; } - ecma_free_value (reject); - ecma_free_value (resolve); ecma_free_value (remaining); - ecma_free_value (value_array); - - JERRY_ASSERT (!ecma_is_value_empty (ret)); - - return ret; -} /* ecma_builtin_promise_do_all */ + ecma_deref_object (values_array_obj_p); + return ret_value; +} /* ecma_builtin_promise_perform_all */ /** * The common function for both Promise.race and Promise.all. @@ -494,7 +493,7 @@ ecma_builtin_promise_do_all (ecma_value_t array, /**< the array for all */ */ static ecma_value_t ecma_builtin_promise_race_or_all (ecma_value_t this_arg, /**< 'this' argument */ - ecma_value_t array, /**< the items to be resolved */ + ecma_value_t iterable, /**< the items to be resolved */ bool is_race) /**< indicates whether it is race function */ { if (!ecma_is_value_object (this_arg)) @@ -530,33 +529,41 @@ ecma_builtin_promise_race_or_all (ecma_value_t this_arg, /**< 'this' argument */ return capability; } - ecma_value_t ret = ECMA_VALUE_EMPTY; + ecma_value_t iterator = ecma_builtin_promise_reject_abrupt (ecma_op_get_iterator (iterable, ECMA_VALUE_EMPTY), + capability); - if (!ecma_is_value_object (array) - || ecma_get_object_type (ecma_get_object_from_value (array)) != ECMA_OBJECT_TYPE_ARRAY) + if (ECMA_IS_VALUE_ERROR (iterator)) { - ret = ecma_builtin_promise_reject_abrupt (capability); ecma_free_value (constructor_value); ecma_free_value (capability); - return ret; + return iterator; } + ecma_value_t ret = ECMA_VALUE_EMPTY; + bool is_done = false; + if (is_race) { - ret = ecma_builtin_promise_do_race (array, capability, constructor_value); + ret = ecma_builtin_promise_perform_race (iterator, capability, constructor_value, &is_done); } else { - ret = ecma_builtin_promise_do_all (array, capability, constructor_value); + ret = ecma_builtin_promise_perform_all (iterator, constructor_value, capability, &is_done); } ecma_free_value (constructor_value); if (ECMA_IS_VALUE_ERROR (ret)) { - ret = jcontext_take_exception (); + if (is_done) + { + ret = ecma_op_iterator_close (iterator); + } + + ret = ecma_builtin_promise_reject_abrupt (ret, capability); } + ecma_free_value (iterator); ecma_free_value (capability); return ret; diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index be91449f1..49eee003b 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -2672,6 +2672,65 @@ ecma_op_species_constructor (ecma_object_t *this_value, /**< This Value */ #endif /* ENABLED (JERRY_ES2015) */ +/** + * 7.3.18 Abstract operation Invoke when property name is a magic string + * + * @return ecma_value result of the invoked function or raised error + * note: returned value must be freed with ecma_free_value + */ +inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE +ecma_op_invoke_by_magic_id (ecma_value_t object, /**< Object value */ + lit_magic_string_id_t magic_string_id, /**< Magic string ID */ + ecma_value_t *args_p, /**< Argument list */ + ecma_length_t args_len) /**< Argument list length */ +{ + return ecma_op_invoke (object, ecma_get_magic_string (magic_string_id), args_p, args_len); +} /* ecma_op_invoke_by_magic_id */ + +/** + * 7.3.18 Abstract operation Invoke + * + * @return ecma_value result of the invoked function or raised error + * note: returned value must be freed with ecma_free_value + */ +ecma_value_t +ecma_op_invoke (ecma_value_t object, /**< Object value */ + ecma_string_t *property_name_p, /**< Property name */ + ecma_value_t *args_p, /**< Argument list */ + ecma_length_t args_len) /**< Argument list length */ +{ + /* 3. */ + ecma_value_t object_value = ecma_op_to_object (object); + if (ECMA_IS_VALUE_ERROR (object_value)) + { + return object_value; + } + + ecma_object_t *object_p = ecma_get_object_from_value (object_value); + ecma_value_t func = ecma_op_object_get (object_p, property_name_p); + + if (ECMA_IS_VALUE_ERROR (func)) + { + ecma_deref_object (object_p); + return func; + } + + /* 4. */ + if (!ecma_op_is_callable (func)) + { + ecma_free_value (func); + ecma_deref_object (object_p); + return ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not callable")); + } + + ecma_object_t *func_obj_p = ecma_get_object_from_value (func); + ecma_value_t call_result = ecma_op_function_call (func_obj_p, object, args_p, args_len); + ecma_deref_object (object_p); + ecma_deref_object (func_obj_p); + + return call_result; +} /* ecma_op_invoke */ + /** * @} * @} diff --git a/jerry-core/ecma/operations/ecma-objects.h b/jerry-core/ecma/operations/ecma-objects.h index 03e32404c..b68e32628 100644 --- a/jerry-core/ecma/operations/ecma-objects.h +++ b/jerry-core/ecma/operations/ecma-objects.h @@ -73,6 +73,10 @@ ecma_value_t ecma_op_is_concat_spreadable (ecma_value_t arg); ecma_value_t ecma_op_is_regexp (ecma_value_t arg); ecma_value_t ecma_op_species_constructor (ecma_object_t *this_value, ecma_builtin_id_t default_constructor_id); #endif /* ENABLED (JERRY_ES2015) */ +ecma_value_t ecma_op_invoke (ecma_value_t object, ecma_string_t *property_name_p, ecma_value_t *args_p, + ecma_length_t args_len); +ecma_value_t ecma_op_invoke_by_magic_id (ecma_value_t object, lit_magic_string_id_t magic_string_id, + ecma_value_t *args_p, ecma_length_t args_len); /** * @} diff --git a/jerry-core/ecma/operations/ecma-promise-object.c b/jerry-core/ecma/operations/ecma-promise-object.c index bed155711..34f97c330 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.c +++ b/jerry-core/ecma/operations/ecma-promise-object.c @@ -249,8 +249,8 @@ ecma_promise_reject_handler (const ecma_value_t function, /**< the function itse /* 4. */ if (ecma_get_already_resolved_bool_value (already_resolved)) { - ecma_free_value (promise); ecma_free_value (already_resolved); + ecma_free_value (promise); return ECMA_VALUE_UNDEFINED; } @@ -325,7 +325,7 @@ ecma_promise_resolve_handler (const ecma_value_t function, /**< the function its /* 8. */ ecma_value_t then = ecma_op_object_get_by_magic_id (ecma_get_object_from_value (argv[0]), - LIT_MAGIC_STRING_THEN); + LIT_MAGIC_STRING_THEN); if (ECMA_IS_VALUE_ERROR (then)) { @@ -409,6 +409,35 @@ ecma_call_builtin_executor (ecma_object_t *executor_p, /**< the executor object return ECMA_VALUE_UNDEFINED; } /* ecma_call_builtin_executor */ +/** + * Helper function for PromiseCreateResovingFucntions. + * + * See also: ES2015 25.4.1.3 2. - 7. + * + * @return pointer to the resolving function + */ +static ecma_object_t * +ecma_promise_create_resolving_functions_helper (ecma_object_t *obj_p, /**< Promise Object */ + ecma_value_t already_resolved, /**< AlreadyResolved property */ + ecma_external_handler_t handler_cb) /**< Callback handler */ +{ + ecma_string_t *str_promise_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE); + ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED); + ecma_object_t *func_obj_p = ecma_op_create_external_function_object (handler_cb); + + ecma_op_object_put (func_obj_p, + str_promise_p, + ecma_make_object_value (obj_p), + false); + + ecma_op_object_put (func_obj_p, + str_already_resolved_p, + already_resolved, + false); + + return func_obj_p; +} /* ecma_promise_create_resolving_functions_helper */ + /** * Create a PromiseCreateResovingFucntions. * @@ -422,36 +451,15 @@ ecma_promise_create_resolving_functions (ecma_object_t *object_p) /**< the promi /* 1. */ ecma_value_t already_resolved = ecma_op_create_boolean_object (ECMA_VALUE_FALSE); - ecma_string_t *str_promise_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_PROMISE); - ecma_string_t *str_already_resolved_p = ecma_get_magic_string (LIT_INTERNAL_MAGIC_STRING_ALREADY_RESOLVED); + /* 2. - 4. */ + ecma_object_t *resolve_p = ecma_promise_create_resolving_functions_helper (object_p, + already_resolved, + ecma_promise_resolve_handler); - /* 2. */ - ecma_object_t *resolve_p; - resolve_p = ecma_op_create_external_function_object (ecma_promise_resolve_handler); - - /* 3. */ - ecma_op_object_put (resolve_p, - str_promise_p, - ecma_make_object_value (object_p), - false); - /* 4. */ - ecma_op_object_put (resolve_p, - str_already_resolved_p, - already_resolved, - false); - /* 5. */ - ecma_object_t *reject_p; - reject_p = ecma_op_create_external_function_object (ecma_promise_reject_handler); - /* 6. */ - ecma_op_object_put (reject_p, - str_promise_p, - ecma_make_object_value (object_p), - false); - /* 7. */ - ecma_op_object_put (reject_p, - str_already_resolved_p, - already_resolved, - false); + /* 5. - 7. */ + ecma_object_t *reject_p = ecma_promise_create_resolving_functions_helper (object_p, + already_resolved, + ecma_promise_reject_handler); /* 8. */ ecma_promise_resolving_functions_t *funcs = jmem_heap_alloc_block (sizeof (ecma_promise_resolving_functions_t)); @@ -459,7 +467,6 @@ ecma_promise_create_resolving_functions (ecma_object_t *object_p) /**< the promi funcs->reject = ecma_make_object_value (reject_p); ecma_free_value (already_resolved); - return funcs; } /* ecma_promise_create_resolving_functions */ diff --git a/tests/jerry/es2015/promise-all-iterator.js b/tests/jerry/es2015/promise-all-iterator.js new file mode 100644 index 000000000..6850fb174 --- /dev/null +++ b/tests/jerry/es2015/promise-all-iterator.js @@ -0,0 +1,76 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function createIterable(arr, methods = {}) { + let iterable = function *() { + let idx = 0; + while (idx < arr.length) { + yield arr[idx]; + idx++; + } + }(); + iterable['return'] = methods['return']; + iterable['throw'] = methods['throw']; + + return iterable; +}; + +// iterator next +Promise.all({[Symbol.iterator]() { return {get next() { throw 5; }}} +}).then(onfullfilled => { + // If this is not called, the promise has failed, and catch must be called. + assert(false); +}).catch(err => { + assert(err === 5); +}); + +// iterator value +Promise.all({ [Symbol.iterator] () { return { next () { return { get value () { throw 5 }}}}} +}).then(onfullfilled => { + // If this is not called, the promise has failed, and catch must be called. + assert(false); +}).catch(err => { + assert(err === 5); +}); + +// iterator done +Promise.all({ [Symbol.iterator] () { return { next () { return { get done () { throw 5 }}}}} +}).then(onfullfilled => { + // If this is not called, the promise has failed, and catch must be called. + assert(false); +}).catch(err => { + assert(err === 5); +}); + +// iterator get +Promise.all({ get [Symbol.iterator] () { throw 5 } +}).then(onfullfilled => { + // If this is not called, the promise has failed, and catch must be called. + assert(false); +}).catch(err => { + assert(err === 5); +}); + +var fulfills = Promise.all(createIterable([ + new Promise(resolve => { resolve("foo"); }), + new Promise(resolve => { resolve("bar"); }), +])); +var rejects = Promise.all(createIterable([ + new Promise((_, reject) => { reject("baz"); }), + new Promise((_, reject) => { reject("qux"); }), +])); + +fulfills.then(result => { assert (result + "" === "foo,bar"); }); +rejects.catch(result => { assert (result === "baz"); }); diff --git a/tests/jerry/es2015/promise-race-iterator.js b/tests/jerry/es2015/promise-race-iterator.js new file mode 100644 index 000000000..2575b88da --- /dev/null +++ b/tests/jerry/es2015/promise-race-iterator.js @@ -0,0 +1,64 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function createIterable(arr, methods = {}) { + let iterable = function *() { + let idx = 0; + while (idx < arr.length) { + yield arr[idx]; + idx++; + } + }(); + iterable['return'] = methods['return']; + iterable['throw'] = methods['throw']; + + return iterable; +}; + +// iterator next +Promise.race({[Symbol.iterator]() { return {get next() { throw 5; }}} +}).catch(err => { + assert(err === 5); +}); + +// iterator value +Promise.race({ [Symbol.iterator] () { return { next () { return { get value () { throw 5 }}}}} +}).catch(err => { + assert(err === 5); +}); + +// iterator done +Promise.race({ [Symbol.iterator] () { return { next () { return { get done () { throw 5 }}}}} +}).catch(err => { + assert(err === 5); +}); + +// iterator get +Promise.race({ get [Symbol.iterator] () { throw 5 } +}).catch(err => { + assert(err === 5); +}); + +var fulfills = Promise.race(createIterable([ + new Promise(resolve => { resolve("foo"); }), + new Promise(resolve => { resolve("bar"); }), +])); +var rejects = Promise.race(createIterable([ + new Promise((_, reject) => { reject("baz"); }), + new Promise((_, reject) => { reject("qux"); }), +])); + +fulfills.then(result => { assert (result + "" === "foo"); }); +rejects.catch(result => { assert (result === "baz"); }); diff --git a/tests/jerry/fail/regression-test-issue-2544.js b/tests/jerry/es2015/regression-test-issue-2544.js similarity index 75% rename from tests/jerry/fail/regression-test-issue-2544.js rename to tests/jerry/es2015/regression-test-issue-2544.js index 946751fcd..07c5ca9d5 100644 --- a/tests/jerry/fail/regression-test-issue-2544.js +++ b/tests/jerry/es2015/regression-test-issue-2544.js @@ -13,4 +13,8 @@ // limitations under the License. Object.defineProperty(Array.prototype, 0, { get : function () { throw $; } }); -Promise.race([ , this]).then(Error); +var asyncPassed = false; +Promise.race([ , this]).then(Error).catch(function(err) { asyncPassed = (err instanceof ReferenceError); }); +new Promise(function() { + throw 5; +}).then(Error).catch(function() { assert(asyncPassed); });