]> git.kaiwu.me - njs.git/commitdiff
The "switch" statement.
authorIgor Sysoev <igor@sysoev.ru>
Tue, 15 Dec 2015 17:17:10 +0000 (20:17 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Tue, 15 Dec 2015 17:17:10 +0000 (20:17 +0300)
njs/njs_disassembler.c
njs/njs_generator.c
njs/njs_parser.c
njs/njs_vm.c
njs/njs_vm.h
njs/test/njs_unit_test.c

index d61645f48bc2482481ffcb881aec451628471c08..4cb04a1beeefe3a94a7a2e4fbdf8438c0ccf4597 100644 (file)
@@ -180,6 +180,7 @@ njs_disassemble(u_char *start, u_char *end)
     njs_vmcode_operation_t     operation;
     njs_vmcode_cond_jump_t     *cond_jump;
     njs_vmcode_prop_next_t     *prop_next;
+    njs_vmcode_equal_jump_t    *equal;
     njs_vmcode_prop_foreach_t  *prop_foreach;
     njs_vmcode_method_frame_t  *method;
 
@@ -230,6 +231,16 @@ njs_disassemble(u_char *start, u_char *end)
             continue;
         }
 
+        if (operation == njs_vmcode_if_equal_jump) {
+            equal = (njs_vmcode_equal_jump_t *) p;
+            p += sizeof(njs_vmcode_equal_jump_t);
+
+            printf("JUMP IF EQUAL     %04lX %04lX +%ld\n",
+                   equal->value1, equal->value2, equal->offset);
+
+            continue;
+        }
+
         if (operation == njs_vmcode_method_frame) {
             method = (njs_vmcode_method_frame_t *) p;
             p += sizeof(njs_vmcode_method_frame_t);
index 72a2aad3801eda59e754477885951c8087f4a167..120ecd994767183272bf444efe81448488833522 100644 (file)
@@ -29,6 +29,8 @@ static nxt_int_t njs_generate_if_statement(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 static nxt_int_t njs_generate_cond_expression(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
+static nxt_int_t njs_generate_switch_statement(njs_vm_t *vm,
+    njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_int_t njs_generate_while_statement(njs_vm_t *vm,
     njs_parser_t *parser, njs_parser_node_t *node);
 static nxt_int_t njs_generate_do_while_statement(njs_vm_t *vm,
@@ -128,6 +130,9 @@ njs_generator(njs_vm_t *vm, njs_parser_t *parser, njs_parser_node_t *node)
     case NJS_TOKEN_CONDITIONAL:
         return njs_generate_cond_expression(vm, parser, node);
 
+    case NJS_TOKEN_SWITCH:
+        return njs_generate_switch_statement(vm, parser, node);
+
     case NJS_TOKEN_WHILE:
         return njs_generate_while_statement(vm, parser, node);
 
@@ -494,6 +499,137 @@ njs_generate_cond_expression(njs_vm_t *vm, njs_parser_t *parser,
 }
 
 
+static nxt_int_t
+njs_generate_switch_statement(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_node_t *swtch)
+{
+    nxt_int_t                ret;
+    njs_index_t              index;
+    njs_parser_node_t        *node, *expr, *branch;
+    njs_vmcode_move_t        *move;
+    njs_vmcode_jump_t        *jump;
+    njs_parser_patch_t       *patch, *next, *patches, **last;
+    njs_vmcode_equal_jump_t  *equal;
+
+    /* The "switch" expression. */
+
+    expr = swtch->left;
+
+    ret = njs_generator(vm, parser, expr);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    index = expr->index;
+
+    if (!expr->temporary) {
+        index = njs_generator_temp_index_get(parser);
+
+        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 = index;
+        move->src = expr->index;
+    }
+
+    ret = njs_generate_start_block(vm, parser, NJS_PARSER_SWITCH, &no_label);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    last = &patches;
+
+    for (branch = swtch->right; branch != NULL; branch = branch->left) {
+
+        if (branch->token != NJS_TOKEN_DEFAULT) {
+
+            /* The "case" expression. */
+
+            node = branch->right;
+
+            ret = njs_generator(vm, parser, node->left);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
+            njs_generate_code(parser, njs_vmcode_equal_jump_t, equal);
+            equal->code.operation = njs_vmcode_if_equal_jump;
+            equal->code.operands = NJS_VMCODE_3OPERANDS;
+            equal->code.retval = NJS_VMCODE_NO_RETVAL;
+            equal->offset = offsetof(njs_vmcode_equal_jump_t, offset);
+            equal->value1 = index;
+            equal->value2 = node->left->index;
+
+            ret = njs_generator_node_index_release(vm, parser, node->left);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return ret;
+            }
+
+            patch = nxt_mem_cache_alloc(vm->mem_cache_pool,
+                                         sizeof(njs_parser_patch_t));
+            if (nxt_slow_path(patch == NULL)) {
+                return NXT_ERROR;
+            }
+
+            patch->address = (u_char *) &equal->offset;
+
+            *last = patch;
+            last = &patch->next;
+        }
+    }
+
+    /* Release either temporary index or temporary expr->index. */
+    ret = njs_generator_index_release(vm, parser, index);
+    if (nxt_slow_path(ret != NXT_OK)) {
+        return ret;
+    }
+
+    njs_generate_code(parser, njs_vmcode_jump_t, jump);
+    jump->code.operation = njs_vmcode_jump;
+    jump->code.operands = NJS_VMCODE_1OPERAND;
+    jump->code.retval = NJS_VMCODE_NO_RETVAL;
+    jump->offset = offsetof(njs_vmcode_jump_t, offset);
+
+    patch = patches;
+
+    for (branch = swtch->right; branch != NULL; branch = branch->left) {
+
+        if (branch->token == NJS_TOKEN_DEFAULT) {
+            jump->offset = parser->code_end - (u_char *) jump;
+            jump = NULL;
+            node = branch;
+
+        } else {
+            *patch->address += parser->code_end - patch->address;
+            next = patch->next;
+
+            nxt_mem_cache_free(vm->mem_cache_pool, patch);
+
+            patch = next;
+            node = branch->right;
+        }
+
+        /* The "case/default" statements. */
+
+        ret = njs_generator(vm, parser, node->right);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return ret;
+        }
+    }
+
+    if (jump != NULL) {
+        /* A "switch" without default case. */
+        jump->offset = parser->code_end - (u_char *) jump;
+    }
+
+    /* Patch "break" statements offsets. */
+    njs_generate_patch_block_exit(vm, parser);
+
+    return NXT_OK;
+}
+
+
 static nxt_int_t
 njs_generate_while_statement(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node)
index 1eb20d5b832db0d2112773d73fbc0145cf8ddb8f..3a053a6777b4188bc1a5cca8b0e86ca96e7358b2 100644 (file)
@@ -49,6 +49,8 @@ static njs_token_t njs_parser_return_statement(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_token_t njs_parser_var_statement(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_if_statement(njs_vm_t *vm, njs_parser_t *parser);
+static njs_token_t njs_parser_switch_statement(njs_vm_t *vm,
+    njs_parser_t *parser);
 static njs_token_t njs_parser_while_statement(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_token_t njs_parser_do_while_statement(njs_vm_t *vm,
@@ -167,6 +169,9 @@ njs_parser_statement(njs_vm_t *vm, njs_parser_t *parser,
     case NJS_TOKEN_IF:
         return njs_parser_if_statement(vm, parser);
 
+    case NJS_TOKEN_SWITCH:
+        return njs_parser_switch_statement(vm, parser);
+
     case NJS_TOKEN_WHILE:
         return njs_parser_while_statement(vm, parser);
 
@@ -746,6 +751,117 @@ njs_parser_if_statement(njs_vm_t *vm, njs_parser_t *parser)
 }
 
 
+static njs_token_t
+njs_parser_switch_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+    njs_token_t        token;
+    njs_parser_node_t  *node, *swtch, *branch, *dflt, **last;
+
+    parser->branch = 1;
+
+    node = NULL;
+    branch = NULL;
+    dflt = NULL;
+
+    token = njs_parser_grouping_expression(vm, parser);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    swtch = njs_parser_node_alloc(vm);
+    if (nxt_slow_path(swtch == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    swtch->token = NJS_TOKEN_SWITCH;
+    swtch->left = parser->node;
+    last = &swtch->right;
+
+    token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_BRACE);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    while (token != NJS_TOKEN_CLOSE_BRACE) {
+
+        if (token == NJS_TOKEN_CASE || token == NJS_TOKEN_DEFAULT) {
+
+            do {
+                node = njs_parser_node_alloc(vm);
+                if (nxt_slow_path(node == NULL)) {
+                    return NJS_TOKEN_ERROR;
+                }
+
+                if (token == NJS_TOKEN_CASE) {
+                    token = njs_parser_token(parser);
+                    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                        return token;
+                    }
+
+                    token = njs_parser_expression(vm, parser, token);
+                    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                        return token;
+                    }
+
+                    node->left = parser->node;
+
+                    branch = njs_parser_node_alloc(vm);
+                    if (nxt_slow_path(branch == NULL)) {
+                        return NJS_TOKEN_ERROR;
+                    }
+
+                    branch->right = node;
+
+                    parser->code_size += sizeof(njs_vmcode_equal_jump_t);
+
+               } else {
+                    if (dflt != NULL) {
+                        /* A duplicate "default" branch. */
+                        return NJS_TOKEN_ILLEGAL;
+                    }
+
+                    branch = node;
+                    branch->token = NJS_TOKEN_DEFAULT;
+                    dflt = branch;
+
+                    token = njs_parser_token(parser);
+                    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                        return token;
+                    }
+                }
+
+                *last = branch;
+                last = &branch->left;
+
+                token = njs_parser_match(parser, token, NJS_TOKEN_COLON);
+                if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+                    return token;
+                }
+
+            } while (token == NJS_TOKEN_CASE || token == NJS_TOKEN_DEFAULT);
+
+            parser->node = NULL;
+
+        } else if (branch == NULL) {
+            /* The first switch statment is not "case/default" keyword. */
+            return NJS_TOKEN_ILLEGAL;
+        }
+
+        token = njs_parser_statement_chain(vm, parser, token);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
+        }
+
+        node->right = parser->node;
+    }
+
+    parser->node = swtch;
+    parser->code_size += sizeof(njs_vmcode_move_t) + sizeof(njs_vmcode_jump_t);
+
+    return njs_parser_token(parser);
+}
+
+
 static njs_token_t
 njs_parser_while_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
index 3dfaf4c61f7caf94122ba672de3e24fb90d882ad..3506018ef943c5162b1eff32bb3d7bedb3496d14 100644 (file)
@@ -2050,6 +2050,20 @@ njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond, njs_value_t *offset)
 }
 
 
+njs_ret_t
+njs_vmcode_if_equal_jump(njs_vm_t *vm, njs_value_t *val1, njs_value_t *val2)
+{
+    njs_vmcode_equal_jump_t  *jump;
+
+    if (njs_values_strict_equal(val1, val2)) {
+        jump = (njs_vmcode_equal_jump_t *) vm->current;
+        return jump->offset;
+    }
+
+    return sizeof(njs_vmcode_3addr_t);
+}
+
+
 njs_ret_t
 njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *invld, njs_value_t *name)
 {
index 54cc27a8cad8d40c216caf927eb7d809e86a9196..b2fe2298b0838dd3ee02492c757ed61be31880b0 100644 (file)
@@ -457,6 +457,14 @@ typedef struct {
 } njs_vmcode_cond_jump_t;
 
 
+typedef struct {
+    njs_vmcode_t               code;
+    njs_ret_t                  offset;
+    njs_index_t                value1;
+    njs_index_t                value2;
+} njs_vmcode_equal_jump_t;
+
+
 typedef struct {
     njs_vmcode_t               code;
     njs_index_t                value;
@@ -816,6 +824,8 @@ njs_ret_t njs_vmcode_if_true_jump(njs_vm_t *vm, njs_value_t *cond,
     njs_value_t *offset);
 njs_ret_t njs_vmcode_if_false_jump(njs_vm_t *vm, njs_value_t *cond,
     njs_value_t *offset);
+njs_ret_t njs_vmcode_if_equal_jump(njs_vm_t *vm, njs_value_t *val1,
+    njs_value_t *val2);
 
 njs_ret_t njs_vmcode_function_frame(njs_vm_t *vm, njs_value_t *invld,
     njs_value_t *name);
index 4a1fc500e904280c597fa99047b0eecfd7a665e4..9371557e5288071a12be9251817939351922420b 100644 (file)
@@ -1374,6 +1374,76 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("a = 3; if (true) if (false); else; a = 2; a"),
       nxt_string("2") },
 
+    /* switch. */
+
+    { nxt_string("switch"),
+      nxt_string("SyntaxError") },
+
+    { nxt_string("switch (1);"),
+      nxt_string("SyntaxError") },
+
+    { nxt_string("switch (1) {}"),
+      nxt_string("undefined") },
+
+    { nxt_string("switch (1) {default:}"),
+      nxt_string("undefined") },
+
+    { nxt_string("switch (1) {case 0:}"),
+      nxt_string("undefined") },
+
+    { nxt_string("switch (1) {default:;}"),
+      nxt_string("undefined") },
+
+    { nxt_string("switch (1) {case 0:;}"),
+      nxt_string("undefined") },
+
+    { nxt_string("var a = 'A'; switch (a) {"
+                 "case 0: a += '0';"
+                 "case 1: a += '1';"
+                 "}; a"),
+      nxt_string("A") },
+
+    { nxt_string("var a = 'A'; switch (0) {"
+                 "case 0: a += '0';"
+                 "case 1: a += '1';"
+                 "}; a"),
+      nxt_string("A01") },
+
+    { nxt_string("var a = 'A'; switch (0) {"
+                 "case 0: a += '0'; break;"
+                 "case 1: a += '1';"
+                 "}; a"),
+      nxt_string("A0") },
+
+    { nxt_string("var a = 'A'; switch (1) {"
+                 "case 0: a += '0';"
+                 "case 1: a += '1';"
+                 "}; a"),
+      nxt_string("A1") },
+
+    { nxt_string("var a = 'A'; switch (2) {"
+                 "case 0: a += '0';"
+                 "case 1: a += '1';"
+                 "default: a += 'D';"
+                 "}; a"),
+      nxt_string("AD") },
+
+    { nxt_string("var a = 'A'; switch (2) {"
+                 "case 0: a += '0';"
+                 "default: a += 'D';"
+                 "case 1: a += '1';"
+                 "}; a"),
+      nxt_string("AD1") },
+
+    { nxt_string("var a = 'A'; function f(x) { a += x; return 0 }"
+                 "switch (a) {"
+                 "case f(1):"
+                 "default:"
+                 "case f(2): a += 'D';"
+                 "case f(3): a += 'T';"
+                 "} a"),
+      nxt_string("A123DT") },
+
     /* continue. */
 
     { nxt_string("continue"),