[njs] Fixed exception handling.

Dmitry Volyntsev xeioex at nginx.com
Fri Nov 17 16:06:04 UTC 2017


details:   http://hg.nginx.org/njs/rev/a83775113025
branches:  
changeset: 422:a83775113025
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Nov 17 18:55:07 2017 +0300
description:
Fixed exception handling.

njs_vm_exception() is removed and combined with njs_vm_retval().
vm->exception is removed either, exceptions are now stored in
vm->retval.  It simplifies the client logic, because previously
njs_vm_exception() had to be called if njs_vm_retval() fails.
Additonally, stack traces are now appended to the retval if an exception
happens.

diffstat:

 nginx/ngx_http_js_module.c      |    6 +-
 nginx/ngx_stream_js_module.c    |    8 +-
 njs/njs.c                       |   56 +++-----------------
 njs/njs_error.c                 |   36 ++++---------
 njs/njs_function.c              |    2 +
 njs/njs_generator.c             |    6 +-
 njs/njs_parser.c                |  108 ++++++++++++++++++++++++---------------
 njs/njs_parser.h                |    4 +
 njs/njs_parser_expression.c     |   25 ++++----
 njs/njs_regexp.c                |   34 +++++-------
 njs/njs_variable.c              |   16 ++--
 njs/njs_vm.c                    |   19 -------
 njs/njs_vm.h                    |    5 -
 njs/njscript.c                  |   91 ++++++++++++++++++++++++++++----
 njs/njscript.h                  |    1 -
 njs/test/njs_benchmark.c        |    9 +--
 njs/test/njs_expect_test.exp    |   16 +++++
 njs/test/njs_interactive_test.c |   99 +++++++++++------------------------
 njs/test/njs_unit_test.c        |   46 +++++++++++-----
 19 files changed, 293 insertions(+), 294 deletions(-)

diffs (truncated from 1269 to 1000 lines):

diff -r 84a95e20f93a -r a83775113025 nginx/ngx_http_js_module.c
--- a/nginx/ngx_http_js_module.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/nginx/ngx_http_js_module.c	Fri Nov 17 18:55:07 2017 +0300
@@ -445,7 +445,7 @@ ngx_http_js_handler(ngx_http_request_t *
     }
 
     if (njs_vm_call(ctx->vm, func, ctx->args, 2) != NJS_OK) {
-        njs_vm_exception(ctx->vm, &exception);
+        njs_vm_retval(ctx->vm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "js exception: %*s", exception.length, exception.start);
@@ -496,7 +496,7 @@ ngx_http_js_variable(ngx_http_request_t 
     }
 
     if (njs_vm_call(ctx->vm, func, ctx->args, 2) != NJS_OK) {
-        njs_vm_exception(ctx->vm, &exception);
+        njs_vm_retval(ctx->vm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "js exception: %*s", exception.length, exception.start);
@@ -1333,7 +1333,7 @@ ngx_http_js_include(ngx_conf_t *cf, ngx_
     rc = njs_vm_compile(jlcf->vm, &start, end);
 
     if (rc != NJS_OK) {
-        njs_vm_exception(jlcf->vm, &text);
+        njs_vm_retval(jlcf->vm, &text);
 
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "%*s, included",
diff -r 84a95e20f93a -r a83775113025 nginx/ngx_stream_js_module.c
--- a/nginx/ngx_stream_js_module.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/nginx/ngx_stream_js_module.c	Fri Nov 17 18:55:07 2017 +0300
@@ -408,7 +408,7 @@ ngx_stream_js_phase_handler(ngx_stream_s
     }
 
     if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
-        njs_vm_exception(ctx->vm, &exception);
+        njs_vm_retval(ctx->vm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s",
                       exception.length, exception.start);
@@ -495,7 +495,7 @@ ngx_stream_js_body_filter(ngx_stream_ses
         ctx->buf = in->buf;
 
         if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
-            njs_vm_exception(ctx->vm, &exception);
+            njs_vm_retval(ctx->vm, &exception);
 
             ngx_log_error(NGX_LOG_ERR, c->log, 0, "js exception: %*s",
                           exception.length, exception.start);
@@ -593,7 +593,7 @@ ngx_stream_js_variable(ngx_stream_sessio
     }
 
     if (njs_vm_call(ctx->vm, func, ctx->arg, 1) != NJS_OK) {
-        njs_vm_exception(ctx->vm, &exception);
+        njs_vm_retval(ctx->vm, &exception);
 
         ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
                       "js exception: %*s", exception.length, exception.start);
@@ -1043,7 +1043,7 @@ ngx_stream_js_include(ngx_conf_t *cf, ng
     rc = njs_vm_compile(jscf->vm, &start, end);
 
     if (rc != NJS_OK) {
-        njs_vm_exception(jscf->vm, &text);
+        njs_vm_retval(jscf->vm, &text);
 
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "%*s, included",
diff -r 84a95e20f93a -r a83775113025 njs/njs.c
--- a/njs/njs.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs.c	Fri Nov 17 18:55:07 2017 +0300
@@ -64,7 +64,6 @@ static nxt_int_t njs_interactive_shell(n
 static nxt_int_t njs_process_file(njs_opts_t *opts, njs_vm_opt_t *vm_options);
 static nxt_int_t njs_process_script(njs_vm_t *vm, njs_opts_t *opts,
     const nxt_str_t *script, nxt_str_t *out);
-static void njs_print_backtrace(nxt_array_t *backtrace);
 static nxt_int_t njs_editline_init(njs_vm_t *vm);
 static char **njs_completion_handler(const char *text, int start, int end);
 static char *njs_completion_generator(const char *text, int state);
@@ -240,10 +239,9 @@ njs_externals_init(njs_opts_t *opts, njs
 static nxt_int_t
 njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options)
 {
-    njs_vm_t     *vm;
-    nxt_int_t    ret;
-    nxt_str_t    line, out;
-    nxt_array_t  *backtrace;
+    njs_vm_t   *vm;
+    nxt_int_t  ret;
+    nxt_str_t  line, out;
 
     vm = njs_vm_create(vm_options);
     if (vm == NULL) {
@@ -282,11 +280,6 @@ njs_interactive_shell(njs_opts_t *opts, 
 
         printf("%.*s\n", (int) out.length, out.start);
 
-        backtrace = njs_vm_backtrace(vm);
-        if (backtrace != NULL) {
-            njs_print_backtrace(backtrace);
-        }
-
         /* editline allocs a new buffer every time. */
         free(line.start);
     }
@@ -307,7 +300,6 @@ njs_process_file(njs_opts_t *opts, njs_v
     nxt_int_t    ret;
     nxt_str_t    out, script;
     struct stat  sb;
-    nxt_array_t  *backtrace;
 
     file = opts->file;
 
@@ -399,11 +391,6 @@ njs_process_file(njs_opts_t *opts, njs_v
 
     if (!opts->disassemble) {
         printf("%.*s\n", (int) out.length, out.start);
-
-        backtrace = njs_vm_backtrace(vm);
-        if (backtrace != NULL) {
-            njs_print_backtrace(backtrace);
-        }
     }
 
     ret = NXT_OK;
@@ -442,44 +429,19 @@ njs_process_script(njs_vm_t *vm, njs_opt
         }
 
         ret = njs_vm_run(vm);
-
-        if (ret == NXT_OK) {
-            if (njs_vm_retval(vm, out) != NXT_OK) {
-                return NXT_ERROR;
-            }
+        if (ret == NXT_AGAIN) {
+            return ret;
+        }
+    }
 
-        } else {
-            njs_vm_exception(vm, out);
-        }
-
-    } else {
-        njs_vm_exception(vm, out);
+    if (njs_vm_retval(vm, out) != NXT_OK) {
+        return NXT_ERROR;
     }
 
     return NXT_OK;
 }
 
 
-static void
-njs_print_backtrace(nxt_array_t *backtrace)
-{
-    nxt_uint_t             i;
-    njs_backtrace_entry_t  *be;
-
-    be = backtrace->start;
-
-    for (i = 0; i < backtrace->items; i++) {
-        if (be[i].line != 0) {
-            printf("at %.*s (:%d)\n", (int) be[i].name.length, be[i].name.start,
-                   be[i].line);
-
-        } else {
-            printf("at %.*s\n", (int) be[i].name.length, be[i].name.start);
-        }
-    }
-}
-
-
 static nxt_int_t
 njs_editline_init(njs_vm_t *vm)
 {
diff -r 84a95e20f93a -r a83775113025 njs/njs_error.c
--- a/njs/njs_error.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_error.c	Fri Nov 17 18:55:07 2017 +0300
@@ -37,7 +37,7 @@ njs_exception_error_create(njs_vm_t *vm,
     size_t        size;
     va_list       args;
     nxt_int_t     ret;
-    njs_value_t   string, *value;
+    njs_value_t   string;
     njs_object_t  *error;
 
     static char  buf[256];
@@ -61,16 +61,9 @@ njs_exception_error_create(njs_vm_t *vm,
         goto memory_error;
     }
 
-    value = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t));
-    if (nxt_slow_path(value == NULL)) {
-        goto memory_error;
-    }
-
-    value->data.u.object = error;
-    value->type = type;
-    value->data.truth = 1;
-
-    vm->exception = value;
+    vm->retval.data.u.object = error;
+    vm->retval.type = type;
+    vm->retval.data.truth = 1;
 
     return;
 
@@ -495,9 +488,8 @@ const njs_object_init_t  njs_uri_error_c
 
 
 static void
-njs_init_memory_error(njs_vm_t *vm)
+njs_set_memory_error(njs_vm_t *vm)
 {
-    njs_value_t             *value;
     njs_object_t            *object;
     njs_object_prototype_t  *prototypes;
 
@@ -516,21 +508,17 @@ njs_init_memory_error(njs_vm_t *vm)
      */
     object->extensible = 0;
 
-    value = &vm->memory_error;
-
-    value->data.type = NJS_OBJECT_INTERNAL_ERROR;
-    value->data.truth = 1;
-    value->data.u.number = NAN;
-    value->data.u.object = object;
+    vm->retval.data.type = NJS_OBJECT_INTERNAL_ERROR;
+    vm->retval.data.truth = 1;
+    vm->retval.data.u.number = NAN;
+    vm->retval.data.u.object = object;
 }
 
 
 void
 njs_exception_memory_error(njs_vm_t *vm)
 {
-    njs_init_memory_error(vm);
-
-    vm->exception = &vm->memory_error;
+    njs_set_memory_error(vm);
 }
 
 
@@ -538,9 +526,7 @@ njs_ret_t
 njs_memory_error_constructor(njs_vm_t *vm, njs_value_t *args,
     nxt_uint_t nargs, njs_index_t unused)
 {
-    njs_init_memory_error(vm);
-
-    vm->retval = vm->memory_error;
+    njs_set_memory_error(vm);
 
     return NXT_OK;
 }
diff -r 84a95e20f93a -r a83775113025 njs/njs_function.c
--- a/njs/njs_function.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_function.c	Fri Nov 17 18:55:07 2017 +0300
@@ -701,6 +701,8 @@ njs_ret_t
 njs_eval_function(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
     njs_index_t unused)
 {
+    njs_exception_internal_error(vm, "Not implemented", NULL);
+
     return NXT_ERROR;
 }
 
diff -r 84a95e20f93a -r a83775113025 njs/njs_generator.c
--- a/njs/njs_generator.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_generator.c	Fri Nov 17 18:55:07 2017 +0300
@@ -1150,8 +1150,7 @@ njs_generate_continue_statement(njs_vm_t
         }
     }
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "SyntaxError: Illegal continue statement");
+    njs_parser_syntax_error(vm, parser, "Illegal continue statement", NULL);
 
     return NXT_ERROR;
 
@@ -1194,8 +1193,7 @@ njs_generate_break_statement(njs_vm_t *v
         }
     }
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "SyntaxError: Illegal break statement");
+    njs_parser_syntax_error(vm, parser, "Illegal break statement", NULL);
 
     return NXT_ERROR;
 
diff -r 84a95e20f93a -r a83775113025 njs/njs_parser.c
--- a/njs/njs_parser.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_parser.c	Fri Nov 17 18:55:07 2017 +0300
@@ -21,6 +21,7 @@
 #include <njs_string.h>
 #include <njs_object.h>
 #include <njs_function.h>
+#include <njs_error.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
 #include <njs_regexp.h>
@@ -196,9 +197,9 @@ njs_parser_scope_begin(njs_vm_t *vm, njs
                     break;
                 }
 
-                nxt_alert(&vm->trace, NXT_LEVEL_ERROR, "SyntaxError: "
-                          "The maximum function nesting level is \"%d\"",
-                          NJS_MAX_NESTING);
+                njs_parser_syntax_error(vm, parser,
+                                        "The maximum function nesting "
+                                        "level is \"%d\"", NJS_MAX_NESTING);
 
                 return NXT_ERROR;
             }
@@ -310,7 +311,7 @@ njs_parser_statement_chain(njs_vm_t *vm,
             }
         }
 
-    } else if (vm->exception == NULL) {
+    } else if (!njs_is_error(&vm->retval)) {
         (void) njs_parser_unexpected_token(vm, parser, token);
     }
 
@@ -770,8 +771,8 @@ njs_parser_return_statement(njs_vm_t *vm
          scope = scope->parent)
     {
         if (scope->type == NJS_SCOPE_GLOBAL) {
-            nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                      "SyntaxError: Illegal return statement");
+            njs_parser_syntax_error(vm, parser, "Illegal return statement",
+                                    NULL);
 
             return NXT_ERROR;
         }
@@ -1053,9 +1054,9 @@ njs_parser_switch_statement(njs_vm_t *vm
 
                } else {
                     if (dflt != NULL) {
-                        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                                  "SyntaxError: More than one default clause "
-                                  "in switch statement");
+                        njs_parser_syntax_error(vm, parser,
+                                                "More than one default clause "
+                                                "in switch statement", NULL);
 
                         return NJS_TOKEN_ILLEGAL;
                     }
@@ -1461,9 +1462,9 @@ njs_parser_for_in_statement(njs_vm_t *vm
     node = parser->node->left;
 
     if (node->token != NJS_TOKEN_NAME) {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR, "ReferenceError: Invalid "
-                  "left-hand side \"%.*s\" in for-in statement",
-                  (int) name->length, name->start);
+        njs_parser_ref_error(vm, parser, "Invalid left-hand side \"%.*s\" "
+                             "in for-in statement", (int) name->length,
+                             name->start);
 
         return NJS_TOKEN_ILLEGAL;
     }
@@ -1686,8 +1687,8 @@ njs_parser_try_statement(njs_vm_t *vm, n
     }
 
     if (try->right == NULL) {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                  "SyntaxError: Missing catch or finally after try");
+        njs_parser_syntax_error(vm, parser, "Missing catch or "
+                                "finally after try", NULL);
 
         return NJS_TOKEN_ILLEGAL;
     }
@@ -1936,9 +1937,9 @@ njs_parser_terminal(njs_vm_t *vm, njs_pa
         break;
 
     case NJS_TOKEN_UNTERMINATED_STRING:
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                  "SyntaxError: Unterminated string \"%.*s\"",
-                  (int) parser->lexer->text.length, parser->lexer->text.start);
+        njs_parser_syntax_error(vm, parser, "Unterminated string \"%.*s\"",
+                                (int) parser->lexer->text.length,
+                                parser->lexer->text.start);
 
         return NJS_TOKEN_ILLEGAL;
 
@@ -2540,9 +2541,9 @@ njs_parser_escape_string_create(njs_vm_t
 
 invalid:
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "SyntaxError: Invalid Unicode code point \"%.*s\"",
-              (int) parser->lexer->text.length, parser->lexer->text.start);
+    njs_parser_syntax_error(vm, parser, "Invalid Unicode code point \"%.*s\"",
+                            (int) parser->lexer->text.length,
+                            parser->lexer->text.start);
 
     return NJS_TOKEN_ILLEGAL;
 }
@@ -2584,13 +2585,12 @@ njs_parser_unexpected_token(njs_vm_t *vm
     njs_token_t token)
 {
     if (token != NJS_TOKEN_END) {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                  "SyntaxError: Unexpected token \"%.*s\"",
-                  (int) parser->lexer->text.length, parser->lexer->text.start);
+        njs_parser_syntax_error(vm, parser, "Unexpected token \"%.*s\"",
+                                (int) parser->lexer->text.length,
+                                parser->lexer->text.start);
 
     } else {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                  "SyntaxError: Unexpected end of input");
+        njs_parser_syntax_error(vm, parser, "Unexpected end of input", NULL);
     }
 
     return NJS_TOKEN_ILLEGAL;
@@ -2601,18 +2601,13 @@ u_char *
 njs_parser_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
     u_char *start)
 {
-    int       n;
     u_char    *p;
-    ssize_t   size;
+    size_t    size;
     njs_vm_t  *vm;
 
-    p = start;
-
-    if (td->level == NXT_LEVEL_CRIT) {
-        size = sizeof("InternalError: ") - 1;
-        memcpy(p, "InternalError: ", size);
-        p = start + size;
-    }
+    size = sizeof("InternalError: ") - 1;
+    memcpy(start, "InternalError: ", size);
+    p = start + size;
 
     vm = trace->data;
 
@@ -2620,16 +2615,43 @@ njs_parser_trace_handler(nxt_trace_t *tr
     p = trace->handler(trace, td, p);
 
     if (vm->parser != NULL) {
-        size = td->end - start;
-
-        n = snprintf((char *) p, size, " in %u", vm->parser->lexer->line);
-
-        if (n < size) {
-            p += n;
-        }
+        njs_exception_internal_error(vm, "%s in %u", start,
+                                     vm->parser->lexer->line);
+    } else {
+        njs_exception_internal_error(vm, "%s", start);
     }
 
-    njs_vm_throw_exception(vm, start, p - start);
-
     return p;
 }
+
+
+void
+njs_parser_syntax_error(njs_vm_t *vm, njs_parser_t *parser, const char* fmt,
+    ...)
+{
+    va_list  args;
+
+    static char  buf[256];
+
+    va_start(args, fmt);
+    (void) vsnprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+
+    njs_exception_syntax_error(vm, "%s in %u", buf, parser->lexer->line);
+}
+
+
+void
+njs_parser_ref_error(njs_vm_t *vm, njs_parser_t *parser, const char* fmt,
+    ...)
+{
+    va_list  args;
+
+    static char  buf[256];
+
+    va_start(args, fmt);
+    (void) vsnprintf(buf, sizeof(buf), fmt, args);
+    va_end(args);
+
+    njs_exception_ref_error(vm, "%s in %u", buf, parser->lexer->line);
+}
diff -r 84a95e20f93a -r a83775113025 njs/njs_parser.h
--- a/njs/njs_parser.h	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_parser.h	Fri Nov 17 18:55:07 2017 +0300
@@ -381,6 +381,10 @@ njs_index_t njs_variable_index(njs_vm_t 
 nxt_bool_t njs_parser_has_side_effect(njs_parser_node_t *node);
 u_char *njs_parser_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
     u_char *start);
+void njs_parser_syntax_error(njs_vm_t *vm, njs_parser_t *parser,
+	const char* fmt, ...);
+void njs_parser_ref_error(njs_vm_t *vm, njs_parser_t *parser, const char* fmt,
+	...);
 nxt_int_t njs_generate_scope(njs_vm_t *vm, njs_parser_t *parser,
     njs_parser_node_t *node);
 
diff -r 84a95e20f93a -r a83775113025 njs/njs_parser_expression.c
--- a/njs/njs_parser_expression.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_parser_expression.c	Fri Nov 17 18:55:07 2017 +0300
@@ -294,8 +294,8 @@ njs_parser_var_expression(njs_vm_t *vm, 
         }
 
         if (!njs_parser_is_lvalue(parser->node)) {
-            nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                      "ReferenceError: Invalid left-hand side in assignment");
+            njs_parser_ref_error(vm, parser,
+                                 "Invalid left-hand side in assignment", NULL);
             return NJS_TOKEN_ILLEGAL;
         }
 
@@ -432,8 +432,8 @@ njs_parser_assignment_expression(njs_vm_
         }
 
         if (!njs_parser_is_lvalue(parser->node)) {
-            nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                      "ReferenceError: Invalid left-hand side in assignment");
+            njs_parser_ref_error(vm, parser, "Invalid left-hand side "
+                                 "in assignment", NULL);
             return NJS_TOKEN_ILLEGAL;
         }
 
@@ -756,9 +756,8 @@ njs_parser_unary_expression(njs_vm_t *vm
     }
 
     if (next == NJS_TOKEN_EXPONENTIATION) {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                  "SyntaxError: Either left-hand side or entire exponentiation "
-                  "must be parenthesized");
+        njs_parser_syntax_error(vm, parser, "Either left-hand side or entire "
+                                "exponentiation must be parenthesized");
 
         return NJS_TOKEN_ILLEGAL;
     }
@@ -796,8 +795,8 @@ njs_parser_unary_expression(njs_vm_t *vm
 
         case NJS_TOKEN_NAME:
         case NJS_TOKEN_UNDEFINED:
-            nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                      "SyntaxError: Delete of an unqualified identifier");
+        njs_parser_syntax_error(vm, parser,
+                                "Delete of an unqualified identifier", NULL);
 
             return NJS_TOKEN_ILLEGAL;
 
@@ -856,8 +855,8 @@ njs_parser_inc_dec_expression(njs_vm_t *
     }
 
     if (!njs_parser_is_lvalue(parser->node)) {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                  "ReferenceError: Invalid left-hand side in prefix operation");
+        njs_parser_ref_error(vm, parser, "Invalid left-hand side "
+                             "in prefix operation", NULL);
         return NJS_TOKEN_ILLEGAL;
     }
 
@@ -911,8 +910,8 @@ njs_parser_post_inc_dec_expression(njs_v
     }
 
     if (!njs_parser_is_lvalue(parser->node)) {
-        nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                "ReferenceError: Invalid left-hand side in postfix operation");
+        njs_parser_ref_error(vm, parser, "Invalid left-hand side "
+                             "in postfix operation", NULL);
         return NJS_TOKEN_ILLEGAL;
     }
 
diff -r 84a95e20f93a -r a83775113025 njs/njs_regexp.c
--- a/njs/njs_regexp.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_regexp.c	Fri Nov 17 18:55:07 2017 +0300
@@ -178,9 +178,9 @@ njs_regexp_literal(njs_vm_t *vm, njs_par
             flags = njs_regexp_flags(&p, lexer->end, 0);
 
             if (nxt_slow_path(flags < 0)) {
-                nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                          "SyntaxError: Invalid RegExp flags \"%.*s\"",
-                          p - lexer->start, lexer->start);
+                njs_parser_syntax_error(vm, parser,
+                                        "Invalid RegExp flags \"%.*s\"",
+                                        p - lexer->start, lexer->start);
 
                 return NJS_TOKEN_ILLEGAL;
             }
@@ -199,9 +199,8 @@ njs_regexp_literal(njs_vm_t *vm, njs_par
         }
     }
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "SyntaxError: Unterminated RegExp \"%.*s\"",
-              p - lexer->start - 1, lexer->start - 1);
+    njs_parser_syntax_error(vm, parser, "Unterminated RegExp \"%.*s\"",
+                            p - lexer->start - 1, lexer->start - 1);
 
     return NJS_TOKEN_ILLEGAL;
 }
@@ -386,9 +385,8 @@ static u_char *
 njs_regexp_compile_trace_handler(nxt_trace_t *trace, nxt_trace_data_t *td,
     u_char *start)
 {
-    int       n;
     u_char    *p;
-    ssize_t   size;
+    size_t    size;
     njs_vm_t  *vm;
 
     size = sizeof("SyntaxError: ") - 1;
@@ -398,20 +396,16 @@ njs_regexp_compile_trace_handler(nxt_tra
     vm = trace->data;
 
     trace = trace->next;
-    p = trace->handler(trace, td, p);
+    p = trace->handler(trace, td, start);
 
     if (vm->parser != NULL) {
-        size = td->end - start;
-
-        n = snprintf((char *) p, size, " in %u", vm->parser->lexer->line);
+        njs_exception_syntax_error(vm, "%s in %u", start,
+                                   vm->parser->lexer->line);
 
-        if (n < size) {
-            p += n;
-        }
+    } else {
+        njs_exception_syntax_error(vm, "%s", start);
     }
 
-    njs_vm_throw_exception(vm, start, p - start);
-
     return p;
 }
 
@@ -442,8 +436,8 @@ njs_regexp_match_trace_handler(nxt_trace
     size_t    size;
     njs_vm_t  *vm;
 
-    size = sizeof("RegExpError: ") - 1;
-    memcpy(start, "RegExpError: ", size);
+    size = sizeof("InternalError: ") - 1;
+    memcpy(start, "InternalError: ", size);
     p = start + size;
 
     vm = trace->data;
@@ -451,7 +445,7 @@ njs_regexp_match_trace_handler(nxt_trace
     trace = trace->next;
     p = trace->handler(trace, td, p);
 
-    njs_vm_throw_exception(vm, start, p - start);
+    njs_exception_internal_error(vm, (const char *) start, NULL);
 
     return p;
 }
diff -r 84a95e20f93a -r a83775113025 njs/njs_variable.c
--- a/njs/njs_variable.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_variable.c	Fri Nov 17 18:55:07 2017 +0300
@@ -20,6 +20,7 @@
 #include <njs_vm.h>
 #include <njs_variable.h>
 #include <njs_parser.h>
+#include <njs_error.h>
 #include <string.h>
 
 
@@ -156,9 +157,9 @@ njs_variable_add(njs_vm_t *vm, njs_parse
 
     /* ret == NXT_DECLINED. */
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "SyntaxError: Identifier \"%.*s\" has already been declared",
-              (int) lhq.key.length, lhq.key.start);
+    njs_parser_syntax_error(vm, parser, "Identifier \"%.*s\" "
+                            "has already been declared",
+                            (int) lhq.key.length, lhq.key.start);
 
     return NULL;
 }
@@ -342,8 +343,8 @@ njs_variable_get(njs_vm_t *vm, njs_parse
         index = (index >> NJS_SCOPE_SHIFT) + 1;
 
         if (index > 255 || vs.scope->argument_closures == 0) {
-            nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-                      "InternalError: too many argument closures");
+            njs_exception_internal_error(vm, "too many argument closures",
+                                         NULL);
 
             return NULL;
         }
@@ -405,9 +406,8 @@ njs_variable_get(njs_vm_t *vm, njs_parse
 
 not_found:
 
-    nxt_alert(&vm->trace, NXT_LEVEL_ERROR,
-              "ReferenceError: \"%.*s\" is not defined",
-              (int) vs.lhq.key.length, vs.lhq.key.start);
+    njs_parser_ref_error(vm, vm->parser, "\"%.*s\" is not defined",
+                         (int) vs.lhq.key.length, vs.lhq.key.start);
 
     return NULL;
 }
diff -r 84a95e20f93a -r a83775113025 njs/njs_vm.c
--- a/njs/njs_vm.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_vm.c	Fri Nov 17 18:55:07 2017 +0300
@@ -3454,25 +3454,6 @@ njs_value_string_copy(njs_vm_t *vm, nxt_
 }
 
 
-void
-njs_vm_throw_exception(njs_vm_t *vm, const u_char *buf, uint32_t size)
-{
-    int32_t      length;
-    njs_value_t  *value;
-
-    value = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t));
-
-    if (nxt_fast_path(value != NULL)) {
-        vm->exception = value;
-
-        length = nxt_utf8_length(buf, size);
-        length = (length >= 0) ? length : 0;
-
-        (void) njs_string_new(vm, value, buf, size, length);
-    }
-}
-
-
 static njs_ret_t
 njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame)
 {
diff -r 84a95e20f93a -r a83775113025 njs/njs_vm.h
--- a/njs/njs_vm.h	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_vm.h	Fri Nov 17 18:55:07 2017 +0300
@@ -931,8 +931,6 @@ struct njs_vm_s {
     njs_native_frame_t       *top_frame;
     njs_frame_t              *active_frame;
 
-    const njs_value_t        *exception;
-
     nxt_lvlhsh_t             externals_hash;
     nxt_lvlhsh_t             variables_hash;
     nxt_lvlhsh_t             values_hash;
@@ -962,7 +960,6 @@ struct njs_vm_s {
      * with the generic type NJS_OBJECT_INTERNAL_ERROR but its own prototype
      * object NJS_PROTOTYPE_MEMORY_ERROR.
      */
-    njs_value_t              memory_error;
     njs_object_t             memory_error_object;
 
     nxt_array_t              *code;  /* of njs_vm_code_t */
@@ -1146,8 +1143,6 @@ njs_ret_t njs_value_to_ext_string(njs_vm
     const njs_value_t *src);
 void njs_number_set(njs_value_t *value, double num);
 
-void njs_vm_throw_exception(njs_vm_t *vm, const 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);
 nxt_int_t njs_builtin_match_native_function(njs_vm_t *vm,
diff -r 84a95e20f93a -r a83775113025 njs/njscript.c
--- a/njs/njscript.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njscript.c	Fri Nov 17 18:55:07 2017 +0300
@@ -24,6 +24,7 @@
 #include <njs_parser.h>
 #include <njs_regexp.h>
 #include <string.h>
+#include <stdio.h>
 
 
 static nxt_int_t njs_vm_init(njs_vm_t *vm);
@@ -198,6 +199,8 @@ njs_vm_create(njs_vm_opt_t *options)
             if (nxt_slow_path(ret != NXT_OK)) {
                 return NULL;
             }
+
+            vm->retval = njs_value_void;
         }
     }
 
@@ -246,6 +249,10 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
     parser->code_size = sizeof(njs_vmcode_stop_t);
     parser->scope_offset = NJS_INDEX_GLOBAL_OFFSET;
 
+    if (vm->backtrace != NULL) {
+        nxt_array_reset(vm->backtrace);
+    }
+
     node = njs_parser(vm, parser, prev);
     if (nxt_slow_path(node == NULL)) {
         goto fail;
@@ -264,10 +271,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
      */
     vm->code = NULL;
 
-    if (vm->backtrace != NULL) {
-        nxt_array_reset(vm->backtrace);
-    }
-
     ret = njs_generate_scope(vm, parser, node);
     if (nxt_slow_path(ret != NXT_OK)) {
         goto fail;
@@ -334,6 +337,8 @@ njs_vm_clone(njs_vm_t *vm, nxt_mem_cache
             goto fail;
         }
 
+        nvm->retval = njs_value_void;
+
         return nvm;
     }
 
@@ -402,8 +407,6 @@ njs_vm_init(njs_vm_t *vm)
         vm->backtrace = backtrace;
     }
 
-    vm->retval = njs_value_void;
-
     vm->trace.level = NXT_LEVEL_TRACE;
     vm->trace.size = 2048;
     vm->trace.handler = njs_parser_trace_handler;
@@ -464,6 +467,10 @@ njs_vm_run(njs_vm_t *vm)
 
     nxt_thread_log_debug("RUN:");
 
+    if (vm->backtrace != NULL) {
+        nxt_array_reset(vm->backtrace);
+    }
+
     ret = njs_vmcode_interpreter(vm);
 
     if (nxt_slow_path(ret == NXT_AGAIN)) {
@@ -515,18 +522,76 @@ njs_vm_return_string(njs_vm_t *vm, u_cha
 nxt_int_t
 njs_vm_retval(njs_vm_t *vm, nxt_str_t *retval)
 {
-    return njs_value_to_ext_string(vm, retval, &vm->retval);
-}
+    u_char                 *p, *start;
+    size_t                 len;
+    nxt_int_t              ret;
+    nxt_uint_t             i;
+    nxt_array_t            *backtrace;
+    njs_backtrace_entry_t  *be;
 
+    if (vm->top_frame == NULL) {
+        /* An exception was thrown during compilation. */
 
-nxt_int_t
-njs_vm_exception(njs_vm_t *vm, nxt_str_t *retval)
-{
-    if (vm->top_frame != NULL) {
+        njs_vm_init(vm);
+    }
+
+    ret = njs_value_to_ext_string(vm, retval, &vm->retval);
+
+    if (ret != NXT_OK) {
+        /* retval evaluation threw an exception. */
+
         vm->top_frame->trap_tries = 0;
+
+        ret = njs_value_to_ext_string(vm, retval, &vm->retval);
+        if (ret != NXT_OK) {
+            return ret;
+        }
     }
 
-    return njs_value_to_ext_string(vm, retval, vm->exception);
+    backtrace = njs_vm_backtrace(vm);
+
+    if (backtrace != NULL) {
+
+        len = retval->length + 1;
+
+        be = backtrace->start;
+
+        for (i = 0; i < backtrace->items; i++) {
+            if (be[i].line != 0) {
+                len += sizeof("    at  (:)\n") - 1 + 10 + be[i].name.length;
+
+            } else {
+                len += sizeof("    at  (native)\n") - 1 + be[i].name.length;
+            }
+        }
+
+        p = nxt_mem_cache_alloc(vm->mem_cache_pool, len);
+        if (p == NULL) {
+            return NXT_ERROR;
+        }
+
+        start = p;
+
+        p = nxt_cpymem(p, retval->start, retval->length);
+        *p++ = '\n';
+
+        for (i = 0; i < backtrace->items; i++) {
+            if (be[i].line != 0) {
+                p += sprintf((char *) p, "    at %.*s (:%u)\n",
+                             (int) be[i].name.length, be[i].name.start,
+                             be[i].line);
+
+            } else {
+                p += sprintf((char *) p, "    at %.*s (native)\n",
+                             (int) be[i].name.length, be[i].name.start);
+            }
+        }
+
+        retval->start = start;
+        retval->length = p - retval->start;
+    }
+
+    return NXT_OK;
 }
 
 
diff -r 84a95e20f93a -r a83775113025 njs/njscript.h
--- a/njs/njscript.h	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njscript.h	Fri Nov 17 18:55:07 2017 +0300
@@ -110,7 +110,6 @@ NXT_EXPORT njs_function_t *njs_vm_functi
 NXT_EXPORT njs_ret_t njs_vm_return_string(njs_vm_t *vm, u_char *start,
     size_t size);
 NXT_EXPORT nxt_int_t njs_vm_retval(njs_vm_t *vm, nxt_str_t *retval);
-NXT_EXPORT nxt_int_t njs_vm_exception(njs_vm_t *vm, nxt_str_t *retval);
 NXT_EXPORT nxt_array_t *njs_vm_backtrace(njs_vm_t *vm);
 
 NXT_EXPORT void njs_disassembler(njs_vm_t *vm);
diff -r 84a95e20f93a -r a83775113025 njs/test/njs_benchmark.c
--- a/njs/test/njs_benchmark.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/test/njs_benchmark.c	Fri Nov 17 18:55:07 2017 +0300
@@ -111,13 +111,10 @@ njs_unit_test_benchmark(nxt_str_t *scrip
             return NXT_ERROR;
         }
 
-        if (njs_vm_run(nvm) == NXT_OK) {
-            if (njs_vm_retval(nvm, &s) != NXT_OK) {
-                return NXT_ERROR;
-            }
+        (void) njs_vm_run(nvm);
 
-        } else {
-            njs_vm_exception(nvm, &s);
+        if (njs_vm_retval(nvm, &s) != NXT_OK) {
+            return NXT_ERROR;
         }
 
         success = nxt_strstr_eq(result, &s);
diff -r 84a95e20f93a -r a83775113025 njs/test/njs_expect_test.exp
--- a/njs/test/njs_expect_test.exp	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/test/njs_expect_test.exp	Fri Nov 17 18:55:07 2017 +0300
@@ -166,3 +166,19 @@ njs_test {
     {"console.help()\r\n"
      "console.help()\r\nVM built-in objects:"}
 }
+
+# Exception in njs_vm_retval()
+njs_test {
+    {"var o = { toString: function() { return [1] } }\r\n"
+     "undefined\r\n>> "}
+    {"o\r\n"
+     "TypeError"}
+}
+
+# Backtraces are reset between invocations
+njs_test {
+    {"JSON.parse(Error())\r\n"
+     "JSON.parse(Error())\r\nSyntaxError: Unexpected token at position 0*at JSON.parse (native)"}
+    {"JSON.parse(Error()\r\n"
+     "JSON.parse(Error()\r\nSyntaxError: Unexpected token \"\" in 1"}
+}
diff -r 84a95e20f93a -r a83775113025 njs/test/njs_interactive_test.c
--- a/njs/test/njs_interactive_test.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/test/njs_interactive_test.c	Fri Nov 17 18:55:07 2017 +0300
@@ -121,69 +121,73 @@ static njs_interactive_test_t  njs_test[
                  "function f(o) {return ff(o)}" ENTER
                  "f({})" ENTER),
       nxt_string("TypeError\n"
-                 "at ff (:1)\n"
-                 "at f (:1)\n"
-                 "at main\n") },
+                 "    at ff (:1)\n"
+                 "    at f (:1)\n"
+                 "    at main (native)\n") },
 
     { nxt_string("function ff(o) {return o.a.a}" ENTER
                  "function f(o) {try {return ff(o)} "
                                  "finally {return o.a.a}}" ENTER
                  "f({})" ENTER),
       nxt_string("TypeError\n"
-                 "at f (:1)\n"
-                 "at main\n") },
+                 "    at f (:1)\n"
+                 "    at main (native)\n") },


More information about the nginx-devel mailing list