diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 26d12af1..d839faef 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -392,13 +392,27 @@ fn evaluateModule(self: *Context, comptime want_result: bool, mod: js.Module, ur // Some module-loading errors aren't handled by TryCatch. We need to // get the error from the module itself. + const exception = mod.getException(); const message = blk: { - const e = mod.getException().toString() catch break :blk "???"; + const e = exception.toString() catch break :blk "???"; break :blk e.toSlice() catch "???"; }; + const stack = blk: { + if (comptime IS_DEBUG == false) { + // SetCaptureStackTraceForUncaughtExceptions is only set in Debug + break :blk ""; + } + + const stack_handle = v8.v8__Exception__GetStackTrace(exception.handle) orelse break :blk ""; + var buf = std.Io.Writer.Allocating.init(self.call_arena); + js.writeStackTrace(self.isolate.handle, stack_handle, &buf.writer) catch break :blk "???"; + break :blk buf.written(); + }; + log.warn(.js, "evaluate module", .{ - .message = message, + .stack = stack, .specifier = url, + .message = message, }); return error.EvaluationError; }; diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 7635e9a2..4709f510 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -135,6 +135,10 @@ pub fn init(app: *App, opts: InitOpts) !Env { v8.v8__Isolate__SetFatalErrorHandler(isolate_handle, fatalCallback); v8.v8__Isolate__SetOOMErrorHandler(isolate_handle, oomCallback); + if (comptime IS_DEBUG) { + v8.v8__Isolate__SetCaptureStackTraceForUncaughtExceptions(isolate_handle, true, 64); + } + isolate.enter(); errdefer isolate.exit(); diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 900b0b59..410aabe9 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -1342,30 +1342,19 @@ fn finalizerPtrGetter(comptime T: type, comptime FT: type) *const fn (*T) *FT { } pub fn stackTrace(self: *const Local) !?[]const u8 { - const isolate = self.isolate; + const isolate = self.isolate.handle; + const stack_handle = v8.v8__StackTrace__CurrentStackTrace__STATIC(isolate, 30) orelse return null; + const separator = log.separator(); - var buf: std.ArrayList(u8) = .empty; - var writer = buf.writer(self.call_arena); - - const stack_trace_handle = v8.v8__StackTrace__CurrentStackTrace__STATIC(isolate.handle, 30).?; - const frame_count = v8.v8__StackTrace__GetFrameCount(stack_trace_handle); - - if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate.handle)) |script| { + var buf = std.Io.Writer.Allocating.init(self.call_arena); + if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate)) |script| { const stack = js.String{ .local = self, .handle = script }; - try writer.print("{s}<{f}>", .{ separator, stack }); + try buf.writer.print("{s}<{f}>", .{ separator, stack }); } - for (0..@intCast(frame_count)) |i| { - const frame_handle = v8.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?; - if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| { - const script = js.String{ .local = self, .handle = name }; - try writer.print("{s}{f}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) }); - } else { - try writer.print("{s}:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) }); - } - } - return buf.items; + try js.writeStackTrace(isolate, stack_handle, &buf.writer); + return buf.written(); } // == Promise Helpers == diff --git a/src/browser/js/js.zig b/src/browser/js/js.zig index 1d4140fe..d159ddbc 100644 --- a/src/browser/js/js.zig +++ b/src/browser/js/js.zig @@ -17,6 +17,8 @@ // along with this program. If not, see . const std = @import("std"); +const lp = @import("lightpanda"); + pub const v8 = @import("v8").c; const string = @import("../../string.zig"); @@ -417,3 +419,19 @@ pub const FinalizerCallback = struct { page.releaseArena(self.arena); } }; + +pub fn writeStackTrace(isolate: *v8.Isolate, stack_handle: *const v8.StackTrace, writer: *std.Io.Writer) !void { + const separator = lp.log.separator(); + const frame_count = v8.v8__StackTrace__GetFrameCount(stack_handle); + + for (0..@intCast(frame_count)) |i| { + const frame_handle = v8.v8__StackTrace__GetFrame(stack_handle, isolate, @intCast(i)).?; + if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| { + var buf: [1024]u8 = undefined; + const n = v8.v8__String__WriteUtf8(name, isolate, &buf, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); + try writer.print("{s}{s}:{d}", .{ separator, buf[0..n], v8.v8__StackFrame__GetLineNumber(frame_handle) }); + } else { + try writer.print("{s}:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) }); + } + } +}