Improve JS value printing

Don't error on JSON.stringify failure (likely caused by circular reference).

In debug mode, try to print [slightly] more meaningful value representation
when default serialization results in [object Object].
This commit is contained in:
Karl Seguin
2025-07-02 11:47:19 +08:00
parent 81fb71b7f7
commit 7a0e7fff13
2 changed files with 51 additions and 5 deletions

View File

@@ -13,8 +13,8 @@
.hash = "tigerbeetle_io-0.0.0-ViLgxpyRBAB5BMfIcj3KMXfbJzwARs9uSl8aRy2OXULd", .hash = "tigerbeetle_io-0.0.0-ViLgxpyRBAB5BMfIcj3KMXfbJzwARs9uSl8aRy2OXULd",
}, },
.v8 = .{ .v8 = .{
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/46ddf8c0a2861a4e5717e6f8d0d81a17e42fa0c9.tar.gz", .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/dd087771378ea854452bcb010309fa9ffe5a9cac.tar.gz",
.hash = "v8-0.0.0-xddH6865AwDiDnu-HjMsqm9wXvP9OZOh_4clh_67iQsq", .hash = "v8-0.0.0-xddH66e8AwBL3O_A8yWQYQIyfMbKHFNVQr_NqM6YjU11",
}, },
//.v8 = .{ .path = "../zig-v8-fork" }, //.v8 = .{ .path = "../zig-v8-fork" },
//.tigerbeetle_io = .{ .path = "../tigerbeetle-io" }, //.tigerbeetle_io = .{ .path = "../tigerbeetle-io" },

View File

@@ -3392,8 +3392,8 @@ const TaggedAnyOpaque = struct {
fn valueToDetailString(arena: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 { fn valueToDetailString(arena: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 {
var str: ?v8.String = null; var str: ?v8.String = null;
if (value.isObject() and !value.isFunction()) { if (value.isObject() and !value.isFunction()) blk: {
str = try v8.Json.stringify(v8_context, value, null); str = v8.Json.stringify(v8_context, value, null) catch break :blk;
if (str.?.lenUtf8(isolate) == 2) { if (str.?.lenUtf8(isolate) == 2) {
// {} isn't useful, null this so that we can get the toDetailString // {} isn't useful, null this so that we can get the toDetailString
@@ -3406,10 +3406,24 @@ fn valueToDetailString(arena: Allocator, value: v8.Value, isolate: v8.Isolate, v
str = try value.toDetailString(v8_context); str = try value.toDetailString(v8_context);
} }
return jsStringToZig(arena, str.?, isolate); const s = try jsStringToZig(arena, str.?, isolate);
if (comptime builtin.mode == .Debug) {
if (std.mem.eql(u8, s, "[object Object]")) {
if (debugValueToString(arena, value.castTo(v8.Object), isolate, v8_context)) |ds| {
return ds;
} else |err| {
log.err(.js, "debug serialize value", .{.err = err});
}
}
}
return s;
} }
fn valueToString(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 { fn valueToString(allocator: Allocator, value: v8.Value, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 {
if (value.isSymbol()) {
// symbol's can't be converted to a string
return allocator.dupe(u8, "$Symbol");
}
const str = try value.toString(v8_context); const str = try value.toString(v8_context);
return jsStringToZig(allocator, str, isolate); return jsStringToZig(allocator, str, isolate);
} }
@@ -3431,6 +3445,38 @@ fn jsStringToZig(allocator: Allocator, str: v8.String, isolate: v8.Isolate) ![]u
return buf; return buf;
} }
fn debugValueToString(arena: Allocator, js_obj: v8.Object, isolate: v8.Isolate, v8_context: v8.Context) ![]u8 {
if (comptime builtin.mode != .Debug) {
@compileError("debugValue can only be called in debug mode");
}
var arr: std.ArrayListUnmanaged(u8) = .empty;
var writer = arr.writer(arena);
const names_arr = js_obj.getOwnPropertyNames(v8_context);
const names_obj = names_arr.castTo(v8.Object);
const len = names_arr.length();
try writer.writeAll("(JSON.stringify failed, dumping top-level fields)\n");
for (0..len) |i| {
const field_name = try names_obj.getAtIndex(v8_context, @intCast(i));
const field_value = try js_obj.getValue(v8_context, field_name);
const name = try valueToString(arena, field_name, isolate, v8_context);
const value = try valueToString(arena, field_value, isolate, v8_context);
try writer.writeAll(name);
try writer.writeAll(": ");
if (std.mem.indexOfAny(u8, value, &std.ascii.whitespace) == null) {
try writer.writeAll(value);
} else {
try writer.writeByte('"');
try writer.writeAll(value);
try writer.writeByte('"');
}
try writer.writeByte(' ');
}
return arr.items;
}
fn stackForLogs(arena: Allocator, isolate: v8.Isolate) !?[]const u8 { fn stackForLogs(arena: Allocator, isolate: v8.Isolate) !?[]const u8 {
std.debug.assert(builtin.mode == .Debug); std.debug.assert(builtin.mode == .Debug);