mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
Improve ergonomics of try catch (and Function's tryCall)
It now returns a Caught struct which contains all information. The Caught struct can be logged directly, providing more consistent logs for caught errors.
This commit is contained in:
@@ -804,9 +804,8 @@ fn _wait(self: *Page, wait_ms: u32) !Session.WaitResult {
|
|||||||
// it AFTER.
|
// it AFTER.
|
||||||
const ms_to_next_task = try scheduler.run();
|
const ms_to_next_task = try scheduler.run();
|
||||||
|
|
||||||
if (try_catch.hasCaught()) {
|
if (try_catch.caught(self.call_arena)) |caught| {
|
||||||
const msg = (try try_catch.err(self.arena)) orelse "unknown";
|
log.info(.js, "page wait", .{ .caught = caught, .src = "scheduler" });
|
||||||
log.info(.js, "page wait", .{ .err = msg, .src = "scheduler" });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const http_active = http_client.active;
|
const http_active = http_client.active;
|
||||||
@@ -1992,9 +1991,9 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
|
|||||||
self._upgrading_element = node;
|
self._upgrading_element = node;
|
||||||
defer self._upgrading_element = prev_upgrading;
|
defer self._upgrading_element = prev_upgrading;
|
||||||
|
|
||||||
var result: JS.Function.Result = undefined;
|
var caught: JS.TryCatch.Caught = undefined;
|
||||||
_ = def.constructor.newInstance(&result) catch |err| {
|
_ = def.constructor.newInstance(&caught) catch |err| {
|
||||||
log.warn(.js, "custom element constructor", .{ .name = name, .err = err });
|
log.warn(.js, "custom element constructor", .{ .name = name, .err = err, .caught = caught });
|
||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -834,12 +834,10 @@ pub const Script = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msg = try_catch.err(page.arena) catch |err| @errorName(err) orelse "unknown";
|
const caught = try_catch.caughtOrError(page.call_arena, error.Unknown);
|
||||||
log.warn(.js, "eval script", .{
|
log.warn(.js, "eval script", .{
|
||||||
.url = url,
|
.url = url,
|
||||||
.err = msg,
|
.caught = caught,
|
||||||
.stack = try_catch.stack(page.call_arena) catch null,
|
|
||||||
.line = try_catch.sourceLineNumber() orelse 0,
|
|
||||||
.cacheable = cacheable,
|
.cacheable = cacheable,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -859,13 +857,12 @@ pub const Script = struct {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
var result: js.Function.Result = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
cb.tryCall(void, .{event}, &result) catch {
|
cb.tryCall(void, .{event}, &caught) catch {
|
||||||
log.warn(.js, "script callback", .{
|
log.warn(.js, "script callback", .{
|
||||||
.url = self.url,
|
.url = self.url,
|
||||||
.type = typ,
|
.type = typ,
|
||||||
.err = result.exception,
|
.caught = caught,
|
||||||
.stack = result.stack,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1386,15 +1386,13 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
|
|||||||
try_catch.init(self);
|
try_catch.init(self);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
break :blk self.module(true, ms.src(), state.specifier, true) catch {
|
break :blk self.module(true, ms.src(), state.specifier, true) catch |err| {
|
||||||
const ex = try_catch.exception(self.call_arena) catch |err| @errorName(err) orelse "unknown error";
|
const caught = try_catch.caughtOrError(self.call_arena, err);
|
||||||
log.err(.js, "module compilation failed", .{
|
log.err(.js, "module compilation failed", .{
|
||||||
|
.caught = caught,
|
||||||
.specifier = state.specifier,
|
.specifier = state.specifier,
|
||||||
.exception = ex,
|
|
||||||
.stack = try_catch.stack(self.call_arena) catch null,
|
|
||||||
.line = try_catch.sourceLineNumber() orelse 0,
|
|
||||||
});
|
});
|
||||||
_ = state.resolver.reject("dynamic compilation failure", self.newString(ex));
|
_ = state.resolver.reject("dynamic compilation failure", self.newString(caught.exception orelse ""));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ pub fn withThis(self: *const Function, value: anytype) !Function {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newInstance(self: *const Function, result: *Result) !js.Object {
|
pub fn newInstance(self: *const Function, caught: *js.TryCatch.Caught) !js.Object {
|
||||||
const ctx = self.ctx;
|
const ctx = self.ctx;
|
||||||
|
|
||||||
var try_catch: js.TryCatch = undefined;
|
var try_catch: js.TryCatch = undefined;
|
||||||
@@ -60,14 +60,7 @@ pub fn newInstance(self: *const Function, result: *Result) !js.Object {
|
|||||||
// This creates a new instance using this Function as a constructor.
|
// This creates a new instance using this Function as a constructor.
|
||||||
// const c_args = @as(?[*]const ?*c.Value, @ptrCast(&.{}));
|
// const c_args = @as(?[*]const ?*c.Value, @ptrCast(&.{}));
|
||||||
const handle = v8.v8__Function__NewInstance(self.handle, ctx.handle, 0, null) orelse {
|
const handle = v8.v8__Function__NewInstance(self.handle, ctx.handle, 0, null) orelse {
|
||||||
if (try_catch.hasCaught()) {
|
caught.* = try_catch.caughtOrError(ctx.call_arena, error.Unknown);
|
||||||
const allocator = ctx.call_arena;
|
|
||||||
result.stack = try_catch.stack(allocator) catch null;
|
|
||||||
result.exception = (try_catch.exception(allocator) catch "???") orelse "???";
|
|
||||||
} else {
|
|
||||||
result.stack = null;
|
|
||||||
result.exception = "???";
|
|
||||||
}
|
|
||||||
return error.JsConstructorFailed;
|
return error.JsConstructorFailed;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -81,25 +74,18 @@ pub fn call(self: *const Function, comptime T: type, args: anytype) !T {
|
|||||||
return self.callWithThis(T, self.getThis(), args);
|
return self.callWithThis(T, self.getThis(), args);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tryCall(self: *const Function, comptime T: type, args: anytype, result: *Result) !T {
|
pub fn tryCall(self: *const Function, comptime T: type, args: anytype, caught: *js.TryCatch.Caught) !T {
|
||||||
return self.tryCallWithThis(T, self.getThis(), args, result);
|
return self.tryCallWithThis(T, self.getThis(), args, caught);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, result: *Result) !T {
|
pub fn tryCallWithThis(self: *const Function, comptime T: type, this: anytype, args: anytype, caught: *js.TryCatch.Caught) !T {
|
||||||
var try_catch: js.TryCatch = undefined;
|
var try_catch: js.TryCatch = undefined;
|
||||||
|
|
||||||
try_catch.init(self.ctx);
|
try_catch.init(self.ctx);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
return self.callWithThis(T, this, args) catch |err| {
|
return self.callWithThis(T, this, args) catch |err| {
|
||||||
if (try_catch.hasCaught()) {
|
caught.* = try_catch.caughtOrError(self.ctx.call_arena, err);
|
||||||
const allocator = self.ctx.call_arena;
|
|
||||||
result.stack = try_catch.stack(allocator) catch null;
|
|
||||||
result.exception = (try_catch.exception(allocator) catch @errorName(err)) orelse @errorName(err);
|
|
||||||
} else {
|
|
||||||
result.stack = null;
|
|
||||||
result.exception = @errorName(err);
|
|
||||||
}
|
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
// Copyright (C) 2023-2026 Lightpanda (Selecy SAS)
|
||||||
//
|
//
|
||||||
// Francis Bouvier <francis@lightpanda.io>
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
// Pierre Tachoire <pierre@lightpanda.io>
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
@@ -36,53 +36,72 @@ pub fn hasCaught(self: TryCatch) bool {
|
|||||||
return v8.v8__TryCatch__HasCaught(&self.handle);
|
return v8.v8__TryCatch__HasCaught(&self.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller needs to deinit the string returned
|
pub fn caught(self: TryCatch, allocator: Allocator) ?Caught {
|
||||||
pub fn exception(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
if (!self.hasCaught()) {
|
||||||
const msg_value = v8.v8__TryCatch__Exception(&self.handle) orelse return null;
|
|
||||||
const msg = js.Value{ .ctx = self.ctx, .handle = msg_value };
|
|
||||||
return try self.ctx.valueToString(msg, .{ .allocator = allocator });
|
|
||||||
}
|
|
||||||
|
|
||||||
// the caller needs to deinit the string returned
|
|
||||||
pub fn stack(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
|
||||||
const ctx = self.ctx;
|
|
||||||
const s_value = v8.v8__TryCatch__StackTrace(&self.handle, ctx.handle) orelse return null;
|
|
||||||
const s = js.Value{ .ctx = ctx, .handle = s_value };
|
|
||||||
return try ctx.valueToString(s, .{ .allocator = allocator });
|
|
||||||
}
|
|
||||||
|
|
||||||
// the caller needs to deinit the string returned
|
|
||||||
pub fn sourceLine(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
|
||||||
const ctx = self.ctx;
|
|
||||||
const msg = v8.v8__TryCatch__Message(&self.handle) orelse return null;
|
|
||||||
const source_line_handle = v8.v8__Message__GetSourceLine(msg, ctx.handle) orelse return null;
|
|
||||||
return try ctx.jsStringToZig(source_line_handle, .{ .allocator = allocator });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sourceLineNumber(self: TryCatch) ?u32 {
|
|
||||||
const ctx = self.ctx;
|
|
||||||
const msg = v8.v8__TryCatch__Message(&self.handle) orelse return null;
|
|
||||||
const line = v8.v8__Message__GetLineNumber(msg, ctx.handle);
|
|
||||||
if (line < 0) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return @intCast(line);
|
|
||||||
|
const ctx = self.ctx;
|
||||||
|
|
||||||
|
var hs: js.HandleScope = undefined;
|
||||||
|
hs.init(ctx.isolate);
|
||||||
|
defer hs.deinit();
|
||||||
|
|
||||||
|
const line: ?u32 = blk: {
|
||||||
|
const handle = v8.v8__TryCatch__Message(&self.handle) orelse return null;
|
||||||
|
const l = v8.v8__Message__GetLineNumber(handle, ctx.handle);
|
||||||
|
break :blk if (l < 0) null else @intCast(l);
|
||||||
|
};
|
||||||
|
|
||||||
|
const exception: ?[]const u8 = blk: {
|
||||||
|
const handle = v8.v8__TryCatch__Exception(&self.handle) orelse break :blk null;
|
||||||
|
break :blk ctx.valueToString(.{ .ctx = ctx, .handle = handle }, .{ .allocator = allocator }) catch |err| @errorName(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stack: ?[]const u8 = blk: {
|
||||||
|
const handle = v8.v8__TryCatch__StackTrace(&self.handle, ctx.handle) orelse break :blk null;
|
||||||
|
break :blk ctx.valueToString(.{ .ctx = ctx, .handle = handle }, .{ .allocator = allocator }) catch |err| @errorName(err);
|
||||||
|
};
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.line = line,
|
||||||
|
.stack = stack,
|
||||||
|
.caught = true,
|
||||||
|
.exception = exception,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// a shorthand method to return either the entire stack message
|
pub fn caughtOrError(self: TryCatch, allocator: Allocator, err: anyerror) Caught {
|
||||||
// or just the exception message
|
return self.caught(allocator) orelse .{
|
||||||
// - in Debug mode return the stack if available
|
.caught = false,
|
||||||
// - otherwise return the exception if available
|
.line = null,
|
||||||
// the caller needs to deinit the string returned
|
.stack = null,
|
||||||
pub fn err(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
.exception = @errorName(err),
|
||||||
if (comptime @import("builtin").mode == .Debug) {
|
};
|
||||||
if (try self.stack(allocator)) |msg| {
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return try self.exception(allocator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *TryCatch) void {
|
pub fn deinit(self: *TryCatch) void {
|
||||||
v8.v8__TryCatch__DESTRUCT(&self.handle);
|
v8.v8__TryCatch__DESTRUCT(&self.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const Caught = struct {
|
||||||
|
line: ?u32,
|
||||||
|
caught: bool,
|
||||||
|
stack: ?[]const u8,
|
||||||
|
exception: ?[]const u8,
|
||||||
|
|
||||||
|
pub fn format(self: Caught, writer: *std.Io.Writer) !void {
|
||||||
|
const separator = @import("../../log.zig").separator();
|
||||||
|
try writer.print("{s}exception: {?s}", .{ separator, self.exception });
|
||||||
|
try writer.print("{s}stack: {?s}", .{ separator, self.stack });
|
||||||
|
try writer.print("{s}line: {?d}", .{ separator, self.line });
|
||||||
|
try writer.print("{s}caught: {any}", .{ separator, self.caught });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn logFmt(self: Caught, comptime prefix: []const u8, writer: anytype) !void {
|
||||||
|
try writer.write(prefix ++ ".exception", self.exception orelse "???");
|
||||||
|
try writer.write(prefix ++ ".stack", self.stack orelse "na");
|
||||||
|
try writer.write(prefix ++ ".line", self.line);
|
||||||
|
try writer.write(prefix ++ ".caught", self.caught);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@@ -174,9 +174,9 @@ pub fn upgradeCustomElement(custom: *Custom, definition: *CustomElementDefinitio
|
|||||||
page._upgrading_element = node;
|
page._upgrading_element = node;
|
||||||
defer page._upgrading_element = prev_upgrading;
|
defer page._upgrading_element = prev_upgrading;
|
||||||
|
|
||||||
var result: js.Function.Result = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
_ = definition.constructor.newInstance(&result) catch |err| {
|
_ = definition.constructor.newInstance(&caught) catch |err| {
|
||||||
log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err });
|
log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err, .caught = caught });
|
||||||
return error.CustomElementUpgradeFailed;
|
return error.CustomElementUpgradeFailed;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -189,9 +189,9 @@ pub fn forEach(self: *DOMTokenList, cb_: js.Function, js_this_: ?js.Object, page
|
|||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var result: js.Function.Result = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
cb.tryCall(void, .{ token, i, self }, &result) catch {
|
cb.tryCall(void, .{ token, i, self }, &caught) catch {
|
||||||
log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "DOMTokenList" });
|
log.debug(.js, "forEach callback", .{ .caught = caught, .source = "DOMTokenList" });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
|
|||||||
@@ -83,9 +83,9 @@ pub fn forEach(self: *NodeList, cb: js.Function, page: *Page) !void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result: js.Function.Result = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
cb.tryCall(void, .{ next.value, i, self }, &result) catch {
|
cb.tryCall(void, .{ next.value, i, self }, &caught) catch {
|
||||||
log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "nodelist" });
|
log.debug(.js, "forEach callback", .{ .caught = caught, .source = "nodelist" });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -195,9 +195,9 @@ pub fn checkAndAttachBuiltIn(element: *Element, page: *Page) !void {
|
|||||||
page._upgrading_element = node;
|
page._upgrading_element = node;
|
||||||
defer page._upgrading_element = prev_upgrading;
|
defer page._upgrading_element = prev_upgrading;
|
||||||
|
|
||||||
var result: js.Function.Result = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
_ = definition.constructor.newInstance(&result) catch |err| {
|
_ = definition.constructor.newInstance(&caught) catch |err| {
|
||||||
log.warn(.js, "custom builtin ctor", .{ .name = is_value, .err = err });
|
log.warn(.js, "custom builtin ctor", .{ .name = is_value, .err = err, .caught = caught });
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,9 +78,9 @@ pub fn forEach(self: *Headers, cb_: js.Function, js_this_: ?js.Object) !void {
|
|||||||
const cb = if (js_this_) |js_this| try cb_.withThis(js_this) else cb_;
|
const cb = if (js_this_) |js_this| try cb_.withThis(js_this) else cb_;
|
||||||
|
|
||||||
for (self._list._entries.items) |entry| {
|
for (self._list._entries.items) |entry| {
|
||||||
var result: js.Function.Result = undefined;
|
var caught: js.TryCatch.Caught = undefined;
|
||||||
cb.tryCall(void, .{ entry.value.str(), entry.name.str(), self }, &result) catch {
|
cb.tryCall(void, .{ entry.value.str(), entry.name.str(), self }, &caught) catch {
|
||||||
log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "headers" });
|
log.debug(.js, "forEach callback", .{ .caught = caught, .source = "headers" });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,10 +333,10 @@ fn writeString(comptime format: Format, value: []const u8, writer: *std.Io.Write
|
|||||||
pub const LogFormatWriter = struct {
|
pub const LogFormatWriter = struct {
|
||||||
writer: *std.Io.Writer,
|
writer: *std.Io.Writer,
|
||||||
|
|
||||||
pub fn write(self: LogFormatWriter, key: []const u8, value: []const u8) !void {
|
pub fn write(self: LogFormatWriter, key: []const u8, value: anytype) !void {
|
||||||
const writer = self.writer;
|
const writer = self.writer;
|
||||||
try writer.print(" {s}=", .{key});
|
try writer.print(" {s}=", .{key});
|
||||||
try writeString(.logfmt, value, writer);
|
try writeValue(.logfmt, value, writer);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -407,8 +407,8 @@ fn runWebApiTest(test_file: [:0]const u8) !void {
|
|||||||
test_browser.runMicrotasks();
|
test_browser.runMicrotasks();
|
||||||
|
|
||||||
js_context.eval("testing.assertOk()", "testing.assertOk()") catch |err| {
|
js_context.eval("testing.assertOk()", "testing.assertOk()") catch |err| {
|
||||||
const msg = try_catch.err(arena_allocator) catch @errorName(err) orelse "unknown";
|
const caught = try_catch.caughtOrError(arena_allocator, err);
|
||||||
std.debug.print("{s}: test failure\nError: {s}\n", .{ test_file, msg });
|
std.debug.print("{s}: test failure\nError: {f}\n", .{ test_file, caught });
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user