[njs] Fixed exit instructions inside try blocks.
Dmitry Volyntsev
xeioex at nginx.com
Wed Dec 26 10:23:40 UTC 2018
details: https://hg.nginx.org/njs/rev/e1e54922e085
branches:
changeset: 696:e1e54922e085
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Wed Dec 26 13:23:16 2018 +0300
description:
Fixed exit instructions inside try blocks.
Fixed instructions: continue, break, return.
diffstat:
njs/njs_disassembler.c | 61 +++++-
njs/njs_generator.c | 468 ++++++++++++++++++++++++++++++++++++------
njs/njs_vm.c | 113 +++++++++-
njs/njs_vm.h | 27 ++-
njs/test/njs_expect_test.exp | 4 +
njs/test/njs_unit_test.c | 280 +++++++++++++++++++++++++
6 files changed, 858 insertions(+), 95 deletions(-)
diffs (truncated from 1325 to 1000 lines):
diff -r 7b1b3eb864e0 -r e1e54922e085 njs/njs_disassembler.c
--- a/njs/njs_disassembler.c Tue Dec 25 09:53:27 2018 +0300
+++ b/njs/njs_disassembler.c Wed Dec 26 13:23:16 2018 +0300
@@ -125,8 +125,6 @@ static njs_code_name_t code_names[] = {
{ njs_vmcode_throw, sizeof(njs_vmcode_throw_t),
nxt_string("THROW ") },
- { njs_vmcode_finally, sizeof(njs_vmcode_finally_t),
- nxt_string("FINALLY ") },
};
@@ -162,15 +160,18 @@ njs_disassemble(u_char *start, u_char *e
njs_vmcode_3addr_t *code3;
njs_vmcode_array_t *array;
njs_vmcode_catch_t *catch;
+ njs_vmcode_finally_t *finally;
njs_vmcode_try_end_t *try_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_try_return_t *try_return;
njs_vmcode_equal_jump_t *equal;
njs_vmcode_prop_foreach_t *prop_foreach;
njs_vmcode_method_frame_t *method;
+ njs_vmcode_try_trampoline_t *try_tramp;
njs_vmcode_function_frame_t *function;
p = start;
@@ -323,8 +324,9 @@ njs_disassemble(u_char *start, u_char *e
if (operation == njs_vmcode_try_start) {
try_start = (njs_vmcode_try_start_t *) p;
- printf("%05zd TRY START %04zX +%zd\n",
- p - start, (size_t) try_start->value,
+ printf("%05zd TRY START %04zX %04zX +%zd\n",
+ p - start, (size_t) try_start->exception_value,
+ (size_t) try_start->exit_value,
(size_t) try_start->offset);
p += sizeof(njs_vmcode_try_start_t);
@@ -332,6 +334,43 @@ njs_disassemble(u_char *start, u_char *e
continue;
}
+ if (operation == njs_vmcode_try_break) {
+ try_tramp = (njs_vmcode_try_trampoline_t *) p;
+
+ printf("%05zd TRY BREAK %04zX %zd\n",
+ p - start, (size_t) try_tramp->exit_value,
+ (size_t) try_tramp->offset);
+
+ p += sizeof(njs_vmcode_try_trampoline_t);
+
+ continue;
+ }
+
+ if (operation == njs_vmcode_try_continue) {
+ try_tramp = (njs_vmcode_try_trampoline_t *) p;
+
+ printf("%05zd TRY CONTINUE %04zX %zd\n",
+ p - start, (size_t) try_tramp->exit_value,
+ (size_t) try_tramp->offset);
+
+ p += sizeof(njs_vmcode_try_trampoline_t);
+
+ continue;
+ }
+
+ if (operation == njs_vmcode_try_return) {
+ try_return = (njs_vmcode_try_return_t *) p;
+
+ printf("%05zd TRY RETURN %04zX %04zX +%zd\n",
+ p - start, (size_t) try_return->save,
+ (size_t) try_return->retval,
+ (size_t) try_return->offset);
+
+ p += sizeof(njs_vmcode_try_return_t);
+
+ continue;
+ }
+
if (operation == njs_vmcode_catch) {
catch = (njs_vmcode_catch_t *) p;
@@ -355,6 +394,20 @@ njs_disassemble(u_char *start, u_char *e
continue;
}
+ if (operation == njs_vmcode_finally) {
+ finally = (njs_vmcode_finally_t *) p;
+
+ printf("%05zd TRY FINALLY %04zX %04zX +%zd +%zd\n",
+ p - start, (size_t) finally->retval,
+ (size_t) finally->exit_value,
+ (size_t) finally->continue_offset,
+ (size_t) finally->break_offset);
+
+ p += sizeof(njs_vmcode_finally_t);
+
+ continue;
+ }
+
code_name = code_names;
n = nxt_nitems(code_names);
diff -r 7b1b3eb864e0 -r e1e54922e085 njs/njs_generator.c
--- a/njs/njs_generator.c Tue Dec 25 09:53:27 2018 +0300
+++ b/njs/njs_generator.c Wed Dec 26 13:23:16 2018 +0300
@@ -20,13 +20,24 @@ struct njs_generator_patch_s {
*/
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;
@@ -70,8 +81,16 @@ static nxt_int_t njs_generate_for_in_sta
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,
@@ -866,6 +885,7 @@ njs_generate_switch_statement(njs_vm_t *
return NXT_ERROR;
}
+ patch->index_offset = 0;
patch->jump_offset = njs_code_offset(generator, equal)
+ offsetof(njs_vmcode_equal_jump_t, offset);
@@ -966,7 +986,7 @@ njs_generate_while_statement(njs_vm_t *v
/* 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);
@@ -1016,7 +1036,7 @@ njs_generate_do_while_statement(njs_vm_t
/* The loop condition. */
- njs_generate_patch_loop_continuation(vm, generator);
+ njs_generate_patch_block(vm, generator, generator->block->continuation);
condition = node->right;
@@ -1100,7 +1120,7 @@ njs_generate_for_statement(njs_vm_t *vm,
/* The loop update. */
- njs_generate_patch_loop_continuation(vm, generator);
+ njs_generate_patch_block(vm, generator, generator->block->continuation);
update = node->right;
@@ -1199,7 +1219,7 @@ njs_generate_for_in_statement(njs_vm_t *
/* 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);
@@ -1258,15 +1278,51 @@ njs_generate_start_block(njs_vm_t *vm, n
}
+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;
@@ -1276,20 +1332,56 @@ njs_generate_patch_loop_continuation(njs
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)
+{
+ njs_generator_patch_t *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;
- njs_generator_patch_t *patch, *next;
block = generator->block;
generator->block = block->next;
- for (patch = block->exit; patch != NULL; patch = next) {
- njs_code_update_offset(generator, patch);
- next = patch->next;
-
- nxt_mem_cache_free(vm->mem_cache_pool, patch);
- }
+ njs_generate_patch_block(vm, generator, block->exit);
nxt_mem_cache_free(vm->mem_cache_pool, block);
}
@@ -1299,22 +1391,22 @@ static nxt_int_t
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;
- }
+ block = njs_generate_find_block(generator->block,
+ NJS_GENERATOR_LOOP | NJS_GENERATOR_TRY);
+
+ if (nxt_slow_path(block == NULL)) {
+ goto syntax_error;
}
- njs_generate_syntax_error(vm, node->token_line,
- "Illegal continue statement");
-
- return NXT_ERROR;
-
-found:
+ if (block->type == NJS_GENERATOR_TRY
+ && njs_generate_find_block(block->next, NJS_GENERATOR_LOOP) == NULL)
+ {
+ goto syntax_error;
+ }
/* TODO: LABEL */
@@ -1331,11 +1423,19 @@ found:
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;
}
@@ -1343,24 +1443,22 @@ static nxt_int_t
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;
- }
+ block = njs_generate_find_block(generator->block, NJS_GENERATOR_ALL);
+
+ if (nxt_slow_path(block == NULL)) {
+ goto syntax_error;
+ }
+
+ if (block->type == NJS_GENERATOR_TRY
+ && njs_generate_find_block(block->next, NJS_GENERATOR_ALL) == NULL)
+ {
+ goto syntax_error;
}
- njs_generate_syntax_error(vm, node->token_line, "Illegal break statement");
-
- return NXT_ERROR;
-
-found:
-
/* TODO: LABEL: loop and switch may have label, block must have label. */
patch = nxt_mem_cache_alloc(vm->mem_cache_pool,
@@ -1376,11 +1474,18 @@ found:
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;
}
@@ -2356,30 +2461,65 @@ static nxt_int_t
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;
}
@@ -2534,13 +2674,16 @@ static nxt_int_t
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);
@@ -2548,25 +2691,82 @@ njs_generate_try_statement(njs_vm_t *vm,
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;
@@ -2592,6 +2792,43 @@ njs_generate_try_statement(njs_vm_t *vm,
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 {
@@ -2610,19 +2847,67 @@ njs_generate_try_statement(njs_vm_t *vm,
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. */
@@ -2631,10 +2916,10 @@ njs_generate_try_statement(njs_vm_t *vm,
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. */
@@ -2644,7 +2929,9 @@ njs_generate_try_statement(njs_vm_t *vm,
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);
@@ -2656,12 +2943,43 @@ njs_generate_try_statement(njs_vm_t *vm,
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);
}
diff -r 7b1b3eb864e0 -r e1e54922e085 njs/njs_vm.c
--- a/njs/njs_vm.c Tue Dec 25 09:53:27 2018 +0300
+++ b/njs/njs_vm.c Wed Dec 26 13:23:16 2018 +0300
@@ -132,7 +132,8 @@ start:
* njs_vmcode_function_call(),
* njs_vmcode_return(),
* njs_vmcode_try_start(),
- * njs_vmcode_try_next(),
+ * njs_vmcode_try_continue(),
+ * njs_vmcode_try_break(),
* njs_vmcode_try_end(),
* njs_vmcode_catch().
* njs_vmcode_throw().
@@ -2580,9 +2581,12 @@ njs_vmcode_stop(njs_vm_t *vm, njs_value_
*/
njs_ret_t
-njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *value, njs_value_t *offset)
+njs_vmcode_try_start(njs_vm_t *vm, njs_value_t *exception_value,
+ njs_value_t *offset)
{
- njs_exception_t *e;
+ njs_value_t *exit_value;
+ njs_exception_t *e;
+ njs_vmcode_try_start_t *try_start;
if (vm->top_frame->exception.catch != NULL) {
e = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_exception_t));
@@ -2597,13 +2601,68 @@ njs_vmcode_try_start(njs_vm_t *vm, njs_v
vm->top_frame->exception.catch = vm->current + (njs_ret_t) offset;
- njs_set_invalid(value);
+ njs_set_invalid(exception_value);
+
+ try_start = (njs_vmcode_try_start_t *) vm->current;
+ exit_value = njs_vmcode_operand(vm, try_start->exit_value);
+
+ njs_set_invalid(exit_value);
+ exit_value->data.u.number = 0;
return sizeof(njs_vmcode_try_start_t);
}
/*
+ * njs_vmcode_try_break() sets exit_value to INVALID 1, and jumps to
+ * the nearest try_end block. The exit_value is checked by njs_vmcode_finally().
+ */
+
+nxt_noinline njs_ret_t
+njs_vmcode_try_break(njs_vm_t *vm, njs_value_t *exit_value,
+ njs_value_t *offset)
+{
+ exit_value->data.u.number = 1;
+
+ return (njs_ret_t) offset;
+}
+
+
+/*
+ * njs_vmcode_try_break() sets exit_value to INVALID -1, and jumps to
+ * the nearest try_end block. The exit_value is checked by njs_vmcode_finally().
+ */
+
+nxt_noinline njs_ret_t
+njs_vmcode_try_continue(njs_vm_t *vm, njs_value_t *exit_value,
+ njs_value_t *offset)
+{
+ exit_value->data.u.number = -1;
+
+ return (njs_ret_t) offset;
+}
+
+/*
+ * njs_vmcode_try_return() saves a return value to use it later by
+ * njs_vmcode_finally(), and jumps to the nearest try_end block.
+ */
+
+nxt_noinline njs_ret_t
+njs_vmcode_try_return(njs_vm_t *vm, njs_value_t *value, njs_value_t *invld)
+{
+ njs_vmcode_try_return_t *try_return;
+
+ vm->retval = *value;
+
+ njs_retain(value);
+
+ try_return = (njs_vmcode_try_return_t *) vm->current;
+
+ return try_return->offset;
+}
+
+
+/*
* njs_vmcode_try_end() is set on the end of a "try" block to remove the block.
* It is also set on the end of a "catch" block followed by a "finally" block.
*/
@@ -2664,24 +2723,48 @@ njs_vmcode_catch(njs_vm_t *vm, njs_value
/*
- * njs_vmcode_finally() is set on the end of a "finally" block to throw
- * uncaught exception.
+ * njs_vmcode_finally() is set on the end of a "finally" or a "catch" block.
+ * 1) to throw uncaught exception.
+ * 2) to make a jump to an enslosing loop exit if "continue" or "break"
+ * statement was used inside try block.
+ * 3) to finalize "return" instruction from "try" block.
*/
njs_ret_t
njs_vmcode_finally(njs_vm_t *vm, njs_value_t *invld, njs_value_t *retval)
{
- njs_value_t *value;
-
- value = njs_vmcode_operand(vm, retval);
-
- if (!njs_is_valid(value)) {
- return sizeof(njs_vmcode_finally_t);
+ njs_value_t *exception_value, *exit_value;
+ njs_vmcode_finally_t *finally;
+
+ exception_value = njs_vmcode_operand(vm, retval);
+
+ if (njs_is_valid(exception_value)) {
+ vm->retval = *exception_value;
+
+ return NXT_ERROR;
}
- vm->retval = *value;
-
- return NXT_ERROR;
+ finally = (njs_vmcode_finally_t *) vm->current;
+ exit_value = njs_vmcode_operand(vm, finally->exit_value);
+
+ /*
+ * exit_value is set by:
+ * vmcode_try_start to INVALID 0
+ * vmcode_try_break to INVALID 1
+ * vmcode_try_continue to INVALID -1
+ * vmcode_try_return to a valid return value
+ */
+
+ if (njs_is_valid(exit_value)) {
+ return njs_vmcode_return(vm, NULL, exit_value);
+
+ } else if (exit_value->data.u.number != 0) {
+ return (njs_ret_t) (exit_value->data.u.number > 0)
+ ? finally->break_offset
+ : finally->continue_offset;
+ }
+
+ return sizeof(njs_vmcode_finally_t);
}
diff -r 7b1b3eb864e0 -r e1e54922e085 njs/njs_vm.h
--- a/njs/njs_vm.h Tue Dec 25 09:53:27 2018 +0300
+++ b/njs/njs_vm.h Wed Dec 26 13:23:16 2018 +0300
@@ -774,13 +774,21 @@ typedef struct {
typedef struct {
njs_vmcode_t code;
njs_ret_t offset;
- njs_index_t value;
+ njs_index_t exception_value;
+ njs_index_t exit_value;
} njs_vmcode_try_start_t;
typedef struct {
njs_vmcode_t code;
njs_ret_t offset;
+ njs_index_t exit_value;
+} njs_vmcode_try_trampoline_t;
+
+
+typedef struct {
+ njs_vmcode_t code;
+ njs_ret_t offset;
njs_index_t exception;
} njs_vmcode_catch_t;
@@ -799,7 +807,18 @@ typedef struct {
typedef struct {
njs_vmcode_t code;
+ njs_index_t save;
njs_index_t retval;
+ njs_ret_t offset;
+} njs_vmcode_try_return_t;
+
+
+typedef struct {
More information about the nginx-devel
mailing list