[njs] Added labels support.

Dmitry Volyntsev xeioex at nginx.com
Mon Feb 25 16:01:16 UTC 2019


details:   https://hg.nginx.org/njs/rev/032827ab80dc
branches:  
changeset: 799:032827ab80dc
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Mon Feb 25 19:00:55 2019 +0300
description:
Added labels support.

diffstat:

 njs/njs_generator.c      |  394 ++++++++++++++++++++++++++++++++++------------
 njs/njs_lexer.c          |   26 +++
 njs/njs_parser.c         |  140 +++++++++++-----
 njs/njs_parser.h         |    4 +
 njs/njs_variable.c       |   92 ++++++++++
 njs/njs_variable.h       |    6 +
 njs/test/njs_unit_test.c |  265 +++++++++++++++++++++++++++++++
 7 files changed, 776 insertions(+), 151 deletions(-)

diffs (truncated from 1383 to 1000 lines):

diff -r 7d0b45c56edf -r 032827ab80dc njs/njs_generator.c
--- a/njs/njs_generator.c	Mon Feb 25 19:00:54 2019 +0300
+++ b/njs/njs_generator.c	Mon Feb 25 19:00:55 2019 +0300
@@ -20,29 +20,35 @@ struct njs_generator_patch_s {
      */
     njs_ret_t                       jump_offset;
     njs_generator_patch_t           *next;
+
+    nxt_str_t                       label;
 };
 
 
 typedef enum {
-    NJS_GENERATOR_BLOCK = 1,
-    NJS_GENERATOR_LOOP = 2,
-    NJS_GENERATOR_SWITCH = 4,
+    NJS_GENERATOR_LOOP = 1,
+    NJS_GENERATOR_SWITCH = 2,
+    NJS_GENERATOR_BLOCK = 4,
     NJS_GENERATOR_TRY = 8,
-
-#define NJS_GENERATOR_ALL          (NJS_GENERATOR_BLOCK                      \
-                                    | NJS_GENERATOR_LOOP                     \
-                                    | NJS_GENERATOR_SWITCH                   \
-                                    | NJS_GENERATOR_TRY)
+#define NJS_GENERATOR_ALL          (NJS_GENERATOR_LOOP | NJS_GENERATOR_SWITCH)
 } njs_generator_block_type_t;
 
 
 struct njs_generator_block_s {
-    njs_generator_block_type_t      type;    /* 2 bits */
+    njs_generator_block_type_t      type;    /* 4 bits */
     nxt_str_t                       label;
+
+    /* list of "continue" instruction offsets to be patched. */
     njs_generator_patch_t           *continuation;
+    /*
+     * list of "return" from try-catch block and "break"
+     * instruction offsets to be patched.
+     */
     njs_generator_patch_t           *exit;
+
     njs_generator_block_t           *next;
 
+    /* exit value index, used only for NJS_GENERATOR_TRY blocks. */
     njs_index_t                     index;
 };
 
@@ -78,16 +84,21 @@ 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 njs_generator_block_t *njs_generate_lookup_block(
+    njs_generator_block_t *block, uint32_t mask, const nxt_str_t *label);
 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);
+    njs_generator_block_t *block, uint32_t mask, const nxt_str_t *label);
+static njs_generator_patch_t *njs_generate_make_continuation_patch(njs_vm_t *vm,
+    njs_generator_block_t *block, const nxt_str_t *label, 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_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 njs_generator_patch_t *njs_generate_make_exit_patch(njs_vm_t *vm,
+    njs_generator_block_t *block, const nxt_str_t *label, njs_ret_t offset);
 static nxt_noinline void njs_generate_patch_block_exit(njs_vm_t *vm,
     njs_generator_t *generator);
+static const nxt_str_t *njs_generate_jump_destination(njs_vm_t *vm,
+    njs_generator_block_t *block, const char *inst_type, uint32_t mask,
+    const nxt_str_t *label1, const nxt_str_t *label2);
 static nxt_int_t njs_generate_continue_statement(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 static nxt_int_t njs_generate_break_statement(njs_vm_t *vm,
@@ -208,7 +219,10 @@ static nxt_int_t njs_generate_function_d
     njs_parser_node_error(vm, node, NJS_OBJECT_SYNTAX_ERROR, fmt, ##__VA_ARGS__)
 
 
-static const nxt_str_t  no_label = { 0, NULL };
+static const nxt_str_t  no_label     = nxt_string("");
+static const nxt_str_t  return_label = nxt_string("@return");
+/* GCC and Clang complain about NULL argument passed to memcmp(). */
+static const nxt_str_t  undef_label  = { 0xffffffff, (u_char *) "" };
 
 
 static nxt_int_t
@@ -432,7 +446,7 @@ njs_generator(njs_vm_t *vm, njs_generato
 
     default:
         nxt_thread_log_debug("unknown token: %d", node->token);
-        njs_syntax_error(vm, "unknown token");
+        njs_internal_error(vm, "Generator failed: unknown token");
 
         return NXT_ERROR;
     }
@@ -845,7 +859,7 @@ njs_generate_switch_statement(njs_vm_t *
     }
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_SWITCH,
-                                   &no_label);
+                                   &swtch->label);
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -886,6 +900,7 @@ njs_generate_switch_statement(njs_vm_t *
 
             patch->jump_offset = njs_code_offset(generator, equal)
                                  + offsetof(njs_vmcode_equal_jump_t, offset);
+            patch->label = no_label;
 
             *last = patch;
             last = &patch->next;
@@ -970,7 +985,7 @@ njs_generate_while_statement(njs_vm_t *v
     /* The loop body. */
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP,
-                                   &no_label);
+                                   &node->label);
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -1020,7 +1035,7 @@ njs_generate_do_while_statement(njs_vm_t
     /* The loop body. */
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP,
-                                   &no_label);
+                                   &node->label);
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -1067,7 +1082,7 @@ njs_generate_for_statement(njs_vm_t *vm,
     njs_vmcode_cond_jump_t  *cond_jump;
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP,
-                                   &no_label);
+                                   &node->label);
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -1178,7 +1193,7 @@ njs_generate_for_in_statement(njs_vm_t *
     njs_vmcode_prop_foreach_t  *prop_foreach;
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_LOOP,
-                                   &no_label);
+                                   &node->label);
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -1278,10 +1293,18 @@ 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)
+njs_generate_lookup_block(njs_generator_block_t *block, uint32_t mask,
+    const nxt_str_t *label)
 {
+    if (nxt_strstr_eq(label, &return_label)) {
+        mask = NJS_GENERATOR_TRY;
+        label = &no_label;
+    }
+
     while (block != NULL) {
-        if (block->type & mask) {
+        if ((block->type & mask) != 0
+            && (label->length == 0 || nxt_strstr_eq(&block->label, label)))
+        {
             return block;
         }
 
@@ -1292,16 +1315,59 @@ njs_generate_find_block(njs_generator_bl
 }
 
 
-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 njs_generator_block_t *
+njs_generate_find_block(njs_generator_block_t *block, uint32_t mask,
+    const nxt_str_t *label)
+{
+    njs_generator_block_t  *dest_block;
+
+    /*
+     * ES5.1: 12.8 The break Statement
+     * "break" without a label is valid only from within
+     * loop or switch statement.
+     */
+    if ((mask & NJS_GENERATOR_ALL) == NJS_GENERATOR_ALL
+        && !nxt_strstr_eq(label, &no_label))
+    {
+        mask |= NJS_GENERATOR_BLOCK;
+    }
+
+    dest_block = njs_generate_lookup_block(block, mask, label);
+
+    if (dest_block != NULL) {
+
+        /*
+         * Looking for intermediate try-catch blocks. Before jumping to
+         * the destination finally blocks have to be executed.
+         */
+
+        while (block != NULL) {
+            if (block->type & NJS_GENERATOR_TRY) {
+                return block;
+            }
+
+            if (block == dest_block) {
+                return block;
+            }
+
+            block = block->next;
+        }
+    }
+
+    return dest_block;
+}
+
+
+static njs_generator_patch_t *
+njs_generate_make_continuation_patch(njs_vm_t *vm, njs_generator_block_t *block,
+    const nxt_str_t *label, njs_ret_t offset)
 {
     njs_generator_patch_t  *patch;
 
     patch = nxt_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t));
     if (nxt_slow_path(patch == NULL)) {
         njs_memory_error(vm);
-        return NXT_ERROR;
+        return NULL;
     }
 
     patch->next = block->continuation;
@@ -1309,7 +1375,9 @@ njs_generate_make_continuation_patch(njs
 
     patch->jump_offset = offset;
 
-    return NXT_OK;
+    patch->label = *label;
+
+    return patch;
 }
 
 
@@ -1328,16 +1396,16 @@ njs_generate_patch_block(njs_vm_t *vm, n
 }
 
 
-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 njs_generator_patch_t *
+njs_generate_make_exit_patch(njs_vm_t *vm, njs_generator_block_t *block,
+    const nxt_str_t *label, njs_ret_t offset)
 {
     njs_generator_patch_t  *patch;
 
     patch = nxt_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t));
     if (nxt_slow_path(patch == NULL)) {
         njs_memory_error(vm);
-        return NXT_ERROR;
+        return NULL;
     }
 
     patch->next = block->exit;
@@ -1345,7 +1413,9 @@ njs_generate_make_exit_patch(njs_vm_t *v
 
     patch->jump_offset = offset;
 
-    return NXT_OK;
+    patch->label = *label;
+
+    return patch;
 }
 
 
@@ -1363,43 +1433,79 @@ njs_generate_patch_block_exit(njs_vm_t *
 }
 
 
+/*
+ * TODO: support multiple destination points from within try-catch block.
+ */
+static const nxt_str_t *
+njs_generate_jump_destination(njs_vm_t *vm, njs_generator_block_t *block,
+    const char *inst_type, uint32_t mask, const nxt_str_t *label1,
+    const nxt_str_t *label2)
+{
+    njs_generator_block_t  *block1, *block2;
+
+    if (label1->length == undef_label.length) {
+        return label2;
+    }
+
+    if (label2->length == undef_label.length) {
+        return label1;
+    }
+
+    block1 = njs_generate_lookup_block(block, mask, label1);
+    block2 = njs_generate_lookup_block(block, mask, label2);
+
+    if (block1 != block2) {
+        njs_internal_error(vm, "%s instructions with different labels "
+                           "(\"%V\" vs \"%V\") "
+                           "from try-catch block are not supported", inst_type,
+                            label1, label2);
+
+        return NULL;
+    }
+
+    return label1;
+}
+
+
 static nxt_int_t
 njs_generate_continue_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
+    const nxt_str_t        *label, *dest;
     njs_vmcode_jump_t      *jump;
     njs_generator_patch_t  *patch;
     njs_generator_block_t  *block;
 
-    block = njs_generate_find_block(generator->block,
-                                    NJS_GENERATOR_LOOP | NJS_GENERATOR_TRY);
+    label = &node->label;
+
+    block = njs_generate_find_block(generator->block, NJS_GENERATOR_LOOP,
+                                    label);
 
     if (nxt_slow_path(block == NULL)) {
         goto syntax_error;
     }
 
-    if (block->type == NJS_GENERATOR_TRY
-        && njs_generate_find_block(block->next, NJS_GENERATOR_LOOP) == NULL)
-    {
-        goto syntax_error;
+    if (block->type == NJS_GENERATOR_TRY && block->continuation != NULL) {
+        dest = njs_generate_jump_destination(vm, block->next, "continue",
+                                             NJS_GENERATOR_LOOP,
+                                             &block->continuation->label,
+                                             label);
+        if (nxt_slow_path(dest == NULL)) {
+            return NXT_ERROR;
+        }
     }
 
-    /* TODO: LABEL */
-
-    patch = nxt_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t));
-
-    if (nxt_fast_path(patch != NULL)) {
-        patch->next = block->continuation;
-        block->continuation = patch;
-
-        njs_generate_code(generator, njs_vmcode_jump_t, jump);
-        jump->code.operation = njs_vmcode_jump;
-        jump->code.operands = NJS_VMCODE_NO_OPERAND;
-        jump->code.retval = NJS_VMCODE_NO_RETVAL;
-        jump->offset = offsetof(njs_vmcode_jump_t, offset);
-
-        patch->jump_offset = njs_code_offset(generator, jump)
-                             + offsetof(njs_vmcode_jump_t, offset);
+    njs_generate_code(generator, njs_vmcode_jump_t, jump);
+    jump->code.operation = njs_vmcode_jump;
+    jump->code.operands = NJS_VMCODE_NO_OPERAND;
+    jump->code.retval = NJS_VMCODE_NO_RETVAL;
+    jump->offset = offsetof(njs_vmcode_jump_t, offset);
+
+    patch = njs_generate_make_continuation_patch(vm, block, label,
+                                         njs_code_offset(generator, jump)
+                                         + offsetof(njs_vmcode_jump_t, offset));
+    if (nxt_slow_path(patch == NULL)) {
+        return NXT_ERROR;
     }
 
     return NXT_OK;
@@ -1416,38 +1522,38 @@ static nxt_int_t
 njs_generate_break_statement(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
+    const nxt_str_t        *label, *dest;
     njs_vmcode_jump_t      *jump;
     njs_generator_patch_t  *patch;
     njs_generator_block_t  *block;
 
-    block = njs_generate_find_block(generator->block, NJS_GENERATOR_ALL);
-
+    label = &node->label;
+
+    block = njs_generate_find_block(generator->block, NJS_GENERATOR_ALL, label);
     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;
+    if (block->type == NJS_GENERATOR_TRY && block->exit != NULL) {
+        dest = njs_generate_jump_destination(vm, block->next, "break/return",
+                                             NJS_GENERATOR_ALL,
+                                             &block->exit->label, label);
+        if (nxt_slow_path(dest == NULL)) {
+            return NXT_ERROR;
+        }
     }
 
-    /* TODO: LABEL: loop and switch may have label, block must have label. */
-
-    patch = nxt_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t));
-
-    if (nxt_fast_path(patch != NULL)) {
-        patch->next = block->exit;
-        block->exit = patch;
-
-        njs_generate_code(generator, njs_vmcode_jump_t, jump);
-        jump->code.operation = njs_vmcode_jump;
-        jump->code.operands = NJS_VMCODE_NO_OPERAND;
-        jump->code.retval = NJS_VMCODE_NO_RETVAL;
-        jump->offset = offsetof(njs_vmcode_jump_t, offset);
-
-        patch->jump_offset = njs_code_offset(generator, jump)
-                             + offsetof(njs_vmcode_jump_t, offset);
+    njs_generate_code(generator, njs_vmcode_jump_t, jump);
+    jump->code.operation = njs_vmcode_jump;
+    jump->code.operands = NJS_VMCODE_NO_OPERAND;
+    jump->code.retval = NJS_VMCODE_NO_RETVAL;
+    jump->offset = offsetof(njs_vmcode_jump_t, offset);
+
+    patch = njs_generate_make_exit_patch(vm, block, label,
+                                         njs_code_offset(generator, jump)
+                                         + offsetof(njs_vmcode_jump_t, offset));
+    if (nxt_slow_path(patch == NULL)) {
+        return NXT_ERROR;
     }
 
     return NXT_OK;
@@ -1483,7 +1589,7 @@ njs_generate_block_statement(njs_vm_t *v
     nxt_int_t  ret;
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_BLOCK,
-                                   &no_label);
+                                   &node->label);
     if (nxt_slow_path(ret != NXT_OK)) {
         return ret;
     }
@@ -2437,9 +2543,10 @@ njs_generate_return_statement(njs_vm_t *
 {
     nxt_int_t                ret;
     njs_index_t              index;
+    const nxt_str_t          *dest;
     njs_vmcode_return_t      *code;
     njs_generator_patch_t    *patch;
-    njs_generator_block_t    *block;
+    njs_generator_block_t    *block, *immediate, *top;
     njs_vmcode_try_return_t  *try_return;
 
     ret = njs_generator(vm, generator, node->right);
@@ -2452,13 +2559,13 @@ njs_generate_return_statement(njs_vm_t *
         index = node->right->index;
 
     } else {
-        index = njs_value_index(vm, &njs_value_void,
-                                generator->runtime);
+        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)) {
+    immediate = njs_generate_lookup_block(generator->block, NJS_GENERATOR_TRY,
+                                          &no_label);
+
+    if (nxt_fast_path(immediate == NULL)) {
         njs_generate_code(generator, njs_vmcode_return_t, code);
         code->code.operation = njs_vmcode_return;
         code->code.operands = NJS_VMCODE_1OPERAND;
@@ -2470,24 +2577,43 @@ njs_generate_return_statement(njs_vm_t *
         return NXT_OK;
     }
 
-    patch = nxt_mp_alloc(vm->mem_pool, sizeof(njs_generator_patch_t));
-    if (nxt_slow_path(patch == NULL)) {
-        return NXT_ERROR;
+    if (immediate->type == NJS_GENERATOR_TRY && immediate->exit != NULL) {
+        dest = njs_generate_jump_destination(vm, immediate->next,
+                                             "break/return",
+                                             NJS_GENERATOR_ALL,
+                                             &immediate->exit->label,
+                                             &return_label);
+        if (nxt_slow_path(dest == NULL)) {
+            return NXT_ERROR;
+        }
     }
 
-    patch->next = block->exit;
-    block->exit = patch;
+    top = immediate;
+    block = immediate->next;
+
+    while (block != NULL) {
+        if (block->type & NJS_GENERATOR_TRY) {
+            top = block;
+        }
+
+        block = block->next;
+    }
 
     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 = block->index;
-
+    try_return->save = top->index;
     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);
+
+    patch = njs_generate_make_exit_patch(vm, immediate, &return_label,
+                                         njs_code_offset(generator, try_return)
+                                         + offsetof(njs_vmcode_try_return_t,
+                                                    offset));
+    if (nxt_slow_path(patch == NULL)) {
+        return NXT_ERROR;
+    }
 
     return NXT_OK;
 }
@@ -2648,9 +2774,13 @@ njs_generate_try_statement(njs_vm_t *vm,
                                  catch_end_offset;
     nxt_int_t                    ret;
     njs_index_t                  exception_index, exit_index, catch_index;
+    nxt_str_t                    try_cont_label, try_exit_label,
+                                 catch_cont_label, catch_exit_label;
+    const nxt_str_t              *dest_label;
     njs_vmcode_catch_t           *catch;
     njs_vmcode_finally_t         *finally;
     njs_vmcode_try_end_t         *try_end, *catch_end;
+    njs_generator_patch_t        *patch;
     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;
@@ -2694,6 +2824,9 @@ njs_generate_try_statement(njs_vm_t *vm,
         return ret;
     }
 
+    try_exit_label = undef_label;
+    try_cont_label = undef_label;
+
     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;
@@ -2701,6 +2834,8 @@ njs_generate_try_statement(njs_vm_t *vm,
     try_end->code.retval = NJS_VMCODE_NO_RETVAL;
 
     if (try_block->exit != NULL) {
+        try_exit_label = try_block->exit->label;
+
         njs_generate_patch_block(vm, generator, try_block->exit);
 
         njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_break);
@@ -2717,6 +2852,8 @@ njs_generate_try_statement(njs_vm_t *vm,
     }
 
     if (try_block->continuation != NULL) {
+        try_cont_label = try_block->continuation->label;
+
         njs_generate_patch_block(vm, generator, try_block->continuation);
 
         njs_generate_code(generator, njs_vmcode_try_trampoline_t, try_continue);
@@ -2740,6 +2877,9 @@ njs_generate_try_statement(njs_vm_t *vm,
 
     node = node->right;
 
+    catch_exit_label = undef_label;
+    catch_cont_label = undef_label;
+
     if (node->token == NJS_TOKEN_CATCH) {
         /* A "try/catch" case. */
 
@@ -2780,21 +2920,31 @@ njs_generate_try_statement(njs_vm_t *vm,
                  * by njs_generate_continue_statement()
                  */
                 block = njs_generate_find_block(generator->block,
-                                                NJS_GENERATOR_LOOP);
-
-                njs_generate_make_continuation_patch(vm, generator, block,
+                                                NJS_GENERATOR_LOOP,
+                                                &try_cont_label);
+
+                patch = njs_generate_make_continuation_patch(vm, block,
+                                                             &try_cont_label,
                             njs_code_offset(generator, finally)
                              + offsetof(njs_vmcode_finally_t, continue_offset));
+                if (nxt_slow_path(patch == NULL)) {
+                    return NXT_ERROR;
+                }
             }
 
             if (try_block->exit != NULL) {
                 block = njs_generate_find_block(generator->block,
-                                                NJS_GENERATOR_ALL);
+                                                NJS_GENERATOR_ALL,
+                                                &try_exit_label);
 
                 if (block != NULL) {
-                    njs_generate_make_exit_patch(vm, generator, block,
+                    patch = njs_generate_make_exit_patch(vm, block,
+                                                         &try_exit_label,
                                 njs_code_offset(generator, finally)
                                 + offsetof(njs_vmcode_finally_t, break_offset));
+                    if (nxt_slow_path(patch == NULL)) {
+                        return NXT_ERROR;
+                    }
                 }
             }
         }
@@ -2838,6 +2988,8 @@ njs_generate_try_statement(njs_vm_t *vm,
             catch_end->code.retval = NJS_VMCODE_NO_RETVAL;
 
             if (catch_block->exit != NULL) {
+                catch_exit_label = catch_block->exit->label;
+
                 njs_generate_patch_block(vm, generator, catch_block->exit);
 
                 njs_generate_code(generator, njs_vmcode_try_trampoline_t,
@@ -2855,6 +3007,8 @@ njs_generate_try_statement(njs_vm_t *vm,
             }
 
             if (catch_block->continuation != NULL) {
+                catch_cont_label = catch_block->continuation->label;
+
                 njs_generate_patch_block(vm, generator,
                                          catch_block->continuation);
 
@@ -2923,27 +3077,57 @@ njs_generate_try_statement(njs_vm_t *vm,
         if (try_block->continuation != NULL
             || (catch_block && catch_block->continuation != NULL))
         {
+            dest_label = njs_generate_jump_destination(vm, generator->block,
+                                                       "try continue",
+                                                       NJS_GENERATOR_LOOP,
+                                                       &try_cont_label,
+                                                       &catch_cont_label);
+            if (nxt_slow_path(dest_label == NULL)) {
+                return NXT_ERROR;
+            }
+
             /*
              * 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));
+                                            NJS_GENERATOR_LOOP, dest_label);
+
+            patch = njs_generate_make_continuation_patch(vm, block, dest_label,
+                             njs_code_offset(generator, finally)
+                             + offsetof(njs_vmcode_finally_t, continue_offset));
+            if (nxt_slow_path(patch == NULL)) {
+                return NXT_ERROR;
+            }
         }
 
         if (try_block->exit != NULL
             || (catch_block != NULL && catch_block->exit != NULL))
         {
+            dest_label = njs_generate_jump_destination(vm, generator->block,
+                                                       "try break/return",
+                                                       NJS_GENERATOR_ALL
+                                                       | NJS_GENERATOR_TRY,
+                                                       &try_exit_label,
+                                                       &catch_exit_label);
+            if (nxt_slow_path(dest_label == NULL)) {
+                return NXT_ERROR;
+            }
+
+            /*
+             * block can be NULL for "return" instruction in
+             * outermost try-catch block.
+             */
             block = njs_generate_find_block(generator->block,
-                                            NJS_GENERATOR_ALL);
+                                            NJS_GENERATOR_ALL
+                                            | NJS_GENERATOR_TRY, dest_label);
             if (block != NULL) {
-                njs_generate_make_exit_patch(vm, generator, block,
+                patch = njs_generate_make_exit_patch(vm, block, dest_label,
                                 njs_code_offset(generator, finally)
                                 + offsetof(njs_vmcode_finally_t, break_offset));
+                if (nxt_slow_path(patch == NULL)) {
+                    return NXT_ERROR;
+                }
             }
         }
     }
diff -r 7d0b45c56edf -r 032827ab80dc njs/njs_lexer.c
--- a/njs/njs_lexer.c	Mon Feb 25 19:00:54 2019 +0300
+++ b/njs/njs_lexer.c	Mon Feb 25 19:00:55 2019 +0300
@@ -292,6 +292,32 @@ void
 njs_lexer_rollback(njs_lexer_t *lexer)
 {
     lexer->start = lexer->prev_start;
+    lexer->token = lexer->prev_token;
+}
+
+
+njs_token_t
+njs_lexer_peek_token(njs_lexer_t *lexer)
+{
+    u_char       *start;
+    njs_token_t  token;
+
+    start = lexer->start;
+
+    while (start < lexer->end) {
+        token = njs_tokens[*start++];
+
+        switch (token) {
+        case NJS_TOKEN_SPACE:
+        case NJS_TOKEN_LINE_END:
+            continue;
+
+        default:
+            return token;
+        }
+    }
+
+    return NJS_TOKEN_END;
 }
 
 
diff -r 7d0b45c56edf -r 032827ab80dc njs/njs_parser.c
--- a/njs/njs_parser.c	Mon Feb 25 19:00:54 2019 +0300
+++ b/njs/njs_parser.c	Mon Feb 25 19:00:55 2019 +0300
@@ -21,6 +21,8 @@ static njs_token_t njs_parser_block_stat
     njs_parser_t *parser);
 static njs_token_t njs_parser_block(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_labelled_statement(njs_vm_t *vm,
+    njs_parser_t *parser);
 static njs_token_t njs_parser_function_declaration(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_token_t njs_parser_function_lambda(njs_vm_t *vm,
@@ -42,10 +44,8 @@ static njs_token_t njs_parser_for_var_in
     njs_parser_t *parser, njs_parser_node_t *name);
 static njs_token_t njs_parser_for_in_statement(njs_vm_t *vm,
     njs_parser_t *parser, nxt_str_t *name, njs_token_t token);
-static njs_token_t njs_parser_continue_statement(njs_vm_t *vm,
-    njs_parser_t *parser);
-static njs_token_t njs_parser_break_statement(njs_vm_t *vm,
-    njs_parser_t *parser);
+static njs_token_t njs_parser_brk_statement(njs_vm_t *vm,
+    njs_parser_t *parser, njs_token_t token);
 static njs_token_t njs_parser_try_statement(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_try_block(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_throw_statement(njs_vm_t *vm,
@@ -216,6 +216,7 @@ njs_parser_scope_begin(njs_vm_t *vm, njs
     scope->argument_closures = 0;
 
     nxt_queue_init(&scope->nested);
+    nxt_lvlhsh_init(&scope->labels);
     nxt_lvlhsh_init(&scope->variables);
     nxt_lvlhsh_init(&scope->references);
 
@@ -354,19 +355,17 @@ njs_parser_statement(njs_vm_t *vm, njs_p
 
     default:
 
+        if (token == NJS_TOKEN_NAME
+            && njs_lexer_peek_token(parser->lexer) == NJS_TOKEN_COLON)
+        {
+            return njs_parser_labelled_statement(vm, parser);
+        }
+
         switch (token) {
         case NJS_TOKEN_VAR:
             token = njs_parser_var_statement(vm, parser);
             break;
 
-        case NJS_TOKEN_CONTINUE:
-            token = njs_parser_continue_statement(vm, parser);
-            break;
-
-        case NJS_TOKEN_BREAK:
-            token = njs_parser_break_statement(vm, parser);
-            break;
-
         case NJS_TOKEN_RETURN:
             token = njs_parser_return_statement(vm, parser);
             break;
@@ -375,6 +374,11 @@ njs_parser_statement(njs_vm_t *vm, njs_p
             token = njs_parser_throw_statement(vm, parser);
             break;
 
+        case NJS_TOKEN_CONTINUE:
+        case NJS_TOKEN_BREAK:
+            token = njs_parser_brk_statement(vm, parser, token);
+            break;
+
         default:
             token = njs_parser_expression(vm, parser, token);
             break;
@@ -506,6 +510,63 @@ njs_parser_variable_reference(njs_vm_t *
 
 
 static njs_token_t
+njs_parser_labelled_statement(njs_vm_t *vm, njs_parser_t *parser)
+{
+    uint32_t        hash;
+    njs_ret_t       ret;
+    nxt_str_t       name;
+    njs_token_t     token;
+    njs_variable_t  *label;
+
+    name = parser->lexer->text;
+    hash = parser->lexer->key_hash;
+
+    label = njs_label_find(vm, parser->scope, &name, hash);
+    if (nxt_slow_path(label != NULL)) {
+        njs_parser_syntax_error(vm, parser, "Label \"%V\" "
+                                "has already been declared", &name);
+        return NJS_TOKEN_ILLEGAL;
+    }
+
+    label = njs_label_add(vm, parser->scope, &name, hash);
+    if (nxt_slow_path(label == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    token = njs_parser_token(parser);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_COLON);
+    if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+        return token;
+    }
+
+    token = njs_parser_statement(vm, parser, token);
+
+    if (nxt_fast_path(token > NJS_TOKEN_ILLEGAL)) {
+
+        if (parser->node != NULL) {
+            /* The statement is not empty block or just semicolon. */
+
+            ret = njs_name_copy(vm, &parser->node->label, &name);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NJS_TOKEN_ERROR;
+            }
+
+            ret = njs_label_remove(vm, parser->scope, &name, hash);
+            if (nxt_slow_path(ret != NXT_OK)) {
+                return NJS_TOKEN_ERROR;
+            }
+        }
+    }
+
+    return token;
+}
+
+
+static njs_token_t
 njs_parser_function_declaration(njs_vm_t *vm, njs_parser_t *parser)
 {
     njs_ret_t          ret;
@@ -1483,12 +1544,15 @@ njs_parser_for_in_statement(njs_vm_t *vm
 
 
 static njs_token_t
-njs_parser_continue_statement(njs_vm_t *vm, njs_parser_t *parser)
+njs_parser_brk_statement(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token)
 {
-    njs_token_t        token;
+    uint32_t           hash;
+    njs_ret_t          ret;
+    nxt_str_t          name;
     njs_parser_node_t  *node;
 
-    node = njs_parser_node_new(vm, parser, NJS_TOKEN_CONTINUE);
+    node = njs_parser_node_new(vm, parser, token);
     if (nxt_slow_path(node == NULL)) {
         return NJS_TOKEN_ERROR;
     }
@@ -1503,37 +1567,21 @@ njs_parser_continue_statement(njs_vm_t *
     case NJS_TOKEN_LINE_END:
         return njs_parser_token(parser);
 
-    case NJS_TOKEN_SEMICOLON:
-    case NJS_TOKEN_CLOSE_BRACE:
-    case NJS_TOKEN_END:
-        return token;
-
-    default:
-        /* TODO: LABEL */
-        return NJS_TOKEN_ILLEGAL;
-    }
-}
-
-
-static njs_token_t
-njs_parser_break_statement(njs_vm_t *vm, njs_parser_t *parser)
-{
-    njs_token_t        token;
-    njs_parser_node_t  *node;
-
-    node = njs_parser_node_new(vm, parser, NJS_TOKEN_BREAK);
-    if (nxt_slow_path(node == NULL)) {
-        return NJS_TOKEN_ERROR;
-    }
-
-    node->token_line = parser->lexer->token_line;
-    parser->node = node;
-
-    token = njs_lexer_token(parser->lexer);
-
-    switch (token) {
-
-    case NJS_TOKEN_LINE_END:
+    case NJS_TOKEN_NAME:
+        name = parser->lexer->text;
+        hash = parser->lexer->key_hash;
+
+        if (njs_label_find(vm, parser->scope, &name, hash) == NULL) {
+            njs_parser_syntax_error(vm, parser, "Undefined label \"%V\"",
+                                    &name);
+            return NJS_TOKEN_ILLEGAL;
+        }
+
+        ret = njs_name_copy(vm, &parser->node->label, &parser->lexer->text);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NJS_TOKEN_ERROR;
+        }
+
         return njs_parser_token(parser);
 
     case NJS_TOKEN_SEMICOLON:
diff -r 7d0b45c56edf -r 032827ab80dc njs/njs_parser.h
--- a/njs/njs_parser.h	Mon Feb 25 19:00:54 2019 +0300
+++ b/njs/njs_parser.h	Mon Feb 25 19:00:55 2019 +0300
@@ -243,6 +243,7 @@ struct njs_parser_scope_s {
     nxt_queue_t                     nested;
 
     njs_parser_scope_t              *parent;
+    nxt_lvlhsh_t                    labels;
     nxt_lvlhsh_t                    variables;
     nxt_lvlhsh_t                    references;
 
@@ -274,6 +275,8 @@ struct njs_parser_node_s {
         njs_parser_node_t           *object;
     } u;
 
+    nxt_str_t                       label;
+
     njs_index_t                     index;
 
     /*
@@ -306,6 +309,7 @@ typedef struct {
 
 njs_token_t njs_lexer_token(njs_lexer_t *lexer);
 void njs_lexer_rollback(njs_lexer_t *lexer);
+njs_token_t njs_lexer_peek_token(njs_lexer_t *lexer);
 nxt_int_t njs_lexer_keywords_init(nxt_mp_t *mcp, nxt_lvlhsh_t *hash);
 njs_token_t njs_lexer_keyword(njs_lexer_t *lexer);
 
diff -r 7d0b45c56edf -r 032827ab80dc njs/njs_variable.c
--- a/njs/njs_variable.c	Mon Feb 25 19:00:54 2019 +0300
+++ b/njs/njs_variable.c	Mon Feb 25 19:00:55 2019 +0300
@@ -92,6 +92,74 @@ njs_variable_add(njs_vm_t *vm, njs_parse
 }
 
 
+njs_variable_t *
+njs_label_add(njs_vm_t *vm, njs_parser_scope_t *scope, nxt_str_t *name,
+    uint32_t hash)
+{
+    nxt_int_t           ret;
+    njs_variable_t      *label;
+    nxt_lvlhsh_query_t  lhq;
+
+    lhq.key_hash = hash;
+    lhq.key = *name;
+    lhq.proto = &njs_variables_hash_proto;
+
+    if (nxt_lvlhsh_find(&scope->labels, &lhq) == NXT_OK) {
+        return lhq.value;
+    }
+
+    label = njs_variable_alloc(vm, &lhq.key, NJS_VARIABLE_CONST);


More information about the nginx-devel mailing list