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;
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);
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,
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);
}
+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)
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,
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);
}
+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)
{
}
+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)
{
} 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;
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);
{ 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"),