]> git.kaiwu.me - njs.git/commitdiff
Fixed logical assignment short-circuit with non-writable properties.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 3 Mar 2026 02:00:09 +0000 (18:00 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Tue, 3 Mar 2026 15:23:09 +0000 (07:23 -0800)
When the logical condition was already satisfied (e.g., falsy for &&=,
truthy for ||=, non-nullish for ??=), the short-circuit jump landed on
the property set instruction instead of past it.  This caused spurious
TypeErrors for non-writable, getter-only, and non-extensible property
targets even though no assignment should occur.

This fixes logical assignment introduced in 1a64ba68.
This change fixes 9 more tests in test262.

src/njs_generator.c
src/test/njs_unit_test.c

index 9de1a145085c9004be05d721e734fcdcda22c5ea..c2f1856d4f2349dce3eb72a03fe2cc1f772c3a35 100644 (file)
@@ -3773,15 +3773,15 @@ njs_generate_logical_assignment_end(njs_vm_t *vm,
                                expr->index, node);
     }
 
-    njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t,
-                             ctx->jump_offset);
-
     if (ctx->prop_index == NJS_INDEX_NONE) {
         ret = njs_generate_global_property_set(vm, generator, lvalue, expr);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
 
+        njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t,
+                                 ctx->jump_offset);
+
         node->index = lvalue->index;
 
         ret = njs_generate_node_index_release(vm, generator, expr);
@@ -3798,6 +3798,9 @@ njs_generate_logical_assignment_end(njs_vm_t *vm,
         return ret;
     }
 
+    njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t,
+                             ctx->jump_offset);
+
     ret = njs_generate_children_indexes_release(vm, generator, lvalue);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
index bbe70ebbb83a6db51578568b7893153b7d9ed2a6..f9b7266e6ead118b0d43571e762496c31380e09e 100644 (file)
@@ -1618,6 +1618,38 @@ static njs_unit_test_t  njs_test[] =
               "log"),
       njs_str("gs") },
 
+    /* Logical assignment: short-circuit with non-writable property. */
+
+    { njs_str("var o = {};"
+              "Object.defineProperty(o, 'x', {value: 0, writable: false});"
+              "o.x &&= 1"),
+      njs_str("0") },
+    { njs_str("var o = {};"
+              "Object.defineProperty(o, 'x', {value: 2, writable: false});"
+              "o.x ||= 1"),
+      njs_str("2") },
+
+    /* Logical assignment: short-circuit with getter-only property. */
+
+    { njs_str("var o = {};"
+              "Object.defineProperty(o, 'x',"
+              "    {get: function() {return 0}, set: undefined});"
+              "o.x &&= 1"),
+      njs_str("0") },
+    { njs_str("var o = {};"
+              "Object.defineProperty(o, 'x',"
+              "    {get: function() {return 2}, set: undefined});"
+              "o.x ||= 1"),
+      njs_str("2") },
+
+    /* Logical assignment: short-circuit with non-extensible object. */
+
+    { njs_str("var o = {};"
+              "Object.preventExtensions(o);"
+              "o.prop &&= 1;"
+              "o.prop"),
+      njs_str("undefined") },
+
     /* Logical assignment: non-lvalue error */
 
     { njs_str("1 ||= 2"),
@@ -1689,6 +1721,21 @@ static njs_unit_test_t  njs_test[] =
               "log"),
       njs_str("gs") },
 
+    /* ??= short-circuit with non-writable property. */
+
+    { njs_str("var o = {};"
+              "Object.defineProperty(o, 'x', {value: 0, writable: false});"
+              "o.x ?\?= 1"),
+      njs_str("0") },
+
+    /* ??= short-circuit with getter-only property. */
+
+    { njs_str("var o = {};"
+              "Object.defineProperty(o, 'x',"
+              "    {get: function() {return 0}, set: undefined});"
+              "o.x ?\?= 1"),
+      njs_str("0") },
+
     /* ??= non-lvalue error */
 
     { njs_str("1 ?\?= 2"),