mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-17 08:48:58 +00:00
Re-introduce postAttach
index_get seems to be ~1000x slower than setting the value directly on the v8.Object. There's a lot of information on "v8 fast properties", and values set directly on objects seem to be heavily optimized. Still, I can't imagine indexed properties are always _that_ slow, so I must be doing something wrong. Still, for now, this brings back the original functionality / behavior / perf. Introduce the ability for Zig functions to take a Env.JsObject parameter. While this isn't currently being used, it aligns with bringing back the postAttach / JSObject functionality in main. Moved function *State to the end of the function list (making it consistent with getters and setters). The optional Env.JsObject parameter comes after the optional state. Removed some uncessary arena deinits from a few webapis.
This commit is contained in:
@@ -28,7 +28,7 @@ pub const Comment = struct {
|
|||||||
pub const Self = parser.Comment;
|
pub const Self = parser.Comment;
|
||||||
pub const prototype = *CharacterData;
|
pub const prototype = *CharacterData;
|
||||||
|
|
||||||
pub fn constructor(state: *const SessionState, data: ?[]const u8) !*parser.Comment {
|
pub fn constructor(data: ?[]const u8, state: *const SessionState) !*parser.Comment {
|
||||||
return parser.documentCreateComment(
|
return parser.documentCreateComment(
|
||||||
parser.documentHTMLToDocument(state.document.?),
|
parser.documentHTMLToDocument(state.document.?),
|
||||||
data orelse "",
|
data orelse "",
|
||||||
|
|||||||
@@ -138,16 +138,16 @@ pub const Document = struct {
|
|||||||
// HTMLCollection in zig here.
|
// HTMLCollection in zig here.
|
||||||
pub fn _getElementsByTagName(
|
pub fn _getElementsByTagName(
|
||||||
self: *parser.Document,
|
self: *parser.Document,
|
||||||
state: *SessionState,
|
|
||||||
tag_name: []const u8,
|
tag_name: []const u8,
|
||||||
|
state: *SessionState,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(state.arena, parser.documentToNode(self), tag_name, true);
|
return try collection.HTMLCollectionByTagName(state.arena, parser.documentToNode(self), tag_name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _getElementsByClassName(
|
pub fn _getElementsByClassName(
|
||||||
self: *parser.Document,
|
self: *parser.Document,
|
||||||
state: *SessionState,
|
|
||||||
classNames: []const u8,
|
classNames: []const u8,
|
||||||
|
state: *SessionState,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
const allocator = state.arena;
|
const allocator = state.arena;
|
||||||
return try collection.HTMLCollectionByClassName(allocator, parser.documentToNode(self), classNames, true);
|
return try collection.HTMLCollectionByClassName(allocator, parser.documentToNode(self), classNames, true);
|
||||||
@@ -212,7 +212,7 @@ pub const Document = struct {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelector(self: *parser.Document, state: *SessionState, selector: []const u8) !?ElementUnion {
|
pub fn _querySelector(self: *parser.Document, selector: []const u8, state: *SessionState) !?ElementUnion {
|
||||||
if (selector.len == 0) return null;
|
if (selector.len == 0) return null;
|
||||||
|
|
||||||
const allocator = state.arena;
|
const allocator = state.arena;
|
||||||
@@ -223,7 +223,7 @@ pub const Document = struct {
|
|||||||
return try Element.toInterface(parser.nodeToElement(n.?));
|
return try Element.toInterface(parser.nodeToElement(n.?));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelectorAll(self: *parser.Document, state: *SessionState, selector: []const u8) !NodeList {
|
pub fn _querySelectorAll(self: *parser.Document, selector: []const u8, state: *SessionState) !NodeList {
|
||||||
const allocator = state.arena;
|
const allocator = state.arena;
|
||||||
return css.querySelectorAll(allocator, parser.documentToNode(self), selector);
|
return css.querySelectorAll(allocator, parser.documentToNode(self), selector);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,8 +226,8 @@ pub const Element = struct {
|
|||||||
|
|
||||||
pub fn _getElementsByTagName(
|
pub fn _getElementsByTagName(
|
||||||
self: *parser.Element,
|
self: *parser.Element,
|
||||||
state: *SessionState,
|
|
||||||
tag_name: []const u8,
|
tag_name: []const u8,
|
||||||
|
state: *SessionState,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(
|
return try collection.HTMLCollectionByTagName(
|
||||||
state.arena,
|
state.arena,
|
||||||
@@ -239,8 +239,8 @@ pub const Element = struct {
|
|||||||
|
|
||||||
pub fn _getElementsByClassName(
|
pub fn _getElementsByClassName(
|
||||||
self: *parser.Element,
|
self: *parser.Element,
|
||||||
state: *SessionState,
|
|
||||||
classNames: []const u8,
|
classNames: []const u8,
|
||||||
|
state: *SessionState,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByClassName(
|
return try collection.HTMLCollectionByClassName(
|
||||||
state.arena,
|
state.arena,
|
||||||
@@ -306,7 +306,7 @@ pub const Element = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelector(self: *parser.Element, state: *SessionState, selector: []const u8) !?Union {
|
pub fn _querySelector(self: *parser.Element, selector: []const u8, state: *SessionState) !?Union {
|
||||||
if (selector.len == 0) return null;
|
if (selector.len == 0) return null;
|
||||||
|
|
||||||
const n = try css.querySelector(state.arena, parser.elementToNode(self), selector);
|
const n = try css.querySelector(state.arena, parser.elementToNode(self), selector);
|
||||||
@@ -316,7 +316,7 @@ pub const Element = struct {
|
|||||||
return try toInterface(parser.nodeToElement(n.?));
|
return try toInterface(parser.nodeToElement(n.?));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelectorAll(self: *parser.Element, state: *SessionState, selector: []const u8) !NodeList {
|
pub fn _querySelectorAll(self: *parser.Element, selector: []const u8, state: *SessionState) !NodeList {
|
||||||
return css.querySelectorAll(state.arena, parser.elementToNode(self), selector);
|
return css.querySelectorAll(state.arena, parser.elementToNode(self), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ pub const EventTarget = struct {
|
|||||||
|
|
||||||
pub fn _addEventListener(
|
pub fn _addEventListener(
|
||||||
self: *parser.EventTarget,
|
self: *parser.EventTarget,
|
||||||
state: *SessionState,
|
|
||||||
eventType: []const u8,
|
eventType: []const u8,
|
||||||
cbk: Env.Callback,
|
cbk: Env.Callback,
|
||||||
capture: ?bool,
|
capture: ?bool,
|
||||||
|
state: *SessionState,
|
||||||
// TODO: hanle EventListenerOptions
|
// TODO: hanle EventListenerOptions
|
||||||
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
||||||
) !void {
|
) !void {
|
||||||
@@ -76,10 +76,10 @@ pub const EventTarget = struct {
|
|||||||
|
|
||||||
pub fn _removeEventListener(
|
pub fn _removeEventListener(
|
||||||
self: *parser.EventTarget,
|
self: *parser.EventTarget,
|
||||||
state: *SessionState,
|
|
||||||
eventType: []const u8,
|
eventType: []const u8,
|
||||||
cbk: Env.Callback,
|
cbk: Env.Callback,
|
||||||
capture: ?bool,
|
capture: ?bool,
|
||||||
|
state: *SessionState,
|
||||||
// TODO: hanle EventListenerOptions
|
// TODO: hanle EventListenerOptions
|
||||||
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
// see #https://github.com/lightpanda-io/jsruntime-lib/issues/114
|
||||||
) !void {
|
) !void {
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ const utils = @import("utils.z");
|
|||||||
const Element = @import("element.zig").Element;
|
const Element = @import("element.zig").Element;
|
||||||
const Union = @import("element.zig").Union;
|
const Union = @import("element.zig").Union;
|
||||||
|
|
||||||
|
const JsObject = @import("../env.zig").JsObject;
|
||||||
|
|
||||||
const Walker = @import("walker.zig").Walker;
|
const Walker = @import("walker.zig").Walker;
|
||||||
const WalkerDepthFirst = @import("walker.zig").WalkerDepthFirst;
|
const WalkerDepthFirst = @import("walker.zig").WalkerDepthFirst;
|
||||||
const WalkerChildren = @import("walker.zig").WalkerChildren;
|
const WalkerChildren = @import("walker.zig").WalkerChildren;
|
||||||
@@ -318,10 +320,6 @@ pub const HTMLCollection = struct {
|
|||||||
cur_idx: ?u32 = undefined,
|
cur_idx: ?u32 = undefined,
|
||||||
cur_node: ?*parser.Node = undefined,
|
cur_node: ?*parser.Node = undefined,
|
||||||
|
|
||||||
// array_like_keys is used to keep reference to array like interface implementation.
|
|
||||||
// the collection generates keys string which must be free on deinit.
|
|
||||||
array_like_keys: std.ArrayListUnmanaged([]u8) = .{},
|
|
||||||
|
|
||||||
// start returns the first node to walk on.
|
// start returns the first node to walk on.
|
||||||
fn start(self: HTMLCollection) !?*parser.Node {
|
fn start(self: HTMLCollection) !?*parser.Node {
|
||||||
if (self.root == null) return null;
|
if (self.root == null) return null;
|
||||||
@@ -403,13 +401,6 @@ pub const HTMLCollection = struct {
|
|||||||
return try Element.toInterface(e);
|
return try Element.toInterface(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn indexed_get(self: *HTMLCollection, index: u32, has_value: *bool) !?Union {
|
|
||||||
return (try self._item(index)) orelse {
|
|
||||||
has_value.* = false;
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn _namedItem(self: *const HTMLCollection, name: []const u8) !?Union {
|
pub fn _namedItem(self: *const HTMLCollection, name: []const u8) !?Union {
|
||||||
if (self.root == null) return null;
|
if (self.root == null) return null;
|
||||||
if (name.len == 0) return null;
|
if (name.len == 0) return null;
|
||||||
@@ -441,13 +432,6 @@ pub const HTMLCollection = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn named_get(self: *HTMLCollection, name: []const u8, has_value: *bool) !?Union {
|
|
||||||
return (try self._namedItem(name)) orelse {
|
|
||||||
has_value.* = false;
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn item_name(elt: *parser.Element) !?[]const u8 {
|
fn item_name(elt: *parser.Element) !?[]const u8 {
|
||||||
if (try parser.elementGetAttribute(elt, "id")) |v| {
|
if (try parser.elementGetAttribute(elt, "id")) |v| {
|
||||||
return v;
|
return v;
|
||||||
@@ -459,10 +443,17 @@ pub const HTMLCollection = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *HTMLCollection, alloc: std.mem.Allocator) void {
|
pub fn postAttach(self: *HTMLCollection, js_obj: JsObject) !void {
|
||||||
for (self.array_like_keys_) |k| alloc.free(k);
|
const len = try self.get_length();
|
||||||
self.array_like_keys.deinit(alloc);
|
for (0..len) |i| {
|
||||||
self.matcher.deinit(alloc);
|
const node = try self.item(@intCast(i)) orelse unreachable;
|
||||||
|
const e = @as(*parser.Element, @ptrCast(node));
|
||||||
|
try js_obj.setIndex(@intCast(i), e);
|
||||||
|
|
||||||
|
if (try item_name(e)) |name| {
|
||||||
|
try js_obj.set(name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ pub const DOMImplementation = struct {
|
|||||||
|
|
||||||
pub fn _createDocumentType(
|
pub fn _createDocumentType(
|
||||||
_: *DOMImplementation,
|
_: *DOMImplementation,
|
||||||
state: *SessionState,
|
|
||||||
qname: []const u8,
|
qname: []const u8,
|
||||||
publicId: []const u8,
|
publicId: []const u8,
|
||||||
systemId: []const u8,
|
systemId: []const u8,
|
||||||
|
state: *SessionState,
|
||||||
) !*parser.DocumentType {
|
) !*parser.DocumentType {
|
||||||
const allocator = state.arena;
|
const allocator = state.arena;
|
||||||
const cqname = try allocator.dupeZ(u8, qname);
|
const cqname = try allocator.dupeZ(u8, qname);
|
||||||
@@ -51,10 +51,10 @@ pub const DOMImplementation = struct {
|
|||||||
|
|
||||||
pub fn _createDocument(
|
pub fn _createDocument(
|
||||||
_: *DOMImplementation,
|
_: *DOMImplementation,
|
||||||
state: *SessionState,
|
|
||||||
namespace: ?[]const u8,
|
namespace: ?[]const u8,
|
||||||
qname: ?[]const u8,
|
qname: ?[]const u8,
|
||||||
doctype: ?*parser.DocumentType,
|
doctype: ?*parser.DocumentType,
|
||||||
|
state: *SessionState,
|
||||||
) !*parser.Document {
|
) !*parser.Document {
|
||||||
const allocator = state.arena;
|
const allocator = state.arena;
|
||||||
var cnamespace: ?[:0]const u8 = null;
|
var cnamespace: ?[:0]const u8 = null;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const parser = @import("../netsurf.zig");
|
|||||||
const SessionState = @import("../env.zig").SessionState;
|
const SessionState = @import("../env.zig").SessionState;
|
||||||
|
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
|
const JsObject = @import("../env.zig").JsObject;
|
||||||
const NodeList = @import("nodelist.zig").NodeList;
|
const NodeList = @import("nodelist.zig").NodeList;
|
||||||
|
|
||||||
pub const Interfaces = .{
|
pub const Interfaces = .{
|
||||||
@@ -84,7 +85,7 @@ pub const MutationObserver = struct {
|
|||||||
return opt orelse .{};
|
return opt orelse .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _observe(self: *MutationObserver, state: *SessionState, node: *parser.Node, options: ?MutationObserverInit) !void {
|
pub fn _observe(self: *MutationObserver, node: *parser.Node, options: ?MutationObserverInit, state: *SessionState) !void {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
const o = try arena.create(Observer);
|
const o = try arena.create(Observer);
|
||||||
o.* = .{
|
o.* = .{
|
||||||
@@ -183,6 +184,11 @@ pub const MutationRecords = struct {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
pub fn postAttach(self: *const MutationRecords, js_obj: JsObject) !void {
|
||||||
|
if (self.first) |mr| {
|
||||||
|
try js_obj.set("0", mr);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const MutationRecord = struct {
|
pub const MutationRecord = struct {
|
||||||
|
|||||||
@@ -18,11 +18,6 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const jsruntime = @import("jsruntime");
|
|
||||||
const Case = jsruntime.test_utils.Case;
|
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
|
||||||
const runScript = jsruntime.test_utils.runScript;
|
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const generate = @import("../../runtime/generate.zig");
|
const generate = @import("../../runtime/generate.zig");
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ const std = @import("std");
|
|||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const jsruntime = @import("jsruntime");
|
const JsObject = @import("../env.zig").JsObject;
|
||||||
const Callback = @import("../env.zig").Callback;
|
const Callback = @import("../env.zig").Callback;
|
||||||
const Case = jsruntime.test_utils.Case;
|
const SessionState = @import("../env.zig").SessionState;
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
|
||||||
|
|
||||||
const NodeUnion = @import("node.zig").Union;
|
const NodeUnion = @import("node.zig").Union;
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
@@ -133,13 +132,22 @@ pub const NodeList = struct {
|
|||||||
return try Node.toInterface(n);
|
return try Node.toInterface(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO entries() https://developer.mozilla.org/en-US/docs/Web/API/NodeList/entries
|
// This code works, but it's _MUCH_ slower than using postAttach. The benefit
|
||||||
pub fn indexed_get(self: *const NodeList, index: u32, has_value: *bool) !?NodeUnion {
|
// of this version, is that it's "live"..but we're talking many orders of
|
||||||
return (try self._item(index)) orelse {
|
// magnitude slower.
|
||||||
has_value.* = false;
|
//
|
||||||
return null;
|
// You can test it by commenting out `postAttach`, uncommenting this and
|
||||||
};
|
// running:
|
||||||
}
|
// zig build wpt -- tests/wpt/dom/nodes/NodeList-static-length-getter-tampered-indexOf-1.html
|
||||||
|
//
|
||||||
|
// I think this _is_ the right way to do it, but I must be doing something
|
||||||
|
// wrong to make it so slow.
|
||||||
|
// pub fn indexed_get(self: *const NodeList, index: u32, has_value: *bool) !?NodeUnion {
|
||||||
|
// return (try self._item(index)) orelse {
|
||||||
|
// has_value.* = false;
|
||||||
|
// return null;
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
pub fn _forEach(self: *NodeList, cbk: Callback) !void { // TODO handle thisArg
|
pub fn _forEach(self: *NodeList, cbk: Callback) !void { // TODO handle thisArg
|
||||||
for (self.nodes.items, 0..) |n, i| {
|
for (self.nodes.items, 0..) |n, i| {
|
||||||
@@ -167,6 +175,15 @@ pub const NodeList = struct {
|
|||||||
pub fn _symbol_iterator(self: *NodeList) NodeListIterator {
|
pub fn _symbol_iterator(self: *NodeList) NodeListIterator {
|
||||||
return self._values();
|
return self._values();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO entries() https://developer.mozilla.org/en-US/docs/Web/API/NodeList/entries
|
||||||
|
pub fn postAttach(self: *NodeList, js_obj: JsObject) !void {
|
||||||
|
const len = self.get_length();
|
||||||
|
for (0..len) |i| {
|
||||||
|
const node = try self._item(@intCast(i)) orelse unreachable;
|
||||||
|
try js_obj.setIndex(i, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ pub const Text = struct {
|
|||||||
pub const Self = parser.Text;
|
pub const Self = parser.Text;
|
||||||
pub const prototype = *CharacterData;
|
pub const prototype = *CharacterData;
|
||||||
|
|
||||||
pub fn constructor(state: *const SessionState, data: ?[]const u8) !*parser.Text {
|
pub fn constructor(data: ?[]const u8, state: *const SessionState) !*parser.Text {
|
||||||
return parser.documentCreateTextNode(
|
return parser.documentCreateTextNode(
|
||||||
parser.documentHTMLToDocument(state.document.?),
|
parser.documentHTMLToDocument(state.document.?),
|
||||||
data orelse "",
|
data orelse "",
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const Interfaces = generate.Tuple(.{
|
|||||||
@import("xmlserializer/xmlserializer.zig").Interfaces,
|
@import("xmlserializer/xmlserializer.zig").Interfaces,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub const JsObject = Env.JsObject;
|
||||||
pub const Callback = Env.Callback;
|
pub const Callback = Env.Callback;
|
||||||
pub const Env = js.Env(*SessionState, Interfaces{});
|
pub const Env = js.Env(*SessionState, Interfaces{});
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ pub const HTMLDocument = struct {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _getElementsByName(self: *parser.DocumentHTML, state: *SessionState, name: []const u8) !NodeList {
|
pub fn _getElementsByName(self: *parser.DocumentHTML, name: []const u8, state: *SessionState) !NodeList {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
var list = NodeList.init();
|
var list = NodeList.init();
|
||||||
errdefer list.deinit(arena);
|
errdefer list.deinit(arena);
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
|
|
||||||
inline fn url(self: *parser.Anchor, state: *SessionState) !URL {
|
inline fn url(self: *parser.Anchor, state: *SessionState) !URL {
|
||||||
const href = try parser.anchorGetHref(self);
|
const href = try parser.anchorGetHref(self);
|
||||||
return URL.constructor(state, href, null); // TODO inject base url
|
return URL.constructor(href, null, state); // TODO inject base url
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
@@ -231,9 +231,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
var u = try url(self, state);
|
var u = try url(self, state);
|
||||||
|
|
||||||
u.uri.scheme = v;
|
u.uri.scheme = v;
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,8 +264,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
u.uri.port = null;
|
u.uri.port = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,7 +278,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, state);
|
||||||
u.uri.host = .{ .raw = v };
|
u.uri.host = .{ .raw = v };
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,8 +298,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
u.uri.port = null;
|
u.uri.port = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,8 +317,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
} else {
|
} else {
|
||||||
u.uri.user = null;
|
u.uri.user = null;
|
||||||
}
|
}
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
@@ -342,8 +337,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
} else {
|
} else {
|
||||||
u.uri.password = null;
|
u.uri.password = null;
|
||||||
}
|
}
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
@@ -358,8 +352,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, state);
|
||||||
u.uri.path = .{ .raw = v };
|
u.uri.path = .{ .raw = v };
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
@@ -379,8 +372,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
} else {
|
} else {
|
||||||
u.uri.query = null;
|
u.uri.query = null;
|
||||||
}
|
}
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
@@ -400,8 +392,7 @@ pub const HTMLAnchorElement = struct {
|
|||||||
} else {
|
} else {
|
||||||
u.uri.fragment = null;
|
u.uri.fragment = null;
|
||||||
}
|
}
|
||||||
const href = try u.format(arena);
|
const href = try u.toString(arena);
|
||||||
defer arena.free(href);
|
|
||||||
|
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const jsruntime = @import("jsruntime");
|
|
||||||
|
|
||||||
const Case = jsruntime.test_utils.Case;
|
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
|
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-history-interface
|
||||||
pub const History = struct {
|
pub const History = struct {
|
||||||
|
|||||||
@@ -21,13 +21,9 @@ const std = @import("std");
|
|||||||
const SessionState = @import("../env.zig").SessionState;
|
const SessionState = @import("../env.zig").SessionState;
|
||||||
|
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const jsruntime = @import("jsruntime");
|
|
||||||
|
|
||||||
const URL = @import("../url/url.zig").URL;
|
const URL = @import("../url/url.zig").URL;
|
||||||
|
|
||||||
const Case = jsruntime.test_utils.Case;
|
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-location-interface
|
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#the-location-interface
|
||||||
pub const Location = struct {
|
pub const Location = struct {
|
||||||
url: ?URL = null,
|
url: ?URL = null,
|
||||||
|
|||||||
@@ -19,10 +19,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const jsruntime = @import("jsruntime");
|
|
||||||
|
|
||||||
const Case = jsruntime.test_utils.Case;
|
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/system-state.html#navigator
|
// https://html.spec.whatwg.org/multipage/system-state.html#navigator
|
||||||
pub const Navigator = struct {
|
pub const Navigator = struct {
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ pub const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle callback arguments.
|
// TODO handle callback arguments.
|
||||||
pub fn _setTimeout(self: *Window, state: *SessionState, cbk: Callback, delay: ?u32) !u32 {
|
pub fn _setTimeout(self: *Window, cbk: Callback, delay: ?u32, state: *SessionState) !u32 {
|
||||||
if (self.timeoutid >= self.timeoutids.len) return error.TooMuchTimeout;
|
if (self.timeoutid >= self.timeoutids.len) return error.TooMuchTimeout;
|
||||||
|
|
||||||
const ddelay: u63 = delay orelse 0;
|
const ddelay: u63 = delay orelse 0;
|
||||||
@@ -128,7 +128,7 @@ pub const Window = struct {
|
|||||||
return self.timeoutid;
|
return self.timeoutid;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _clearTimeout(self: *Window, state: *SessionState, id: u32) !void {
|
pub fn _clearTimeout(self: *Window, id: u32, state: *SessionState) !void {
|
||||||
// I do would prefer return an error in this case, but it seems some JS
|
// I do would prefer return an error in this case, but it seems some JS
|
||||||
// uses invalid id, in particular id 0.
|
// uses invalid id, in particular id 0.
|
||||||
// So we silently ignore invalid id for now.
|
// So we silently ignore invalid id for now.
|
||||||
|
|||||||
@@ -18,9 +18,6 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const jsruntime = @import("jsruntime");
|
|
||||||
const Case = jsruntime.test_utils.Case;
|
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
|
||||||
const DOMError = @import("../netsurf.zig").DOMError;
|
const DOMError = @import("../netsurf.zig").DOMError;
|
||||||
|
|
||||||
const log = std.log.scoped(.storage);
|
const log = std.log.scoped(.storage);
|
||||||
|
|||||||
@@ -44,7 +44,11 @@ pub const URL = struct {
|
|||||||
uri: std.Uri,
|
uri: std.Uri,
|
||||||
search_params: URLSearchParams,
|
search_params: URLSearchParams,
|
||||||
|
|
||||||
pub fn constructor(state: *SessionState, url: []const u8, base: ?[]const u8) !URL {
|
pub fn constructor(
|
||||||
|
url: []const u8,
|
||||||
|
base: ?[]const u8,
|
||||||
|
state: *SessionState,
|
||||||
|
) !URL {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" });
|
const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" });
|
||||||
errdefer arena.free(raw);
|
errdefer arena.free(raw);
|
||||||
@@ -63,13 +67,8 @@ pub const URL = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_origin(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_origin(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(state.arena);
|
||||||
defer buf.deinit();
|
|
||||||
|
|
||||||
try self.uri.writeToStream(.{
|
try self.uri.writeToStream(.{
|
||||||
.scheme = true,
|
.scheme = true,
|
||||||
.authentication = false,
|
.authentication = false,
|
||||||
@@ -78,32 +77,27 @@ pub const URL = struct {
|
|||||||
.query = false,
|
.query = false,
|
||||||
.fragment = false,
|
.fragment = false,
|
||||||
}, buf.writer());
|
}, buf.writer());
|
||||||
return try buf.toOwnedSlice();
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get_href returns the URL by writing all its components.
|
// get_href returns the URL by writing all its components.
|
||||||
// The query is replaced by a dump of search params.
|
// The query is replaced by a dump of search params.
|
||||||
//
|
//
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_href(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_href(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
// retrieve the query search from search_params.
|
// retrieve the query search from search_params.
|
||||||
const cur = self.uri.query;
|
const cur = self.uri.query;
|
||||||
defer self.uri.query = cur;
|
defer self.uri.query = cur;
|
||||||
var q = std.ArrayList(u8).init(arena);
|
var q = std.ArrayList(u8).init(arena);
|
||||||
defer q.deinit();
|
|
||||||
try self.search_params.values.encode(q.writer());
|
try self.search_params.values.encode(q.writer());
|
||||||
self.uri.query = .{ .percent_encoded = q.items };
|
self.uri.query = .{ .percent_encoded = q.items };
|
||||||
|
|
||||||
return try self.format(arena);
|
return try self.toString(arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
// format the url with all its components.
|
// format the url with all its components.
|
||||||
pub fn format(self: *URL, arena: std.mem.Allocator) ![]const u8 {
|
pub fn toString(self: *URL, arena: std.mem.Allocator) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(arena);
|
var buf = std.ArrayList(u8).init(arena);
|
||||||
defer buf.deinit();
|
|
||||||
|
|
||||||
try self.uri.writeToStream(.{
|
try self.uri.writeToStream(.{
|
||||||
.scheme = true,
|
.scheme = true,
|
||||||
@@ -113,12 +107,9 @@ pub const URL = struct {
|
|||||||
.query = uriComponentNullStr(self.uri.query).len > 0,
|
.query = uriComponentNullStr(self.uri.query).len > 0,
|
||||||
.fragment = uriComponentNullStr(self.uri.fragment).len > 0,
|
.fragment = uriComponentNullStr(self.uri.fragment).len > 0,
|
||||||
}, buf.writer());
|
}, buf.writer());
|
||||||
return try buf.toOwnedSlice();
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_protocol(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_protocol(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
return try std.mem.concat(state.arena, u8, &[_][]const u8{ self.uri.scheme, ":" });
|
return try std.mem.concat(state.arena, u8, &[_][]const u8{ self.uri.scheme, ":" });
|
||||||
}
|
}
|
||||||
@@ -131,12 +122,8 @@ pub const URL = struct {
|
|||||||
return uriComponentNullStr(self.uri.password);
|
return uriComponentNullStr(self.uri.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_host(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_host(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(state.arena);
|
||||||
defer buf.deinit();
|
|
||||||
|
|
||||||
try self.uri.writeToStream(.{
|
try self.uri.writeToStream(.{
|
||||||
.scheme = false,
|
.scheme = false,
|
||||||
@@ -146,25 +133,20 @@ pub const URL = struct {
|
|||||||
.query = false,
|
.query = false,
|
||||||
.fragment = false,
|
.fragment = false,
|
||||||
}, buf.writer());
|
}, buf.writer());
|
||||||
return try buf.toOwnedSlice();
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hostname(self: *URL) []const u8 {
|
pub fn get_hostname(self: *URL) []const u8 {
|
||||||
return uriComponentNullStr(self.uri.host);
|
return uriComponentNullStr(self.uri.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_port(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_port(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
if (self.uri.port == null) return try arena.dupe(u8, "");
|
if (self.uri.port == null) return try arena.dupe(u8, "");
|
||||||
|
|
||||||
var buf = std.ArrayList(u8).init(arena);
|
var buf = std.ArrayList(u8).init(arena);
|
||||||
defer buf.deinit();
|
|
||||||
|
|
||||||
try std.fmt.formatInt(self.uri.port.?, 10, .lower, .{}, buf.writer());
|
try std.fmt.formatInt(self.uri.port.?, 10, .lower, .{}, buf.writer());
|
||||||
return try buf.toOwnedSlice();
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pathname(self: *URL) []const u8 {
|
pub fn get_pathname(self: *URL) []const u8 {
|
||||||
@@ -172,24 +154,17 @@ pub const URL = struct {
|
|||||||
return uriComponentStr(self.uri.path);
|
return uriComponentStr(self.uri.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_search(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_search(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
if (self.search_params.get_size() == 0) return try arena.dupe(u8, "");
|
if (self.search_params.get_size() == 0) return try arena.dupe(u8, "");
|
||||||
|
|
||||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||||
defer buf.deinit(arena);
|
|
||||||
|
|
||||||
try buf.append(arena, '?');
|
try buf.append(arena, '?');
|
||||||
try self.search_params.values.encode(buf.writer(arena));
|
try self.search_params.values.encode(buf.writer(arena));
|
||||||
return buf.toOwnedSlice(arena);
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the caller must free the returned string.
|
|
||||||
// TODO return a disposable string
|
|
||||||
// https://github.com/lightpanda-io/jsruntime-lib/issues/195
|
|
||||||
pub fn get_hash(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_hash(self: *URL, state: *SessionState) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = state.arena;
|
||||||
if (self.uri.fragment == null) return try arena.dupe(u8, "");
|
if (self.uri.fragment == null) return try arena.dupe(u8, "");
|
||||||
@@ -226,7 +201,7 @@ fn uriComponentStr(c: std.Uri.Component) []const u8 {
|
|||||||
pub const URLSearchParams = struct {
|
pub const URLSearchParams = struct {
|
||||||
values: query.Values,
|
values: query.Values,
|
||||||
|
|
||||||
pub fn constructor(state: *SessionState, qs: ?[]const u8) !URLSearchParams {
|
pub fn constructor(qs: ?[]const u8, state: *SessionState) !URLSearchParams {
|
||||||
return init(state.arena, qs);
|
return init(state.arena, qs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -451,7 +451,7 @@ pub const XMLHttpRequest = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO body can be either a XMLHttpRequestBodyInit or a document
|
// TODO body can be either a XMLHttpRequestBodyInit or a document
|
||||||
pub fn _send(self: *XMLHttpRequest, session_state: *SessionState, body: ?[]const u8) !void {
|
pub fn _send(self: *XMLHttpRequest, body: ?[]const u8, session_state: *SessionState) !void {
|
||||||
if (self.state != .opened) return DOMError.InvalidState;
|
if (self.state != .opened) return DOMError.InvalidState;
|
||||||
if (self.send_flag) return DOMError.InvalidState;
|
if (self.send_flag) return DOMError.InvalidState;
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ pub const XMLSerializer = struct {
|
|||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _serializeToString(_: *const XMLSerializer, state: *SessionState, root: *parser.Node) ![]const u8 {
|
pub fn _serializeToString(_: *const XMLSerializer, root: *parser.Node, state: *SessionState) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(state.arena);
|
||||||
if (try parser.nodeType(root) == .document) {
|
if (try parser.nodeType(root) == .document) {
|
||||||
try dump.writeHTML(@as(*parser.Document, @ptrCast(root)), buf.writer());
|
try dump.writeHTML(@as(*parser.Document, @ptrCast(root)), buf.writer());
|
||||||
|
|||||||
@@ -975,8 +975,23 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
|||||||
// typeTaggedAnyOpaque, we'll also know there that
|
// typeTaggedAnyOpaque, we'll also know there that
|
||||||
// the type is empty and can create an empty instance.
|
// the type is empty and can create an empty instance.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not move this _AFTER_ the postAttach code.
|
||||||
|
// postAttach is likely to call back into this function
|
||||||
|
// mutating our identity_map, and making the gop pointers
|
||||||
|
// invalid.
|
||||||
const js_persistent = PersistentObject.init(isolate, js_obj);
|
const js_persistent = PersistentObject.init(isolate, js_obj);
|
||||||
gop.value_ptr.* = js_persistent;
|
gop.value_ptr.* = js_persistent;
|
||||||
|
|
||||||
|
if (@hasDecl(ptr.child, "postAttach")) {
|
||||||
|
const obj_wrap = JsObject{ .js_obj = js_obj, .executor = self };
|
||||||
|
switch (@typeInfo(@TypeOf(ptr.child.postAttach)).@"fn".params.len) {
|
||||||
|
2 => try value.postAttach(obj_wrap),
|
||||||
|
3 => try value.postAttach(self.state, obj_wrap),
|
||||||
|
else => @compileError(@typeName(ptr.child) ++ ".postAttach must take 2 or 3 parameters"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return js_persistent;
|
return js_persistent;
|
||||||
},
|
},
|
||||||
else => @compileError("Expected a struct or pointer, got " ++ @typeName(T) ++ " (constructors must return struct or pointers)"),
|
else => @compileError("Expected a struct or pointer, got " ++ @typeName(T) ++ " (constructors must return struct or pointers)"),
|
||||||
@@ -1117,6 +1132,41 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const JsObject = struct {
|
||||||
|
js_obj: v8.Object,
|
||||||
|
executor: *Executor,
|
||||||
|
|
||||||
|
// If a Zig struct wants the Object parameter, it'll declare a
|
||||||
|
// function like:
|
||||||
|
// fn _length(self: *const NodeList, js_obj: Env.Object) usize
|
||||||
|
//
|
||||||
|
// When we're trying to call this function, we can't just do
|
||||||
|
// if (params[i].type.? == Object)
|
||||||
|
// Because there is _no_ object, there's only an Env.Object, where
|
||||||
|
// Env is a generic.
|
||||||
|
// We could probably figure out a way to do this, but simply checking
|
||||||
|
// for this declaration is _a lot_ easier.
|
||||||
|
const _JSOBJECT_ID_KLUDGE = true;
|
||||||
|
|
||||||
|
pub fn setIndex(self: JsObject, index: usize, value: anytype) !void {
|
||||||
|
const key = switch (index) {
|
||||||
|
inline 0...1000 => |i| std.fmt.comptimePrint("{d}", .{i}),
|
||||||
|
else => try std.fmt.allocPrint(self.executor.scope_arena.allocator(), "{d}", .{index}),
|
||||||
|
};
|
||||||
|
return self.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(self: JsObject, key: []const u8, value: anytype) !void {
|
||||||
|
const executor = self.executor;
|
||||||
|
|
||||||
|
const js_key = v8.String.initUtf8(executor.isolate, key);
|
||||||
|
const js_value = try executor.zigValueToJs(value);
|
||||||
|
if (!self.js_obj.setValue(executor.context, js_key, js_value)) {
|
||||||
|
return error.FailedToSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const TryCatch = struct {
|
pub const TryCatch = struct {
|
||||||
inner: v8.TryCatch,
|
inner: v8.TryCatch,
|
||||||
executor: *const Executor,
|
executor: *const Executor,
|
||||||
@@ -1641,64 +1691,93 @@ fn Caller(comptime E: type) type {
|
|||||||
// parameters into the array.
|
// parameters into the array.
|
||||||
fn getArgs(self: *const Self, comptime named_function: anytype, comptime offset: usize, info: anytype) !ParamterTypes(@TypeOf(named_function.func)) {
|
fn getArgs(self: *const Self, comptime named_function: anytype, comptime offset: usize, info: anytype) !ParamterTypes(@TypeOf(named_function.func)) {
|
||||||
const F = @TypeOf(named_function.func);
|
const F = @TypeOf(named_function.func);
|
||||||
const zig_function_parameters = @typeInfo(F).@"fn".params;
|
|
||||||
|
|
||||||
var args: ParamterTypes(F) = undefined;
|
var args: ParamterTypes(F) = undefined;
|
||||||
if (zig_function_parameters.len == 0) {
|
|
||||||
|
const params = @typeInfo(F).@"fn".params[offset..];
|
||||||
|
// Except for the constructor, the first parameter is always `self`
|
||||||
|
// This isn't something we'll bind from JS, so skip it.
|
||||||
|
const params_to_map = blk: {
|
||||||
|
if (params.len == 0) {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last parameter is the State, set it, and exclude it
|
||||||
|
// from our params slice, because we don't want to bind it to
|
||||||
|
// a JS argument
|
||||||
|
if (comptime isState(params[params.len - 1].type.?)) {
|
||||||
|
@field(args, std.fmt.comptimePrint("{d}", .{params.len - 1 + offset})) = self.executor.state;
|
||||||
|
break :blk params[0 .. params.len - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last parameter is a JsObject, set it, and exclude it
|
||||||
|
// from our params slice, because we don't want to bind it to
|
||||||
|
// a JS argument
|
||||||
|
if (comptime isJsObject(params[params.len - 1].type.?)) {
|
||||||
|
@field(args, std.fmt.comptimePrint("{d}", .{params.len - 1 + offset})) = .{
|
||||||
|
.handle = info.getThis(),
|
||||||
|
.executor = self.executor,
|
||||||
|
};
|
||||||
|
|
||||||
|
// AND the 2nd last parameter is state
|
||||||
|
if (params.len > 1 and comptime isState(params[params.len - 2].type.?)) {
|
||||||
|
@field(args, std.fmt.comptimePrint("{d}", .{params.len - 2 + offset})) = self.executor.state;
|
||||||
|
break :blk params[0 .. params.len - 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk params[0 .. params.len - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have neither a State nor a JsObject. All params must be
|
||||||
|
// bound to a JavaScript value.
|
||||||
|
break :blk params;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (params_to_map.len == 0) {
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
const adjusted_offset = blk: {
|
|
||||||
if (zig_function_parameters.len > offset and comptime isState(zig_function_parameters[offset].type.?)) {
|
|
||||||
@field(args, std.fmt.comptimePrint("{d}", .{offset})) = self.executor.state;
|
|
||||||
break :blk offset + 1;
|
|
||||||
} else {
|
|
||||||
break :blk offset;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const js_parameter_count = info.length();
|
const js_parameter_count = info.length();
|
||||||
const expected_js_parameters = zig_function_parameters.len - adjusted_offset;
|
const last_js_parameter = params_to_map.len - 1;
|
||||||
|
|
||||||
var is_variadic = false;
|
var is_variadic = false;
|
||||||
const last_parameter_index = zig_function_parameters.len - 1;
|
|
||||||
{
|
{
|
||||||
// This is going to get complicated. If the last Zig paremeter
|
// This is going to get complicated. If the last Zig paremeter
|
||||||
// is a slice AND the corresponding javascript parameter is
|
// is a slice AND the corresponding javascript parameter is
|
||||||
// NOT an an array, then we'll treat it as a variadic.
|
// NOT an an array, then we'll treat it as a variadic.
|
||||||
|
|
||||||
const last_parameter_type = zig_function_parameters[last_parameter_index].type.?;
|
const last_parameter_type = params_to_map[params_to_map.len - 1].type.?;
|
||||||
const last_parameter_type_info = @typeInfo(last_parameter_type);
|
const last_parameter_type_info = @typeInfo(last_parameter_type);
|
||||||
if (last_parameter_type_info == .pointer and last_parameter_type_info.pointer.size == .slice) {
|
if (last_parameter_type_info == .pointer and last_parameter_type_info.pointer.size == .slice) {
|
||||||
const slice_type = last_parameter_type_info.pointer.child;
|
const slice_type = last_parameter_type_info.pointer.child;
|
||||||
const corresponding_js_index = last_parameter_index - adjusted_offset;
|
const corresponding_js_value = info.getArg(@as(u32, @intCast(last_js_parameter)));
|
||||||
const corresponding_js_value = info.getArg(@as(u32, @intCast(corresponding_js_index)));
|
|
||||||
if (corresponding_js_value.isArray() == false and slice_type != u8) {
|
if (corresponding_js_value.isArray() == false and slice_type != u8) {
|
||||||
is_variadic = true;
|
is_variadic = true;
|
||||||
if (js_parameter_count == 0) {
|
if (js_parameter_count == 0) {
|
||||||
@field(args, tupleFieldName(last_parameter_index)) = &.{};
|
@field(args, tupleFieldName(params_to_map.len + offset - 1)) = &.{};
|
||||||
} else {
|
} else {
|
||||||
const arr = try self.call_allocator.alloc(last_parameter_type_info.pointer.child, js_parameter_count - expected_js_parameters + 1);
|
const arr = try self.call_allocator.alloc(last_parameter_type_info.pointer.child, js_parameter_count - params_to_map.len + 1);
|
||||||
for (arr, corresponding_js_index..) |*a, i| {
|
for (arr, last_js_parameter..) |*a, i| {
|
||||||
const js_value = info.getArg(@as(u32, @intCast(i)));
|
const js_value = info.getArg(@as(u32, @intCast(i)));
|
||||||
a.* = try self.jsValueToZig(named_function, slice_type, js_value);
|
a.* = try self.jsValueToZig(named_function, slice_type, js_value);
|
||||||
}
|
}
|
||||||
@field(args, tupleFieldName(last_parameter_index)) = arr;
|
@field(args, tupleFieldName(params_to_map.len + offset - 1)) = arr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline for (zig_function_parameters[adjusted_offset..], 0..) |param, i| {
|
inline for (params_to_map, 0..) |param, i| {
|
||||||
const field_index = comptime i + adjusted_offset;
|
const field_index = comptime i + offset;
|
||||||
if (comptime field_index == last_parameter_index) {
|
if (comptime i == params_to_map.len - 1) {
|
||||||
if (is_variadic) {
|
if (is_variadic) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comptime isState(param.type.?)) {
|
if (comptime isState(param.type.?)) {
|
||||||
@compileError("State must be the 2nd parameter: " ++ named_function.full_name);
|
@compileError("State must be the last parameter (or 2nd last if there's a JsObject): " ++ named_function.full_name);
|
||||||
|
} else if (comptime isJsObject(param.type.?)) {
|
||||||
|
@compileError("JsObject must be the last parameter: " ++ named_function.full_name);
|
||||||
} else if (i >= js_parameter_count) {
|
} else if (i >= js_parameter_count) {
|
||||||
if (@typeInfo(param.type.?) != .optional) {
|
if (@typeInfo(param.type.?) != .optional) {
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
@@ -1882,6 +1961,10 @@ fn Caller(comptime E: type) type {
|
|||||||
const Const_State = if (ti == .pointer) *const ti.pointer.child else State;
|
const Const_State = if (ti == .pointer) *const ti.pointer.child else State;
|
||||||
return T == State or T == Const_State;
|
return T == State or T == Const_State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn isJsObject(comptime T: type) bool {
|
||||||
|
return @typeInfo(T) == .@"struct" and @hasDecl(T, "_JSOBJECT_ID_KLUDGE");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
const MyList = struct {
|
const MyList = struct {
|
||||||
items: []u8,
|
items: []u8,
|
||||||
|
|
||||||
pub fn constructor(state: State, elem1: u8, elem2: u8, elem3: u8) MyList {
|
pub fn constructor(elem1: u8, elem2: u8, elem3: u8, state: State) MyList {
|
||||||
var items = state.arena.alloc(u8, 3) catch unreachable;
|
var items = state.arena.alloc(u8, 3) catch unreachable;
|
||||||
items[0] = elem1;
|
items[0] = elem1;
|
||||||
items[1] = elem2;
|
items[1] = elem2;
|
||||||
|
|||||||
@@ -480,7 +480,7 @@ pub const JsRunner = struct {
|
|||||||
return self.executor.exec(src, null) catch |err| {
|
return self.executor.exec(src, null) catch |err| {
|
||||||
if (try try_catch.err(self.arena)) |msg| {
|
if (try try_catch.err(self.arena)) |msg| {
|
||||||
err_msg.* = msg;
|
err_msg.* = msg;
|
||||||
std.debug.print("Error runnign script: {s}\n", .{msg});
|
std.debug.print("Error running script: {s}\n", .{msg});
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user