diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index a11023a9..0d2a7040 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -32,6 +32,7 @@ const ScriptManager = @import("../ScriptManager.zig"); const Allocator = std.mem.Allocator; const PersistentObject = v8.Persistent(v8.Object); +const PersistentValue = v8.Persistent(v8.Value); const PersistentModule = v8.Persistent(v8.Module); const PersistentPromise = v8.Persistent(v8.Promise); const PersistentFunction = v8.Persistent(v8.Function); @@ -85,6 +86,9 @@ identity_map: std.AutoHashMapUnmanaged(usize, PersistentObject) = .empty, // we now simply persist every time persist() is called. js_object_list: std.ArrayListUnmanaged(PersistentObject) = .empty, +// js_value_list tracks persisted js values. +js_value_list: std.ArrayListUnmanaged(PersistentValue) = .empty, + // Various web APIs depend on having a persistent promise resolver. They // require for this PromiseResolver to be valid for a lifetime longer than // the function that resolves/rejects them. @@ -161,6 +165,10 @@ pub fn deinit(self: *Context) void { p.deinit(); } + for (self.js_value_list.items) |*p| { + p.deinit(); + } + for (self.persisted_promise_resolvers.items) |*p| { p.deinit(); } diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index 8cba1688..329fb128 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -23,6 +23,8 @@ const v8 = js.v8; const Allocator = std.mem.Allocator; +const PersistentValue = v8.Persistent(v8.Value); + const Value = @This(); js_val: v8.Value, context: *js.Context, @@ -49,6 +51,16 @@ pub fn fromJson(ctx: *js.Context, json: []const u8) !Value { return Value{ .context = ctx, .js_val = value }; } +pub fn persist(self: Value) !Value { + const js_val = self.js_val; + var context = self.context; + + const persisted = PersistentValue.init(context.isolate, js_val); + try context.js_value_list.append(context.arena, persisted); + + return Value{ .context = context, .js_val = persisted.toValue() }; +} + pub fn toObject(self: Value) js.Object { return .{ .context = self.context, diff --git a/src/browser/tests/net/fetch.html b/src/browser/tests/net/fetch.html index a8159959..2d8c6266 100644 --- a/src/browser/tests/net/fetch.html +++ b/src/browser/tests/net/fetch.html @@ -35,7 +35,9 @@ const json = await response.json(); testing.expectEqual('9000!!!', json.over); - + testing.expectEqual("number", typeof json.updated_at); + testing.expectEqual(1765867200000, json.updated_at); + testing.expectEqual({over: '9000!!!',updated_at:1765867200000}, json); }); diff --git a/src/browser/tests/net/xhr.html b/src/browser/tests/net/xhr.html index 82e9b6d1..dbb55653 100644 --- a/src/browser/tests/net/xhr.html +++ b/src/browser/tests/net/xhr.html @@ -75,6 +75,9 @@ testing.expectEqual(200, req3.status); testing.expectEqual('OK', req3.statusText); testing.expectEqual('9000!!!', req3.response.over); + testing.expectEqual("number", typeof json.updated_at); + testing.expectEqual(1765867200000, json.updated_at); + testing.expectEqual({over: '9000!!!',updated_at:1765867200000}, json); }); diff --git a/src/browser/webapi/net/Response.zig b/src/browser/webapi/net/Response.zig index 3e423691..4995e600 100644 --- a/src/browser/webapi/net/Response.zig +++ b/src/browser/webapi/net/Response.zig @@ -120,15 +120,11 @@ pub fn getText(self: *const Response, page: *Page) !js.Promise { pub fn getJson(self: *Response, page: *Page) !js.Promise { const body = self._body orelse ""; - const value = std.json.parseFromSliceLeaky( - std.json.Value, - page.call_arena, - body, - .{}, - ) catch |err| { + const value = js.Value.fromJson(page.js, body) catch |err| { return page.js.rejectPromise(.{@errorName(err)}); }; - return page.js.resolvePromise(value); + const pvalue = try value.persist(); + return page.js.resolvePromise(pvalue); } pub const JsApi = struct { diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index fc216016..0d903249 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -67,7 +67,7 @@ const ReadyState = enum(u8) { const Response = union(ResponseType) { text: []const u8, - json: std.json.Value, + json: js.Value, document: *Node.Document, }; @@ -254,8 +254,9 @@ pub fn getResponse(self: *XMLHttpRequest, page: *Page) !?Response { const res: Response = switch (self._response_type) { .text => .{ .text = data }, .json => blk: { - const parsed = try std.json.parseFromSliceLeaky(std.json.Value, page.call_arena, data, .{}); - break :blk .{ .json = parsed }; + const value = try js.Value.fromJson(page.js, data); + const pvalue = try value.persist(); + break :blk .{ .json = pvalue }; }, .document => blk: { const document = try page._factory.node(Node.Document{ ._proto = undefined, ._type = .generic }); diff --git a/src/testing.zig b/src/testing.zig index cd615aaf..3bc24a8e 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -516,7 +516,7 @@ fn testHTTPHandler(req: *std.http.Server.Request) !void { } if (std.mem.eql(u8, path, "/xhr/json")) { - return req.respond("{\"over\":\"9000!!!\"}", .{ + return req.respond("{\"over\":\"9000!!!\",\"updated_at\":1765867200000}", .{ .extra_headers = &.{ .{ .name = "Content-Type", .value = "application/json" }, },