From 110f75c99dabb6539d74c0cd43d13206615c39f8 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Thu, 28 Nov 2019 11:54:27 +0100 Subject: [PATCH] Implement the core of the generator functions. (#3368) Some things are missing: - yield* support - generator definition in object literal - the hidden GeneratorFunction JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- jerry-core/ecma/base/ecma-gc.c | 176 ++++++++++++++++ jerry-core/ecma/base/ecma-globals.h | 29 +++ jerry-core/ecma/base/ecma-helpers-value.c | 28 ++- jerry-core/ecma/base/ecma-helpers.h | 2 + .../ecma-builtin-generator-prototype.c | 156 ++++++++++++++ .../ecma-builtin-generator-prototype.inc.h | 40 ++++ .../ecma/builtin-objects/ecma-builtins.inc.h | 7 + .../ecma/operations/ecma-function-object.c | 25 ++- jerry-core/include/jerryscript-snapshot.h | 2 +- jerry-core/lit/lit-magic-strings.inc.h | 12 +- jerry-core/lit/lit-magic-strings.ini | 2 + jerry-core/parser/js/byte-code.h | 29 ++- jerry-core/parser/js/js-lexer.c | 45 ++++ jerry-core/parser/js/js-parser-expr.c | 43 ++++ jerry-core/parser/js/js-parser-internal.h | 38 ++-- jerry-core/parser/js/js-parser-statm.c | 23 +- jerry-core/parser/js/js-parser-util.c | 6 + jerry-core/parser/js/js-parser.c | 31 +++ jerry-core/parser/js/js-parser.h | 3 + jerry-core/parser/js/js-scanner-internal.h | 3 + jerry-core/parser/js/js-scanner-util.c | 25 ++- jerry-core/parser/js/js-scanner.c | 57 ++++- jerry-core/profiles/README.md | 9 +- jerry-core/vm/opcodes.c | 173 +++++++++++++++ jerry-core/vm/opcodes.h | 12 +- jerry-core/vm/vm-defines.h | 13 +- jerry-core/vm/vm-stack.c | 125 +++++++++-- jerry-core/vm/vm-stack.h | 18 ++ jerry-core/vm/vm.c | 50 ++++- jerry-core/vm/vm.h | 8 + tests/jerry/es2015/generator-return.js | 74 +++++++ tests/jerry/es2015/generator-throw.js | 82 ++++++++ tests/jerry/es2015/generator-yield.js | 73 +++++++ tests/jerry/es2015/generator.js | 199 ++++++++++++++++++ tests/unit-core/test-snapshot.c | 2 +- 35 files changed, 1535 insertions(+), 85 deletions(-) create mode 100644 jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c create mode 100644 jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.inc.h create mode 100644 tests/jerry/es2015/generator-return.js create mode 100644 tests/jerry/es2015/generator-throw.js create mode 100644 tests/jerry/es2015/generator-yield.js create mode 100644 tests/jerry/es2015/generator.js diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index 8ad33d1ee..d38211ef6 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -293,6 +293,7 @@ ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended #endif /* ENABLED (JERRY_ES2015_BUILTIN_PROMISE) */ #if ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) + /** * Mark objects referenced by Map/Set built-in. */ @@ -359,6 +360,73 @@ ecma_gc_mark_container_object (ecma_object_t *object_p) /**< object */ #endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ +#if ENABLED (JERRY_ES2015) + +/** + * Mark objects referenced by inactive generator functions, async functions, etc. + */ +static void +ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ +{ + vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; + + if (!ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p->extended_object.u.class_prop.extra_info)) + { + /* All objects referenced by running executable objects are strong roots, + * and a finished executable object cannot refer to other values. */ + return; + } + + ecma_gc_set_object_visited (executable_object_p->frame_ctx.lex_env_p); + + if (ecma_is_value_object (executable_object_p->frame_ctx.this_binding)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->frame_ctx.this_binding)); + } + + const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p; + size_t register_end; + + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; + register_end = args_p->register_end; + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; + register_end = args_p->register_end; + } + + ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); + ecma_value_t *register_end_p = register_p + register_end; + + while (register_p < register_end_p) + { + if (ecma_is_value_object (*register_p)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p)); + } + + register_p++; + } + + register_p += executable_object_p->frame_ctx.context_depth; + register_end_p = executable_object_p->frame_ctx.stack_top_p; + + while (register_p < register_end_p) + { + if (ecma_is_value_object (*register_p)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p)); + } + + register_p++; + } +} /* ecma_gc_mark_executable_object */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Mark objects as visited starting from specified object as root */ @@ -435,6 +503,13 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ break; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_CONTAINER) */ +#if ENABLED (JERRY_ES2015) + case LIT_MAGIC_STRING_GENERATOR_UL: + { + ecma_gc_mark_executable_object (object_p); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ default: { break; @@ -641,6 +716,100 @@ ecma_free_fast_access_array (ecma_object_t *object_p) /**< fast access mode arra ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); } /* ecma_free_fast_access_array */ +#if ENABLED (JERRY_ES2015) + +/** + * Free non-objects referenced by inactive generator functions, async functions, etc. + * + * @return total object size + */ +static size_t +ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */ +{ + vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; + + const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p; + size_t size, register_end; + + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; + + register_end = args_p->register_end; + size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; + + register_end = args_p->register_end; + size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); + } + + size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); + + JERRY_ASSERT (!(executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_RUNNING)); + + ecma_bytecode_deref ((ecma_compiled_code_t *) bytecode_header_p); + + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED) + { + return size; + } + + ecma_free_value_if_not_object (executable_object_p->frame_ctx.this_binding); + + ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); + ecma_value_t *register_end_p = register_p + register_end; + + while (register_p < register_end_p) + { + ecma_free_value_if_not_object (*register_p++); + } + + if (executable_object_p->frame_ctx.context_depth > 0) + { + ecma_value_t *context_end_p = register_p; + + register_p += executable_object_p->frame_ctx.context_depth; + + ecma_value_t *context_top_p = register_p; + + do + { + context_top_p[-1] &= (uint32_t) ~VM_CONTEXT_HAS_LEX_ENV; + + uint32_t offsets = vm_get_context_value_offsets (context_top_p); + + while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) + { + int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); + + if (ecma_is_value_object (context_top_p[offset])) + { + context_top_p[offset] = ECMA_VALUE_UNDEFINED; + } + + offsets >>= VM_CONTEXT_OFFSET_SHIFT; + } + + context_top_p = vm_stack_context_abort (&executable_object_p->frame_ctx, context_top_p); + } + while (context_top_p > context_end_p); + } + + register_end_p = executable_object_p->frame_ctx.stack_top_p; + + while (register_p < register_end_p) + { + ecma_free_value_if_not_object (*register_p++); + } + + return size; +} /* ecma_gc_free_executable_object */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * Free properties of an object */ @@ -930,6 +1099,13 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ break; } #endif /* ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW) */ +#if ENABLED (JERRY_ES2015) + case LIT_MAGIC_STRING_GENERATOR_UL: + { + ext_object_size = ecma_gc_free_executable_object (object_p); + break; + } +#endif /* ENABLED (JERRY_ES2015) */ default: { /* The undefined id represents an uninitialized class. */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 7119e0a7e..f662f26b4 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1750,6 +1750,35 @@ typedef struct #endif /* ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) */ +#if ENABLED (JERRY_ES2015) + +/** + * Executable (e.g. generator, async) object flags. + */ +typedef enum +{ + ECMA_EXECUTABLE_OBJECT_COMPLETED = (1u << 0), /**< executable object is completed and cannot be resumed */ + ECMA_EXECUTABLE_OBJECT_RUNNING = (1u << 1), /**< executable object is currently running */ +} ecma_executable_object_flags_t; + +/** + * Checks whether the executable object is waiting for resuming. + */ +#define ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED(extra_info) \ + (!((extra_info) & (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_EXECUTABLE_OBJECT_RUNNING))) + +/** + * Executable (e.g. generator, async) object flags. + */ +typedef enum +{ + ECMA_GENERATOR_NEXT, /**< generator should continue its execution */ + ECMA_GENERATOR_RETURN, /**< generator should perform a return operation */ + ECMA_GENERATOR_THROW, /**< generator should perform a throw operation */ +} ecma_generator_yield_mode_t; + +#endif /* ENABLED (JERRY_ES2015) */ + #if ENABLED (JERRY_ES2015_BUILTIN_DATAVIEW) /** * Description of DataView objects. diff --git a/jerry-core/ecma/base/ecma-helpers-value.c b/jerry-core/ecma/base/ecma-helpers-value.c index e84fd256a..8be045d82 100644 --- a/jerry-core/ecma/base/ecma-helpers-value.c +++ b/jerry-core/ecma/base/ecma-helpers-value.c @@ -855,10 +855,10 @@ ecma_fast_copy_value (ecma_value_t value) /**< value description */ * * @return copy of the given value */ -ecma_value_t +inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE ecma_copy_value_if_not_object (ecma_value_t value) /**< value description */ { - if (ecma_get_value_type_field (value) != ECMA_TYPE_OBJECT) + if (!ecma_is_value_object (value)) { return ecma_copy_value (value); } @@ -866,6 +866,30 @@ ecma_copy_value_if_not_object (ecma_value_t value) /**< value description */ return value; } /* ecma_copy_value_if_not_object */ +/** + * Increase reference counter of a value if it is an object. + */ +inline void JERRY_ATTR_ALWAYS_INLINE +ecma_ref_if_object (ecma_value_t value) /**< value description */ +{ + if (ecma_is_value_object (value)) + { + ecma_ref_object (ecma_get_object_from_value (value)); + } +} /* ecma_ref_if_object */ + +/** + * Decrease reference counter of a value if it is an object. + */ +inline void JERRY_ATTR_ALWAYS_INLINE +ecma_deref_if_object (ecma_value_t value) /**< value description */ +{ + if (ecma_is_value_object (value)) + { + ecma_deref_object (ecma_get_object_from_value (value)); + } +} /* ecma_deref_if_object */ + /** * Assign a new value to an ecma-value * diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 4b0ae2c1a..899cf3a7e 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -218,6 +218,8 @@ ecma_value_t JERRY_ATTR_CONST ecma_invert_boolean_value (ecma_value_t value); ecma_value_t ecma_copy_value (ecma_value_t value); ecma_value_t ecma_fast_copy_value (ecma_value_t value); ecma_value_t ecma_copy_value_if_not_object (ecma_value_t value); +void ecma_ref_if_object (ecma_value_t value); +void ecma_deref_if_object (ecma_value_t value); ecma_value_t ecma_update_float_number (ecma_value_t float_value, ecma_number_t new_number); void ecma_value_assign_value (ecma_value_t *value_p, ecma_value_t ecma_value); void ecma_value_assign_number (ecma_value_t *value_p, ecma_number_t ecma_number); diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c new file mode 100644 index 000000000..c532a67b8 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c @@ -0,0 +1,156 @@ +/* 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. + */ + +#include "ecma-builtins.h" +#include "ecma-exceptions.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-iterator-object.h" +#include "opcodes.h" +#include "vm-defines.h" + +#if ENABLED (JERRY_ES2015) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-generator-prototype.inc.h" +#define BUILTIN_UNDERSCORED_ID generator_prototype +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup generator ECMA Generator object built-in + * @{ + */ + +/** + * Helper function for next / return / throw + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_generator_prototype_object_do (ecma_value_t this_arg, /**< this argument */ + ecma_value_t arg, /**< argument */ + uint8_t yield_mode) /**< yield mode */ +{ + vm_executable_object_t *executable_object_p = NULL; + + if (ecma_is_value_object (this_arg)) + { + ecma_object_t *object_p = ecma_get_object_from_value (this_arg); + + if (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_CLASS) + { + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + + if (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_GENERATOR_UL) + { + executable_object_p = (vm_executable_object_t *) ext_object_p; + } + } + } + + if (executable_object_p == NULL) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a generator object.")); + } + + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_RUNNING) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Generator is currently under execution.")); + } + + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED) + { + return ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); + } + + executable_object_p->frame_ctx.call_operation = yield_mode; + + ecma_value_t value = opfunc_resume_executable_object (executable_object_p, arg); + + if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (value))) + { + return value; + } + + bool done = (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED); + ecma_value_t result = ecma_create_iter_result_object (value, ecma_make_boolean_value (done)); + + ecma_fast_free_value (value); + return result; +} /* ecma_builtin_generator_prototype_object_do */ + +/** + * The Generator.prototype object's 'next' routine + * + * See also: + * ECMA-262 v6, 25.3.1.2 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_generator_prototype_object_next (ecma_value_t this_arg, /**< this argument */ + ecma_value_t next_arg) /**< next argument */ +{ + return ecma_builtin_generator_prototype_object_do (this_arg, next_arg, ECMA_GENERATOR_NEXT); +} /* ecma_builtin_generator_prototype_object_next */ + +/** + * The Generator.prototype object's 'return' routine + * + * See also: + * ECMA-262 v6, 25.3.1.3 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_generator_prototype_object_return (ecma_value_t this_arg, /**< this argument */ + ecma_value_t return_arg) /**< return argument */ +{ + return ecma_builtin_generator_prototype_object_do (this_arg, return_arg, ECMA_GENERATOR_RETURN); +} /* ecma_builtin_generator_prototype_object_return */ + +/** + * The Generator.prototype object's 'throw' routine + * + * See also: + * ECMA-262 v6, 25.3.1.4 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_builtin_generator_prototype_object_throw (ecma_value_t this_arg, /**< this argument */ + ecma_value_t throw_arg) /**< throw argument */ +{ + return ecma_builtin_generator_prototype_object_do (this_arg, throw_arg, ECMA_GENERATOR_THROW); +} /* ecma_builtin_generator_prototype_object_throw */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_ES2015) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.inc.h new file mode 100644 index 000000000..588de23c0 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.inc.h @@ -0,0 +1,40 @@ +/* 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. + */ + +/* + * Generator.prototype built-in description + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_ES2015) + +/* Object properties: + * (property name, object pointer getter) */ + +/* ECMA-262 v6, 25.3.1.5 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_GENERATOR_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* Routine properties: + * (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */ +ROUTINE (LIT_MAGIC_STRING_NEXT, ecma_builtin_generator_prototype_object_next, 1, 1) +ROUTINE (LIT_MAGIC_STRING_RETURN, ecma_builtin_generator_prototype_object_return, 1, 1) +ROUTINE (LIT_MAGIC_STRING_THROW, ecma_builtin_generator_prototype_object_throw, 1, 1) + +#endif /* ENABLED (JERRY_ES2015) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h index ed010d1b9..c4148ce32 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h @@ -524,7 +524,14 @@ BUILTIN_ROUTINE (ECMA_BUILTIN_ID_SYMBOL, true, symbol) +BUILTIN (ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE, + ECMA_OBJECT_TYPE_GENERAL, + ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, + true, + generator_prototype) + #endif /* ENABLED (JERRY_ES2015) */ + #if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) /* The %IteratorPrototype% object (ECMA-262 v6, 25.1.2) */ BUILTIN (ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index f37c364a5..9fc16d5d2 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -789,21 +789,24 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ bool free_this_binding = false; const ecma_compiled_code_t *bytecode_data_p = ecma_op_function_get_compiled_code (ext_func_p); + uint16_t status_flags = bytecode_data_p->status_flags; #if ENABLED (JERRY_ES2015) - bool is_class_constructor = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_CONSTRUCTOR) != 0; - - if (is_class_constructor && !ecma_op_function_has_construct_flag (arguments_list_p)) + if (JERRY_UNLIKELY (status_flags & (CBC_CODE_FLAGS_CONSTRUCTOR | CBC_CODE_FLAGS_GENERATOR))) { - return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'.")); + if ((status_flags & CBC_CODE_FLAGS_CONSTRUCTOR) && !ecma_op_function_has_construct_flag (arguments_list_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Class constructor cannot be invoked without 'new'.")); + } + if ((status_flags & CBC_CODE_FLAGS_GENERATOR) && ecma_op_function_has_construct_flag (arguments_list_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Generator functions cannot be invoked with 'new'.")); + } } #endif /* ENABLED (JERRY_ES2015) */ - bool is_strict = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0; - bool is_no_lex_env = (bytecode_data_p->status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) != 0; - /* 1. */ - if (!is_strict) + if (!(status_flags & CBC_CODE_FLAGS_STRICT_MODE)) { if (ecma_is_value_undefined (this_binding) || ecma_is_value_null (this_binding)) @@ -825,7 +828,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ /* 5. */ ecma_object_t *local_env_p; - if (is_no_lex_env) + if (status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED) { local_env_p = scope_p; } @@ -841,7 +844,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ bytecode_data_p); } #if ENABLED (JERRY_ES2015) - if (JERRY_UNLIKELY (is_class_constructor)) + if (JERRY_UNLIKELY (status_flags & CBC_CODE_FLAGS_CONSTRUCTOR)) { ecma_op_set_class_this_binding (local_env_p, this_binding); } @@ -854,7 +857,7 @@ ecma_op_function_call (ecma_object_t *func_obj_p, /**< Function object */ arguments_list_p, arguments_list_len); - if (!is_no_lex_env) + if (!(status_flags & CBC_CODE_FLAGS_LEXICAL_ENV_NOT_NEEDED)) { ecma_deref_object (local_env_p); } diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index b041fffc1..d3482abde 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 (31u) +#define JERRY_SNAPSHOT_VERSION (32u) /** * 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 6f58950c5..ce35d5e3c 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -149,7 +149,8 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_JOIN, "join") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_KEYS, "keys") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NAME, "name") -#if ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) \ +#if ENABLED (JERRY_ES2015) \ +|| ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) \ || ENABLED (JERRY_ES2015_BUILTIN_MAP) \ || ENABLED (JERRY_ES2015_BUILTIN_SET) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_NEXT, "next") @@ -246,6 +247,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SLICE, "slice") || ENABLED (JERRY_ES2015) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPLIT, "split") #endif +#if ENABLED (JERRY_ES2015) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_THROW, "throw") +#endif #if ENABLED (JERRY_BUILTIN_MATH) && ENABLED (JERRY_ES2015_BUILTIN) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TRUNC, "trunc") #endif @@ -322,7 +326,8 @@ 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) +#if ENABLED (JERRY_ES2015) \ +|| ENABLED (JERRY_ES2015_BUILTIN_ITERATOR) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_RETURN, "return") #endif #if ENABLED (JERRY_BUILTIN_REGEXP) && ENABLED (JERRY_BUILTIN_STRING) \ @@ -521,6 +526,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARGUMENTS_UL, "Arguments") #if ENABLED (JERRY_BUILTIN_ERRORS) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_EVAL_ERROR_UL, "EvalError") #endif +#if ENABLED (JERRY_ES2015) +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GENERATOR_UL, "Generator") +#endif #if ENABLED (JERRY_ES2015_BUILTIN_TYPEDARRAY) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_INT8_ARRAY_UL, "Int8Array") #endif diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index 00d6ec721..bb39c3b5c 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -113,6 +113,7 @@ LIT_MAGIC_STRING_ROUND = "round" LIT_MAGIC_STRING_SHIFT = "shift" LIT_MAGIC_STRING_SLICE = "slice" LIT_MAGIC_STRING_SPLIT = "split" +LIT_MAGIC_STRING_THROW = "throw" LIT_MAGIC_STRING_TRUNC = "trunc" LIT_MAGIC_STRING_VALUE = "value" LIT_MAGIC_STRING_RESOURCE_EVAL = "" @@ -226,6 +227,7 @@ LIT_MAGIC_STRING_ARGUMENTS = "arguments" LIT_MAGIC_STRING_DECODE_URI = "decodeURI" LIT_MAGIC_STRING_ENCODE_URI = "encodeURI" LIT_MAGIC_STRING_FIND_INDEX = "findIndex" +LIT_MAGIC_STRING_GENERATOR_UL = "Generator" LIT_MAGIC_STRING_GET_UTC_DAY_UL = "getUTCDay" LIT_MAGIC_STRING_GET_UINT16_UL = "getUint16" LIT_MAGIC_STRING_GET_UINT32_UL = "getUint32" diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 4bf3c095e..14de8e89c 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -188,18 +188,18 @@ /* Stack consumption of opcodes with context. */ -/* PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION must be <= 3 */ -#define PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION 3 -/* PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION must be <= 4 */ -#define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4 -/* PARSER_BLOCK_CONTEXT_STACK_ALLOCATION must be <= 3 */ -#define PARSER_BLOCK_CONTEXT_STACK_ALLOCATION 1 -/* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ -#define PARSER_WITH_CONTEXT_STACK_ALLOCATION 1 -/* PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION must be <= 4 */ -#define PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION 1 /* PARSER_TRY_CONTEXT_STACK_ALLOCATION must be <= 3 */ #define PARSER_TRY_CONTEXT_STACK_ALLOCATION 2 +/* PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION 4 +/* PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION must be <= 3 */ +#define PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION 3 +/* PARSER_BLOCK_CONTEXT_STACK_ALLOCATION must be <= 3 */ +#define PARSER_WITH_CONTEXT_STACK_ALLOCATION 1 +/* PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_BLOCK_CONTEXT_STACK_ALLOCATION 1 +/* PARSER_WITH_CONTEXT_STACK_ALLOCATION must be <= 4 */ +#define PARSER_SUPER_CLASS_CONTEXT_STACK_ALLOCATION 1 /** * Opcode definitions. @@ -654,6 +654,12 @@ VM_OC_INITIALIZER_PUSH_PROP | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_SPREAD_NEW, CBC_HAS_POP_STACK_BYTE_ARG, 0, \ VM_OC_SPREAD_ARGUMENTS | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_EXT_CREATE_GENERATOR, CBC_NO_FLAG, 0, \ + VM_OC_CREATE_GENERATOR) \ + CBC_OPCODE (CBC_EXT_YIELD, CBC_NO_FLAG, -1, \ + VM_OC_YIELD | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_EXT_CONTINUE_EXEC, CBC_NO_FLAG, 1, \ + VM_OC_CONTINUE_EXEC) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_EXT_END, CBC_NO_FLAG, 0, \ @@ -743,7 +749,8 @@ typedef enum CBC_CODE_FLAGS_STATIC_FUNCTION = (1u << 8), /**< this function is a static snapshot function */ CBC_CODE_FLAGS_DEBUGGER_IGNORE = (1u << 9), /**< this function should be ignored by debugger */ CBC_CODE_FLAGS_CONSTRUCTOR = (1u << 10), /**< this function is a constructor */ - CBC_CODE_FLAGS_REST_PARAMETER = (1u << 11), /**< this function has rest parameter */ + CBC_CODE_FLAGS_GENERATOR = (1u << 11), /**< this function is a generator */ + CBC_CODE_FLAGS_REST_PARAMETER = (1u << 12), /**< this function has rest parameter */ } cbc_code_flags; /** diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index bf9f59de0..37070ab2c 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -604,6 +604,19 @@ lexer_parse_identifier (parser_context_t *context_p, /**< context */ { if (JERRY_UNLIKELY (keyword_p->type >= LEXER_FIRST_FUTURE_STRICT_RESERVED_WORD)) { +#if ENABLED (JERRY_ES2015) + if (keyword_p->type == LEXER_KEYW_YIELD && (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)) + { + if (context_p->status_flags & PARSER_DISALLOW_YIELD) + { + parser_raise_error (context_p, PARSER_ERR_YIELD_NOT_ALLOWED); + } + + context_p->token.type = (uint8_t) LEXER_KEYW_YIELD; + break; + } +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->status_flags & PARSER_IS_STRICT) { parser_raise_error (context_p, PARSER_ERR_STRICT_IDENT_NOT_ALLOWED); @@ -1521,6 +1534,38 @@ lexer_check_arrow_param (parser_context_t *context_p) /**< context */ || context_p->source_p[1] != LIT_CHAR_EQUALS); } /* lexer_check_arrow_param */ +/** + * Checks whether the yield expression has no argument. + * + * @return true if it has no argument + */ +bool +lexer_check_yield_no_arg (parser_context_t *context_p) /**< context */ +{ + if (context_p->token.flags & LEXER_WAS_NEWLINE) + { + return true; + } + + switch (context_p->token.type) + { + case LEXER_RIGHT_BRACE: + case LEXER_RIGHT_PAREN: + case LEXER_RIGHT_SQUARE: + case LEXER_COMMA: + case LEXER_COLON: + case LEXER_SEMICOLON: + case LEXER_EOS: + { + return true; + } + default: + { + return false; + } + } +} /* lexer_check_yield_no_arg */ + #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 61196bab2..1b56ff1ba 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -983,6 +983,22 @@ parser_parse_function_expression (parser_context_t *context_p, /**< context */ parser_line_counter_t debugger_column = context_p->token.column; #endif /* ENABLED (JERRY_DEBUGGER) */ +#if ENABLED (JERRY_ES2015) + uint32_t parent_status_flags = context_p->status_flags; + + if (lexer_check_next_character (context_p, LIT_CHAR_ASTERISK)) + { + /* The name of the function cannot be yield. */ + context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_YIELD; + status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_YIELD; + lexer_consume_next_character (context_p); + } + else + { + context_p->status_flags &= (uint32_t) ~(PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_YIELD); + } +#endif /* ENABLED (JERRY_ES2015) */ + if (!lexer_check_next_character (context_p, LIT_CHAR_LEFT_PAREN)) { lexer_next_token (context_p); @@ -1019,6 +1035,10 @@ parser_parse_function_expression (parser_context_t *context_p, /**< context */ function_name_index = context_p->lit_object.index; } + +#if ENABLED (JERRY_ES2015) + context_p->status_flags = parent_status_flags; +#endif /* ENABLED (JERRY_ES2015) */ } if (context_p->last_cbc_opcode == CBC_PUSH_LITERAL) @@ -1513,6 +1533,29 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ PARSER_IS_FUNCTION | PARSER_IS_ARROW_FUNCTION | PARSER_ARROW_PARSE_ARGS); return parser_abort_parsing_after_arrow (context_p); } + case LEXER_KEYW_YIELD: + { + JERRY_ASSERT ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) + && !(context_p->status_flags & PARSER_DISALLOW_YIELD)); + + parser_check_assignment_expr (context_p); + lexer_next_token (context_p); + + if (!lexer_check_yield_no_arg (context_p)) + { + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + } + else + { + parser_emit_cbc (context_p, CBC_PUSH_UNDEFINED); + } + + parser_emit_cbc_ext (context_p, CBC_EXT_YIELD); + parser_emit_cbc_ext (context_p, CBC_EXT_CONTINUE_EXEC); + + return (context_p->token.type != LEXER_RIGHT_PAREN + && context_p->token.type != LEXER_COMMA); + } #endif /* ENABLED (JERRY_ES2015) */ default: { diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index 7ac4819d2..74c52edab 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -55,32 +55,33 @@ typedef enum PARSER_INSIDE_WITH = (1u << 9), /**< code block is inside a with statement */ PARSER_RESOLVE_BASE_FOR_CALLS = (1u << 10), /**< the this object must be resolved when * a function without a base object is called */ - PARSER_HAS_INITIALIZED_VARS = (1u << 11), /**< a CBC_INITIALIZE_VARS instruction must be emitted */ - PARSER_HAS_LATE_LIT_INIT = (1u << 12), /**< allocate memory for this string after + PARSER_HAS_LATE_LIT_INIT = (1u << 11), /**< allocate memory for this string after * the local parser data is freed */ - PARSER_NO_END_LABEL = (1u << 13), /**< return instruction must be inserted + PARSER_NO_END_LABEL = (1u << 12), /**< return instruction must be inserted * after the last byte code */ - PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 14), /**< pending (unsent) breakpoint + PARSER_DEBUGGER_BREAKPOINT_APPENDED = (1u << 13), /**< pending (unsent) breakpoint * info is available */ #if ENABLED (JERRY_ES2015) - PARSER_INSIDE_BLOCK = (1u << 15), /**< script has a lexical environment for let and const */ - PARSER_IS_ARROW_FUNCTION = (1u << 16), /**< an arrow function is parsed */ - PARSER_ARROW_PARSE_ARGS = (1u << 17), /**< parse the argument list of an arrow function */ - PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM = (1u << 18), /**< function has a non simple parameter */ - PARSER_FUNCTION_HAS_REST_PARAM = (1u << 19), /**< function has rest parameter */ + PARSER_INSIDE_BLOCK = (1u << 14), /**< script has a lexical environment for let and const */ + PARSER_IS_ARROW_FUNCTION = (1u << 15), /**< an arrow function is parsed */ + PARSER_ARROW_PARSE_ARGS = (1u << 16), /**< parse the argument list of an arrow function */ + PARSER_IS_GENERATOR_FUNCTION = (1u << 17), /**< a generator function is parsed */ + PARSER_DISALLOW_YIELD = (1u << 18), /**< throw SyntaxError for yield expression */ + PARSER_FUNCTION_HAS_NON_SIMPLE_PARAM = (1u << 19), /**< function has a non simple parameter */ + PARSER_FUNCTION_HAS_REST_PARAM = (1u << 20), /**< function has rest parameter */ /* These four status flags must be in this order. See PARSER_CLASS_PARSE_OPTS_OFFSET. */ - PARSER_CLASS_CONSTRUCTOR = (1u << 20), /**< a class constructor is parsed (this value must be kept in + PARSER_CLASS_CONSTRUCTOR = (1u << 21), /**< a class constructor is parsed (this value must be kept in * in sync with ECMA_PARSE_CLASS_CONSTRUCTOR) */ - PARSER_CLASS_HAS_SUPER = (1u << 21), /**< class has super reference */ - PARSER_CLASS_IMPLICIT_SUPER = (1u << 22), /**< class has implicit parent class */ - PARSER_CLASS_STATIC_FUNCTION = (1u << 23), /**< this function is a static class method */ - PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 24), /**< super property call or assignment */ - PARSER_IS_EVAL = (1u << 25), /**< eval code */ + PARSER_CLASS_HAS_SUPER = (1u << 22), /**< class has super reference */ + PARSER_CLASS_IMPLICIT_SUPER = (1u << 23), /**< class has implicit parent class */ + PARSER_CLASS_STATIC_FUNCTION = (1u << 24), /**< this function is a static class method */ + PARSER_CLASS_SUPER_PROP_REFERENCE = (1u << 25), /**< super property call or assignment */ + PARSER_IS_EVAL = (1u << 26), /**< eval code */ #endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) - PARSER_IS_MODULE = (1u << 26), /**< an export / import keyword is encountered */ - PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 27), /**< parsing a function or class default export */ - PARSER_MODULE_STORE_IDENT = (1u << 28), /**< store identifier of the current export statement */ + PARSER_IS_MODULE = (1u << 27), /**< an export / import keyword is encountered */ + PARSER_MODULE_DEFAULT_CLASS_OR_FUNC = (1u << 28), /**< parsing a function or class default export */ + PARSER_MODULE_STORE_IDENT = (1u << 29), /**< store identifier of the current export statement */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ #ifndef JERRY_NDEBUG PARSER_SCANNING_SUCCESSFUL = (1u << 30), /**< scanning process was successful */ @@ -624,6 +625,7 @@ uint8_t lexer_consume_next_character (parser_context_t *context_p); void lexer_skip_empty_statements (parser_context_t *context_p); bool lexer_check_arrow (parser_context_t *context_p); bool lexer_check_arrow_param (parser_context_t *context_p); +bool lexer_check_yield_no_arg (parser_context_t *context_p); #endif /* ENABLED (JERRY_ES2015) */ void lexer_parse_string (parser_context_t *context_p); void lexer_expect_identifier (parser_context_t *context_p, uint8_t literal_type); diff --git a/jerry-core/parser/js/js-parser-statm.c b/jerry-core/parser/js/js-parser-statm.c index 31b5d7fea..8d7972a25 100644 --- a/jerry-core/parser/js/js-parser-statm.c +++ b/jerry-core/parser/js/js-parser-statm.c @@ -627,9 +627,6 @@ parser_parse_var_statement (parser_context_t *context_p) /**< context */ static void parser_parse_function_statement (parser_context_t *context_p) /**< context */ { - uint32_t status_flags; - lexer_literal_t *literal_p; - JERRY_ASSERT (context_p->token.type == LEXER_KEYW_FUNCTION); #if ENABLED (JERRY_ES2015) @@ -646,6 +643,16 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ parser_line_counter_t debugger_column = context_p->token.column; #endif /* ENABLED (JERRY_DEBUGGER) */ +#if ENABLED (JERRY_ES2015) + bool is_generator_function = false; + + if (lexer_check_next_character (context_p, LIT_CHAR_ASTERISK)) + { + is_generator_function = true; + lexer_consume_next_character (context_p); + } +#endif /* ENABLED (JERRY_ES2015) */ + lexer_expect_identifier (context_p, LEXER_NEW_IDENT_LITERAL); JERRY_ASSERT (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL); @@ -663,13 +670,19 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ context_p->status_flags &= (uint32_t) ~(PARSER_MODULE_STORE_IDENT); #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ - status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; + uint32_t status_flags = PARSER_IS_FUNCTION | PARSER_IS_CLOSURE; if (context_p->lit_object.type != LEXER_LITERAL_OBJECT_ANY) { JERRY_ASSERT (context_p->lit_object.type == LEXER_LITERAL_OBJECT_EVAL || context_p->lit_object.type == LEXER_LITERAL_OBJECT_ARGUMENTS); status_flags |= PARSER_HAS_NON_STRICT_ARG; } +#if ENABLED (JERRY_ES2015) + if (is_generator_function) + { + status_flags |= PARSER_IS_GENERATOR_FUNCTION | PARSER_DISALLOW_YIELD; + } +#endif /* ENABLED (JERRY_ES2015) */ #if ENABLED (JERRY_DEBUGGER) if (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) @@ -700,7 +713,7 @@ parser_parse_function_statement (parser_context_t *context_p) /**< context */ JERRY_ASSERT (scope_stack_p[1].map_from == PARSER_SCOPE_STACK_FUNC); - literal_p = PARSER_GET_LITERAL ((size_t) scope_stack_p[1].map_to); + lexer_literal_t *literal_p = PARSER_GET_LITERAL ((size_t) scope_stack_p[1].map_to); JERRY_ASSERT ((literal_p->type == LEXER_UNUSED_LITERAL || literal_p->type == LEXER_FUNCTION_LITERAL) && literal_p->status_flags == 0); diff --git a/jerry-core/parser/js/js-parser-util.c b/jerry-core/parser/js/js-parser-util.c index d8ea887d3..a1d6217fb 100644 --- a/jerry-core/parser/js/js-parser-util.c +++ b/jerry-core/parser/js/js-parser-util.c @@ -932,6 +932,12 @@ parser_error_to_string (parser_error_t error) /**< error code */ { return "Arguments is not allowed to be used here in strict mode."; } +#if ENABLED (JERRY_ES2015) + case PARSER_ERR_YIELD_NOT_ALLOWED: + { + return "Incorrect use of yield keyword."; + } +#endif /* ENABLED (JERRY_ES2015) */ case PARSER_ERR_DELETE_IDENT_NOT_ALLOWED: { return "Deleting identifier is not allowed in strict mode."; diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 572cb6fcf..85c795563 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -679,6 +679,11 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code { JERRY_DEBUG_MSG (",constructor"); } + + if (compiled_code_p->status_flags & CBC_CODE_FLAGS_GENERATOR) + { + JERRY_DEBUG_MSG (",generator"); + } #endif /* ENABLED (JERRY_ES2015) */ JERRY_DEBUG_MSG ("]\n"); @@ -1276,6 +1281,11 @@ parser_post_processing (parser_context_t *context_p) /**< context */ compiled_code_p->status_flags |= CBC_CODE_FLAGS_CONSTRUCTOR; } + if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) + { + compiled_code_p->status_flags |= CBC_CODE_FLAGS_GENERATOR; + } + if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_REST_PARAMETER; @@ -1622,6 +1632,16 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ if (context_p->token.type == end_type) { +#if ENABLED (JERRY_ES2015) + if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) + { + parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR); + parser_emit_cbc_ext (context_p, CBC_EXT_CONTINUE_EXEC); + parser_emit_cbc (context_p, CBC_POP); + } + + context_p->status_flags &= (uint32_t) ~PARSER_DISALLOW_YIELD; +#endif /* ENABLED (JERRY_ES2015) */ scanner_create_variables (context_p, SCANNER_CREATE_VARS_NO_OPTS); return; } @@ -1786,6 +1806,17 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ parser_raise_error (context_p, error); } +#if ENABLED (JERRY_ES2015) + if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) + { + parser_emit_cbc_ext (context_p, CBC_EXT_CREATE_GENERATOR); + parser_emit_cbc_ext (context_p, CBC_EXT_CONTINUE_EXEC); + parser_emit_cbc (context_p, CBC_POP); + } + + context_p->status_flags &= (uint32_t) ~PARSER_DISALLOW_YIELD; +#endif /* ENABLED (JERRY_ES2015) */ + scanner_revert_active (context_p); scanner_create_variables (context_p, SCANNER_CREATE_VARS_IS_FUNCTION_BODY); } /* parser_parse_function_arguments */ diff --git a/jerry-core/parser/js/js-parser.h b/jerry-core/parser/js/js-parser.h index 8ad734609..74d40d941 100644 --- a/jerry-core/parser/js/js-parser.h +++ b/jerry-core/parser/js/js-parser.h @@ -73,6 +73,9 @@ typedef enum PARSER_ERR_STRICT_IDENT_NOT_ALLOWED, /**< identifier name is reserved in strict mode */ PARSER_ERR_EVAL_NOT_ALLOWED, /**< eval is not allowed here in strict mode */ PARSER_ERR_ARGUMENTS_NOT_ALLOWED, /**< arguments is not allowed here in strict mode */ +#if ENABLED (JERRY_ES2015) + PARSER_ERR_YIELD_NOT_ALLOWED, /**< yield keyword is not allowed */ +#endif /* ENABLED (JERRY_ES2015) */ PARSER_ERR_DELETE_IDENT_NOT_ALLOWED, /**< identifier delete is not allowed in strict mode */ PARSER_ERR_EVAL_CANNOT_ASSIGNED, /**< eval cannot be assigned in strict mode */ PARSER_ERR_ARGUMENTS_CANNOT_ASSIGNED, /**< arguments cannot be assigned in strict mode */ diff --git a/jerry-core/parser/js/js-scanner-internal.h b/jerry-core/parser/js/js-scanner-internal.h index 7bad87245..fee847e42 100644 --- a/jerry-core/parser/js/js-scanner-internal.h +++ b/jerry-core/parser/js/js-scanner-internal.h @@ -175,6 +175,9 @@ typedef enum #if ENABLED (JERRY_ES2015_MODULE_SYSTEM) SCANNER_LITERAL_POOL_IN_EXPORT = (1 << 6), /**< the declared variables are exported by the module system */ #endif /* ENABLED (JERRY_ES2015_MODULE_SYSTEM) */ +#if ENABLED (JERRY_ES2015) + SCANNER_LITERAL_POOL_GENERATOR = (1 << 7), /**< generator function */ +#endif /* ENABLED (JERRY_ES2015) */ } scanner_literal_pool_flags_t; /** diff --git a/jerry-core/parser/js/js-scanner-util.c b/jerry-core/parser/js/js-scanner-util.c index a9f9f297c..c8c3e6b6c 100644 --- a/jerry-core/parser/js/js-scanner-util.c +++ b/jerry-core/parser/js/js-scanner-util.c @@ -402,10 +402,13 @@ scanner_push_literal_pool (parser_context_t *context_p, /**< context */ JERRY_ASSERT (prev_literal_pool_p != NULL); status_flags |= SCANNER_LITERAL_POOL_NO_ARGUMENTS; - if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_IN_WITH) - { - status_flags |= SCANNER_LITERAL_POOL_IN_WITH; - } +#if ENABLED (JERRY_ES2015) + const uint16_t copied_flags = SCANNER_LITERAL_POOL_IN_WITH | SCANNER_LITERAL_POOL_GENERATOR; +#else /* !ENABLED (JERRY_ES2015) */ + const uint16_t copied_flags = SCANNER_LITERAL_POOL_IN_WITH; +#endif /* ENABLED (JERRY_ES2015) */ + + status_flags |= (uint16_t) (prev_literal_pool_p->status_flags & copied_flags); } parser_list_init (&literal_pool_p->literal_pool, @@ -775,6 +778,20 @@ scanner_pop_literal_pool (parser_context_t *context_p, /**< context */ prev_literal_pool_p->no_declarations = (uint16_t) no_declarations; } +#if ENABLED (JERRY_ES2015) + if (is_function && prev_literal_pool_p != NULL) + { + if (prev_literal_pool_p->status_flags & SCANNER_LITERAL_POOL_GENERATOR) + { + context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; + } + else + { + context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; + } + } +#endif /* ENABLED (JERRY_ES2015) */ + scanner_context_p->active_literal_pool_p = literal_pool_p->prev_p; parser_list_free (&literal_pool_p->literal_pool); diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index ae0d3d73a..76e793f5e 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -179,7 +179,9 @@ scanner_process_arrow (parser_context_t *context_p, /**< context */ scanner_literal_pool_t *literal_pool_p = scanner_context_p->active_literal_pool_p; literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_FUNCTION_WITHOUT_ARGUMENTS; - literal_pool_p->status_flags &= (uint16_t) ~SCANNER_LITERAL_POOL_IN_WITH; + literal_pool_p->status_flags &= (uint16_t) ~(SCANNER_LITERAL_POOL_IN_WITH | SCANNER_LITERAL_POOL_GENERATOR); + + context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; scanner_filter_arguments (context_p, scanner_context_p); @@ -208,6 +210,8 @@ scanner_process_simple_arrow (parser_context_t *context_p, /**< context */ PARSER_PLUS_EQUAL_LC (context_p->column, 2); context_p->token.flags = (uint8_t) (context_p->token.flags & ~LEXER_NO_SKIP_SPACES); + context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; + scanner_check_arrow_body (context_p, scanner_context_p); } /* scanner_process_simple_arrow */ @@ -480,7 +484,21 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ { scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); +#if ENABLED (JERRY_ES2015) + context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; +#endif /* ENABLED (JERRY_ES2015) */ + lexer_next_token (context_p); + +#if ENABLED (JERRY_ES2015) + if (context_p->token.type == LEXER_MULTIPLY) + { + scanner_context_p->active_literal_pool_p->status_flags |= SCANNER_LITERAL_POOL_GENERATOR; + context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; + lexer_next_token (context_p); + } +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->token.type == LEXER_LITERAL && context_p->token.lit_location.type == LEXER_IDENT_LITERAL) { @@ -609,6 +627,19 @@ scanner_scan_primary_expression (parser_context_t *context_p, /**< context */ #endif /* ENABLED (JERRY_ES2015) */ break; } +#if ENABLED (JERRY_ES2015) + case LEXER_KEYW_YIELD: + { + lexer_next_token (context_p); + + if (lexer_check_yield_no_arg (context_p)) + { + scanner_context_p->mode = SCAN_MODE_PRIMARY_EXPRESSION_END; + } + + return SCAN_KEEP_TOKEN; + } +#endif /* ENABLED (JERRY_ES2015) */ case LEXER_RIGHT_PAREN: { if (stack_top == SCAN_STACK_PAREN_EXPRESSION) @@ -1590,6 +1621,23 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ case LEXER_KEYW_FUNCTION: { lexer_next_token (context_p); + + uint16_t status_flags = SCANNER_LITERAL_POOL_FUNCTION; + +#if ENABLED (JERRY_ES2015) + if (context_p->token.type == LEXER_MULTIPLY) + { + status_flags |= SCANNER_LITERAL_POOL_GENERATOR; + lexer_next_token (context_p); + /* This flag should be set after the function name is read. */ + context_p->status_flags |= PARSER_IS_GENERATOR_FUNCTION; + } + else + { + context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; + } +#endif /* ENABLED (JERRY_ES2015) */ + if (context_p->token.type != LEXER_LITERAL || context_p->token.lit_location.type != LEXER_IDENT_LITERAL) { @@ -1618,7 +1666,7 @@ scanner_scan_statement (parser_context_t *context_p, /**< context */ literal_p->type |= SCANNER_LITERAL_IS_VAR | SCANNER_LITERAL_IS_FUNC; #endif /* ENABLED (JERRY_ES2015) */ - scanner_push_literal_pool (context_p, scanner_context_p, SCANNER_LITERAL_POOL_FUNCTION); + scanner_push_literal_pool (context_p, scanner_context_p, status_flags); scanner_context_p->mode = SCAN_MODE_FUNCTION_ARGUMENTS; parser_stack_push_uint8 (context_p, SCAN_STACK_FUNCTION_STATEMENT); @@ -3102,6 +3150,7 @@ scan_completed: #if ENABLED (JERRY_ES2015) JERRY_ASSERT (scanner_context.active_binding_list_p == NULL); + JERRY_ASSERT (!(context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION)); #endif /* ENABLED (JERRY_ES2015) */ JERRY_ASSERT (scanner_context.active_literal_pool_p == NULL); @@ -3147,6 +3196,10 @@ scan_completed: } } PARSER_TRY_END + +#if ENABLED (JERRY_ES2015) + context_p->status_flags &= (uint32_t) ~PARSER_IS_GENERATOR_FUNCTION; +#endif /* ENABLED (JERRY_ES2015) */ } PARSER_TRY_END diff --git a/jerry-core/profiles/README.md b/jerry-core/profiles/README.md index 6a5aa41cc..8d5e01533 100644 --- a/jerry-core/profiles/README.md +++ b/jerry-core/profiles/README.md @@ -118,12 +118,15 @@ defined to `1`. Enables or disable the [module system](http://www.ecma-international.org/ecma-262/6.0/#sec-modules) language element. * `JERRY_ES2015`: Enables or disable all of the implemented [ECMAScript2015 features](http://www.ecma-international.org/ecma-262/6.0/). * [arrow functions](http://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions) language element. - * [symbol](https://www.ecma-international.org/ecma-262/6.0/#sec-symbol-objects) language element. * [class](https://www.ecma-international.org/ecma-262/6.0/#sec-class-definitions) language element. - * [for of](https://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements) language element. * [default value](http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions) for formal parameters. - * [rest parameter](http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions) language element. + * [destructuring assignment](http://www.ecma-international.org/ecma-262/6.0/#sec-destructuring-assignment) language element. + * [destructuring binding pattern](http://www.ecma-international.org/ecma-262/6.0/#sec-destructuring-binding-patterns) declarations. * [enhanced object initializer](http://www.ecma-international.org/ecma-262/6.0/#sec-object-initializer) language element. + * [for-of](https://www.ecma-international.org/ecma-262/6.0/#sec-for-in-and-for-of-statements) language element. + * [generator functions](http://www.ecma-international.org/ecma-262/6.0/#sec-generator-function-definitions) language element. + * [rest parameter](http://www.ecma-international.org/ecma-262/6.0/#sec-function-definitions) language element. + * [symbol](https://www.ecma-international.org/ecma-262/6.0/#sec-symbol-objects) language element. * [template strings](http://www.ecma-international.org/ecma-262/6.0/#sec-static-semantics-templatestrings) language element. This option is evaulated first, any other `JERRY_ES2015_` defines will override that specific entry. Equivalent with setting the following defines to the `JERRY_ES2015` value: diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index a0c5de478..2a0bf04ee 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -30,6 +30,7 @@ #include "jcontext.h" #include "opcodes.h" #include "vm-defines.h" +#include "vm-stack.h" /** \addtogroup vm Virtual machine * @{ @@ -280,6 +281,7 @@ opfunc_for_in (ecma_value_t left_value, /**< left value */ } /* opfunc_for_in */ #if ENABLED (JERRY_ES2015) + /** * 'VM_OC_APPEND_ARRAY' opcode handler specialized for spread objects * @@ -454,6 +456,7 @@ opfunc_spread_arguments (ecma_value_t *stack_top_p, /**< pointer to the current return buff_p; } /* opfunc_spread_arguments */ + #endif /* ENABLED (JERRY_ES2015) */ /** @@ -539,6 +542,176 @@ opfunc_append_array (ecma_value_t *stack_top_p, /**< current stack top */ return ECMA_VALUE_EMPTY; } /* opfunc_append_array */ +#if ENABLED (JERRY_ES2015) + +/** + * Create an executable object using the current frame context + * + * @return executable object + */ +ecma_value_t +opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ +{ + const ecma_compiled_code_t *bytecode_header_p = frame_ctx_p->bytecode_header_p; + size_t size; + + ecma_bytecode_ref ((ecma_compiled_code_t *) bytecode_header_p); + + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; + size = ((size_t) args_p->register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; + size = ((size_t) args_p->register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); + } + + size_t total_size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); + + ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE), + total_size, + ECMA_OBJECT_TYPE_CLASS); + + vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; + + executable_object_p->extended_object.u.class_prop.class_id = LIT_MAGIC_STRING_GENERATOR_UL; + executable_object_p->extended_object.u.class_prop.extra_info = 0; + + JERRY_ASSERT (!frame_ctx_p->is_eval_code); + JERRY_ASSERT (frame_ctx_p->context_depth == 0); + + vm_frame_ctx_t *new_frame_ctx_p = &(executable_object_p->frame_ctx); + *new_frame_ctx_p = *frame_ctx_p; + + /* The old register values are discarded. */ + ecma_value_t *new_registers_p = VM_GET_REGISTERS (new_frame_ctx_p); + memcpy (new_registers_p, VM_GET_REGISTERS (frame_ctx_p), size); + + size_t stack_top = (size_t) (frame_ctx_p->stack_top_p - VM_GET_REGISTERS (frame_ctx_p)); + ecma_value_t *new_stack_top_p = new_registers_p + stack_top; + + new_frame_ctx_p->stack_top_p = new_stack_top_p; + + /* Initial state is "not running", so all object references are released. */ + + while (new_registers_p < new_stack_top_p) + { + ecma_deref_if_object (*new_registers_p++); + } + + new_frame_ctx_p->this_binding = ecma_copy_value_if_not_object (new_frame_ctx_p->this_binding); + + JERRY_CONTEXT (vm_top_context_p) = new_frame_ctx_p->prev_context_p; + + return ecma_make_object_value (object_p); +} /* opfunc_create_executable_object */ + +/** + * Resume the execution of an inactive executable object + * + * @return value provided by the execution + */ +ecma_value_t +opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, /**< executable object */ + ecma_value_t value) /**< value pushed onto the stack */ +{ + const ecma_compiled_code_t *bytecode_header_p = executable_object_p->frame_ctx.bytecode_header_p; + ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); + ecma_value_t *register_end_p; + + if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) + { + cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; + register_end_p = register_p + args_p->register_end; + } + else + { + cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; + register_end_p = register_p + args_p->register_end; + } + + while (register_p < register_end_p) + { + ecma_ref_if_object (*register_p++); + } + + if (executable_object_p->frame_ctx.context_depth > 0) + { + vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p, + executable_object_p->frame_ctx.context_depth, + register_p, + true); + + register_p += executable_object_p->frame_ctx.context_depth; + } + + ecma_value_t *stack_top_p = executable_object_p->frame_ctx.stack_top_p; + + while (register_p < stack_top_p) + { + ecma_ref_if_object (*register_p++); + } + + uint8_t *byte_code_p = executable_object_p->frame_ctx.byte_code_p; + + JERRY_ASSERT (byte_code_p[0] == CBC_EXT_OPCODE && byte_code_p[1] == CBC_EXT_CONTINUE_EXEC); + + *register_p++ = ecma_copy_value (value); + executable_object_p->frame_ctx.stack_top_p = register_p; + + JERRY_ASSERT (ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p->extended_object.u.class_prop.extra_info)); + + executable_object_p->extended_object.u.class_prop.extra_info |= ECMA_EXECUTABLE_OBJECT_RUNNING; + + executable_object_p->frame_ctx.prev_context_p = JERRY_CONTEXT (vm_top_context_p); + JERRY_CONTEXT (vm_top_context_p) = &executable_object_p->frame_ctx; + + ecma_value_t result = vm_execute (&executable_object_p->frame_ctx); + + executable_object_p->extended_object.u.class_prop.extra_info &= (uint16_t) ~ECMA_EXECUTABLE_OBJECT_RUNNING; + + if (executable_object_p->frame_ctx.call_operation != VM_EXEC_RETURN) + { + JERRY_ASSERT (executable_object_p->frame_ctx.call_operation == VM_NO_EXEC_OP); + + /* All resources are released. */ + executable_object_p->extended_object.u.class_prop.extra_info |= ECMA_EXECUTABLE_OBJECT_COMPLETED; + return result; + } + + JERRY_CONTEXT (vm_top_context_p) = executable_object_p->frame_ctx.prev_context_p; + + register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); + + while (register_p < register_end_p) + { + ecma_deref_if_object (*register_p++); + } + + if (executable_object_p->frame_ctx.context_depth > 0) + { + vm_ref_lex_env_chain (executable_object_p->frame_ctx.lex_env_p, + executable_object_p->frame_ctx.context_depth, + register_p, + false); + + register_p += executable_object_p->frame_ctx.context_depth; + } + + stack_top_p = executable_object_p->frame_ctx.stack_top_p; + + while (register_p < stack_top_p) + { + ecma_deref_if_object (*register_p++); + } + + return result; +} /* opfunc_resume_executable_object */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * @} * @} diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 60a19e4b0..d6bb01aef 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -101,12 +101,20 @@ vm_op_delete_var (ecma_value_t name_literal, ecma_object_t *lex_env_p); ecma_collection_t * opfunc_for_in (ecma_value_t left_value, ecma_value_t *result_obj_p); +#if ENABLED (JERRY_ES2015) +ecma_collection_t * +opfunc_spread_arguments (ecma_value_t *stack_top_p, uint8_t argument_list_len); +#endif /* ENABLED (JERRY_ES2015) */ + ecma_value_t opfunc_append_array (ecma_value_t *stack_top_p, uint16_t values_length); #if ENABLED (JERRY_ES2015) -ecma_collection_t * -opfunc_spread_arguments (ecma_value_t *stack_top_p, uint8_t argument_list_len); +ecma_value_t +opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p); + +ecma_value_t +opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ecma_value_t value); #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/vm/vm-defines.h b/jerry-core/vm/vm-defines.h index fb1d2b1c8..15d4c1a25 100644 --- a/jerry-core/vm/vm-defines.h +++ b/jerry-core/vm/vm-defines.h @@ -64,12 +64,21 @@ typedef struct vm_frame_ctx_t /** * Get register list corresponding to the frame context. */ -#define VM_GET_REGISTERS(frame_ctx_p) ((ecma_value_t *) (frame_ctx_p + 1)) +#define VM_GET_REGISTERS(frame_ctx_p) ((ecma_value_t *) ((frame_ctx_p) + 1)) /** * Read or write a specific register. */ -#define VM_GET_REGISTER(frame_ctx_p, i) (((ecma_value_t *) (frame_ctx_p + 1))[i]) +#define VM_GET_REGISTER(frame_ctx_p, i) (((ecma_value_t *) ((frame_ctx_p) + 1))[i]) + +/** + * Generator frame context. + */ +typedef struct +{ + ecma_extended_object_t extended_object; /**< extended object part */ + vm_frame_ctx_t frame_ctx; /**< frame context part */ +} vm_executable_object_t; /** * @} diff --git a/jerry-core/vm/vm-stack.c b/jerry-core/vm/vm-stack.c index 654a5babf..b4e1b6d85 100644 --- a/jerry-core/vm/vm-stack.c +++ b/jerry-core/vm/vm-stack.c @@ -56,35 +56,21 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ case VM_CONTEXT_FINALLY_RETURN: { ecma_free_value (vm_stack_top_p[-2]); - - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); - vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; - break; + /* FALLTHRU */ } case VM_CONTEXT_FINALLY_JUMP: case VM_CONTEXT_TRY: + case VM_CONTEXT_CATCH: { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_TRY_CONTEXT_STACK_ALLOCATION; break; } - case VM_CONTEXT_CATCH: - { - JERRY_ASSERT (PARSER_TRY_CONTEXT_STACK_ALLOCATION > PARSER_WITH_CONTEXT_STACK_ALLOCATION); - - const uint16_t size_diff = PARSER_TRY_CONTEXT_STACK_ALLOCATION - PARSER_WITH_CONTEXT_STACK_ALLOCATION; - - VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, size_diff); - vm_stack_top_p -= size_diff; - /* FALLTHRU */ - } #if ENABLED (JERRY_ES2015) case VM_CONTEXT_BLOCK: -#endif /* ENABLED (JERRY_ES2015) */ - case VM_CONTEXT_WITH: -#if ENABLED (JERRY_ES2015) case VM_CONTEXT_SUPER_CLASS: #endif /* ENABLED (JERRY_ES2015) */ + case VM_CONTEXT_WITH: { VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_WITH_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_WITH_CONTEXT_STACK_ALLOCATION; @@ -116,7 +102,7 @@ vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ ecma_collection_destroy (collection_p); - ecma_deref_object (ecma_get_object_from_value (vm_stack_top_p[-4])); + ecma_free_value (vm_stack_top_p[-4]); VM_MINUS_EQUAL_U16 (frame_ctx_p->context_depth, PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION); vm_stack_top_p -= PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; @@ -296,6 +282,109 @@ vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ return false; } /* vm_stack_find_finally */ +#if ENABLED (JERRY_ES2015) + +/** + * Get the offsets of ecma values from the specified item of a context. + * + * @return array of offsets, last item represents the size of the context item + */ +uint32_t +vm_get_context_value_offsets (ecma_value_t *context_item_p) /**< any item of a context */ +{ + switch (VM_GET_CONTEXT_TYPE (context_item_p[-1])) + { + case VM_CONTEXT_FINALLY_THROW: + case VM_CONTEXT_FINALLY_RETURN: + { + return (2 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_TRY_CONTEXT_STACK_ALLOCATION; + } + case VM_CONTEXT_FINALLY_JUMP: + case VM_CONTEXT_TRY: + case VM_CONTEXT_CATCH: + { + return PARSER_TRY_CONTEXT_STACK_ALLOCATION; + } +#if ENABLED (JERRY_ES2015) + case VM_CONTEXT_BLOCK: + case VM_CONTEXT_SUPER_CLASS: +#endif /* ENABLED (JERRY_ES2015) */ + case VM_CONTEXT_WITH: + { + return PARSER_WITH_CONTEXT_STACK_ALLOCATION; + } +#if ENABLED (JERRY_ES2015) + case VM_CONTEXT_FOR_OF: + { + return ((3 << (VM_CONTEXT_OFFSET_SHIFT * 2)) + | (2 << (VM_CONTEXT_OFFSET_SHIFT)) + | PARSER_FOR_OF_CONTEXT_STACK_ALLOCATION); + } +#endif /* ENABLED (JERRY_ES2015) */ + default: + { + return (4 << (VM_CONTEXT_OFFSET_SHIFT)) | PARSER_FOR_IN_CONTEXT_STACK_ALLOCATION; + } + } +} /* vm_get_context_value_offsets */ + +/** + * Ref / deref lexical environments in the chain using the current context. + */ +void +vm_ref_lex_env_chain (ecma_object_t *lex_env_p, /**< top of lexical environment */ + uint16_t context_depth, /**< depth of function context */ + ecma_value_t *context_end_p, /**< end of function context */ + bool do_ref) /**< ref or deref lexical environments */ +{ + ecma_value_t *context_top_p = context_end_p + context_depth; + JERRY_ASSERT (context_top_p > context_end_p); + + do + { + if (context_top_p[-1] & VM_CONTEXT_HAS_LEX_ENV) + { + JERRY_ASSERT (lex_env_p->u2.outer_reference_cp != JMEM_CP_NULL); + ecma_object_t *next_lex_env_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, lex_env_p->u2.outer_reference_cp); + + if (do_ref) + { + ecma_ref_object (lex_env_p); + } + else + { + ecma_deref_object (lex_env_p); + } + + lex_env_p = next_lex_env_p; + } + + uint32_t offsets = vm_get_context_value_offsets (context_top_p); + + while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) + { + int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); + + if (do_ref) + { + ecma_ref_if_object (context_top_p[offset]); + } + else + { + ecma_deref_if_object (context_top_p[offset]); + } + + offsets >>= VM_CONTEXT_OFFSET_SHIFT; + } + + JERRY_ASSERT (context_top_p >= context_end_p + offsets); + context_top_p -= offsets; + } + while (context_top_p > context_end_p); +} /* vm_ref_lex_env_chain */ + +#endif /* ENABLED (JERRY_ES2015) */ + /** * @} * @} diff --git a/jerry-core/vm/vm-stack.h b/jerry-core/vm/vm-stack.h index 16f0212e4..cb47177b7 100644 --- a/jerry-core/vm/vm-stack.h +++ b/jerry-core/vm/vm-stack.h @@ -79,9 +79,27 @@ typedef enum #define VM_CONTEXT_IS_FINALLY(context_type) \ ((context_type) <= VM_CONTEXT_FINALLY_RETURN) +/** + * Shift needs to be applied to get the next item of the offset array. + */ +#define VM_CONTEXT_OFFSET_SHIFT 4 + +/** + * Checks whether an offset is available. + */ +#define VM_CONTEXT_HAS_NEXT_OFFSET(offsets) ((offsets) >= (1 << VM_CONTEXT_OFFSET_SHIFT)) + +/** + * Gets the next offset from the offset array. + */ +#define VM_CONTEXT_GET_NEXT_OFFSET(offsets) (-((int32_t) ((offsets) & ((1 << VM_CONTEXT_OFFSET_SHIFT) - 1)))) + ecma_value_t *vm_stack_context_abort (vm_frame_ctx_t *frame_ctx_p, ecma_value_t *vm_stack_top_p); bool vm_stack_find_finally (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top_ref_p, vm_stack_context_type_t finally_type, uint32_t search_limit); +uint32_t vm_get_context_value_offsets (ecma_value_t *context_item_p); +void vm_ref_lex_env_chain (ecma_object_t *lex_env_p, uint16_t context_depth, + ecma_value_t *context_end_p, bool do_ref); /** * @} diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 40de76bb3..b2bb619e5 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -2062,7 +2062,47 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ frame_ctx_p->stack_top_p = stack_top_p; return ECMA_VALUE_UNDEFINED; } - #endif /* ENABLED (JERRY_ES2015) */ + case VM_OC_CREATE_GENERATOR: + { + frame_ctx_p->call_operation = VM_EXEC_RETURN; + frame_ctx_p->byte_code_p = byte_code_p; + frame_ctx_p->stack_top_p = stack_top_p; + return opfunc_create_executable_object (frame_ctx_p); + } + case VM_OC_YIELD: + { + frame_ctx_p->call_operation = VM_EXEC_RETURN; + frame_ctx_p->byte_code_p = byte_code_p; + frame_ctx_p->stack_top_p = stack_top_p; + return left_value; + } + case VM_OC_CONTINUE_EXEC: + { + if (JERRY_UNLIKELY (frame_ctx_p->call_operation == ECMA_GENERATOR_RETURN)) + { + ecma_value_t *stack_bottom_p = VM_GET_REGISTERS (frame_ctx_p) + register_end + frame_ctx_p->context_depth; + + result = *(--stack_top_p); + + while (stack_top_p > stack_bottom_p) + { + ecma_fast_free_value (*(--stack_top_p)); + } + + goto error; + } + + if (JERRY_UNLIKELY (frame_ctx_p->call_operation == ECMA_GENERATOR_THROW)) + { + JERRY_CONTEXT (error_value) = *(--stack_top_p); + JERRY_CONTEXT (status_flags) |= ECMA_STATUS_EXCEPTION; + + result = ECMA_VALUE_ERROR; + goto error; + } + continue; + } +#endif /* ENABLED (JERRY_ES2015) */ case VM_OC_PUSH_ELISON: { *stack_top_p++ = ECMA_VALUE_ARRAY_HOLE; @@ -3804,8 +3844,8 @@ free_both_values: free_left_value: ecma_fast_free_value (left_value); } -error: +error: ecma_fast_free_value (left_value); ecma_fast_free_value (right_value); @@ -4031,7 +4071,7 @@ vm_init_exec (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ * * @return ecma value */ -static ecma_value_t JERRY_ATTR_NOINLINE +ecma_value_t JERRY_ATTR_NOINLINE vm_execute (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { while (true) @@ -4056,6 +4096,10 @@ vm_execute (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ vm_spread_operation (frame_ctx_p); break; } + case VM_EXEC_RETURN: + { + return completion_value; + } #endif /* ENABLED (JERRY_ES2015) */ case VM_EXEC_CONSTRUCT: { diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index e9976d301..80496c2eb 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -255,6 +255,9 @@ typedef enum VM_OC_REST_INITIALIZER, /**< create rest object inside an array pattern */ VM_OC_INITIALIZER_PUSH_PROP, /**< push property for object initializer */ VM_OC_SPREAD_ARGUMENTS, /**< perform function call/construct with spreaded arguments */ + VM_OC_CREATE_GENERATOR, /**< create a generator object */ + VM_OC_YIELD, /**< yield operation */ + VM_OC_CONTINUE_EXEC, /**< first byte code after a function is resumed */ #endif /* ENABLED (JERRY_ES2015) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -305,6 +308,9 @@ typedef enum 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 */ VM_OC_SPREAD_ARGUMENTS = VM_OC_NONE, /**< perform function call/construct with spreaded arguments */ + VM_OC_CREATE_GENERATOR = VM_OC_NONE, /**< create a generator object */ + VM_OC_YIELD = VM_OC_NONE, /**< yield operation */ + VM_OC_CONTINUE_EXEC = VM_OC_NONE, /**< first byte code after a function is resumed */ #endif /* !ENABLED (JERRY_ES2015) */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ @@ -388,6 +394,7 @@ typedef enum VM_EXEC_CALL, /**< invoke a function */ VM_EXEC_SUPER_CALL, /**< invoke a function through 'super' keyword */ VM_EXEC_SPREAD_OP, /**< call/construct operation with spreaded argument list */ + VM_EXEC_RETURN, /**< return with the completion value without freeing registers */ VM_EXEC_CONSTRUCT, /**< construct a new object */ } vm_call_operation; @@ -400,6 +407,7 @@ ecma_value_t vm_run_module (const ecma_compiled_code_t *bytecode_p, ecma_object_ ecma_value_t vm_run (const ecma_compiled_code_t *bytecode_header_p, ecma_value_t this_binding_value, ecma_object_t *lex_env_p, const ecma_value_t *arg_list_p, ecma_length_t arg_list_len); +ecma_value_t vm_execute (vm_frame_ctx_t *frame_ctx_p); bool vm_is_strict_mode (void); bool vm_is_direct_eval_form_call (void); diff --git a/tests/jerry/es2015/generator-return.js b/tests/jerry/es2015/generator-return.js new file mode 100644 index 000000000..b14540e9a --- /dev/null +++ b/tests/jerry/es2015/generator-return.js @@ -0,0 +1,74 @@ +/* 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 check_result(result, value, done) +{ + assert(result.value === value) + assert(result.done === done) +} + +function * gen1(a) { + return "a: " + (yield a.p) +} + +var f = gen1({}) +check_result(f.return(4), 4, true) +check_result(f.next(), undefined, true) + +f = gen1({ p:"x" }) +check_result(f.next(), "x", false) +check_result(f.return(10), 10, true) +check_result(f.next(), undefined, true) + +f = gen1({ p:"b" }) +check_result(f.next(), "b", false) +check_result(f.next(), "a: undefined", true) +check_result(f.next(), undefined, true) + +function*gen2() { + try { + for (let i in { x:1, y:2 }) + { + assert((yield i) === "33") + } + assert(false) + } catch (e) { + assert(false) + } finally { + yield "z" + } +} + +f = gen2() +check_result(f.return("ret"), "ret", true) +check_result(f.next(), undefined, true) + +f = gen2() +check_result(f.next(), "x", false) +check_result(f.return("ret"), "z", false) +check_result(f.next(), "ret", true) +check_result(f.next(), undefined, true) + +function* gen3() { + try { + return 8 + } finally { + yield 1 + } +} + +f = gen3() +check_result(f.next(), 1, false) +check_result(f.return(2), 2, true) diff --git a/tests/jerry/es2015/generator-throw.js b/tests/jerry/es2015/generator-throw.js new file mode 100644 index 000000000..da99bdcba --- /dev/null +++ b/tests/jerry/es2015/generator-throw.js @@ -0,0 +1,82 @@ +/* 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 check_result(result, value, done) +{ + assert(result.value === value) + assert(result.done === done) +} + +function check_throw(str, expected) +{ + try { + eval(str) + assert(false); + } catch (e) { + assert(e === expected); + } +} + +function * gen1(a) { + return "a: " + (yield a.p) +} + +var f = gen1({}) +check_throw("f.throw(4)", 4) +check_result(f.next(), undefined, true) + +f = gen1({ p:"x" }) +check_result(f.next(), "x", false) +check_throw("f.throw(10)", 10) +check_result(f.next(), undefined, true) + +f = gen1({ p:"b" }) +check_result(f.next(), "b", false) +check_result(f.next(), "a: undefined", true) +check_result(f.next(), undefined, true) + +function*gen2() { + try { + for (let i in { x:1, y:2 }) + { + assert((yield i) === "33") + } + assert(false) + } finally { + yield "z" + } +} + +f = gen2() +check_throw("f.throw('throw')", "throw") +check_result(f.next(), undefined, true) + +f = gen2() +check_result(f.next(), "x", false) +check_result(f.throw("throw"), "z", false) +check_throw("f.next()", "throw") +check_result(f.next(), undefined, true) + +function* gen3() { + try { + return 8 + } finally { + yield 1 + } +} + +f = gen3() +check_result(f.next(), 1, false) +check_throw("f.throw(2)", 2) diff --git a/tests/jerry/es2015/generator-yield.js b/tests/jerry/es2015/generator-yield.js new file mode 100644 index 000000000..3c1dd53cd --- /dev/null +++ b/tests/jerry/es2015/generator-yield.js @@ -0,0 +1,73 @@ +/* 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. + */ + +/* This file checks yield syntax errors. */ + +function check_syntax_error(str) +{ + try { + eval(str); + assert(false); + } catch (e) { + assert(e instanceof SyntaxError); + } +} + +function * gen() +{ + yield , yield + + yield + , yield + + (yield + ) + + yield[ + 1] +} + +function*gen2() +{ + 1 ? + yield + : + yield +} + +var gen3 = function*(){ + (yield)/[yield] +} + +check_syntax_error("function *gen(){ yield % yield }"); +check_syntax_error("function *gen(){ (yield) % yield }"); +check_syntax_error("function *gen(){ yield % (yield) }"); +check_syntax_error("function *gen(){ (yield\n1) }"); +check_syntax_error("function *gen(){ function yield() {} }"); +check_syntax_error("function *gen(){ (yield)=>1 }"); +check_syntax_error("function *gen(){ yield => 1 }"); + +function *gen4() { + var f = function yield(i) { + if (i = 0) + return yield(i + 1) + + return 39 + } + + return f(0) +} + +assert(gen4().next().value === 39); diff --git a/tests/jerry/es2015/generator.js b/tests/jerry/es2015/generator.js new file mode 100644 index 000000000..c34fc5a78 --- /dev/null +++ b/tests/jerry/es2015/generator.js @@ -0,0 +1,199 @@ +/* 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. + */ + +/* This file checks core generator operations. */ + +function check_result(result, value, done) +{ + assert(result.value === value) + assert(result.done === done) +} + +function * gen1(a = (t = 8)) { + var o = { p: 2 } + var x = 3.25 + + assert((o.p + (yield 10)) === 23) + assert((o.p + (yield 11)) === 24) + return x +} + +/* Cannot be invoked with new. */ +try { + new gen1 + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +/* Fully read values. */ +var t = 0 +var g = gen1() +assert(t === 8) + +check_result(g.next(20), 10, false) +check_result(g.next(21), 11, false) +check_result(g.next(22), 3.25, true) +check_result(g.next(23), undefined, true) + +/* Partly read values (gc needs to free a suspended generator). */ +t = 0 +g = gen1() +assert(t === 8) + +check_result(g.next(20), 10, false) + +function * gen2() { + for (i in { x:0, y:1, z:2 }) + { + let a = eval("'s'") + + var b = yield a + i + assert (b === ++t) + } +} + +/* Fully read values. */ +t = 0 +f = gen2() + +check_result(f.next(0), "sx", false) +check_result(f.next(1), "sy", false) +check_result(f.next(2), "sz", false) +check_result(f.next(3), undefined, true) +check_result(f.next(4), undefined, true) + +/* Partly read values (gc needs to free a suspended generator). */ +f = gen2() + +t = 0 +check_result(f.next(0), "sx", false) + +function *gen3() { + function f(yield) { + return -yield * 2 + } + + var g = (v) => { + assert(v === 6) + } + + g(yield yield f(++t)) + + return 77 +} + +/* Fully read values. */ +t = 0 +f = gen3() + +check_result(f.next(0), -2, false) +check_result(f.next(88), 88, false) +check_result(f.next(6), 77, true) + +/* Partly read values (gc needs to free a suspended generator). */ +t = 0 +f = gen3() + +check_result(f.next(0), -2, false) + +function + /* generator: */ * + /* name: */ gen4() { + + let a = eval("5") + with ({a}) + { + let a = eval("6") + + for (let a = 10; a < 11; a++) + { + let a = eval("7") + yield (a) + } + + yield a, !assert(a === 6) + } + assert((yield a) === undefined) +} + +/* Fully read values. */ +f = gen4() + +check_result(f.next(), 7, false) +check_result(f.next(), 6, false) +check_result(f.next(), 5, false) +check_result(f.next(), undefined, true) + +/* Partly read values (gc needs to free a suspended generator). */ +f = gen4() + +check_result(f.next(), 7, false) + +function*gen5(a,b,c,d) { + yield a + yield b + yield c + yield d +} + +/* Fully read values. */ +t = [] +for(let i of gen5(1,3,5,7)) { + t.push(i) +} + +assert(t.length === 4) +assert(t[0] === 1) +assert(t[1] === 3) +assert(t[2] === 5) +assert(t[3] === 7) + +/* Partly read values (gc needs to free a suspended generator). */ +t = [] +for(let i of gen5(1,3,5,7)) { + t.push(i) + if (i === 3) { + break + } +} + +assert(t.length === 2) +assert(t[0] === 1) +assert(t[1] === 3) + +/* Recursive generator call. */ +function* gen6(a,b,c,d) { + yield f.next() +} + +f = gen6() + +try { + f.next() + assert(false) +} catch (e) { + assert(e instanceof TypeError) +} + +/* Parameterless yield. */ +function* gen7() { + yield +} + +f = gen7() +check_result(f.next(), undefined, false) +check_result(f.next(), undefined, true) + diff --git a/tests/unit-core/test-snapshot.c b/tests/unit-core/test-snapshot.c index 24bd9d100..2c5964875 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, 0x1F, 0x00, 0x00, 0x00, + 0x4A, 0x52, 0x52, 0x59, 0x20, 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,