[njs] Making backtrace a property of a thrown exception.

Dmitry Volyntsev xeioex at nginx.com
Fri Nov 29 14:28:31 UTC 2019


details:   https://hg.nginx.org/njs/rev/e20023dd991d
branches:  
changeset: 1270:e20023dd991d
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Nov 29 17:11:11 2019 +0300
description:
Making backtrace a property of a thrown exception.

diffstat:

 src/njs.h                       |    6 +-
 src/njs_error.c                 |  172 ++++++++++++++++++++++--
 src/njs_error.h                 |    2 +
 src/njs_json.c                  |    4 -
 src/njs_object_hash.h           |    9 +
 src/njs_shell.c                 |   22 +-
 src/njs_vm.c                    |  276 +++++++++++++++++----------------------
 src/njs_vm.h                    |    7 +-
 src/njs_vmcode.c                |   14 +-
 src/test/njs_interactive_test.c |    5 +-
 test/njs_expect_test.exp        |   21 ++-
 11 files changed, 332 insertions(+), 206 deletions(-)

diffs (837 lines):

diff -r 5bbbc4361799 -r e20023dd991d src/njs.h
--- a/src/njs.h	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs.h	Fri Nov 29 17:11:11 2019 +0300
@@ -258,19 +258,19 @@ NJS_EXPORT njs_int_t njs_vm_value_string
 NJS_EXPORT u_char *njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value,
     uint32_t size);
 NJS_EXPORT njs_int_t njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval,
-    const njs_value_t *value, uintptr_t *next);
+    njs_value_t *value, uintptr_t *next);
 
 /*
  * Converts a value to string.
  */
 NJS_EXPORT njs_int_t njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst,
-    const njs_value_t *src);
+    njs_value_t *src);
 
 /*
  * Calls njs_vm_value_to_string(), if exception was thrown adds backtrace.
  */
 NJS_EXPORT njs_int_t njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst,
-    const njs_value_t *src);
+    njs_value_t *src);
 NJS_EXPORT njs_int_t njs_vm_retval_string(njs_vm_t *vm, njs_str_t *dst);
 
 NJS_EXPORT njs_int_t njs_vm_value_dump(njs_vm_t *vm, njs_str_t *dst,
diff -r 5bbbc4361799 -r e20023dd991d src/njs_error.c
--- a/src/njs_error.c	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_error.c	Fri Nov 29 17:11:11 2019 +0300
@@ -10,6 +10,7 @@
 
 static const njs_value_t  njs_error_message_string = njs_string("message");
 static const njs_value_t  njs_error_name_string = njs_string("name");
+static const njs_value_t  njs_error_stack_string = njs_string("stack");
 
 
 void
@@ -59,6 +60,127 @@ njs_error_fmt_new(njs_vm_t *vm, njs_valu
 }
 
 
+static njs_int_t
+njs_error_stack_new(njs_vm_t *vm, njs_object_t *error, njs_value_t *retval)
+{
+    njs_int_t           ret;
+    njs_str_t           string;
+    njs_arr_t           *stack;
+    njs_value_t         value;
+    njs_native_frame_t  *frame;
+
+    njs_set_object(&value, error);
+
+    ret = njs_error_to_string(vm, retval, &value);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_backtrace_entry_t));
+    if (njs_slow_path(stack == NULL)) {
+        return NJS_ERROR;
+    }
+
+    frame = vm->top_frame;
+
+    for ( ;; ) {
+        if (njs_vm_add_backtrace_entry(vm, stack, frame) != NJS_OK) {
+            break;
+        }
+
+        frame = frame->previous;
+
+        if (frame == NULL) {
+            break;
+        }
+    }
+
+    njs_string_get(retval, &string);
+
+    ret = njs_vm_backtrace_to_string(vm, stack, &string);
+
+    njs_arr_destroy(stack);
+
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    return njs_string_set(vm, retval, string.start, string.length);
+}
+
+
+njs_int_t
+njs_error_stack_attach(njs_vm_t *vm, njs_value_t *value)
+{
+    njs_int_t           ret;
+    njs_object_t        *error;
+    njs_object_prop_t   *prop;
+    njs_lvlhsh_query_t  lhq;
+
+    if (njs_slow_path(!njs_is_error(value))) {
+        return NJS_DECLINED;
+    }
+
+    if (vm->debug == NULL || vm->start == NULL) {
+        return NJS_OK;
+    }
+
+    error = njs_object(value);
+
+    lhq.replace = 0;
+    lhq.pool = vm->mem_pool;
+    lhq.proto = &njs_object_hash_proto;
+
+    lhq.key = njs_str_value("stack");
+    lhq.key_hash = NJS_STACK_HASH;
+
+    prop = njs_object_prop_alloc(vm, &njs_error_stack_string,
+                                 &njs_value_undefined, 1);
+    if (njs_slow_path(prop == NULL)) {
+        return NJS_ERROR;
+    }
+
+    prop->enumerable = 0;
+
+    ret = njs_error_stack_new(vm, error, &prop->value);
+    if (njs_slow_path(ret == NJS_ERROR)) {
+        njs_internal_error(vm, "njs_error_stack_new() failed");
+        return NJS_ERROR;
+    }
+
+    if (ret == NJS_OK) {
+        lhq.value = prop;
+
+        ret = njs_lvlhsh_insert(&error->hash, &lhq);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            njs_internal_error(vm, "lvlhsh insert failed");
+            return NJS_ERROR;
+        }
+    }
+
+    return NJS_OK;
+}
+
+
+njs_int_t
+njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack)
+{
+    njs_int_t  ret;
+
+    ret = njs_value_property(vm, value, njs_value_arg(&njs_error_stack_string),
+                             stack);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    if (!njs_is_string(stack)) {
+        return NJS_DECLINED;
+    }
+
+    return NJS_OK;
+}
+
+
 njs_object_t *
 njs_error_alloc(njs_vm_t *vm, njs_object_type_t type, const njs_value_t *name,
     const njs_value_t *message)
@@ -83,11 +205,11 @@ njs_error_alloc(njs_vm_t *vm, njs_object
 
     lhq.replace = 0;
     lhq.pool = vm->mem_pool;
+    lhq.proto = &njs_object_hash_proto;
 
     if (name != NULL) {
         lhq.key = njs_str_value("name");
         lhq.key_hash = NJS_NAME_HASH;
-        lhq.proto = &njs_object_hash_proto;
 
         prop = njs_object_prop_alloc(vm, &njs_error_name_string, name, 1);
         if (njs_slow_path(prop == NULL)) {
@@ -106,7 +228,6 @@ njs_error_alloc(njs_vm_t *vm, njs_object
     if (message!= NULL) {
         lhq.key = njs_str_value("message");
         lhq.key_hash = NJS_MESSAGE_HASH;
-        lhq.proto = &njs_object_hash_proto;
 
         prop = njs_object_prop_alloc(vm, &njs_error_message_string, message, 1);
         if (njs_slow_path(prop == NULL)) {
@@ -605,20 +726,8 @@ njs_error_prototype_value_of(njs_vm_t *v
 
 
 static njs_int_t
-njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    if (nargs < 1 || !njs_is_object(&args[0])) {
-        njs_type_error(vm, "\"this\" argument is not an object");
-        return NJS_ERROR;
-    }
-
-    return njs_error_to_string(vm, &vm->retval, &args[0]);
-}
-
-
-njs_int_t
-njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error)
+njs_error_to_string2(njs_vm_t *vm, njs_value_t *retval,
+    const njs_value_t *error, njs_bool_t want_stack)
 {
     size_t              length;
     u_char              *p;
@@ -630,6 +739,17 @@ njs_error_to_string(njs_vm_t *vm, njs_va
 
     static const njs_value_t  default_name = njs_string("Error");
 
+    if (want_stack) {
+        ret = njs_error_stack(vm, njs_value_arg(error), retval);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (ret == NJS_OK) {
+            return NJS_OK;
+        }
+    }
+
     njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH);
 
     ret = njs_object_property(vm, error, &lhq, &value1);
@@ -708,6 +828,26 @@ njs_error_to_string(njs_vm_t *vm, njs_va
 }
 
 
+static njs_int_t
+njs_error_prototype_to_string(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    if (nargs < 1 || !njs_is_object(&args[0])) {
+        njs_type_error(vm, "\"this\" argument is not an object");
+        return NJS_ERROR;
+    }
+
+    return njs_error_to_string2(vm, &vm->retval, &args[0], 0);
+}
+
+
+njs_int_t
+njs_error_to_string(njs_vm_t *vm, njs_value_t *retval, const njs_value_t *error)
+{
+    return njs_error_to_string2(vm, retval, error, 1);
+}
+
+
 static const njs_object_prop_t  njs_error_prototype_properties[] =
 {
     {
diff -r 5bbbc4361799 -r e20023dd991d src/njs_error.h
--- a/src/njs_error.h	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_error.h	Fri Nov 29 17:11:11 2019 +0300
@@ -44,6 +44,8 @@ njs_object_t *njs_error_alloc(njs_vm_t *
     const njs_value_t *name, const njs_value_t *message);
 njs_int_t njs_error_to_string(njs_vm_t *vm, njs_value_t *retval,
     const njs_value_t *error);
+njs_int_t njs_error_stack(njs_vm_t *vm, njs_value_t *value, njs_value_t *stack);
+njs_int_t njs_error_stack_attach(njs_vm_t *vm, njs_value_t *value);
 
 
 extern const njs_object_type_init_t  njs_error_type_init;
diff -r 5bbbc4361799 -r e20023dd991d src/njs_json.c
--- a/src/njs_json.c	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_json.c	Fri Nov 29 17:11:11 2019 +0300
@@ -2144,10 +2144,6 @@ njs_vm_value_dump(njs_vm_t *vm, njs_str_
     njs_property_query_t  pq;
     njs_json_stringify_t  *stringify, dump_stringify;
 
-    if (njs_vm_backtrace(vm) != NULL) {
-        goto exception;
-    }
-
     stringify = &dump_stringify;
 
     stringify->vm = vm;
diff -r 5bbbc4361799 -r e20023dd991d src/njs_object_hash.h
--- a/src/njs_object_hash.h	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_object_hash.h	Fri Nov 29 17:11:11 2019 +0300
@@ -411,6 +411,15 @@
         's'), 'e'), 't')
 
 
+#define NJS_STACK_HASH                                                        \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        's'), 't'), 'a'), 'c'), 'k')
+
+
 #define NJS_STRING_HASH                                                       \
     njs_djb_hash_add(                                                         \
     njs_djb_hash_add(                                                         \
diff -r 5bbbc4361799 -r e20023dd991d src/njs_shell.c
--- a/src/njs_shell.c	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_shell.c	Fri Nov 29 17:11:11 2019 +0300
@@ -722,19 +722,23 @@ njs_output(njs_opts_t *opts, njs_vm_t *v
 {
     njs_str_t  out;
 
-    if (!opts->silent) {
+    if (opts->silent) {
+        return;
+    }
+
+    if (ret == NJS_OK) {
         if (njs_vm_retval_dump(vm, &out, 1) != NJS_OK) {
-            out = njs_str_value("failed to get retval from VM");
-            ret = NJS_ERROR;
+            njs_stderror("Shell:failed to get retval from VM\n");
+            return;
         }
 
-        if (ret != NJS_OK) {
-            njs_stderror("%V\n", &out);
+        if (vm->options.accumulative) {
+            njs_printf("%V\n", &out);
+        }
 
-        } else if (vm->options.accumulative) {
-            njs_print(out.start, out.length);
-            njs_printf("\n");
-        }
+    } else {
+        njs_vm_retval_string(vm, &out);
+        njs_stderror("Thrown:\n%V\n", &out);
     }
 }
 
diff -r 5bbbc4361799 -r e20023dd991d src/njs_vm.c
--- a/src/njs_vm.c	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_vm.c	Fri Nov 29 17:11:11 2019 +0300
@@ -153,10 +153,6 @@ njs_vm_compile(njs_vm_t *vm, u_char **st
 
     parser->lexer = &lexer;
 
-    if (vm->backtrace != NULL) {
-        njs_arr_reset(vm->backtrace);
-    }
-
     njs_set_undefined(&vm->retval);
 
     ret = njs_parser(vm, parser, prev);
@@ -471,10 +467,6 @@ njs_vm_post_event(njs_vm_t *vm, njs_vm_e
 njs_int_t
 njs_vm_run(njs_vm_t *vm)
 {
-    if (njs_slow_path(vm->backtrace != NULL)) {
-        njs_arr_reset(vm->backtrace);
-    }
-
     return njs_vm_handle_events(vm);
 }
 
@@ -648,120 +640,11 @@ njs_vm_memory_error(njs_vm_t *vm)
 }
 
 
-njs_arr_t *
-njs_vm_backtrace(njs_vm_t *vm)
-{
-    if (vm->backtrace != NULL && !njs_arr_is_empty(vm->backtrace)) {
-        return vm->backtrace;
-    }
-
-    return NULL;
-}
-
-
-static njs_int_t
-njs_vm_backtrace_dump(njs_vm_t *vm, njs_str_t *dst, const njs_value_t *src)
+njs_int_t
+njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src)
 {
-    u_char                 *p, *start, *end;
-    size_t                 len, count;
-    njs_uint_t             i;
-    njs_arr_t              *backtrace;
-    njs_backtrace_entry_t  *be, *prev;
-
-    backtrace = njs_vm_backtrace(vm);
-
-    len = dst->length + 1;
-
-    count = 0;
-    prev = NULL;
-
-    be = backtrace->start;
-
-    for (i = 0; i < backtrace->items; i++) {
-        if (i != 0 && prev->name.start == be->name.start
-            && prev->line == be->line)
-        {
-            count++;
-
-        } else {
-
-            if (count != 0) {
-                len += njs_length("      repeats  times\n")
-                       + NJS_INT_T_LEN;
-                count = 0;
-            }
-
-            len += be->name.length + njs_length("    at  ()\n");
-
-            if (be->line != 0) {
-                len += be->file.length + NJS_INT_T_LEN + 1;
-
-            } else {
-                len += njs_length("native");
-            }
-        }
-
-        prev = be;
-        be++;
-    }
-
-    p = njs_mp_alloc(vm->mem_pool, len);
-    if (p == NULL) {
-        njs_memory_error(vm);
-        return NJS_ERROR;
-    }
-
-    start = p;
-    end = start + len;
-
-    p = njs_cpymem(p, dst->start, dst->length);
-    *p++ = '\n';
-
-    count = 0;
-    prev = NULL;
-
-    be = backtrace->start;
-
-    for (i = 0; i < backtrace->items; i++) {
-        if (i != 0 && prev->name.start == be->name.start
-            && prev->line == be->line)
-        {
-            count++;
-
-        } else {
-            if (count != 0) {
-                p = njs_sprintf(p, end, "      repeats %uz times\n",
-                                count);
-                count = 0;
-            }
-
-            p = njs_sprintf(p, end, "    at %V ", &be->name);
-
-            if (be->line != 0) {
-                p = njs_sprintf(p, end, "(%V:%uD)\n", &be->file,
-                                be->line);
-
-            } else {
-                p = njs_sprintf(p, end, "(native)\n");
-            }
-        }
-
-        prev = be;
-        be++;
-    }
-
-    dst->start = start;
-    dst->length = p - dst->start;
-
-    return NJS_OK;
-}
-
-
-njs_int_t
-njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, const njs_value_t *src)
-{
-    njs_int_t   ret;
-    njs_uint_t  exception;
+    njs_int_t    ret;
+    njs_uint_t   exception;
 
     if (njs_slow_path(src->type == NJS_NUMBER
                       && njs_number(src) == 0
@@ -771,26 +654,17 @@ njs_vm_value_string(njs_vm_t *vm, njs_st
         return NJS_OK;
     }
 
-    exception = 1;
+    exception = 0;
 
 again:
 
     ret = njs_vm_value_to_string(vm, dst, src);
-
     if (njs_fast_path(ret == NJS_OK)) {
-
-        if (njs_vm_backtrace(vm) != NULL) {
-            ret = njs_vm_backtrace_dump(vm, dst, src);
-            if (njs_slow_path(ret != NJS_OK)) {
-                return NJS_ERROR;
-            }
-        }
-
         return NJS_OK;
     }
 
-    if (exception) {
-        exception = 0;
+    if (!exception) {
+        exception = 1;
 
         /* value evaluation threw an exception. */
 
@@ -966,18 +840,18 @@ njs_vm_object_prop(njs_vm_t *vm, const n
 
 
 njs_int_t
-njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, const njs_value_t *src)
+njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src)
 {
     u_char       *start;
     size_t       size;
     njs_int_t    ret;
-    njs_value_t  value;
+    njs_value_t  value, stack;
 
     if (njs_slow_path(src == NULL)) {
         return NJS_ERROR;
     }
 
-    if (njs_slow_path(njs_is_error(src))) {
+    if (njs_is_error(src)) {
 
         /* MemoryError is a nonextensible internal error. */
 
@@ -987,6 +861,15 @@ njs_vm_value_to_string(njs_vm_t *vm, njs
             njs_string_get(&njs_string_memory_error, dst);
             return NJS_OK;
         }
+
+        ret = njs_error_stack(vm, src, &stack);
+        if (njs_slow_path(ret == NJS_ERROR)) {
+            return ret;
+        }
+
+        if (ret == NJS_OK) {
+            src = &stack;
+        }
     }
 
     value = *src;
@@ -1020,7 +903,7 @@ njs_vm_value_to_string(njs_vm_t *vm, njs
 
 njs_int_t
 njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval,
-    const njs_value_t *value, uintptr_t *next)
+    njs_value_t *value, uintptr_t *next)
 {
     uintptr_t    n;
     njs_array_t  *array;
@@ -1060,31 +943,19 @@ njs_vm_value_string_copy(njs_vm_t *vm, n
 
 
 njs_int_t
-njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame)
+njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
+    njs_native_frame_t *native_frame)
 {
     njs_int_t              ret;
-    njs_arr_t              *backtrace;
     njs_uint_t             i;
     njs_function_t         *function;
-    njs_native_frame_t     *native_frame;
     njs_function_debug_t   *debug_entry;
     njs_function_lambda_t  *lambda;
     njs_backtrace_entry_t  *be;
 
-    if (njs_slow_path(vm->backtrace == NULL)) {
-        backtrace = njs_arr_create(vm->mem_pool, 4,
-                                   sizeof(njs_backtrace_entry_t));
-        if (njs_slow_path(backtrace == NULL)) {
-            return NJS_ERROR;
-        }
-
-        vm->backtrace = backtrace;
-    }
-
-    native_frame = &frame->native;
     function = native_frame->function;
 
-    be = njs_arr_add(vm->backtrace);
+    be = njs_arr_add(stack);
     if (njs_slow_path(be == NULL)) {
         return NJS_ERROR;
     }
@@ -1143,6 +1014,107 @@ njs_vm_add_backtrace_entry(njs_vm_t *vm,
 }
 
 
+njs_int_t
+njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
+{
+    u_char                 *p, *start, *end;
+    size_t                 len, count;
+    njs_uint_t             i;
+    njs_backtrace_entry_t  *be, *prev;
+
+    if (backtrace->items == 0) {
+        return NJS_OK;
+    }
+
+    len = dst->length + 1;
+
+    count = 0;
+    prev = NULL;
+
+    be = backtrace->start;
+
+    for (i = 0; i < backtrace->items; i++) {
+        if (i != 0 && prev->name.start == be->name.start
+            && prev->line == be->line)
+        {
+            count++;
+
+        } else {
+
+            if (count != 0) {
+                len += njs_length("      repeats  times\n")
+                       + NJS_INT_T_LEN;
+                count = 0;
+            }
+
+            len += be->name.length + njs_length("    at  ()\n");
+
+            if (be->line != 0) {
+                len += be->file.length + NJS_INT_T_LEN + 1;
+
+            } else {
+                len += njs_length("native");
+            }
+        }
+
+        prev = be;
+        be++;
+    }
+
+    p = njs_mp_alloc(vm->mem_pool, len);
+    if (p == NULL) {
+        njs_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    start = p;
+    end = start + len;
+
+    p = njs_cpymem(p, dst->start, dst->length);
+    *p++ = '\n';
+
+    count = 0;
+    prev = NULL;
+
+    be = backtrace->start;
+
+    for (i = 0; i < backtrace->items; i++) {
+        if (i != 0 && prev->name.start == be->name.start
+            && prev->line == be->line)
+        {
+            count++;
+
+        } else {
+            if (count != 0) {
+                p = njs_sprintf(p, end, "      repeats %uz times\n",
+                                count);
+                count = 0;
+            }
+
+            p = njs_sprintf(p, end, "    at %V ", &be->name);
+
+            if (be->line != 0) {
+                p = njs_sprintf(p, end, "(%V:%uD)\n", &be->file,
+                                be->line);
+
+            } else {
+                p = njs_sprintf(p, end, "(native)\n");
+            }
+        }
+
+        prev = be;
+        be++;
+    }
+
+    dst->start = start;
+    dst->length = p - dst->start;
+
+    return NJS_OK;
+}
+
+
+
+
 void *
 njs_lvlhsh_alloc(void *data, size_t size)
 {
diff -r 5bbbc4361799 -r e20023dd991d src/njs_vm.h
--- a/src/njs_vm.h	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_vm.h	Fri Nov 29 17:11:11 2019 +0300
@@ -224,7 +224,6 @@ struct njs_vm_s {
     njs_random_t             random;
 
     njs_arr_t                *debug;
-    njs_arr_t                *backtrace;
 
     /*
      * njs_property_query() uses it to store reference to a temporary
@@ -272,14 +271,16 @@ struct njs_vm_shared_s {
 
 void njs_vm_scopes_restore(njs_vm_t *vm, njs_frame_t *frame,
     njs_native_frame_t *previous);
-njs_int_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_frame_t *frame);
+njs_int_t njs_vm_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
+    njs_native_frame_t *native_frame);
+njs_int_t njs_vm_backtrace_to_string(njs_vm_t *vm, njs_arr_t *stack,
+    njs_str_t *dst);
 
 njs_int_t njs_builtin_objects_create(njs_vm_t *vm);
 njs_int_t njs_builtin_objects_clone(njs_vm_t *vm, njs_value_t *global);
 njs_int_t njs_builtin_match_native_function(njs_vm_t *vm,
     njs_function_native_t func, njs_str_t *name);
 
-njs_arr_t *njs_vm_backtrace(njs_vm_t *vm);
 njs_arr_t *njs_vm_completions(njs_vm_t *vm, njs_str_t *expression);
 
 void *njs_lvlhsh_alloc(void *data, size_t size);
diff -r 5bbbc4361799 -r e20023dd991d src/njs_vmcode.c
--- a/src/njs_vmcode.c	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/njs_vmcode.c	Fri Nov 29 17:11:11 2019 +0300
@@ -898,6 +898,10 @@ next:
 
 error:
 
+    if (njs_is_error(&vm->retval)) {
+        (void) njs_error_stack_attach(vm, &vm->retval);
+    }
+
     for ( ;; ) {
         frame = (njs_frame_t *) vm->top_frame;
 
@@ -906,19 +910,9 @@ error:
         if (catch != NULL) {
             pc = catch;
 
-            if (vm->backtrace != NULL) {
-                njs_arr_reset(vm->backtrace);
-            }
-
             goto next;
         }
 
-        if (vm->debug != NULL
-            && njs_vm_add_backtrace_entry(vm, frame) != NJS_OK)
-        {
-            break;
-        }
-
         previous = frame->native.previous;
         if (previous == NULL) {
             break;
diff -r 5bbbc4361799 -r e20023dd991d src/test/njs_interactive_test.c
--- a/src/test/njs_interactive_test.c	Thu Nov 28 13:25:00 2019 +0300
+++ b/src/test/njs_interactive_test.c	Fri Nov 29 17:11:11 2019 +0300
@@ -243,10 +243,7 @@ static njs_interactive_test_t  njs_test[
                  "    at main (native)\n") },
 
     { njs_str("function f(n) { if (n == 0) { throw 'a'; } return f(n-1); }; f(2)" ENTER),
-      njs_str("a\n"
-                 "    at f (:1)\n"
-                 "      repeats 2 times\n"
-                 "    at main (native)\n") },
+      njs_str("a") },
 
     /* Exception in njs_vm_retval_string() */
 
diff -r 5bbbc4361799 -r e20023dd991d test/njs_expect_test.exp
--- a/test/njs_expect_test.exp	Thu Nov 28 13:25:00 2019 +0300
+++ b/test/njs_expect_test.exp	Fri Nov 29 17:11:11 2019 +0300
@@ -258,7 +258,7 @@ njs_test {
 
 njs_test {
     {"console.ll()\r\n"
-     "console.ll()\r\nTypeError: (intermediate value)\\\[\"ll\"] is not a function"}
+     "console.ll()\r\nThrown:\r\nTypeError: (intermediate value)\\\[\"ll\"] is not a function"}
 }
 
 njs_test {
@@ -280,7 +280,7 @@ njs_test {
 # Backtraces for external objects
 njs_test {
     {"console.log(console.a.a)\r\n"
-     "console.log(console.a.a)\r\nTypeError:*at print (native)"}
+     "console.log(console.a.a)\r\nThrown:\r\nTypeError:*at print (native)"}
 }
 
 # dumper
@@ -321,9 +321,9 @@ njs_test {
 # 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\nThrown:\r\nSyntaxError: Unexpected token at position 0*at JSON.parse (native)"}
     {"JSON.parse(Error()\r\n"
-     "JSON.parse(Error()\r\nSyntaxError: Unexpected token \"\" in shell:1"}
+     "JSON.parse(Error()\r\nThrown:\r\nSyntaxError: Unexpected token \"\" in shell:1"}
 }
 
 njs_test {
@@ -335,7 +335,18 @@ njs_test {
 
 njs_test {
     {"(function() { throw 'test' })()\r\n"
-     "test\r\n    at anonymous (shell:1)"}
+     "Thrown:\r\ntest"}
+}
+
+njs_test {
+    {"function f() { return ({}.a.a); }\r\n"
+     "undefined"}
+    {"var e; try {f()} catch (ee) {e = ee}\r\n"
+     "undefined"}
+    {"Object.keys(null)\r\n"
+     "Thrown:\r\nTypeError: cannot convert null argument to object"}
+    {"e\r\n"
+     "TypeError: cannot get property \"a\" of undefined*at f (shell:1)"}
 }
 
 # Non-ASCII characters


More information about the nginx-devel mailing list