This closes #419 issue on GitHub.
src/njs_buffer.c \
src/njs_iterator.c \
src/njs_scope.c \
+ src/njs_async.c \
"
NJS_LIB_TEST_SRCS=" \
--- /dev/null
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) Nginx, Inc.
+ */
+
+#include <njs_main.h>
+
+
+static void
+njs_async_context_free(njs_vm_t *vm, njs_native_frame_t *frame);
+
+
+njs_int_t
+njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
+{
+ njs_int_t ret;
+ njs_value_t ctor;
+ njs_async_ctx_t *ctx;
+ njs_native_frame_t *frame;
+
+ frame = vm->top_frame;
+ frame->retval = retval;
+
+ ctx = frame->function->context;
+
+ njs_set_function(&ctor, &vm->constructors[NJS_OBJ_TYPE_PROMISE]);
+
+ ctx->capability = njs_promise_new_capability(vm, &ctor);
+ if (njs_slow_path(ctx->capability == NULL)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_function_lambda_call(vm);
+
+ if (ret == NJS_OK) {
+ ret = njs_function_call(vm, njs_function(&ctx->capability->resolve),
+ &njs_value_undefined, retval, 1, &vm->retval);
+
+ } else if (ret == NJS_ERROR) {
+ if (njs_is_memory_error(vm, &vm->retval)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_function_call(vm, njs_function(&ctx->capability->reject),
+ &njs_value_undefined, &vm->retval, 1,
+ &vm->retval);
+ }
+
+ *retval = ctx->capability->promise;
+
+ return ret;
+}
+
+
+njs_int_t
+njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ njs_int_t ret;
+ njs_value_t **cur_local, **cur_closures, **cur_temp, *value;
+ njs_frame_t *frame;
+ njs_async_ctx_t *ctx;
+ njs_native_frame_t *top, *async;
+
+ ctx = vm->top_frame->function->context;
+
+ value = njs_arg(args, nargs, 1);
+ if (njs_is_error(value)) {
+ goto failed;
+ }
+
+ async = ctx->await;
+
+ cur_local = vm->levels[NJS_LEVEL_LOCAL];
+ cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
+ cur_temp = vm->levels[NJS_LEVEL_TEMP];
+ top = vm->top_frame;
+ frame = vm->active_frame;
+
+ vm->levels[NJS_LEVEL_LOCAL] = async->local;
+ vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(async->function);
+ vm->levels[NJS_LEVEL_TEMP] = async->temp;
+
+ vm->top_frame = async;
+ vm->active_frame = (njs_frame_t *) async;
+
+ *njs_scope_value(vm, ctx->index) = *value;
+ vm->retval = *value;
+
+ vm->top_frame->retval = &vm->retval;
+
+ ret = njs_vmcode_interpreter(vm, async->pc);
+
+ vm->levels[NJS_LEVEL_LOCAL] = cur_local;
+ vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
+ vm->levels[NJS_LEVEL_TEMP] = cur_temp;
+
+ vm->top_frame = top;
+ vm->active_frame = frame;
+
+ if (ret == NJS_OK) {
+ ret = njs_function_call(vm, njs_function(&ctx->capability->resolve),
+ &njs_value_undefined, &vm->retval, 1, &vm->retval);
+
+ njs_async_context_free(vm, vm->top_frame);
+
+ } else if (ret == NJS_ERROR) {
+ if (njs_is_memory_error(vm, &vm->retval)) {
+ return NJS_ERROR;
+ }
+
+ value = &vm->retval;
+
+ goto failed;
+ }
+
+ return ret;
+
+failed:
+
+ (void) njs_function_call(vm, njs_function(&ctx->capability->reject),
+ &njs_value_undefined, value, 1, &vm->retval);
+
+ njs_async_context_free(vm, vm->top_frame);
+
+ return NJS_ERROR;
+}
+
+
+njs_int_t
+njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ njs_value_t *value;
+ njs_async_ctx_t *ctx;
+
+ ctx = vm->top_frame->function->context;
+
+ value = njs_arg(args, nargs, 1);
+
+ if (ctx->await->pc == ctx->pc) {
+ (void) njs_function_call(vm, njs_function(&ctx->capability->reject),
+ &njs_value_undefined, value, 1, &vm->retval);
+
+ njs_async_context_free(vm, vm->top_frame);
+
+ return NJS_ERROR;
+ }
+
+ return njs_await_fulfilled(vm, args, nargs, unused);
+}
+
+
+static void
+njs_async_context_free(njs_vm_t *vm, njs_native_frame_t *frame)
+{
+ njs_async_ctx_t *ctx;
+
+ ctx = frame->function->context;
+
+ njs_mp_free(vm->mem_pool, ctx->capability);
+ njs_mp_free(vm->mem_pool, ctx);
+
+ frame->function->context = NULL;
+}
+
+
+static const njs_object_prop_t njs_async_constructor_properties[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("name"),
+ .value = njs_string("AsyncFunction"),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("length"),
+ .value = njs_value(NJS_NUMBER, 1, 1.0),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("prototype"),
+ .value = njs_prop_handler(njs_object_prototype_create),
+ },
+};
+
+
+const njs_object_init_t njs_async_constructor_init = {
+ njs_async_constructor_properties,
+ njs_nitems(njs_async_constructor_properties),
+};
+
+
+static const njs_object_prop_t njs_async_prototype_properties[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("AsyncFunction"),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("constructor"),
+ .value = njs_prop_handler(njs_object_prototype_create_constructor),
+ .configurable = 1,
+ },
+};
+
+
+const njs_object_init_t njs_async_prototype_init = {
+ njs_async_prototype_properties,
+ njs_nitems(njs_async_prototype_properties),
+};
+
+
+const njs_object_type_init_t njs_async_function_type_init = {
+ .constructor = njs_native_ctor(njs_function_constructor, 1, 1),
+ .constructor_props = &njs_async_constructor_init,
+ .prototype_props = &njs_async_prototype_init,
+ .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
+const njs_object_prop_t njs_async_function_instance_properties[] =
+{
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("length"),
+ .value = njs_prop_handler(njs_function_instance_length),
+ .configurable = 1,
+ },
+};
+
+
+const njs_object_init_t njs_async_function_instance_init = {
+ njs_async_function_instance_properties,
+ njs_nitems(njs_async_function_instance_properties),
+};
--- /dev/null
+
+/*
+ * Copyright (C) Alexander Borisov
+ * Copyright (C) Nginx, Inc.
+ */
+
+#ifndef _NJS_ASYNC_H_INCLUDED_
+#define _NJS_ASYNC_H_INCLUDED_
+
+
+typedef struct {
+ njs_promise_capability_t *capability;
+ njs_native_frame_t *await;
+ uintptr_t index;
+ u_char *pc;
+} njs_async_ctx_t;
+
+
+njs_int_t njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval);
+njs_int_t njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused);
+njs_int_t njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused);
+
+
+extern const njs_object_type_init_t njs_async_function_type_init;
+extern const njs_object_init_t njs_async_function_instance_init;
+
+
+#endif /* _NJS_ASYNC_H_INCLUDED_ */
&njs_symbol_type_init,
&njs_string_type_init,
&njs_function_type_init,
+ &njs_async_function_type_init,
&njs_regexp_type_init,
&njs_date_type_init,
&njs_promise_type_init,
return NJS_ERROR;
}
+ ret = njs_object_hash_init(vm, &shared->async_function_instance_hash,
+ &njs_async_function_instance_init);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
ret = njs_object_hash_init(vm, &shared->arrow_instance_hash,
&njs_arrow_instance_init);
if (njs_slow_path(ret != NJS_OK)) {
size_t size;
njs_uint_t i;
njs_object_t *object_prototype, *function_prototype,
- *typed_array_prototype, *error_prototype,
+ *typed_array_prototype, *error_prototype, *async_prototype,
*typed_array_ctor, *error_ctor;
/*
function_prototype = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+ async_prototype = &vm->prototypes[NJS_OBJ_TYPE_ASYNC_FUNCTION].object;
+ async_prototype->__proto__ = function_prototype;
+
for (i = NJS_OBJ_TYPE_OBJECT; i < NJS_OBJ_TYPE_NORMAL_MAX; i++) {
vm->constructors[i].object.__proto__ = function_prototype;
}
{ NJS_VMCODE_DEBUGGER, sizeof(njs_vmcode_debugger_t),
njs_str("DEBUGGER ") },
+
+ { NJS_VMCODE_AWAIT, sizeof(njs_vmcode_await_t),
+ njs_str("AWAIT ") },
};
njs_function_t *
-njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda)
+njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
+ njs_bool_t async)
{
size_t size;
+ njs_object_t *proto;
njs_function_t *function;
size = sizeof(njs_function_t) + lambda->nclosures * sizeof(njs_value_t *);
if (function->ctor) {
function->object.shared_hash = vm->shared->function_instance_hash;
+ } else if (async) {
+ function->object.shared_hash = vm->shared->async_function_instance_hash;
+
} else {
function->object.shared_hash = vm->shared->arrow_instance_hash;
}
- function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+ if (async) {
+ proto = &vm->prototypes[NJS_OBJ_TYPE_ASYNC_FUNCTION].object;
+
+ } else {
+ proto = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+ }
+
+ function->object.__proto__ = proto;
function->object.type = NJS_FUNCTION;
+
function->object.extensible = 1;
return function;
njs_function_t *
njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
{
- njs_function_t *function, *copy;
+ njs_function_t *function, *copy;
+ njs_object_type_t type;
function = njs_function(value);
return NULL;
}
+ type = njs_function_object_type(vm, function);
+
if (copy->ctor) {
copy->object.shared_hash = vm->shared->function_instance_hash;
+ } else if (type == NJS_OBJ_TYPE_ASYNC_FUNCTION) {
+ copy->object.shared_hash = vm->shared->async_function_instance_hash;
+
} else {
copy->object.shared_hash = vm->shared->arrow_instance_hash;
}
njs_function_t *
njs_function_copy(njs_vm_t *vm, njs_function_t *function)
{
- size_t size, n;
- njs_value_t **from, **to;
- njs_function_t *copy;
+ size_t size, n;
+ njs_value_t **from, **to;
+ njs_function_t *copy;
+ njs_object_type_t type;
n = (function->native) ? 0 : function->u.lambda->nclosures;
}
*copy = *function;
- copy->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
+
+ type = njs_function_object_type(vm, function);
+
+ copy->object.__proto__ = &vm->prototypes[type].object;
copy->object.shared = 0;
if (n == 0) {
njs_value_t *value, *bound, **new, **temp;
njs_frame_t *frame;
njs_function_t *target;
+ njs_async_ctx_t *ctx;
njs_native_frame_t *native_frame;
njs_function_lambda_t *lambda;
lambda = target->u.lambda;
}
+ if (njs_function_object_type(vm, target) == NJS_OBJ_TYPE_ASYNC_FUNCTION) {
+ ctx = njs_mp_alloc(vm->mem_pool, sizeof(njs_async_ctx_t));
+ if (njs_slow_path(ctx == NULL)) {
+ njs_memory_error(vm);
+ return NJS_ERROR;
+ }
+
+ ctx->await = NULL;
+ target->context = ctx;
+ }
+
args_count = function->args_offset + njs_max(nargs, lambda->nargs);
value_count = args_count + njs_max(args_count, lambda->nlocal);
}
+njs_int_t
+njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
+{
+ njs_native_frame_t *frame;
+
+ frame = vm->top_frame;
+ frame->retval = retval;
+
+ if (njs_function_object_type(vm, frame->function)
+ == NJS_OBJ_TYPE_ASYNC_FUNCTION)
+ {
+ return njs_async_function_frame_invoke(vm, retval);
+ }
+
+ if (frame->native) {
+ return njs_function_native_call(vm);
+
+ } else {
+ return njs_function_lambda_call(vm);
+ }
+}
+
+
void
njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native)
{
}
+njs_int_t
+njs_function_frame_save(njs_vm_t *vm, njs_native_frame_t *native, u_char *pc)
+{
+ size_t value_count, n;
+ njs_value_t *start, *end, *p, **new, *value, **local;
+ njs_function_t *function;
+ njs_native_frame_t *active;
+
+ active = &vm->active_frame->native;
+ value_count = njs_function_frame_value_count(active);
+
+ function = active->function;
+
+ new = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE);
+ value = (njs_value_t *) (new + value_count
+ + function->u.lambda->temp);
+
+ *native = *active;
+
+ native->arguments = value;
+ native->arguments_offset = value + (function->args_offset - 1);
+ native->local = new + njs_function_frame_args_count(active);
+ native->temp = new + value_count;
+ native->pc = pc;
+
+ start = njs_function_frame_values(active, &end);
+ p = native->arguments;
+
+ while (start < end) {
+ *p = *start++;
+ *new++ = p++;
+ }
+
+ /* Move all arguments. */
+
+ p = native->arguments;
+ local = native->local + function->args_offset;
+
+ for (n = 0; n < function->args_count; n++) {
+ if (!njs_is_valid(p)) {
+ njs_set_undefined(p);
+ }
+
+ *local++ = p++;
+ }
+
+ return NJS_OK;
+}
+
+
+njs_object_type_t
+njs_function_object_type(njs_vm_t *vm, njs_function_t *function)
+{
+ if (function->object.shared_hash.slot
+ == vm->shared->async_function_instance_hash.slot)
+ {
+ return NJS_OBJ_TYPE_ASYNC_FUNCTION;
+ }
+
+ return NJS_OBJ_TYPE_FUNCTION;
+}
+
+
njs_int_t
njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
njs_function_lambda_t *lambda)
}
-static njs_int_t
+njs_int_t
njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused)
+ njs_index_t async)
{
njs_chb_t chain;
njs_int_t ret;
NJS_TOKEN_ILLEGAL
};
+ static const njs_token_type_t safe_ast_async[] = {
+ NJS_TOKEN_END,
+ NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION,
+ NJS_TOKEN_STATEMENT,
+ NJS_TOKEN_RETURN,
+ NJS_TOKEN_THIS,
+ NJS_TOKEN_ILLEGAL
+ };
+
if (!vm->options.unsafe && nargs != 2) {
goto fail;
}
njs_chb_init(&chain, vm->mem_pool);
- njs_chb_append_literal(&chain, "(function(");
+ if (async) {
+ njs_chb_append_literal(&chain, "(async function(");
+
+ } else {
+ njs_chb_append_literal(&chain, "(function(");
+ }
for (i = 1; i < nargs - 1; i++) {
ret = njs_value_to_chain(vm, &chain, njs_argument(args, i));
*/
node = parser.node;
- type = &safe_ast[0];
+ type = (async) ? &safe_ast_async[0] : &safe_ast[0];
for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
if (node == NULL) {
lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
- function = njs_function_alloc(vm, lambda);
+ function = njs_function_alloc(vm, lambda, (njs_bool_t) async);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
};
-static njs_int_t
+njs_int_t
njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
{
};
-njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda);
+njs_function_t *njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
+ njs_bool_t async);
njs_function_t *njs_function_value_copy(njs_vm_t *vm, njs_value_t *value);
njs_int_t njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
njs_value_t *name, const char *prefix);
njs_native_frame_t *frame);
njs_int_t njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
+njs_int_t njs_function_constructor(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused);
+njs_int_t njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop,
+ njs_value_t *value, njs_value_t *setval, njs_value_t *retval);
njs_int_t njs_eval_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
njs_index_t unused);
njs_int_t njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
njs_int_t njs_function_native_call(njs_vm_t *vm);
njs_native_frame_t *njs_function_frame_alloc(njs_vm_t *vm, size_t size);
void njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *frame);
+njs_int_t njs_function_frame_save(njs_vm_t *vm, njs_native_frame_t *native,
+ u_char *pc);
+njs_object_type_t njs_function_object_type(njs_vm_t *vm,
+ njs_function_t *function);
njs_int_t njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
njs_function_lambda_t *lambda);
njs_int_t njs_function_capture_global_closures(njs_vm_t *vm,
njs_function_t *function);
+njs_int_t njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval);
njs_inline njs_function_lambda_t *
}
-njs_inline njs_int_t
-njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
-{
- njs_native_frame_t *frame;
-
- frame = vm->top_frame;
- frame->retval = retval;
-
- if (frame->native) {
- return njs_function_native_call(vm);
-
- } else {
- return njs_function_lambda_call(vm);
- }
-}
-
-
njs_inline njs_int_t
njs_function_call(njs_vm_t *vm, njs_function_t *function,
const njs_value_t *this, const njs_value_t *args,
}
+njs_inline size_t
+njs_function_frame_size(njs_native_frame_t *frame)
+{
+ size_t size;
+ uintptr_t start;
+
+ start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE);
+ size = ((uintptr_t) frame->arguments - start) / sizeof(njs_value_t *);
+
+ return NJS_FRAME_SIZE + (size * sizeof(njs_value_t *))
+ + (size * sizeof(njs_value_t));
+}
+
+
+njs_inline size_t
+njs_function_frame_args_count(njs_native_frame_t *frame)
+{
+ uintptr_t start;
+
+ start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE);
+
+ return ((uintptr_t) frame->local - start) / sizeof(njs_value_t *);
+}
+
+
+njs_inline size_t
+njs_function_frame_value_count(njs_native_frame_t *frame)
+{
+ uintptr_t start;
+
+ start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE);
+
+ return ((uintptr_t) frame->temp - start) / sizeof(njs_value_t *);
+}
+
+
+njs_inline njs_value_t *
+njs_function_frame_values(njs_native_frame_t *frame, njs_value_t **end)
+{
+ size_t count;
+ uintptr_t start;
+
+ start = (uintptr_t) ((u_char *) frame + NJS_FRAME_SIZE);
+ count = ((uintptr_t) frame->arguments - start) / sizeof(njs_value_t *);
+
+ *end = frame->arguments + count;
+
+ return frame->arguments;
+}
+
+
extern const njs_object_type_init_t njs_function_type_init;
extern const njs_object_init_t njs_function_instance_init;
extern const njs_object_init_t njs_arrow_instance_init;
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_export_statement_end(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
+static njs_int_t njs_generate_await(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node);
+static njs_int_t njs_generate_await_end(njs_vm_t *vm,
+ njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_wo_dest(njs_vm_t *vm,
njs_generator_t *generator, njs_parser_node_t *node);
static njs_int_t njs_generate_wo_dest_after(njs_vm_t *vm,
return njs_generate_array(vm, generator, node);
case NJS_TOKEN_FUNCTION_EXPRESSION:
+ case NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION:
return njs_generate_function_expression(vm, generator, node);
case NJS_TOKEN_FUNCTION:
return njs_generate_name(vm, generator, node);
case NJS_TOKEN_FUNCTION_DECLARATION:
+ case NJS_TOKEN_ASYNC_FUNCTION_DECLARATION:
return njs_generate_function_declaration(vm, generator, node);
case NJS_TOKEN_FUNCTION_CALL:
case NJS_TOKEN_EXPORT:
return njs_generate_export_statement(vm, generator, node);
+ case NJS_TOKEN_AWAIT:
+ return njs_generate_await(vm, generator, node);
+
default:
njs_thread_log_debug("unknown token: %d", node->token);
njs_internal_error(vm, "Generator failed: unknown token");
njs_generate_code(generator, njs_vmcode_function_t, function,
NJS_VMCODE_FUNCTION, 1, node);
function->lambda = lambda;
+ function->async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION);
node->index = njs_generate_object_dest_index(vm, generator, node);
if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
njs_generate_code(generator, njs_vmcode_function_t, function,
NJS_VMCODE_FUNCTION, 1, node);
function->lambda = lambda;
+ function->async = 0;
node->index = njs_generate_object_dest_index(vm, generator, node);
if (njs_slow_path(node->index == NJS_INDEX_ERROR)) {
njs_parser_node_t *node)
{
njs_int_t ret;
+ njs_bool_t async;
njs_variable_t *var;
njs_function_t *function;
njs_function_lambda_t *lambda;
return ret;
}
- function = njs_function_alloc(vm, lambda);
+ async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_DECLARATION);
+ function = njs_function_alloc(vm, lambda, async);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
}
+static njs_int_t
+njs_generate_await(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_generator_next(generator, njs_generate, node->right);
+
+ return njs_generator_after(vm, generator,
+ njs_queue_first(&generator->stack), node,
+ njs_generate_await_end, NULL, 0);
+}
+
+
+static njs_int_t
+njs_generate_await_end(njs_vm_t *vm, njs_generator_t *generator,
+ njs_parser_node_t *node)
+{
+ njs_index_t index;
+ njs_vmcode_await_t *code;
+
+ index = node->right->index;
+
+ if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+ return NJS_ERROR;
+ }
+
+ njs_generate_code(generator, njs_vmcode_await_t, code,
+ NJS_VMCODE_AWAIT, 1, node);
+ code->retval = index;
+ node->index = index;
+
+ return njs_generator_stack_pop(vm, generator, NULL);
+}
+
+
static njs_int_t
njs_generate_wo_dest(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
NJS_TOKEN_ARGUMENT,
NJS_TOKEN_RETURN,
+ NJS_TOKEN_ASYNC_FUNCTION_DECLARATION,
+ NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION,
+
NJS_TOKEN_REGEXP,
NJS_TOKEN_EXTERNAL,
#include <njs_date.h>
#include <njs_promise.h>
#include <njs_iterator.h>
+#include <njs_async.h>
#include <njs_math.h>
#include <njs_json.h>
njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_unary_expression_next(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_await(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current);
+static njs_int_t njs_parser_await_after(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current);
static njs_int_t njs_parser_exponentiation_expression(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current);
}
-static njs_int_t
-njs_parser_async_function_expression(njs_parser_t *parser,
- njs_lexer_token_t *token, njs_queue_link_t *current)
-{
- return njs_parser_not_supported(parser, token);
-}
-
-
static njs_int_t
njs_parser_generator_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
static njs_int_t
-njs_parser_function_or_generator(njs_parser_t *parser,
- njs_lexer_token_t *token, njs_queue_link_t *current)
+njs_parser_function_or_generator_handler(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current, njs_bool_t is_async)
{
njs_parser_node_t *node, *cur;
- if (token->type != NJS_TOKEN_FUNCTION) {
- return NJS_DECLINED;
- }
-
cur = parser->node;
if (token->type == NJS_TOKEN_MULTIPLICATION) {
njs_parser_next(parser, njs_parser_generator_declaration);
} else {
- node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_DECLARATION);
+ if (is_async) {
+ node = njs_parser_node_new(parser,
+ NJS_TOKEN_ASYNC_FUNCTION_DECLARATION);
+ } else {
+ node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_DECLARATION);
+ }
+
if (node == NULL) {
return NJS_ERROR;
}
}
+static njs_int_t
+njs_parser_function_or_generator(njs_parser_t *parser,
+ njs_lexer_token_t *token, njs_queue_link_t *current)
+{
+ if (token->type != NJS_TOKEN_FUNCTION) {
+ return NJS_DECLINED;
+ }
+
+ return njs_parser_function_or_generator_handler(parser, token, current, 0);
+}
+
+
static njs_int_t
njs_parser_async_function_or_generator(njs_parser_t *parser,
njs_lexer_token_t *token, njs_queue_link_t *current)
return NJS_DECLINED;
}
- if (token->type == NJS_TOKEN_MULTIPLICATION) {
- njs_parser_next(parser, njs_parser_generator_declaration);
- } else {
- njs_parser_next(parser, njs_parser_async_function_expression);
- }
+ njs_lexer_consume_token(parser->lexer, 1);
- return NJS_OK;
+ return njs_parser_function_or_generator_handler(parser, token, current, 1);
}
goto reference;
}
+ njs_lexer_consume_token(parser->lexer, 1);
+
next = njs_lexer_peek_token(parser->lexer, next, 0);
if (njs_slow_path(next == NULL)) {
return NJS_ERROR;
/* GeneratorExpression */
if (next->type == NJS_TOKEN_MULTIPLICATION) {
- njs_lexer_consume_token(parser->lexer, 1);
njs_parser_next(parser, njs_parser_async_generator_expression);
} else {
- njs_parser_next(parser, njs_parser_async_function_expression);
+ node = njs_parser_node_new(parser,
+ NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION);
+ if (node == NULL) {
+ return NJS_ERROR;
+ }
+
+ node->token_line = next->line;
+ parser->node = node;
+
+ njs_parser_next(parser, njs_parser_function_expression);
}
- return NJS_OK;
+ break;
/* RegularExpressionLiteral */
case NJS_TOKEN_DIVISION:
* ArgumentList , )
*/
+ parser->in_args = 1;
+
if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) {
njs_lexer_consume_token(parser->lexer, 1);
return njs_parser_stack_pop(parser);
njs_parser_parenthesis_or_comma(njs_parser_t *parser, njs_lexer_token_t *token,
njs_queue_link_t *current)
{
+ parser->in_args = 0;
+
if (token->type == NJS_TOKEN_CLOSE_PARENTHESIS) {
njs_lexer_consume_token(parser->lexer, 1);
return njs_parser_stack_pop(parser);
/* AwaitExpression */
case NJS_TOKEN_AWAIT:
- return njs_parser_not_supported(parser, token);
+ njs_parser_next(parser, njs_parser_await);
+ return NJS_OK;
default:
njs_parser_next(parser, njs_parser_update_expression);
}
+static njs_int_t
+njs_parser_await(njs_parser_t *parser, njs_lexer_token_t *token,
+ njs_queue_link_t *current)
+{
+ njs_parser_node_t *node;
+ njs_parser_scope_t *scope;
+
+ scope = njs_function_scope(parser->scope);
+
+ if (!scope->async) {
+ njs_parser_syntax_error(parser,
+ "await is only valid in async functions");
+ return NJS_ERROR;
+ }
+
+ node = parser->node;
+
+ if (parser->in_args) {
+ njs_parser_syntax_error(parser, "await in arguments not supported");
+ return NJS_ERROR;
+ }
+
+ node = njs_parser_node_new(parser, NJS_TOKEN_AWAIT);
+ if (njs_slow_path(node == NULL)) {
+ return NJS_ERROR;
+ }
+
+ node->token_line = token->line;
+
+ njs_lexer_consume_token(parser->lexer, 1);
+
+ parser->node = NULL;
+
+ njs_parser_next(parser, njs_parser_unary_expression);
+
+ return njs_parser_after(parser, current, node, 0,
+ njs_parser_await_after);
+}
+
+
+static njs_int_t
+njs_parser_await_after(njs_parser_t *parser, njs_lexer_token_t *token,
+ njs_queue_link_t *current)
+{
+ parser->target->right = parser->node;
+ parser->node = parser->target;
+
+ return njs_parser_stack_pop(parser);
+}
+
+
/*
* 12.6 Exponentiation Operator.
*/
{
njs_bool_t rest_parameters;
+ if (token->type == NJS_TOKEN_ASYNC) {
+ token = njs_lexer_peek_token(parser->lexer, token, 1);
+ if (token == NULL) {
+ return NJS_ERROR;
+ }
+ }
+
if (token->type != NJS_TOKEN_OPEN_PARENTHESIS
&& !njs_lexer_token_is_binding_identifier(token))
{
{
njs_int_t ret;
uintptr_t unique_id;
+ njs_bool_t async;
njs_variable_t *var;
njs_parser_node_t *node;
return NJS_ERROR;
}
+ async = (node->token_type == NJS_TOKEN_ASYNC_FUNCTION_DECLARATION);
+ parser->scope->async = async;
+
njs_parser_next(parser, njs_parser_function_parse);
return njs_parser_after(parser, current, node, 1,
{
njs_int_t ret;
uintptr_t unique_id;
+ njs_bool_t async;
njs_variable_t *var;
njs_function_lambda_t *lambda;
return NJS_ERROR;
}
+ async = (parser->node->token_type == NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION);
+ parser->scope->async = async;
+
var = NULL;
if (njs_lexer_token_is_binding_identifier(token)) {
return NJS_ERROR;
}
- lambda = njs_function_lambda_alloc(parser->vm, 1);
+ lambda = njs_function_lambda_alloc(parser->vm, !async);
if (lambda == NULL) {
return NJS_ERROR;
}
{
njs_int_t ret;
uintptr_t unique_id;
+ njs_bool_t async;
njs_variable_t *arg, *var;
njs_parser_node_t *node, *name;
njs_function_lambda_t *lambda;
- node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ if (token->type == NJS_TOKEN_ASYNC) {
+ njs_lexer_consume_token(parser->lexer, 1);
+
+ token = njs_lexer_token(parser->lexer, 0);
+ if (token == NULL) {
+ return NJS_ERROR;
+ }
+
+ async = 1;
+ node = njs_parser_node_new(parser, NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION);
+
+ } else {
+ async = 0;
+ node = njs_parser_node_new(parser, NJS_TOKEN_FUNCTION_EXPRESSION);
+ }
+
if (node == NULL) {
return NJS_ERROR;
}
return NJS_ERROR;
}
+ parser->scope->async = async;
+
name = njs_parser_node_new(parser, NJS_TOKEN_NAME);
if (name == NULL) {
return NJS_ERROR;
uint8_t module;
uint8_t arrow_function;
uint8_t dest_disable;
+ uint8_t async;
};
uintptr_t undefined_id;
njs_bool_t strict_semicolon;
uint32_t line;
+ njs_bool_t in_args;
};
#include <njs_main.h>
-typedef enum {
- NJS_PROMISE_PENDING = 0,
- NJS_PROMISE_FULFILL,
- NJS_PROMISE_REJECTED
-} njs_promise_type_t;
-
typedef enum {
NJS_PROMISE_HANDLE = 0,
NJS_PROMISE_REJECT
NJS_PROMISE_ANY
} njs_promise_function_type_t;
-typedef struct {
- njs_promise_type_t state;
- njs_value_t result;
- njs_queue_t fulfill_queue;
- njs_queue_t reject_queue;
- njs_bool_t is_handled;
-} njs_promise_data_t;
-
-typedef struct {
- njs_value_t promise;
- njs_value_t resolve;
- njs_value_t reject;
-} njs_promise_capability_t;
-
typedef struct {
njs_promise_capability_t *capability;
njs_promise_type_t type;
njs_promise_t *promise, njs_promise_rejection_type_t operation);
static njs_int_t njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t retval);
-static njs_promise_t *njs_promise_resolve(njs_vm_t *vm,
- njs_value_t *constructor, njs_value_t *x);
static njs_int_t njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args,
njs_uint_t nargs, njs_index_t retval);
-static njs_int_t njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
- njs_value_t *fulfilled, njs_value_t *rejected,
- njs_promise_capability_t *capability);
static njs_int_t njs_promise_then_finally_function(njs_vm_t *vm,
njs_value_t *args, njs_uint_t nargs, njs_index_t unused);
static njs_int_t njs_promise_then_finally_return(njs_vm_t *vm,
}
-static njs_function_t *
+njs_function_t *
njs_promise_create_function(njs_vm_t *vm, size_t context_size)
{
njs_function_t *function;
}
-static njs_promise_capability_t *
+njs_promise_capability_t *
njs_promise_new_capability(njs_vm_t *vm, njs_value_t *constructor)
{
njs_int_t ret;
}
-static njs_promise_t *
+njs_promise_t *
njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor, njs_value_t *x)
{
njs_int_t ret;
}
-static njs_int_t
+njs_int_t
njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
njs_value_t *fulfilled, njs_value_t *rejected,
njs_promise_capability_t *capability)
#define _NJS_PROMISE_H_INCLUDED_
-njs_int_t
-njs_promise_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
- njs_index_t unused);
+typedef enum {
+ NJS_PROMISE_PENDING = 0,
+ NJS_PROMISE_FULFILL,
+ NJS_PROMISE_REJECTED
+} njs_promise_type_t;
+
+typedef struct {
+ njs_value_t promise;
+ njs_value_t resolve;
+ njs_value_t reject;
+} njs_promise_capability_t;
+
+typedef struct {
+ njs_promise_type_t state;
+ njs_value_t result;
+ njs_queue_t fulfill_queue;
+ njs_queue_t reject_queue;
+ njs_bool_t is_handled;
+} njs_promise_data_t;
+
+
+njs_int_t njs_promise_constructor(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t unused);
+njs_promise_capability_t *njs_promise_new_capability(njs_vm_t *vm,
+ njs_value_t *constructor);
+njs_function_t *njs_promise_create_function(njs_vm_t *vm, size_t context_size);
+njs_int_t njs_promise_perform_then(njs_vm_t *vm, njs_value_t *value,
+ njs_value_t *fulfilled, njs_value_t *rejected,
+ njs_promise_capability_t *capability);
+njs_promise_t *njs_promise_resolve(njs_vm_t *vm, njs_value_t *constructor,
+ njs_value_t *x);
extern const njs_object_type_init_t njs_promise_type_init;
njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
uintptr_t unique_id, njs_variable_type_t type)
{
+ njs_bool_t ctor;
njs_value_t **declr;
njs_variable_t *var;
njs_parser_scope_t *root;
return NULL;
}
- lambda = njs_function_lambda_alloc(parser->vm, 1);
+ ctor = parser->node->token_type != NJS_TOKEN_ASYNC_FUNCTION_DECLARATION;
+
+ lambda = njs_function_lambda_alloc(parser->vm, ctor);
if (lambda == NULL) {
return NULL;
}
return ret;
}
- return njs_vmcode_interpreter(vm, vm->start);
+ ret = njs_vmcode_interpreter(vm, vm->start);
+
+ return (ret == NJS_ERROR) ? NJS_ERROR : NJS_OK;
}
NJS_OBJ_TYPE_SYMBOL,
NJS_OBJ_TYPE_STRING,
NJS_OBJ_TYPE_FUNCTION,
+ NJS_OBJ_TYPE_ASYNC_FUNCTION,
NJS_OBJ_TYPE_REGEXP,
NJS_OBJ_TYPE_DATE,
NJS_OBJ_TYPE_PROMISE,
njs_lvlhsh_t array_instance_hash;
njs_lvlhsh_t string_instance_hash;
njs_lvlhsh_t function_instance_hash;
+ njs_lvlhsh_t async_function_instance_hash;
njs_lvlhsh_t arrow_instance_hash;
njs_lvlhsh_t arguments_object_instance_hash;
njs_lvlhsh_t regexp_instance_hash;
static njs_jump_off_t njs_vmcode_return(njs_vm_t *vm, njs_value_t *invld,
njs_value_t *retval);
+static njs_jump_off_t njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await);
+
static njs_jump_off_t njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value,
njs_value_t *offset, u_char *pc);
static njs_jump_off_t njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *value,
njs_value_t numeric1, numeric2, primitive1, primitive2;
njs_frame_t *frame;
njs_jump_off_t ret;
+ njs_vmcode_await_t *await;
njs_native_frame_t *previous, *native;
njs_property_next_t *next;
njs_vmcode_finally_t *finally;
break;
+ case NJS_VMCODE_AWAIT:
+ await = (njs_vmcode_await_t *) pc;
+ return njs_vmcode_await(vm, await);
+
case NJS_VMCODE_TRY_START:
ret = njs_vmcode_try_start(vm, value1, value2, pc);
if (njs_slow_path(ret == NJS_ERROR)) {
code = (njs_vmcode_function_t *) pc;
lambda = code->lambda;
- function = njs_function_alloc(vm, lambda);
+ function = njs_function_alloc(vm, lambda, code->async);
if (njs_slow_path(function == NULL)) {
return NJS_ERROR;
}
}
+static njs_jump_off_t
+njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await)
+{
+ size_t size;
+ njs_int_t ret;
+ njs_frame_t *frame;
+ njs_value_t ctor, val, on_fulfilled, on_rejected, *value;
+ njs_promise_t *promise;
+ njs_function_t *fulfilled, *rejected;
+ njs_async_ctx_t *ctx;
+ njs_native_frame_t *active;
+
+ active = &vm->active_frame->native;
+ ctx = active->function->context;
+
+ value = njs_scope_valid_value(vm, await->retval);
+ if (njs_slow_path(value == NULL)) {
+ return NJS_ERROR;
+ }
+
+ njs_set_function(&ctor, &vm->constructors[NJS_OBJ_TYPE_PROMISE]);
+
+ promise = njs_promise_resolve(vm, &ctor, value);
+ if (njs_slow_path(promise == NULL)) {
+ return NJS_ERROR;
+ }
+
+ if (ctx->await == NULL) {
+ size = njs_function_frame_size(active);
+
+ fulfilled = njs_promise_create_function(vm, size);
+ if (njs_slow_path(fulfilled == NULL)) {
+ return NJS_ERROR;
+ }
+
+ ctx->await = fulfilled->context;
+
+ ret = njs_function_frame_save(vm, ctx->await, NULL);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ } else {
+ fulfilled = njs_promise_create_function(vm, 0);
+ if (njs_slow_path(fulfilled == NULL)) {
+ return NJS_ERROR;
+ }
+ }
+
+ ctx->pc = (u_char *) await + sizeof(njs_vmcode_await_t);
+ ctx->index = await->retval;
+
+ frame = (njs_frame_t *) active;
+
+ if (frame->exception.catch != NULL) {
+ ctx->await->pc = frame->exception.catch;
+
+ } else {
+ ctx->await->pc = ctx->pc;
+ }
+
+ fulfilled->context = ctx;
+ fulfilled->args_count = 1;
+ fulfilled->u.native = njs_await_fulfilled;
+
+ rejected = njs_promise_create_function(vm, 0);
+ if (njs_slow_path(rejected == NULL)) {
+ return NJS_ERROR;
+ }
+
+ rejected->context = ctx;
+ rejected->args_count = 1;
+ rejected->u.native = njs_await_rejected;
+
+ njs_set_object(&val, &promise->object);
+ njs_set_function(&on_fulfilled, fulfilled);
+ njs_set_function(&on_rejected, rejected);
+
+ ret = njs_promise_perform_then(vm, &val, &on_fulfilled, &on_rejected, NULL);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ (void) njs_vmcode_return(vm, NULL, &vm->retval);
+
+ return NJS_AGAIN;
+}
+
+
/*
* njs_vmcode_try_start() is set on the start of a "try" block to create
* a "try" block, to set a catch address to the start of a "catch" or
NJS_VMCODE_ARGUMENTS,
NJS_VMCODE_PROTO_INIT,
+ NJS_VMCODE_AWAIT,
+
NJS_VMCODE_TRY_START,
NJS_VMCODE_THROW,
NJS_VMCODE_TRY_BREAK,
njs_vmcode_t code;
njs_index_t retval;
njs_function_lambda_t *lambda;
+ njs_bool_t async;
} njs_vmcode_function_t;
} njs_vmcode_debugger_t;
+typedef struct {
+ njs_vmcode_t code;
+ njs_index_t retval;
+} njs_vmcode_await_t;
+
+
njs_int_t njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc);
njs_object_t *njs_function_new_object(njs_vm_t *vm, njs_value_t *constructor);
{ njs_str("const const"),
njs_str("SyntaxError: Unexpected token \"const\" in 1") },
+
+ /* Async/Await */
+
+ { njs_str("async function f() {}; f.prototype"),
+ njs_str("undefined") },
+
+ { njs_str("async function f() {await 1}"),
+ njs_str("undefined") },
+
+ { njs_str("function f() {await 1}"),
+ njs_str("SyntaxError: await is only valid in async functions in 1") },
+
+ { njs_str("async function f() {function a() {await 1}}"),
+ njs_str("SyntaxError: await is only valid in async functions in 1") },
+
+ { njs_str("async function f() {() => {await 1}}"),
+ njs_str("SyntaxError: await is only valid in async functions in 1") },
+
+ { njs_str("function f() {async () => {await 1}}"),
+ njs_str("undefined") },
+
+ { njs_str("let f = async () => {await 1}"),
+ njs_str("undefined") },
+
+ { njs_str("let f = () => {await 1}"),
+ njs_str("SyntaxError: await is only valid in async functions in 1") },
+
+ { njs_str("(async function() {await 1})"),
+ njs_str("[object AsyncFunction]") },
+
+ { njs_str("(function() {await 1})"),
+ njs_str("SyntaxError: await is only valid in async functions in 1") },
+
+ { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;"
+ "ctor"),
+ njs_str("[object Function]") },
+
+ { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;"
+ "ctor()"),
+ njs_str("[object AsyncFunction]") },
+
+ { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;"
+ "new ctor();"),
+ njs_str("[object AsyncFunction]") },
+
+ { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;"
+ "let f = new ctor(); f()"),
+ njs_str("[object Promise]") },
+
+ { njs_str("let ctor = Object.getPrototypeOf(async function(){}).constructor;"
+ "let f = new ctor('x', 'await 1; return x'); f(1)"),
+ njs_str("[object Promise]") },
+
+ { njs_str("let f = new Function('x', 'await 1; return x'); f(1)"),
+ njs_str("SyntaxError: await is only valid in async functions in runtime:1") },
+
+ { njs_str("new AsyncFunction()"),
+ njs_str("ReferenceError: \"AsyncFunction\" is not defined") },
+
+ { njs_str("(async function() {console.log(await 111)})"),
+ njs_str("SyntaxError: await in arguments not supported in 1") },
+
+ { njs_str("(async function() {console.log('Number: ' + await 111)})"),
+ njs_str("SyntaxError: await in arguments not supported in 1") },
+
+ { njs_str("function f(a) {}"
+ "(async function() {f(await 111)})"),
+ njs_str("SyntaxError: await in arguments not supported in 1") },
+
+ { njs_str("function f(a, b, c) {}"
+ "(async function() {f(1, 'a', await 111)})"),
+ njs_str("SyntaxError: await in arguments not supported in 1") },
+
+ { njs_str("function f(a) {}"
+ "(async function() {f('Number: ' + await 111)})"),
+ njs_str("SyntaxError: await in arguments not supported in 1") },
};
--- /dev/null
+async function af(x) {
+ const y = await new Promise(resolve => {resolve(x + 10)});
+
+ return x + y;
+}
+
+af(50).then(v => console.log(v));
--- /dev/null
+async function af(x) {
+ return x;
+}
+
+af(12345).then(v => console.log(v));
--- /dev/null
+async function add(x) {
+ return await new Promise((resolve, reject) => {reject(x)}).catch(v => v + 1);
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+async function add(x) {
+ return await new Promise((resolve, reject) => {reject(x)})
+ .finally(() => console.log(x + 1));
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+let stage = [];
+
+async function f() {
+ let sum = 0;
+
+ stage.push(2);
+
+ for (let x = 4; x < 14; x++) {
+ sum += await new Promise((resolve, reject) => {resolve(x)});
+
+ stage.push(x);
+ }
+
+ stage.push("end");
+
+ return sum;
+}
+
+stage.push(1);
+
+f().then(v => {console.log(v, stage.join(", "))})
+
+stage.push(3);
--- /dev/null
+function pr(x) {
+ return new Promise(resolve => {resolve(x)});
+}
+
+async function add(x) {
+ const a = pr(20);
+ const b = pr(50);
+ return await a + await b;
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+async function add(x) {
+ return await new Promise((resolve, reject) => {reject(x)});
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+function pr(x) {
+ return new Promise(resolve => {resolve(x)})
+ .then(v => v).then(v => v);
+}
+
+let stage = [];
+
+async function f() {
+ let sum = 0;
+
+ stage.push(2);
+
+ const a1 = await pr(10);
+
+ stage.push(4);
+
+ const a2 = await pr(20);
+
+ stage.push(5);
+
+ return a1 + a2;
+}
+
+stage.push(1);
+
+f().then(v => {console.log(v, stage.join(", "))})
+
+stage.push(3);
--- /dev/null
+function pr(x) {
+ return new Promise(resolve => {resolve(x)}).then(v => {throw v});
+}
+
+async function add(x) {
+ const a = await pr(x);
+ const b = await pr(x);
+
+ return a + b;
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+function pr(x) {
+ return new Promise(resolve => {resolve(x)});
+}
+
+async function add(x) {
+ const a = await pr(x);
+
+ throw a + 1;
+
+ const b = await pr(x + 10);
+
+ return a + b;
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+function pr(x) {
+ return new Promise(resolve => {resolve(x)}).then(v => {throw v}).catch(v => v);
+}
+
+async function add(x) {
+ const a = await pr(x);
+ const b = await pr(x + 10);
+
+ return a + b;
+}
+
+add(50).then(v => {console.log(v)});
--- /dev/null
+function pr(x) {
+ return new Promise(resolve => {resolve(x)});
+}
+
+async function add(x) {
+ const a = await pr(x);
+
+ throw a + 1;
+
+ const b = await pr(x + 10);
+
+ return a + b;
+}
+
+add(50).then(v => {console.log(v - 1)}).catch(v => console.log(v));
--- /dev/null
+async function af() {
+ try {
+ await new Promise(function(resolve, reject) {
+ reject("reject");
+ });
+
+ console.log("shouldn't happen");
+ }
+ catch (v) {
+ console.log(v);
+ }
+ finally {
+ console.log("finally");
+ }
+
+ return "end";
+};
+
+af().then(v => console.log(v));
--- /dev/null
+async function af() {
+ try {
+ await new Promise(function(resolve, reject) {
+ reject("reject");
+ });
+
+ console.log("shouldn't happen: try");
+ }
+ finally {
+ await new Promise(function(resolve, reject) {
+ reject("finally reject");
+ });
+
+ console.log("shouldn't happen: finally");
+ }
+
+ return "shouldn't happen: end";
+};
+
+af().then(v => console.log(v));
--- /dev/null
+async function af() {
+ try {
+ throw "try";
+
+ console.log("shouldn't happen: try");
+ }
+ finally {
+ console.log("finally");
+ }
+
+ return "shouldn't happen: end";
+};
+
+af().then(v => console.log(v));
--- /dev/null
+async function af() {
+ try {
+ throw "try";
+
+ console.log("shouldn't happen: try");
+ }
+ catch (v) {
+ console.log(v);
+ }
+ finally {
+ console.log("finally");
+ }
+
+ return "end";
+};
+
+af().then(v => console.log(v));
njs_run {"./test/js/promise_race_throw.js"} \
"rejected:one"
+
+# Async/Await
+
+njs_run {"./test/js/async_await_inline.js"} \
+"70"
+
+njs_run {"./test/js/async_await_add.js"} \
+"110"
+
+njs_run {"./test/js/async_await_stages.js"} \
+"30 1, 2, 3, 4, 5"
+
+njs_run {"./test/js/async_await_for.js"} \
+"85 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, end"
+
+njs_run {"./test/js/async_await_blank.js"} \
+"12345"
+
+njs_run {"./test/js/async_await_reject.js"} \
+"Thrown:
+Error: unhandled promise rejection: 50"
+
+njs_run {"./test/js/async_await_catch.js"} \
+"51"
+
+njs_run {"./test/js/async_await_finally.js"} \
+"51
+Thrown:
+Error: unhandled promise rejection: 50"
+
+njs_run {"./test/js/async_await_throw.js"} \
+"Thrown:
+Error: unhandled promise rejection: 50"
+
+njs_run {"./test/js/async_await_throw_catch.js"} \
+"110"
+
+njs_run {"./test/js/async_await_throw_async.js"} \
+"Thrown:
+Error: unhandled promise rejection: 51"
+
+njs_run {"./test/js/async_await_throw_catch_async.js"} \
+"51"
+
+njs_run {"./test/js/async_await_try_catch.js"} \
+"reject
+finally
+end"
+
+njs_run {"./test/js/async_await_try_finally.js"} \
+"Thrown:
+Error: unhandled promise rejection: finally reject"
+
+njs_run {"./test/js/async_await_try_throw.js"} \
+"finally
+Thrown:
+Error: unhandled promise rejection: try"
+
+njs_run {"./test/js/async_await_try_throw_catch.js"} \
+"try
+finally
+end"
+