]> git.kaiwu.me - njs.git/commitdiff
Fixed stack attaching by stringifying function addresses in place.
authorDmitry Volyntsev <xeioex@nginx.com>
Wed, 28 Jan 2026 22:54:29 +0000 (14:54 -0800)
committerDmitry Volyntsev <xeioexception@gmail.com>
Thu, 5 Feb 2026 23:32:12 +0000 (15:32 -0800)
This fixed Error stack output on CLI.

src/njs_error.c
src/njs_error.h
src/njs_value.c
src/njs_vm.c
src/njs_vm.h
src/njs_vmcode.c
src/test/njs_unit_test.c

index 57ab477a962313e391dc2d87b2a798178c3ef226..e8de835fc5aafb0397b37370b7c20e3447185d1d 100644 (file)
@@ -8,28 +8,6 @@
 #include <njs_main.h>
 
 
-typedef struct {
-    union {
-        njs_function_t                *function;
-        u_char                        *pc;
-    } u;
-    uint8_t                           native;
-} njs_stack_entry_t;
-
-
-typedef struct {
-    njs_str_t                         name;
-    njs_str_t                         file;
-    uint32_t                          line;
-} njs_backtrace_entry_t;
-
-
-static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
-    njs_stack_entry_t *se);
-static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace,
-    njs_str_t *dst);
-
-
 void
 njs_error_new(njs_vm_t *vm, njs_value_t *dst, njs_object_t *proto,
     u_char *start, size_t size)
@@ -89,74 +67,115 @@ njs_error_fmt_new(njs_vm_t *vm, njs_value_t *dst, njs_object_type_t type,
 }
 
 
-static njs_int_t
-njs_error_stack_new(njs_vm_t *vm, njs_object_value_t *error)
+void
+njs_error_stack_attach(njs_vm_t *vm, njs_value_t value)
 {
-    njs_arr_t           *stack;
-    njs_stack_entry_t   *se;
+    size_t              count;
+    uint32_t            line, prev_line;
+    njs_int_t           ret;
+    njs_str_t           name, file, prev_name;
+    njs_chb_t           chain;
+    njs_value_t         *stackval;
+    njs_vm_code_t       *code;
+    njs_function_t      *function;
     njs_native_frame_t  *frame;
 
-    stack = njs_arr_create(vm->mem_pool, 4, sizeof(njs_stack_entry_t));
-    if (njs_slow_path(stack == NULL)) {
-        return NJS_ERROR;
+    if (njs_slow_path(!vm->options.backtrace
+                      || !njs_is_error(&value))
+                      || njs_object(&value)->stack_attached)
+    {
+        return;
     }
 
-    frame = vm->top_frame;
+    NJS_CHB_MP_INIT(&chain, vm->mem_pool);
+
+    count = 0;
+    prev_line = 0;
+    prev_name = njs_str_value("");
+
+    for (frame = vm->top_frame; frame != NULL; frame = frame->previous) {
+        function = frame->native ? frame->function : NULL;
+
+        if (function != NULL && function->bound != NULL) {
+            continue;
+        }
+
+        line = 0;
+        file = njs_str_value("");
 
-    for ( ;; ) {
-        if (frame->native || frame->pc != NULL) {
-            se = njs_arr_add(stack);
-            if (njs_slow_path(se == NULL)) {
-                return NJS_ERROR;
+        if (!frame->native) {
+            if (frame->pc == NULL) {
+                continue;
             }
 
-            se->native = frame->native;
+            code = njs_lookup_code(vm, frame->pc);
+
+            if (code != NULL) {
+                name = code->name;
+
+                if (name.length == 0) {
+                    name = njs_entry_anonymous;
+                }
 
-            if (se->native) {
-                se->u.function = frame->function;
+                line = njs_lookup_line(code->lines, frame->pc - code->start);
+
+                if (!vm->options.quiet) {
+                    file = code->file;
+                }
 
             } else {
-                se->u.pc = frame->pc;
+                name = njs_entry_unknown;
             }
-        }
 
-        frame = frame->previous;
+        } else {
+            ret = njs_builtin_match_native_function(vm, function, &name);
+            if (ret != NJS_OK) {
+                name = njs_entry_unknown;
+            }
+        }
 
-        if (frame == NULL) {
-            break;
+        if (count != 0 && name.start == prev_name.start
+            && line == prev_line)
+        {
+            count++;
+            continue;
         }
-    }
 
-    njs_data(&error->value) = stack;
+        if (count > 1) {
+            njs_chb_sprintf(&chain, 64, "      repeats %uz times\n", count);
+        }
 
-    return NJS_OK;
-}
+        count = 1;
+        prev_name = name;
+        prev_line = line;
 
+        njs_chb_sprintf(&chain, 10 + name.length, "    at %V ", &name);
 
-njs_int_t
-njs_error_stack_attach(njs_vm_t *vm, njs_value_t value)
-{
-    njs_int_t  ret;
-
-    if (njs_slow_path(!njs_is_error(&value))
-        || njs_object(&value)->stack_attached)
-    {
-        return NJS_DECLINED;
+        if (line != 0) {
+            njs_chb_sprintf(&chain, 12 + file.length, "(%V:%uD)\n",
+                            &file, line);
+        } else {
+            njs_chb_append_literal(&chain, "(native)\n");
+        }
     }
 
-    if (njs_slow_path(!vm->options.backtrace || vm->start == NULL)) {
-        return NJS_OK;
+    if (count > 1) {
+        njs_chb_sprintf(&chain, 64, "      repeats %uz times\n", count);
     }
 
-    ret = njs_error_stack_new(vm, value.data.u.object_value);
-    if (njs_slow_path(ret != NJS_OK)) {
-        njs_internal_error(vm, "njs_error_stack_new() failed");
-        return NJS_ERROR;
+    if (njs_chb_size(&chain) == 0) {
+        return;
     }
 
-    njs_object(&value)->stack_attached = 1;
+    stackval = njs_object_value(&value);
 
-    return NJS_OK;
+    ret = njs_string_create_chb(vm, stackval, &chain);
+
+    njs_chb_destroy(&chain);
+
+    if (njs_fast_path(ret == NJS_OK)) {
+        njs_object(&value)->stack_attached = 1;
+    }
 }
 
 
@@ -193,7 +212,7 @@ njs_error_alloc(njs_vm_t *vm, njs_object_t *proto, const njs_value_t *name,
         goto memory_error;
     }
 
-    njs_set_data(&ov->value, NULL, NJS_DATA_TAG_ANY);
+    njs_set_undefined(&ov->value);
 
     error = &ov->object;
     njs_flathsh_init(&error->hash);
@@ -703,12 +722,11 @@ static njs_int_t
 njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused,
     njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
 {
-    njs_int_t          ret;
-    njs_str_t          string;
-    njs_arr_t          *stack, *backtrace;
-    njs_uint_t         i;
-    njs_value_t        rv, *stackval;
-    njs_stack_entry_t  *se;
+    u_char       *p;
+    size_t       length;
+    njs_int_t    ret;
+    njs_str_t    msg, trace;
+    njs_value_t  msg_val, *stackval;
 
     if (retval != NULL) {
         if (!njs_is_error(value)) {
@@ -723,53 +741,29 @@ njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused
             return NJS_OK;
         }
 
-        if (!njs_is_data(stackval, NJS_DATA_TAG_ANY)) {
+        if (!njs_is_string(stackval)) {
             njs_value_assign(retval, stackval);
             return NJS_OK;
         }
 
-        stack = njs_data(stackval);
-        if (stack == NULL) {
-            njs_set_undefined(retval);
-            return NJS_OK;
-        }
-
-        se = stack->start;
-
-        backtrace = njs_arr_create(vm->mem_pool, stack->items,
-                                   sizeof(njs_backtrace_entry_t));
-        if (njs_slow_path(backtrace == NULL)) {
-            return NJS_ERROR;
-        }
-
-        for (i = 0; i < stack->items; i++) {
-            if (njs_add_backtrace_entry(vm, backtrace, &se[i]) != NJS_OK) {
-                return NJS_ERROR;
-            }
-        }
-
-        ret = njs_error_to_string2(vm, &rv, value, 0);
+        ret = njs_error_to_string2(vm, &msg_val, value, 0);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
 
-        njs_string_get(vm, &rv, &string);
-
-        ret = njs_backtrace_to_string(vm, backtrace, &string);
+        njs_string_get(vm, &msg_val, &msg);
+        njs_string_get(vm, stackval, &trace);
 
-        njs_arr_destroy(backtrace);
-        njs_arr_destroy(stack);
-
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
+        length = msg.length + 1 + trace.length;
 
-        ret = njs_string_create(vm, stackval, string.start, string.length);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
+        p = njs_string_alloc(vm, retval, msg.length + 1 + trace.length, length);
+        if (njs_slow_path(p == NULL)) {
+            return NJS_ERROR;
         }
 
-        njs_value_assign(retval, stackval);
+        p = njs_cpymem(p, msg.start, msg.length);
+        *p++ = '\n';
+        memcpy(p, trace.start, trace.length);
 
         return NJS_OK;
     }
@@ -778,7 +772,7 @@ njs_error_prototype_stack(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused
 
     if (njs_is_error(value)) {
         stackval = njs_object_value(value);
-        njs_set_data(stackval, NULL, NJS_DATA_TAG_ANY);
+        njs_set_undefined(stackval);
     }
 
     return NJS_OK;
@@ -1088,119 +1082,3 @@ const njs_object_type_init_t  njs_aggregate_error_type_init = {
     .prototype_props = &njs_aggregate_error_prototype_init,
     .prototype_value = { .object = { .type = NJS_OBJECT } },
 };
-
-
-static njs_int_t
-njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
-    njs_stack_entry_t *se)
-{
-    njs_int_t              ret;
-    njs_vm_code_t          *code;
-    njs_function_t         *function;
-    njs_backtrace_entry_t  *be;
-
-    function = se->native ? se->u.function : NULL;
-
-    if (function != NULL && function->bound != NULL) {
-        /* Skip. */
-        return NJS_OK;
-    }
-
-    be = njs_arr_add(stack);
-    if (njs_slow_path(be == NULL)) {
-        return NJS_ERROR;
-    }
-
-    be->line = 0;
-    be->file = njs_str_value("");
-
-    if (function != NULL && function->native) {
-        ret = njs_builtin_match_native_function(vm, function, &be->name);
-        if (ret == NJS_OK) {
-            return NJS_OK;
-        }
-
-        be->name = njs_entry_native;
-
-        return NJS_OK;
-    }
-
-    code = njs_lookup_code(vm, se->u.pc);
-
-    if (code != NULL) {
-        be->name = code->name;
-
-        if (be->name.length == 0) {
-            be->name = njs_entry_anonymous;
-        }
-
-        be->line = njs_lookup_line(code->lines, se->u.pc - code->start);
-        if (!vm->options.quiet) {
-            be->file = code->file;
-        }
-
-        return NJS_OK;
-    }
-
-    be->name = njs_entry_unknown;
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
-{
-    size_t                 count;
-    njs_chb_t              chain;
-    njs_int_t              ret;
-    njs_uint_t             i;
-    njs_backtrace_entry_t  *be, *prev;
-
-    if (backtrace->items == 0) {
-        return NJS_OK;
-    }
-
-    NJS_CHB_MP_INIT(&chain, njs_vm_memory_pool(vm));
-
-    njs_chb_append_str(&chain, dst);
-    njs_chb_append(&chain, "\n", 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) {
-                njs_chb_sprintf(&chain, 64, "      repeats %uz times\n", count);
-                count = 0;
-            }
-
-            njs_chb_sprintf(&chain, 10 + be->name.length, "    at %V ",
-                            &be->name);
-
-            if (be->line != 0) {
-                njs_chb_sprintf(&chain, 12 + be->file.length,
-                                "(%V:%uD)\n", &be->file, be->line);
-
-            } else {
-                njs_chb_append(&chain, "(native)\n", 9);
-            }
-        }
-
-        prev = be;
-        be++;
-    }
-
-    ret = njs_chb_join(&chain, dst);
-    njs_chb_destroy(&chain);
-
-    return ret;
-}
index 651b6229f15e4d9f46009af1ed456f03addda5ce..4660660ba794fd3d0769b400162d30c9fcc24ba0 100644 (file)
@@ -43,7 +43,7 @@ njs_object_t *njs_error_alloc(njs_vm_t *vm, njs_object_t *proto,
 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);
+void njs_error_stack_attach(njs_vm_t *vm, njs_value_t value);
 
 
 extern const njs_object_type_init_t  njs_error_type_init;
index 7d4c0a39d3401aa5169baf27eb29b12be5db121a..2a7f7fd574818ff49b280d8cbe79d18a23359542 100644 (file)
@@ -738,7 +738,7 @@ njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
 
         case NJS_OBJECT_VALUE:
             ov = (njs_object_value_t *) proto;
-            if (!njs_is_string(&ov->value)) {
+            if (!njs_is_string(&ov->value) || proto->error_data) {
                 break;
             }
 
index 348316a14f1d2791bd8d823ebee31b5c56a726be..5220faeb91bf3fe5359f778699a48faa00144216 100644 (file)
@@ -14,7 +14,6 @@ static njs_int_t njs_vm_protos_init(njs_vm_t *vm, njs_value_t *global);
 const njs_str_t  njs_entry_empty =          njs_str("");
 const njs_str_t  njs_entry_main =           njs_str("main");
 const njs_str_t  njs_entry_module =         njs_str("module");
-const njs_str_t  njs_entry_native =         njs_str("native");
 const njs_str_t  njs_entry_unknown =        njs_str("unknown");
 const njs_str_t  njs_entry_anonymous =      njs_str("anonymous");
 
index 46f566e3c7f404980ecd0a15dc435350ef7ba360..53c14c9e6169e96309d1d0c74a0d0d86f5dba50a 100644 (file)
@@ -260,7 +260,6 @@ void njs_flathsh_proto_free(void *data, void *p, size_t size);
 extern const njs_str_t    njs_entry_empty;
 extern const njs_str_t    njs_entry_main;
 extern const njs_str_t    njs_entry_module;
-extern const njs_str_t    njs_entry_native;
 extern const njs_str_t    njs_entry_unknown;
 extern const njs_str_t    njs_entry_anonymous;
 
index 626f85cff1f83e0870c94d9b2a911c380f230124..81d3bdd72a2e4f836c4a69960e78da7e883296bb 100644 (file)
@@ -1854,7 +1854,7 @@ error:
     if (njs_is_error(&vm->exception)) {
         vm->active_frame->native.pc = pc;
 
-        (void) njs_error_stack_attach(vm, vm->exception);
+        njs_error_stack_attach(vm, vm->exception);
     }
 
     for ( ;; ) {
index a89d801615b0e186ed07939896d09121094ae40e..9de4eb4186515d182008566eac60f86aaedaec59 100644 (file)
@@ -21514,6 +21514,9 @@ static njs_unit_test_t  njs_shell_test[] =
 
 static njs_unit_test_t  njs_backtraces_test[] =
 {
+    { njs_str("var e = new Error(); e[0] = 1"),
+      njs_str("1") },
+
     { njs_str("function ff(o) {return o.a.a};"
               "function f(o) {return ff(o)};"
               "f({})"),
@@ -21673,6 +21676,9 @@ static njs_unit_test_t  njs_backtraces_test[] =
               "    at TypedArray.prototype.every (native)\n"
               "    at main (:1)\n") },
 
+    { njs_str("var e = new Error('oops'); e.stack = 123; e.stack"),
+      njs_str("123") },
+
     /* line numbers */
 
     { njs_str("/**/(function(){throw Error();})()"),