From 826ee34ab744bc9d7ed799e0a33dc5771e973578 Mon Sep 17 00:00:00 2001 From: Igor Sysoev Date: Tue, 15 Dec 2015 20:17:10 +0300 Subject: [PATCH] The "switch" statement. --- njs/njs_disassembler.c | 11 ++++ njs/njs_generator.c | 136 +++++++++++++++++++++++++++++++++++++++ njs/njs_parser.c | 116 +++++++++++++++++++++++++++++++++ njs/njs_vm.c | 14 ++++ njs/njs_vm.h | 10 +++ njs/test/njs_unit_test.c | 70 ++++++++++++++++++++ 6 files changed, 357 insertions(+) diff --git a/njs/njs_disassembler.c b/njs/njs_disassembler.c index d61645f4..4cb04a1b 100644 --- a/njs/njs_disassembler.c +++ b/njs/njs_disassembler.c @@ -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); diff --git a/njs/njs_generator.c b/njs/njs_generator.c index 72a2aad3..120ecd99 100644 --- a/njs/njs_generator.c +++ b/njs/njs_generator.c @@ -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) diff --git a/njs/njs_parser.c b/njs/njs_parser.c index 1eb20d5b..3a053a67 100644 --- a/njs/njs_parser.c +++ b/njs/njs_parser.c @@ -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) { diff --git a/njs/njs_vm.c b/njs/njs_vm.c index 3dfaf4c6..3506018e 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -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) { diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 54cc27a8..b2fe2298 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -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); diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 4a1fc500..9371557e 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -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"), -- 2.47.3