From: Igor Sysoev Date: Tue, 9 Feb 2016 15:17:22 +0000 (+0300) Subject: Logical AND and OR operations support short-circuit evaluation. X-Git-Tag: 0.1.0~71 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=44f1839de23f8eb5d67c104723ce24ea0c3226c7;p=njs.git Logical AND and OR operations support short-circuit evaluation. --- diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index 169a95eb..50362444 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -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); diff --git a/njs/njs_generator.c b/njs/njs_generator.c index f5680cf9..ba51a755 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -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)) { diff --git a/njs/njs_nonrecursive_parser.c b/njs/njs_nonrecursive_parser.c index 3c4f530c..9591a05b 100644 --- a/njs/njs_nonrecursive_parser.c +++ b/njs/njs_nonrecursive_parser.c @@ -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 }, } }; diff --git a/njs/njs_parser_expression.c b/njs/njs_parser_expression.c index 66fff0fd..1a1f7ef1 100644 --- a/njs/njs_parser_expression.c +++ b/njs/njs_parser_expression.c @@ -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); diff --git a/njs/njs_vm.c b/njs/njs_vm.c index d0a27d6a..9bd9d7dc 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -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); } diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 469d6415..0910488a 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -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, diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index ceb17dda..95bc59ec 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -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") },