[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