diff --git a/src/browser/browser.zig b/src/browser/browser.zig index 6cd043c6..0508a391 100644 --- a/src/browser/browser.zig +++ b/src/browser/browser.zig @@ -134,6 +134,9 @@ pub const Page = struct { self.session.env.stop(); // TODO unload document: https://html.spec.whatwg.org/#unloading-documents + // clear netsurf memory arena. + parser.deinit(); + _ = self.arena.reset(.free_all); } @@ -211,6 +214,9 @@ pub const Page = struct { fn loadHTMLDoc(self: *Page, reader: anytype, charset: []const u8) !void { const alloc = self.arena.allocator(); + // start netsurf memory arena. + try parser.init(); + log.debug("parse html with charset {s}", .{charset}); const ccharset = try alloc.dupeZ(u8, charset); diff --git a/src/calloc.zig b/src/calloc.zig deleted file mode 100644 index fd2ceacb..00000000 --- a/src/calloc.zig +++ /dev/null @@ -1,164 +0,0 @@ -// MIT License -// Copyright 2024 Lightpanda -// Original copyright 2021 pfg and marler8997 -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -// associated documentation files (the "Software"), to deal in the Software without restriction, -// including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all copies or substantial -// portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT -// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -// --------------------------- - -// This file is a mix between: -// - malloc functions of ziglibc (https://github.com/marler8997/ziglibc/blob/main/src/cstd.zig) -// for the general logic -// - this gist https://gist.github.com/pfgithub/65c13d7dc889a4b2ba25131994be0d20 -// for the header "magic" validator -// + some refacto and comments to make the code more clear - -const std = @import("std"); - -// TODO: this uses a global variable -// it does not allow to set a context-based allocator -var alloc: std.mem.Allocator = undefined; - -pub fn setCAllocator(allocator: std.mem.Allocator) void { - alloc = allocator; -} - -// Alloc mechanism -// --------------- - -// C malloc does not know the type of the buffer allocated, -// instead it uses a metadata header at the begining of the buffer to store the allocated size. -// We copy this behavior by allocating in Zig the size requested + the size of the header. -// On this header we store not only the size allocated but also a "magic" validator -// to check if the C pointer as been allocated through those cutom malloc functions. - -// The total buffer looks like that: -// [Zig buf] = [header][C pointer] - -const al = @alignOf(std.c.max_align_t); - -const Header = struct { - comptime { - if (@alignOf(Header) > al) @compileError("oops"); - } - - const len = std.mem.alignForward(usize, al, @sizeOf(Header)); - - const MAGIC = 0xABCDEF; - const NOMAGIC = 0; - - magic: usize = MAGIC, - size: usize, -}; - -// Buffer manipulation functions - -// setHeader on a buffer allocated in Zig -inline fn setHeader(buf: anytype, size: usize) void { - // cast buffer to an header - const header = @as(*Header, @ptrCast(buf)); - // and set the relevant information on it (size and "magic" validator) - header.* = .{ .size = size }; -} - -// getHeader from a C pointer -fn getHeader(ptr: [*]u8) *Header { - // use arithmetic to get (ie. backward) the buffer pointer from the C pointer - const buf = ptr - Header.len; - // convert many-item pointer to single pointer and cast to an header - // return @ptrFromInt(@intFromPtr(buf)); - // and cast it to an header pointer - return @ptrCast(@as([*]align(@alignOf(*Header)) u8, @alignCast(buf))); -} - -// getBuf from an header -fn getBuf(header: *Header) []align(al) u8 { - // cast header pointer to a many-item buffer pointer - const buf_ptr = @as([*]u8, @ptrCast(header)); - // return the buffer with corresponding length - const buf = buf_ptr[0..header.size]; - return @alignCast(buf); -} - -inline fn cPtr(buf: [*]align(al) u8) [*]align(al) u8 { - // use arithmetic to get (ie. forward) the C pointer from the buffer pointer - return buf + Header.len; -} - -// Custom malloc functions - -pub export fn m_alloc(size: usize) callconv(.C) ?[*]align(al) u8 { - std.debug.assert(size > 0); // TODO: what should we do in this case? - const buf_len = Header.len + size; - const buf = alloc.alignedAlloc(u8, al, buf_len) catch |err| switch (err) { - error.OutOfMemory => return null, - }; - setHeader(buf, buf_len); - return cPtr(buf.ptr); -} - -pub export fn re_alloc(ptr: ?[*]align(al) u8, size: usize) callconv(.C) ?[*]align(al) u8 { - if (ptr == null) return m_alloc(size); - const header = getHeader(ptr.?); - const buf = getBuf(header); - if (size == 0) { - alloc.free(buf); - return null; - } - - const buf_len = Header.len + size; - if (alloc.rawResize(buf, std.math.log2(al), buf_len, @returnAddress())) { - setHeader(buf.ptr, buf_len); - return ptr; - } - - const new_buf = alloc.reallocAdvanced( - buf, - buf_len, - @returnAddress(), - ) catch |e| switch (e) { - error.OutOfMemory => return null, - }; - setHeader(new_buf.ptr, buf_len); - return cPtr(new_buf.ptr); -} - -export fn c_alloc(nmemb: usize, size: usize) callconv(.C) ?[*]align(al) u8 { - const total = std.math.mul(usize, nmemb, size) catch { - // TODO: set errno - // errno = c.ENOMEM; - return null; - }; - const ptr = m_alloc(total) orelse return null; - @memset(ptr[0..total], 0); - return ptr; -} - -pub export fn f_ree(ptr: ?[*]align(al) u8) callconv(.C) void { - if (ptr == null) return; - - // check header - const header = getHeader(ptr.?); - if (header.magic != Header.MAGIC) { - // either doble-free or allocated outside those custom mallocs - // TODO: why? - return; - } - header.magic = Header.NOMAGIC; // prevent double free - - const buf = getBuf(header); - alloc.free(buf); -} diff --git a/src/dom/namednodemap.zig b/src/dom/namednodemap.zig index 3e1793f1..cbca8c7d 100644 --- a/src/dom/namednodemap.zig +++ b/src/dom/namednodemap.zig @@ -73,9 +73,7 @@ pub fn testExecFn( .{ .src = "a.item(1)", .ex = "null" }, .{ .src = "a.getNamedItem('id')", .ex = "[object Attr]" }, .{ .src = "a.getNamedItem('foo')", .ex = "null" }, - // TODO: with setCAllocator this test fails with a segfault - // see https://github.com/lightpanda-io/browsercore/issues/197 - // .{ .src = "a.setNamedItem(a.getNamedItem('id'))", .ex = "[object Attr]" }, + .{ .src = "a.setNamedItem(a.getNamedItem('id'))", .ex = "[object Attr]" }, }; try checkCases(js_env, &setItem); } diff --git a/src/main.zig b/src/main.zig index 1dc34dc6..2d6aa4bc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -56,6 +56,9 @@ pub fn main() !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); + try parser.init(); + defer parser.deinit(); + // document const file = try std.fs.cwd().openFile("test.html", .{}); defer file.close(); diff --git a/src/main_get.zig b/src/main_get.zig index 15c4f90e..de6191c6 100644 --- a/src/main_get.zig +++ b/src/main_get.zig @@ -2,7 +2,6 @@ const std = @import("std"); const Browser = @import("browser/browser.zig").Browser; const jsruntime = @import("jsruntime"); -const setCAllocator = @import("calloc.zig").setCAllocator; const apiweb = @import("apiweb.zig"); pub const Types = jsruntime.reflect(apiweb.Interfaces); @@ -30,10 +29,6 @@ pub fn main() !void { } const allocator = gpa.allocator(); - var c_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer c_arena.deinit(); - setCAllocator(c_arena.allocator()); - var args = try std.process.argsWithAllocator(allocator); defer args.deinit(); @@ -70,9 +65,10 @@ pub fn main() !void { defer browser.deinit(); var page = try browser.currentSession().createPage(); - defer page.end(); + defer page.deinit(); try page.navigate(url); + defer page.end(); if (dump) { try page.dump(std.io.getStdOut()); diff --git a/src/main_shell.zig b/src/main_shell.zig index f6d4941c..43271b3d 100644 --- a/src/main_shell.zig +++ b/src/main_shell.zig @@ -1,7 +1,6 @@ const std = @import("std"); const jsruntime = @import("jsruntime"); -const setCAllocator = @import("calloc.zig").setCAllocator; const parser = @import("netsurf.zig"); const apiweb = @import("apiweb.zig"); @@ -38,9 +37,8 @@ pub fn main() !void { var arena = std.heap.ArenaAllocator.init(gpa.allocator()); defer arena.deinit(); - var c_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer c_arena.deinit(); - setCAllocator(c_arena.allocator()); + try parser.init(); + defer parser.deinit(); // document const file = try std.fs.cwd().openFile("test.html", .{}); diff --git a/src/run_tests.zig b/src/run_tests.zig index 8220336c..14a235b7 100644 --- a/src/run_tests.zig +++ b/src/run_tests.zig @@ -5,7 +5,6 @@ const jsruntime = @import("jsruntime"); const generate = @import("generate.zig"); const pretty = @import("pretty"); -const setCAllocator = @import("calloc.zig").setCAllocator; const parser = @import("netsurf.zig"); const apiweb = @import("apiweb.zig"); const Window = @import("html/window.zig").Window; @@ -39,6 +38,9 @@ fn testExecFn( js_env: *jsruntime.Env, comptime execFn: jsruntime.ContextExecFn, ) anyerror!void { + try parser.init(); + defer parser.deinit(); + // start JS env try js_env.start(alloc); defer js_env.stop(); @@ -88,7 +90,6 @@ fn testsAllExecFn( inline for (testFns) |testFn| { try testExecFn(alloc, js_env, testFn); - _ = c_arena.reset(.retain_capacity); } } @@ -117,8 +118,6 @@ const Run = enum { unit, }; -var c_arena: std.heap.ArenaAllocator = undefined; - pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); @@ -152,10 +151,6 @@ pub fn main() !void { } } - c_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - defer c_arena.deinit(); - setCAllocator(c_arena.allocator()); - // run js tests if (run == .all or run == .browser) try run_js(out); @@ -163,6 +158,9 @@ pub fn main() !void { if (run == .all or run == .unit) { std.debug.print("\n", .{}); for (builtin.test_functions) |test_fn| { + try parser.init(); + defer parser.deinit(); + try test_fn.func(); std.debug.print("{s}\tOK\n", .{test_fn.name}); } diff --git a/src/wpt/run.zig b/src/wpt/run.zig index 686363c0..467436a5 100644 --- a/src/wpt/run.zig +++ b/src/wpt/run.zig @@ -17,6 +17,8 @@ const Types = @import("../main_wpt.zig").Types; // It loads first the js libs files. pub fn run(arena: *std.heap.ArenaAllocator, comptime dir: []const u8, f: []const u8, loader: *FileLoader) !jsruntime.JSResult { const alloc = arena.allocator(); + try parser.init(); + defer parser.deinit(); // document const file = try std.fs.cwd().openFile(f, .{});