diff --git a/jerry-core/ecma/operations/ecma-iterator-object.c b/jerry-core/ecma/operations/ecma-iterator-object.c index a2859c95f..fa2585a9d 100644 --- a/jerry-core/ecma/operations/ecma-iterator-object.c +++ b/jerry-core/ecma/operations/ecma-iterator-object.c @@ -26,6 +26,7 @@ #include "ecma-objects.h" #include "ecma-objects-general.h" #include "ecma-function-object.h" +#include "jcontext.h" /** \addtogroup ecma ECMA * @{ @@ -393,6 +394,97 @@ ecma_op_iterator_step (ecma_value_t iterator) /**< iterator value */ return result; } /* ecma_op_iterator_step */ +/** + * IteratorClose operation + * + * See also: ECMA-262 v6, 7.4.6 + * + * @return ECMA_VALUE_EMPTY - if "return" is succesfully invoked, + * and the operation is called with normal completion + * ECMA_VALUE_ERROR - otherwise + */ +ecma_value_t +ecma_op_iterator_close (ecma_value_t iterator) /**< iterator value */ +{ + /* 1. */ + JERRY_ASSERT (ecma_is_value_object (iterator)); + + /* 2. */ + ecma_value_t completion = ECMA_VALUE_EMPTY; + + if (JERRY_CONTEXT (status_flags) & ECMA_STATUS_EXCEPTION) + { + completion = JERRY_CONTEXT (error_value); + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; + } + + /* 3. */ + ecma_value_t return_method = ecma_op_get_method_by_magic_id (iterator, LIT_MAGIC_STRING_RETURN); + + /* 4. */ + if (ECMA_IS_VALUE_ERROR (return_method)) + { + ecma_free_value (completion); + return return_method; + } + + /* 5. */ + if (ecma_is_value_undefined (return_method)) + { + if (ecma_is_value_empty (completion)) + { + return ECMA_VALUE_UNDEFINED; + } + + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + return ECMA_VALUE_ERROR; + } + + /* 6. */ + ecma_object_t *return_obj_p = ecma_get_object_from_value (return_method); + ecma_value_t inner_result = ecma_op_function_call (return_obj_p, iterator, NULL, 0); + ecma_deref_object (return_obj_p); + + /* 7. */ + if (!ecma_is_value_empty (completion)) + { + if (ECMA_IS_VALUE_ERROR (inner_result)) + { + ecma_free_value (JERRY_CONTEXT (error_value)); + JERRY_CONTEXT (error_value) = completion; + } + + ecma_free_value (inner_result); + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + return ECMA_VALUE_ERROR; + } + + /* 8. */ + if (ECMA_IS_VALUE_ERROR (inner_result)) + { + ecma_free_value (completion); + return inner_result; + } + + /* 9. */ + bool is_object = ecma_is_value_object (inner_result); + ecma_free_value (inner_result); + + if (!is_object) + { + ecma_free_value (completion); + return ecma_raise_type_error (ECMA_ERR_MSG ("method 'return' is not callable.")); + } + + /* 10. */ + if (ecma_is_value_empty (completion)) + { + return ECMA_VALUE_UNDEFINED; + } + + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + return ECMA_VALUE_ERROR; +} /* ecma_op_iterator_close */ #endif /* ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */ /** * @} diff --git a/jerry-core/ecma/operations/ecma-iterator-object.h b/jerry-core/ecma/operations/ecma-iterator-object.h index d4b81e614..83160017d 100644 --- a/jerry-core/ecma/operations/ecma-iterator-object.h +++ b/jerry-core/ecma/operations/ecma-iterator-object.h @@ -52,6 +52,9 @@ ecma_op_iterator_value (ecma_value_t iter_result); ecma_value_t ecma_op_iterator_step (ecma_value_t iterator); +ecma_value_t +ecma_op_iterator_close (ecma_value_t iterator); + #endif /* ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) */ /** diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index faa2c9a5a..043fdccd4 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -968,19 +968,21 @@ ecma_op_object_get_by_symbol_id (ecma_object_t *object_p, /**< the object */ } /* ecma_op_object_get_by_symbol_id */ /** - * GetMethod operation the property is a well-known symbol + * GetMethod operation * * See also: ECMA-262 v6, 7.3.9 * * Note: * Returned value must be freed with ecma_free_value. * - * @return iterator fucntion object - if success + * @return iterator function object - if success * raised error - otherwise */ -ecma_value_t -ecma_op_get_method_by_symbol_id (ecma_value_t value, /**< ecma value */ - lit_magic_string_id_t property_id) /**< property symbol id */ +static ecma_value_t +ecma_op_get_method_by_id (ecma_value_t value, /**< ecma value */ + lit_magic_string_id_t id, /**< property magic id */ + bool is_symbol_id) /**< true - if id represents a symbol id + * false - otherwise */ { /* 2. */ ecma_value_t obj_value = ecma_op_to_object (value); @@ -991,7 +993,16 @@ ecma_op_get_method_by_symbol_id (ecma_value_t value, /**< ecma value */ } ecma_object_t *obj_p = ecma_get_object_from_value (obj_value); - ecma_value_t func = ecma_op_object_get_by_symbol_id (obj_p, property_id); + ecma_value_t func; + + if (is_symbol_id) + { + func = ecma_op_object_get_by_symbol_id (obj_p, id); + } + else + { + func = ecma_op_object_get_by_magic_id (obj_p, id); + } ecma_deref_object (obj_p); /* 3. */ @@ -1015,7 +1026,43 @@ ecma_op_get_method_by_symbol_id (ecma_value_t value, /**< ecma value */ /* 6. */ return func; +} /* ecma_op_get_method_by_id */ + +/** + * GetMethod operation when the property is a well-known symbol + * + * See also: ECMA-262 v6, 7.3.9 + * + * Note: + * Returned value must be freed with ecma_free_value. + * + * @return iterator function object - if success + * raised error - otherwise + */ +ecma_value_t +ecma_op_get_method_by_symbol_id (ecma_value_t value, /**< ecma value */ + lit_magic_string_id_t symbol_id) /**< property symbol id */ +{ + return ecma_op_get_method_by_id (value, symbol_id, true); } /* ecma_op_get_method_by_symbol_id */ + +/** + * GetMethod operation when the property is a magic string + * + * See also: ECMA-262 v6, 7.3.9 + * + * Note: + * Returned value must be freed with ecma_free_value. + * + * @return iterator function object - if success + * raised error - otherwise + */ +ecma_value_t +ecma_op_get_method_by_magic_id (ecma_value_t value, /**< ecma value */ + lit_magic_string_id_t magic_id) /**< property magic id */ +{ + return ecma_op_get_method_by_id (value, magic_id, false); +} /* ecma_op_get_method_by_magic_id */ #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/ecma/operations/ecma-objects.h b/jerry-core/ecma/operations/ecma-objects.h index b323da239..c5d56114c 100644 --- a/jerry-core/ecma/operations/ecma-objects.h +++ b/jerry-core/ecma/operations/ecma-objects.h @@ -43,7 +43,8 @@ ecma_value_t ecma_op_object_get_by_uint32_index (ecma_object_t *object_p, uint32 ecma_value_t ecma_op_object_get_by_magic_id (ecma_object_t *object_p, lit_magic_string_id_t property_id); #if ENABLED (JERRY_ES2015) ecma_value_t ecma_op_object_get_by_symbol_id (ecma_object_t *object_p, lit_magic_string_id_t property_id); -ecma_value_t ecma_op_get_method_by_symbol_id (ecma_value_t value, lit_magic_string_id_t property_id); +ecma_value_t ecma_op_get_method_by_symbol_id (ecma_value_t value, lit_magic_string_id_t symbol_id); +ecma_value_t ecma_op_get_method_by_magic_id (ecma_value_t value, lit_magic_string_id_t magic_id); #endif /* ENABLED (JERRY_ES2015) */ ecma_value_t ecma_op_object_put (ecma_object_t *object_p, ecma_string_t *property_name_p, ecma_value_t value, bool is_throw); diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index c5421af74..b041fffc1 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (30u) +#define JERRY_SNAPSHOT_VERSION (31u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/lit/lit-magic-strings.inc.h b/jerry-core/lit/lit-magic-strings.inc.h index 8196cd63d..cee2de28b 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -322,6 +322,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REJECT, "reject") #if ENABLED (JERRY_BUILTIN_STRING) && ENABLED (JERRY_ES2015_BUILTIN) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REPEAT, "repeat") #endif +#if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RETURN, "return") +#endif #if ENABLED (JERRY_BUILTIN_REGEXP) && ENABLED (JERRY_BUILTIN_STRING) \ || ENABLED (JERRY_ES2015) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SEARCH, "search") diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index ee3f678eb..7d337f8b2 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -144,6 +144,7 @@ LIT_MAGIC_STRING_RANDOM = "random" LIT_MAGIC_STRING_REDUCE = "reduce" LIT_MAGIC_STRING_REJECT = "reject" LIT_MAGIC_STRING_REPEAT = "repeat" +LIT_MAGIC_STRING_RETURN = "return" LIT_MAGIC_STRING_SEARCH = "search" LIT_MAGIC_STRING_SOURCE = "source" LIT_MAGIC_STRING_SPLICE = "splice" diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index f03698540..4bf3c095e 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -644,6 +644,8 @@ VM_OC_ITERATOR_STEP) \ CBC_OPCODE (CBC_EXT_ITERATOR_STEP_PROP, CBC_NO_FLAG, 1, \ VM_OC_ITERATOR_STEP) \ + CBC_OPCODE (CBC_EXT_ITERATOR_CLOSE, CBC_NO_FLAG, -1, \ + VM_OC_ITERATOR_CLOSE | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_REST_INITIALIZER, CBC_NO_FLAG, 1, \ VM_OC_REST_INITIALIZER) \ CBC_OPCODE (CBC_EXT_REST_INITIALIZER_PROP, CBC_NO_FLAG, 1, \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 2be123705..e22ecbcc3 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -2491,8 +2491,8 @@ parser_parse_array_initializer (parser_context_t *context_p, /**< context */ } } - /* pop the iterator */ - parser_emit_cbc (context_p, CBC_POP); + /* close the iterator */ + parser_emit_cbc_ext (context_p, CBC_EXT_ITERATOR_CLOSE); parser_pattern_finalize (context_p, flags, &end_pos); } /* parser_parse_array_initializer */ diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 14d5411cf..9a65ea950 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1965,6 +1965,17 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ *stack_top_p++ = value; continue; } + case VM_OC_ITERATOR_CLOSE: + { + result = ecma_op_iterator_close (left_value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + goto free_left_value; + } case VM_OC_DEFAULT_INITIALIZER: { JERRY_ASSERT (stack_top_p > frame_ctx_p->registers_p + register_end); @@ -3883,6 +3894,8 @@ error: JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_EXCEPTION_THROWN); #endif /* ENABLED (JERRY_DEBUGGER) */ + JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_EXCEPTION; + byte_code_p = frame_ctx_p->byte_code_p; if (VM_GET_CONTEXT_TYPE (stack_top_p[-1]) == VM_CONTEXT_FINALLY_THROW) diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 4198f7999..cffbf6c2d 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -250,6 +250,7 @@ typedef enum VM_OC_PUSH_SPREAD_ELEMENT, /**< push spread element */ VM_OC_GET_ITERATOR, /**< GetIterator abstract operation */ VM_OC_ITERATOR_STEP, /**< IteratorStep abstract operation */ + VM_OC_ITERATOR_CLOSE, /**< IteratorClose abstract operation */ VM_OC_DEFAULT_INITIALIZER, /**< default initializer inside a pattern */ VM_OC_REST_INITIALIZER, /**< create rest object inside an array pattern */ VM_OC_INITIALIZER_PUSH_PROP, /**< push property for object initializer */ @@ -299,6 +300,7 @@ typedef enum VM_OC_PUSH_SPREAD_ELEMENT = VM_OC_NONE, /**< push spread element */ VM_OC_GET_ITERATOR = VM_OC_NONE, /**< GetIterator abstract operation */ VM_OC_ITERATOR_STEP = VM_OC_NONE, /**< IteratorStep abstract operation */ + VM_OC_ITERATOR_CLOSE = VM_OC_NONE, /**< IteratorClose abstract operation */ VM_OC_DEFAULT_INITIALIZER = VM_OC_NONE, /**< default initializer inside a pattern */ VM_OC_REST_INITIALIZER = VM_OC_NONE, /**< create rest object inside an array pattern */ VM_OC_INITIALIZER_PUSH_PROP = VM_OC_NONE, /**< push property for object initializer */ diff --git a/tests/jerry/es2015/array-pattern.js b/tests/jerry/es2015/array-pattern.js index 12093f776..f962d1501 100644 --- a/tests/jerry/es2015/array-pattern.js +++ b/tests/jerry/es2015/array-pattern.js @@ -247,3 +247,60 @@ mustThrow ("var [a] = { [Symbol.iterator] () { return { next () { return { get d [((a))] = [7]; assert (a === 7); }) (); + +// Test iterator closing +function __createIterableObject (arr, methods) { + methods = methods || {}; + if (typeof Symbol !== 'function' || !Symbol.iterator) { + return {}; + } + arr.length++; + var iterator = { + next: function() { + return { value: arr.shift(), done: arr.length <= 0 }; + }, + 'return': methods['return'], + 'throw': methods['throw'] + }; + var iterable = {}; + iterable[Symbol.iterator] = function () { return iterator; }; + return iterable; +}; + +(function () { + var closed = false; + var iter = __createIterableObject([1, 2, 3], { + 'return': function() { closed = true; return {}; } + }); + var [a, b] = iter; + assert (closed === true); + assert (a === 1); + assert (b === 2); +}) (); + +mustThrow ("var iter = __createIterableObject([], " + + "{ get 'return'() { throw new TypeError() }});" + + "var [a] = iter"); + +mustThrow ("var iter = __createIterableObject([], " + + "{ 'return': 5 });" + + "var [a] = iter"); + +mustThrow ("var iter = __createIterableObject([], " + + "{ 'return': function() { return 5; }});" + + "var [a] = iter"); + +mustThrow ("try { throw 5 } catch (e) {" + + "var iter = __createIterableObject([], " + + "{ get 'return'() { throw new TypeError() }});" + + "var [a] = iter }"); + +mustThrow ("try { throw 5 } catch (e) {" + + "var iter = __createIterableObject([], " + + "{ 'return': 5 });" + + "var [a] = iter }"); + +mustThrow ("try { throw 5 } catch (e) {" + + "var iter = __createIterableObject([], " + + "{ 'return': function() { return 5; }});" + + "var [a] = iter }"); diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index 55f1dd237..24bd9d100 100644 --- a/tests/unit-core/test-snapshot.c +++ b/tests/unit-core/test-snapshot.c @@ -223,7 +223,7 @@ main (void) /* Check the snapshot data. Unused bytes should be filled with zeroes */ const uint8_t expected_data[] = { - 0x4A, 0x52, 0x52, 0x59, 0x1E, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x41, 0x00, 0x01, 0x00,