[njs] Syntax error messages are more verbose and have line number.

Igor Sysoev igor at sysoev.ru
Wed Jul 13 12:28:13 UTC 2016


details:   http://hg.nginx.org/njs/rev/07d2be75a7db
branches:  
changeset: 120:07d2be75a7db
user:      Igor Sysoev <igor at sysoev.ru>
date:      Wed Jul 13 13:56:12 2016 +0300
description:
Syntax error messages are more verbose and have line number.

diffstat:

 njs/njs_builtin.c             |    2 +
 njs/njs_generator.c           |   66 +++++++++++--
 njs/njs_lexer.c               |   23 ++++-
 njs/njs_nonrecursive_parser.c |    2 +-
 njs/njs_parser.c              |  187 ++++++++++++++++++++++++++++++++---------
 njs/njs_parser.h              |   16 +++
 njs/njs_parser_expression.c   |   35 ++++++-
 njs/njs_regexp.c              |   89 +++++++++++++++----
 njs/njs_regexp.h              |    3 +-
 njs/njs_string.c              |   60 +------------
 njs/njs_string.h              |    2 -
 njs/njs_vm.c                  |   24 +++++-
 njs/njs_vm.h                  |    7 +-
 njs/njscript.c                |    3 +-
 njs/test/njs_unit_test.c      |  100 +++++++++++++++++-----
 15 files changed, 442 insertions(+), 177 deletions(-)

diffs (truncated from 1568 to 1000 lines):

diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_builtin.c
--- a/njs/njs_builtin.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_builtin.c	Wed Jul 13 13:56:12 2016 +0300
@@ -19,6 +19,8 @@
 #include <njs_object.h>
 #include <njs_array.h>
 #include <njs_function.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
 #include <njs_regexp.h>
 #include <njs_date.h>
 #include <njs_math.h>
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_generator.c
--- a/njs/njs_generator.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_generator.c	Wed Jul 13 13:56:12 2016 +0300
@@ -20,6 +20,13 @@
 #include <njs_variable.h>
 #include <njs_parser.h>
 #include <string.h>
+#include <stdio.h>
+
+
+typedef enum {
+    NJS_GENERATOR_ERROR_ILLEGAL_CONTINUE = 0,
+    NJS_GENERATOR_ERROR_ILLEGAL_BREAK,
+} njs_generator_error_t;
 
 
 static nxt_int_t njs_generator(njs_vm_t *vm, njs_parser_t *parser,
@@ -117,6 +124,8 @@ static nxt_noinline nxt_int_t njs_genera
 static nxt_noinline nxt_int_t njs_generator_index_release(njs_vm_t *vm,
     njs_parser_t *parser, njs_index_t index);
 nxt_inline nxt_bool_t njs_generator_is_constant(njs_parser_node_t *node);
+static nxt_int_t njs_generator_error(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_generator_error_t err);
 
 
 static const nxt_str_t  no_label = { 0, NULL };
@@ -1067,19 +1076,20 @@ njs_generate_continue_statement(njs_vm_t
 {
     njs_vmcode_jump_t   *jump;
     njs_parser_patch_t  *patch;
-
-    if (parser->block == NULL) {
-        vm->exception = &njs_exception_syntax_error;
-        return NXT_ERROR;
+    njs_parser_block_t  *block;
+
+    for (block = parser->block; block != NULL; block = block->next) {
+        if (block->type == NJS_PARSER_LOOP) {
+            goto found;
+        }
     }
 
+    return njs_generator_error(vm, node, NJS_GENERATOR_ERROR_ILLEGAL_CONTINUE);
+
+found:
+
     /* TODO: LABEL */
 
-    if (parser->block->type != NJS_PARSER_LOOP) {
-        vm->exception = &njs_exception_syntax_error;
-        return NXT_ERROR;
-    }
-
     patch = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_patch_t));
 
     if (nxt_fast_path(patch != NULL)) {
@@ -1105,12 +1115,20 @@ njs_generate_break_statement(njs_vm_t *v
 {
     njs_vmcode_jump_t   *jump;
     njs_parser_patch_t  *patch;
-
-    if (parser->block == NULL) {
-        vm->exception = &njs_exception_syntax_error;
-        return NXT_ERROR;
+    njs_parser_block_t  *block;
+
+    for (block = parser->block; block != NULL; block = block->next) {
+        if (block->type == NJS_PARSER_LOOP
+            || block->type == NJS_PARSER_SWITCH)
+        {
+            goto found;
+        }
     }
 
+    return njs_generator_error(vm, node, NJS_GENERATOR_ERROR_ILLEGAL_BREAK);
+
+found:
+
     /* TODO: LABEL: loop and switch may have label, block must have label. */
 
     patch = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_patch_t));
@@ -2466,3 +2484,25 @@ njs_generator_is_constant(njs_parser_nod
     return (node->token >= NJS_TOKEN_FIRST_CONST
             && node->token <= NJS_TOKEN_LAST_CONST);
 }
+
+
+static nxt_int_t
+njs_generator_error(njs_vm_t *vm, njs_parser_node_t *node,
+    njs_generator_error_t err)
+{
+    uint32_t     size;
+    const char   *msg;
+    u_char       buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    static const char  *errors[] = {
+        "SyntaxError: Illegal continue statement in %u",
+        "SyntaxError: Illegal break statement in %u",
+    };
+
+    msg = errors[err];
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    msg, node->token_line);
+
+    return njs_vm_throw_exception(vm, buf, size);
+}
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_lexer.c
--- a/njs/njs_lexer.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_lexer.c	Wed Jul 13 13:56:12 2016 +0300
@@ -47,7 +47,7 @@ static const uint8_t  njs_tokens[256]  n
                 NJS_TOKEN_ILLEGAL,           NJS_TOKEN_ILLEGAL,
     /* \t */    NJS_TOKEN_ILLEGAL,           NJS_TOKEN_SPACE,
     /* \n */    NJS_TOKEN_LINE_END,          NJS_TOKEN_ILLEGAL,
-    /* \r */    NJS_TOKEN_ILLEGAL,           NJS_TOKEN_LINE_END,
+    /* \r */    NJS_TOKEN_ILLEGAL,           NJS_TOKEN_SPACE,
                 NJS_TOKEN_ILLEGAL,           NJS_TOKEN_ILLEGAL,
 
     /* 0x10 */  NJS_TOKEN_ILLEGAL,           NJS_TOKEN_ILLEGAL,
@@ -300,6 +300,8 @@ njs_lexer_next_token(njs_lexer_t *lexer)
     njs_token_t              token;
     const njs_lexer_multi_t  *multi;
 
+    lexer->text.data = lexer->start;
+
     while (lexer->start < lexer->end) {
         c = *lexer->start++;
 
@@ -308,6 +310,7 @@ njs_lexer_next_token(njs_lexer_t *lexer)
         switch (token) {
 
         case NJS_TOKEN_SPACE:
+            lexer->text.data = lexer->start;
             continue;
 
         case NJS_TOKEN_LETTER:
@@ -396,6 +399,10 @@ njs_lexer_next_token(njs_lexer_t *lexer)
             goto multi;
 
         case NJS_TOKEN_LINE_END:
+            lexer->line++;
+
+            /* Fall through. */
+
         case NJS_TOKEN_BITWISE_NOT:
         case NJS_TOKEN_OPEN_PARENTHESIS:
         case NJS_TOKEN_CLOSE_PARENTHESIS:
@@ -408,6 +415,7 @@ njs_lexer_next_token(njs_lexer_t *lexer)
         case NJS_TOKEN_COLON:
         case NJS_TOKEN_SEMICOLON:
         case NJS_TOKEN_CONDITIONAL:
+            lexer->text.len = lexer->start - lexer->text.data;
             return token;
 
         default:  /* NJS_TOKEN_ILLEGAL */
@@ -449,6 +457,7 @@ njs_lexer_word(njs_lexer_t *lexer, u_cha
         0x00, 0x00, 0x00, 0x00, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
     };
 
+    lexer->token_line = lexer->line;
     lexer->key_hash = nxt_djb_hash_add(NXT_DJB_HASH_INIT, c);
     lexer->text.data = lexer->start - 1;
 
@@ -489,7 +498,7 @@ njs_lexer_string(njs_lexer_t *lexer, u_c
 
         if (c == '\\') {
             if (p == lexer->end) {
-                return NJS_TOKEN_ILLEGAL;
+                break;
             }
 
             p++;
@@ -510,7 +519,10 @@ njs_lexer_string(njs_lexer_t *lexer, u_c
         }
     }
 
-    return NJS_TOKEN_ILLEGAL;
+    lexer->text.data--;
+    lexer->text.len = p - lexer->text.data;
+
+    return NJS_TOKEN_UNTERMINATED_STRING;
 }
 
 
@@ -590,7 +602,8 @@ njs_lexer_multi(njs_lexer_t *lexer, njs_
                 lexer->start++;
 
                 if (multi->count == 0) {
-                    return multi->token;
+                    token = multi->token;
+                    break;
                 }
 
                 return njs_lexer_multi(lexer, multi->token, multi->count,
@@ -603,6 +616,8 @@ njs_lexer_multi(njs_lexer_t *lexer, njs_
         } while (n != 0);
     }
 
+    lexer->text.len = lexer->start - lexer->text.data;
+
     return token;
 }
 
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_nonrecursive_parser.c
--- a/njs/njs_nonrecursive_parser.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_nonrecursive_parser.c	Wed Jul 13 13:56:12 2016 +0300
@@ -16,9 +16,9 @@
 #include <njs_vm.h>
 #include <njs_number.h>
 #include <njs_object.h>
-#include <njs_regexp.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
+#include <njs_regexp.h>
 #include <string.h>
 
 
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_parser.c
--- a/njs/njs_parser.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_parser.c	Wed Jul 13 13:56:12 2016 +0300
@@ -18,10 +18,11 @@
 #include <njs_string.h>
 #include <njs_object.h>
 #include <njs_function.h>
-#include <njs_regexp.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
+#include <njs_regexp.h>
 #include <string.h>
+#include <stdio.h>
 
 
 /*
@@ -52,18 +53,22 @@ static njs_token_t njs_parser_var_statem
 static njs_token_t njs_parser_if_statement(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_switch_statement(njs_vm_t *vm,
     njs_parser_t *parser);
+static njs_token_t njs_parser_duplicate_default_branch(njs_vm_t *vm,
+    njs_parser_t *parser);
 static njs_token_t njs_parser_while_statement(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_token_t njs_parser_do_while_statement(njs_vm_t *vm,
     njs_parser_t *parser);
 static njs_token_t njs_parser_for_statement(njs_vm_t *vm, njs_parser_t *parser);
 static njs_token_t njs_parser_for_in_statement(njs_vm_t *vm,
-    njs_parser_t *parser, njs_token_t token);
+    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_try_statement(njs_vm_t *vm, njs_parser_t *parser);
+static njs_token_t njs_parser_missing_catch_or_finally(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,
     njs_parser_t *parser);
@@ -78,7 +83,9 @@ static njs_token_t njs_parser_object(njs
 static njs_token_t njs_parser_array(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *obj);
 static njs_token_t njs_parser_escape_string_create(njs_vm_t *vm,
-    njs_value_t *value);
+    njs_parser_t *parser, njs_value_t *value);
+static njs_token_t njs_parser_unexpected_token(njs_vm_t *vm,
+    njs_parser_t *parser, njs_token_t token);
 
 
 njs_parser_node_t *
@@ -154,7 +161,7 @@ njs_parser_statement_chain(njs_vm_t *vm,
         }
 
     } else if (vm->exception == NULL) {
-        vm->exception = &njs_exception_syntax_error;
+        (void) njs_parser_unexpected_token(vm, parser, token);
     }
 
     return token;
@@ -269,14 +276,14 @@ njs_parser_block(njs_vm_t *vm, njs_parse
 
 
 nxt_inline njs_token_t
-njs_parser_match(njs_parser_t *parser, njs_token_t token,
+njs_parser_match(njs_vm_t *vm, njs_parser_t *parser, njs_token_t token,
     njs_token_t match)
 {
     if (nxt_fast_path(token == match)) {
         return njs_parser_token(parser);
     }
 
-    return NJS_TOKEN_ILLEGAL;
+    return njs_parser_unexpected_token(vm, parser, token);
 }
 
 
@@ -303,7 +310,7 @@ njs_parser_function_declaration(njs_vm_t
     }
 
     if (token != NJS_TOKEN_NAME) {
-        return NJS_TOKEN_ERROR;
+        return NJS_TOKEN_ILLEGAL;
     }
 
     var = njs_parser_variable(vm, parser, &level);
@@ -463,7 +470,7 @@ njs_parser_function_lambda(njs_vm_t *vm,
 
     parser = lambda->u.parser;
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -640,12 +647,9 @@ njs_parser_var_statement(njs_vm_t *vm, n
         }
 
         if (token != NJS_TOKEN_NAME) {
-            /* TODO: message. */
             return NJS_TOKEN_ILLEGAL;
         }
 
-        nxt_thread_log_debug("JS: %V", &parser->lexer->text);
-
         var = njs_parser_variable(vm, parser, &level);
         if (nxt_slow_path(var == NULL)) {
             return NJS_TOKEN_ERROR;
@@ -802,7 +806,7 @@ njs_parser_switch_statement(njs_vm_t *vm
     swtch->left = parser->node;
     last = &swtch->right;
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_BRACE);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_BRACE);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -841,8 +845,7 @@ njs_parser_switch_statement(njs_vm_t *vm
 
                } else {
                     if (dflt != NULL) {
-                        /* A duplicate "default" branch. */
-                        return NJS_TOKEN_ILLEGAL;
+                        return njs_parser_duplicate_default_branch(vm, parser);
                     }
 
                     branch = node;
@@ -858,7 +861,7 @@ njs_parser_switch_statement(njs_vm_t *vm
                 *last = branch;
                 last = &branch->left;
 
-                token = njs_parser_match(parser, token, NJS_TOKEN_COLON);
+                token = njs_parser_match(vm, parser, token, NJS_TOKEN_COLON);
                 if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
                     return token;
                 }
@@ -888,6 +891,22 @@ njs_parser_switch_statement(njs_vm_t *vm
 
 
 static njs_token_t
+njs_parser_duplicate_default_branch(njs_vm_t *vm, njs_parser_t *parser)
+{
+    uint32_t  size;
+    u_char    buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    "SyntaxError: More than one default clause "
+                    "in switch statement in %u", parser->lexer->line);
+
+    (void) njs_vm_throw_exception(vm, buf, size);
+
+    return NJS_TOKEN_ILLEGAL;
+}
+
+
+static njs_token_t
 njs_parser_while_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
     njs_token_t        token;
@@ -954,7 +973,7 @@ njs_parser_do_while_statement(njs_vm_t *
 
     node = njs_parser_node_alloc(vm);
     if (nxt_slow_path(node == NULL)) {
-        return NJS_TOKEN_ILLEGAL;
+        return NJS_TOKEN_ERROR;
     }
 
     node->token = NJS_TOKEN_DO;
@@ -970,6 +989,7 @@ njs_parser_do_while_statement(njs_vm_t *
 static njs_token_t
 njs_parser_for_statement(njs_vm_t *vm, njs_parser_t *parser)
 {
+    nxt_str_t          name;
     njs_token_t        token;
     njs_parser_node_t  *node, *init, *condition, *update, *cond, *body;
 
@@ -984,12 +1004,13 @@ njs_parser_for_statement(njs_vm_t *vm, n
         return token;
     }
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
 
     if (token != NJS_TOKEN_SEMICOLON) {
+        name = parser->lexer->text;
 
         token = njs_parser_expression(vm, parser, token);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
@@ -999,11 +1020,11 @@ njs_parser_for_statement(njs_vm_t *vm, n
         init = parser->node;
 
         if (init->token == NJS_TOKEN_IN) {
-            return njs_parser_for_in_statement(vm, parser, token);
+            return njs_parser_for_in_statement(vm, parser, &name, token);
         }
     }
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_SEMICOLON);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_SEMICOLON);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -1018,7 +1039,7 @@ njs_parser_for_statement(njs_vm_t *vm, n
         condition = parser->node;
     }
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_SEMICOLON);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_SEMICOLON);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -1033,7 +1054,7 @@ njs_parser_for_statement(njs_vm_t *vm, n
         update = parser->node;
     }
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -1076,14 +1097,23 @@ njs_parser_for_statement(njs_vm_t *vm, n
 
 
 static njs_token_t
-njs_parser_for_in_statement(njs_vm_t *vm, njs_parser_t *parser,
+njs_parser_for_in_statement(njs_vm_t *vm, njs_parser_t *parser, nxt_str_t *name,
     njs_token_t token)
 {
+    uint32_t           size;
     njs_parser_node_t  *node;
+    u_char             buf[NJS_EXCEPTION_BUF_LENGTH];
 
     node = parser->node->left;
 
     if (node->token != NJS_TOKEN_NAME) {
+        size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                        "ReferenceError: Invalid left-hand side \"%.*s\" "
+                        "in for-in statement in %u",
+                        (int) name->len, name->data, parser->lexer->line);
+
+        (void) njs_vm_throw_exception(vm, buf, size);
+
         return NJS_TOKEN_ILLEGAL;
     }
 
@@ -1097,7 +1127,7 @@ njs_parser_for_in_statement(njs_vm_t *vm
     node->token = NJS_TOKEN_FOR_IN;
     node->left = parser->node;
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -1127,6 +1157,7 @@ njs_parser_continue_statement(njs_vm_t *
     }
 
     node->token = NJS_TOKEN_CONTINUE;
+    node->token_line = parser->lexer->token_line;
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_jump_t);
 
@@ -1161,6 +1192,7 @@ njs_parser_break_statement(njs_vm_t *vm,
     }
 
     node->token = NJS_TOKEN_BREAK;
+    node->token_line = parser->lexer->token_line;
     parser->node = node;
     parser->code_size += sizeof(njs_vmcode_jump_t);
 
@@ -1214,7 +1246,7 @@ njs_parser_try_statement(njs_vm_t *vm, n
             return token;
         }
 
-        token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
+        token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
             return token;
         }
@@ -1223,8 +1255,6 @@ njs_parser_try_statement(njs_vm_t *vm, n
             return NJS_TOKEN_ILLEGAL;
         }
 
-        nxt_thread_log_debug("CATCH: %V", &parser->lexer->text);
-
         catch = njs_parser_node_alloc(vm);
         if (nxt_slow_path(catch == NULL)) {
             return NJS_TOKEN_ERROR;
@@ -1297,8 +1327,7 @@ njs_parser_try_statement(njs_vm_t *vm, n
     }
 
     if (try->right == NULL) {
-        /* TODO: message */
-        return NJS_TOKEN_ILLEGAL;
+        return njs_parser_missing_catch_or_finally(vm, parser);
     }
 
     parser->node = try;
@@ -1309,6 +1338,22 @@ njs_parser_try_statement(njs_vm_t *vm, n
 
 
 static njs_token_t
+njs_parser_missing_catch_or_finally(njs_vm_t *vm, njs_parser_t *parser)
+{
+    uint32_t  size;
+    u_char    buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    "SyntaxError: Missing catch or finally after try in %u",
+                    parser->lexer->line);
+
+    (void) njs_vm_throw_exception(vm, buf, size);
+
+    return NJS_TOKEN_ILLEGAL;
+}
+
+
+static njs_token_t
 njs_parser_try_block(njs_vm_t *vm, njs_parser_t *parser)
 {
     njs_token_t  token;
@@ -1366,7 +1411,7 @@ njs_parser_grouping_expression(njs_vm_t 
         return token;
     }
 
-    token = njs_parser_match(parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
+    token = njs_parser_match(vm, parser, token, NJS_TOKEN_OPEN_PARENTHESIS);
     if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
         return token;
     }
@@ -1376,7 +1421,7 @@ njs_parser_grouping_expression(njs_vm_t 
         return token;
     }
 
-    return njs_parser_match(parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
+    return njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
 }
 
 
@@ -1435,7 +1480,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
             return token;
         }
 
-        return njs_parser_match(parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
+        return njs_parser_match(vm, parser, token, NJS_TOKEN_CLOSE_PARENTHESIS);
     }
 
     if (token == NJS_TOKEN_FUNCTION) {
@@ -1534,9 +1579,9 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
         return token;
 
     case NJS_TOKEN_DIVISION:
-        ret = njs_regexp_literal(vm, parser, &node->u.value);
-        if (nxt_slow_path(ret != NXT_OK)) {
-            return NJS_TOKEN_ILLEGAL;
+        token = njs_regexp_literal(vm, parser, &node->u.value);
+        if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
+            return token;
         }
 
         nxt_thread_log_debug("REGEX: '%V'", &parser->lexer->text);
@@ -1561,13 +1606,17 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
 
         nxt_thread_log_debug("JS: '%V'", &parser->lexer->text);
 
-        ret = njs_parser_escape_string_create(vm, &node->u.value);
+        ret = njs_parser_escape_string_create(vm, parser, &node->u.value);
         if (nxt_slow_path(ret != NJS_TOKEN_STRING)) {
             return ret;
         }
 
         break;
 
+    case NJS_TOKEN_UNTERMINATED_STRING:
+        return njs_parser_error(vm, parser,
+                                NJS_PARSER_ERROR_UNTERMINATED_STRING);
+
     case NJS_TOKEN_NUMBER:
         nxt_thread_log_debug("JS: %f", parser->lexer->number);
 
@@ -1652,8 +1701,7 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
         return njs_parser_builtin_function(vm, parser, node);
 
     default:
-        vm->exception = &njs_exception_syntax_error;
-        return NJS_TOKEN_ILLEGAL;
+        return njs_parser_unexpected_token(vm, parser, token);
     }
 
     parser->node = node;
@@ -1769,7 +1817,7 @@ njs_parser_object(njs_vm_t *vm, njs_pars
             return token;
         }
 
-        token = njs_parser_match(parser, token, NJS_TOKEN_COLON);
+        token = njs_parser_match(vm, parser, token, NJS_TOKEN_COLON);
         if (nxt_slow_path(token <= NJS_TOKEN_ILLEGAL)) {
             return token;
         }
@@ -1944,7 +1992,8 @@ njs_parser_string_create(njs_vm_t *vm, n
 
 
 static njs_token_t
-njs_parser_escape_string_create(njs_vm_t *vm, njs_value_t *value)
+njs_parser_escape_string_create(njs_vm_t *vm, njs_parser_t *parser,
+    njs_value_t *value)
 {
     u_char   c, *p, *start, *dst, *src, *end, *hex_end;
     size_t   size, length, hex_length, skip;
@@ -1962,8 +2011,8 @@ njs_parser_escape_string_create(njs_vm_t
         size = 0;
         length = 0;
 
-        src = vm->parser->lexer->text.data;
-        end = src + vm->parser->lexer->text.len;
+        src = parser->lexer->text.data;
+        end = src + parser->lexer->text.len;
 
         while (src < end) {
             c = *src++;
@@ -1995,7 +2044,8 @@ njs_parser_escape_string_create(njs_vm_t
                         }
 
                         if (hex_length == 0 || hex_length > 6) {
-                            return NJS_TOKEN_ILLEGAL;
+                            return njs_parser_error(vm, parser,
+                                                    NJS_PARSER_ERROR_UNICODE);
                         }
 
                         skip = 1;
@@ -2070,12 +2120,12 @@ njs_parser_escape_string_create(njs_vm_t
             hex_end = src + hex_length;
 
             if (hex_end > end) {
-                return NJS_TOKEN_ILLEGAL;
+                return njs_parser_error(vm, parser, NJS_PARSER_ERROR_UNICODE);
             }
 
             u = njs_number_radix_parse(src, hex_end, 16, 1);
             if (nxt_slow_path(u < 0)) {
-                return NJS_TOKEN_ILLEGAL;
+                return njs_parser_error(vm, parser, NJS_PARSER_ERROR_UNICODE);
             }
 
             src = hex_end + skip;
@@ -2154,3 +2204,52 @@ njs_parser_has_side_effect(njs_parser_no
 
     return side_effect;
 }
+
+
+static njs_token_t
+njs_parser_unexpected_token(njs_vm_t *vm, njs_parser_t *parser,
+    njs_token_t token)
+{
+    uint32_t     size;
+    u_char       buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    if (token != NJS_TOKEN_END) {
+        return njs_parser_error(vm, parser, NJS_PARSER_ERROR_UNEXPECTED_TOKEN);
+    }
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    "SyntaxError: Unexpected end of input in %u",
+                    parser->lexer->line);
+
+    (void) njs_vm_throw_exception(vm, buf, size);
+
+    return NJS_TOKEN_ILLEGAL;
+}
+
+
+njs_token_t
+njs_parser_error(njs_vm_t *vm, njs_parser_t *parser, njs_parser_error_t err)
+{
+    uint32_t     size;
+    njs_lexer_t  *lexer;
+    const char   *msg;
+    u_char       buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    static const char  *errors[] = {
+        "SyntaxError: Unexpected token \"%.*s\" in %u",
+        "SyntaxError: Unterminated string \"%.*s\" in %u",
+        "SyntaxError: Invalid Unicode code point \"%.*s\" in %u",
+        "SyntaxError: Unterminated RegExp \"%.*s\" in %u",
+        "SyntaxError: Invalid RegExp flags \"%.*s\" in %u",
+    };
+
+    msg = errors[err];
+    lexer = parser->lexer;
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    msg, (int) lexer->text.len, lexer->text.data, lexer->line);
+
+    (void) njs_vm_throw_exception(vm, buf, size);
+
+    return NJS_TOKEN_ILLEGAL;
+}
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_parser.h
--- a/njs/njs_parser.h	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_parser.h	Wed Jul 13 13:56:12 2016 +0300
@@ -117,6 +117,7 @@ typedef enum {
 #define NJS_TOKEN_LAST_CONST      NJS_TOKEN_STRING
 
     NJS_TOKEN_ESCAPE_STRING,
+    NJS_TOKEN_UNTERMINATED_STRING,
     NJS_TOKEN_NAME,
 
     NJS_TOKEN_OBJECT,
@@ -191,6 +192,9 @@ typedef struct {
     uint8_t                         property;      /* 1 bit */
     uint32_t                        key_hash;
 
+    uint32_t                        token_line;
+    uint32_t                        line;
+
     nxt_str_t                       text;
     double                          number;
 
@@ -224,6 +228,7 @@ struct njs_parser_node_s {
     njs_lvalue_state_t              lvalue:2;   /* 2 bits */
     uint8_t                         ctor:1;     /* 1 bit  */
     uint8_t                         temporary;  /* 1 bit  */
+    uint32_t                        token_line;
 
     union {
         uint32_t                    length;
@@ -313,6 +318,15 @@ struct njs_parser_s {
 };
 
 
+typedef enum {
+    NJS_PARSER_ERROR_UNEXPECTED_TOKEN = 0,
+    NJS_PARSER_ERROR_UNTERMINATED_STRING,
+    NJS_PARSER_ERROR_UNICODE,
+    NJS_PARSER_ERROR_UNTERMINATED_REGEXP,
+    NJS_PARSER_ERROR_REGEXP_FLAGS,
+} njs_parser_error_t;
+
+
 njs_token_t njs_lexer_token(njs_lexer_t *lexer);
 nxt_int_t njs_lexer_keywords_init(nxt_mem_cache_pool_t *mcp,
     nxt_lvlhsh_t *hash);
@@ -339,6 +353,8 @@ njs_token_t njs_parser_token(njs_parser_
 nxt_int_t njs_parser_string_create(njs_vm_t *vm, njs_value_t *value);
 njs_index_t njs_parser_index(njs_parser_t *parser, uint32_t scope);
 nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
+njs_token_t njs_parser_error(njs_vm_t *vm, njs_parser_t *parser,
+    njs_parser_error_t err);
 nxt_int_t njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_parser_expression.c
--- a/njs/njs_parser_expression.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_parser_expression.c	Wed Jul 13 13:56:12 2016 +0300
@@ -9,6 +9,7 @@
 #include <nxt_clang.h>
 #include <nxt_alignment.h>
 #include <nxt_stub.h>
+#include <nxt_utf8.h>
 #include <nxt_array.h>
 #include <nxt_lvlhsh.h>
 #include <nxt_random.h>
@@ -16,11 +17,13 @@
 #include <njscript.h>
 #include <njs_vm.h>
 #include <njs_number.h>
+#include <njs_string.h>
 #include <njs_object.h>
 #include <njs_function.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
 #include <string.h>
+#include <stdio.h>
 
 
 typedef struct {
@@ -71,6 +74,8 @@ static njs_token_t njs_parser_property_e
     njs_parser_t *parser, njs_token_t token);
 static njs_token_t njs_parser_property_brackets(njs_vm_t *vm,
     njs_parser_t *parser, njs_token_t token);
+static njs_token_t njs_parser_invalid_lvalue(njs_vm_t *vm,
+    njs_parser_t *parser, const char* operation);
 
 
 static const njs_parser_expression_t
@@ -288,8 +293,7 @@ njs_parser_var_expression(njs_vm_t *vm, 
         node = parser->node;
 
         if (node->lvalue == NJS_LVALUE_NONE) {
-            nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
-            return NJS_TOKEN_ILLEGAL;
+            return njs_parser_invalid_lvalue(vm, parser, "assignment");
         }
 
         pending = NULL;
@@ -434,8 +438,7 @@ njs_parser_assignment_expression(njs_vm_
         node = parser->node;
 
         if (node->lvalue == NJS_LVALUE_NONE) {
-            nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
-            return NJS_TOKEN_ILLEGAL;
+            return njs_parser_invalid_lvalue(vm, parser, "assignment");
         }
 
         pending = NULL;
@@ -807,8 +810,7 @@ njs_parser_inc_dec_expression(njs_vm_t *
     }
 
     if (parser->node->lvalue == NJS_LVALUE_NONE) {
-        nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
-        return NJS_TOKEN_ILLEGAL;
+        return njs_parser_invalid_lvalue(vm, parser, "prefix operation");
     }
 
     node = njs_parser_node_alloc(vm);
@@ -860,8 +862,7 @@ njs_parser_post_inc_dec_expression(njs_v
     }
 
     if (parser->node->lvalue == NJS_LVALUE_NONE) {
-        nxt_thread_log_error(NXT_LOG_ALERT, "lvalue required");
-        return NJS_TOKEN_ILLEGAL;
+        return njs_parser_invalid_lvalue(vm, parser, "postfix operation");
     }
 
     node = njs_parser_node_alloc(vm);
@@ -1142,3 +1143,21 @@ njs_parser_arguments(njs_vm_t *vm, njs_p
 
     return token;
 }
+
+
+static njs_token_t
+njs_parser_invalid_lvalue(njs_vm_t *vm, njs_parser_t *parser,
+    const char *operation)
+{
+    uint32_t  size;
+    u_char    buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    "ReferenceError: Invalid left-hand side in %s in %u",
+                    operation, parser->lexer->line);
+
+    (void) njs_vm_throw_exception(vm, buf, size);
+
+    return NJS_TOKEN_ILLEGAL;
+
+}
diff -r 5e7e498eb90d -r 07d2be75a7db njs/njs_regexp.c
--- a/njs/njs_regexp.c	Mon Jul 11 15:24:29 2016 +0300
+++ b/njs/njs_regexp.c	Wed Jul 13 13:56:12 2016 +0300
@@ -24,11 +24,12 @@
 #include <njs_object_hash.h>
 #include <njs_array.h>
 #include <njs_function.h>
+#include <njs_variable.h>
+#include <njs_parser.h>
 #include <njs_regexp.h>
 #include <njs_regexp_pattern.h>
-#include <njs_variable.h>
-#include <njs_parser.h>
 #include <string.h>
+#include <stdio.h>
 
 
 static void *njs_regexp_malloc(size_t size, void *memory_data);
@@ -133,7 +134,7 @@ njs_regexp_constructor(njs_vm_t *vm, njs
 }
 
 
-nxt_int_t
+njs_token_t
 njs_regexp_literal(njs_vm_t *vm, njs_parser_t *parser, njs_value_t *value)
 {
     u_char                *p;
@@ -154,11 +155,15 @@ njs_regexp_literal(njs_vm_t *vm, njs_par
             lexer->text.data = lexer->start;
             lexer->text.len = p - lexer->text.data;
             p++;
+            lexer->start = p;
 
             flags = njs_regexp_flags(&p, lexer->end, 0);
 
             if (nxt_slow_path(flags < 0)) {
-                return NXT_ERROR;
+                lexer->text.data = lexer->start;
+                lexer->text.len = p - lexer->text.data;
+                return njs_parser_error(vm, parser,
+                                        NJS_PARSER_ERROR_REGEXP_FLAGS);
             }
 
             lexer->start = p;
@@ -166,16 +171,19 @@ njs_regexp_literal(njs_vm_t *vm, njs_par
             pattern = njs_regexp_pattern_create(vm, lexer->text.data,
                                                 lexer->text.len, flags);
             if (nxt_slow_path(pattern == NULL)) {
-                return NXT_ERROR;
+                return NJS_TOKEN_ILLEGAL;
             }
 
             value->data.u.data = pattern;
 
-            return NXT_OK;
+            return NJS_TOKEN_REGEXP;
         }
     }
 
-    return NXT_ERROR;
+    lexer->text.data = lexer->start - 1;
+    lexer->text.len = p - lexer->text.data;
+
+    return njs_parser_error(vm, parser, NJS_PARSER_ERROR_UNTERMINATED_REGEXP);
 }
 
 
@@ -203,16 +211,28 @@ njs_regexp_flags(u_char **start, u_char 
             flag = NJS_REGEXP_MULTILINE;
             break;
 
-        default:
-            if (bound) {
-                return NJS_REGEXP_INVALID_FLAG;
+        case ';':
+        case ' ':
+        case '\t':
+        case '\r':
+        case '\n':
+        case ',':
+        case ')':
+        case ']':
+        case '}':
+        case '.':
+            if (!bound) {
+                goto done;
             }
 
-            goto done;
+            /* Fall through. */
+
+        default:
+            goto invalid;
         }
 
         if (nxt_slow_path((flags & flag) != 0)) {
-            return NJS_REGEXP_INVALID_FLAG;
+            goto invalid;
         }
 
         flags |= flag;
@@ -223,6 +243,12 @@ done:
     *start = p;
 
     return flags;
+
+invalid:
+
+    *start = p + 1;
+
+    return NJS_REGEXP_INVALID_FLAG;
 }
 
 
@@ -298,10 +324,7 @@ njs_regexp_pattern_create(njs_vm_t *vm, 
     if (nxt_fast_path(ret >= 0)) {
 
         if (nxt_slow_path((u_int) ret != pattern->ncaptures)) {
-            nxt_thread_log_error(NXT_LOG_ERR, "numbers of captures in byte "
-                           "and UTF-8 versions of RegExp \"%s\" vary: %d vs %d",



More information about the nginx-devel mailing list