]> git.kaiwu.me - njs.git/commitdiff
Fixed async ctx erasing when a function is called multiple times.
authorAlexander Borisov <alexander.borisov@nginx.com>
Thu, 2 Sep 2021 16:32:27 +0000 (19:32 +0300)
committerAlexander Borisov <alexander.borisov@nginx.com>
Thu, 2 Sep 2021 16:32:27 +0000 (19:32 +0300)
The bug was introduced in 92d10cd761e2.

src/njs_async.c
src/njs_function.c
src/njs_value.h
src/njs_vmcode.c
test/js/async_await_many_call.js [new file with mode: 0644]
test/njs_expect_test.exp

index 3c8c95dbf5bfe2f8c2f5db24978ec2d2c6ec1d3b..97ce87ff17f0b86ed99a5d0e06797b1297d14309 100644 (file)
@@ -8,33 +8,33 @@
 
 
 static void
-njs_async_context_free(njs_vm_t *vm, njs_native_frame_t *frame);
+njs_async_context_free(njs_vm_t *vm, njs_async_ctx_t *ctx);
 
 
 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;
+    njs_int_t                 ret;
+    njs_value_t               ctor;
+    njs_native_frame_t        *frame;
+    njs_promise_capability_t  *capability;
 
     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)) {
+    capability = njs_promise_new_capability(vm, &ctor);
+    if (njs_slow_path(capability == NULL)) {
         return NJS_ERROR;
     }
 
+    frame->function->context = capability;
+
     ret = njs_function_lambda_call(vm);
 
     if (ret == NJS_OK) {
-        ret = njs_function_call(vm, njs_function(&ctx->capability->resolve),
+        ret = njs_function_call(vm, njs_function(&capability->resolve),
                                 &njs_value_undefined, retval, 1, &vm->retval);
 
     } else if (ret == NJS_ERROR) {
@@ -42,12 +42,12 @@ njs_async_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
             return NJS_ERROR;
         }
 
-        ret = njs_function_call(vm, njs_function(&ctx->capability->reject),
+        ret = njs_function_call(vm, njs_function(&capability->reject),
                                 &njs_value_undefined, &vm->retval, 1,
                                 &vm->retval);
     }
 
-    *retval = ctx->capability->promise;
+    *retval = capability->promise;
 
     return ret;
 }
@@ -60,6 +60,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_int_t           ret;
     njs_value_t         **cur_local, **cur_closures, **cur_temp, *value;
     njs_frame_t         *frame;
+    njs_function_t      *function;
     njs_async_ctx_t     *ctx;
     njs_native_frame_t  *top, *async;
 
@@ -71,6 +72,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     async = ctx->await;
+    function = async->function;
 
     cur_local = vm->levels[NJS_LEVEL_LOCAL];
     cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
@@ -90,8 +92,14 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     vm->top_frame->retval = &vm->retval;
 
+    function->context = ctx->capability;
+    function->await = ctx;
+
     ret = njs_vmcode_interpreter(vm, ctx->pc);
 
+    function->context = NULL;
+    function->await = NULL;
+
     vm->levels[NJS_LEVEL_LOCAL] = cur_local;
     vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
     vm->levels[NJS_LEVEL_TEMP] = cur_temp;
@@ -103,7 +111,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         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);
+        njs_async_context_free(vm, ctx);
 
     } else if (ret == NJS_ERROR) {
         if (njs_is_memory_error(vm, &vm->retval)) {
@@ -122,7 +130,7 @@ 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);
+    njs_async_context_free(vm, ctx);
 
     return NJS_ERROR;
 }
@@ -143,7 +151,7 @@ njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         (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);
+        njs_async_context_free(vm, ctx);
 
         return NJS_ERROR;
     }
@@ -155,16 +163,10 @@ njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static void
-njs_async_context_free(njs_vm_t *vm, njs_native_frame_t *frame)
+njs_async_context_free(njs_vm_t *vm, njs_async_ctx_t *ctx)
 {
-    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;
 }
 
 
index c32211774bc853833d67159754a08dd0828cb7e7..087889cdc67825d7d00a58ba385c54cc61820a2b 100644 (file)
@@ -427,7 +427,6 @@ 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;
 
@@ -454,17 +453,6 @@ 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);
 
index f60b8c3d83e9f5b3e469200602f465fc0ecf0363..71eb523322f585cc3a33cb3af68098ea7e27e1c3 100644 (file)
@@ -284,6 +284,7 @@ struct njs_function_s {
     } u;
 
     void                              *context;
+    void                              *await;
 
     njs_value_t                       *bound;
 };
index 8c3d439f98b1ab9464523f748542d7209d385403..e19a5995555c12ed51eace5daff806c0ea3ed3c6 100644 (file)
@@ -1827,7 +1827,6 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await)
     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)) {
@@ -1841,7 +1840,15 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await)
         return NJS_ERROR;
     }
 
-    if (ctx->await == NULL) {
+    ctx = active->function->await;
+
+    if (ctx == NULL) {
+        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;
+        }
+
         size = njs_function_frame_size(active);
 
         fulfilled = njs_promise_create_function(vm, size);
@@ -1850,6 +1857,9 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await)
         }
 
         ctx->await = fulfilled->context;
+        ctx->capability = active->function->context;
+
+        active->function->context = NULL;
 
         ret = njs_function_frame_save(vm, ctx->await, NULL);
         if (njs_slow_path(ret != NJS_OK)) {
diff --git a/test/js/async_await_many_call.js b/test/js/async_await_many_call.js
new file mode 100644 (file)
index 0000000..52f24ac
--- /dev/null
@@ -0,0 +1,30 @@
+async function test(name) {
+    let k1, k2;
+
+    switch (name) {
+        case "First":
+            k1 = await Promise.resolve("SUN");
+            k2 = await Promise.resolve("MOON");
+            break;
+
+        case "Second":
+            k1 = await Promise.resolve("CAT");
+            k2 = await Promise.resolve("MOUSE");
+            break;
+
+        case "Third":
+            k1 = await Promise.resolve("MAN");
+            k2 = await Promise.resolve("WOMAN");
+            break;
+
+        default:
+            break;
+    }
+
+    return `${name}: ${k1} ${k2}`;
+};
+
+Promise.all(['First', 'Second', 'Third'].map(v => test(v)))
+.then(results => {
+    console.log(results)
+})
index 1eb491ee4d935c81b0ba04a09e8d1709a4632387..e04a89edffa51f4e31b4228393ef5368b23f38e5 100644 (file)
@@ -1173,3 +1173,6 @@ end"
 
 njs_run {"./test/js/async_await_try_resolve.js"} \
 "key: resolve"
+
+njs_run {"./test/js/async_await_many_call.js"} \
+"\\\['First: SUN MOON','Second: CAT MOUSE','Third: MAN WOMAN']"