]> git.kaiwu.me - njs.git/commitdiff
Syntax error messages are more verbose and have line number.
authorIgor Sysoev <igor@sysoev.ru>
Wed, 13 Jul 2016 10:56:12 +0000 (13:56 +0300)
committerIgor Sysoev <igor@sysoev.ru>
Wed, 13 Jul 2016 10:56:12 +0000 (13:56 +0300)
15 files changed:
njs/njs_builtin.c
njs/njs_generator.c
njs/njs_lexer.c
njs/njs_nonrecursive_parser.c
njs/njs_parser.c
njs/njs_parser.h
njs/njs_parser_expression.c
njs/njs_regexp.c
njs/njs_regexp.h
njs/njs_string.c
njs/njs_string.h
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.c
njs/test/njs_unit_test.c

index c53a0047a53d43421990f2c2de5e7e5bdc61edaf..a40a7f86deaa5c60c46a89971870c3444309236c 100644 (file)
@@ -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>
index eb18e45b618d6b1c978cab6553d62ea21b9dc41b..a06dd512d75a79be3e7a223f33be51981561b509 100644 (file)
 #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_generator_node_index_release(njs_vm_t *vm,
 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,18 +1076,19 @@ njs_generate_continue_statement(njs_vm_t *vm, njs_parser_t *parser,
 {
     njs_vmcode_jump_t   *jump;
     njs_parser_patch_t  *patch;
+    njs_parser_block_t  *block;
 
-    if (parser->block == NULL) {
-        vm->exception = &njs_exception_syntax_error;
-        return NXT_ERROR;
+    for (block = parser->block; block != NULL; block = block->next) {
+        if (block->type == NJS_PARSER_LOOP) {
+            goto found;
+        }
     }
 
-    /* TODO: LABEL */
+    return njs_generator_error(vm, node, NJS_GENERATOR_ERROR_ILLEGAL_CONTINUE);
 
-    if (parser->block->type != NJS_PARSER_LOOP) {
-        vm->exception = &njs_exception_syntax_error;
-        return NXT_ERROR;
-    }
+found:
+
+    /* TODO: LABEL */
 
     patch = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_parser_patch_t));
 
@@ -1105,12 +1115,20 @@ njs_generate_break_statement(njs_vm_t *vm, njs_parser_t *parser,
 {
     njs_vmcode_jump_t   *jump;
     njs_parser_patch_t  *patch;
+    njs_parser_block_t  *block;
 
-    if (parser->block == NULL) {
-        vm->exception = &njs_exception_syntax_error;
-        return NXT_ERROR;
+    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_node_t *node)
     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);
+}
index 211086e5f71f09d9f7f9eca4651bb7324442a219..94a0c914b1a937f3971089a100f13a2c5d0b9ec3 100644 (file)
@@ -47,7 +47,7 @@ static const uint8_t  njs_tokens[256]  nxt_aligned(64) = {
                 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_char c)
         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_char quote)
 
         if (c == '\\') {
             if (p == lexer->end) {
-                return NJS_TOKEN_ILLEGAL;
+                break;
             }
 
             p++;
@@ -510,7 +519,10 @@ njs_lexer_string(njs_lexer_t *lexer, u_char quote)
         }
     }
 
-    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_token_t token, nxt_uint_t n,
                 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_token_t token, nxt_uint_t n,
         } while (n != 0);
     }
 
+    lexer->text.len = lexer->start - lexer->text.data;
+
     return token;
 }
 
index b92c0ba447cc995bcbcc9b839ecd0dde17a65039..7e91fa3d7c78c70cb490e3b5e00068601ea99ff2 100644 (file)
@@ -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>
 
 
index cadfc3ceb07610025cf551083dc4ce41a71f2818..dce0ef5dbc937859c9024924d30d0255a3cb869e 100644 (file)
 #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_statement(njs_vm_t *vm, njs_parser_t *parser);
 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_vm_t *vm, njs_parser_t *parser,
 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, njs_parser_t *parser,
         }
 
     } 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_parser_t *parser)
 
 
 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 *vm, njs_parser_t *parser)
     }
 
     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, njs_function_lambda_t *lambda,
 
     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, njs_parser_t *parser)
         }
 
         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, njs_parser_t *parser)
     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, njs_parser_t *parser)
 
                } 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, njs_parser_t *parser)
                 *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;
                 }
@@ -887,6 +890,22 @@ 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)
+{
+    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)
 {
@@ -954,7 +973,7 @@ njs_parser_do_while_statement(njs_vm_t *vm, njs_parser_t *parser)
 
     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 *vm, njs_parser_t *parser)
 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, njs_parser_t *parser)
         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, njs_parser_t *parser)
         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, njs_parser_t *parser)
         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, njs_parser_t *parser)
         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, njs_parser_t *parser)
 
 
 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, njs_parser_t *parser,
     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 *vm, njs_parser_t *parser)
     }
 
     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, njs_parser_t *parser)
     }
 
     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, njs_parser_t *parser)
             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, njs_parser_t *parser)
             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, njs_parser_t *parser)
     }
 
     if (try->right == NULL) {
-        /* TODO: message */
-        return NJS_TOKEN_ILLEGAL;
+        return njs_parser_missing_catch_or_finally(vm, parser);
     }
 
     parser->node = try;
@@ -1308,6 +1337,22 @@ 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)
+{
+    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)
 {
@@ -1366,7 +1411,7 @@ njs_parser_grouping_expression(njs_vm_t *vm, njs_parser_t *parser)
         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 *vm, njs_parser_t *parser)
         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_parser_t *parser, njs_token_t token)
             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_parser_t *parser, njs_token_t token)
         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_parser_t *parser, njs_token_t token)
 
         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_parser_t *parser, njs_token_t token)
         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_parser_t *parser, njs_parser_node_t *obj)
             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, njs_value_t *value)
 
 
 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 *vm, njs_value_t *value)
         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 *vm, njs_value_t *value)
                         }
 
                         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 *vm, njs_value_t *value)
             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_node_t *node)
 
     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;
+}
index f8d4ca981ecd60ab6266d3064472c91dec60c63a..a40b4ae4478847e5f406a40182c71ee1bf43a12e 100644 (file)
@@ -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_t *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);
 
index c3536c2d67118bd635d4d998f8156fe18ae3755c..c6e02ecd2c1b4ea547d509d3c313c06276971b40 100644 (file)
@@ -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>
 #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_expression(njs_vm_t *vm,
     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, njs_parser_t *parser, njs_token_t token)
         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_t *vm, njs_parser_t *parser,
         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 *vm, njs_parser_t *parser,
     }
 
     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_vm_t *vm, njs_parser_t *parser,
     }
 
     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_parser_t *parser,
 
     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;
+
+}
index 614524375de3cb89703ae1cb194d3b92913f71e3..5c9f19d07693b313ae769966edf651953620b6ee 100644 (file)
 #include <njs_object_hash.h>
 #include <njs_array.h>
 #include <njs_function.h>
-#include <njs_regexp.h>
-#include <njs_regexp_pattern.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
+#include <njs_regexp.h>
+#include <njs_regexp_pattern.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_value_t *args, nxt_uint_t nargs,
 }
 
 
-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_parser_t *parser, njs_value_t *value)
             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_parser_t *parser, njs_value_t *value)
             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 *end, nxt_bool_t bound)
             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, u_char *start, size_t length,
     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",
-                           &pattern->source[1], pattern->ncaptures, ret);
-
+            vm->exception = &njs_exception_internal_error;
             nxt_mem_cache_free(vm->mem_cache_pool, pattern);
             return NULL;
         }
@@ -321,7 +344,9 @@ static int
 njs_regexp_pattern_compile(njs_vm_t *vm, nxt_regex_t *regex, u_char *source,
     int options)
 {
+    uint32_t   size;
     nxt_int_t  ret;
+    u_char     buf[NJS_EXCEPTION_BUF_LENGTH];
 
     /* Zero length means a zero-terminated string. */
     ret = nxt_regex_compile(regex, source, 0, options, vm->regex_context);
@@ -330,7 +355,17 @@ njs_regexp_pattern_compile(njs_vm_t *vm, nxt_regex_t *regex, u_char *source,
         return regex->ncaptures;
     }
 
-    return njs_string_exception(vm, NJS_SYNTAX_ERROR, vm->regex_context->error);
+    if (vm->parser != NULL) {
+        size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                        "SyntaxError: %s in %u",
+                        vm->regex_context->error, vm->parser->lexer->line);
+
+    } else {
+        size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                        "SyntaxError: %s", vm->regex_context->error);
+    }
+
+    return njs_vm_throw_exception(vm, buf, size);
 }
 
 
@@ -487,8 +522,7 @@ njs_regexp_prototype_test(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
             retval = &njs_value_true;
 
         } else if (ret != NGX_REGEX_NOMATCH) {
-            return njs_string_exception(vm, NJS_INTERNAL_ERROR,
-                                        vm->regex_context->error);
+            return njs_regexp_match_error(vm);
         }
     }
 
@@ -564,8 +598,7 @@ njs_regexp_prototype_exec(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
         if (nxt_slow_path(ret != NGX_REGEX_NOMATCH)) {
             nxt_regex_match_data_free(match_data, vm->regex_context);
 
-            return njs_string_exception(vm, NJS_INTERNAL_ERROR,
-                                        vm->regex_context->error);
+            return njs_regexp_match_error(vm);
         }
     }
 
@@ -699,10 +732,24 @@ njs_regexp_string_create(njs_vm_t *vm, njs_value_t *value, u_char *start,
     }
 
     vm->exception = &njs_exception_internal_error;
+
     return NXT_ERROR;
 }
 
 
+njs_ret_t
+njs_regexp_match_error(njs_vm_t *vm)
+{
+    uint32_t  size;
+    u_char    buf[NJS_EXCEPTION_BUF_LENGTH];
+
+    size = snprintf((char *) buf, NJS_EXCEPTION_BUF_LENGTH,
+                    "RegExpError: %s", vm->regex_context->error);
+
+    return njs_vm_throw_exception(vm, buf, size);
+}
+
+
 static const njs_object_prop_t  njs_regexp_constructor_properties[] =
 {
     /* RegExp.name == "RegExp". */
index 2743b679aa6d0e7071f8507b08b43c0effdfca6b..dbafdc978affa090237de28a8a5e5e78bd771e78 100644 (file)
@@ -35,13 +35,14 @@ struct njs_regexp_s {
 njs_ret_t njs_regexp_init(njs_vm_t *vm);
 njs_ret_t njs_regexp_constructor(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
-nxt_int_t njs_regexp_literal(njs_vm_t *vm, njs_parser_t *parser,
+njs_token_t njs_regexp_literal(njs_vm_t *vm, njs_parser_t *parser,
     njs_value_t *value);
 njs_regexp_pattern_t *njs_regexp_pattern_create(njs_vm_t *vm,
     u_char *string, size_t length, njs_regexp_flags_t flags);
 njs_regexp_t *njs_regexp_alloc(njs_vm_t *vm, njs_regexp_pattern_t *pattern);
 njs_ret_t njs_regexp_prototype_exec(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused);
+njs_ret_t njs_regexp_match_error(njs_vm_t *vm);
 
 
 extern const njs_object_init_t  njs_regexp_constructor_init;
index 7fcd26d8cf549cbef6b4a72a2c751bc5896a6d6f..098706e69870275428f228c270f67d4481572d19 100644 (file)
 #include <njs_object_hash.h>
 #include <njs_array.h>
 #include <njs_function.h>
-#include <njs_regexp.h>
-#include <njs_regexp_pattern.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
+#include <njs_regexp.h>
+#include <njs_regexp_pattern.h>
 #include <string.h>
+#include <stdio.h>
 
 
 static nxt_noinline void njs_string_slice_prop(njs_string_prop_t *string,
@@ -1460,8 +1461,7 @@ njs_string_prototype_search(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
                 index = njs_string_index(&string, captures[0]);
 
             } else if (ret != NGX_REGEX_NOMATCH) {
-                return njs_string_exception(vm, NJS_INTERNAL_ERROR,
-                                            vm->regex_context->error);
+                return njs_regexp_match_error(vm);
             }
         }
     }
@@ -1616,8 +1616,7 @@ njs_string_prototype_match(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
                 break;
 
             } else {
-                return njs_string_exception(vm, NJS_INTERNAL_ERROR,
-                                            vm->regex_context->error);
+                return njs_regexp_match_error(vm);
             }
 
         } while (string.size > 0);
@@ -1769,8 +1768,7 @@ njs_string_prototype_split(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
                     next = (u_char *) end + 1;
 
                 } else {
-                    return njs_string_exception(vm, NJS_INTERNAL_ERROR,
-                                                vm->regex_context->error);
+                    return njs_regexp_match_error(vm);
                 }
 
                 /* Empty split regexp. */
@@ -2248,49 +2246,3 @@ njs_value_index(njs_vm_t *vm, njs_parser_t *parser, const njs_value_t *src)
 
     return (njs_index_t) value;
 }
-
-
-nxt_int_t
-njs_string_exception(njs_vm_t *vm, njs_exception_error_t exception, u_char *msg)
-{
-    u_char       *p, *start;
-    uint32_t     msg_length, size, length;
-    nxt_str_t    *error;
-    njs_value_t  *value;
-
-    static nxt_str_t  errors[] = {
-        nxt_string("SyntaxError: "),
-        nxt_string("InternalError: "),
-    };
-
-    value = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t));
-    if (nxt_slow_path(value == NULL)) {
-        return NXT_ERROR;
-    }
-
-    error = &errors[exception];
-
-    msg_length = (msg != NULL) ? strlen((char *) msg) : 0;
-    length = nxt_utf8_length(msg, msg_length);
-
-    size = error->len + msg_length;
-    length += error->len;
-
-    start = njs_string_alloc(vm, value, size, length);
-
-    if (nxt_fast_path(start != NULL)) {
-        memcpy(start, error->data, error->len);
-        p = start + error->len;
-
-        memcpy(p, msg, msg_length);
-
-        if (size != length && length >= NJS_STRING_MAP_OFFSET) {
-            njs_string_offset_map_init(start, size);
-        }
-    }
-
-    vm->exception = value;
-
-    return NXT_ERROR;
-}
-
index 79428f5940d9a9151136cda87eb99f4a09744cdd..e651c7056328a2c1fa493a207b8fe2b7817210bd 100644 (file)
@@ -107,8 +107,6 @@ double njs_string_to_number(njs_value_t *value, nxt_bool_t exact);
 
 njs_index_t njs_value_index(njs_vm_t *vm, njs_parser_t *parser,
     const njs_value_t *src);
-nxt_int_t njs_string_exception(njs_vm_t *vm, njs_exception_error_t exception,
-    u_char *msg);
 
 extern const njs_object_init_t  njs_string_constructor_init;
 extern const njs_object_init_t  njs_string_prototype_init;
index c2729036949041273f41df359f89a612a6494afb..caca69e71895c0495112f77e715c4b21c3701048 100644 (file)
@@ -8,6 +8,7 @@
 #include <nxt_clang.h>
 #include <nxt_alignment.h>
 #include <nxt_stub.h>
+#include <nxt_utf8.h>
 #include <nxt_djb_hash.h>
 #include <nxt_array.h>
 #include <nxt_lvlhsh.h>
 #include <njs_object_hash.h>
 #include <njs_array.h>
 #include <njs_function.h>
-#include <njs_regexp.h>
 #include <njs_extern.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
+#include <njs_regexp.h>
 #include <string.h>
 
 
@@ -3316,6 +3317,27 @@ njs_value_string_copy(njs_vm_t *vm, nxt_str_t *retval, njs_value_t *value,
 }
 
 
+njs_ret_t
+njs_vm_throw_exception(njs_vm_t *vm, u_char *buf, uint32_t size)
+{
+    uint32_t     length;
+    njs_value_t  *value;
+
+    value = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t));
+    if (nxt_slow_path(value == NULL)) {
+        return NJS_TOKEN_ERROR;
+    }
+
+    vm->exception = value;
+
+    length = nxt_utf8_length(buf, size);
+
+    (void) njs_string_new(vm, value, buf, size, length);
+
+    return NXT_ERROR;
+}
+
+
 void
 njs_debug(njs_index_t index, njs_value_t *value)
 {
index a21a0e9f9ad90664c8b5abcf4bb34f06aa7559a9..461b4caeecb1f95bda67a6471bbc3b37c7062f30 100644 (file)
@@ -827,10 +827,7 @@ struct njs_vm_shared_s {
 };
 
 
-typedef enum {
-    NJS_SYNTAX_ERROR = 0,
-    NJS_INTERNAL_ERROR,
-} njs_exception_error_t;
+#define NJS_EXCEPTION_BUF_LENGTH  2048
 
 
 nxt_int_t njs_vmcode_interpreter(njs_vm_t *vm);
@@ -972,6 +969,8 @@ njs_ret_t njs_value_to_ext_string(njs_vm_t *vm, nxt_str_t *dst,
     const njs_value_t *src);
 void njs_number_set(njs_value_t *value, double num);
 
+njs_ret_t njs_vm_throw_exception(njs_vm_t *vm, u_char *buf, uint32_t size);
+
 nxt_int_t njs_builtin_objects_create(njs_vm_t *vm);
 nxt_int_t njs_builtin_objects_clone(njs_vm_t *vm);
 
index 37144ed201c822da1bc3a880774d9a043138c551..9938e4ee831f2b0536fd4a1b847a8f8156e7c5c5 100644 (file)
@@ -18,9 +18,9 @@
 #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>
 
 
@@ -192,6 +192,7 @@ njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end,
     parser->lexer = lexer;
     lexer->start = *start;
     lexer->end = end;
+    lexer->line = 1;
     lexer->keywords_hash = vm->shared->keywords_hash;
 
     parser->code_size = sizeof(njs_vmcode_stop_t);
index 0f493fbae55f8029a9e1a17f7b17ae52e878106f..7acf67c8fd64599f6d868f8130b140feded28b4f 100644 (file)
@@ -47,7 +47,10 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("undefined") },
 
     { nxt_string("var; a"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \";\" in 1") },
+
+    { nxt_string("var + a"),
+      nxt_string("SyntaxError: Unexpected token \"+\" in 1") },
 
     { nxt_string("var \n a \n = 1; a"),
       nxt_string("1") },
@@ -1373,7 +1376,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("1") },
 
     { nxt_string("a = 0; a \n ++"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected end of input in 2") },
 
     { nxt_string("a = 1 ? 2 \n : 3"),
       nxt_string("2") },
@@ -1400,7 +1403,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("NaN undefined") },
 
     { nxt_string("var a += 1"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \"+=\" in 1") },
 
     { nxt_string("var a = a + 1"),
       nxt_string("undefined") },
@@ -1473,13 +1476,26 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var a = 3; if (true) if (false); else; a = 2; a"),
       nxt_string("2") },
 
+    /* do while. */
+
+    { nxt_string("do { break } if (false)"),
+      nxt_string("SyntaxError: Unexpected token \"if\" in 1") },
+
+    /* for in. */
+
+    { nxt_string("for (null in undefined);"),
+      nxt_string("ReferenceError: Invalid left-hand side \"null\" in for-in statement in 1") },
+
     /* switch. */
 
     { nxt_string("switch"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected end of input in 1") },
 
     { nxt_string("switch (1);"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \";\" in 1") },
+
+    { nxt_string("switch (1) { do { } while (1) }"),
+      nxt_string("SyntaxError: Unexpected token \"do\" in 1") },
 
     { nxt_string("switch (1) {}"),
       nxt_string("undefined") },
@@ -1493,6 +1509,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("switch (1) {default:;}"),
       nxt_string("undefined") },
 
+    { nxt_string("switch (1) {default:; default:}"),
+      nxt_string("SyntaxError: More than one default clause in switch statement in 1") },
+
     { nxt_string("switch (1) {case 0:;}"),
       nxt_string("undefined") },
 
@@ -1546,10 +1565,10 @@ static njs_unit_test_t  njs_test[] =
     /* continue. */
 
     { nxt_string("continue"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Illegal continue statement in 1") },
 
     { nxt_string("do continue while (false)"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \"while\" in 1") },
 
     { nxt_string("do continue; while (false)"),
       nxt_string("undefined") },
@@ -1604,10 +1623,10 @@ static njs_unit_test_t  njs_test[] =
     /* break. */
 
     { nxt_string("break"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Illegal break statement in 1") },
 
     { nxt_string("do break while (true)"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \"while\" in 1") },
 
     { nxt_string("do break; while (true)"),
       nxt_string("undefined") },
@@ -1750,8 +1769,17 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("void 0"),
       nxt_string("undefined") },
 
+    { nxt_string("null = 1"),
+      nxt_string("ReferenceError: Invalid left-hand side in assignment in 1") },
+
     { nxt_string("undefined = 1"),
-      nxt_string("SyntaxError") },
+      nxt_string("ReferenceError: Invalid left-hand side in assignment in 1") },
+
+    { nxt_string("null++"),
+      nxt_string("ReferenceError: Invalid left-hand side in postfix operation in 1") },
+
+    { nxt_string("++null"),
+      nxt_string("ReferenceError: Invalid left-hand side in prefix operation in 1") },
 
     { nxt_string("var a; b = a; a = 1; a +' '+ b"),
       nxt_string("1 undefined") },
@@ -1858,6 +1886,9 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("var y = 5; x = { a:y }; x.a"),
       nxt_string("5") },
 
+    { nxt_string("x = { a: 1; b: 2 }"),
+      nxt_string("SyntaxError: Unexpected token \";\" in 1") },
+
     { nxt_string("x = { a: 1, b: x.a }"),
       nxt_string("ReferenceError") },
 
@@ -1970,10 +2001,12 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("3 * [5,7]"),
       nxt_string("NaN") },
 
-
     { nxt_string("a = [ 1, 2, 3 ]; a[0] + a[1] + a[2]"),
       nxt_string("6") },
 
+    { nxt_string("a = [ 1, 2; 3 ]; a[0] + a[1] + a[2]"),
+      nxt_string("SyntaxError: Unexpected token \";\" in 1") },
+
     { nxt_string("a = [ 1, 2, 3 ]; a[0] +' '+ a[1] +' '+ a[2] +' '+ a[3]"),
       nxt_string("1 2 3 undefined") },
 
@@ -2285,32 +2318,38 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("'a\\\r\nb'"),
       nxt_string("ab") },
 
+    { nxt_string("'abcde"),
+      nxt_string("SyntaxError: Unterminated string \"'abcde\" in 1") },
+
+    { nxt_string("'\\"),
+      nxt_string("SyntaxError: Unterminated string \"'\\\" in 1") },
+
     { nxt_string("'\\'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unterminated string \"'\\'\" in 1") },
 
     { nxt_string("'\\u03B1'"),
       nxt_string("α") },
 
     { nxt_string("'\\u'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u\" in 1") },
 
     { nxt_string("'\\u03B'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u03B\" in 1") },
 
     { nxt_string("'\\u{61}\\u{3B1}\\u{20AC}'"),
       nxt_string("aα€") },
 
     { nxt_string("'\\u'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u\" in 1") },
 
     { nxt_string("'\\u{'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u{\" in 1") },
 
     { nxt_string("'\\u{}'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u{}\" in 1") },
 
     { nxt_string("'\\u{1234567}'"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Invalid Unicode code point \"\\u{1234567}\" in 1") },
 
     { nxt_string("'\\x61'"),
       nxt_string("a") },
@@ -2563,7 +2602,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("NaN") },
 
     { nxt_string("a = 'abcdef'; a.3"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \"3\" in 1") },
 
     { nxt_string("'abcdef'[3]"),
       nxt_string("d") },
@@ -2989,6 +3028,9 @@ static njs_unit_test_t  njs_test[] =
 
     /* Functions. */
 
+    { nxt_string("function () { } f()"),
+      nxt_string("SyntaxError: Unexpected token \"(\" in 1") },
+
     { nxt_string("function f() { } f()"),
       nxt_string("undefined") },
 
@@ -3163,7 +3205,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("20") },
 
     { nxt_string("var f = function b(a) { a *= 2; return a } = 5"),
-      nxt_string("SyntaxError") },
+      nxt_string("ReferenceError: Invalid left-hand side in assignment in 1") },
 
     { nxt_string("function a() { return { x:2} }; var b = a(); b.x"),
       nxt_string("2") },
@@ -3211,7 +3253,7 @@ static njs_unit_test_t  njs_test[] =
       nxt_string("3") },
 
     { nxt_string("var a = 0, function(a) { return a + 1 }(2); a"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \"function\" in 1") },
 
     { nxt_string("var a = (0, function(a) { return a + 1 }(2)); a"),
       nxt_string("3") },
@@ -3432,8 +3474,14 @@ static njs_unit_test_t  njs_test[] =
 
     /* RegExp. */
 
+    { nxt_string("/./x"),
+      nxt_string("SyntaxError: Invalid RegExp flags \"x\" in 1") },
+
+    { nxt_string("/"),
+      nxt_string("SyntaxError: Unterminated RegExp \"/\" in 1") },
+
     { nxt_string("/(/.test('')"),
-      nxt_string("SyntaxError: pcre_compile(\"(\") failed: missing )") },
+      nxt_string("SyntaxError: pcre_compile(\"(\") failed: missing ) in 1") },
 
     { nxt_string("/^$/.test('')"),
       nxt_string("true") },
@@ -3568,6 +3616,12 @@ static njs_unit_test_t  njs_test[] =
     { nxt_string("try { throw null } catch (e) { throw e }"),
       nxt_string("") },
 
+    { nxt_string("try { throw null } catch (null) { throw e }"),
+      nxt_string("SyntaxError: Unexpected token \"null\" in 1") },
+
+    { nxt_string("try {}"),
+      nxt_string("SyntaxError: Missing catch or finally after try in 1") },
+
     { nxt_string("var a = 0; try { a = 5 }"
                  "catch (e) { a = 9 } finally { a++ } a"),
       nxt_string("6") },
@@ -4548,7 +4602,7 @@ static njs_unit_test_t  njs_test[] =
     /* es5id: 8.2_A2 */
 
     { nxt_string("var null;"),
-      nxt_string("SyntaxError") },
+      nxt_string("SyntaxError: Unexpected token \"null\" in 1") },
 
     /* es5id: 8.2_A3 */