Skip to content

Commit 7f407f2

Browse files
committed
use module() for dynamic imports
1 parent c5bb625 commit 7f407f2

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
@@ -1029,12 +1029,16 @@ const Script = struct {
10291029
.cacheable = cacheable,
10301030
});
10311031

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

1037-
result catch {
1041+
if (failed) {
10381042
if (page.delayed_navigation) {
10391043
return error.Terminated;
10401044
}
@@ -1049,7 +1053,8 @@ const Script = struct {
10491053

10501054
try self.executeCallback("onerror", page);
10511055
return error.JsErr;
1052-
};
1056+
}
1057+
10531058
try self.executeCallback("onload", page);
10541059
}
10551060

src/runtime/js.zig

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

773773
// compile and eval a JS module
774-
// It doesn't wait for callbacks execution
775-
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !void {
776-
if (!cacheable) {
777-
return self.moduleNoCache(src, url);
778-
}
779-
774+
// It returns null if the module is already compiled and in the cache.
775+
// It returns a v8.Promise if the module must be evaluated.
776+
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !?v8.Promise {
780777
const arena = self.context_arena;
781778

782-
const gop = try self.module_cache.getOrPut(arena, url);
783-
if (gop.found_existing) {
784-
return;
779+
if (cacheable) {
780+
const value = self.module_cache.get(url);
781+
if (value != null) return null;
785782
}
786783
errdefer _ = self.module_cache.remove(url);
787784

@@ -791,30 +788,22 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
791788
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
792789
errdefer _ = self.module_identifier.remove(m.getIdentityHash());
793790

794-
gop.key_ptr.* = owned_url;
795-
gop.value_ptr.* = PersistentModule.init(self.isolate, m);
791+
if (cacheable) {
792+
try self.module_cache.put(arena, owned_url, PersistentModule.init(self.isolate, m));
793+
}
796794

797795
// resolveModuleCallback loads module's dependencies.
798796
const v8_context = self.v8_context;
799797
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
800798
return error.ModuleInstantiationError;
801799
}
802800

803-
_ = try m.evaluate(v8_context);
804-
}
805-
806-
fn moduleNoCache(self: *JsContext, src: []const u8, url: []const u8) !void {
807-
const m = try compileModule(self.isolate, src, url);
808-
809-
const arena = self.context_arena;
810-
const owned_url = try arena.dupe(u8, url);
811-
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
812-
813-
const v8_context = self.v8_context;
814-
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
815-
return error.ModuleInstantiationError;
816-
}
817-
_ = try m.evaluate(v8_context);
801+
const evaluated = try m.evaluate(v8_context);
802+
// https://v8.github.io/api/head/classv8_1_1Module.html#a1f1758265a4082595757c3251bb40e0f
803+
// Must be a promise that gets returned here.
804+
std.debug.assert(evaluated.isPromise());
805+
const promise = v8.Promise{ .handle = evaluated.handle };
806+
return promise;
818807
}
819808

820809
// Wrap a v8.Exception
@@ -1573,32 +1562,32 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15731562
.{ .alloc = .if_needed },
15741563
) catch unreachable;
15751564

1576-
_dynamicModuleCallback(context, resource_str, normalized_specifier, &resolver);
1565+
log.debug(.js, "dynamic import", .{
1566+
.specifier = specifier_str,
1567+
.resource = resource_str,
1568+
.referrer_full = referrer_full_url,
1569+
.normalized_specifier = normalized_specifier,
1570+
});
1571+
1572+
_dynamicModuleCallback(context, normalized_specifier, &resolver) catch |err| {
1573+
log.err(.js, "dynamic module callback", .{
1574+
.err = err,
1575+
});
1576+
// Must be rejected at this point
1577+
// otherwise, we will just wait on a pending promise.
1578+
std.debug.assert(resolver.getPromise().getState() == .kRejected);
1579+
};
15771580
return @constCast(resolver.getPromise().handle);
15781581
}
15791582

15801583
fn _dynamicModuleCallback(
15811584
context: *JsContext,
1582-
resource: []const u8,
15831585
specifier: []const u8,
15841586
resolver: *const v8.PromiseResolver,
1585-
) void {
1587+
) !void {
15861588
const iso = context.isolate;
15871589
const ctx = context.v8_context;
15881590

1589-
// Check module cache first.
1590-
if (context.module_cache.get(specifier)) |cached_module| {
1591-
const namespace = cached_module.castToModule().getModuleNamespace();
1592-
_ = resolver.resolve(ctx, namespace);
1593-
return;
1594-
}
1595-
1596-
log.info(.js, "dynamic import", .{
1597-
.specifier = specifier,
1598-
.resource = resource,
1599-
.normalized = specifier,
1600-
});
1601-
16021591
const module_loader = context.module_loader;
16031592
const source = module_loader.func(module_loader.ptr, specifier) catch {
16041593
const error_msg = v8.String.initUtf8(iso, "Failed to load module");
@@ -1614,71 +1603,30 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
16141603
try_catch.init(context);
16151604
defer try_catch.deinit();
16161605

1617-
const new_module = compileModule(iso, source, specifier) catch {
1606+
const maybe_promise = context.module(source, specifier, true) catch {
16181607
log.err(.js, "module compilation failed", .{
16191608
.specifier = specifier,
16201609
.exception = try_catch.exception(context.call_arena) catch "unknown error",
16211610
.stack = try_catch.stack(context.call_arena) catch null,
1622-
});
1623-
const error_msg = if (try_catch.hasCaught()) blk: {
1624-
const exception_str = try_catch.exception(context.call_arena) catch "Compilation error";
1625-
break :blk v8.String.initUtf8(iso, exception_str orelse "Compilation error");
1626-
} else v8.String.initUtf8(iso, "Module compilation failed");
1627-
1628-
_ = resolver.reject(ctx, error_msg.toValue());
1629-
return;
1630-
};
1631-
1632-
// Insert into Module Cache.
1633-
context.module_identifier.putNoClobber(context.context_arena, new_module.getIdentityHash(), specifier) catch unreachable;
1634-
context.module_cache.putNoClobber(context.context_arena, specifier, v8.Persistent(v8.Module).init(iso, new_module)) catch unreachable;
1635-
1636-
const instantiated = new_module.instantiate(ctx, JsContext.resolveModuleCallback) catch {
1637-
log.err(.js, "module instantiation failed", .{
1638-
.specifier = specifier,
1639-
.exception = try_catch.exception(context.call_arena) catch "unknown error",
1640-
.stack = try_catch.stack(context.call_arena) catch null,
1641-
});
1642-
const error_msg = if (try_catch.hasCaught()) blk: {
1643-
const exception_str = try_catch.exception(context.call_arena) catch "Instantiation error";
1644-
break :blk v8.String.initUtf8(iso, exception_str orelse "Instantiation error");
1645-
} else v8.String.initUtf8(iso, "Module instantiation failed");
1646-
1647-
_ = resolver.reject(ctx, error_msg.toValue());
1648-
return;
1649-
};
1650-
1651-
if (!instantiated) {
1652-
const error_msg = v8.String.initUtf8(iso, "Module did not instantiate");
1653-
_ = resolver.reject(ctx, error_msg.toValue());
1654-
return;
1655-
}
1656-
1657-
const evaluated = new_module.evaluate(ctx) catch {
1658-
log.err(.js, "module evaluation failed", .{
1659-
.specifier = specifier,
1660-
.exception = try_catch.exception(context.call_arena) catch "unknown error",
1661-
.stack = try_catch.stack(context.call_arena) catch null,
16621611
.line = try_catch.sourceLineNumber() orelse 0,
16631612
});
16641613
const error_msg = if (try_catch.hasCaught()) blk: {
16651614
const exception_str = try_catch.exception(context.call_arena) catch "Evaluation error";
16661615
break :blk v8.String.initUtf8(iso, exception_str orelse "Evaluation error");
16671616
} else v8.String.initUtf8(iso, "Module evaluation failed");
1668-
16691617
_ = resolver.reject(ctx, error_msg.toValue());
16701618
return;
16711619
};
1620+
const new_module = context.module_cache.get(specifier).?.castToModule();
16721621

1673-
if (evaluated.isPromise()) {
1674-
const promise = v8.Promise{ .handle = evaluated.handle };
1675-
1622+
if (maybe_promise) |promise| {
1623+
// This means we must wait for the evaluation.
16761624
const EvaluationData = struct {
16771625
module: v8.Persistent(v8.Module),
16781626
resolver: v8.Persistent(v8.PromiseResolver),
16791627
};
16801628

1681-
const ev_data = context.context_arena.create(EvaluationData) catch unreachable;
1629+
const ev_data = try context.context_arena.create(EvaluationData);
16821630
ev_data.* = .{
16831631
.module = v8.Persistent(v8.Module).init(iso, new_module),
16841632
.resolver = v8.Persistent(v8.PromiseResolver).init(iso, resolver.*),
@@ -1695,7 +1643,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
16951643
const cb_resolver = data.resolver.castToPromiseResolver();
16961644

16971645
const namespace = cb_module.getModuleNamespace();
1698-
log.warn(.js, "module then promise", .{
1646+
log.info(.js, "dynamic import complete", .{
1647+
.module = cb_module,
16991648
.namespace = namespace,
17001649
});
17011650
_ = cb_resolver.resolve(cb_context, namespace);
@@ -1704,11 +1653,15 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
17041653

17051654
const catch_callback = v8.Function.initWithData(ctx, struct {
17061655
pub fn callback(info: ?*const v8.c.FunctionCallbackInfo) callconv(.c) void {
1707-
log.warn(.js, "module catch promise", .{});
17081656
const cb_info = v8.FunctionCallbackInfo{ .handle = info.? };
17091657
const cb_context = cb_info.getIsolate().getCurrentContext();
17101658
const data: *EvaluationData = @ptrCast(@alignCast(cb_info.getExternalValue()));
1659+
const cb_module = data.module.castToModule();
17111660
const cb_resolver = data.resolver.castToPromiseResolver();
1661+
1662+
log.err(.js, "dynamic import failed", .{
1663+
.module = cb_module,
1664+
});
17121665
_ = cb_resolver.reject(cb_context, cb_info.getData());
17131666
}
17141667
}.callback, external);
@@ -1723,8 +1676,14 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
17231676
return;
17241677
};
17251678
} else {
1679+
// This means it is already present in the cache.
17261680
const namespace = new_module.getModuleNamespace();
1681+
log.info(.js, "dynamic import complete", .{
1682+
.module = new_module,
1683+
.namespace = namespace,
1684+
});
17271685
_ = resolver.resolve(ctx, namespace);
1686+
return;
17281687
}
17291688
}
17301689
};

0 commit comments

Comments
 (0)