Skip to content

Commit f521621

Browse files
committed
use module() for dynamic imports
1 parent 7f6c628 commit f521621

File tree

2 files changed

+59
-95
lines changed

2 files changed

+59
-95
lines changed

src/browser/page.zig

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,12 +1030,16 @@ const Script = struct {
10301030
.cacheable = cacheable,
10311031
});
10321032

1033-
const result = switch (self.kind) {
1034-
.javascript => page.main_context.eval(body, src),
1035-
.module => page.main_context.module(body, src, cacheable),
1033+
const failed = blk: {
1034+
switch (self.kind) {
1035+
.javascript => _ = page.main_context.eval(body, src) catch break :blk true,
1036+
// We don't care about waiting for the evaluation here.
1037+
.module => _ = page.main_context.module(body, src, cacheable) catch break :blk true,
1038+
}
1039+
break :blk false;
10361040
};
10371041

1038-
result catch {
1042+
if (failed) {
10391043
if (page.delayed_navigation) {
10401044
return error.Terminated;
10411045
}
@@ -1050,7 +1054,8 @@ const Script = struct {
10501054

10511055
try self.executeCallback("onerror", page);
10521056
return error.JsErr;
1053-
};
1057+
}
1058+
10541059
try self.executeCallback("onload", page);
10551060
}
10561061

src/runtime/js.zig

Lines changed: 49 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -743,17 +743,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
743743
}
744744

745745
// compile and eval a JS module
746-
// It doesn't wait for callbacks execution
747-
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !void {
748-
if (!cacheable) {
749-
return self.moduleNoCache(src, url);
750-
}
751-
746+
// It returns null if the module is already compiled and in the cache.
747+
// It returns a v8.Promise if the module must be evaluated.
748+
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !?v8.Promise {
752749
const arena = self.context_arena;
753750

754-
const gop = try self.module_cache.getOrPut(arena, url);
755-
if (gop.found_existing) {
756-
return;
751+
if (cacheable) {
752+
const value = self.module_cache.get(url);
753+
if (value != null) return null;
757754
}
758755
errdefer _ = self.module_cache.remove(url);
759756

@@ -763,30 +760,22 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
763760
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
764761
errdefer _ = self.module_identifier.remove(m.getIdentityHash());
765762

766-
gop.key_ptr.* = owned_url;
767-
gop.value_ptr.* = PersistentModule.init(self.isolate, m);
763+
if (cacheable) {
764+
try self.module_cache.put(arena, owned_url, PersistentModule.init(self.isolate, m));
765+
}
768766

769767
// resolveModuleCallback loads module's dependencies.
770768
const v8_context = self.v8_context;
771769
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
772770
return error.ModuleInstantiationError;
773771
}
774772

775-
_ = try m.evaluate(v8_context);
776-
}
777-
778-
fn moduleNoCache(self: *JsContext, src: []const u8, url: []const u8) !void {
779-
const m = try compileModule(self.isolate, src, url);
780-
781-
const arena = self.context_arena;
782-
const owned_url = try arena.dupe(u8, url);
783-
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
784-
785-
const v8_context = self.v8_context;
786-
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
787-
return error.ModuleInstantiationError;
788-
}
789-
_ = try m.evaluate(v8_context);
773+
const evaluated = try m.evaluate(v8_context);
774+
// https://v8.github.io/api/head/classv8_1_1Module.html#a1f1758265a4082595757c3251bb40e0f
775+
// Must be a promise that gets returned here.
776+
std.debug.assert(evaluated.isPromise());
777+
const promise = v8.Promise{ .handle = evaluated.handle };
778+
return promise;
790779
}
791780

792781
// Wrap a v8.Exception
@@ -1545,32 +1534,32 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15451534
.{ .alloc = .if_needed },
15461535
) catch unreachable;
15471536

1548-
_dynamicModuleCallback(context, resource_str, normalized_specifier, &resolver);
1537+
log.debug(.js, "dynamic import", .{
1538+
.specifier = specifier_str,
1539+
.resource = resource_str,
1540+
.referrer_full = referrer_full_url,
1541+
.normalized_specifier = normalized_specifier,
1542+
});
1543+
1544+
_dynamicModuleCallback(context, normalized_specifier, &resolver) catch |err| {
1545+
log.err(.js, "dynamic module callback", .{
1546+
.err = err,
1547+
});
1548+
// Must be rejected at this point
1549+
// otherwise, we will just wait on a pending promise.
1550+
std.debug.assert(resolver.getPromise().getState() == .kRejected);
1551+
};
15491552
return @constCast(resolver.getPromise().handle);
15501553
}
15511554

15521555
fn _dynamicModuleCallback(
15531556
context: *JsContext,
1554-
resource: []const u8,
15551557
specifier: []const u8,
15561558
resolver: *const v8.PromiseResolver,
1557-
) void {
1559+
) !void {
15581560
const iso = context.isolate;
15591561
const ctx = context.v8_context;
15601562

1561-
// Check module cache first.
1562-
if (context.module_cache.get(specifier)) |cached_module| {
1563-
const namespace = cached_module.castToModule().getModuleNamespace();
1564-
_ = resolver.resolve(ctx, namespace);
1565-
return;
1566-
}
1567-
1568-
log.info(.js, "dynamic import", .{
1569-
.specifier = specifier,
1570-
.resource = resource,
1571-
.normalized = specifier,
1572-
});
1573-
15741563
const module_loader = context.module_loader;
15751564
const source = module_loader.func(module_loader.ptr, specifier) catch {
15761565
const error_msg = v8.String.initUtf8(iso, "Failed to load module");
@@ -1586,71 +1575,30 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15861575
try_catch.init(context);
15871576
defer try_catch.deinit();
15881577

1589-
const new_module = compileModule(iso, source, specifier) catch {
1578+
const maybe_promise = context.module(source, specifier, true) catch {
15901579
log.err(.js, "module compilation failed", .{
15911580
.specifier = specifier,
15921581
.exception = try_catch.exception(context.call_arena) catch "unknown error",
15931582
.stack = try_catch.stack(context.call_arena) catch null,
1594-
});
1595-
const error_msg = if (try_catch.hasCaught()) blk: {
1596-
const exception_str = try_catch.exception(context.call_arena) catch "Compilation error";
1597-
break :blk v8.String.initUtf8(iso, exception_str orelse "Compilation error");
1598-
} else v8.String.initUtf8(iso, "Module compilation failed");
1599-
1600-
_ = resolver.reject(ctx, error_msg.toValue());
1601-
return;
1602-
};
1603-
1604-
// Insert into Module Cache.
1605-
context.module_identifier.putNoClobber(context.context_arena, new_module.getIdentityHash(), specifier) catch unreachable;
1606-
context.module_cache.putNoClobber(context.context_arena, specifier, v8.Persistent(v8.Module).init(iso, new_module)) catch unreachable;
1607-
1608-
const instantiated = new_module.instantiate(ctx, JsContext.resolveModuleCallback) catch {
1609-
log.err(.js, "module instantiation failed", .{
1610-
.specifier = specifier,
1611-
.exception = try_catch.exception(context.call_arena) catch "unknown error",
1612-
.stack = try_catch.stack(context.call_arena) catch null,
1613-
});
1614-
const error_msg = if (try_catch.hasCaught()) blk: {
1615-
const exception_str = try_catch.exception(context.call_arena) catch "Instantiation error";
1616-
break :blk v8.String.initUtf8(iso, exception_str orelse "Instantiation error");
1617-
} else v8.String.initUtf8(iso, "Module instantiation failed");
1618-
1619-
_ = resolver.reject(ctx, error_msg.toValue());
1620-
return;
1621-
};
1622-
1623-
if (!instantiated) {
1624-
const error_msg = v8.String.initUtf8(iso, "Module did not instantiate");
1625-
_ = resolver.reject(ctx, error_msg.toValue());
1626-
return;
1627-
}
1628-
1629-
const evaluated = new_module.evaluate(ctx) catch {
1630-
log.err(.js, "module evaluation failed", .{
1631-
.specifier = specifier,
1632-
.exception = try_catch.exception(context.call_arena) catch "unknown error",
1633-
.stack = try_catch.stack(context.call_arena) catch null,
16341583
.line = try_catch.sourceLineNumber() orelse 0,
16351584
});
16361585
const error_msg = if (try_catch.hasCaught()) blk: {
16371586
const exception_str = try_catch.exception(context.call_arena) catch "Evaluation error";
16381587
break :blk v8.String.initUtf8(iso, exception_str orelse "Evaluation error");
16391588
} else v8.String.initUtf8(iso, "Module evaluation failed");
1640-
16411589
_ = resolver.reject(ctx, error_msg.toValue());
16421590
return;
16431591
};
1592+
const new_module = context.module_cache.get(specifier).?.castToModule();
16441593

1645-
if (evaluated.isPromise()) {
1646-
const promise = v8.Promise{ .handle = evaluated.handle };
1647-
1594+
if (maybe_promise) |promise| {
1595+
// This means we must wait for the evaluation.
16481596
const EvaluationData = struct {
16491597
module: v8.Persistent(v8.Module),
16501598
resolver: v8.Persistent(v8.PromiseResolver),
16511599
};
16521600

1653-
const ev_data = context.context_arena.create(EvaluationData) catch unreachable;
1601+
const ev_data = try context.context_arena.create(EvaluationData);
16541602
ev_data.* = .{
16551603
.module = v8.Persistent(v8.Module).init(iso, new_module),
16561604
.resolver = v8.Persistent(v8.PromiseResolver).init(iso, resolver.*),
@@ -1667,7 +1615,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
16671615
const cb_resolver = data.resolver.castToPromiseResolver();
16681616

16691617
const namespace = cb_module.getModuleNamespace();
1670-
log.warn(.js, "module then promise", .{
1618+
log.info(.js, "dynamic import complete", .{
1619+
.module = cb_module,
16711620
.namespace = namespace,
16721621
});
16731622
_ = cb_resolver.resolve(cb_context, namespace);
@@ -1676,11 +1625,15 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
16761625

16771626
const catch_callback = v8.Function.initWithData(ctx, struct {
16781627
pub fn callback(info: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void {
1679-
log.warn(.js, "module catch promise", .{});
16801628
const cb_info = v8.FunctionCallbackInfo{ .handle = info.? };
16811629
const cb_context = cb_info.getIsolate().getCurrentContext();
16821630
const data: *EvaluationData = @ptrCast(@alignCast(cb_info.getExternalValue()));
1631+
const cb_module = data.module.castToModule();
16831632
const cb_resolver = data.resolver.castToPromiseResolver();
1633+
1634+
log.err(.js, "dynamic import failed", .{
1635+
.module = cb_module,
1636+
});
16841637
_ = cb_resolver.reject(cb_context, cb_info.getData());
16851638
}
16861639
}.callback, external);
@@ -1695,8 +1648,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
16951648
return;
16961649
};
16971650
} else {
1651+
// This means it is already present in the cache.
16981652
const namespace = new_module.getModuleNamespace();
1653+
log.info(.js, "dynamic import complete", .{
1654+
.module = new_module,
1655+
.namespace = namespace,
1656+
});
16991657
_ = resolver.resolve(ctx, namespace);
1658+
return;
17001659
}
17011660
}
17021661
};

0 commit comments

Comments
 (0)