diff --git a/src/browser/EventManager.zig b/src/browser/EventManager.zig index fba73b2e..2d866733 100644 --- a/src/browser/EventManager.zig +++ b/src/browser/EventManager.zig @@ -92,7 +92,7 @@ pub const Callback = union(enum) { pub fn register(self: *EventManager, target: *EventTarget, typ: []const u8, callback: Callback, opts: RegisterOptions) !void { if (comptime IS_DEBUG) { - log.debug(.event, "eventManager.register", .{ .type = typ, .capture = opts.capture, .once = opts.once, .target = target }); + log.debug(.event, "eventManager.register", .{ .type = typ, .capture = opts.capture, .once = opts.once, .target = target.toString() }); } // If a signal is provided and already aborted, don't register the listener diff --git a/src/browser/Page.zig b/src/browser/Page.zig index f07e0fb3..29deac0e 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -713,10 +713,6 @@ fn _documentIsComplete(self: *Page) !void { ls.toLocal(maybe_inline_listener), .{ .context = "Page dispatch load events" }, ); - - if (comptime IS_DEBUG) { - log.debug(.page, "load event for element", .{ .element = element }); - } } // `_to_load` can be cleaned here. diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 842df842..13d4fbb2 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -126,6 +126,8 @@ scheduler: Scheduler, // down. shutting_down: bool = false, +unknown_properties: (if (IS_DEBUG) std.StringHashMapUnmanaged(UnknownPropertyStat) else void) = if (IS_DEBUG) .{} else {}, + const ModuleEntry = struct { // Can be null if we're asynchrously loading the module, in // which case resolver_promise cannot be null. @@ -158,6 +160,16 @@ pub fn fromIsolate(isolate: js.Isolate) *Context { } pub fn deinit(self: *Context) void { + if (comptime IS_DEBUG) { + var it = self.unknown_properties.iterator(); + while (it.next()) |kv| { + log.debug(.unknown_prop, "unknown property", .{ + .property = kv.key_ptr.*, + .occurrences = kv.value_ptr.count, + .first_stack = kv.value_ptr.first_stack, + }); + } + } defer self.env.app.arena_pool.release(self.arena); var hs: js.HandleScope = undefined; @@ -1092,3 +1104,8 @@ pub fn stopHeapProfiler(self: *Context) !struct { []const u8, []const u8 } { return .{ allocating, snapshot }; } + +const UnknownPropertyStat = struct { + count: usize, + first_stack: []const u8, +}; diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 4571cff1..163851cf 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -459,11 +459,8 @@ pub fn unknownWindowPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8 .{ "CLOSURE_FLAGS", {} }, }); if (!ignored.has(property)) { - log.debug(.unknown_prop, "unknown global property", .{ - .info = "but the property can exist in pure JS", - .stack = local.stackTrace() catch "???", - .property = property, - }); + const key = std.fmt.bufPrint(&local.ctx.page.buf, "Window:{s}", .{property}) catch return 0; + logUnknownProperty(local, key) catch return 0; } } @@ -514,12 +511,8 @@ pub fn unknownObjectPropertyCallback(comptime JsApi: type) *const fn (?*const v8 const ignored = std.StaticStringMap(void).initComptime(.{}); if (!ignored.has(property)) { - log.debug(.unknown_prop, "unknown object property", .{ - .object = if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi), - .info = "but the property can exist in pure JS", - .stack = local.stackTrace() catch "???", - .property = property, - }); + const key = std.fmt.bufPrint(&local.ctx.page.buf, "{s}:{s}", .{ if (@hasDecl(JsApi.Meta, "name")) JsApi.Meta.name else @typeName(JsApi), property }) catch return 0; + logUnknownProperty(local, key) catch return 0; } // not intercepted return 0; @@ -527,6 +520,20 @@ pub fn unknownObjectPropertyCallback(comptime JsApi: type) *const fn (?*const v8 }.wrap; } +fn logUnknownProperty(local: *const js.Local, key: []const u8) !void { + const ctx = local.ctx; + const gop = try ctx.unknown_properties.getOrPut(ctx.arena, key); + if (gop.found_existing) { + gop.value_ptr.count += 1; + } else { + gop.key_ptr.* = try ctx.arena.dupe(u8, key); + gop.value_ptr.* = .{ + .count = 1, + .first_stack = try ctx.arena.dupe(u8, (try local.stackTrace()) orelse "???"), + }; + } +} + // Given a Type, returns the length of the prototype chain, including self fn prototypeChainLength(comptime T: type) usize { var l: usize = 1; diff --git a/src/browser/webapi/EventTarget.zig b/src/browser/webapi/EventTarget.zig index d1e6c67a..e15dc243 100644 --- a/src/browser/webapi/EventTarget.zig +++ b/src/browser/webapi/EventTarget.zig @@ -137,7 +137,7 @@ pub fn format(self: *EventTarget, writer: *std.Io.Writer) !void { pub fn toString(self: *EventTarget) []const u8 { return switch (self._type) { - .node => |n| return n.className(), + .node => return "[object Node]", .generic => return "[object EventTarget]", .window => return "[object Window]", .xhr => return "[object XMLHttpRequestEventTarget]",