Merge pull request #830 from lightpanda-io/SetHostInitializeImportMetaObjectCallback
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled

Implement ImportMeta callback
This commit is contained in:
Karl Seguin
2025-07-03 09:17:47 +08:00
committed by GitHub
7 changed files with 58 additions and 11 deletions

View File

@@ -17,7 +17,7 @@ inputs:
zig-v8:
description: 'zig v8 version to install'
required: false
default: 'v0.1.25'
default: 'v0.1.27'
v8:
description: 'v8 version to install'
required: false

View File

@@ -4,7 +4,7 @@ ARG MINISIG=0.12
ARG ZIG=0.14.1
ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U
ARG V8=13.6.233.8
ARG ZIG_V8=v0.1.25
ARG ZIG_V8=v0.1.27
ARG TARGETPLATFORM
RUN apt-get update -yq && \

View File

@@ -13,8 +13,8 @@
.hash = "tigerbeetle_io-0.0.0-ViLgxpyRBAB5BMfIcj3KMXfbJzwARs9uSl8aRy2OXULd",
},
.v8 = .{
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/23718cdb99aaa45105d0a7e0ca22587bf77d3b5f.tar.gz",
.hash = "v8-0.0.0-xddH68m2AwDf42Qp2Udz4wTMVH8p71si7yLlUoZkPeEz",
.url = "https://github.com/lightpanda-io/zig-v8-fork/archive/46ddf8c0a2861a4e5717e6f8d0d81a17e42fa0c9.tar.gz",
.hash = "v8-0.0.0-xddH6865AwDiDnu-HjMsqm9wXvP9OZOh_4clh_67iQsq",
},
//.v8 = .{ .path = "../zig-v8-fork" },
//.tigerbeetle_io = .{ .path = "../tigerbeetle-io" },

View File

@@ -1001,7 +1001,11 @@ const Script = struct {
try_catch.init(page.main_context);
defer try_catch.deinit();
const src = self.src orelse page.url.raw;
const src: []const u8 = blk: {
const s = self.src orelse break :blk page.url.raw;
break :blk try URL.stitch(page.arena, s, page.url.raw, .{.alloc = .if_needed});
};
// if self.src is null, then this is an inline script, and it should
// not be cached.
const cacheable = self.src != null;

View File

@@ -580,7 +580,7 @@ fn parseCommonArg(
var it = std.mem.splitScalar(u8, str, ',');
while (it.next()) |part| {
try arr.append(allocator, std.meta.stringToEnum(log.Scope, part) orelse {
log.fatal(.app, "invalid option choice", .{ .arg = "--log_scope_filter", .value = part });
log.fatal(.app, "invalid option choice", .{ .arg = "--log_filter_scopes", .value = part });
return false;
});
}

View File

@@ -197,6 +197,16 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
isolate.enter();
errdefer isolate.exit();
isolate.setHostInitializeImportMetaObjectCallback(struct {
fn callback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.C) void {
const v8_context = v8.Context{.handle = c_context.?};
const js_context: *JsContext = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
js_context.initializeImportMeta(v8.Module{.handle = c_module.?}, v8.Object{.handle = c_meta.?}) catch |err| {
log.err(.js, "import meta", .{ .err = err });
};
}
}.callback);
var temp_scope: v8.HandleScope = undefined;
v8.HandleScope.init(&temp_scope, isolate);
defer temp_scope.deinit();
@@ -726,13 +736,15 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
fn moduleNoCache(self: *JsContext, src: []const u8, url: []const u8) !void {
const m = try compileModule(self.isolate, src, url);
const arena = self.context_arena;
const owned_url = try arena.dupe(u8, url);
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
const v8_context = self.v8_context;
if (try m.instantiate(v8_context, resolveModuleCallback) == false) {
return error.ModuleInstantiationError;
}
const arena = self.context_arena;
const owned_url = try arena.dupe(u8, url);
try self.module_identifier.putNoClobber(arena, m.getIdentityHash(), owned_url);
_ = try m.evaluate(v8_context);
}
@@ -1279,6 +1291,20 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
return self.createException(js_value);
}
fn initializeImportMeta(self: *JsContext, m: v8.Module, meta: v8.Object) !void {
const url = self.module_identifier.get(m.getIdentityHash()) orelse {
// Shouldn't be possible.
return error.UnknownModuleReferrer;
};
const js_key = v8.String.initUtf8(self.isolate, "url");
const js_value = try self.zigValueToJs(url);
const res = meta.defineOwnProperty(self.v8_context, js_key.toName(), js_value, 0) orelse false;
if (!res) {
return error.FailedToSet;
}
}
// Callback from V8, asking us to load a module. The "specifier" is
// the src of the module to load.
fn resolveModuleCallback(
@@ -1335,7 +1361,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
try_catch.init(self);
defer try_catch.deinit();
const m = compileModule(self.isolate, source, specifier) catch |err| {
const m = compileModule(self.isolate, source, normalized_specifier) catch |err| {
log.warn(.js, "compile resolved module", .{
.specifier = specifier,
.stack = try_catch.stack(self.call_arena) catch null,

View File

@@ -111,7 +111,7 @@ pub const URL = struct {
return src;
}
var normalized_src = if (std.mem.startsWith(u8, src, "/")) src[1..] else src;
var normalized_src = src;
while (std.mem.startsWith(u8, normalized_src, "./")) {
normalized_src = normalized_src[2..];
}
@@ -131,6 +131,13 @@ pub const URL = struct {
}
};
if (normalized_src[0] == '/') {
if (std.mem.indexOfScalarPos(u8, base, protocol_end, '/')) |pos| {
return std.fmt.allocPrint(allocator, "{s}{s}", .{ base[0..pos], normalized_src });
}
// not sure what to do here...error? Just let it fallthrough for now.
}
if (std.mem.lastIndexOfScalar(u8, base[protocol_end..], '/')) |index| {
const last_slash_pos = index + protocol_end;
if (last_slash_pos == base.len - 1) {
@@ -257,6 +264,16 @@ test "URL: Stitching Base & Src URLs (No Ending Slash)" {
try testing.expectString("https://lightpanda.io/something.js", result);
}
test "URL: Stitching Base with absolute src" {
const allocator = testing.allocator;
const base = "https://lightpanda.io/hello";
const src = "/abc/something.js";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://lightpanda.io/abc/something.js", result);
}
test "URL: Stiching Base & Src URLs (Both Local)" {
const allocator = testing.allocator;