]> git.kaiwu.me - njs.git/commitdiff
Logical AND and OR operations support short-circuit evaluation.
authorIgor Sysoev <igor@sysoev.ru>
Tue, 9 Feb 2016 15:17:22 +0000 (18:17 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Tue, 9 Feb 2016 15:17:22 +0000 (18:17 +0300)
njs/njs_disassembler.c
njs/njs_generator.c
njs/njs_nonrecursive_parser.c
njs/njs_parser_expression.c
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c

index 169a95eb4f802905e7a758e1b6ae20e32bfab938..503624449e8a7c5fe0c6ea219a3d9ed50fbea014 100644 (file)
@@ -99,10 +99,6 @@ static njs_code_name_t  code_names[] = {
 
     { njs_vmcode_logical_not, sizeof(njs_vmcode_2addr_t),
           nxt_string("LOGICAL NOT     ") },
-    { njs_vmcode_logical_and, sizeof(njs_vmcode_3addr_t),
-          nxt_string("LOGICAL AND     ") },
-    { njs_vmcode_logical_or, sizeof(njs_vmcode_3addr_t),
-          nxt_string("LOGICAL OR      ") },
 
     { njs_vmcode_bitwise_not, sizeof(njs_vmcode_2addr_t),
           nxt_string("BINARY NOT      ") },
@@ -179,6 +175,7 @@ njs_disassemble(u_char *start, u_char *end)
     njs_vmcode_try_start_t     *try_start;
     njs_vmcode_operation_t     operation;
     njs_vmcode_cond_jump_t     *cond_jump;
+    njs_vmcode_test_jump_t     *test_jump;
     njs_vmcode_prop_next_t     *prop_next;
     njs_vmcode_equal_jump_t    *equal;
     njs_vmcode_prop_foreach_t  *prop_foreach;
@@ -248,6 +245,28 @@ njs_disassemble(u_char *start, u_char *end)
             continue;
         }
 
+        if (operation == njs_vmcode_test_if_true) {
+            test_jump = (njs_vmcode_test_jump_t *) p;
+            p += sizeof(njs_vmcode_test_jump_t);
+
+            printf("TEST IF TRUE      %04zX %04zX +%zd\n",
+                   (size_t) test_jump->retval, (size_t) test_jump->value,
+                   (size_t) test_jump->offset);
+
+            continue;
+        }
+
+        if (operation == njs_vmcode_test_if_false) {
+            test_jump = (njs_vmcode_test_jump_t *) p;
+            p += sizeof(njs_vmcode_test_jump_t);
+
+            printf("TEST IF FALSE     %04zX %04zX +%zd\n",
+                   (size_t) test_jump->retval, (size_t) test_jump->value,
+                   (size_t) test_jump->offset);
+
+            continue;
+        }
+
         if (operation == njs_vmcode_method_frame) {
             method = (njs_vmcode_method_frame_t *) p;
             p += sizeof(njs_vmcode_method_frame_t);
index f5680cf9b482a311142e2a7be3d88d99c52076e0..ba51a755694ec133a3fa9164712db11906265792 100644 (file)
@@ -71,6 +71,8 @@ static nxt_int_t njs_generate_regexp(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 static nxt_int_t njs_generate_delete(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
+static nxt_int_t njs_generate_test_jump_expression(njs_vm_t *vm,
+    njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_int_t njs_generate_3addr_operation(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_int_t njs_generate_2addr_operation(njs_vm_t *vm,
@@ -189,8 +191,6 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
 
         /* Fall through. */
 
-    case NJS_TOKEN_LOGICAL_OR:
-    case NJS_TOKEN_LOGICAL_AND:
     case NJS_TOKEN_BITWISE_OR:
     case NJS_TOKEN_BITWISE_XOR:
     case NJS_TOKEN_BITWISE_AND:
@@ -215,6 +215,10 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_PROPERTY:
         return njs_generate_3addr_operation(vm, parser, node);
 
+    case NJS_TOKEN_LOGICAL_AND:
+    case NJS_TOKEN_LOGICAL_OR:
+        return njs_generate_test_jump_expression(vm, parser, node);
+
     case NJS_TOKEN_DELETE:
         return njs_generate_delete(vm, parser, node);
 
@@ -1550,6 +1554,63 @@ done:
 }
 
 
+static nxt_int_t
+njs_generate_test_jump_expression(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *node)
+{
+    nxt_int_t               ret;
+    njs_vmcode_move_t       *move;
+    njs_vmcode_test_jump_t  *test_jump;
+
+    ret = njs_generator(vm, parser, node->left);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    njs_generate_code(parser, njs_vmcode_test_jump_t, test_jump);
+    test_jump->code.operation = node->u.operation;
+    test_jump->code.operands = NJS_VMCODE_2OPERANDS;
+    test_jump->code.retval = NJS_VMCODE_RETVAL;
+    test_jump->value = node->left->index;
+
+    ret = njs_generator_node_index_release(vm, parser, node->left);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    node->index = njs_generator_dest_index(vm, parser, node);
+    if (nxt_slow_path(node->index == NJS_INDEX_ERROR)) {
+        return node->index;
+    }
+
+    test_jump->retval = node->index;
+
+    ret = njs_generator(vm, parser, node->right);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    /*
+     * The right expression usually uses node->index as destination,
+     * however, if the expression is a literal, variable or assignment,
+     * then a MOVE operation is required.
+     */
+
+    if (node->index != node->right->index) {
+        njs_generate_code(parser, njs_vmcode_move_t, move);
+        move->code.operation = njs_vmcode_move;
+        move->code.operands = NJS_VMCODE_2OPERANDS;
+        move->code.retval = NJS_VMCODE_RETVAL;
+        move->dst = node->index;
+        move->src = node->right->index;
+    }
+
+    test_jump->offset = parser->code_end - (u_char *) test_jump;
+
+    return njs_generator_node_index_release(vm, parser, node->right);
+}
+
+
 static nxt_int_t
 njs_generate_3addr_operation(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
@@ -2209,7 +2270,7 @@ njs_generator_dest_index(njs_vm_t *vm, njs_parser_t *parser,
 
     dest = node->dest;
 
-    if (dest != NULL) {
+    if (dest != NULL && dest->index != NJS_INDEX_NONE) {
         dest->lvalue = NJS_LVALUE_ASSIGNED;
 
         return dest->index;
@@ -2227,7 +2288,7 @@ njs_generator_object_dest_index(njs_parser_t *parser, njs_parser_node_t *node)
 
     dest = node->dest;
 
-    if (dest != NULL) {
+    if (dest != NULL && dest->index != NJS_INDEX_NONE) {
         index = dest->index;
 
         if (njs_is_callee_argument_index(index)) {
index 3c4f530c73e21bfb4fabd1725f33ce4d60a19354..9591a05ba616def453cfc443cf3b3108d8631077 100644 (file)
@@ -1291,7 +1291,7 @@ static const njs_parser_switch_t  njs_parser_logical_and_expression_switch = {
     NJS_PARSER_TEST_LINE_END,
     1, {
         { NJS_TOKEN_LOGICAL_AND,
-          njs_parser_binary_expression, (void *) njs_vmcode_logical_and,
+          njs_parser_binary_expression, (void *) njs_vmcode_test_if_false,
           njs_parser_logical_and_expression_primed },
     }
 };
@@ -1326,7 +1326,7 @@ static const njs_parser_switch_t  njs_parser_logical_or_expression_switch = {
     NJS_PARSER_TEST_LINE_END,
     1, {
         { NJS_TOKEN_LOGICAL_OR,
-          njs_parser_binary_expression, (void *) njs_vmcode_logical_or,
+          njs_parser_binary_expression, (void *) njs_vmcode_test_if_true,
           njs_parser_logical_or_expression_primed },
     }
 };
index 66fff0fd1ee88e02c89e6c0b0d382d305b68b5ee..1a1f7ef1d235a6f4b4d89822816cb7293e25c0f2 100644 (file)
@@ -25,6 +25,7 @@
 typedef struct {
     njs_token_t                    token;
     njs_vmcode_operation_t         operation;
+    size_t                         size;
 } njs_parser_operation_t;
 
 
@@ -77,9 +78,12 @@ static const njs_parser_expression_t
     njs_parser_unary_expression,
     NULL,
     3, {
-        { NJS_TOKEN_MULTIPLICATION, njs_vmcode_multiplication },
-        { NJS_TOKEN_DIVISION, njs_vmcode_division },
-        { NJS_TOKEN_REMAINDER, njs_vmcode_remainder },
+        { NJS_TOKEN_MULTIPLICATION, njs_vmcode_multiplication,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_DIVISION, njs_vmcode_division,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_REMAINDER, njs_vmcode_remainder,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -90,8 +94,10 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_factor_expression,
     2, {
-        { NJS_TOKEN_ADDITION, njs_vmcode_addition },
-        { NJS_TOKEN_SUBSTRACTION, njs_vmcode_substraction },
+        { NJS_TOKEN_ADDITION, njs_vmcode_addition,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_SUBSTRACTION, njs_vmcode_substraction,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -102,10 +108,12 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_addition_expression,
     3, {
-        { NJS_TOKEN_LEFT_SHIFT, njs_vmcode_left_shift },
-        { NJS_TOKEN_RIGHT_SHIFT, njs_vmcode_right_shift },
-        { NJS_TOKEN_UNSIGNED_RIGHT_SHIFT,
-          njs_vmcode_unsigned_right_shift },
+        { NJS_TOKEN_LEFT_SHIFT, njs_vmcode_left_shift,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_RIGHT_SHIFT, njs_vmcode_right_shift,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_UNSIGNED_RIGHT_SHIFT, njs_vmcode_unsigned_right_shift,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -116,12 +124,18 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_bitwise_shift_expression,
     6, {
-        { NJS_TOKEN_LESS, njs_vmcode_less },
-        { NJS_TOKEN_LESS_OR_EQUAL, njs_vmcode_less_or_equal },
-        { NJS_TOKEN_GREATER, njs_vmcode_greater },
-        { NJS_TOKEN_GREATER_OR_EQUAL, njs_vmcode_greater_or_equal },
-        { NJS_TOKEN_IN, njs_vmcode_property_in },
-        { NJS_TOKEN_INSTANCEOF, njs_vmcode_instance_of },
+        { NJS_TOKEN_LESS, njs_vmcode_less,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_LESS_OR_EQUAL, njs_vmcode_less_or_equal,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_GREATER, njs_vmcode_greater,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_GREATER_OR_EQUAL, njs_vmcode_greater_or_equal,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_IN, njs_vmcode_property_in,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_INSTANCEOF, njs_vmcode_instance_of,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -132,10 +146,14 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_relational_expression,
     4, {
-        { NJS_TOKEN_EQUAL, njs_vmcode_equal },
-        { NJS_TOKEN_NOT_EQUAL, njs_vmcode_not_equal },
-        { NJS_TOKEN_STRICT_EQUAL, njs_vmcode_strict_equal },
-        { NJS_TOKEN_STRICT_NOT_EQUAL, njs_vmcode_strict_not_equal },
+        { NJS_TOKEN_EQUAL, njs_vmcode_equal,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_NOT_EQUAL, njs_vmcode_not_equal,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_STRICT_EQUAL, njs_vmcode_strict_equal,
+          sizeof(njs_vmcode_3addr_t) },
+        { NJS_TOKEN_STRICT_NOT_EQUAL, njs_vmcode_strict_not_equal,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -146,7 +164,8 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_equality_expression,
     1, {
-        { NJS_TOKEN_BITWISE_AND, njs_vmcode_bitwise_and },
+        { NJS_TOKEN_BITWISE_AND, njs_vmcode_bitwise_and,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -157,7 +176,8 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_bitwise_and_expression,
     1, {
-        { NJS_TOKEN_BITWISE_XOR, njs_vmcode_bitwise_xor },
+        { NJS_TOKEN_BITWISE_XOR, njs_vmcode_bitwise_xor,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -168,7 +188,8 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_bitwise_xor_expression,
     1, {
-        { NJS_TOKEN_BITWISE_OR, njs_vmcode_bitwise_or },
+        { NJS_TOKEN_BITWISE_OR, njs_vmcode_bitwise_or,
+          sizeof(njs_vmcode_3addr_t) },
     }
 };
 
@@ -179,7 +200,8 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_bitwise_or_expression,
     1, {
-        { NJS_TOKEN_LOGICAL_AND, njs_vmcode_logical_and },
+        { NJS_TOKEN_LOGICAL_AND, njs_vmcode_test_if_false,
+          sizeof(njs_vmcode_test_jump_t) + sizeof(njs_vmcode_move_t) },
     }
 };
 
@@ -190,7 +212,8 @@ static const njs_parser_expression_t
     njs_parser_binary_expression,
     &njs_parser_logical_and_expression,
     1, {
-        { NJS_TOKEN_LOGICAL_OR, njs_vmcode_logical_or },
+        { NJS_TOKEN_LOGICAL_OR, njs_vmcode_test_if_true,
+          sizeof(njs_vmcode_test_jump_t) + sizeof(njs_vmcode_move_t) },
     }
 };
 
@@ -201,7 +224,7 @@ static const njs_parser_expression_t
     njs_parser_assignment_expression,
     NULL,
     1, {
-        { NJS_TOKEN_COMMA, NULL },
+        { NJS_TOKEN_COMMA, NULL, 0 },
     }
 };
 
@@ -578,7 +601,6 @@ njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
 {
     nxt_int_t                     n;
     njs_parser_node_t             *node;
-    njs_vmcode_operation_t        operation;
     const njs_parser_operation_t  *op;
 
     token = expr->next(vm, parser, expr->expression, token);
@@ -592,7 +614,6 @@ njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
 
         do {
             if (op->token == token) {
-                operation = op->operation;
                 goto found;
             }
 
@@ -623,9 +644,11 @@ njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
 
         node->token = token;
-        node->u.operation = operation;
+        node->u.operation = op->operation;
         node->left = parser->node;
-        parser->code_size += sizeof(njs_vmcode_3addr_t);
+        node->left->dest = node;
+
+        parser->code_size += op->size;
 
         token = njs_parser_token(parser);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
@@ -638,6 +661,7 @@ njs_parser_binary_expression(njs_vm_t *vm, njs_parser_t *parser,
         }
 
         node->right = parser->node;
+        node->right->dest = node;
         parser->node = node;
     }
 }
@@ -741,6 +765,7 @@ njs_parser_unary_expression(njs_vm_t *vm, njs_parser_t *parser,
     node->token = token;
     node->u.operation = operation;
     node->left = parser->node;
+    node->left->dest = node;
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_2addr_t);
 
index d0a27d6a6f7ffb7b6036b8e6eb71228772085951..9bd9d7dc56bd2af7f422b453d57c4bbe89a4e279 100644 (file)
@@ -1637,37 +1637,33 @@ njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value, njs_value_t *inlvd)
 
 
 njs_ret_t
-njs_vmcode_logical_and(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+njs_vmcode_test_if_true(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 {
-    njs_value_t  *retval;
+    njs_vmcode_test_jump_t  *test_jump;
 
-    if (njs_is_true(val1)) {
-        retval = val2;
+    vm->retval = *value;
 
-    } else {
-        retval = val1;
+    if (njs_is_true(value)) {
+        test_jump = (njs_vmcode_test_jump_t *) vm->current;
+        return test_jump->offset;
     }
 
-    vm->retval = *retval;
-
     return sizeof(njs_vmcode_3addr_t);
 }
 
 
 njs_ret_t
-njs_vmcode_logical_or(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+njs_vmcode_test_if_false(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
 {
-    njs_value_t  *retval;
+    njs_vmcode_test_jump_t  *test_jump;
 
-    if (njs_is_true(val1)) {
-        retval = val1;
+    vm->retval = *value;
 
-    } else {
-        retval = val2;
+    if (!njs_is_true(value)) {
+        test_jump = (njs_vmcode_test_jump_t *) vm->current;
+        return test_jump->offset;
     }
 
-    vm->retval = *retval;
-
     return sizeof(njs_vmcode_3addr_t);
 }
 
index 469d64151571ce6e262ad00e67cbc9a521c593ce..0910488afeddca252f9d4e2ddd9eb2d371db49b8 100644 (file)
@@ -492,6 +492,14 @@ typedef struct {
 } njs_vmcode_equal_jump_t;
 
 
+typedef struct {
+    njs_vmcode_t               code;
+    njs_index_t                retval;
+    njs_index_t                value;
+    njs_ret_t                  offset;
+} njs_vmcode_test_jump_t;
+
+
 typedef struct {
     njs_vmcode_t               code;
     njs_index_t                value;
@@ -817,10 +825,10 @@ njs_ret_t njs_vmcode_remainder(njs_vm_t *vm, njs_value_t *val1,
     njs_value_t *val2);
 njs_ret_t njs_vmcode_logical_not(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *inlvd);
-njs_ret_t njs_vmcode_logical_and(njs_vm_t *vm, njs_value_t *val1,
-    njs_value_t *val2);
-njs_ret_t njs_vmcode_logical_or(njs_vm_t *vm, njs_value_t *val1,
-    njs_value_t *val2);
+njs_ret_t njs_vmcode_test_if_true(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *invld);
+njs_ret_t njs_vmcode_test_if_false(njs_vm_t *vm, njs_value_t *value,
+    njs_value_t *invld);
 njs_ret_t njs_vmcode_bitwise_not(njs_vm_t *vm, njs_value_t *value,
     njs_value_t *inlvd);
 njs_ret_t njs_vmcode_bitwise_and(njs_vm_t *vm, njs_value_t *val1,
index ceb17dda8b0a75948ea87d2411e8be29a15a3b01..95bc59ec59502df6f2e2fb7d8585dd55e4e4a81c 100644 (file)
@@ -337,9 +337,30 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("1 || 2"),
       nxt_string("1") },
 
+    { nxt_string("var a = 1; 1 || (a = 2); a"),
+      nxt_string("1") },
+
+    { nxt_string("1 || 2 || 3"),
+      nxt_string("1") },
+
+    { nxt_string("1 || (2 + 2) || 3"),
+      nxt_string("1") },
+
     { nxt_string("1 && 2"),
       nxt_string("2") },
 
+    { nxt_string("1 && 2 && 3"),
+      nxt_string("3") },
+
+    { nxt_string("var a = 1; 0 && (a = 2); a"),
+      nxt_string("1") },
+
+    { nxt_string("false && true || true"),
+      nxt_string("true") },
+
+    { nxt_string("false && (true || true)"),
+      nxt_string("false") },
+
     { nxt_string("a = true; a = -~!a"),
       nxt_string("1") },