]> git.kaiwu.me - njs.git/commitdiff
Introduced Async/Await implementation.
authorAlexander Borisov <alexander.borisov@nginx.com>
Wed, 1 Sep 2021 13:31:08 +0000 (16:31 +0300)
committerAlexander Borisov <alexander.borisov@nginx.com>
Wed, 1 Sep 2021 13:31:08 +0000 (16:31 +0300)
This closes #419 issue on GitHub.

37 files changed:
auto/sources
src/njs_async.c [new file with mode: 0644]
src/njs_async.h [new file with mode: 0644]
src/njs_builtin.c
src/njs_disassembler.c
src/njs_function.c
src/njs_function.h
src/njs_generator.c
src/njs_lexer.h
src/njs_main.h
src/njs_parser.c
src/njs_parser.h
src/njs_promise.c
src/njs_promise.h
src/njs_variable.c
src/njs_vm.c
src/njs_vm.h
src/njs_vmcode.c
src/njs_vmcode.h
src/test/njs_unit_test.c
test/js/async_await_add.js [new file with mode: 0644]
test/js/async_await_blank.js [new file with mode: 0644]
test/js/async_await_catch.js [new file with mode: 0644]
test/js/async_await_finally.js [new file with mode: 0644]
test/js/async_await_for.js [new file with mode: 0644]
test/js/async_await_inline.js [new file with mode: 0644]
test/js/async_await_reject.js [new file with mode: 0644]
test/js/async_await_stages.js [new file with mode: 0644]
test/js/async_await_throw.js [new file with mode: 0644]
test/js/async_await_throw_async.js [new file with mode: 0644]
test/js/async_await_throw_catch.js [new file with mode: 0644]
test/js/async_await_throw_catch_async.js [new file with mode: 0644]
test/js/async_await_try_catch.js [new file with mode: 0644]
test/js/async_await_try_finally.js [new file with mode: 0644]
test/js/async_await_try_throw.js [new file with mode: 0644]
test/js/async_await_try_throw_catch.js [new file with mode: 0644]
test/njs_expect_test.exp

index e2756186bef86a027a797bd1b45d727ac8cfee3c..4e5e65cc476f3e2932954e503710d618c7d2bd9c 100644 (file)
@@ -60,6 +60,7 @@ NJS_LIB_SRCS=" \
    src/njs_buffer.c \
    src/njs_iterator.c \
    src/njs_scope.c \
+   src/njs_async.c \
 "
 
 NJS_LIB_TEST_SRCS=" \
diff --git a/src/njs_async.c b/src/njs_async.c
new file mode 100644 (file)
index 0000000..4d9b810
--- /dev/null
@@ -0,0 +1,245 @@
+
+/*
+ * 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),
+};
diff --git a/src/njs_async.h b/src/njs_async.h
new file mode 100644 (file)
index 0000000..598abfb
--- /dev/null
@@ -0,0 +1,30 @@
+
+/*
+ * 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_ */
index 3611a2a7d2a8e72f4506319ea1af5b7d8307afcd..4e48d6aba887cea67cda9f7580594a87e372c39c 100644 (file)
@@ -74,6 +74,7 @@ static const njs_object_type_init_t *const
     &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,
@@ -181,6 +182,12 @@ njs_builtin_objects_create(njs_vm_t *vm)
         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)) {
@@ -342,7 +349,7 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global)
     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;
 
     /*
@@ -384,6 +391,9 @@ njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global)
 
     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;
     }
index 010d53efc86c925b56ef6ab1a176ec9cecf71307..4c93a04fa37a747cc9f1c119275ae1e1cf16ec58 100644 (file)
@@ -156,6 +156,9 @@ static njs_code_name_t  code_names[] = {
 
     { NJS_VMCODE_DEBUGGER, sizeof(njs_vmcode_debugger_t),
           njs_str("DEBUGGER        ") },
+
+    { NJS_VMCODE_AWAIT, sizeof(njs_vmcode_await_t),
+          njs_str("AWAIT           ") },
 };
 
 
index 087765d4b46dc9c112ec8f375efd9fda67931a13..c32211774bc853833d67159754a08dd0828cb7e7 100644 (file)
@@ -9,9 +9,11 @@
 
 
 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 *);
@@ -34,12 +36,23 @@ njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda)
     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;
@@ -73,7 +86,8 @@ njs_vm_function_alloc(njs_vm_t *vm, njs_function_native_t native)
 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);
 
@@ -87,9 +101,14 @@ njs_function_value_copy(njs_vm_t *vm, njs_value_t *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;
     }
@@ -173,9 +192,10 @@ njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
 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;
 
@@ -187,7 +207,10 @@ njs_function_copy(njs_vm_t *vm, njs_function_t *function)
     }
 
     *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) {
@@ -404,6 +427,7 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
     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;
 
@@ -430,6 +454,17 @@ njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
         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);
 
@@ -724,6 +759,29 @@ njs_function_native_call(njs_vm_t *vm)
 }
 
 
+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)
 {
@@ -744,6 +802,69 @@ 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)
@@ -970,9 +1091,9 @@ njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
 }
 
 
-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;
@@ -997,13 +1118,27 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         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));
@@ -1055,7 +1190,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
          */
 
         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) {
@@ -1097,7 +1232,7 @@ njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     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;
     }
@@ -1147,7 +1282,7 @@ const njs_object_init_t  njs_function_constructor_init = {
 };
 
 
-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)
 {
index 7f4b814f30289abbc3e02adcf47fde0414f1ad83..07e058bdd5ca437e951de567421de72de0a6b8e1 100644 (file)
@@ -85,7 +85,8 @@ struct njs_frame_s {
 };
 
 
-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);
@@ -96,6 +97,10 @@ njs_int_t njs_function_rest_parameters_init(njs_vm_t *vm,
     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,
@@ -111,10 +116,15 @@ njs_int_t njs_function_lambda_call(njs_vm_t *vm);
 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 *
@@ -161,23 +171,6 @@ njs_function_previous_frame(njs_native_frame_t *frame)
 }
 
 
-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,
@@ -210,6 +203,57 @@ njs_function_closures(const njs_function_t *func)
 }
 
 
+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;
index 6c31b1992eb1eb03085b2ddd22547f2530c54bef..eb32411ceef2c7fec11ee720919912e8c87a3e47 100644 (file)
@@ -325,6 +325,10 @@ static njs_int_t njs_generate_export_statement(njs_vm_t *vm,
     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,
@@ -658,6 +662,7 @@ njs_generate(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node)
         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:
@@ -679,6 +684,7 @@ njs_generate(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node)
         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:
@@ -702,6 +708,9 @@ njs_generate(njs_vm_t *vm, njs_generator_t *generator, njs_parser_node_t *node)
     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");
@@ -3055,6 +3064,7 @@ njs_generate_function_expression(njs_vm_t *vm, njs_generator_t *generator,
     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)) {
@@ -3090,6 +3100,7 @@ njs_generate_function(njs_vm_t *vm, njs_generator_t *generator,
     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)) {
@@ -3555,6 +3566,7 @@ njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator,
     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;
@@ -3587,7 +3599,8 @@ njs_generate_function_declaration(njs_vm_t *vm, njs_generator_t *generator,
         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;
     }
@@ -4655,6 +4668,40 @@ njs_generate_export_statement_end(njs_vm_t *vm, njs_generator_t *generator,
 }
 
 
+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)
index 32c0dda49727e2ccbcbee25fbfff00a70979637e..b20c5934b45ad81de19ec66120293d1a97a7f698 100644 (file)
@@ -148,6 +148,9 @@ typedef enum {
     NJS_TOKEN_ARGUMENT,
     NJS_TOKEN_RETURN,
 
+    NJS_TOKEN_ASYNC_FUNCTION_DECLARATION,
+    NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION,
+
     NJS_TOKEN_REGEXP,
 
     NJS_TOKEN_EXTERNAL,
index 5b23d37238f269e51ea351913081812c21900aa1..4e8722ffe98b35910f9e499554968e88c705a57d 100644 (file)
@@ -73,6 +73,7 @@
 #include <njs_date.h>
 #include <njs_promise.h>
 #include <njs_iterator.h>
+#include <njs_async.h>
 
 #include <njs_math.h>
 #include <njs_json.h>
index 653e9081ca39ee60f49bd69030d748d0b6cf957d..b8e64f717a4402d978b5c854a6260b0e62dc4a0c 100644 (file)
@@ -132,6 +132,10 @@ static njs_int_t njs_parser_unary_expression_after(njs_parser_t *parser,
     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);
@@ -732,14 +736,6 @@ njs_parser_async_generator_expression(njs_parser_t *parser,
 }
 
 
-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)
@@ -757,15 +753,11 @@ njs_parser_class_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
 
 
 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) {
@@ -773,7 +765,13 @@ njs_parser_function_or_generator(njs_parser_t *parser,
         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;
         }
@@ -790,6 +788,18 @@ njs_parser_function_or_generator(njs_parser_t *parser,
 }
 
 
+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)
@@ -807,13 +817,9 @@ njs_parser_async_function_or_generator(njs_parser_t *parser,
         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);
 }
 
 
@@ -1078,6 +1084,8 @@ njs_parser_primary_expression_test(njs_parser_t *parser,
             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;
@@ -1085,14 +1093,22 @@ njs_parser_primary_expression_test(njs_parser_t *parser,
 
         /* 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:
@@ -2707,6 +2723,8 @@ njs_parser_arguments(njs_parser_t *parser, njs_lexer_token_t *token,
      * 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);
@@ -2723,6 +2741,8 @@ static njs_int_t
 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);
@@ -3337,7 +3357,8 @@ njs_parser_unary_expression(njs_parser_t *parser, njs_lexer_token_t *token,
 
     /* 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);
@@ -3441,6 +3462,57 @@ njs_parser_unary_expression_next(njs_parser_t *parser,
 }
 
 
+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.
  */
@@ -4130,6 +4202,13 @@ njs_parser_match_arrow_expression(njs_parser_t *parser,
 {
     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))
     {
@@ -6573,6 +6652,7 @@ njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
 {
     njs_int_t          ret;
     uintptr_t          unique_id;
+    njs_bool_t         async;
     njs_variable_t     *var;
     njs_parser_node_t  *node;
 
@@ -6619,6 +6699,9 @@ njs_parser_function_declaration(njs_parser_t *parser, njs_lexer_token_t *token,
         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,
@@ -6675,6 +6758,7 @@ njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token,
 {
     njs_int_t              ret;
     uintptr_t              unique_id;
+    njs_bool_t             async;
     njs_variable_t         *var;
     njs_function_lambda_t  *lambda;
 
@@ -6683,6 +6767,9 @@ njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *token,
         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)) {
@@ -6725,7 +6812,7 @@ njs_parser_function_expression(njs_parser_t *parser, njs_lexer_token_t *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;
     }
@@ -6895,11 +6982,27 @@ njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token,
 {
     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;
     }
@@ -6912,6 +7015,8 @@ njs_parser_arrow_function(njs_parser_t *parser, njs_lexer_token_t *token,
         return NJS_ERROR;
     }
 
+    parser->scope->async = async;
+
     name = njs_parser_node_new(parser, NJS_TOKEN_NAME);
     if (name == NULL) {
         return NJS_ERROR;
index 97a274413229c6ad2ab4a9a1e01739ae888f8d2a..399a489b560aa000a515d20a8e995a7447c45f7d 100644 (file)
@@ -30,6 +30,7 @@ struct njs_parser_scope_s {
     uint8_t                         module;
     uint8_t                         arrow_function;
     uint8_t                         dest_disable;
+    uint8_t                         async;
 };
 
 
@@ -82,6 +83,7 @@ struct njs_parser_s {
     uintptr_t                       undefined_id;
     njs_bool_t                      strict_semicolon;
     uint32_t                        line;
+    njs_bool_t                      in_args;
 };
 
 
index 34e48e10b3d71dee9b71d3ca51eec90a16eaf40a..49c3bb83c96d9b1891a3e95593969db80bae5119 100644 (file)
@@ -7,12 +7,6 @@
 #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
@@ -24,20 +18,6 @@ typedef enum {
     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;
@@ -84,13 +64,8 @@ static njs_int_t njs_promise_host_rejection_tracker(njs_vm_t *vm,
     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,
@@ -254,7 +229,7 @@ njs_promise_constructor_call(njs_vm_t *vm, njs_function_t *function)
 }
 
 
-static njs_function_t *
+njs_function_t *
 njs_promise_create_function(njs_vm_t *vm, size_t context_size)
 {
     njs_function_t         *function;
@@ -332,7 +307,7 @@ njs_promise_create_resolving_functions(njs_vm_t *vm, njs_promise_t *promise,
 }
 
 
-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;
@@ -791,7 +766,7 @@ njs_promise_object_resolve(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
-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;
@@ -946,7 +921,7 @@ failed:
 }
 
 
-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)
index bb645f34025dd2320664aa2b43bdf954c1a1f72e..3d7a70dc2f510527344641b07185d1ec8e77e53b 100644 (file)
@@ -7,9 +7,37 @@
 #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;
index e48196f624bcb4b287622a32428ea4393ec0d7c7..811886c72296680e0ee7b03d8a8d77ff5fea92f8 100644 (file)
@@ -38,6 +38,7 @@ njs_variable_t *
 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;
@@ -61,7 +62,9 @@ njs_variable_function_add(njs_parser_t *parser, njs_parser_scope_t *scope,
             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;
         }
index 4ccc5771f1812dd4ab2b506d4571ff719045b755..1f07d32ab0f747900cf7f31f78ef12273e5ec7c0 100644 (file)
@@ -465,7 +465,9 @@ njs_vm_start(njs_vm_t *vm)
         return ret;
     }
 
-    return njs_vmcode_interpreter(vm, vm->start);
+    ret = njs_vmcode_interpreter(vm, vm->start);
+
+    return (ret == NJS_ERROR) ? NJS_ERROR : NJS_OK;
 }
 
 
index 1539a285015022b51a37719349fcad36abedbef4..2efa859d8b3f5cfc2850e32ce5e29f393ff87498 100644 (file)
@@ -43,6 +43,7 @@ typedef enum {
     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,
@@ -224,6 +225,7 @@ struct njs_vm_shared_s {
     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;
index c24a599c9d5ee0345640f80d43dd4e9b8759eeb4..8c3d439f98b1ab9464523f748542d7209d385403 100644 (file)
@@ -42,6 +42,8 @@ static njs_jump_off_t njs_vmcode_debugger(njs_vm_t *vm);
 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,
@@ -90,6 +92,7 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc)
     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;
@@ -821,6 +824,10 @@ next:
 
                 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)) {
@@ -1066,7 +1073,7 @@ njs_vmcode_function(njs_vm_t *vm, u_char *pc)
     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;
     }
@@ -1807,6 +1814,95 @@ 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)
+{
+    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
index 6a2fc26eba3100106e91c28ff6853354da650ad1..c15a4bd9e740ee1c5ee001b76d724770d3eca6f3 100644 (file)
@@ -50,6 +50,8 @@ enum {
     NJS_VMCODE_ARGUMENTS,
     NJS_VMCODE_PROTO_INIT,
 
+    NJS_VMCODE_AWAIT,
+
     NJS_VMCODE_TRY_START,
     NJS_VMCODE_THROW,
     NJS_VMCODE_TRY_BREAK,
@@ -210,6 +212,7 @@ typedef struct {
     njs_vmcode_t               code;
     njs_index_t                retval;
     njs_function_lambda_t      *lambda;
+    njs_bool_t                 async;
 } njs_vmcode_function_t;
 
 
@@ -428,6 +431,12 @@ typedef struct {
 } 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);
index facab4214baebb4c3d4241e58e76623196349316..9875ba88f7c75dbaaaeece65d88157f828acba79 100644 (file)
@@ -20435,6 +20435,82 @@ static njs_unit_test_t  njs_test[] =
 
     { 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") },
 };
 
 
diff --git a/test/js/async_await_add.js b/test/js/async_await_add.js
new file mode 100644 (file)
index 0000000..62d46e7
--- /dev/null
@@ -0,0 +1,7 @@
+async function af(x) {
+    const y = await new Promise(resolve => {resolve(x + 10)});
+
+    return x + y;
+}
+
+af(50).then(v => console.log(v));
diff --git a/test/js/async_await_blank.js b/test/js/async_await_blank.js
new file mode 100644 (file)
index 0000000..deaa8cf
--- /dev/null
@@ -0,0 +1,5 @@
+async function af(x) {
+    return x;
+}
+
+af(12345).then(v => console.log(v));
diff --git a/test/js/async_await_catch.js b/test/js/async_await_catch.js
new file mode 100644 (file)
index 0000000..c47a512
--- /dev/null
@@ -0,0 +1,5 @@
+async function add(x) {
+    return await new Promise((resolve, reject) => {reject(x)}).catch(v => v + 1);
+}
+
+add(50).then(v => {console.log(v)});
diff --git a/test/js/async_await_finally.js b/test/js/async_await_finally.js
new file mode 100644 (file)
index 0000000..5c2ae73
--- /dev/null
@@ -0,0 +1,6 @@
+async function add(x) {
+    return await new Promise((resolve, reject) => {reject(x)})
+                                                 .finally(() => console.log(x + 1));
+}
+
+add(50).then(v => {console.log(v)});
diff --git a/test/js/async_await_for.js b/test/js/async_await_for.js
new file mode 100644 (file)
index 0000000..1256a06
--- /dev/null
@@ -0,0 +1,23 @@
+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);
diff --git a/test/js/async_await_inline.js b/test/js/async_await_inline.js
new file mode 100644 (file)
index 0000000..2901738
--- /dev/null
@@ -0,0 +1,11 @@
+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)});
diff --git a/test/js/async_await_reject.js b/test/js/async_await_reject.js
new file mode 100644 (file)
index 0000000..eeaec34
--- /dev/null
@@ -0,0 +1,5 @@
+async function add(x) {
+    return await new Promise((resolve, reject) => {reject(x)});
+}
+
+add(50).then(v => {console.log(v)});
diff --git a/test/js/async_await_stages.js b/test/js/async_await_stages.js
new file mode 100644 (file)
index 0000000..e3ff18e
--- /dev/null
@@ -0,0 +1,28 @@
+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);
diff --git a/test/js/async_await_throw.js b/test/js/async_await_throw.js
new file mode 100644 (file)
index 0000000..8923e5d
--- /dev/null
@@ -0,0 +1,12 @@
+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)});
diff --git a/test/js/async_await_throw_async.js b/test/js/async_await_throw_async.js
new file mode 100644 (file)
index 0000000..b5b6bb2
--- /dev/null
@@ -0,0 +1,15 @@
+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)});
diff --git a/test/js/async_await_throw_catch.js b/test/js/async_await_throw_catch.js
new file mode 100644 (file)
index 0000000..5e9025f
--- /dev/null
@@ -0,0 +1,12 @@
+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)});
diff --git a/test/js/async_await_throw_catch_async.js b/test/js/async_await_throw_catch_async.js
new file mode 100644 (file)
index 0000000..e7c5103
--- /dev/null
@@ -0,0 +1,15 @@
+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));
diff --git a/test/js/async_await_try_catch.js b/test/js/async_await_try_catch.js
new file mode 100644 (file)
index 0000000..5c11ec5
--- /dev/null
@@ -0,0 +1,19 @@
+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));
diff --git a/test/js/async_await_try_finally.js b/test/js/async_await_try_finally.js
new file mode 100644 (file)
index 0000000..9feb1b5
--- /dev/null
@@ -0,0 +1,20 @@
+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));
diff --git a/test/js/async_await_try_throw.js b/test/js/async_await_try_throw.js
new file mode 100644 (file)
index 0000000..d5f51e5
--- /dev/null
@@ -0,0 +1,14 @@
+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));
diff --git a/test/js/async_await_try_throw_catch.js b/test/js/async_await_try_throw_catch.js
new file mode 100644 (file)
index 0000000..d4bd5b3
--- /dev/null
@@ -0,0 +1,17 @@
+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));
index 5c5ed2da9656cf740d8e778ab54fca291743d6ec..d505098a86d1ce089098061d7ca58ef97b9c120c 100644 (file)
@@ -1108,3 +1108,66 @@ njs_run {"./test/js/promise_race.js"} \
 
 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"
+