From b5b4659c87aa059aa0706491d24f97a6a7b2ed10 Mon Sep 17 00:00:00 2001 From: Francis Bouvier Date: Thu, 21 Sep 2023 17:05:38 +0200 Subject: [PATCH] backport some node_lexbor modifications Signed-off-by: Francis Bouvier --- src/dom.zig | 14 ++-- src/dom/node.zig | 12 +++ src/generate.zig | 189 +++++++++++++++++++++++++++++------------- src/html/document.zig | 20 ++--- src/html/elements.zig | 10 +-- 5 files changed, 166 insertions(+), 79 deletions(-) diff --git a/src/dom.zig b/src/dom.zig index 6d481fe3..d0fcd42f 100644 --- a/src/dom.zig +++ b/src/dom.zig @@ -4,12 +4,13 @@ const Console = @import("jsruntime").Console; // DOM const EventTarget = @import("dom/event_target.zig").EventTarget; -const Node = @import("dom/node.zig").Node; +const N = @import("dom/node.zig"); const Element = @import("dom/element.zig").Element; const Document = @import("dom/document.zig").Document; // HTML pub const HTMLDocument = @import("html/document.zig").HTMLDocument; +const HTMLElem = @import("html/elements.zig"); const E = @import("html/elements.zig"); @@ -19,14 +20,15 @@ const interfaces = .{ // DOM EventTarget, - Node, + N.Node, + N.Types, Element, Document, // HTML HTMLDocument, - E.HTMLElement, - E.HTMLMediaElement, - E.HTMLElementsTypes, + HTMLElem.HTMLElement, + HTMLElem.HTMLMediaElement, + HTMLElem.Types, }; -pub const Interfaces = generate.TupleInst(generate.TupleT(interfaces), interfaces); +pub const Interfaces = generate.Tuple(interfaces); diff --git a/src/dom/node.zig b/src/dom/node.zig index d5067c05..f2daf427 100644 --- a/src/dom/node.zig +++ b/src/dom/node.zig @@ -1,11 +1,23 @@ const std = @import("std"); +const generate = @import("../generate.zig"); + const parser = @import("../netsurf.zig"); const EventTarget = @import("event_target.zig").EventTarget; +const HTMLDocument = @import("../html/document.zig").HTMLDocument; +const HTMLElem = @import("../html/elements.zig"); pub const Node = struct { pub const Self = parser.Node; pub const prototype = *EventTarget; pub const mem_guarantied = true; }; + +pub const Types = generate.Tuple(.{ + HTMLElem.Types, + HTMLDocument, +}); +const Generated = generate.Union.compile(Types); +pub const Union = Generated._union; +pub const Tags = Generated._enum; diff --git a/src/generate.zig b/src/generate.zig index 553197c9..36ec653e 100644 --- a/src/generate.zig +++ b/src/generate.zig @@ -1,13 +1,33 @@ const std = @import("std"); const builtin = @import("builtin"); +// Utils +// ----- + +fn itoa(comptime i: u8) ![]u8 { + var len: usize = undefined; + if (i < 10) { + len = 1; + } else if (i < 100) { + len = 2; + } else { + return error.GenerateTooMuchMembers; + } + var buf: [len]u8 = undefined; + return try std.fmt.bufPrint(buf[0..], "{d}", .{i}); +} + fn fmtName(comptime T: type) []const u8 { var it = std.mem.splitBackwards(u8, @typeName(T), "."); return it.first(); } +// Union +// ----- + // Generate a flatten tagged Union from various structs and union of structs // TODO: make this function more generic +// TODO: dedup pub const Union = struct { _enum: type, _union: type, @@ -152,38 +172,15 @@ pub const Union = struct { } }; -fn itoa(comptime i: u8) ![]u8 { - var len: usize = undefined; - if (i < 10) { - len = 1; - } else if (i < 100) { - len = 2; - } else { - return error.GenerateTooMuchMembers; - } - var buf: [len]u8 = undefined; - return try std.fmt.bufPrint(buf[0..], "{d}", .{i}); -} +// Tuple +// ----- -// Generate a flatten tuple type from various structs and tuple of structs. -// TODO: make this function more generic -pub fn TupleT(comptime tuple: anytype) type { - - // check types provided - const tuple_T = @TypeOf(tuple); - const tuple_info = @typeInfo(tuple_T); - if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) { - @compileError("GenerateArgNotTuple"); - } - - const tuple_members = tuple_info.Struct.fields; - - // first iteration to get the total number of members - var members_nb = 0; - for (tuple_members) |member| { +fn tupleNb(comptime tuple: anytype) usize { + var nb = 0; + for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| { const member_T = @field(tuple, member.name); if (@TypeOf(member_T) == type) { - members_nb += 1; + nb += 1; } else { const member_info = @typeInfo(@TypeOf(member_T)); if (member_info != .Struct and !member_info.Struct.is_tuple) { @@ -194,14 +191,82 @@ pub fn TupleT(comptime tuple: anytype) type { @compileError("GenerateMemberTupleChildNotType"); } } - members_nb += member_info.Struct.fields.len; + nb += member_info.Struct.fields.len; } } + return nb; +} - // second iteration to generate the tuple type - var fields: [members_nb]std.builtin.Type.StructField = undefined; +fn tupleTypes(comptime nb: usize, comptime tuple: anytype) [nb]type { + var types: [nb]type = undefined; var done = 0; - while (done < members_nb) { + for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| { + const T = @field(tuple, member.name); + if (@TypeOf(T) == type) { + types[done] = T; + done += 1; + } else { + const info = @typeInfo(@TypeOf(T)); + for (info.Struct.fields) |field| { + types[done] = @field(T, field.name); + done += 1; + } + } + } + return types; +} + +fn isDup(comptime nb: usize, comptime list: [nb]type, comptime T: type, comptime i: usize) bool { + for (list, 0..) |item, index| { + if (i >= index) { + // check sequentially + continue; + } + if (T == item) { + return true; + } + } + return false; +} + +fn dedupIndexes(comptime nb: usize, comptime types: [nb]type) [nb]i32 { + var dedup_indexes: [nb]i32 = undefined; + for (types, 0..) |T, i| { + if (isDup(nb, types, T, i)) { + dedup_indexes[i] = -1; + } else { + dedup_indexes[i] = i; + } + } + return dedup_indexes; +} + +fn dedupNb(comptime nb: usize, comptime dedup_indexes: [nb]i32) usize { + var dedup_nb = 0; + for (dedup_indexes) |index| { + if (index != -1) { + dedup_nb += 1; + } + } + return dedup_nb; +} + +fn TupleT(comptime tuple: anytype) type { + @setEvalBranchQuota(100000); + + // logic + const nb = tupleNb(tuple); + const types = tupleTypes(nb, tuple); + const dedup_indexes = dedupIndexes(nb, types); + const dedup_nb = dedupNb(nb, dedup_indexes); + + // generate the tuple type + var fields: [dedup_nb]std.builtin.Type.StructField = undefined; + var done = 0; + for (dedup_indexes) |index| { + if (index == -1) { + continue; + } fields[done] = .{ .name = try itoa(done), .type = type, @@ -221,39 +286,36 @@ pub fn TupleT(comptime tuple: anytype) type { return @Type(std.builtin.Type{ .Struct = info }); } -// Instantiate a flatten tuple from various structs and tuple of structs -// You need to call first TupleT to generate the according type +// Create a flatten tuple from various structs and tuple of structs +// Duplicates will be removed. // TODO: make this function more generic -pub fn TupleInst(comptime T: type, comptime tuple: anytype) T { +pub fn Tuple(comptime tuple: anytype) TupleT(tuple) { // check types provided const tuple_T = @TypeOf(tuple); const tuple_info = @typeInfo(tuple_T); - const tuple_members = tuple_info.Struct.fields; + if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) { + @compileError("GenerateArgNotTuple"); + } + + // generate the type + const T = TupleT(tuple); + + // logic + const nb = tupleNb(tuple); + const types = tupleTypes(nb, tuple); + const dedup_indexes = dedupIndexes(nb, types); // instantiate the tuple var t: T = undefined; var done = 0; - for (tuple_members) |member| { - const member_T = @field(tuple, member.name); - var member_info: std.builtin.Type = undefined; - if (@TypeOf(member_T) == type) { - member_info = @typeInfo(member_T); - } else { - member_info = @typeInfo(@TypeOf(member_T)); - } - var member_detail = member_info.Struct; - if (member_detail.is_tuple) { - for (member_detail.fields) |field| { - const name = try itoa(done); - @field(t, name) = @field(member_T, field.name); - done += 1; - } - } else { - const name = try itoa(done); - @field(t, name) = @field(tuple, member.name); - done += 1; + for (dedup_indexes) |index| { + if (index == -1) { + continue; } + const name = try itoa(done); + @field(t, name) = types[index]; + done += 1; } return t; } @@ -322,7 +384,7 @@ pub fn tests() !void { // Tuple from structs const tuple_structs = .{ Astruct, Bstruct }; - const tFromStructs = TupleInst(TupleT(tuple_structs), tuple_structs); + const tFromStructs = Tuple(tuple_structs); const t_from_structs = @typeInfo(@TypeOf(tFromStructs)); try std.testing.expect(t_from_structs == .Struct); try std.testing.expect(t_from_structs.Struct.is_tuple); @@ -332,7 +394,7 @@ pub fn tests() !void { // Tuple from tuple and structs const tuple_mix = .{ tFromStructs, Cstruct }; - const tFromMix = TupleInst(TupleT(tuple_mix), tuple_mix); + const tFromMix = Tuple(tuple_mix); const t_from_mix = @typeInfo(@TypeOf(tFromMix)); try std.testing.expect(t_from_mix == .Struct); try std.testing.expect(t_from_mix.Struct.is_tuple); @@ -341,5 +403,16 @@ pub fn tests() !void { try std.testing.expect(@field(tFromMix, "1") == Bstruct); try std.testing.expect(@field(tFromMix, "2") == Cstruct); + // Tuple with dedup + const tuple_dedup = .{ Cstruct, Astruct, tFromStructs, Bstruct }; + const tFromDedup = Tuple(tuple_dedup); + const t_from_dedup = @typeInfo(@TypeOf(tFromDedup)); + try std.testing.expect(t_from_dedup == .Struct); + try std.testing.expect(t_from_dedup.Struct.is_tuple); + try std.testing.expect(t_from_dedup.Struct.fields.len == 3); + try std.testing.expect(@field(tFromDedup, "0") == Cstruct); + try std.testing.expect(@field(tFromDedup, "1") == Astruct); + try std.testing.expect(@field(tFromDedup, "2") == Bstruct); + std.debug.print("Generate Tuple: OK\n", .{}); } diff --git a/src/html/document.zig b/src/html/document.zig index 51c59e28..8738d307 100644 --- a/src/html/document.zig +++ b/src/html/document.zig @@ -7,8 +7,7 @@ const Case = jsruntime.test_utils.Case; const checkCases = jsruntime.test_utils.checkCases; const Document = @import("../dom/document.zig").Document; - -const E = @import("elements.zig"); +const HTMLElem = @import("elements.zig"); pub const HTMLDocument = struct { pub const Self = parser.DocumentHTML; @@ -22,19 +21,19 @@ pub const HTMLDocument = struct { return parser.documentHTMLBody(self); } - pub fn _getElementById(self: *parser.DocumentHTML, id: []u8) ?*parser.ElementHTML { + pub fn _getElementById(self: *parser.DocumentHTML, id: []u8) ?HTMLElem.Union { const doc = parser.documentHTMLToDocument(self); - const elem = parser.documentGetElementById(doc, id); - if (elem) |value| { - return @as(*parser.ElementHTML, @ptrCast(value)); + const elem_dom = parser.documentGetElementById(doc, id); + if (elem_dom) |elem| { + return HTMLElem.toInterface(HTMLElem.Union, elem); } return null; } - pub fn _createElement(self: *parser.DocumentHTML, tag_name: []const u8) E.HTMLElements { + pub fn _createElement(self: *parser.DocumentHTML, tag_name: []const u8) HTMLElem.Union { const doc_dom = parser.documentHTMLToDocument(self); const base = parser.documentCreateElement(doc_dom, tag_name); - return E.ElementToHTMLElementInterface(base); + return HTMLElem.toInterface(HTMLElem.Union, base); } }; @@ -51,13 +50,14 @@ pub fn testExecFn( .{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" }, .{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" }, .{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" }, + .{ .src = "document.body.localName === 'body'", .ex = "true" }, }; try checkCases(js_env, &constructor); var getElementById = [_]Case{ .{ .src = "let getElementById = document.getElementById('content')", .ex = "undefined" }, - .{ .src = "getElementById.constructor.name", .ex = "HTMLElement" }, - .{ .src = "getElementById.localName", .ex = "main" }, + .{ .src = "getElementById.constructor.name", .ex = "HTMLDivElement" }, + .{ .src = "getElementById.localName", .ex = "div" }, }; try checkCases(js_env, &getElementById); diff --git a/src/html/elements.zig b/src/html/elements.zig index 056559b3..012bec3d 100644 --- a/src/html/elements.zig +++ b/src/html/elements.zig @@ -12,7 +12,7 @@ pub const HTMLElement = struct { pub const mem_guarantied = true; }; -pub const HTMLElementsTypes = .{ +pub const Types = .{ HTMLUnknownElement, HTMLAnchorElement, HTMLAreaElement, @@ -74,9 +74,9 @@ pub const HTMLElementsTypes = .{ HTMLUListElement, HTMLVideoElement, }; -const HTMLElementsGenerated = generate.Union.compile(HTMLElementsTypes); -pub const HTMLElements = HTMLElementsGenerated._union; -pub const HTMLElementsTags = HTMLElementsGenerated._enum; +const Generated = generate.Union.compile(Types); +pub const Union = Generated._union; +pub const Tags = Generated._enum; // Deprecated HTMLElements in Chrome (2023/03/15) // HTMLContentelement @@ -454,7 +454,7 @@ pub const HTMLVideoElement = struct { pub const mem_guarantied = true; }; -pub fn ElementToHTMLElementInterface(elem: *parser.Element) HTMLElements { +pub fn toInterface(comptime T: type, elem: *parser.Element) T { const tag = parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(elem))); return switch (tag) { .a => .{ .HTMLAnchorElement = @as(*parser.Anchor, @ptrCast(elem)) },