Welcome! Log In Create A New Profile

Advanced

[njs] Introduced Async/Await implementation.

Alexander Borisov
September 01, 2021 09:34AM
details: https://hg.nginx.org/njs/rev/92d10cd761e2
branches:
changeset: 1698:92d10cd761e2
user: Alexander Borisov <alexander.borisov@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);
_______________________________________________
nginx-devel mailing list
nginx-devel@nginx.org
http://mailman.nginx.org/mailman/listinfo/nginx-devel
Subject Author Views Posted

[njs] Introduced Async/Await implementation.

Alexander Borisov 429 September 01, 2021 09:34AM



Sorry, you do not have permission to post/reply in this forum.

Online Users

Guests: 285
Record Number of Users: 8 on April 13, 2023
Record Number of Guests: 421 on December 02, 2018
Powered by nginx      Powered by FreeBSD      PHP Powered      Powered by MariaDB      ipv6 ready