From: Dmitry Volyntsev Date: Wed, 28 Jan 2026 22:54:29 +0000 (-0800) Subject: Fixed stack attaching by stringifying function addresses in place. X-Git-Tag: 0.9.6~26 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=4edef59f796f3af4601034cbe4adc9c38300123e;p=njs.git Fixed stack attaching by stringifying function addresses in place. This fixed Error stack output on CLI. --- diff --git a/src/njs_error.c b/src/njs_error.c index 57ab477a..e8de835f 100644 --- a/src/njs_error.c +++ b/src/njs_error.c @@ -8,28 +8,6 @@ #include -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; -} diff --git a/src/njs_error.h b/src/njs_error.h index 651b6229..4660660b 100644 --- a/src/njs_error.h +++ b/src/njs_error.h @@ -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; diff --git a/src/njs_value.c b/src/njs_value.c index 7d4c0a39..2a7f7fd5 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -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; } diff --git a/src/njs_vm.c b/src/njs_vm.c index 348316a1..5220faeb 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -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"); diff --git a/src/njs_vm.h b/src/njs_vm.h index 46f566e3..53c14c9e 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -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; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 626f85cf..81d3bdd7 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -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 ( ;; ) { diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c index a89d8016..9de4eb41 100644 --- a/src/test/njs_unit_test.c +++ b/src/test/njs_unit_test.c @@ -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();})()"),