diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index ecc49ed7f..ac7aab014 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -404,6 +404,27 @@ 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 (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_ASYNC_GENERATOR_CALLED) + { + ecma_value_t task = executable_object_p->extended_object.u.class_prop.u.head; + + while (!ECMA_IS_INTERNAL_VALUE_NULL (task)) + { + ecma_async_generator_task_t *task_p; + task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, task); + + JERRY_ASSERT (ecma_is_value_object (task_p->promise)); + ecma_gc_set_object_visited (ecma_get_object_from_value (task_p->promise)); + + if (ecma_is_value_object (task_p->operation_value)) + { + ecma_gc_set_object_visited (ecma_get_object_from_value (task_p->operation_value)); + } + + task = task_p->next; + } + } + 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, @@ -584,6 +605,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ #endif /* ENABLED (JERRY_BUILTIN_CONTAINER) */ #if ENABLED (JERRY_ESNEXT) case LIT_MAGIC_STRING_GENERATOR_UL: + case LIT_MAGIC_STRING_ASYNC_GENERATOR_UL: { ecma_gc_mark_executable_object (object_p); break; @@ -850,6 +872,23 @@ ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */ ecma_bytecode_deref ((ecma_compiled_code_t *) bytecode_header_p); + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_ASYNC_GENERATOR_CALLED) + { + ecma_value_t task = executable_object_p->extended_object.u.class_prop.u.head; + + while (!ECMA_IS_INTERNAL_VALUE_NULL (task)) + { + ecma_async_generator_task_t *task_p; + task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, task); + + JERRY_ASSERT (ecma_is_value_object (task_p->promise)); + ecma_free_value_if_not_object (task_p->operation_value); + + task = task_p->next; + jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); + } + } + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED) { return size; @@ -1167,6 +1206,7 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ #endif /* ENABLED (JERRY_BUILTIN_DATAVIEW) */ #if ENABLED (JERRY_ESNEXT) case LIT_MAGIC_STRING_GENERATOR_UL: + case LIT_MAGIC_STRING_ASYNC_GENERATOR_UL: { ext_object_size = ecma_gc_free_executable_object (object_p); break; diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index 03a3ce23e..c88c05f91 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -120,6 +120,7 @@ typedef enum ECMA_PARSE_FUNCTION_CONTEXT = (1u << 8), /**< function context is present (ECMA_PARSE_DIRECT_EVAL must be set) */ ECMA_PARSE_GENERATOR_FUNCTION = (1u << 9), /**< generator function is parsed */ + ECMA_PARSE_ASYNC_FUNCTION = (1u << 10), /**< async function is parsed */ /* These flags are internally used by the parser. */ #ifndef JERRY_NDEBUG @@ -870,6 +871,7 @@ typedef struct ecma_value_t value; /**< value of the object (e.g. boolean, number, string, etc.) */ uint32_t length; /**< length related property (e.g. length of ArrayBuffer) */ ecma_value_t target; /**< [[ProxyTarget]] internal property */ + ecma_value_t head; /**< points to the async generator task queue head item */ } u; } class_prop; @@ -1862,6 +1864,7 @@ typedef enum ECMA_EXECUTABLE_OBJECT_RUNNING = (1u << 1), /**< executable object is currently running */ /* Generator specific flags. */ ECMA_GENERATOR_ITERATE_AND_YIELD = (1u << 2), /**< the generator performs a yield* operation */ + ECMA_ASYNC_GENERATOR_CALLED = (1u << 3), /**< the async generator was executed before */ } ecma_executable_object_flags_t; /** @@ -1870,6 +1873,23 @@ typedef enum #define ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED(extra_info) \ (!((extra_info) & (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_EXECUTABLE_OBJECT_RUNNING))) +/** + * Enqueued task of an AsyncGenerator. + * + * An execution of a task has three steps: + * 1) Perform a next/throw/return operation + * 2) Resume the execution of the AsyncGenerator + * 3) Fulfill or reject a promise if the AsyncGenerator yielded a value + * (these Promises are created by the AsyncGenerator itself) + */ +typedef struct +{ + ecma_value_t next; /**< points to the next task which will be performed after this task is completed */ + ecma_value_t promise; /**< promise which will be fulfilled or rejected after this task is completed */ + ecma_value_t operation_value; /**< value argument of the operation */ + uint8_t operation_type; /**< type of operation (see ecma_async_generator_operation_type_t) */ +} ecma_async_generator_task_t; + #endif /* ENABLED (JERRY_ESNEXT) */ #if ENABLED (JERRY_BUILTIN_DATAVIEW) diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index 98d901b39..289220978 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -127,13 +127,13 @@ typedef enum * Set an internal property value from pointer. */ #define ECMA_SET_INTERNAL_VALUE_POINTER(field, pointer) \ - (field) = ((ecma_value_t) pointer) + ((field) = ((ecma_value_t) pointer)) /** * Set an internal property value from pointer. Pointer can be NULL. */ #define ECMA_SET_INTERNAL_VALUE_ANY_POINTER(field, pointer) \ - (field) = ((ecma_value_t) pointer) + ((field) = ((ecma_value_t) pointer)) /** * Convert an internal property value to pointer. @@ -147,6 +147,12 @@ typedef enum #define ECMA_GET_INTERNAL_VALUE_ANY_POINTER(type, field) \ ((type *) field) +/** + * Checks whether an internal property is NULL. + */ +#define ECMA_IS_INTERNAL_VALUE_NULL(field) \ + ((field) == ((ecma_value_t) NULL)) + #else /* !ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ /** @@ -173,6 +179,12 @@ typedef enum #define ECMA_GET_INTERNAL_VALUE_ANY_POINTER(type, field) \ ECMA_GET_POINTER (type, field) +/** + * Checks whether an internal property is NULL. + */ +#define ECMA_IS_INTERNAL_VALUE_NULL(field) \ + ((field) == ((ecma_value_t) JMEM_CP_NULL)) + #endif /* ECMA_VALUE_CAN_STORE_UINTPTR_VALUE_DIRECTLY */ /** diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.c b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.c new file mode 100644 index 000000000..6f861a099 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.c @@ -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. + */ + +#include "ecma-globals.h" + +#if ENABLED (JERRY_ESNEXT) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" +#include "ecma-function-object.h" + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-generator-function.inc.h" +#define BUILTIN_UNDERSCORED_ID async_generator_function +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup asyncgenerator ECMA AsyncGeneratorFunction object built-in + * @{ + */ + +/** + * Handle calling [[Call]] of built-in AsyncGeneratorFunction object + * + * @return constructed async generator function object - if success + * raised error otherwise + */ +ecma_value_t +ecma_builtin_async_generator_function_dispatch_call (const ecma_value_t *arguments_list_p, /**< arguments list */ + ecma_length_t arguments_list_len) /**< number of arguments */ +{ + JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); + + return ecma_op_create_dynamic_function (arguments_list_p, + arguments_list_len, + ECMA_PARSE_GENERATOR_FUNCTION | ECMA_PARSE_ASYNC_FUNCTION); +} /* ecma_builtin_async_generator_function_dispatch_call */ + +/** + * Handle calling [[Construct]] of built-in AsyncGeneratorFunction object + * + * @return constructed async generator function object - if success + * raised error otherwise + */ +ecma_value_t +ecma_builtin_async_generator_function_dispatch_construct (const ecma_value_t *arguments_list_p, /**< arguments list */ + ecma_length_t arguments_list_len) /**< number of arguments */ +{ + return ecma_builtin_async_generator_function_dispatch_call (arguments_list_p, arguments_list_len); +} /* ecma_builtin_async_generator_function_dispatch_construct */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.inc.h new file mode 100644 index 000000000..339a9dc10 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-function.inc.h @@ -0,0 +1,41 @@ +/* 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. + */ + +/* + * %AsyncGeneratorFunction% built-in description + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_ESNEXT) + +/* ECMA-262 v10, 25.3.2 */ +STRING_VALUE (LIT_MAGIC_STRING_NAME, + LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* ECMA-262 v10, 25.3.2.1 */ +NUMBER_VALUE (LIT_MAGIC_STRING_LENGTH, + 1, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* ECMA-262 v6, 25.3.2.2 */ +OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, + ECMA_BUILTIN_ID_ASYNC_GENERATOR, + ECMA_PROPERTY_FIXED) + +#endif /* ENABLED (JERRY_ESNEXT) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.c new file mode 100644 index 000000000..1f0ac4062 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.c @@ -0,0 +1,147 @@ +/* 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-async-generator-object.h" +#include "ecma-builtins.h" +#include "ecma-exceptions.h" +#include "ecma-gc.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-iterator-object.h" +#include "ecma-promise-object.h" +#include "jcontext.h" +#include "opcodes.h" +#include "vm-defines.h" + +#if ENABLED (JERRY_ESNEXT) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +/** + * This object has a custom dispatch function. + */ +#define BUILTIN_CUSTOM_DISPATCH + +/** + * List of built-in routine identifiers. + */ +enum +{ + ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_START = ECMA_BUILTIN_ID__COUNT - 1, + ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT, + ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW, + ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_RETURN +}; + +/** + * Convert routine type to operation type.. + */ +#define ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION(type) \ + ((ecma_async_generator_operation_type_t) ((type) - ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT)) + +JERRY_STATIC_ASSERT (ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT) + == ECMA_ASYNC_GENERATOR_DO_NEXT, + convert_ecma_async_generator_routine_next_to_ecma_async_generator_do_next_failed); + +JERRY_STATIC_ASSERT (ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW) + == ECMA_ASYNC_GENERATOR_DO_THROW, + convert_ecma_async_generator_routine_throw_to_ecma_async_generator_do_throw_failed); + +JERRY_STATIC_ASSERT (ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_RETURN) + == ECMA_ASYNC_GENERATOR_DO_RETURN, + convert_ecma_async_generator_routine_return_to_ecma_async_generator_do_return_failed); + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-generator-prototype.inc.h" +#define BUILTIN_UNDERSCORED_ID async_generator_prototype +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup asyncgenerator ECMA AsyncGenerator.prototype object built-in + * @{ + */ + +/** + * Dispatcher of the Generator built-in's routines + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_builtin_async_generator_prototype_dispatch_routine (uint16_t builtin_routine_id, /**< built-in wide routine + * identifier */ + ecma_value_t this_arg, /**< 'this' argument value */ + const ecma_value_t arguments_list_p[], /**< list of arguments + * passed to + * routine */ + ecma_length_t arguments_number) /**< length of arguments' + * list */ +{ + JERRY_UNUSED (arguments_number); + + 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_ASYNC_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 an async generator object.")); + } + + if (executable_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED) + { + ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); + + if (JERRY_UNLIKELY (builtin_routine_id == ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW)) + { + return ecma_promise_reject_or_resolve (promise, arguments_list_p[0], false); + } + + ecma_value_t iter_result = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); + ecma_value_t result = ecma_promise_reject_or_resolve (promise, iter_result, true); + ecma_free_value (iter_result); + return result; + } + + return ecma_async_generator_enqueue (executable_object_p, + ECMA_ASYNC_GENERATOR_ROUTINE_TO_OPERATION (builtin_routine_id), + arguments_list_p[0]); +} /* ecma_builtin_async_generator_prototype_dispatch_routine */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.inc.h new file mode 100644 index 000000000..b28c40283 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator-prototype.inc.h @@ -0,0 +1,45 @@ +/* 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. + */ + +/* + * AsyncGenerator.prototype built-in description + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_ESNEXT) + +/* 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_ASYNC_GENERATOR_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* ECMA-262 v6, 25.2.3.1 */ +OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, + ECMA_BUILTIN_ID_ASYNC_GENERATOR, + 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_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_NEXT, 1, 1) +ROUTINE (LIT_MAGIC_STRING_RETURN, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_RETURN, 1, 1) +ROUTINE (LIT_MAGIC_STRING_THROW, ECMA_ASYNC_GENERATOR_PROTOTYPE_ROUTINE_THROW, 1, 1) + +#endif /* ENABLED (JERRY_ESNEXT) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.c b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.c new file mode 100644 index 000000000..2778e2dfc --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.c @@ -0,0 +1,43 @@ +/* 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-globals.h" + +#if ENABLED (JERRY_ESNEXT) + +#define ECMA_BUILTINS_INTERNAL +#include "ecma-builtins-internal.h" + +#define BUILTIN_INC_HEADER_NAME "ecma-builtin-async-generator.inc.h" +#define BUILTIN_UNDERSCORED_ID async_generator +#include "ecma-builtin-internal-routines-template.inc.h" + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmabuiltins + * @{ + * + * \addtogroup asyncgenerator ECMA AsyncGenerator object built-in + * @{ + */ + +/** + * @} + * @} + * @} + */ + +#endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.inc.h new file mode 100644 index 000000000..911e61940 --- /dev/null +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-async-generator.inc.h @@ -0,0 +1,41 @@ +/* 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. + */ + +/* + * %AsyncGenerator% built-in description (AsyncGeneratorFunction.prototype) + */ + +#include "ecma-builtin-helpers-macro-defines.inc.h" + +#if ENABLED (JERRY_ESNEXT) + +/* ECMA-262 v6, 25.3.2.3.1 */ +OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR, + ECMA_BUILTIN_ID_ASYNC_GENERATOR_FUNCTION, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* ECMA-262 v6, 25.3.2.3.2 */ +OBJECT_VALUE (LIT_MAGIC_STRING_PROTOTYPE, + ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +/* ECMA-262 v6, 25.3.2.3.3 */ +STRING_VALUE (LIT_GLOBAL_SYMBOL_TO_STRING_TAG, + LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, + ECMA_PROPERTY_FLAG_CONFIGURABLE) + +#endif /* ENABLED (JERRY_ESNEXT) */ + +#include "ecma-builtin-helpers-macro-undefs.inc.h" diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c index abaad78e1..7383b0b93 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-generator-prototype.c @@ -31,7 +31,7 @@ /** * This object has a custom dispatch function. */ - #define BUILTIN_CUSTOM_DISPATCH +#define BUILTIN_CUSTOM_DISPATCH /** * List of built-in routine identifiers. @@ -58,14 +58,6 @@ enum * @{ */ -/** - * Byte code sequence which returns from the generator. - */ -static const uint8_t ecma_builtin_generator_prototype_return[2] = -{ - CBC_EXT_OPCODE, CBC_EXT_RETURN -}; - /** * Helper function for next / return / throw * @@ -120,7 +112,7 @@ ecma_builtin_generator_prototype_object_do (vm_executable_object_t *executable_o if (resume_mode == ECMA_ITERATOR_RETURN) { - executable_object_p->frame_ctx.byte_code_p = ecma_builtin_generator_prototype_return; + executable_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; } else if (resume_mode == ECMA_ITERATOR_THROW) { diff --git a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h index 0944817cc..081be2106 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h +++ b/jerry-core/ecma/builtin-objects/ecma-builtins.inc.h @@ -651,6 +651,27 @@ BUILTIN (ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE, true, generator_prototype) +/* The %(AsyncGeneratorFunction)% object */ +BUILTIN_ROUTINE (ECMA_BUILTIN_ID_ASYNC_GENERATOR_FUNCTION, + ECMA_OBJECT_TYPE_FUNCTION, + ECMA_BUILTIN_ID_FUNCTION, + true, + async_generator_function) + +/* The %(AsyncGenerator)% object */ +BUILTIN (ECMA_BUILTIN_ID_ASYNC_GENERATOR, + ECMA_OBJECT_TYPE_GENERAL, + ECMA_BUILTIN_ID_FUNCTION_PROTOTYPE, + true, + async_generator) + +/* The %(AsyncGenerator).prototype% object */ +BUILTIN (ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE, + ECMA_OBJECT_TYPE_GENERAL, + ECMA_BUILTIN_ID_ITERATOR_PROTOTYPE, + true, + async_generator_prototype) + #if ENABLED (JERRY_BUILTIN_SET) /* The %SetIteratorPrototype% object (ECMA-262 v6, 23.2.5.2) */ BUILTIN (ECMA_BUILTIN_ID_SET_ITERATOR_PROTOTYPE, diff --git a/jerry-core/ecma/operations/ecma-async-generator-object.c b/jerry-core/ecma/operations/ecma-async-generator-object.c new file mode 100644 index 000000000..b35941b95 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-async-generator-object.c @@ -0,0 +1,181 @@ +/* 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-alloc.h" +#include "ecma-async-generator-object.h" +#include "ecma-builtins.h" +#include "ecma-globals.h" +#include "ecma-helpers.h" +#include "ecma-iterator-object.h" +#include "ecma-promise-object.h" +#include "jcontext.h" +#include "opcodes.h" +#include "vm.h" + +#if ENABLED (JERRY_ESNEXT) + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaasyncgeneratorobject ECMA AsyncGenerator object related routines + * @{ + */ + +/** + * Enqueue a task into the command queue of an async generator + * + * @return ecma Promise value + * Returned value must be freed with ecma_free_value. + */ +ecma_value_t +ecma_async_generator_enqueue (vm_executable_object_t *async_generator_object_p, /**< async generator */ + ecma_async_generator_operation_type_t operation, /**< operation */ + ecma_value_t value) /**< value argument of operation */ +{ + ecma_async_generator_task_t *task_p = jmem_heap_alloc_block (sizeof (ecma_async_generator_task_t)); + + ECMA_SET_INTERNAL_VALUE_ANY_POINTER (task_p->next, NULL); + task_p->operation_value = ecma_copy_value_if_not_object (value); + task_p->operation_type = (uint8_t) operation; + + ecma_object_t *old_new_target_p = JERRY_CONTEXT (current_new_target); + JERRY_CONTEXT (current_new_target) = ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE); + ecma_value_t result = ecma_op_create_promise_object (ECMA_VALUE_EMPTY, ECMA_PROMISE_EXECUTOR_EMPTY); + JERRY_CONTEXT (current_new_target) = old_new_target_p; + task_p->promise = result; + + ecma_value_t head = async_generator_object_p->extended_object.u.class_prop.u.head; + + if (ECMA_IS_INTERNAL_VALUE_NULL (head)) + { + ECMA_SET_INTERNAL_VALUE_POINTER (async_generator_object_p->extended_object.u.class_prop.u.head, task_p); + + if (async_generator_object_p->extended_object.u.class_prop.extra_info & ECMA_ASYNC_GENERATOR_CALLED) + { + ecma_value_t executable_object = ecma_make_object_value ((ecma_object_t *) async_generator_object_p); + ecma_enqueue_promise_async_generator_job (executable_object); + return result; + } + + async_generator_object_p->extended_object.u.class_prop.extra_info |= ECMA_ASYNC_GENERATOR_CALLED; + ecma_async_generator_run (async_generator_object_p); + return result; + } + + /* Append the new task at the end. */ + ecma_async_generator_task_t *prev_task_p; + prev_task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, head); + + while (!ECMA_IS_INTERNAL_VALUE_NULL (prev_task_p->next)) + { + prev_task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, prev_task_p->next); + } + + ECMA_SET_INTERNAL_VALUE_POINTER (prev_task_p->next, task_p); + return result; +} /* ecma_async_generator_enqueue */ + +/** + * Execute the next task in the command queue of the async generator + */ +void +ecma_async_generator_run (vm_executable_object_t *async_generator_object_p) /**< async generator */ +{ + JERRY_ASSERT (async_generator_object_p->extended_object.u.class_prop.class_id + == LIT_MAGIC_STRING_ASYNC_GENERATOR_UL); + JERRY_ASSERT (!ECMA_IS_INTERNAL_VALUE_NULL (async_generator_object_p->extended_object.u.class_prop.u.head)); + + ecma_value_t head = async_generator_object_p->extended_object.u.class_prop.u.head; + ecma_async_generator_task_t *task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, head); + + if (task_p->operation_type == ECMA_ASYNC_GENERATOR_DO_RETURN) + { + async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_return; + } + else if (task_p->operation_type == ECMA_ASYNC_GENERATOR_DO_THROW) + { + async_generator_object_p->frame_ctx.byte_code_p = opfunc_resume_executable_object_with_throw; + } + + ecma_value_t value = task_p->operation_value; + ecma_ref_if_object (value); + task_p->operation_value = ECMA_VALUE_UNDEFINED; + + value = opfunc_resume_executable_object (async_generator_object_p, value); + + if (async_generator_object_p->extended_object.u.class_prop.extra_info & ECMA_EXECUTABLE_OBJECT_COMPLETED) + { + JERRY_ASSERT (head == async_generator_object_p->extended_object.u.class_prop.u.head); + ecma_async_generator_finalize (async_generator_object_p, value); + } +} /* ecma_async_generator_run */ + +/** + * Finalize the promises of an executable generator + */ +void +ecma_async_generator_finalize (vm_executable_object_t *async_generator_object_p, /**< async generator */ + ecma_value_t value) /**< final value (takes reference) */ +{ + ecma_value_t next = async_generator_object_p->extended_object.u.class_prop.u.head; + ecma_async_generator_task_t *task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, next); + + ECMA_SET_INTERNAL_VALUE_ANY_POINTER (async_generator_object_p->extended_object.u.class_prop.u.head, NULL); + + if (ECMA_IS_VALUE_ERROR (value)) + { + value = jcontext_take_exception (); + ecma_reject_promise (task_p->promise, value); + } + else + { + ecma_value_t result = ecma_create_iter_result_object (value, ECMA_VALUE_TRUE); + ecma_fulfill_promise (task_p->promise, result); + ecma_free_value (result); + } + + ecma_free_value (value); + + next = task_p->next; + jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); + + while (!ECMA_IS_INTERNAL_VALUE_NULL (next)) + { + task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, next); + + if (task_p->operation_type != ECMA_ASYNC_GENERATOR_DO_THROW) + { + value = ecma_create_iter_result_object (ECMA_VALUE_UNDEFINED, ECMA_VALUE_TRUE); + ecma_fulfill_promise (task_p->promise, value); + ecma_free_value (value); + } + else + { + ecma_reject_promise (task_p->promise, task_p->operation_value); + } + + ecma_free_value_if_not_object (task_p->operation_value); + + next = task_p->next; + jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); + } +} /* ecma_async_generator_finalize */ + +#endif /* ENABLED (JERRY_ESNEXT) */ + +/** + * @} + * @} + */ diff --git a/jerry-core/ecma/operations/ecma-async-generator-object.h b/jerry-core/ecma/operations/ecma-async-generator-object.h new file mode 100644 index 000000000..f535a6e29 --- /dev/null +++ b/jerry-core/ecma/operations/ecma-async-generator-object.h @@ -0,0 +1,54 @@ +/* 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. + */ + +#ifndef ECMA_ASYNC_GENERATOR_OBJECT_H +#define ECMA_ASYNC_GENERATOR_OBJECT_H + +#include "ecma-globals.h" +#include "vm-defines.h" + +#if ENABLED (JERRY_ESNEXT) + +/** \addtogroup ecma ECMA + * @{ + * + * \addtogroup ecmaasyncgeneratorobject ECMA AsyncGenerator object related routines + * @{ + */ + +/** + * AsyncGenerator command types. + */ +typedef enum +{ + ECMA_ASYNC_GENERATOR_DO_NEXT, /**< async generator next operation */ + ECMA_ASYNC_GENERATOR_DO_THROW, /**< async generator throw operation */ + ECMA_ASYNC_GENERATOR_DO_RETURN, /**< async generator return operation */ +} ecma_async_generator_operation_type_t; + +ecma_value_t ecma_async_generator_enqueue (vm_executable_object_t *async_generator_object_p, + ecma_async_generator_operation_type_t operation, ecma_value_t value); + +void ecma_async_generator_run (vm_executable_object_t *async_generator_object_p); +void ecma_async_generator_finalize (vm_executable_object_t *async_generator_object_p, ecma_value_t value); + +#endif /* ENABLED (JERRY_ESNEXT) */ + +/** + * @} + * @} + */ + +#endif /* !ECMA_ASYNC_GENERATOR_OBJECT_H */ diff --git a/jerry-core/ecma/operations/ecma-function-object.c b/jerry-core/ecma/operations/ecma-function-object.c index 678bb7e48..d18439f8a 100644 --- a/jerry-core/ecma/operations/ecma-function-object.c +++ b/jerry-core/ecma/operations/ecma-function-object.c @@ -395,23 +395,28 @@ ecma_op_create_dynamic_function (const ecma_value_t *arguments_list_p, /**< argu #if ENABLED (JERRY_ESNEXT) ecma_object_t *new_target_p = JERRY_CONTEXT (current_new_target); - bool is_generator_func = parse_opts & ECMA_PARSE_GENERATOR_FUNCTION; - if (is_generator_func) + if (JERRY_UNLIKELY (parse_opts & (ECMA_PARSE_GENERATOR_FUNCTION | ECMA_PARSE_ASYNC_FUNCTION))) { fallback_proto = ECMA_BUILTIN_ID_GENERATOR; - } - if (new_target_p == NULL) - { - if (is_generator_func) + if (parse_opts & ECMA_PARSE_ASYNC_FUNCTION) + { + fallback_proto = ECMA_BUILTIN_ID_ASYNC_GENERATOR; + + if (new_target_p == NULL) + { + new_target_p = ecma_builtin_get (ECMA_BUILTIN_ID_ASYNC_GENERATOR_FUNCTION); + } + } + else if (new_target_p == NULL) { new_target_p = ecma_builtin_get (ECMA_BUILTIN_ID_GENERATOR_FUNCTION); } - else - { - new_target_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION); - } + } + else if (new_target_p == NULL) + { + new_target_p = ecma_builtin_get (ECMA_BUILTIN_ID_FUNCTION); } ecma_object_t *proto = ecma_op_get_prototype_from_constructor (new_target_p, fallback_proto); @@ -475,6 +480,20 @@ ecma_op_create_generator_function_object (ecma_object_t *scope_p, /**< function' return ecma_op_create_function_object (scope_p, bytecode_data_p, ECMA_BUILTIN_ID_GENERATOR); } /* ecma_op_create_generator_function_object */ +/** + * AsyncGeneratorFunction object creation operation. + * + * See also: ECMA-262 v10, 25.3 + * + * @return pointer to newly created Function object + */ +ecma_object_t * +ecma_op_create_async_generator_function_object (ecma_object_t *scope_p, /**< function's scope */ + const ecma_compiled_code_t *bytecode_data_p) /**< byte-code array */ +{ + return ecma_op_create_function_object (scope_p, bytecode_data_p, ECMA_BUILTIN_ID_ASYNC_GENERATOR); +} /* ecma_op_create_async_generator_function_object */ + /** * Arrow function object creation operation. * @@ -1294,6 +1313,11 @@ ecma_op_function_construct (ecma_object_t *func_obj_p, /**< Function object */ message_p = ECMA_ERR_MSG ("Async functions cannot be invoked with 'new'."); break; } + case CBC_FUNCTION_ASYNC_GENERATOR: + { + message_p = ECMA_ERR_MSG ("Async generator functions cannot be invoked with 'new'."); + break; + } case CBC_FUNCTION_ARROW: { message_p = ECMA_ERR_MSG ("Arrow functions cannot be invoked with 'new'."); @@ -1397,6 +1421,14 @@ ecma_op_lazy_instantiate_prototype_object (ecma_object_t *object_p) /**< the fun ECMA_OBJECT_TYPE_GENERAL); init_constructor = false; } + + if (CBC_FUNCTION_GET_TYPE (byte_code_p->status_flags) == CBC_FUNCTION_ASYNC_GENERATOR) + { + proto_object_p = ecma_create_object (ecma_builtin_get (ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE), + 0, + ECMA_OBJECT_TYPE_GENERAL); + init_constructor = false; + } } #endif /* ENABLED (JERRY_ESNEXT) */ diff --git a/jerry-core/ecma/operations/ecma-function-object.h b/jerry-core/ecma/operations/ecma-function-object.h index bc87ed557..47a0ccf6f 100644 --- a/jerry-core/ecma/operations/ecma-function-object.h +++ b/jerry-core/ecma/operations/ecma-function-object.h @@ -61,6 +61,9 @@ ecma_op_function_get_super_constructor (ecma_object_t *func_obj_p); ecma_object_t * ecma_op_create_generator_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p); +ecma_object_t * +ecma_op_create_async_generator_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p); + ecma_object_t * ecma_op_create_arrow_function_object (ecma_object_t *scope_p, const ecma_compiled_code_t *bytecode_data_p, ecma_value_t this_binding); diff --git a/jerry-core/ecma/operations/ecma-jobqueue.c b/jerry-core/ecma/operations/ecma-jobqueue.c index c7fb2fe4d..653042a06 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.c +++ b/jerry-core/ecma/operations/ecma-jobqueue.c @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "ecma-async-generator-object.h" #include "ecma-function-object.h" #include "ecma-globals.h" #include "ecma-helpers.h" @@ -57,6 +58,15 @@ typedef struct ecma_value_t argument; /**< argument for the reaction */ } ecma_job_promise_async_reaction_t; +/** + * Description of the PromiseAsyncGeneratorJob + */ +typedef struct +{ + ecma_job_queue_item_t header; /**< job queue item header */ + ecma_value_t executable_object; /**< executable object */ +} ecma_job_promise_async_generator_t; + /** * Description of the PromiseResolveThenableJob */ @@ -129,6 +139,20 @@ ecma_free_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_p) jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_reaction_t)); } /* ecma_free_promise_async_reaction_job */ +/** + * Free the heap and the member of the PromiseAsyncGeneratorJob. + */ +static void +ecma_free_promise_async_generator_job (ecma_job_promise_async_generator_t *job_p) /**< points to the + * PromiseAsyncReactionJob */ +{ + JERRY_ASSERT (job_p != NULL); + + ecma_free_value (job_p->executable_object); + + jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_generator_t)); +} /* ecma_free_promise_async_generator_job */ + /** * Free the heap and the member of the PromiseResolveThenableJob. */ @@ -240,13 +264,34 @@ ecma_process_promise_async_reaction_job (ecma_job_promise_async_reaction_t *job_ } ecma_value_t result = opfunc_resume_executable_object (executable_object_p, job_p->argument); - /* Argument reference is taken by opfunc_resume_executable_object. */ + /* Argument reference has been taken by opfunc_resume_executable_object. */ job_p->argument = ECMA_VALUE_UNDEFINED; - ecma_free_promise_async_reaction_job (job_p); + uint16_t expected_bits = (ECMA_EXECUTABLE_OBJECT_COMPLETED | ECMA_ASYNC_GENERATOR_CALLED); + if ((executable_object_p->extended_object.u.class_prop.extra_info & expected_bits) == expected_bits) + { + ecma_async_generator_finalize (executable_object_p, result); + result = ECMA_VALUE_UNDEFINED; + } + + ecma_free_promise_async_reaction_job (job_p); return result; } /* ecma_process_promise_async_reaction_job */ +/** + * The processor for PromiseAsyncGeneratorJob. + */ +static void +ecma_process_promise_async_generator_job (ecma_job_promise_async_generator_t *job_p) /**< the job to be operated */ +{ + ecma_object_t *object_p = ecma_get_object_from_value (job_p->executable_object); + + ecma_async_generator_run ((vm_executable_object_t *) object_p); + + ecma_free_value (job_p->executable_object); + jmem_heap_free_block (job_p, sizeof (ecma_job_promise_async_generator_t)); +} /* ecma_process_promise_async_generator_job */ + /** * Process the PromiseResolveThenableJob. * @@ -359,6 +404,20 @@ ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, /**< ex ecma_enqueue_job (&job_p->header); } /* ecma_enqueue_promise_async_reaction_job */ +/** + * Enqueue a PromiseAsyncGeneratorJob into the job queue. + */ +void +ecma_enqueue_promise_async_generator_job (ecma_value_t executable_object) /**< executable object */ +{ + ecma_job_promise_async_generator_t *job_p; + job_p = (ecma_job_promise_async_generator_t *) jmem_heap_alloc_block (sizeof (ecma_job_promise_async_generator_t)); + job_p->header.next_and_type = ECMA_JOB_PROMISE_ASYNC_GENERATOR; + job_p->executable_object = ecma_copy_value (executable_object); + + ecma_enqueue_job (&job_p->header); +} /* ecma_enqueue_promise_async_generator_job */ + /** * Enqueue a PromiseResolveThenableJob into the job queue. */ @@ -413,6 +472,11 @@ ecma_process_all_enqueued_jobs (void) ret = ecma_process_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p); break; } + case ECMA_JOB_PROMISE_ASYNC_GENERATOR: + { + ecma_process_promise_async_generator_job ((ecma_job_promise_async_generator_t *) job_p); + break; + } default: { JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE); @@ -450,6 +514,11 @@ ecma_free_all_enqueued_jobs (void) ecma_free_promise_async_reaction_job ((ecma_job_promise_async_reaction_t *) job_p); break; } + case ECMA_JOB_PROMISE_ASYNC_GENERATOR: + { + ecma_free_promise_async_generator_job ((ecma_job_promise_async_generator_t *) job_p); + break; + } default: { JERRY_ASSERT (ecma_job_queue_get_type (job_p) == ECMA_JOB_PROMISE_THENABLE); diff --git a/jerry-core/ecma/operations/ecma-jobqueue.h b/jerry-core/ecma/operations/ecma-jobqueue.h index 5f9f21cfc..fb27ba555 100644 --- a/jerry-core/ecma/operations/ecma-jobqueue.h +++ b/jerry-core/ecma/operations/ecma-jobqueue.h @@ -33,6 +33,7 @@ typedef enum ECMA_JOB_PROMISE_REACTION, /**< promise reaction job */ ECMA_JOB_PROMISE_ASYNC_REACTION_FULFILLED, /**< fulfilled promise async reaction job */ ECMA_JOB_PROMISE_ASYNC_REACTION_REJECTED, /**< rejected promise async reaction job */ + ECMA_JOB_PROMISE_ASYNC_GENERATOR, /**< continue async generator */ ECMA_JOB_PROMISE_THENABLE, /**< promise thenable job */ } ecma_job_queue_item_type_t; @@ -49,6 +50,7 @@ void ecma_job_queue_init (void); void ecma_enqueue_promise_reaction_job (ecma_value_t capability, ecma_value_t handler, ecma_value_t argument); void ecma_enqueue_promise_async_reaction_job (ecma_value_t executable_object, ecma_value_t argument, bool is_rejected); +void ecma_enqueue_promise_async_generator_job (ecma_value_t executable_object); void ecma_enqueue_promise_resolve_thenable_job (ecma_value_t promise, ecma_value_t thenable, ecma_value_t then); void ecma_free_all_enqueued_jobs (void); diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index f5e69d1ca..cf1ebfeaf 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -2781,6 +2781,10 @@ ecma_object_get_class_name (ecma_object_t *obj_p) /**< object */ { return LIT_MAGIC_STRING_GENERATOR_UL; } + case ECMA_BUILTIN_ID_ASYNC_GENERATOR: + { + return LIT_MAGIC_STRING_ASYNC_GENERATOR_UL; + } #endif /* ENABLED (JERRY_ESNEXT) */ #if ENABLED (JERRY_BUILTIN_JSON) case ECMA_BUILTIN_ID_JSON: diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 1dfbf1219..80bc10162 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 (49u) +#define JERRY_SNAPSHOT_VERSION (50u) /** * 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 22f38e167..bee41a0c3 100644 --- a/jerry-core/lit/lit-magic-strings.inc.h +++ b/jerry-core/lit/lit-magic-strings.inc.h @@ -812,6 +812,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TO_EXPONENTIAL_UL, "toExponential") #endif #if ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARRAY_ITERATOR_UL, "Array Iterator") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_UL, "AsyncGenerator") #endif #if ENABLED (JERRY_BUILTIN_ERRORS) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_REFERENCE_ERROR_UL, "ReferenceError") @@ -885,6 +886,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL, "getOwnPropert LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL, "propertyIsEnumerable") #if ENABLED (JERRY_ESNEXT) LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL, "getOwnPropertySymbols") +LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL, "AsyncGeneratorFunction") #endif LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL, "getOwnPropertyDescriptor") LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__FUNCTION_TO_STRING, "function(){/* ecmascript */}") @@ -1041,7 +1043,11 @@ LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (21, LIT_MAGIC_STRING_GET_OWN_PROPERTY_S #else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (21, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) #endif +#if ENABLED (JERRY_ESNEXT) +LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (22, LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL) +#else LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (22, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) +#endif LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (23, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (24, LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL) LIT_MAGIC_STRING_FIRST_STRING_WITH_SIZE (25, LIT_MAGIC_STRING__FUNCTION_TO_STRING) diff --git a/jerry-core/lit/lit-magic-strings.ini b/jerry-core/lit/lit-magic-strings.ini index b284a757e..d656aeb2b 100644 --- a/jerry-core/lit/lit-magic-strings.ini +++ b/jerry-core/lit/lit-magic-strings.ini @@ -328,6 +328,7 @@ LIT_MAGIC_STRING_SET_UTC_MINUTES_UL = "setUTCMinutes" LIT_MAGIC_STRING_SET_UTC_SECONDS_UL = "setUTCSeconds" LIT_MAGIC_STRING_TO_EXPONENTIAL_UL = "toExponential" LIT_MAGIC_STRING_ARRAY_ITERATOR_UL = "Array Iterator" +LIT_MAGIC_STRING_ASYNC_GENERATOR_UL = "AsyncGenerator" LIT_MAGIC_STRING_REFERENCE_ERROR_UL = "ReferenceError" LIT_MAGIC_STRING_DEFINE_PROPERTY_UL = "defineProperty" LIT_MAGIC_STRING_DELETE_PROPERTY_UL = "deleteProperty" @@ -362,5 +363,6 @@ LIT_MAGIC_STRING_TO_LOCALE_TIME_STRING_UL = "toLocaleTimeString" LIT_MAGIC_STRING_GET_OWN_PROPERTY_NAMES_UL = "getOwnPropertyNames" LIT_MAGIC_STRING_PROPERTY_IS_ENUMERABLE_UL = "propertyIsEnumerable" LIT_MAGIC_STRING_GET_OWN_PROPERTY_SYMBOLS_UL = "getOwnPropertySymbols" +LIT_MAGIC_STRING_ASYNC_GENERATOR_FUNCTION_UL = "AsyncGeneratorFunction" LIT_MAGIC_STRING_GET_OWN_PROPERTY_DESCRIPTOR_UL = "getOwnPropertyDescriptor" LIT_MAGIC_STRING__FUNCTION_TO_STRING = "function(){/* ecmascript */}" diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index cbfe79474..ef671057b 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t) */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 119, +JERRY_STATIC_ASSERT (CBC_EXT_END == 121, number_of_cbc_ext_opcodes_changed); #if ENABLED (JERRY_PARSER) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 9a4696dc6..f71785eb1 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -734,8 +734,12 @@ VM_OC_YIELD) \ CBC_OPCODE (CBC_EXT_YIELD_ITERATOR, CBC_NO_FLAG, 0, \ VM_OC_YIELD) \ + CBC_OPCODE (CBC_EXT_ASYNC_YIELD, CBC_NO_FLAG, 0, \ + VM_OC_ASYNC_YIELD) \ CBC_OPCODE (CBC_EXT_AWAIT, CBC_NO_FLAG, 0, \ VM_OC_AWAIT) \ + CBC_OPCODE (CBC_EXT_GENERATOR_AWAIT, CBC_NO_FLAG, 0, \ + VM_OC_GENERATOR_AWAIT) \ CBC_OPCODE (CBC_EXT_ASYNC_EXIT, CBC_NO_FLAG, 0, \ VM_OC_ASYNC_EXIT) \ CBC_OPCODE (CBC_EXT_RETURN, CBC_NO_FLAG, -1, \ @@ -852,6 +856,7 @@ typedef enum /* The following functions cannot be constructed (see CBC_FUNCTION_IS_CONSTRUCTABLE) */ CBC_FUNCTION_GENERATOR, /**< generator function */ CBC_FUNCTION_ASYNC, /**< async function */ + CBC_FUNCTION_ASYNC_GENERATOR, /**< async generator function */ /* The following functions has no prototype (see CBC_FUNCTION_HAS_PROTOTYPE) */ CBC_FUNCTION_ARROW, /**< arrow function */ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 7059253c8..2ff3bd10f 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -1964,14 +1964,20 @@ parser_parse_unary_expression (parser_context_t *context_p, /**< context */ parser_check_assignment_expr (context_p); lexer_next_token (context_p); - cbc_ext_opcode_t opcode = CBC_EXT_YIELD; - + cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) ? CBC_EXT_ASYNC_YIELD + : CBC_EXT_YIELD); if (!lexer_check_yield_no_arg (context_p)) { if (context_p->token.type == LEXER_MULTIPLY) { lexer_next_token (context_p); opcode = CBC_EXT_YIELD_ITERATOR; + + /* TODO: support yield * in async generator. Currently a meaningless error is thrown. */ + if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + { + parser_raise_error (context_p, PARSER_ERR_INVALID_CHARACTER); + } } parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); @@ -2389,7 +2395,9 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ #if ENABLED (JERRY_ESNEXT) else if (JERRY_UNLIKELY (token == LEXER_KEYW_AWAIT)) { - parser_emit_cbc_ext (context_p, CBC_EXT_AWAIT); + cbc_ext_opcode_t opcode = ((context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) ? CBC_EXT_GENERATOR_AWAIT + : CBC_EXT_AWAIT); + parser_emit_cbc_ext (context_p, opcode); } #endif /* ENABLED (JERRY_ESNEXT) */ else diff --git a/jerry-core/parser/js/js-parser-internal.h b/jerry-core/parser/js/js-parser-internal.h index bf3ef8345..0097df35a 100644 --- a/jerry-core/parser/js/js-parser-internal.h +++ b/jerry-core/parser/js/js-parser-internal.h @@ -201,6 +201,12 @@ typedef enum #define PARSER_GET_EVAL_FLAG(type) \ ((type) >> JERRY_LOG2 (ECMA_PARSE_ALLOW_SUPER)) +/** + * Check non-generator async functions + */ +#define PARSER_IS_NORMAL_ASYNC_FUNCTION(status_flags) \ + (((status_flags) & (PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION)) == PARSER_IS_ASYNC_FUNCTION) + #else /* !ENABLED (JERRY_ESNEXT) */ /** diff --git a/jerry-core/parser/js/js-parser.c b/jerry-core/parser/js/js-parser.c index 329ece3ec..65bcdafbb 100644 --- a/jerry-core/parser/js/js-parser.c +++ b/jerry-core/parser/js/js-parser.c @@ -692,6 +692,11 @@ parse_print_final_cbc (ecma_compiled_code_t *compiled_code_p, /**< compiled code JERRY_DEBUG_MSG (",async"); break; } + case CBC_FUNCTION_ASYNC_GENERATOR: + { + JERRY_DEBUG_MSG (",async_generator"); + break; + } case CBC_FUNCTION_ARROW: { JERRY_DEBUG_MSG (",arrow"); @@ -957,7 +962,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ JERRY_ASSERT (!(context_p->status_flags & PARSER_NO_END_LABEL)); } - if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { PARSER_MINUS_EQUAL_U16 (context_p->stack_depth, PARSER_TRY_CONTEXT_STACK_ALLOCATION); #ifndef JERRY_NDEBUG @@ -1195,7 +1200,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ context_p->status_flags &= (uint32_t) ~PARSER_NO_END_LABEL; #if ENABLED (JERRY_ESNEXT) - if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { length++; } @@ -1350,31 +1355,42 @@ parser_post_processing (parser_context_t *context_p) /**< context */ } #if ENABLED (JERRY_ESNEXT) + uint16_t function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL); + if (context_p->status_flags & (PARSER_IS_PROPERTY_GETTER | PARSER_IS_PROPERTY_SETTER)) { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ACCESSOR); + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ACCESSOR); } else if (context_p->status_flags & PARSER_IS_ARROW_FUNCTION) { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ARROW); + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ARROW); } else if (context_p->status_flags & PARSER_IS_GENERATOR_FUNCTION) { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_GENERATOR); + if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + { + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC_GENERATOR); + } + else + { + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_GENERATOR); + } } else if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC); + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_ASYNC); } else if (context_p->status_flags & PARSER_CLASS_CONSTRUCTOR) { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR); + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_CONSTRUCTOR); } else { - compiled_code_p->status_flags |= CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL); + function_type = CBC_FUNCTION_TO_TYPE_BITS (CBC_FUNCTION_NORMAL); } + compiled_code_p->status_flags |= function_type; + if (context_p->status_flags & PARSER_FUNCTION_HAS_REST_PARAM) { compiled_code_p->status_flags |= CBC_CODE_FLAGS_REST_PARAMETER; @@ -1582,7 +1598,7 @@ parser_post_processing (parser_context_t *context_p) /**< context */ *dst_p++ = CBC_RETURN_WITH_BLOCK; #if ENABLED (JERRY_ESNEXT) - if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { dst_p[-1] = CBC_EXT_OPCODE; dst_p[0] = CBC_EXT_ASYNC_EXIT; @@ -1745,14 +1761,7 @@ parser_parse_function_arguments (parser_context_t *context_p, /**< context */ bool has_duplicated_arg_names = false; - /* TODO: Currently async iterators are not supported, so generators ignore the async modifier. */ - uint32_t mask = (PARSER_IS_GENERATOR_FUNCTION | PARSER_IS_ASYNC_FUNCTION); - if ((context_p->status_flags & mask) == mask) - { - context_p->status_flags &= (uint32_t) ~PARSER_IS_ASYNC_FUNCTION; - } - - if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + if (PARSER_IS_NORMAL_ASYNC_FUNCTION (context_p->status_flags)) { parser_branch_t branch; parser_emit_cbc_ext_forward_branch (context_p, CBC_EXT_TRY_CREATE_CONTEXT, &branch); @@ -2058,6 +2067,10 @@ parser_parse_source (const uint8_t *arg_list_p, /**< function argument list */ { context.status_flags |= PARSER_IS_GENERATOR_FUNCTION; } + if (parse_opts & ECMA_PARSE_ASYNC_FUNCTION) + { + context.status_flags |= PARSER_IS_ASYNC_FUNCTION; + } #endif /* ENABLED (JERRY_ESNEXT) */ } diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index 0e7fd63bc..3197a0376 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -2335,6 +2335,10 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ { status_flags |= SCANNER_LITERAL_POOL_GENERATOR; } + if (context_p->status_flags & PARSER_IS_ASYNC_FUNCTION) + { + status_flags |= SCANNER_LITERAL_POOL_ASYNC; + } #endif /* ENABLED (JERRY_ESNEXT) */ scanner_push_literal_pool (context_p, &scanner_context, status_flags); diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 440605200..f08da36a0 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -600,11 +600,20 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context size_t total_size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); ecma_object_t *proto_p = NULL; + /* Async function objects are not accessible, so their class_id is not relevant. */ + uint16_t class_id = LIT_MAGIC_STRING_GENERATOR_UL; if (type == VM_CREATE_EXECUTABLE_OBJECT_GENERATOR) { - proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_function_obj_p), - ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE); + ecma_builtin_id_t default_proto_id = ECMA_BUILTIN_ID_GENERATOR_PROTOTYPE; + + if (CBC_FUNCTION_GET_TYPE (frame_ctx_p->bytecode_header_p->status_flags) == CBC_FUNCTION_ASYNC_GENERATOR) + { + default_proto_id = ECMA_BUILTIN_ID_ASYNC_GENERATOR_PROTOTYPE; + class_id = LIT_MAGIC_STRING_ASYNC_GENERATOR_UL; + } + + proto_p = ecma_op_get_prototype_from_constructor (JERRY_CONTEXT (current_function_obj_p), default_proto_id); } ecma_object_t *object_p = ecma_create_object (proto_p, @@ -618,9 +627,9 @@ opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context ecma_deref_object (proto_p); } - /* Async function objects are not accessible, so their class_id is not relevant. */ - executable_object_p->extended_object.u.class_prop.class_id = LIT_MAGIC_STRING_GENERATOR_UL; + executable_object_p->extended_object.u.class_prop.class_id = class_id; executable_object_p->extended_object.u.class_prop.extra_info = 0; + ECMA_SET_INTERNAL_VALUE_ANY_POINTER (executable_object_p->extended_object.u.class_prop.u.head, NULL); JERRY_ASSERT (!frame_ctx_p->is_eval_code); @@ -683,6 +692,14 @@ const uint8_t opfunc_resume_executable_object_with_throw[1] = CBC_THROW }; +/** + * Byte code which resumes an executable object with return + */ +const uint8_t opfunc_resume_executable_object_with_return[2] = +{ + CBC_EXT_OPCODE, CBC_EXT_RETURN +}; + /** * Resume the execution of an inactive executable object * diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index fb8e55af5..e82967438 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -130,6 +130,7 @@ vm_executable_object_t * opfunc_create_executable_object (vm_frame_ctx_t *frame_ctx_p, vm_create_executable_object_type_t type); extern const uint8_t opfunc_resume_executable_object_with_throw[]; +extern const uint8_t opfunc_resume_executable_object_with_return[]; ecma_value_t opfunc_resume_executable_object (vm_executable_object_t *executable_object_p, ecma_value_t value); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index f3cee79f6..0437a700d 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -482,6 +482,11 @@ vm_construct_literal_object (vm_frame_ctx_t *frame_ctx_p, /**< frame context */ func_obj_p = ecma_op_create_generator_function_object (frame_ctx_p->lex_env_p, bytecode_p); break; } + case CBC_FUNCTION_ASYNC_GENERATOR: + { + func_obj_p = ecma_op_create_async_generator_function_object (frame_ctx_p->lex_env_p, bytecode_p); + break; + } case CBC_FUNCTION_ARROW: { func_obj_p = ecma_op_create_arrow_function_object (frame_ctx_p->lex_env_p, @@ -2190,6 +2195,37 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ frame_ctx_p->stack_top_p = --stack_top_p; return *stack_top_p; } + case VM_OC_ASYNC_YIELD: + { + const uintptr_t object_offset = (uintptr_t) offsetof (vm_executable_object_t, frame_ctx); + ecma_extended_object_t *async_generator_object_p; + async_generator_object_p = (ecma_extended_object_t *) (((uintptr_t) frame_ctx_p) - object_offset); + + ecma_async_generator_task_t *task_p; + task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, + async_generator_object_p->u.class_prop.u.head); + + ecma_value_t iter_result = ecma_create_iter_result_object (stack_top_p[-1], ECMA_VALUE_FALSE); + ecma_fulfill_promise (task_p->promise, iter_result); + + ecma_free_value (iter_result); + ecma_free_value (stack_top_p[-1]); + async_generator_object_p->u.class_prop.u.head = task_p->next; + + if (!ECMA_IS_INTERNAL_VALUE_NULL (task_p->next)) + { + ecma_value_t executable_object = ecma_make_object_value ((ecma_object_t *) async_generator_object_p); + ecma_enqueue_promise_async_generator_job (executable_object); + } + + JERRY_ASSERT (task_p->operation_value == ECMA_VALUE_UNDEFINED); + jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); + + 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 ECMA_VALUE_UNDEFINED; + } case VM_OC_AWAIT: { ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); @@ -2239,6 +2275,30 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ return result; } + case VM_OC_GENERATOR_AWAIT: + { + ecma_value_t promise = ecma_make_object_value (ecma_builtin_get (ECMA_BUILTIN_ID_PROMISE)); + ecma_value_t argument = *(--stack_top_p); + + result = ecma_promise_reject_or_resolve (promise, argument, true); + ecma_free_value (argument); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + + 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; + + const uintptr_t object_offset = (uintptr_t) offsetof (vm_executable_object_t, frame_ctx); + + ecma_object_t *object_p = (ecma_object_t *) (((uintptr_t) frame_ctx_p) - object_offset); + ecma_promise_async_then (result, ecma_make_object_value (object_p)); + ecma_free_value (result); + return ECMA_VALUE_UNDEFINED; + } case VM_OC_EXT_RETURN: { result = left_value; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 46f059535..9d06a73a3 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -271,7 +271,9 @@ typedef enum 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_ASYNC_YIELD, /**< async yield operation */ VM_OC_AWAIT, /**< await operation */ + VM_OC_GENERATOR_AWAIT, /**< generator await operation */ VM_OC_EXT_RETURN, /**< return which also clears the stack */ VM_OC_ASYNC_EXIT, /**< return from async function */ VM_OC_STRING_CONCAT, /**< string concatenation */ @@ -340,7 +342,9 @@ typedef enum 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_ASYNC_YIELD = VM_OC_NONE, /**< async yield operation */ VM_OC_AWAIT = VM_OC_NONE, /**< await operation */ + VM_OC_GENERATOR_AWAIT = VM_OC_NONE, /**< generator await operation */ VM_OC_EXT_RETURN = VM_OC_NONE, /**< return which also clears the stack */ VM_OC_ASYNC_EXIT = VM_OC_NONE, /**< return from async function */ VM_OC_STRING_CONCAT = VM_OC_NONE, /**< string concatenation */ diff --git a/tests/jerry/es.next/function-async-gen1.js b/tests/jerry/es.next/function-async-gen1.js new file mode 100644 index 000000000..5d67a3502 --- /dev/null +++ b/tests/jerry/es.next/function-async-gen1.js @@ -0,0 +1,250 @@ +// 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. + +var successCount = 0 + +function check_fulfilled(p, value, done) +{ + assert(p instanceof Promise) + + p.then(function(v) { + assert(v.value === value) + assert(v.done === done) + successCount++ + }, function() { + assert(false) + }) +} + +function check_rejected(p, value) +{ + assert(p instanceof Promise) + + p.then(function(v) { + assert(false) + }, function(v) { + assert(v === value) + successCount++ + }) +} + +// Test 1 + +var gen, r, o + +async function *f1(p, o) { + assert((yield "Test1") === "Test2") + await p + + assert((yield 1.5) === 2.5) + await "String" + + return o +} + +assert(Object.prototype.toString.call(f1) === "[object AsyncGeneratorFunction]") + +o = {} +gen = f1(new Promise(function(resolve, reject) { r = resolve }), o) + +assert(Object.prototype.toString.call(gen) === "[object AsyncGenerator]") + +check_fulfilled(gen.next(), "Test1", false) +check_fulfilled(gen.next("Test2"), 1.5, false) +check_fulfilled(gen.next(2.5), o, true) +check_fulfilled(gen.next(), undefined, true) +check_fulfilled(gen.next(), undefined, true) + +r(1) + +// Test 2 + +async function *f2(o) { + try { + await {} + yield o + assert(false) + } catch (e) { + assert(e === "Throw") + } + + try { + await "String" + yield 12 + assert(false) + } catch (e) { + assert(e === o) + + throw o + } +} + +o = {} +gen = f2(o) +check_fulfilled(gen.next(), o, false) +check_fulfilled(gen.throw("Throw"), 12, false) +check_rejected(gen.throw(o), o) + +check_fulfilled(gen.next(), undefined, true) +check_rejected(gen.throw(), undefined) +check_fulfilled(gen.return(), undefined, true) + +// Test 3 + +async function *f3() { + throw "Msg" +} + +gen = f3() +check_rejected(gen.next(), "Msg") +gen = f3() +check_rejected(gen.throw("End"), "End") + +// Test 4 + +async function *f4() { + if (state === 0) { + state = 1; + } + + await 1 + + if (state === 3) { + state = 4; + } +} + +var state = 0 +gen = f4() +gen.next() + +if (state === 1) { + state = 2 +} + +gen.next() + +if (state === 2) { + state = 3 +} + +// Test 5 + +async function *f5() { + if (state2 === 0) { + state2 = 1; + } + + yield 1 + + if (state2 === 3) { + state2 = 4; + } +} + +var state2 = 0 +gen = f5() +gen.next() + +if (state2 === 1) { + state2 = 2 +} + +gen.next() + +if (state2 === 2) { + state2 = 3 +} + +// Test 6 + +async function *f6() { + return "Res" +} + +var genLate = f6() +var p = genLate.next() + +assert(p instanceof Promise) + +p.then(function(v) { + assert(v.value === "Res") + assert(v.done === true) + successCount++ + + check_fulfilled(genLate.next(), undefined, true) + var o = {} + check_rejected(genLate.throw(o), o) + check_fulfilled(genLate.return(), undefined, true) +}, function() { + assert(false) +}) + +// Test 7 + +var AsyncGeneratorFun = Object.getPrototypeOf(async function *() {}).constructor; + +o = {} +gen = AsyncGeneratorFun("p, o, x = 5.5", "assert((await p) === 'P'); yield o; return x") +gen = gen(new Promise(function(resolve, reject) { r = resolve }), o) + +check_fulfilled(gen.next(), o, false) +check_fulfilled(gen.next(), 5.5, true) +check_fulfilled(gen.next(), undefined, true) +check_fulfilled(gen.next(), undefined, true) + +r("P") + +// Test 8 + +async function *f8() { + var o = {} + function f() {} + + check_fulfilled(selfGen.next(), o, false) + check_fulfilled(selfGen.next(), f, true) + check_fulfilled(selfGen.next(), undefined, true) + + yield "Str" + yield o + return f +} + +var selfGen = f8(); +check_fulfilled(selfGen.next(), "Str", false) + +// Test 9 + +async function *f9() { + try { + yield "X" + } finally { + successCount++; + } + yield "Y" + return 1; +} + +gen = f9() +check_fulfilled(gen.next(), "X", false) +check_fulfilled(gen.return("Ret"), "Ret", true) +check_fulfilled(gen.next(), undefined, true) + +// END + +function __checkAsync() { + assert(successCount === 29) + assert(state === 4) + assert(state2 === 4) +}