From 7ff3e785307715565d7f082a844b7fd11279f722 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Tue, 23 Sep 2025 16:44:40 +0900 Subject: [PATCH 01/12] Call `ref()` before copy `ZigStackFrame` --- src/bake/DevServer/ErrorReportRequest.zig | 1 + src/bun.js/VirtualMachine.zig | 1 + src/bun.js/bindings/ZigStackFrame.zig | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/src/bake/DevServer/ErrorReportRequest.zig b/src/bake/DevServer/ErrorReportRequest.zig index 8cca4662e341..8150f75ce7bf 100644 --- a/src/bake/DevServer/ErrorReportRequest.zig +++ b/src/bake/DevServer/ErrorReportRequest.zig @@ -209,6 +209,7 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) ! continue; // skip runtime frames } + frame.ref(); frames.items[i] = frame; i += 1; } diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index 221d0e0b2c82..b6a05a2684df 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -2686,6 +2686,7 @@ pub fn remapZigException( continue; } + frame.ref(); frames[j] = frame; j += 1; } diff --git a/src/bun.js/bindings/ZigStackFrame.zig b/src/bun.js/bindings/ZigStackFrame.zig index 3d9bc442ae69..4086d0448dd4 100644 --- a/src/bun.js/bindings/ZigStackFrame.zig +++ b/src/bun.js/bindings/ZigStackFrame.zig @@ -11,6 +11,11 @@ pub const ZigStackFrame = extern struct { /// This informs formatters whether to display as a blob URL or not remapped: bool = false, + pub fn ref(this: *const ZigStackFrame) void { + this.function_name.ref(); + this.source_url.ref(); + } + pub fn deinit(this: *ZigStackFrame) void { this.function_name.deref(); this.source_url.deref(); From cb4d2f5deae7b4e676e65ebc437d3633f341ece2 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Wed, 24 Sep 2025 10:45:36 +0900 Subject: [PATCH 02/12] Call more `deinit` for `ZigException.holder` --- src/ast/Macro.zig | 2 ++ src/logger.zig | 1 + 2 files changed, 3 insertions(+) diff --git a/src/ast/Macro.zig b/src/ast/Macro.zig index 97863921a40f..89ccb9ee17e3 100644 --- a/src/ast/Macro.zig +++ b/src/ast/Macro.zig @@ -524,6 +524,8 @@ pub const Runner = struct { if (comptime Environment.isDebug) Output.prettyln("[macro] call {s}", .{function_name}); exception_holder = jsc.ZigException.Holder.init(); + defer exception_holder.deinit(macro.vm); + var js_args: []jsc.JSValue = &.{}; var js_processed_args_len: usize = 0; defer { diff --git a/src/logger.zig b/src/logger.zig index bd858b74589c..469860e01e14 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -403,6 +403,7 @@ pub const Msg = struct { pub fn fromJS(allocator: std.mem.Allocator, globalObject: *bun.jsc.JSGlobalObject, file: string, err: bun.jsc.JSValue) bun.JSError!Msg { var zig_exception_holder: bun.jsc.ZigException.Holder = bun.jsc.ZigException.Holder.init(); + defer zig_exception_holder.deinit(globalObject.bunVM()); if (err.toError()) |value| { value.toZigException(globalObject, zig_exception_holder.zigException()); } else { From 512ba81c34a40ccfb7fb7a62df8cb48ae51900a5 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Wed, 24 Sep 2025 10:45:57 +0900 Subject: [PATCH 03/12] Remove unused `toZigException` from `logger.zig` --- src/logger.zig | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/logger.zig b/src/logger.zig index 469860e01e14..c8b8f4c7ec27 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -1336,15 +1336,6 @@ pub const Log = struct { if (needs_newline) _ = try to.write("\n"); } - - pub fn toZigException(this: *const Log, allocator: std.mem.Allocator) *js.ZigException.Holder { - var holder = try allocator.create(js.ZigException.Holder); - holder.* = js.ZigException.Holder.init(); - var zig_exception: *js.ZigException = holder.zigException(); - zig_exception.exception = this; - zig_exception.code = js.JSErrorCode.BundlerError; - return holder; - } }; pub inline fn usize2Loc(loc: usize) Loc { From 307f3a3e7419c0757af7689e4cc08f4d5b130a1d Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 24 Sep 2025 02:09:40 +0000 Subject: [PATCH 04/12] [autofix.ci] apply automated fixes --- src/logger.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/logger.zig b/src/logger.zig index c8b8f4c7ec27..4587615a3f6f 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -1609,7 +1609,6 @@ const Output = bun.Output; const StringBuilder = bun.StringBuilder; const assert = bun.assert; const default_allocator = bun.default_allocator; -const js = bun.jsc; const jsc = bun.jsc; const strings = bun.strings; const Index = bun.ast.Index; From 18dac725df6d6eb151d7a3f153df8ac756a5f62d Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 27 Sep 2025 18:19:07 +0900 Subject: [PATCH 05/12] Remove `frame.ref()` --- src/bake/DevServer/ErrorReportRequest.zig | 1 - src/bun.js/VirtualMachine.zig | 1 - src/bun.js/bindings/ZigStackFrame.zig | 5 ----- 3 files changed, 7 deletions(-) diff --git a/src/bake/DevServer/ErrorReportRequest.zig b/src/bake/DevServer/ErrorReportRequest.zig index 8150f75ce7bf..8cca4662e341 100644 --- a/src/bake/DevServer/ErrorReportRequest.zig +++ b/src/bake/DevServer/ErrorReportRequest.zig @@ -209,7 +209,6 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) ! continue; // skip runtime frames } - frame.ref(); frames.items[i] = frame; i += 1; } diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index b6a05a2684df..221d0e0b2c82 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -2686,7 +2686,6 @@ pub fn remapZigException( continue; } - frame.ref(); frames[j] = frame; j += 1; } diff --git a/src/bun.js/bindings/ZigStackFrame.zig b/src/bun.js/bindings/ZigStackFrame.zig index 4086d0448dd4..3d9bc442ae69 100644 --- a/src/bun.js/bindings/ZigStackFrame.zig +++ b/src/bun.js/bindings/ZigStackFrame.zig @@ -11,11 +11,6 @@ pub const ZigStackFrame = extern struct { /// This informs formatters whether to display as a blob URL or not remapped: bool = false, - pub fn ref(this: *const ZigStackFrame) void { - this.function_name.ref(); - this.source_url.ref(); - } - pub fn deinit(this: *ZigStackFrame) void { this.function_name.deref(); this.source_url.deref(); From 24a075e57b9ceff5dd11a8ba95ddca6b2556a67f Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 27 Sep 2025 18:32:20 +0900 Subject: [PATCH 06/12] Clear extra frames with zero --- src/bun.js/VirtualMachine.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index 221d0e0b2c82..48d6fe76fdf8 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -2689,6 +2689,8 @@ pub fn remapZigException( frames[j] = frame; j += 1; } + + @memset(frames[j..], jsc.ZigStackFrame.Zero); exception.stack.frames_len = @as(u8, @truncate(j)); frames.len = j; } From 679524b19984770067867f2bd26303724f35cdd1 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 27 Sep 2025 18:36:17 +0900 Subject: [PATCH 07/12] Remove extra defer --- src/ast/Macro.zig | 2 -- src/logger.zig | 1 - 2 files changed, 3 deletions(-) diff --git a/src/ast/Macro.zig b/src/ast/Macro.zig index 89ccb9ee17e3..97863921a40f 100644 --- a/src/ast/Macro.zig +++ b/src/ast/Macro.zig @@ -524,8 +524,6 @@ pub const Runner = struct { if (comptime Environment.isDebug) Output.prettyln("[macro] call {s}", .{function_name}); exception_holder = jsc.ZigException.Holder.init(); - defer exception_holder.deinit(macro.vm); - var js_args: []jsc.JSValue = &.{}; var js_processed_args_len: usize = 0; defer { diff --git a/src/logger.zig b/src/logger.zig index 4587615a3f6f..c778cc9cadee 100644 --- a/src/logger.zig +++ b/src/logger.zig @@ -403,7 +403,6 @@ pub const Msg = struct { pub fn fromJS(allocator: std.mem.Allocator, globalObject: *bun.jsc.JSGlobalObject, file: string, err: bun.jsc.JSValue) bun.JSError!Msg { var zig_exception_holder: bun.jsc.ZigException.Holder = bun.jsc.ZigException.Holder.init(); - defer zig_exception_holder.deinit(globalObject.bunVM()); if (err.toError()) |value| { value.toZigException(globalObject, zig_exception_holder.zigException()); } else { From b8581873f453bcdd7d8c40d06313b313839c435e Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Mon, 29 Sep 2025 14:22:02 +0900 Subject: [PATCH 08/12] Use `frames_len` as `frame_count` as possible --- src/bun.js/VirtualMachine.zig | 2 -- src/bun.js/bindings/bindings.cpp | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index 48d6fe76fdf8..221d0e0b2c82 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -2689,8 +2689,6 @@ pub fn remapZigException( frames[j] = frame; j += 1; } - - @memset(frames[j..], jsc.ZigStackFrame.Zero); exception.stack.frames_len = @as(u8, @truncate(j)); frames.len = j; } diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index a8fce2532d6e..adb4e7b77b3e 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -4750,7 +4750,8 @@ static void populateStackTrace(JSC::VM& vm, const WTF::Vector& uint8_t frame_i = 0; size_t stack_frame_i = 0; const size_t total_frame_count = frames.size(); - const uint8_t frame_count = total_frame_count < trace.frames_cap ? total_frame_count : trace.frames_cap; + ASSERT(trace.frames_len <= trace.frames_cap); + const uint8_t frame_count = std::min(static_cast(total_frame_count), trace.frames_len > 0 ? trace.frames_len : trace.frames_cap); while (frame_i < frame_count && stack_frame_i < total_frame_count) { // Skip native frames From 23387fe754bde44ca996f47b16be4b6a6e4c17ae Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 3 Oct 2025 11:33:39 +0900 Subject: [PATCH 09/12] Revert needless change --- src/bun.js/bindings/bindings.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index adb4e7b77b3e..a8fce2532d6e 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -4750,8 +4750,7 @@ static void populateStackTrace(JSC::VM& vm, const WTF::Vector& uint8_t frame_i = 0; size_t stack_frame_i = 0; const size_t total_frame_count = frames.size(); - ASSERT(trace.frames_len <= trace.frames_cap); - const uint8_t frame_count = std::min(static_cast(total_frame_count), trace.frames_len > 0 ? trace.frames_len : trace.frames_cap); + const uint8_t frame_count = total_frame_count < trace.frames_cap ? total_frame_count : trace.frames_cap; while (frame_i < frame_count && stack_frame_i < total_frame_count) { // Skip native frames From 9888b031396ba73a1def1355db59622578df2e7e Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 3 Oct 2025 11:49:06 +0900 Subject: [PATCH 10/12] Add `jsc_stack_frame_index` field to `ZigStackFrame` --- src/bun.js/bindings/ZigStackFrame.zig | 4 ++++ src/bun.js/bindings/headers-handwritten.h | 1 + 2 files changed, 5 insertions(+) diff --git a/src/bun.js/bindings/ZigStackFrame.zig b/src/bun.js/bindings/ZigStackFrame.zig index 3d9bc442ae69..4082b86f25c1 100644 --- a/src/bun.js/bindings/ZigStackFrame.zig +++ b/src/bun.js/bindings/ZigStackFrame.zig @@ -11,6 +11,9 @@ pub const ZigStackFrame = extern struct { /// This informs formatters whether to display as a blob URL or not remapped: bool = false, + /// -1 means not set. + jsc_stack_frame_index: i32 = -1, + pub fn deinit(this: *ZigStackFrame) void { this.function_name.deref(); this.source_url.deref(); @@ -213,6 +216,7 @@ pub const ZigStackFrame = extern struct { .source_url = .empty, .position = .invalid, .is_async = false, + .jsc_stack_frame_index = -1, }; pub fn nameFormatter(this: *const ZigStackFrame, comptime enable_color: bool) NameFormatter { diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 502014086089..82bf2ec0bc7b 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -183,6 +183,7 @@ typedef struct ZigStackFrame { ZigStackFrameCode code_type; bool is_async; bool remapped; + int32_t jsc_stack_frame_index; } ZigStackFrame; typedef struct ZigStackTrace { From 15e54ce9a82daf9d63b3524e8e4ac3b3b997d2ab Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 3 Oct 2025 11:49:42 +0900 Subject: [PATCH 11/12] `populateStackTrace` should loop in `frames_len` when `flags` is `OnlySourceLines` --- src/bun.js/bindings/bindings.cpp | 44 ++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index a8fce2532d6e..581a71044e3f 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -4747,25 +4747,37 @@ class V8StackTraceIterator { static void populateStackTrace(JSC::VM& vm, const WTF::Vector& frames, ZigStackTrace& trace, JSC::JSGlobalObject* globalObject, PopulateStackTraceFlags flags) { - uint8_t frame_i = 0; - size_t stack_frame_i = 0; - const size_t total_frame_count = frames.size(); - const uint8_t frame_count = total_frame_count < trace.frames_cap ? total_frame_count : trace.frames_cap; - - while (frame_i < frame_count && stack_frame_i < total_frame_count) { - // Skip native frames - while (stack_frame_i < total_frame_count && !(frames.at(stack_frame_i).hasLineAndColumnInfo()) && !(frames.at(stack_frame_i).isWasmFrame())) { + if (flags == PopulateStackTraceFlags::OnlyPosition) { + uint8_t frame_i = 0; + size_t stack_frame_i = 0; + const size_t total_frame_count = frames.size(); + const uint8_t frame_count = total_frame_count < trace.frames_cap ? total_frame_count : trace.frames_cap; + + while (frame_i < frame_count && stack_frame_i < total_frame_count) { + // Skip native frames + while (stack_frame_i < total_frame_count && !(frames.at(stack_frame_i).hasLineAndColumnInfo()) && !(frames.at(stack_frame_i).isWasmFrame())) { + stack_frame_i++; + } + if (stack_frame_i >= total_frame_count) + break; + + ZigStackFrame& frame = trace.frames_ptr[frame_i]; + frame.jsc_stack_frame_index = static_cast(stack_frame_i); + populateStackFrame(vm, trace, frames[stack_frame_i], frame, frame_i == 0, &trace.referenced_source_provider, globalObject, flags); stack_frame_i++; + frame_i++; + } + trace.frames_len = frame_i; + } else if (flags == PopulateStackTraceFlags::OnlySourceLines) { + for (uint8_t i = 0; i < trace.frames_len; i++) { + ZigStackFrame& frame = trace.frames_ptr[i]; + // A call with flags set to OnlySourceLines always follows a call with flags set to OnlyPosition, + // so jsc_stack_frame_index is always a valid value here. + ASSERT(frame.jsc_stack_frame_index >= 0); + ASSERT(static_cast(frame.jsc_stack_frame_index) < frames.size()); + populateStackFrame(vm, trace, frames[frame.jsc_stack_frame_index], frame, i == 0, &trace.referenced_source_provider, globalObject, flags); } - if (stack_frame_i >= total_frame_count) - break; - - ZigStackFrame& frame = trace.frames_ptr[frame_i]; - populateStackFrame(vm, trace, frames[stack_frame_i], frame, frame_i == 0, &trace.referenced_source_provider, globalObject, flags); - stack_frame_i++; - frame_i++; } - trace.frames_len = frame_i; } static JSC::JSValue getNonObservable(JSC::VM& vm, JSC::JSGlobalObject* global, JSC::JSObject* obj, const JSC::PropertyName& propertyName) From 0e206dc169543505b634bcb4adbeef8b3e7be9e8 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 3 Oct 2025 15:01:49 +0900 Subject: [PATCH 12/12] Add `ZigStackFrame` constructor --- src/bun.js/bindings/headers-handwritten.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 82bf2ec0bc7b..d3fbf76c75e6 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -184,6 +184,17 @@ typedef struct ZigStackFrame { bool is_async; bool remapped; int32_t jsc_stack_frame_index; + + ZigStackFrame() + : function_name {} + , source_url {} + , position {} + , code_type {} + , is_async(false) + , remapped(false) + , jsc_stack_frame_index(-1) + { + } } ZigStackFrame; typedef struct ZigStackTrace {