[njs] Fixed pre/post increment/decrement in assignment operations.

Alexander Borisov alexander.borisov at nginx.com
Fri Jul 24 10:04:13 UTC 2020


details:   https://hg.nginx.org/njs/rev/78f0887e5aa6
branches:  
changeset: 1478:78f0887e5aa6
user:      Alexander Borisov <alexander.borisov at nginx.com>
date:      Thu Jul 23 13:24:10 2020 +0300
description:
Fixed pre/post increment/decrement in assignment operations.

Previously, the compound assignment operations did not create temporary
index for increments/decrements in the generator. The result was that
the increment/decrement changed the value immediately in place, which
led to incorrect calculations.

The fix is to use a separate temporary index for increments/decrements in
assignment operations.

This closes #271 issue on GitHub.

diffstat:

 src/njs_generator.c      |  30 ++++++++++++++++++++++++-
 src/njs_lexer.h          |  11 +++++----
 src/test/njs_unit_test.c |  56 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 91 insertions(+), 6 deletions(-)

diffs (165 lines):

diff -r 7f5c5a425d03 -r 78f0887e5aa6 src/njs_generator.c
--- a/src/njs_generator.c	Wed Jul 22 15:21:15 2020 +0300
+++ b/src/njs_generator.c	Thu Jul 23 13:24:10 2020 +0300
@@ -1799,7 +1799,7 @@ njs_generate_operation_assignment(njs_vm
     njs_parser_node_t *node)
 {
     njs_int_t              ret;
-    njs_index_t            index;
+    njs_index_t            index, src;
     njs_parser_node_t      *lvalue, *expr, *object, *property;
     njs_vmcode_move_t      *move;
     njs_vmcode_3addr_t     *code;
@@ -1876,6 +1876,34 @@ njs_generate_operation_assignment(njs_vm
         return ret;
     }
 
+    if (njs_slow_path(njs_parser_has_side_effect(node->right))) {
+        /*
+         * Preserve object and property values stored in variables in a case
+         * if the variables can be changed by side effects in expression.
+         */
+        if (object->token_type == NJS_TOKEN_NAME) {
+            src = object->index;
+
+            index = njs_generate_node_temp_index_get(vm, generator, object);
+            if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+                return NJS_ERROR;
+            }
+
+            njs_generate_code_move(generator, move, index, src, object);
+        }
+
+        if (property->token_type == NJS_TOKEN_NAME) {
+            src = property->index;
+
+            index = njs_generate_node_temp_index_get(vm, generator, property);
+            if (njs_slow_path(index == NJS_INDEX_ERROR)) {
+                return NJS_ERROR;
+            }
+
+            njs_generate_code_move(generator, move, index, src, property);
+        }
+    }
+
     index = njs_generate_node_temp_index_get(vm, generator, node);
     if (njs_slow_path(index == NJS_INDEX_ERROR)) {
         return NJS_ERROR;
diff -r 7f5c5a425d03 -r 78f0887e5aa6 src/njs_lexer.h
--- a/src/njs_lexer.h	Wed Jul 22 15:21:15 2020 +0300
+++ b/src/njs_lexer.h	Thu Jul 23 13:24:10 2020 +0300
@@ -51,7 +51,12 @@ typedef enum {
     NJS_TOKEN_BITWISE_XOR_ASSIGNMENT,
     NJS_TOKEN_BITWISE_AND_ASSIGNMENT,
 
-#define NJS_TOKEN_LAST_ASSIGNMENT   NJS_TOKEN_BITWISE_AND_ASSIGNMENT
+    NJS_TOKEN_INCREMENT,
+    NJS_TOKEN_DECREMENT,
+    NJS_TOKEN_POST_INCREMENT,
+    NJS_TOKEN_POST_DECREMENT,
+
+#define NJS_TOKEN_LAST_ASSIGNMENT   NJS_TOKEN_POST_DECREMENT
 
     NJS_TOKEN_EQUAL,
     NJS_TOKEN_STRICT_EQUAL,
@@ -60,13 +65,9 @@ typedef enum {
 
     NJS_TOKEN_ADDITION,
     NJS_TOKEN_UNARY_PLUS,
-    NJS_TOKEN_INCREMENT,
-    NJS_TOKEN_POST_INCREMENT,
 
     NJS_TOKEN_SUBSTRACTION,
     NJS_TOKEN_UNARY_NEGATION,
-    NJS_TOKEN_DECREMENT,
-    NJS_TOKEN_POST_DECREMENT,
 
     NJS_TOKEN_MULTIPLICATION,
 
diff -r 7f5c5a425d03 -r 78f0887e5aa6 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Wed Jul 22 15:21:15 2020 +0300
+++ b/src/test/njs_unit_test.c	Thu Jul 23 13:24:10 2020 +0300
@@ -2183,6 +2183,20 @@ static njs_unit_test_t  njs_test[] =
                  "var b = ++a; a +' '+ b"),
       njs_str("NaN NaN") },
 
+    { njs_str("var a = 0; a = a + ++a; a"),
+      njs_str("1") },
+
+    { njs_str("var a = 0; a += a + ++a; a"),
+      njs_str("1") },
+
+    { njs_str("var i = 0, arr = ['a', 'b'];"
+              "arr[i] = arr[i] + arr[++i]; arr"),
+      njs_str("ab,b") },
+
+    { njs_str("var i = 0, arr = ['a', 'b'];"
+              "arr[i] += arr[i] + arr[++i]; arr"),
+      njs_str("aab,b") },
+
     /* Post increment. */
 
     { njs_str("var a = 1;   a++"),
@@ -2265,6 +2279,20 @@ static njs_unit_test_t  njs_test[] =
                  "var b = a++; a +' '+ b"),
       njs_str("NaN NaN") },
 
+    { njs_str("var a = 0; a = a + a++; a"),
+      njs_str("0") },
+
+    { njs_str("var a = 0; a += a + a++; a"),
+      njs_str("0") },
+
+    { njs_str("var i = 1, arr = ['a', 'b'];"
+              "arr[i] = arr[i] + arr[i++]; arr"),
+      njs_str("a,bb") },
+
+    { njs_str("var i = 1, arr = ['a', 'b'];"
+              "arr[i] += arr[i] + arr[i++]; arr"),
+      njs_str("a,bbb") },
+
     /* Decrement. */
 
     { njs_str("var a = 1;   --a"),
@@ -2347,6 +2375,20 @@ static njs_unit_test_t  njs_test[] =
                  "var b = --a; a +' '+ b"),
       njs_str("NaN NaN") },
 
+    { njs_str("var a = 0; a = a + --a; a"),
+      njs_str("-1") },
+
+    { njs_str("var a = 0; a -= a + --a; a"),
+      njs_str("1") },
+
+    { njs_str("var i = 1, arr = ['a', 'b'];"
+              "arr[i] = arr[i] + arr[--i]; arr"),
+      njs_str("a,ba") },
+
+    { njs_str("var i = 1, arr = ['a', 'b'];"
+              "arr[i] += arr[i] + arr[--i]; arr"),
+      njs_str("a,bba") },
+
     /* Post decrement. */
 
     { njs_str("var a = 1;   a--"),
@@ -2429,6 +2471,20 @@ static njs_unit_test_t  njs_test[] =
                  "var b = a--; a +' '+ b"),
       njs_str("NaN NaN") },
 
+    { njs_str("var a = 0; a = a + a--; a"),
+      njs_str("0") },
+
+    { njs_str("var a = 0; a += a + a--; a"),
+      njs_str("0") },
+
+    { njs_str("var i = 1, arr = ['a', 'b'];"
+              "arr[i] = arr[i] + arr[i--]; arr"),
+      njs_str("a,bb") },
+
+    { njs_str("var i = 1, arr = ['a', 'b'];"
+              "arr[i] += arr[i] + arr[i--]; arr"),
+      njs_str("a,bbb") },
+
     /**/
 
     { njs_str("var a, b; a = 2; b = ++a + ++a; a + ' ' + b"),


More information about the nginx-devel mailing list