mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
ScriptManager & HttpClient support for JS modules
Improve cleanup/shutdown (*cough* memory leaks *cough*)
This commit is contained in:
@@ -77,7 +77,6 @@ pub fn deinit(self: *ScriptManager) void {
|
||||
}
|
||||
|
||||
pub fn reset(self: *ScriptManager) void {
|
||||
self.client.abort();
|
||||
self.clearList(&self.scripts);
|
||||
self.clearList(&self.deferred);
|
||||
self.static_scripts_done = false;
|
||||
@@ -178,6 +177,8 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
|
||||
|
||||
if (source == .@"inline" and self.scripts.first == null) {
|
||||
// inline script with no pending scripts, execute it immediately.
|
||||
// (if there is a pending script, then we cannot execute this immediately
|
||||
// as it needs to be executed in order)
|
||||
return script.eval(page);
|
||||
}
|
||||
|
||||
@@ -187,7 +188,7 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
|
||||
.script = script,
|
||||
.complete = false,
|
||||
.manager = self,
|
||||
.node = undefined,
|
||||
.node = .{ .data = pending_script },
|
||||
};
|
||||
|
||||
if (source == .@"inline") {
|
||||
@@ -211,6 +212,34 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
|
||||
});
|
||||
}
|
||||
|
||||
pub fn blockingGet(self: *ScriptManager, url: [:0]const u8) !BlockingResult {
|
||||
var blocking = Blocking{
|
||||
.allocator = self.allocator,
|
||||
.buffer_pool = &self.buffer_pool,
|
||||
};
|
||||
|
||||
var client = self.client;
|
||||
try client.request(.{
|
||||
.url = url,
|
||||
.method = .GET,
|
||||
.ctx = &blocking,
|
||||
.header_done_callback = Blocking.headerCallback,
|
||||
.data_callback = Blocking.dataCallback,
|
||||
.done_callback = Blocking.doneCallback,
|
||||
.error_callback = Blocking.errorCallback,
|
||||
});
|
||||
|
||||
// rely on http's timeout settings to avoid an endless/long loop.
|
||||
while (true) {
|
||||
try client.tick(1000);
|
||||
switch (blocking.state) {
|
||||
.running => {},
|
||||
.done => |result| return result,
|
||||
.err => |err| return err,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn staticScriptsDone(self: *ScriptManager) void {
|
||||
std.debug.assert(self.static_scripts_done == false);
|
||||
self.static_scripts_done = true;
|
||||
@@ -350,6 +379,11 @@ const PendingScript = struct {
|
||||
if (self.manager.getList(&self.script)) |list| {
|
||||
self.node.data = self;
|
||||
list.append(&self.node);
|
||||
} else {
|
||||
// async scripts don't get added to a list, because we can execute
|
||||
// them in any order
|
||||
std.debug.assert(self.script.is_async);
|
||||
self.manager.async_count += 1;
|
||||
}
|
||||
|
||||
// if the script is async, it isn't tracked in a list, because we can
|
||||
@@ -530,8 +564,8 @@ const Script = struct {
|
||||
};
|
||||
|
||||
const BufferPool = struct {
|
||||
free: List = .{},
|
||||
available: usize,
|
||||
count: usize,
|
||||
available: List = .{},
|
||||
allocator: Allocator,
|
||||
max_concurrent_transfers: u8,
|
||||
node_pool: std.heap.MemoryPool(List.Node),
|
||||
@@ -540,8 +574,8 @@ const BufferPool = struct {
|
||||
|
||||
fn init(allocator: Allocator, max_concurrent_transfers: u8) BufferPool {
|
||||
return .{
|
||||
.free = .{},
|
||||
.available = 0,
|
||||
.available = .{},
|
||||
.count = 0,
|
||||
.allocator = allocator,
|
||||
.max_concurrent_transfers = max_concurrent_transfers,
|
||||
.node_pool = std.heap.MemoryPool(List.Node).init(allocator),
|
||||
@@ -551,20 +585,21 @@ const BufferPool = struct {
|
||||
fn deinit(self: *BufferPool) void {
|
||||
const allocator = self.allocator;
|
||||
|
||||
var node = self.free.first;
|
||||
var node = self.available.first;
|
||||
while (node) |n| {
|
||||
node = n.next;
|
||||
n.data.deinit(allocator);
|
||||
node = n.next;
|
||||
}
|
||||
self.node_pool.deinit();
|
||||
}
|
||||
|
||||
fn get(self: *BufferPool) ArrayListUnmanaged(u8) {
|
||||
const node = self.free.popFirst() orelse {
|
||||
const node = self.available.popFirst() orelse {
|
||||
// return a new buffer
|
||||
return .{};
|
||||
};
|
||||
|
||||
self.count -= 1;
|
||||
defer self.node_pool.destroy(node);
|
||||
return node.data;
|
||||
}
|
||||
@@ -575,8 +610,9 @@ const BufferPool = struct {
|
||||
// create mutable copy
|
||||
var b = buffer;
|
||||
|
||||
if (self.available == self.max_concurrent_transfers) {
|
||||
if (self.count == self.max_concurrent_transfers) {
|
||||
b.deinit(self.allocator);
|
||||
return;
|
||||
}
|
||||
|
||||
const node = self.node_pool.create() catch |err| {
|
||||
@@ -587,7 +623,81 @@ const BufferPool = struct {
|
||||
|
||||
b.clearRetainingCapacity();
|
||||
node.data = b;
|
||||
self.available += 1;
|
||||
self.free.append(node);
|
||||
self.count += 1;
|
||||
self.available.append(node);
|
||||
}
|
||||
};
|
||||
|
||||
const Blocking = struct {
|
||||
allocator: Allocator,
|
||||
buffer_pool: *BufferPool,
|
||||
state: State = .{ .running = {} },
|
||||
buffer: std.ArrayListUnmanaged(u8) = .{},
|
||||
|
||||
const State = union(enum) {
|
||||
running: void,
|
||||
err: anyerror,
|
||||
done: BlockingResult,
|
||||
};
|
||||
|
||||
fn startCallback(transfer: *HttpClient.Transfer) !void {
|
||||
log.debug(.http, "script fetch start", .{ .req = transfer, .blocking = true });
|
||||
}
|
||||
|
||||
fn headerCallback(transfer: *HttpClient.Transfer) !void {
|
||||
const header = &transfer.response_header.?;
|
||||
log.debug(.http, "script header", .{
|
||||
.req = transfer,
|
||||
.blocking = true,
|
||||
.status = header.status,
|
||||
.content_type = header.contentType(),
|
||||
});
|
||||
|
||||
if (header.status != 200) {
|
||||
return error.InvalidStatusCode;
|
||||
}
|
||||
|
||||
var self: *Blocking = @alignCast(@ptrCast(transfer.ctx));
|
||||
self.buffer = self.buffer_pool.get();
|
||||
}
|
||||
|
||||
fn dataCallback(transfer: *HttpClient.Transfer, data: []const u8) !void {
|
||||
var self: *Blocking = @alignCast(@ptrCast(transfer.ctx));
|
||||
self.buffer.appendSlice(self.allocator, data) catch |err| {
|
||||
log.err(.http, "SM.dataCallback", .{
|
||||
.err = err,
|
||||
.len = data.len,
|
||||
.blocking = true,
|
||||
.transfer = transfer,
|
||||
});
|
||||
return err;
|
||||
};
|
||||
}
|
||||
|
||||
fn doneCallback(ctx: *anyopaque) !void {
|
||||
var self: *Blocking = @alignCast(@ptrCast(ctx));
|
||||
self.state = .{ .done = .{
|
||||
.buffer = self.buffer,
|
||||
.buffer_pool = self.buffer_pool,
|
||||
} };
|
||||
}
|
||||
|
||||
fn errorCallback(ctx: *anyopaque, err: anyerror) void {
|
||||
var self: *Blocking = @alignCast(@ptrCast(ctx));
|
||||
self.state = .{ .err = err };
|
||||
self.buffer_pool.release(self.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
pub const BlockingResult = struct {
|
||||
buffer: std.ArrayListUnmanaged(u8),
|
||||
buffer_pool: *BufferPool,
|
||||
|
||||
pub fn deinit(self: *BlockingResult) void {
|
||||
self.buffer_pool.release(self.buffer);
|
||||
}
|
||||
|
||||
pub fn src(self: *const BlockingResult) []const u8 {
|
||||
return self.buffer.items;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -150,12 +150,13 @@ pub const Page = struct {
|
||||
}
|
||||
|
||||
fn reset(self: *Page) void {
|
||||
_ = self.session.browser.page_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 });
|
||||
// this will reset the http_client
|
||||
self.script_manager.reset();
|
||||
self.scheduler.reset();
|
||||
self.http_client.abort();
|
||||
self.script_manager.reset();
|
||||
|
||||
self.document_state = .parsing;
|
||||
self.mode = .{ .pre = {} };
|
||||
_ = self.session.browser.page_arena.reset(.{ .retain_with_limit = 1 * 1024 * 1024 });
|
||||
}
|
||||
|
||||
fn runMicrotasks(ctx: *anyopaque) ?u32 {
|
||||
@@ -229,13 +230,9 @@ pub const Page = struct {
|
||||
try Node.prepend(head, &[_]Node.NodeOrText{.{ .node = parser.elementToNode(base) }});
|
||||
}
|
||||
|
||||
pub fn fetchModuleSource(ctx: *anyopaque, src: []const u8) !?[]const u8 {
|
||||
_ = ctx;
|
||||
_ = src;
|
||||
// @newhttp
|
||||
return error.NewHTTP;
|
||||
// const self: *Page = @ptrCast(@alignCast(ctx));
|
||||
// return self.fetchData("module", src);
|
||||
pub fn fetchModuleSource(ctx: *anyopaque, src: [:0]const u8) !ScriptManager.BlockingResult {
|
||||
const self: *Page = @ptrCast(@alignCast(ctx));
|
||||
return self.script_manager.blockingGet(src);
|
||||
}
|
||||
|
||||
pub fn wait(self: *Page, wait_sec: usize) void {
|
||||
|
||||
@@ -52,6 +52,8 @@ transfer_pool: std.heap.MemoryPool(Transfer),
|
||||
queue_node_pool: std.heap.MemoryPool(RequestQueue.Node),
|
||||
//@newhttp
|
||||
http_proxy: ?std.Uri = null,
|
||||
blocking: Handle,
|
||||
blocking_active: if (builtin.mode == .Debug) bool else void = if (builtin.mode == .Debug) false else {},
|
||||
|
||||
const RequestQueue = std.DoublyLinkedList(Request);
|
||||
|
||||
@@ -69,13 +71,17 @@ pub fn init(allocator: Allocator, ca_blob: c.curl_blob, opts: Http.Opts) !*Clien
|
||||
errdefer _ = c.curl_multi_cleanup(multi);
|
||||
|
||||
var handles = try Handles.init(allocator, client, ca_blob, opts);
|
||||
errdefer handles.deinit(allocator, multi);
|
||||
errdefer handles.deinit(allocator);
|
||||
|
||||
var blocking = try Handle.init(client, ca_blob, opts);
|
||||
errdefer blocking.deinit();
|
||||
|
||||
client.* = .{
|
||||
.queue = .{},
|
||||
.active = 0,
|
||||
.multi = multi,
|
||||
.handles = handles,
|
||||
.blocking = blocking,
|
||||
.allocator = allocator,
|
||||
.transfer_pool = transfer_pool,
|
||||
.queue_node_pool = queue_node_pool,
|
||||
@@ -85,7 +91,10 @@ pub fn init(allocator: Allocator, ca_blob: c.curl_blob, opts: Http.Opts) !*Clien
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Client) void {
|
||||
self.handles.deinit(self.allocator, self.multi);
|
||||
self.abort();
|
||||
self.blocking.deinit();
|
||||
self.handles.deinit(self.allocator);
|
||||
|
||||
_ = c.curl_multi_cleanup(self.multi);
|
||||
|
||||
self.transfer_pool.deinit();
|
||||
@@ -94,7 +103,15 @@ pub fn deinit(self: *Client) void {
|
||||
}
|
||||
|
||||
pub fn abort(self: *Client) void {
|
||||
self.handles.abort(self.multi);
|
||||
while (self.handles.in_use.first) |node| {
|
||||
var transfer = Transfer.fromEasy(node.data.easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err, .source = "abort" });
|
||||
continue;
|
||||
};
|
||||
transfer.req.error_callback(transfer.ctx, error.Abort);
|
||||
self.endTransfer(transfer);
|
||||
}
|
||||
std.debug.assert(self.active == 0);
|
||||
|
||||
var n = self.queue.first;
|
||||
while (n) |node| {
|
||||
@@ -102,7 +119,6 @@ pub fn abort(self: *Client) void {
|
||||
self.queue_node_pool.destroy(node);
|
||||
}
|
||||
self.queue = .{};
|
||||
self.active = 0;
|
||||
|
||||
// Maybe a bit of overkill
|
||||
// We can remove some (all?) of these once we're confident its right.
|
||||
@@ -116,17 +132,18 @@ pub fn abort(self: *Client) void {
|
||||
}
|
||||
|
||||
pub fn tick(self: *Client, timeout_ms: usize) !void {
|
||||
var handles = &self.handles.available;
|
||||
var handles = &self.handles;
|
||||
while (true) {
|
||||
if (handles.first == null) {
|
||||
if (handles.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
const queue_node = self.queue.popFirst() orelse break;
|
||||
const req = queue_node.data;
|
||||
self.queue_node_pool.destroy(queue_node);
|
||||
|
||||
defer self.queue_node_pool.destroy(queue_node);
|
||||
|
||||
const handle = handles.popFirst().?.data;
|
||||
try self.makeRequest(handle, queue_node.data);
|
||||
// we know this exists, because we checked isEmpty() above
|
||||
const handle = handles.getFreeHandle().?;
|
||||
try self.makeRequest(handle, req);
|
||||
}
|
||||
|
||||
try self.perform(@intCast(timeout_ms));
|
||||
@@ -142,6 +159,19 @@ pub fn request(self: *Client, req: Request) !void {
|
||||
self.queue.append(node);
|
||||
}
|
||||
|
||||
// See ScriptManager.blockingGet
|
||||
pub fn blockingRequest(self: *Client, req: Request) !void {
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
std.debug.assert(self.blocking_active == false);
|
||||
self.blocking_active = true;
|
||||
}
|
||||
defer if (comptime builtin.mode == .Debug) {
|
||||
self.blocking_active = false;
|
||||
};
|
||||
|
||||
return self.makeRequest(&self.blocking, req);
|
||||
}
|
||||
|
||||
fn makeRequest(self: *Client, handle: *Handle, req: Request) !void {
|
||||
const easy = handle.easy;
|
||||
|
||||
@@ -215,8 +245,9 @@ fn perform(self: *Client, timeout_ms: c_int) !void {
|
||||
const ctx = transfer.ctx;
|
||||
const done_callback = transfer.req.done_callback;
|
||||
const error_callback = transfer.req.error_callback;
|
||||
// release it ASAP so that it's avaiable (since some done_callbacks
|
||||
// will load more resources).
|
||||
|
||||
// release it ASAP so that it's available; some done_callbacks
|
||||
// will load more resources.
|
||||
self.endTransfer(transfer);
|
||||
|
||||
if (errorCheck(msg.data.result)) {
|
||||
@@ -266,18 +297,9 @@ const Handles = struct {
|
||||
|
||||
var available: HandleList = .{};
|
||||
for (0..count) |i| {
|
||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
||||
errdefer _ = c.curl_easy_cleanup(easy);
|
||||
|
||||
handles[i] = .{
|
||||
.easy = easy,
|
||||
.client = client,
|
||||
.node = undefined,
|
||||
};
|
||||
try handles[i].configure(ca_blob, opts);
|
||||
|
||||
handles[i].node.data = &handles[i];
|
||||
available.append(&handles[i].node);
|
||||
handles[i] = try Handle.init(client, ca_blob, opts);
|
||||
handles[i].node = .{ .data = &handles[i] };
|
||||
available.append(&handles[i].node.?);
|
||||
}
|
||||
|
||||
return .{
|
||||
@@ -287,22 +309,15 @@ const Handles = struct {
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *Handles, allocator: Allocator, multi: *c.CURLM) void {
|
||||
self.abort(multi);
|
||||
fn deinit(self: *Handles, allocator: Allocator) void {
|
||||
for (self.handles) |*h| {
|
||||
_ = c.curl_easy_cleanup(h.easy);
|
||||
h.deinit();
|
||||
}
|
||||
allocator.free(self.handles);
|
||||
}
|
||||
|
||||
fn abort(self: *Handles, multi: *c.CURLM) void {
|
||||
while (self.in_use.first) |node| {
|
||||
const handle = node.data;
|
||||
errorMCheck(c.curl_multi_remove_handle(multi, handle.easy)) catch |err| {
|
||||
log.err(.http, "remove handle", .{ .err = err });
|
||||
};
|
||||
self.release(handle);
|
||||
}
|
||||
fn isEmpty(self: *const Handles) bool {
|
||||
return self.available.first == null;
|
||||
}
|
||||
|
||||
fn getFreeHandle(self: *Handles) ?*Handle {
|
||||
@@ -316,7 +331,10 @@ const Handles = struct {
|
||||
}
|
||||
|
||||
fn release(self: *Handles, handle: *Handle) void {
|
||||
const node = &handle.node;
|
||||
// client.blocking is a handle without a node, it doesn't exist in the
|
||||
// eitehr the in_use or available lists.
|
||||
const node = &(handle.node orelse return);
|
||||
|
||||
self.in_use.remove(node);
|
||||
node.prev = null;
|
||||
node.next = null;
|
||||
@@ -324,19 +342,15 @@ const Handles = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// wraps a c.CURL (an easy handle), mostly to make it easier to keep a
|
||||
// handle_pool and error_buffer associated with each easy handle
|
||||
// wraps a c.CURL (an easy handle)
|
||||
const Handle = struct {
|
||||
easy: *c.CURL,
|
||||
client: *Client,
|
||||
node: Handles.HandleList.Node,
|
||||
error_buffer: [c.CURL_ERROR_SIZE:0]u8 = undefined,
|
||||
node: ?Handles.HandleList.Node,
|
||||
|
||||
// Is called by Handles when already partially initialized. Done like this
|
||||
// so that we have a stable pointer to error_buffer.
|
||||
fn configure(self: *Handle, ca_blob: c.curl_blob, opts: Http.Opts) !void {
|
||||
const easy = self.easy;
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_ERRORBUFFER, &self.error_buffer));
|
||||
fn init(client: *Client, ca_blob: c.curl_blob, opts: Http.Opts) !Handle {
|
||||
const easy = c.curl_easy_init() orelse return error.FailedToInitializeEasy;
|
||||
errdefer _ = c.curl_easy_cleanup(easy);
|
||||
|
||||
// timeouts
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_TIMEOUT_MS, @as(c_long, @intCast(opts.timeout_ms))));
|
||||
@@ -348,9 +362,9 @@ const Handle = struct {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_REDIR_PROTOCOLS_STR, "HTTP,HTTPS")); // remove FTP and FTPS from the default
|
||||
|
||||
// callbacks
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERDATA, self));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERDATA, easy));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_HEADERFUNCTION, Transfer.headerCallback));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEDATA, self));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEDATA, easy));
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEFUNCTION, Transfer.bodyCallback));
|
||||
|
||||
// tls
|
||||
@@ -367,6 +381,16 @@ const Handle = struct {
|
||||
if (comptime Http.ENABLE_DEBUG) {
|
||||
try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_VERBOSE, @as(c_long, 1)));
|
||||
}
|
||||
|
||||
return .{
|
||||
.easy = easy,
|
||||
.node = null,
|
||||
.client = client,
|
||||
};
|
||||
}
|
||||
|
||||
fn deinit(self: *const Handle) void {
|
||||
_ = c.curl_easy_cleanup(self.easy);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -430,9 +454,9 @@ pub const Transfer = struct {
|
||||
// libcurl should only ever emit 1 header at a time
|
||||
std.debug.assert(header_count == 1);
|
||||
|
||||
const handle: *Handle = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(handle.easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err });
|
||||
const easy: *c.CURL = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err, .source = "header callback" });
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -467,7 +491,7 @@ pub const Transfer = struct {
|
||||
transfer._redirecting = false;
|
||||
|
||||
var url: [*c]u8 = undefined;
|
||||
errorCheck(c.curl_easy_getinfo(handle.easy, c.CURLINFO_EFFECTIVE_URL, &url)) catch |err| {
|
||||
errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_EFFECTIVE_URL, &url)) catch |err| {
|
||||
log.err(.http, "failed to get URL", .{ .err = err });
|
||||
return 0;
|
||||
};
|
||||
@@ -514,9 +538,9 @@ pub const Transfer = struct {
|
||||
// libcurl should only ever emit 1 chunk at a time
|
||||
std.debug.assert(chunk_count == 1);
|
||||
|
||||
const handle: *Handle = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(handle.easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err });
|
||||
const easy: *c.CURL = @alignCast(@ptrCast(data));
|
||||
var transfer = fromEasy(easy) catch |err| {
|
||||
log.err(.http, "get private info", .{ .err = err, .source = "body callback" });
|
||||
return c.CURL_WRITEFUNC_ERROR;
|
||||
};
|
||||
|
||||
|
||||
@@ -668,7 +668,11 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
|
||||
const ModuleLoader = struct {
|
||||
ptr: *anyopaque,
|
||||
func: *const fn (ptr: *anyopaque, specifier: []const u8) anyerror!?[]const u8,
|
||||
func: *const fn (ptr: *anyopaque, url: [:0]const u8) anyerror!BlockingResult,
|
||||
|
||||
// Don't like having to reach into ../browser/ here. But can't think
|
||||
// of a good way to fix this.
|
||||
const BlockingResult = @import("../browser/ScriptManager.zig").BlockingResult;
|
||||
};
|
||||
|
||||
// no init, started with executor.createJsContext()
|
||||
@@ -1416,11 +1420,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn _resolveModuleCallback(
|
||||
self: *JsContext,
|
||||
referrer: v8.Module,
|
||||
specifier: []const u8,
|
||||
) !?*const v8.C_Module {
|
||||
fn _resolveModuleCallback(self: *JsContext, referrer: v8.Module, specifier: []const u8) !?*const v8.C_Module {
|
||||
const referrer_path = self.module_identifier.get(referrer.getIdentityHash()) orelse {
|
||||
// Shouldn't be possible.
|
||||
return error.UnknownModuleReferrer;
|
||||
@@ -1430,29 +1430,32 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
self.call_arena,
|
||||
specifier,
|
||||
referrer_path,
|
||||
.{ .alloc = .if_needed },
|
||||
.{ .alloc = .if_needed, .null_terminated = true },
|
||||
);
|
||||
|
||||
if (self.module_cache.get(normalized_specifier)) |pm| {
|
||||
return pm.handle;
|
||||
}
|
||||
|
||||
const module_loader = self.module_loader;
|
||||
const source = try module_loader.func(module_loader.ptr, normalized_specifier) orelse return null;
|
||||
const m: v8.Module = blk: {
|
||||
const module_loader = self.module_loader;
|
||||
var fetch_result = try module_loader.func(module_loader.ptr, normalized_specifier);
|
||||
defer fetch_result.deinit();
|
||||
|
||||
var try_catch: TryCatch = undefined;
|
||||
try_catch.init(self);
|
||||
defer try_catch.deinit();
|
||||
var try_catch: TryCatch = undefined;
|
||||
try_catch.init(self);
|
||||
defer try_catch.deinit();
|
||||
|
||||
const m = compileModule(self.isolate, source, normalized_specifier) catch |err| {
|
||||
log.warn(.js, "compile resolved module", .{
|
||||
.specifier = specifier,
|
||||
.stack = try_catch.stack(self.call_arena) catch null,
|
||||
.src = try_catch.sourceLine(self.call_arena) catch "err",
|
||||
.line = try_catch.sourceLineNumber() orelse 0,
|
||||
.exception = (try_catch.exception(self.call_arena) catch @errorName(err)) orelse @errorName(err),
|
||||
});
|
||||
return null;
|
||||
break :blk compileModule(self.isolate, fetch_result.src(), normalized_specifier) catch |err| {
|
||||
log.warn(.js, "compile resolved module", .{
|
||||
.specifier = specifier,
|
||||
.stack = try_catch.stack(self.call_arena) catch null,
|
||||
.src = try_catch.sourceLine(self.call_arena) catch "err",
|
||||
.line = try_catch.sourceLineNumber() orelse 0,
|
||||
.exception = (try_catch.exception(self.call_arena) catch @errorName(err)) orelse @errorName(err),
|
||||
});
|
||||
return null;
|
||||
};
|
||||
};
|
||||
|
||||
// We were hoping to find the module in our cache, and thus used
|
||||
@@ -1568,7 +1571,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
context.context_arena,
|
||||
specifier_str,
|
||||
resource_str,
|
||||
.{ .alloc = .if_needed },
|
||||
.{ .alloc = .if_needed, .null_terminated = true },
|
||||
) catch unreachable;
|
||||
|
||||
log.debug(.js, "dynamic import", .{
|
||||
@@ -1590,41 +1593,41 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
|
||||
fn _dynamicModuleCallback(
|
||||
self: *JsContext,
|
||||
specifier: []const u8,
|
||||
specifier: [:0]const u8,
|
||||
resolver: *const v8.PromiseResolver,
|
||||
) !void {
|
||||
const iso = self.isolate;
|
||||
const ctx = self.v8_context;
|
||||
|
||||
const module_loader = self.module_loader;
|
||||
const source = module_loader.func(module_loader.ptr, specifier) catch {
|
||||
const error_msg = v8.String.initUtf8(iso, "Failed to load module");
|
||||
_ = resolver.reject(ctx, error_msg.toValue());
|
||||
return;
|
||||
} orelse {
|
||||
const error_msg = v8.String.initUtf8(iso, "Module source not available");
|
||||
_ = resolver.reject(ctx, error_msg.toValue());
|
||||
return;
|
||||
};
|
||||
|
||||
var try_catch: TryCatch = undefined;
|
||||
try_catch.init(self);
|
||||
defer try_catch.deinit();
|
||||
|
||||
const maybe_promise = self.module(source, specifier, true) catch {
|
||||
log.err(.js, "module compilation failed", .{
|
||||
.specifier = specifier,
|
||||
.exception = try_catch.exception(self.call_arena) catch "unknown error",
|
||||
.stack = try_catch.stack(self.call_arena) catch null,
|
||||
.line = try_catch.sourceLineNumber() orelse 0,
|
||||
});
|
||||
const error_msg = if (try_catch.hasCaught()) blk: {
|
||||
const exception_str = try_catch.exception(self.call_arena) catch "Evaluation error";
|
||||
break :blk v8.String.initUtf8(iso, exception_str orelse "Evaluation error");
|
||||
} else v8.String.initUtf8(iso, "Module evaluation failed");
|
||||
_ = resolver.reject(ctx, error_msg.toValue());
|
||||
return;
|
||||
const maybe_promise: ?v8.Promise = blk: {
|
||||
const module_loader = self.module_loader;
|
||||
var fetch_result = module_loader.func(module_loader.ptr, specifier) catch {
|
||||
const error_msg = v8.String.initUtf8(iso, "Failed to load module");
|
||||
_ = resolver.reject(ctx, error_msg.toValue());
|
||||
return;
|
||||
};
|
||||
defer fetch_result.deinit();
|
||||
|
||||
break :blk self.module(fetch_result.src(), specifier, true) catch {
|
||||
log.err(.js, "module compilation failed", .{
|
||||
.specifier = specifier,
|
||||
.exception = try_catch.exception(self.call_arena) catch "unknown error",
|
||||
.stack = try_catch.stack(self.call_arena) catch null,
|
||||
.line = try_catch.sourceLineNumber() orelse 0,
|
||||
});
|
||||
const error_msg = if (try_catch.hasCaught()) eblk: {
|
||||
const exception_str = try_catch.exception(self.call_arena) catch "Evaluation error";
|
||||
break :eblk v8.String.initUtf8(iso, exception_str orelse "Evaluation error");
|
||||
} else v8.String.initUtf8(iso, "Module evaluation failed");
|
||||
_ = resolver.reject(ctx, error_msg.toValue());
|
||||
return;
|
||||
};
|
||||
};
|
||||
|
||||
const new_module = self.module_cache.get(specifier).?.castToModule();
|
||||
|
||||
if (maybe_promise) |promise| {
|
||||
@@ -3815,7 +3818,11 @@ const NoopInspector = struct {
|
||||
};
|
||||
|
||||
const ErrorModuleLoader = struct {
|
||||
pub fn fetchModuleSource(_: *anyopaque, _: []const u8) !?[]const u8 {
|
||||
// Don't like having to reach into ../browser/ here. But can't think
|
||||
// of a good way to fix this.
|
||||
const BlockingResult = @import("../browser/ScriptManager.zig").BlockingResult;
|
||||
|
||||
pub fn fetchModuleSource(_: *anyopaque, _: [:0]const u8) !BlockingResult {
|
||||
return error.NoModuleLoadConfigured;
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user