Skip to content

Commit 1fc871c

Browse files
committed
fragile but working
1 parent 25206e9 commit 1fc871c

File tree

1 file changed

+95
-51
lines changed

1 file changed

+95
-51
lines changed

src/runtime/js.zig

Lines changed: 95 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
204204
import_attrs: ?*const v8.c.FixedArray,
205205
) callconv(.c) ?*v8.c.Promise {
206206
_ = host_defined_options;
207-
_ = resource_name;
208207
_ = import_attrs;
209208
const ctx: v8.Context = .{ .handle = v8_ctx.? };
210209
const context: *JsContext = @ptrFromInt(ctx.getEmbedderData(1).castTo(v8.BigInt).getUint64());
@@ -217,11 +216,54 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
217216
_ = resolver.reject(ctx, error_msg.toValue());
218217
return @constCast(resolver.getPromise().handle);
219218
};
219+
const resource: v8.String = .{ .handle = resource_name.? };
220+
const resource_str = jsStringToZig(context.call_arena, resource, iso) catch {
221+
const error_msg = v8.String.initUtf8(iso, "Failed to parse module resource");
222+
_ = resolver.reject(ctx, error_msg.toValue());
223+
return @constCast(resolver.getPromise().handle);
224+
};
225+
226+
const referrer_full_url = blk: {
227+
// Search through module_identifier values to find matching resource
228+
var it = context.module_identifier.valueIterator();
229+
while (it.next()) |full_url| {
230+
// Extract just the filename from the full URL
231+
const last_slash = std.mem.lastIndexOfScalar(u8, full_url.*, '/') orelse 0;
232+
const filename = full_url.*[last_slash + 1 ..];
233+
234+
// Compare with our resource string (removing ./ prefix if present)
235+
const resource_clean = if (std.mem.startsWith(u8, resource_str, "./"))
236+
resource_str[2..]
237+
else
238+
resource_str;
239+
240+
if (std.mem.eql(u8, filename, resource_clean)) {
241+
break :blk full_url.*;
242+
}
243+
}
220244

221-
log.info(.js, "dynamic import", .{ .specifier = specifier_str });
245+
// Fallback - maybe it's already a full URL in some cases?
246+
break :blk resource_str;
247+
};
248+
249+
const normalized_specifier = @import("../url.zig").stitch(
250+
context.context_arena,
251+
specifier_str,
252+
referrer_full_url,
253+
.{ .alloc = .if_needed },
254+
) catch unreachable;
255+
256+
// TODO: we need to resolve the full URL here and normalize it.
257+
// That way we can pass the correct one in the module_loader.
258+
259+
log.info(.js, "dynamic import", .{
260+
.specifier = specifier_str,
261+
.resource = resource_str,
262+
.normalized = normalized_specifier,
263+
});
222264

223265
const module_loader = context.module_loader;
224-
const source = module_loader.func(module_loader.ptr, specifier_str) catch {
266+
const source = module_loader.func(module_loader.ptr, normalized_specifier) catch {
225267
const error_msg = v8.String.initUtf8(iso, "Failed to load module");
226268
_ = resolver.reject(ctx, error_msg.toValue());
227269
return @constCast(resolver.getPromise().handle);
@@ -250,6 +292,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
250292
return @constCast(resolver.getPromise().handle);
251293
};
252294

295+
context.module_identifier.putNoClobber(context.context_arena, module.getIdentityHash(), normalized_specifier) catch unreachable;
296+
context.module_cache.putNoClobber(context.context_arena, normalized_specifier, v8.Persistent(v8.Module).init(iso, module)) catch unreachable;
297+
253298
const instantiated = module.instantiate(ctx, JsContext.resolveModuleCallback) catch {
254299
log.err(.js, "module instantiation failed", .{
255300
.specifier = specifier_str,
@@ -308,8 +353,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
308353
const cb_isolate = cb_info.getIsolate();
309354
const cb_context = cb_isolate.getCurrentContext();
310355
const data: *EvaluationData = @ptrCast(@alignCast(cb_info.getExternalValue()));
311-
const cb_module = v8.Module{ .handle = data.module.handle };
312-
const cb_resolver = v8.PromiseResolver{ .handle = data.resolver.handle };
356+
const cb_module = data.module.castToModule();
357+
const cb_resolver = data.resolver.castToPromiseResolver();
313358

314359
const namespace = cb_module.getModuleNamespace();
315360
log.warn(.js, "module then promise", .{
@@ -325,7 +370,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
325370
const cb_info = v8.FunctionCallbackInfo{ .handle = info.? };
326371
const cb_context = cb_info.getIsolate().getCurrentContext();
327372
const data: *EvaluationData = @ptrCast(@alignCast(cb_info.getExternalValue()));
328-
const cb_resolver = v8.PromiseResolver{ .handle = data.resolver.handle };
373+
const cb_resolver = data.resolver.castToPromiseResolver();
329374
_ = cb_resolver.reject(cb_context, cb_info.getData());
330375
}
331376
}.callback, external);
@@ -878,38 +923,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
878923
_ = try m.evaluate(v8_context);
879924
}
880925

881-
pub fn module2(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !v8.Module {
882-
if (!cacheable) {
883-
return self.moduleNoCache2(src, url);
884-
}
885-
886-
const arena = self.context_arena;
887-
888-
const gop = try self.module_cache.getOrPut(arena, url);
889-
if (gop.found_existing) {
890-
return v8.Module{ .handle = gop.value_ptr.handle };
891-
}
892-
errdefer _ = self.module_cache.remove(url);
893-
894-
const m = try compileModule(self.isolate, src, url);
895-
896-
const owned_url = try arena.dupe(u8, url);
897-
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
898-
errdefer _ = self.module_identifier.remove(m.getIdentityHash());
899-
900-
gop.key_ptr.* = owned_url;
901-
gop.value_ptr.* = PersistentModule.init(self.isolate, m);
902-
903-
// resolveModuleCallback loads module's dependencies.
904-
const v8_context = self.v8_context;
905-
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
906-
return error.ModuleInstantiationError;
907-
}
908-
909-
_ = try m.evaluate(v8_context);
910-
return m;
911-
}
912-
913926
fn moduleNoCache(self: *JsContext, src: []const u8, url: []const u8) !void {
914927
const m = try compileModule(self.isolate, src, url);
915928
const v8_context = self.v8_context;
@@ -922,19 +935,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
922935
_ = try m.evaluate(v8_context);
923936
}
924937

925-
fn moduleNoCache2(self: *JsContext, src: []const u8, url: []const u8) !v8.Module {
926-
const m = try compileModule(self.isolate, src, url);
927-
const v8_context = self.v8_context;
928-
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
929-
return error.ModuleInstantiationError;
930-
}
931-
const arena = self.context_arena;
932-
const owned_url = try arena.dupe(u8, url);
933-
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
934-
_ = try m.evaluate(v8_context);
935-
return m;
936-
}
937-
938938
// Wrap a v8.Exception
939939
fn createException(self: *const JsContext, e: v8.Value) Exception {
940940
return .{
@@ -1554,6 +1554,50 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
15541554
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_specifier);
15551555
return m.handle;
15561556
}
1557+
1558+
fn _resolveModuleCallback2(
1559+
self: *JsContext,
1560+
base_url: []const u8,
1561+
specifier: []const u8,
1562+
) !?*const v8.C_Module {
1563+
const normalized_specifier = try @import("../url.zig").stitch(
1564+
self.call_arena,
1565+
specifier,
1566+
base_url,
1567+
.{ .alloc = .if_needed },
1568+
);
1569+
1570+
if (self.module_cache.get(normalized_specifier)) |pm| {
1571+
return pm.handle;
1572+
}
1573+
1574+
const module_loader = self.module_loader;
1575+
const source = try module_loader.func(module_loader.ptr, normalized_specifier) orelse return null;
1576+
1577+
var try_catch: TryCatch = undefined;
1578+
try_catch.init(self);
1579+
defer try_catch.deinit();
1580+
1581+
const m = compileModule(self.isolate, source, specifier) catch |err| {
1582+
log.warn(.js, "compile resolved module", .{
1583+
.specifier = specifier,
1584+
.stack = try_catch.stack(self.call_arena) catch null,
1585+
.src = try_catch.sourceLine(self.call_arena) catch "err",
1586+
.line = try_catch.sourceLineNumber() orelse 0,
1587+
.exception = (try_catch.exception(self.call_arena) catch @errorName(err)) orelse @errorName(err),
1588+
});
1589+
return null;
1590+
};
1591+
1592+
// We were hoping to find the module in our cache, and thus used
1593+
// the short-lived call_arena to create the normalized_specifier.
1594+
// But now this'll live for the lifetime of the context.
1595+
const arena = self.context_arena;
1596+
const owned_specifier = try arena.dupe(u8, normalized_specifier);
1597+
try self.module_cache.put(arena, owned_specifier, PersistentModule.init(self.isolate, m));
1598+
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_specifier);
1599+
return m.handle;
1600+
}
15571601
};
15581602

15591603
pub const Function = struct {

0 commit comments

Comments
 (0)