mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Merge pull request #1365 from lightpanda-io/try_catch_caught
Try catch caught
This commit is contained in:
@@ -804,9 +804,8 @@ fn _wait(self: *Page, wait_ms: u32) !Session.WaitResult {
|
||||
// it AFTER.
|
||||
const ms_to_next_task = try scheduler.run();
|
||||
|
||||
if (try_catch.hasCaught()) {
|
||||
const msg = (try try_catch.err(self.arena)) orelse "unknown";
|
||||
log.info(.js, "page wait", .{ .err = msg, .src = "scheduler" });
|
||||
if (try_catch.caught(self.call_arena)) |caught| {
|
||||
log.info(.js, "page wait", .{ .caught = caught, .src = "scheduler" });
|
||||
}
|
||||
|
||||
const http_active = http_client.active;
|
||||
@@ -1992,9 +1991,9 @@ pub fn createElementNS(self: *Page, namespace: Element.Namespace, name: []const
|
||||
self._upgrading_element = node;
|
||||
defer self._upgrading_element = prev_upgrading;
|
||||
|
||||
var result: JS.Function.Result = undefined;
|
||||
_ = def.constructor.newInstance(&result) catch |err| {
|
||||
log.warn(.js, "custom element constructor", .{ .name = name, .err = err });
|
||||
var caught: JS.TryCatch.Caught = undefined;
|
||||
_ = def.constructor.newInstance(&caught) catch |err| {
|
||||
log.warn(.js, "custom element constructor", .{ .name = name, .err = err, .caught = caught });
|
||||
return node;
|
||||
};
|
||||
|
||||
|
||||
@@ -834,12 +834,10 @@ pub const Script = struct {
|
||||
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", .{
|
||||
.url = url,
|
||||
.err = msg,
|
||||
.stack = try_catch.stack(page.call_arena) catch null,
|
||||
.line = try_catch.sourceLineNumber() orelse 0,
|
||||
.caught = caught,
|
||||
.cacheable = cacheable,
|
||||
});
|
||||
|
||||
@@ -859,13 +857,12 @@ pub const Script = struct {
|
||||
return;
|
||||
};
|
||||
|
||||
var result: js.Function.Result = undefined;
|
||||
cb.tryCall(void, .{event}, &result) catch {
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
cb.tryCall(void, .{event}, &caught) catch {
|
||||
log.warn(.js, "script callback", .{
|
||||
.url = self.url,
|
||||
.type = typ,
|
||||
.err = result.exception,
|
||||
.stack = result.stack,
|
||||
.caught = caught,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1386,15 +1386,13 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
|
||||
try_catch.init(self);
|
||||
defer try_catch.deinit();
|
||||
|
||||
break :blk self.module(true, ms.src(), state.specifier, true) catch {
|
||||
const ex = try_catch.exception(self.call_arena) catch |err| @errorName(err) orelse "unknown error";
|
||||
break :blk self.module(true, ms.src(), state.specifier, true) catch |err| {
|
||||
const caught = try_catch.caughtOrError(self.call_arena, err);
|
||||
log.err(.js, "module compilation failed", .{
|
||||
.caught = caught,
|
||||
.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;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
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.
|
||||
// const c_args = @as(?[*]const ?*c.Value, @ptrCast(&.{}));
|
||||
const handle = v8.v8__Function__NewInstance(self.handle, ctx.handle, 0, null) orelse {
|
||||
if (try_catch.hasCaught()) {
|
||||
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 = "???";
|
||||
}
|
||||
caught.* = try_catch.caughtOrError(ctx.call_arena, error.Unknown);
|
||||
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);
|
||||
}
|
||||
|
||||
pub fn tryCall(self: *const Function, comptime T: type, args: anytype, result: *Result) !T {
|
||||
return self.tryCallWithThis(T, self.getThis(), args, result);
|
||||
pub fn tryCall(self: *const Function, comptime T: type, args: anytype, caught: *js.TryCatch.Caught) !T {
|
||||
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;
|
||||
|
||||
try_catch.init(self.ctx);
|
||||
defer try_catch.deinit();
|
||||
|
||||
return self.callWithThis(T, this, args) catch |err| {
|
||||
if (try_catch.hasCaught()) {
|
||||
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);
|
||||
}
|
||||
caught.* = try_catch.caughtOrError(self.ctx.call_arena, 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>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
@@ -36,53 +36,72 @@ pub fn hasCaught(self: TryCatch) bool {
|
||||
return v8.v8__TryCatch__HasCaught(&self.handle);
|
||||
}
|
||||
|
||||
// the caller needs to deinit the string returned
|
||||
pub fn exception(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
||||
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) {
|
||||
pub fn caught(self: TryCatch, allocator: Allocator) ?Caught {
|
||||
if (!self.hasCaught()) {
|
||||
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
|
||||
// or just the exception message
|
||||
// - in Debug mode return the stack if available
|
||||
// - otherwise return the exception if available
|
||||
// the caller needs to deinit the string returned
|
||||
pub fn err(self: TryCatch, allocator: Allocator) !?[]const u8 {
|
||||
if (comptime @import("builtin").mode == .Debug) {
|
||||
if (try self.stack(allocator)) |msg| {
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
return try self.exception(allocator);
|
||||
pub fn caughtOrError(self: TryCatch, allocator: Allocator, err: anyerror) Caught {
|
||||
return self.caught(allocator) orelse .{
|
||||
.caught = false,
|
||||
.line = null,
|
||||
.stack = null,
|
||||
.exception = @errorName(err),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *TryCatch) void {
|
||||
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;
|
||||
defer page._upgrading_element = prev_upgrading;
|
||||
|
||||
var result: js.Function.Result = undefined;
|
||||
_ = definition.constructor.newInstance(&result) catch |err| {
|
||||
log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
_ = definition.constructor.newInstance(&caught) catch |err| {
|
||||
log.warn(.js, "custom element upgrade", .{ .name = definition.name, .err = err, .caught = caught });
|
||||
return error.CustomElementUpgradeFailed;
|
||||
};
|
||||
|
||||
|
||||
@@ -245,9 +245,9 @@ pub fn deliverEntries(self: *IntersectionObserver, page: *Page) !void {
|
||||
}
|
||||
|
||||
const entries = try self.takeRecords(page);
|
||||
var result: js.Function.Result = undefined;
|
||||
self._callback.tryCall(void, .{ entries, self }, &result) catch |err| {
|
||||
log.err(.page, "IntsctObserver.deliverEntries", .{ .err = result.exception, .stack = result.stack });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
self._callback.tryCall(void, .{ entries, self }, &caught) catch |err| {
|
||||
log.err(.page, "IntsctObserver.deliverEntries", .{ .err = err, .caught = caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -248,9 +248,9 @@ pub fn deliverRecords(self: *MutationObserver, page: *Page) !void {
|
||||
// Take a copy of the records and clear the list before calling callback
|
||||
// This ensures mutations triggered during the callback go into a fresh list
|
||||
const records = try self.takeRecords(page);
|
||||
var result: js.Function.Result = undefined;
|
||||
self._callback.tryCall(void, .{ records, self }, &result) catch |err| {
|
||||
log.err(.page, "MutObserver.deliverRecords", .{ .err = result.exception, .stack = result.stack });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
self._callback.tryCall(void, .{ records, self }, &caught) catch |err| {
|
||||
log.err(.page, "MutObserver.deliverRecords", .{ .err = err, .caught = caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -151,9 +151,9 @@ pub inline fn hasRecords(self: *const PerformanceObserver) bool {
|
||||
/// Runs the PerformanceObserver's callback with records; emptying it out.
|
||||
pub fn dispatch(self: *PerformanceObserver, page: *Page) !void {
|
||||
const records = try self.takeRecords(page);
|
||||
var result: js.Function.Result = undefined;
|
||||
self._callback.tryCall(void, .{ EntryList{ ._entries = records }, self }, &result) catch |err| {
|
||||
log.err(.page, "PerfObserver.dispatch", .{ .err = result.exception, .stack = result.stack });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
self._callback.tryCall(void, .{ EntryList{ ._entries = records }, self }, &caught) catch |err| {
|
||||
log.err(.page, "PerfObserver.dispatch", .{ .err = err, .caught = caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -189,9 +189,9 @@ pub fn forEach(self: *DOMTokenList, cb_: js.Function, js_this_: ?js.Object, page
|
||||
if (gop.found_existing) {
|
||||
continue;
|
||||
}
|
||||
var result: js.Function.Result = undefined;
|
||||
cb.tryCall(void, .{ token, i, self }, &result) catch {
|
||||
log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "DOMTokenList" });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
cb.tryCall(void, .{ token, i, self }, &caught) catch {
|
||||
log.debug(.js, "forEach callback", .{ .caught = caught, .source = "DOMTokenList" });
|
||||
return;
|
||||
};
|
||||
i += 1;
|
||||
|
||||
@@ -83,9 +83,9 @@ pub fn forEach(self: *NodeList, cb: js.Function, page: *Page) !void {
|
||||
return;
|
||||
}
|
||||
|
||||
var result: js.Function.Result = undefined;
|
||||
cb.tryCall(void, .{ next.value, i, self }, &result) catch {
|
||||
log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "nodelist" });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
cb.tryCall(void, .{ next.value, i, self }, &caught) catch {
|
||||
log.debug(.js, "forEach callback", .{ .caught = caught, .source = "nodelist" });
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -195,9 +195,9 @@ pub fn checkAndAttachBuiltIn(element: *Element, page: *Page) !void {
|
||||
page._upgrading_element = node;
|
||||
defer page._upgrading_element = prev_upgrading;
|
||||
|
||||
var result: js.Function.Result = undefined;
|
||||
_ = definition.constructor.newInstance(&result) catch |err| {
|
||||
log.warn(.js, "custom builtin ctor", .{ .name = is_value, .err = err });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
_ = definition.constructor.newInstance(&caught) catch |err| {
|
||||
log.warn(.js, "custom builtin ctor", .{ .name = is_value, .err = err, .caught = caught });
|
||||
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_;
|
||||
|
||||
for (self._list._entries.items) |entry| {
|
||||
var result: js.Function.Result = undefined;
|
||||
cb.tryCall(void, .{ entry.value.str(), entry.name.str(), self }, &result) catch {
|
||||
log.debug(.js, "forEach callback", .{ .err = result.exception, .stack = result.stack, .source = "headers" });
|
||||
var caught: js.TryCatch.Caught = undefined;
|
||||
cb.tryCall(void, .{ entry.value.str(), entry.name.str(), self }, &caught) catch {
|
||||
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 {
|
||||
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;
|
||||
try writer.print(" {s}=", .{key});
|
||||
try writeString(.logfmt, value, writer);
|
||||
try writeValue(.logfmt, value, writer);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -94,9 +94,8 @@ pub fn run(allocator: Allocator, file: []const u8, session: *lp.Session) !void {
|
||||
_ = session.wait(2000);
|
||||
|
||||
js_context.eval("testing.assertOk()", "testing.assertOk()") catch |err| {
|
||||
const msg = try_catch.err(allocator) catch @errorName(err) orelse "unknown";
|
||||
|
||||
std.debug.print("{s}: test failure\nError: {s}\n", .{ file, msg });
|
||||
const caught = try_catch.caughtOrError(allocator, err);
|
||||
std.debug.print("{s}: test failure\nError: {f}\n", .{ file, caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -125,13 +125,15 @@ fn run(
|
||||
|
||||
// Check the final test status.
|
||||
js_context.eval("report.status", "teststatus") catch |err| {
|
||||
err_out.* = try_catch.err(arena) catch @errorName(err) orelse "unknown";
|
||||
const caught = try_catch.caughtOrError(arena, err);
|
||||
err_out.* = caught.exception;
|
||||
return err;
|
||||
};
|
||||
|
||||
// return the detailed result.
|
||||
const value = js_context.exec("report.log", "report") catch |err| {
|
||||
err_out.* = try_catch.err(arena) catch @errorName(err) orelse "unknown";
|
||||
const caught = try_catch.caughtOrError(arena, err);
|
||||
err_out.* = caught.exception;
|
||||
return err;
|
||||
};
|
||||
|
||||
|
||||
@@ -407,8 +407,8 @@ fn runWebApiTest(test_file: [:0]const u8) !void {
|
||||
test_browser.runMicrotasks();
|
||||
|
||||
js_context.eval("testing.assertOk()", "testing.assertOk()") catch |err| {
|
||||
const msg = try_catch.err(arena_allocator) catch @errorName(err) orelse "unknown";
|
||||
std.debug.print("{s}: test failure\nError: {s}\n", .{ test_file, msg });
|
||||
const caught = try_catch.caughtOrError(arena_allocator, err);
|
||||
std.debug.print("{s}: test failure\nError: {f}\n", .{ test_file, caught });
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user