*/
njs_ret_t jump_offset;
njs_generator_patch_t *next;
+ /*
+ * index_offset is used for patching vmcode_try_return instruction
+ * inside try blocks.
+ */
+ njs_index_t index_offset;
};
typedef enum {
- NJS_GENERATOR_BLOCK = 0,
- NJS_GENERATOR_LOOP,
- NJS_GENERATOR_SWITCH,
+ NJS_GENERATOR_BLOCK = 1,
+ NJS_GENERATOR_LOOP = 2,
+ NJS_GENERATOR_SWITCH = 4,
+ NJS_GENERATOR_TRY = 8,
+
+#define NJS_GENERATOR_ALL (NJS_GENERATOR_BLOCK \
+ | NJS_GENERATOR_LOOP \
+ | NJS_GENERATOR_SWITCH \
+ | NJS_GENERATOR_TRY)
} njs_generator_block_type_t;
static nxt_noinline nxt_int_t njs_generate_start_block(njs_vm_t *vm,
njs_generator_t *generator, njs_generator_block_type_t type,
const nxt_str_t *label);
-static nxt_noinline void njs_generate_patch_loop_continuation(njs_vm_t *vm,
- njs_generator_t *generator);
+static njs_generator_block_t *njs_generate_find_block(
+ njs_generator_block_t *block, uint32_t mask);
+static nxt_int_t njs_generate_make_continuation_patch(njs_vm_t *vm,
+ njs_generator_t *generator, njs_generator_block_t *block, njs_ret_t offset);
+static nxt_noinline void njs_generate_patch_block(njs_vm_t *vm,
+ njs_generator_t *generator, njs_generator_patch_t *list);
+static nxt_noinline void njs_generate_patch_try_exit_block(njs_vm_t *vm,
+ njs_generator_t *generator, njs_generator_patch_t *list, njs_index_t dest);
+static nxt_int_t njs_generate_make_exit_patch(njs_vm_t *vm,
+ njs_generator_t *generator, njs_generator_block_t *block, njs_ret_t offset);
static nxt_noinline void njs_generate_patch_block_exit(njs_vm_t *vm,
njs_generator_t *generator);
static nxt_int_t njs_generate_continue_statement(njs_vm_t *vm,
return NXT_ERROR;
}
+ patch->index_offset = 0;
patch->jump_offset = njs_code_offset(generator, equal)
+ offsetof(njs_vmcode_equal_jump_t, offset);
/* The loop condition. */
- njs_generate_patch_loop_continuation(vm, generator);
+ njs_generate_patch_block(vm, generator, generator->block->continuation);
njs_code_set_jump_offset(generator, njs_vmcode_jump_t, jump_offset);
/* The loop condition. */
- njs_generate_patch_loop_continuation(vm, generator);
+ njs_generate_patch_block(vm, generator, generator->block->continuation);
condition = node->right;
/* The loop update. */
- njs_generate_patch_loop_continuation(vm, generator);
+ njs_generate_patch_block(vm, generator, generator->block->continuation);
update = node->right;
/* The loop iterator. */
- njs_generate_patch_loop_continuation(vm, generator);
+ njs_generate_patch_block(vm, generator, generator->block->continuation);
njs_code_set_jump_offset(generator, njs_vmcode_prop_foreach_t, prop_offset);
}
+static njs_generator_block_t *
+njs_generate_find_block(njs_generator_block_t *block, uint32_t mask)
+{
+ while (block != NULL) {
+ if (block->type & mask) {
+ return block;
+ }
+
+ block = block->next;
+ }
+
+ return NULL;
+}
+
+
+static nxt_int_t
+njs_generate_make_continuation_patch(njs_vm_t *vm, njs_generator_t *generator,
+ njs_generator_block_t *block, njs_ret_t offset)
+{
+ njs_generator_patch_t *patch;
+
+ patch = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_generator_patch_t));
+ if (nxt_slow_path(patch == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ patch->next = block->continuation;
+ block->continuation = patch;
+
+ patch->index_offset = 0;
+ patch->jump_offset = offset;
+
+ return NXT_OK;
+}
+
+
static nxt_noinline void
-njs_generate_patch_loop_continuation(njs_vm_t *vm, njs_generator_t *generator)
+njs_generate_patch_block(njs_vm_t *vm, njs_generator_t *generator,
+ njs_generator_patch_t *list)
{
- njs_generator_block_t *block;
njs_generator_patch_t *patch, *next;
- block = generator->block;
-
- for (patch = block->continuation; patch != NULL; patch = next) {
+ for (patch = list; patch != NULL; patch = next) {
njs_code_update_offset(generator, patch);
next = patch->next;
static nxt_noinline void
-njs_generate_patch_block_exit(njs_vm_t *vm, njs_generator_t *generator)
+njs_generate_patch_try_exit_block(njs_vm_t *vm, njs_generator_t *generator,
+ njs_generator_patch_t *list, njs_index_t dest)
{
- njs_generator_block_t *block;
njs_generator_patch_t *patch, *next;
- block = generator->block;
- generator->block = block->next;
-
- for (patch = block->exit; patch != NULL; patch = next) {
+ for (patch = list; patch != NULL; patch = next) {
njs_code_update_offset(generator, patch);
next = patch->next;
+ if (patch->index_offset != 0) {
+ *(njs_code_ptr(generator, njs_index_t, patch->index_offset)) = dest;
+ }
+
nxt_mem_cache_free(vm->mem_cache_pool, patch);
}
+}
+
+
+static nxt_int_t
+njs_generate_make_exit_patch(njs_vm_t *vm, njs_generator_t *generator,
+ njs_generator_block_t *block, njs_ret_t offset)
+{
+ njs_generator_patch_t *patch;
+
+ patch = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_generator_patch_t));
+ if (nxt_slow_path(patch == NULL)) {
+ njs_memory_error(vm);
+ return NXT_ERROR;
+ }
+
+ patch->next = block->exit;
+ block->exit = patch;
+
+ patch->index_offset = 0;
+ patch->jump_offset = offset;
+
+ return NXT_OK;
+}
+
+
+static nxt_noinline void
+njs_generate_patch_block_exit(njs_vm_t *vm, njs_generator_t *generator)
+{
+ njs_generator_block_t *block;
+
+ block = generator->block;
+ generator->block = block->next;
+
+ njs_generate_patch_block(vm, generator, block->exit);
nxt_mem_cache_free(vm->mem_cache_pool, block);
}
njs_generate_continue_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_vmcode_jump_t *jump;
+ njs_vmcode_jump_t *jump;
njs_generator_patch_t *patch;
njs_generator_block_t *block;
- for (block = generator->block; block != NULL; block = block->next) {
- if (block->type == NJS_GENERATOR_LOOP) {
- goto found;
- }
- }
-
- njs_generate_syntax_error(vm, node->token_line,
- "Illegal continue statement");
+ block = njs_generate_find_block(generator->block,
+ NJS_GENERATOR_LOOP | NJS_GENERATOR_TRY);
- return NXT_ERROR;
+ if (nxt_slow_path(block == NULL)) {
+ goto syntax_error;
+ }
-found:
+ if (block->type == NJS_GENERATOR_TRY
+ && njs_generate_find_block(block->next, NJS_GENERATOR_LOOP) == NULL)
+ {
+ goto syntax_error;
+ }
/* TODO: LABEL */
jump->code.retval = NJS_VMCODE_NO_RETVAL;
jump->offset = offsetof(njs_vmcode_jump_t, offset);
+ patch->index_offset = 0;
patch->jump_offset = njs_code_offset(generator, jump)
+ offsetof(njs_vmcode_jump_t, offset);
}
return NXT_OK;
+
+syntax_error:
+
+ njs_generate_syntax_error(vm, node->token_line,
+ "Illegal continue statement");
+
+ return NXT_ERROR;
}
njs_generate_break_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_vmcode_jump_t *jump;
+ njs_vmcode_jump_t *jump;
njs_generator_patch_t *patch;
njs_generator_block_t *block;
- for (block = generator->block; block != NULL; block = block->next) {
- if (block->type == NJS_GENERATOR_LOOP
- || block->type == NJS_GENERATOR_SWITCH)
- {
- goto found;
- }
- }
-
- njs_generate_syntax_error(vm, node->token_line, "Illegal break statement");
+ block = njs_generate_find_block(generator->block, NJS_GENERATOR_ALL);
- return NXT_ERROR;
+ if (nxt_slow_path(block == NULL)) {
+ goto syntax_error;
+ }
-found:
+ if (block->type == NJS_GENERATOR_TRY
+ && njs_generate_find_block(block->next, NJS_GENERATOR_ALL) == NULL)
+ {
+ goto syntax_error;
+ }
/* TODO: LABEL: loop and switch may have label, block must have label. */
jump->code.retval = NJS_VMCODE_NO_RETVAL;
jump->offset = offsetof(njs_vmcode_jump_t, offset);
+ patch->index_offset = 0;
patch->jump_offset = njs_code_offset(generator, jump)
+ offsetof(njs_vmcode_jump_t, offset);
}
return NXT_OK;
+
+syntax_error:
+
+ njs_generate_syntax_error(vm, node->token_line, "Illegal break statement");
+
+ return NXT_ERROR;
}
njs_generate_return_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- nxt_int_t ret;
- njs_index_t index;
- njs_vmcode_return_t *code;
+ nxt_int_t ret;
+ njs_index_t index;
+ njs_vmcode_return_t *code;
+ njs_generator_patch_t *patch;
+ njs_generator_block_t *block;
+ njs_vmcode_try_return_t *try_return;
ret = njs_generator(vm, generator, node->right);
- if (nxt_fast_path(ret == NXT_OK)) {
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
+ if (node->right != NULL) {
+ index = node->right->index;
+
+ } else {
+ index = njs_value_index(vm, &njs_value_void,
+ generator->runtime);
+ }
+
+ block = njs_generate_find_block(generator->block, NJS_GENERATOR_TRY);
+
+ if (nxt_fast_path(block == NULL)) {
njs_generate_code(generator, njs_vmcode_return_t, code);
code->code.operation = njs_vmcode_return;
code->code.operands = NJS_VMCODE_1OPERAND;
code->code.retval = NJS_VMCODE_NO_RETVAL;
- if (node->right != NULL) {
- index = node->right->index;
-
- } else {
- index = njs_value_index(vm, &njs_value_void, generator->runtime);
- }
-
code->retval = index;
node->index = index;
+
+ return NXT_OK;
}
- return ret;
+ patch = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ sizeof(njs_generator_patch_t));
+ if (nxt_slow_path(patch == NULL)) {
+ return NXT_ERROR;
+ }
+
+ patch->next = block->exit;
+ block->exit = patch;
+
+ njs_generate_code(generator, njs_vmcode_try_return_t, try_return);
+ try_return->code.operation = njs_vmcode_try_return;
+ try_return->code.operands = NJS_VMCODE_2OPERANDS;
+ try_return->code.retval = NJS_VMCODE_RETVAL;
+ try_return->retval = index;
+
+ try_return->save = index;
+ patch->index_offset = njs_code_offset(generator, try_return)
+ + offsetof(njs_vmcode_try_return_t, save);
+
+ try_return->offset = offsetof(njs_vmcode_try_return_t, offset);
+ patch->jump_offset = njs_code_offset(generator, try_return)
+ + offsetof(njs_vmcode_try_return_t, offset);
+
+ return NXT_OK;
}
njs_generate_try_statement(njs_vm_t *vm, njs_generator_t *generator,
njs_parser_node_t *node)
{
- njs_ret_t try_offset, catch_offset;
- nxt_int_t ret;
- njs_index_t index, catch_index;
- njs_vmcode_catch_t *catch;
- njs_vmcode_finally_t *finally;
- njs_vmcode_try_end_t *try_end, *catch_end;
- njs_vmcode_try_start_t *try_start;
+ njs_ret_t try_offset, try_end_offset, catch_offset,
+ catch_end_offset;
+ nxt_int_t ret;
+ njs_index_t exception_index, exit_index, catch_index;
+ njs_vmcode_catch_t *catch;
+ njs_vmcode_finally_t *finally;
+ njs_vmcode_try_end_t *try_end, *catch_end;
+ njs_generator_block_t *block, *try_block, *catch_block;
+ njs_vmcode_try_start_t *try_start;
+ njs_vmcode_try_trampoline_t *try_break, *try_continue;
njs_generate_code(generator, njs_vmcode_try_start_t, try_start);
try_offset = njs_code_offset(generator, try_start);
try_start->code.operands = NJS_VMCODE_2OPERANDS;
try_start->code.retval = NJS_VMCODE_NO_RETVAL;
- index = njs_generate_temp_index_get(vm, generator, node);
- if (nxt_slow_path(index == NJS_INDEX_ERROR)) {
+ exception_index = njs_generate_temp_index_get(vm, generator, node);
+ if (nxt_slow_path(exception_index == NJS_INDEX_ERROR)) {
return NXT_ERROR;
}
- try_start->value = index;
+ try_start->exception_value = exception_index;
+
+ /*
+ * exit_value is used in njs_vmcode_finally to make a decision
+ * which way to go after "break", "continue" and "return" instruction
+ * inside "try" or "catch" blocks.
+ */
+
+ exit_index = njs_generate_temp_index_get(vm, generator, node);
+ if (nxt_slow_path(exit_index == NJS_INDEX_ERROR)) {
+ return NXT_ERROR;
+ }
+
+ try_start->exit_value = exit_index;
+
+ ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_TRY, &no_label);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
ret = njs_generator(vm, generator, node->left);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
+ try_block = generator->block;
+
njs_generate_code(generator, njs_vmcode_try_end_t, try_end);
+ try_end_offset = njs_code_offset(generator, try_end);
try_end->code.operation = njs_vmcode_try_end;
try_end->code.operands = NJS_VMCODE_NO_OPERAND;
try_end->code.retval = NJS_VMCODE_NO_RETVAL;
+ if (try_block->exit != NULL) {
+ njs_generate_patch_try_exit_block(vm, generator, try_block->exit,
+ exit_index);
+
+ njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break);
+ try_break->code.operation = njs_vmcode_try_break;
+ try_break->code.operands = NJS_VMCODE_2OPERANDS;
+ try_break->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ try_break->exit_value = exit_index;
+
+ try_break->offset = -sizeof(njs_vmcode_try_end_t);
+
+ } else {
+ try_break = NULL;
+ }
+
+ if (try_block->continuation != NULL) {
+ njs_generate_patch_block(vm, generator, try_block->continuation);
+
+ njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue);
+ try_continue->code.operation = njs_vmcode_try_continue;
+ try_continue->code.operands = NJS_VMCODE_2OPERANDS;
+ try_continue->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ try_continue->exit_value = exit_index;
+
+ try_continue->offset = -sizeof(njs_vmcode_try_end_t);
+
+ if (try_break != NULL) {
+ try_continue->offset -= sizeof(njs_vmcode_try_trampoline_t);
+ }
+ }
+
+ generator->block = try_block->next;
+
njs_code_set_jump_offset(generator, njs_vmcode_try_start_t, try_offset);
- try_offset = njs_code_offset(generator, try_end);
+ try_offset = try_end_offset;
node = node->right;
njs_code_set_jump_offset(generator, njs_vmcode_try_end_t, try_offset);
+ if (try_block->continuation != NULL || try_block->exit != NULL) {
+ njs_generate_code(generator, njs_vmcode_finally_t, finally);
+ finally->code.operation = njs_vmcode_finally;
+ finally->code.operands = NJS_VMCODE_2OPERANDS;
+ finally->code.retval = NJS_VMCODE_NO_RETVAL;
+ finally->retval = exception_index;
+ finally->exit_value = exit_index;
+ finally->continue_offset = offsetof(njs_vmcode_finally_t,
+ continue_offset);
+ finally->break_offset = offsetof(njs_vmcode_finally_t,
+ break_offset);
+
+ if (try_block->continuation != NULL) {
+ /*
+ * block != NULL is checked
+ * by njs_generate_continue_statement()
+ */
+ block = njs_generate_find_block(generator->block,
+ NJS_GENERATOR_LOOP);
+
+ njs_generate_make_continuation_patch(vm, generator, block,
+ njs_code_offset(generator, finally)
+ + offsetof(njs_vmcode_finally_t, continue_offset));
+ }
+
+ if (try_block->exit != NULL) {
+ block = njs_generate_find_block(generator->block,
+ NJS_GENERATOR_ALL);
+
+ if (block != NULL) {
+ njs_generate_make_exit_patch(vm, generator, block,
+ njs_code_offset(generator, finally)
+ + offsetof(njs_vmcode_finally_t, break_offset));
+ }
+ }
+ }
+
/* TODO: release exception variable index. */
} else {
catch->code.retval = NJS_VMCODE_NO_RETVAL;
catch->exception = catch_index;
+ ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_TRY,
+ &no_label);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return ret;
+ }
+
ret = njs_generator(vm, generator, node->left->right);
if (nxt_slow_path(ret != NXT_OK)) {
return ret;
}
+ catch_block = generator->block;
+
njs_generate_code(generator, njs_vmcode_try_end_t, catch_end);
+ catch_end_offset = njs_code_offset(generator, catch_end);
catch_end->code.operation = njs_vmcode_try_end;
catch_end->code.operands = NJS_VMCODE_NO_OPERAND;
catch_end->code.retval = NJS_VMCODE_NO_RETVAL;
+ if (catch_block->exit != NULL) {
+ njs_generate_patch_try_exit_block(vm, generator,
+ catch_block->exit,
+ exit_index);
+
+ njs_generate_code(generator, njs_vmcode_try_trampoline_t,
+ try_break);
+ try_break->code.operation = njs_vmcode_try_break;
+ try_break->code.operands = NJS_VMCODE_2OPERANDS;
+ try_break->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ try_break->exit_value = exit_index;
+
+ try_break->offset = -sizeof(njs_vmcode_try_end_t);
+
+ } else {
+ try_break = NULL;
+ }
+
+ if (catch_block->continuation != NULL) {
+ njs_generate_patch_block(vm, generator,
+ catch_block->continuation);
+
+ njs_generate_code(generator, njs_vmcode_try_trampoline_t,
+ try_continue);
+ try_continue->code.operation = njs_vmcode_try_continue;
+ try_continue->code.operands = NJS_VMCODE_2OPERANDS;
+ try_continue->code.retval = NJS_VMCODE_NO_RETVAL;
+
+ try_continue->exit_value = exit_index;
+
+ try_continue->offset = -sizeof(njs_vmcode_try_end_t);
+
+ if (try_break != NULL) {
+ try_continue->offset -= sizeof(njs_vmcode_try_trampoline_t);
+ }
+ }
+
+ generator->block = catch_block->next;
+
njs_code_set_jump_offset(generator, njs_vmcode_catch_t,
catch_offset);
- catch_offset = njs_code_offset(generator, catch_end);
/* TODO: release exception variable index. */
catch->code.operands = NJS_VMCODE_2OPERANDS;
catch->code.retval = NJS_VMCODE_NO_RETVAL;
catch->offset = sizeof(njs_vmcode_catch_t);
- catch->exception = index;
+ catch->exception = exception_index;
njs_code_set_jump_offset(generator, njs_vmcode_try_end_t,
- catch_offset);
+ catch_end_offset);
} else {
/* A try/finally case. */
catch->code.operands = NJS_VMCODE_2OPERANDS;
catch->code.retval = NJS_VMCODE_NO_RETVAL;
catch->offset = sizeof(njs_vmcode_catch_t);
- catch->exception = index;
+ catch->exception = exception_index;
+
+ catch_block = NULL;
}
njs_code_set_jump_offset(generator, njs_vmcode_try_end_t, try_offset);
njs_generate_code(generator, njs_vmcode_finally_t, finally);
finally->code.operation = njs_vmcode_finally;
- finally->code.operands = NJS_VMCODE_1OPERAND;
+ finally->code.operands = NJS_VMCODE_2OPERANDS;
finally->code.retval = NJS_VMCODE_NO_RETVAL;
- finally->retval = index;
+ finally->retval = exception_index;
+ finally->exit_value = exit_index;
+ finally->continue_offset = offsetof(njs_vmcode_finally_t,
+ continue_offset);
+ finally->break_offset = offsetof(njs_vmcode_finally_t, break_offset);
+
+ if (try_block->continuation != NULL
+ || (catch_block && catch_block->continuation != NULL))
+ {
+ /*
+ * block != NULL is checked
+ * by njs_generate_continue_statement()
+ */
+ block = njs_generate_find_block(generator->block,
+ NJS_GENERATOR_LOOP);
+
+ njs_generate_make_continuation_patch(vm, generator, block,
+ njs_code_offset(generator, finally)
+ + offsetof(njs_vmcode_finally_t, continue_offset));
+ }
+
+ if (try_block->exit != NULL
+ || (catch_block != NULL && catch_block->exit != NULL))
+ {
+ block = njs_generate_find_block(generator->block,
+ NJS_GENERATOR_ALL);
+ if (block != NULL) {
+ njs_generate_make_exit_patch(vm, generator, block,
+ njs_code_offset(generator, finally)
+ + offsetof(njs_vmcode_finally_t, break_offset));
+ }
+ }
}
- return njs_generate_index_release(vm, generator, index);
+ return njs_generate_index_release(vm, generator, exception_index);
}
{ nxt_string("throw\nnull"),
nxt_string("SyntaxError: Illegal newline after throw in 2") },
+ { nxt_string("for (var x in [1,2]) { try{ continue; } catch(e) {} } throw 1"),
+ nxt_string("1") },
+
+ { nxt_string("for (var x in [1,2]) { try{ break; } catch(e) {} } throw 1"),
+ nxt_string("1") },
+
+ { nxt_string("try\n {\n continue; } catch(e) {}"),
+ nxt_string("SyntaxError: Illegal continue statement in 3") },
+
+ { nxt_string("var a = 1; "
+ "switch (a) {"
+ "default:"
+ " try\n {\n continue; } "
+ " catch(e) {}"
+ "}"),
+ nxt_string("SyntaxError: Illegal continue statement in 3") },
+
+ { nxt_string("try\n {\n break; } catch(e) {}"),
+ nxt_string("SyntaxError: Illegal break statement in 3") },
+
+ { nxt_string("try\n { }\n catch(e) {continue;}"),
+ nxt_string("SyntaxError: Illegal continue statement in 3") },
+
+ { nxt_string("try { } catch(e) {break;}"),
+ nxt_string("SyntaxError: Illegal break statement in 1") },
+
+ { nxt_string("try { continue; } finally {}"),
+ nxt_string("SyntaxError: Illegal continue statement in 1") },
+
+ { nxt_string("try { break; } finally {}"),
+ nxt_string("SyntaxError: Illegal break statement in 1") },
+
+ { nxt_string("try\n {\n try\n {\n continue; } finally {} } finally {}"),
+ nxt_string("SyntaxError: Illegal continue statement in 5") },
+
+ /* break from try in try/catch. */
+
+ { nxt_string("function f(n) {"
+ " var pre = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try { "
+ " if (n == 'b') {break;}"
+ " }"
+ " catch (e) {};"
+ " post++"
+ " }"
+ " return [pre, post];"
+ "}; njs.dump([f(),f('b')])"),
+ nxt_string("[[3,3],"
+ "[1,0]]") },
+
+ { nxt_string("function f(v, n) {"
+ " var pre = 0; var post = 0; var case2 = 0;"
+ " switch (v) {"
+ " case 1: "
+ " pre++;"
+ " try { "
+ " if (n == 'b') {break;}"
+ " }"
+ " catch (e) {};"
+ " post++;"
+ " break;"
+ " default:"
+ " case2++;"
+ " }"
+ " return [pre, post, case2];"
+ "}; njs.dump([f(),f(1)])"),
+ nxt_string("[[0,0,1],"
+ "[1,1,0]]") },
+
+ /* continue from try in try/catch. */
+
+ { nxt_string("function f(n) {"
+ " var pre = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try { "
+ " if (n == 'c') {continue;}"
+ " }"
+ " catch (e) {};"
+ " post++"
+ " }"
+ " return [pre, post];"
+ "}; njs.dump([f(),f('c')])"),
+ nxt_string("[[3,3],"
+ "[3,0]]") },
+
+ /* Multiple break/continue from try in try/catch. */
+
+ { nxt_string("function f(n) {"
+ " var pre = 0; var mid = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try { "
+ " if (n == 'c') {continue;}"
+ " if (n == 'b') {break;}"
+ " mid++;"
+ " if (n == 'c2') {continue;}"
+ " if (n == 'b2') {break;}"
+ " }"
+ " catch (e) {};"
+ " post++"
+ " }"
+ " return [pre, mid, post];"
+ "}; njs.dump([f(),f('c'),f('b'),f('c2'),f('b2')])"),
+ nxt_string("[[3,3,3],"
+ "[3,0,0],"
+ "[1,0,0],"
+ "[3,3,0],"
+ "[1,1,0]]") },
+
+ /* Multiple break/continue from catch in try/catch. */
+
+ { nxt_string("function f(t, n) {"
+ " var pre = 0; var mid = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try { "
+ " if (t) {throw 'a'}"
+ " }"
+ " catch (e) {"
+ " if (n == 'c') {continue;}"
+ " if (n == 'b') {break;}"
+ " mid++;"
+ " if (n == 'c2') {continue;}"
+ " if (n == 'b2') {break;}"
+ " };"
+ " post++"
+ " }"
+ " return [pre, mid, post];"
+ "}; njs.dump([f(), f(1), f(1, 'c'), f(1, 'b'), f(1, 'c2'), f(1, 'b2')])"),
+ nxt_string("[[3,0,3],"
+ "[3,3,3],"
+ "[3,0,0],"
+ "[1,0,0],"
+ "[3,3,0],"
+ "[1,1,0]]") },
+
+ /* break from try in try/finally. */
+
+ { nxt_string("function f(n) {"
+ " var pre = 0; var mid = 0; var fin = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try { "
+ " if (n == 'c') {continue;}"
+ " if (n == 'b') {break;}"
+ " mid++;"
+ " if (n == 'c2') {continue;}"
+ " if (n == 'b2') {break;}"
+ " }"
+ " finally {fin++};"
+ " post++"
+ " }"
+ " return [pre, mid, fin, post];"
+ "}; njs.dump([f(),f('c'),f('b'),f('c2'),f('b2')])"),
+ nxt_string("[[3,3,3,3],"
+ "[3,0,3,0],"
+ "[1,0,1,0],"
+ "[3,3,3,0],"
+ "[1,1,1,0]]") },
+
+ /* Multiple break/continue from try in try/catch/finally. */
+
+ { nxt_string("function f(n) {"
+ " var pre = 0; var mid = 0; var fin = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try { "
+ " if (n == 'c') {continue;}"
+ " if (n == 'b') {break;}"
+ " mid++;"
+ " if (n == 'c2') {continue;}"
+ " if (n == 'b2') {break;}"
+ " }"
+ " catch (e) {}"
+ " finally {fin++};"
+ " post++"
+ " }"
+ " return [pre, mid, fin, post];"
+ "}; njs.dump([f(),f('c'),f('b'),f('c2'),f('b2')])"),
+ nxt_string("[[3,3,3,3],"
+ "[3,0,3,0],"
+ "[1,0,1,0],"
+ "[3,3,3,0],"
+ "[1,1,1,0]]") },
+
+ /* Multiple break/continue from catch in try/catch/finally. */
+
+ { nxt_string("function f(t, n) {"
+ " var pre = 0; var mid = 0; var fin = 0; var post = 0;"
+ " for (var x in [1, 2, 3]) {"
+ " pre++;"
+ " try {if (t) {throw 'a'}}"
+ " catch (e) { "
+ " if (n == 'c') {continue;}"
+ " if (n == 'b') {break;}"
+ " mid++;"
+ " if (n == 'c2') {continue;}"
+ " if (n == 'b2') {break;}"
+ " }"
+ " finally {fin++};"
+ " post++"
+ " }"
+ " return [pre, mid, fin, post];"
+ "}; njs.dump([f(), f(1), f(1, 'c'), f(1, 'b'), f(1, 'c2'), f(1, 'b2')])"),
+ nxt_string("[[3,0,3,3],"
+ "[3,3,3,3],"
+ "[3,0,3,0],"
+ "[1,0,1,0],"
+ "[3,3,3,0],"
+ "[1,1,1,0]]") },
+
+ /* Multiple return from try. */
+
+ { nxt_string("var r = 0; "
+ "function f(i, n) {"
+ " try { "
+ " var a = 'x'; "
+ " if (i != 0) {"
+ " return a.repeat(n);"
+ " } else {"
+ " return;"
+ " }"
+ " }"
+ " catch (e) { } "
+ " finally { r++; }};"
+ "[f(1,1), f(1,2), f(0), r]"),
+ nxt_string("x,xx,,3") },
+
+ { nxt_string("var r = 0; "
+ "function f(i) {"
+ " try { "
+ " return i;"
+ " }"
+ " catch (e) { } "
+ " finally { r++; }};"
+ "[f(true), f(false), r]"),
+ nxt_string("true,false,2") },
+
+ /* Multiple return from catch. */
+
+ { nxt_string("var r = 0; "
+ "function f(i, n) {"
+ " try { "
+ " throw 1;"
+ " }"
+ " catch (e) { "
+ " var a = 'x'; "
+ " if (i != 0) {"
+ " return a.repeat(n);"
+ " } else {"
+ " return;"
+ " }"
+ " } "
+ " finally { r++; }};"
+ "[f(1,1), f(1,2), f(0), r]"),
+ nxt_string("x,xx,,3") },
+
+ /* return overrun by finally. */
+
+ { nxt_string("function f() {"
+ " try { "
+ " return 'a';"
+ " }"
+ " catch (e) { "
+ " } "
+ " finally { "
+ " return 'b'; "
+ " }}; "
+ "f()"),
+ nxt_string("b") },
+
+ { nxt_string("(function (f, val) { "
+ " try { return f(val); } "
+ " finally { return val; }"
+ "})(function () {throw 'a'}, 'v')"),
+ nxt_string("v") },
+
{ nxt_string("var o = { valueOf: function() { return '3' } }; --o"),
nxt_string("2") },