Skip to content

Commit 7fefbac

Browse files
committed
use module() for dynamic imports
1 parent 766d982 commit 7fefbac

File tree

2 files changed

+59
-92
lines changed

2 files changed

+59
-92
lines changed

src/browser/page.zig

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,12 +1012,16 @@ const Script = struct {
10121012
.cacheable = cacheable,
10131013
});
10141014

1015-
const result = switch (self.kind) {
1016-
.javascript => page.main_context.eval(body, src),
1017-
.module => page.main_context.module(body, src, cacheable),
1015+
const failed = blk: {
1016+
switch (self.kind) {
1017+
.javascript => _ = page.main_context.eval(body, src) catch break :blk true,
1018+
// We don't care about waiting for the evaluation here.
1019+
.module => _ = page.main_context.module(body, src, cacheable) catch break :blk true,
1020+
}
1021+
break :blk false;
10181022
};
10191023

1020-
result catch {
1024+
if (failed) {
10211025
if (page.delayed_navigation) {
10221026
return error.Terminated;
10231027
}
@@ -1032,7 +1036,8 @@ const Script = struct {
10321036

10331037
try self.executeCallback("onerror", page);
10341038
return error.JsErr;
1035-
};
1039+
}
1040+
10361041
try self.executeCallback("onload", page);
10371042
}
10381043

src/runtime/js.zig

Lines changed: 49 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -695,17 +695,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
695695
}
696696

697697
// compile and eval a JS module
698-
// It doesn't wait for callbacks execution
699-
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !void {
700-
if (!cacheable) {
701-
return self.moduleNoCache(src, url);
702-
}
703-
698+
// It returns null if the module is already compiled and in the cache.
699+
// It returns a v8.Promise if the module must be evaluated.
700+
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !?v8.Promise {
704701
const arena = self.context_arena;
705702

706-
const gop = try self.module_cache.getOrPut(arena, url);
707-
if (gop.found_existing) {
708-
return;
703+
if (cacheable) {
704+
const value = self.module_cache.get(url);
705+
if (value != null) return null;
709706
}
710707
errdefer _ = self.module_cache.remove(url);
711708

@@ -715,28 +712,22 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
715712
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
716713
errdefer _ = self.module_identifier.remove(m.getIdentityHash());
717714

718-
gop.key_ptr.* = owned_url;
719-
gop.value_ptr.* = PersistentModule.init(self.isolate, m);
715+
if (cacheable) {
716+
try self.module_cache.put(arena, owned_url, PersistentModule.init(self.isolate, m));
717+
}
720718

721719
// resolveModuleCallback loads module's dependencies.
722720
const v8_context = self.v8_context;
723721
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
724722
return error.ModuleInstantiationError;
725723
}
726724

727-
_ = try m.evaluate(v8_context);
728-
}
729-
730-
fn moduleNoCache(self: *JsContext, src: []const u8, url: []const u8) !void {
731-
const m = try compileModule(self.isolate, src, url);
732-
const v8_context = self.v8_context;
733-
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
734-
return error.ModuleInstantiationError;
735-
}
736-
const arena = self.context_arena;
737-
const owned_url = try arena.dupe(u8, url);
738-
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
739-
_ = try m.evaluate(v8_context);
725+
const evaluated = try m.evaluate(v8_context);
726+
// https://v8.github.io/api/head/classv8_1_1Module.html#a1f1758265a4082595757c3251bb40e0f
727+
// Must be a promise that gets returned here.
728+
std.debug.assert(evaluated.isPromise());
729+
const promise = v8.Promise{ .handle = evaluated.handle };
730+
return promise;
740731
}
741732

742733
// Wrap a v8.Exception
@@ -1412,27 +1403,28 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
14121403
.{ .alloc = .if_needed },
14131404
) catch unreachable;
14141405

1415-
_dynamicModuleCallback(context, resource_str, normalized_specifier, &resolver);
1406+
log.debug(.js, "dynamic import", .{
1407+
.specifier = specifier_str,
1408+
.resource = resource_str,
1409+
.referrer_full = referrer_full_url,
1410+
.normalized_specifier = normalized_specifier,
1411+
});
1412+
1413+
_dynamicModuleCallback(context, normalized_specifier, &resolver) catch |err| {
1414+
log.err(.js, "dynamic module callback", .{
1415+
.err = err,
1416+
});
1417+
// Must be rejected at this point
1418+
// otherwise, we will just wait on a pending promise.
1419+
std.debug.assert(resolver.getPromise().getState() == .kRejected);
1420+
};
14161421
return @constCast(resolver.getPromise().handle);
14171422
}
14181423

1419-
fn _dynamicModuleCallback(context: *JsContext, resource: []const u8, specifier: []const u8, resolver: *const v8.PromiseResolver) void {
1424+
fn _dynamicModuleCallback(context: *JsContext, specifier: []const u8, resolver: *const v8.PromiseResolver) !void {
14201425
const iso = context.isolate;
14211426
const ctx = context.v8_context;
14221427

1423-
// Check module cache first.
1424-
if (context.module_cache.get(specifier)) |cached_module| {
1425-
const namespace = cached_module.castToModule().getModuleNamespace();
1426-
_ = resolver.resolve(ctx, namespace);
1427-
return;
1428-
}
1429-
1430-
log.info(.js, "dynamic import", .{
1431-
.specifier = specifier,
1432-
.resource = resource,
1433-
.normalized = specifier,
1434-
});
1435-
14361428
const module_loader = context.module_loader;
14371429
const source = module_loader.func(module_loader.ptr, specifier) catch {
14381430
const error_msg = v8.String.initUtf8(iso, "Failed to load module");
@@ -1448,71 +1440,30 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
14481440
try_catch.init(context);
14491441
defer try_catch.deinit();
14501442

1451-
const new_module = compileModule(iso, source, specifier) catch {
1443+
const maybe_promise = context.module(source, specifier, true) catch {
14521444
log.err(.js, "module compilation failed", .{
14531445
.specifier = specifier,
14541446
.exception = try_catch.exception(context.call_arena) catch "unknown error",
14551447
.stack = try_catch.stack(context.call_arena) catch null,
1456-
});
1457-
const error_msg = if (try_catch.hasCaught()) blk: {
1458-
const exception_str = try_catch.exception(context.call_arena) catch "Compilation error";
1459-
break :blk v8.String.initUtf8(iso, exception_str orelse "Compilation error");
1460-
} else v8.String.initUtf8(iso, "Module compilation failed");
1461-
1462-
_ = resolver.reject(ctx, error_msg.toValue());
1463-
return;
1464-
};
1465-
1466-
// Insert into Module Cache.
1467-
context.module_identifier.putNoClobber(context.context_arena, new_module.getIdentityHash(), specifier) catch unreachable;
1468-
context.module_cache.putNoClobber(context.context_arena, specifier, v8.Persistent(v8.Module).init(iso, new_module)) catch unreachable;
1469-
1470-
const instantiated = new_module.instantiate(ctx, JsContext.resolveModuleCallback) catch {
1471-
log.err(.js, "module instantiation failed", .{
1472-
.specifier = specifier,
1473-
.exception = try_catch.exception(context.call_arena) catch "unknown error",
1474-
.stack = try_catch.stack(context.call_arena) catch null,
1475-
});
1476-
const error_msg = if (try_catch.hasCaught()) blk: {
1477-
const exception_str = try_catch.exception(context.call_arena) catch "Instantiation error";
1478-
break :blk v8.String.initUtf8(iso, exception_str orelse "Instantiation error");
1479-
} else v8.String.initUtf8(iso, "Module instantiation failed");
1480-
1481-
_ = resolver.reject(ctx, error_msg.toValue());
1482-
return;
1483-
};
1484-
1485-
if (!instantiated) {
1486-
const error_msg = v8.String.initUtf8(iso, "Module did not instantiate");
1487-
_ = resolver.reject(ctx, error_msg.toValue());
1488-
return;
1489-
}
1490-
1491-
const evaluated = new_module.evaluate(ctx) catch {
1492-
log.err(.js, "module evaluation failed", .{
1493-
.specifier = specifier,
1494-
.exception = try_catch.exception(context.call_arena) catch "unknown error",
1495-
.stack = try_catch.stack(context.call_arena) catch null,
14961448
.line = try_catch.sourceLineNumber() orelse 0,
14971449
});
14981450
const error_msg = if (try_catch.hasCaught()) blk: {
14991451
const exception_str = try_catch.exception(context.call_arena) catch "Evaluation error";
15001452
break :blk v8.String.initUtf8(iso, exception_str orelse "Evaluation error");
15011453
} else v8.String.initUtf8(iso, "Module evaluation failed");
1502-
15031454
_ = resolver.reject(ctx, error_msg.toValue());
15041455
return;
15051456
};
1457+
const new_module = context.module_cache.get(specifier).?.castToModule();
15061458

1507-
if (evaluated.isPromise()) {
1508-
const promise = v8.Promise{ .handle = evaluated.handle };
1509-
1459+
if (maybe_promise) |promise| {
1460+
// This means we must wait for the evaluation.
15101461
const EvaluationData = struct {
15111462
module: v8.Persistent(v8.Module),
15121463
resolver: v8.Persistent(v8.PromiseResolver),
15131464
};
15141465

1515-
const ev_data = context.context_arena.create(EvaluationData) catch unreachable;
1466+
const ev_data = try context.context_arena.create(EvaluationData);
15161467
ev_data.* = .{
15171468
.module = v8.Persistent(v8.Module).init(iso, new_module),
15181469
.resolver = v8.Persistent(v8.PromiseResolver).init(iso, resolver.*),
@@ -1529,7 +1480,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15291480
const cb_resolver = data.resolver.castToPromiseResolver();
15301481

15311482
const namespace = cb_module.getModuleNamespace();
1532-
log.warn(.js, "module then promise", .{
1483+
log.info(.js, "dynamic import complete", .{
1484+
.module = cb_module,
15331485
.namespace = namespace,
15341486
});
15351487
_ = cb_resolver.resolve(cb_context, namespace);
@@ -1538,11 +1490,15 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15381490

15391491
const catch_callback = v8.Function.initWithData(ctx, struct {
15401492
pub fn callback(info: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void {
1541-
log.warn(.js, "module catch promise", .{});
15421493
const cb_info = v8.FunctionCallbackInfo{ .handle = info.? };
15431494
const cb_context = cb_info.getIsolate().getCurrentContext();
15441495
const data: *EvaluationData = @ptrCast(@alignCast(cb_info.getExternalValue()));
1496+
const cb_module = data.module.castToModule();
15451497
const cb_resolver = data.resolver.castToPromiseResolver();
1498+
1499+
log.err(.js, "dynamic import failed", .{
1500+
.module = cb_module,
1501+
});
15461502
_ = cb_resolver.reject(cb_context, cb_info.getData());
15471503
}
15481504
}.callback, external);
@@ -1557,8 +1513,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15571513
return;
15581514
};
15591515
} else {
1516+
// This means it is already present in the cache.
15601517
const namespace = new_module.getModuleNamespace();
1518+
log.info(.js, "dynamic import complete", .{
1519+
.module = new_module,
1520+
.namespace = namespace,
1521+
});
15611522
_ = resolver.resolve(ctx, namespace);
1523+
return;
15621524
}
15631525
}
15641526
};

0 commit comments

Comments
 (0)