[njs] Introduced Async/Await implementation.

Alexander Borisov alexander.borisov at nginx.com
Wed Sep 1 13:31:57 UTC 2021


details:   https://hg.nginx.org/njs/rev/92d10cd761e2
branches:  
changeset: 1698:92d10cd761e2
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Wed Sep 01 16:31:08 2021 +0300
description:
Introduced Async/Await implementation.

This closes #419 issue on GitHub.

diffstat:

 auto/sources                             |    1 +
 src/njs_async.c                          |  245 +++++++++++++++++++++++++++++++
 src/njs_async.h                          |   30 +++
 src/njs_builtin.c                        |   12 +-
 src/njs_disassembler.c                   |    3 +
 src/njs_function.c                       |  161 ++++++++++++++++++-
 src/njs_function.h                       |   80 +++++++--
 src/njs_generator.c                      |   49 ++++++-
 src/njs_lexer.h                          |    3 +
 src/njs_main.h                           |    1 +
 src/njs_parser.c                         |  165 +++++++++++++++++---
 src/njs_parser.h                         |    2 +
 src/njs_promise.c                        |   33 +---
 src/njs_promise.h                        |   34 +++-
 src/njs_variable.c                       |    5 +-
 src/njs_vm.c                             |    4 +-
 src/njs_vm.h                             |    2 +
 src/njs_vmcode.c                         |   98 ++++++++++++-
 src/njs_vmcode.h                         |    9 +
 src/test/njs_unit_test.c                 |   76 +++++++++
 test/js/async_await_add.js               |    7 +
 test/js/async_await_blank.js             |    5 +
 test/js/async_await_catch.js             |    5 +
 test/js/async_await_finally.js           |    6 +
 test/js/async_await_for.js               |   23 ++
 test/js/async_await_inline.js            |   11 +
 test/js/async_await_reject.js            |    5 +
 test/js/async_await_stages.js            |   28 +++
 test/js/async_await_throw.js             |   12 +
 test/js/async_await_throw_async.js       |   15 +
 test/js/async_await_throw_catch.js       |   12 +
 test/js/async_await_throw_catch_async.js |   15 +
 test/js/async_await_try_catch.js         |   19 ++
 test/js/async_await_try_finally.js       |   20 ++
 test/js/async_await_try_throw.js         |   14 +
 test/js/async_await_try_throw_catch.js   |   17 ++
 test/njs_expect_test.exp                 |   63 +++++++
 37 files changed, 1192 insertions(+), 98 deletions(-)

diffs (truncated from 2023 to 1000 lines):

diff -r 5439e59a255e -r 92d10cd761e2 auto/sources
--- a/auto/sources	Wed Sep 01 16:31:08 2021 +0300
+++ b/auto/sources	Wed Sep 01 16:31:08 2021 +0300
@@ -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 -r 5439e59a255e -r 92d10cd761e2 src/njs_async.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_async.c	Wed Sep 01 16:31:08 2021 +0300
@@ -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 -r 5439e59a255e -r 92d10cd761e2 src/njs_async.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_async.h	Wed Sep 01 16:31:08 2021 +0300
@@ -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_ */
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_builtin.c
--- a/src/njs_builtin.c	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_builtin.c	Wed Sep 01 16:31:08 2021 +0300
@@ -74,6 +74,7 @@ static const njs_object_type_init_t *con
     &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, 
     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, 
 
     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;
     }
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_disassembler.c
--- a/src/njs_disassembler.c	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_disassembler.c	Wed Sep 01 16:31:08 2021 +0300
@@ -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           ") },
 };
 
 
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_function.c
--- a/src/njs_function.c	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_function.c	Wed Sep 01 16:31:08 2021 +0300
@@ -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_fun
     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_
 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, nj
         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_
 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_func
     }
 
     *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_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, 
         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)
 {
@@ -745,6 +803,69 @@ njs_function_frame_free(njs_vm_t *vm, nj
 
 
 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 *
 }
 
 
-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, n
         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, n
          */
 
         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, n
 
     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_co
 };
 
 
-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)
 {
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_function.h
--- a/src/njs_function.h	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_function.h	Wed Sep 01 16:31:08 2021 +0300
@@ -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_i
     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_v
 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 *
@@ -162,23 +172,6 @@ njs_function_previous_frame(njs_native_f
 
 
 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_uint_t nargs, njs_value_t *retval)
@@ -210,6 +203,57 @@ njs_function_closures(const njs_function
 }
 
 
+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;
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_generator.c
--- a/src/njs_generator.c	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_generator.c	Wed Sep 01 16:31:08 2021 +0300
@@ -325,6 +325,10 @@ static njs_int_t njs_generate_export_sta
     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
         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
         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
     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_
     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_
     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
     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
         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;
     }
@@ -4656,6 +4669,40 @@ njs_generate_export_statement_end(njs_vm
 
 
 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)
 {
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_lexer.h
--- a/src/njs_lexer.h	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_lexer.h	Wed Sep 01 16:31:08 2021 +0300
@@ -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,
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_main.h
--- a/src/njs_main.h	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_main.h	Wed Sep 01 16:31:08 2021 +0300
@@ -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>
diff -r 5439e59a255e -r 92d10cd761e2 src/njs_parser.c
--- a/src/njs_parser.c	Wed Sep 01 16:31:08 2021 +0300
+++ b/src/njs_parser.c	Wed Sep 01 16:31:08 2021 +0300
@@ -132,6 +132,10 @@ static njs_int_t njs_parser_unary_expres
     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);
@@ -733,14 +737,6 @@ njs_parser_async_generator_expression(nj
 
 
 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_
 
 
 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_par
         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;
         }
@@ -791,6 +789,18 @@ njs_parser_function_or_generator(njs_par
 
 
 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(n
         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);
-    }
-
-    return NJS_OK;
+    njs_lexer_consume_token(parser->lexer, 1);
+
+    return njs_parser_function_or_generator_handler(parser, token, current, 1);
 }
 
 
@@ -1078,6 +1084,8 @@ njs_parser_primary_expression_test(njs_p
             goto reference;
         }
 
+        njs_lexer_consume_token(parser->lexer, 1);


More information about the nginx-devel mailing list