mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-14 15:28:57 +00:00
Refactor the ScriptManager
This PR introduces two major changes to the ScriptManager. 1 - Simplification. Rather than having a `Script`, `PendingScript`, `AsyncModule` and `SyncModule`, there is only a `Script`, with an added `mode` union. All of the previous objects had the same behavior (collect the response in a buffer), up to the point of execution, which is where the mode comes in. 2 - Correctness Whether or not the previous version was "incorrect", it was difficult to use correctly. Specifically, the previous version would execute async scripts and async modules as soon as they're done. That seems allowed, but it caused issues with module loading in Context.js. Specifically, between compiling and instantiating a module, or between instantiation and evaluation, an async script or module could be evaluated. It isn't clear whether v8 allows that, but if it does, it introduces a lot of new potential states (specifically, unexpected changes to the v8.Module's status) that we have to handle. This version only evaluate scripts in the `evaluate`, which doesn't allow recursive calls (so a waitForImport, which continues to pump the HTTP loop, can never result in `evaluate` being called again). This undoes the change made in https://github.com/lightpanda-io/browser/pull/1158 because I do not think it's possible to have multiple waiters waiting for the same (or even different) modules. The linked issue points to a crash in https://www.nytimes.com which doesn't crash with this version.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -262,7 +262,7 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
|
||||
const owned_specifier = try self.arena.dupeZ(u8, normalized_specifier);
|
||||
gop.key_ptr.* = owned_specifier;
|
||||
gop.value_ptr.* = .{};
|
||||
try self.script_manager.?.getModule(owned_specifier, url);
|
||||
try self.script_manager.?.preloadImport(owned_specifier, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1213,17 +1213,17 @@ fn _resolveModuleCallback(self: *Context, referrer: v8.Module, specifier: []cons
|
||||
// harm in handling this case.
|
||||
@branchHint(.unlikely);
|
||||
gop.value_ptr.* = .{};
|
||||
try self.script_manager.?.getModule(normalized_specifier, referrer_path);
|
||||
try self.script_manager.?.preloadImport(normalized_specifier, referrer_path);
|
||||
}
|
||||
|
||||
var fetch_result = try self.script_manager.?.waitForModule(normalized_specifier);
|
||||
defer fetch_result.deinit();
|
||||
var source = try self.script_manager.?.waitForImport(normalized_specifier);
|
||||
defer source.deinit();
|
||||
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(self);
|
||||
defer try_catch.deinit();
|
||||
|
||||
const entry = self.module(true, fetch_result.src(), normalized_specifier, true) catch |err| {
|
||||
const entry = self.module(true, source.src(), normalized_specifier, true) catch |err| {
|
||||
switch (err) {
|
||||
error.EvaluationError => {
|
||||
// This is a sentinel value telling us that the error was already
|
||||
@@ -1297,7 +1297,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
||||
};
|
||||
|
||||
// Next, we need to actually load it.
|
||||
self.script_manager.?.getAsyncModule(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| {
|
||||
self.script_manager.?.getAsyncImport(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| {
|
||||
const error_msg = v8.String.initUtf8(isolate, @errorName(err));
|
||||
_ = resolver.reject(self.v8_context, error_msg.toValue());
|
||||
};
|
||||
@@ -1330,24 +1330,24 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
||||
return promise;
|
||||
}
|
||||
|
||||
fn dynamicModuleSourceCallback(ctx: *anyopaque, fetch_result_: anyerror!ScriptManager.GetResult) void {
|
||||
fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptManager.ModuleSource) void {
|
||||
const state: *DynamicModuleResolveState = @ptrCast(@alignCast(ctx));
|
||||
var self = state.context;
|
||||
|
||||
var fetch_result = fetch_result_ catch |err| {
|
||||
var ms = module_source_ catch |err| {
|
||||
const error_msg = v8.String.initUtf8(self.isolate, @errorName(err));
|
||||
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
|
||||
return;
|
||||
};
|
||||
|
||||
const module_entry = blk: {
|
||||
defer fetch_result.deinit();
|
||||
defer ms.deinit();
|
||||
|
||||
var try_catch: js.TryCatch = undefined;
|
||||
try_catch.init(self);
|
||||
defer try_catch.deinit();
|
||||
|
||||
break :blk self.module(true, fetch_result.src(), state.specifier, true) catch {
|
||||
break :blk self.module(true, ms.src(), state.specifier, true) catch {
|
||||
const ex = try_catch.exception(self.call_arena) catch |err| @errorName(err) orelse "unknown error";
|
||||
log.err(.js, "module compilation failed", .{
|
||||
.specifier = state.specifier,
|
||||
|
||||
Reference in New Issue
Block a user