diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index 75e2495d..90962415 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -53,6 +53,10 @@ pub fn toString(self: Value, allocator: Allocator) ![]const u8 { return self.context.valueToString(self.js_val, .{ .allocator = allocator }); } +pub fn toBool(self: Value) bool { + return self.js_val.toBool(self.context.isolate); +} + pub fn fromJson(ctx: *js.Context, json: []const u8) !Value { const json_string = v8.String.initUtf8(ctx.isolate, json); const value = try v8.Json.parse(ctx.v8_context, json_string); @@ -86,19 +90,3 @@ pub fn toArray(self: Value) js.Array { .js_arr = self.js_val.castTo(v8.Array), }; } - -// pub const Value = struct { -// value: v8.Value, -// context: *const Context, - -// // the caller needs to deinit the string returned -// pub fn toString(self: Value, allocator: Allocator) ![]const u8 { -// return self.context.valueToString(self.value, .{ .allocator = allocator }); -// } - -// pub fn fromJson(ctx: *Context, json: []const u8) !Value { -// const json_string = v8.String.initUtf8(ctx.isolate, json); -// const value = try v8.Json.parse(ctx.v8_context, json_string); -// return Value{ .context = ctx, .value = value }; -// } -// }; diff --git a/src/browser/webapi/Console.zig b/src/browser/webapi/Console.zig index 5fe4f80a..65efebd6 100644 --- a/src/browser/webapi/Console.zig +++ b/src/browser/webapi/Console.zig @@ -23,10 +23,27 @@ const Page = @import("../Page.zig"); const logger = @import("../../log.zig"); const Console = @This(); -_pad: bool = false, + +_timers: std.StringHashMapUnmanaged(u64) = .{}, +_counts: std.StringHashMapUnmanaged(u64) = .{}, pub const init: Console = .{}; +pub fn trace(_: *const Console, values: []js.Object, page: *Page) !void { + logger.debug(.js, "console.trace", .{ + .stack = page.js.stackTrace() catch "???", + .args = ValueWriter{ .page = page, .values = values }, + }); +} + +pub fn debug(_: *const Console, values: []js.Object, page: *Page) void { + logger.debug(.js, "console.debug", .{ValueWriter{ .page = page, .values = values }}); +} + +pub fn info(_: *const Console, values: []js.Object, page: *Page) void { + logger.info(.js, "console.info", .{ValueWriter{ .page = page, .values = values }}); +} + pub fn log(_: *const Console, values: []js.Object, page: *Page) void { logger.info(.js, "console.log", .{ValueWriter{ .page = page, .values = values }}); } @@ -35,10 +52,82 @@ pub fn warn(_: *const Console, values: []js.Object, page: *Page) void { logger.warn(.js, "console.warn", .{ValueWriter{ .page = page, .values = values }}); } +pub fn clear(_: *const Console) void {} + +pub fn assert(_: *const Console, assertion: js.Value, values: []js.Object, page: *Page) void { + if (assertion.toBool()) { + return; + } + logger.warn(.js, "console.assert", .{ValueWriter{ .page = page, .values = values }}); +} + pub fn @"error"(_: *const Console, values: []js.Object, page: *Page) void { logger.warn(.js, "console.error", .{ValueWriter{ .page = page, .values = values, .include_stack = true }}); } +pub fn count(self: *Console, label_: ?[]const u8, page: *Page) !void { + const label = label_ orelse "default"; + const gop = try self._counts.getOrPut(page.arena, label); + + var current: u64 = 0; + if (gop.found_existing) { + current = gop.value_ptr.*; + } else { + gop.key_ptr.* = try page.dupeString(label); + } + + const c = current + 1; + gop.value_ptr.* = c; + + logger.info(.js, "console.count", .{ .label = label, .count = c }); +} + +pub fn countReset(self: *Console, label_: ?[]const u8) !void { + const label = label_ orelse "default"; + const kv = self._counts.fetchRemove(label) orelse { + logger.info(.js, "console.countReset", .{ .label = label, .err = "invalid label" }); + return; + }; + logger.info(.js, "console.countReset", .{ .label = label, .count = kv.value }); +} + +pub fn time(self: *Console, label_: ?[]const u8, page: *Page) !void { + const label = label_ orelse "default"; + const gop = try self._timers.getOrPut(page.arena, label); + + if (gop.found_existing) { + logger.info(.js, "console.time", .{ .label = label, .err = "duplicate timer" }); + return; + } + gop.key_ptr.* = try page.dupeString(label); + gop.value_ptr.* = timestamp(); +} + +pub fn timeLog(self: *Console, label_: ?[]const u8) void { + const elapsed = timestamp(); + const label = label_ orelse "default"; + const start = self._timers.get(label) orelse { + logger.info(.js, "console.timeLog", .{ .label = label, .err = "invalid timer" }); + return; + }; + logger.info(.js, "console.timeLog", .{ .label = label, .elapsed = elapsed - start }); +} + +pub fn timeEnd(self: *Console, label_: ?[]const u8) void { + const elapsed = timestamp(); + const label = label_ orelse "default"; + const kv = self._timers.fetchRemove(label) orelse { + logger.info(.js, "console.timeEnd", .{ .label = label, .err = "invalid timer" }); + return; + }; + + logger.info(.js, "console.timeEnd", .{ .label = label, .elapsed = elapsed - kv.value }); +} + +fn timestamp() u64 { + return @import("../../datetime.zig").timestamp(.monotonic); +} + const ValueWriter = struct { page: *Page, values: []js.Object, @@ -81,7 +170,18 @@ pub const JsApi = struct { pub const empty_with_no_proto = true; }; + pub const trace = bridge.function(Console.trace, .{}); + pub const debug = bridge.function(Console.debug, .{}); + pub const info = bridge.function(Console.info, .{}); pub const log = bridge.function(Console.log, .{}); pub const warn = bridge.function(Console.warn, .{}); + pub const clear = bridge.function(Console.clear, .{}); + pub const assert = bridge.function(Console.assert, .{}); pub const @"error" = bridge.function(Console.@"error", .{}); + pub const exception = bridge.function(Console.@"error", .{}); + pub const count = bridge.function(Console.count, .{}); + pub const countReset = bridge.function(Console.countReset, .{}); + pub const time = bridge.function(Console.time, .{}); + pub const timeLog = bridge.function(Console.timeLog, .{}); + pub const timeEnd = bridge.function(Console.timeEnd, .{}); };