]> git.kaiwu.me - njs.git/commitdiff
Propertly handling NJS_DECLINE in promise native functions.
authorDmitry Volyntsev <xeioex@nginx.com>
Thu, 16 Jun 2022 00:10:39 +0000 (17:10 -0700)
committerDmitry Volyntsev <xeioex@nginx.com>
Thu, 16 Jun 2022 00:10:39 +0000 (17:10 -0700)
Previously, NJS_DECLINE was returned from a Promise.all() and friends
when "resolve" property was not found in a promise constructor.
NJS_DECLINE was treated as NJS_ERROR in one place, but as NJS_OK in a
different place during the promise function evaluation.  As a result,
the VM was left in inconsistent state during stack unwinding which
resulted in a garbage return value.

The fix is to ensure that only NJS_ERROR or NJS_OK is returned
from ordinary native functions.

This closes #545 issue on Github.

src/njs_promise.c
src/test/njs_unit_test.c

index bfde8cce3846484645b3184a8a266371ec51e6cc..fca8e6d693f135c3898376d7ebe0c2ecd0abc972 100644 (file)
@@ -389,7 +389,7 @@ njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value,
 
     ret = njs_value_property(vm, value, njs_value_arg(&string_constructor),
                              dst);
-    if (njs_slow_path(ret != NJS_OK)) {
+    if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
 
@@ -1255,7 +1255,7 @@ njs_promise_all(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     ret = njs_value_property(vm, promise_ctor, njs_value_arg(&string_resolve),
                              &resolve);
-    if (njs_slow_path(ret != NJS_OK)) {
+    if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
 
@@ -1715,7 +1715,7 @@ njs_promise_race(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     ret = njs_value_property(vm, promise_ctor, njs_value_arg(&string_resolve),
                              &resolve);
-    if (njs_slow_path(ret != NJS_OK)) {
+    if (njs_slow_path(ret == NJS_ERROR)) {
         return ret;
     }
 
index d338c79f447f903c81cb663839ab000792dcb6a5..1841de1e622a3c0cbf1b6b09d00148808671a623 100644 (file)
@@ -21211,6 +21211,28 @@ static njs_unit_test_t  njs_externals_test[] =
     { njs_str("$r.retval(Promise.all([async () => [await x('X')]]))"),
       njs_str("[object Promise]") },
 
+    { njs_str("var r = [1].map(v => {"
+              "    function C(a) {"
+              "        a(a, parseInt);"
+              "    };"
+              ""
+              "    Promise.all.apply(C);"
+              "});"
+              "r[0]"),
+              /* TODO: RejectAbrupt() exception should not percolate */
+      njs_str("TypeError: resolve is not callable") },
+
+    { njs_str("var r = [1].map(v => {"
+              "    function C(a) {"
+              "        a(a, parseInt);"
+              "    };"
+              ""
+              "    Promise.race.apply(C);"
+              "});"
+              "r[0]"),
+              /* TODO: RejectAbrupt() exception should not percolate */
+      njs_str("TypeError: resolve is not callable") },
+
     { njs_str("let obj = { a: 1, b: 2};"
               "function cb(r) { r.retval(obj.a); }"
               "$r.subrequest($r)"