mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
import more types
This commit is contained in:
51
src/browser/js/BigInt.zig
Normal file
51
src/browser/js/BigInt.zig
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("js.zig");
|
||||||
|
const v8 = js.v8;
|
||||||
|
|
||||||
|
const BigInt = @This();
|
||||||
|
|
||||||
|
handle: *const v8.c.Integer,
|
||||||
|
|
||||||
|
pub fn initI64(isolate_handle: *v8.c.Isolate, val: i64) BigInt {
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__BigInt__New(isolate_handle, val).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn initU64(isolate_handle: *v8.c.Isolate, val: u64) BigInt {
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__BigInt__NewFromUnsigned(isolate_handle, val).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getUint64(self: BigInt) u64 {
|
||||||
|
return v8.c.v8__BigInt__Uint64Value(self.handle, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getInt64(self: BigInt) i64 {
|
||||||
|
return v8.c.v8__BigInt__Int64Value(self.handle, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toValue(self: BigInt) js.Value {
|
||||||
|
return .{
|
||||||
|
.ctx = undefined, // Will be set by caller if needed
|
||||||
|
.handle = @ptrCast(self.handle),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -32,10 +32,8 @@ const ScriptManager = @import("../ScriptManager.zig");
|
|||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const PersistentObject = v8.Persistent(v8.Object);
|
const PersistentObject = v8.Persistent(v8.Object);
|
||||||
const PersistentValue = v8.Persistent(v8.Value);
|
|
||||||
const PersistentModule = v8.Persistent(v8.Module);
|
const PersistentModule = v8.Persistent(v8.Module);
|
||||||
const PersistentPromise = v8.Persistent(v8.Promise);
|
const PersistentPromise = v8.Persistent(v8.Promise);
|
||||||
const PersistentFunction = v8.Persistent(v8.Function);
|
|
||||||
const TaggedAnyOpaque = js.TaggedAnyOpaque;
|
const TaggedAnyOpaque = js.TaggedAnyOpaque;
|
||||||
|
|
||||||
// Loosely maps to a Browser Page.
|
// Loosely maps to a Browser Page.
|
||||||
@@ -125,14 +123,14 @@ const ModuleEntry = struct {
|
|||||||
|
|
||||||
pub fn fromC(c_context: *const v8.C_Context) *Context {
|
pub fn fromC(c_context: *const v8.C_Context) *Context {
|
||||||
const data = v8.c.v8__Context__GetEmbedderData(c_context, 1).?;
|
const data = v8.c.v8__Context__GetEmbedderData(c_context, 1).?;
|
||||||
const big_int = v8.BigInt{ .handle = @ptrCast(data) };
|
const big_int = js.BigInt{ .handle = @ptrCast(data) };
|
||||||
return @ptrFromInt(big_int.getUint64());
|
return @ptrFromInt(big_int.getUint64());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fromIsolate(isolate: js.Isolate) *Context {
|
pub fn fromIsolate(isolate: js.Isolate) *Context {
|
||||||
const v8_context = v8.c.v8__Isolate__GetCurrentContext(isolate.handle).?;
|
const v8_context = v8.c.v8__Isolate__GetCurrentContext(isolate.handle).?;
|
||||||
const data = v8.c.v8__Context__GetEmbedderData(v8_context, 1).?;
|
const data = v8.c.v8__Context__GetEmbedderData(v8_context, 1).?;
|
||||||
const big_int = v8.BigInt{ .handle = @ptrCast(data) };
|
const big_int = js.BigInt{ .handle = @ptrCast(data) };
|
||||||
return @ptrFromInt(big_int.getUint64());
|
return @ptrFromInt(big_int.getUint64());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +210,7 @@ pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value {
|
|||||||
|
|
||||||
const scr = try compileScript(v8_isolate, v8_context, src, name);
|
const scr = try compileScript(v8_isolate, v8_context, src, name);
|
||||||
|
|
||||||
const value = scr.run(v8_context) catch {
|
const value = scr.run(v8_context.handle) catch {
|
||||||
return error.ExecutionError;
|
return error.ExecutionError;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -245,7 +243,8 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
|
|||||||
// compileModule is synchronous - nothing can modify the cache during compilation
|
// compileModule is synchronous - nothing can modify the cache during compilation
|
||||||
std.debug.assert(gop.value_ptr.module == null);
|
std.debug.assert(gop.value_ptr.module == null);
|
||||||
|
|
||||||
gop.value_ptr.module = PersistentModule.init(v8_isolate, m);
|
const v8_module = v8.Module{ .handle = m.handle };
|
||||||
|
gop.value_ptr.module = PersistentModule.init(v8_isolate, v8_module);
|
||||||
if (!gop.found_existing) {
|
if (!gop.found_existing) {
|
||||||
gop.key_ptr.* = owned_url;
|
gop.key_ptr.* = owned_url;
|
||||||
}
|
}
|
||||||
@@ -257,11 +256,11 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
|
|||||||
try self.postCompileModule(mod, owned_url);
|
try self.postCompileModule(mod, owned_url);
|
||||||
|
|
||||||
const v8_context = v8.Context{ .handle = self.handle };
|
const v8_context = v8.Context{ .handle = self.handle };
|
||||||
if (try mod.instantiate(v8_context, resolveModuleCallback) == false) {
|
if (try mod.instantiate(v8_context.handle, resolveModuleCallback) == false) {
|
||||||
return error.ModuleInstantiationError;
|
return error.ModuleInstantiationError;
|
||||||
}
|
}
|
||||||
|
|
||||||
const evaluated = mod.evaluate(v8_context) catch {
|
const evaluated = mod.evaluate(v8_context.handle) catch {
|
||||||
std.debug.assert(mod.getStatus() == .kErrored);
|
std.debug.assert(mod.getStatus() == .kErrored);
|
||||||
|
|
||||||
// Some module-loading errors aren't handled by TryCatch. We need to
|
// Some module-loading errors aren't handled by TryCatch. We need to
|
||||||
@@ -319,7 +318,7 @@ pub fn stringToFunction(self: *Context, str: []const u8) !js.Function {
|
|||||||
const v8_context = v8.Context{ .handle = self.handle };
|
const v8_context = v8.Context{ .handle = self.handle };
|
||||||
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
const script = try compileScript(v8_isolate, v8_context, full, null);
|
const script = try compileScript(v8_isolate, v8_context, full, null);
|
||||||
const js_value = script.run(v8_context) catch {
|
const js_value = script.run(v8_context.handle) catch {
|
||||||
return error.ExecutionError;
|
return error.ExecutionError;
|
||||||
};
|
};
|
||||||
if (!js_value.isFunction()) {
|
if (!js_value.isFunction()) {
|
||||||
@@ -331,7 +330,7 @@ pub fn stringToFunction(self: *Context, str: []const u8) !js.Function {
|
|||||||
// After we compile a module, whether it's a top-level one, or a nested one,
|
// After we compile a module, whether it's a top-level one, or a nested one,
|
||||||
// we always want to track its identity (so that, if this module imports other
|
// we always want to track its identity (so that, if this module imports other
|
||||||
// modules, we can resolve the full URL), and preload any dependent modules.
|
// modules, we can resolve the full URL), and preload any dependent modules.
|
||||||
fn postCompileModule(self: *Context, mod: v8.Module, url: [:0]const u8) !void {
|
fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8) !void {
|
||||||
try self.module_identifier.putNoClobber(self.arena, mod.getIdentityHash(), url);
|
try self.module_identifier.putNoClobber(self.arena, mod.getIdentityHash(), url);
|
||||||
|
|
||||||
const v8_context = v8.Context{ .handle = self.handle };
|
const v8_context = v8.Context{ .handle = self.handle };
|
||||||
@@ -500,8 +499,8 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (T == js.Promise) {
|
if (T == js.Promise) {
|
||||||
// we're returning a v8.Promise
|
// we're returning a js.Promise
|
||||||
return value.toObject().toValue();
|
return .{ .handle = @ptrCast(value.handle) };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (T == js.Exception) {
|
if (T == js.Exception) {
|
||||||
@@ -1189,26 +1188,15 @@ pub fn stackTrace(self: *const Context) !?[]const u8 {
|
|||||||
|
|
||||||
// == Promise Helpers ==
|
// == Promise Helpers ==
|
||||||
pub fn rejectPromise(self: *Context, value: anytype) !js.Promise {
|
pub fn rejectPromise(self: *Context, value: anytype) !js.Promise {
|
||||||
const ctx = v8.Context{ .handle = self.handle };
|
var resolver = js.PromiseResolver.init(self);
|
||||||
var resolver = v8.PromiseResolver.init(ctx);
|
resolver.reject("Context.rejectPromise", value);
|
||||||
const js_value = try self.zigValueToJs(value, .{});
|
return resolver.promise();
|
||||||
if (resolver.reject(ctx, js_value) == null) {
|
|
||||||
return error.FailedToResolvePromise;
|
|
||||||
}
|
|
||||||
self.runMicrotasks();
|
|
||||||
return resolver.getPromise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolvePromise(self: *Context, value: anytype) !js.Promise {
|
pub fn resolvePromise(self: *Context, value: anytype) !js.Promise {
|
||||||
const ctx = v8.Context{ .handle = self.handle };
|
var resolver = js.PromiseResolver.init(self);
|
||||||
const js_value = try self.zigValueToJs(value, .{});
|
resolver.resolve("Context.resolvePromise", value);
|
||||||
|
return resolver.promise();
|
||||||
var resolver = v8.PromiseResolver.init(ctx);
|
|
||||||
if (resolver.resolve(ctx, js_value) == null) {
|
|
||||||
return error.FailedToResolvePromise;
|
|
||||||
}
|
|
||||||
self.runMicrotasks();
|
|
||||||
return resolver.getPromise();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runMicrotasks(self: *Context) void {
|
pub fn runMicrotasks(self: *Context) void {
|
||||||
@@ -1230,12 +1218,12 @@ fn PromiseResolverType(comptime lifetime: PromiseResolverLifetime) type {
|
|||||||
return error{OutOfMemory}!js.PersistentPromiseResolver;
|
return error{OutOfMemory}!js.PersistentPromiseResolver;
|
||||||
}
|
}
|
||||||
pub fn createPromiseResolver(self: *Context, comptime lifetime: PromiseResolverLifetime) PromiseResolverType(lifetime) {
|
pub fn createPromiseResolver(self: *Context, comptime lifetime: PromiseResolverLifetime) PromiseResolverType(lifetime) {
|
||||||
const v8_context = v8.Context{ .handle = self.handle };
|
|
||||||
const resolver = v8.PromiseResolver.init(v8_context);
|
|
||||||
if (comptime lifetime == .none) {
|
if (comptime lifetime == .none) {
|
||||||
return .{ .context = self, .resolver = resolver };
|
return js.PromiseResolver.init(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const v8_context = v8.Context{ .handle = self.handle };
|
||||||
|
const resolver = v8.PromiseResolver.init(v8_context);
|
||||||
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
const persisted = v8.Persistent(v8.PromiseResolver).init(v8_isolate, resolver);
|
const persisted = v8.Persistent(v8.PromiseResolver).init(v8_isolate, resolver);
|
||||||
|
|
||||||
@@ -1266,7 +1254,7 @@ fn resolveModuleCallback(
|
|||||||
log.err(.js, "resolve module", .{ .err = err });
|
log.err(.js, "resolve module", .{ .err = err });
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
const referrer = v8.Module{ .handle = c_referrer.? };
|
const referrer = js.Module{ .handle = c_referrer.? };
|
||||||
|
|
||||||
return self._resolveModuleCallback(referrer, specifier) catch |err| {
|
return self._resolveModuleCallback(referrer, specifier) catch |err| {
|
||||||
log.err(.js, "resolve module", .{
|
log.err(.js, "resolve module", .{
|
||||||
@@ -1319,7 +1307,7 @@ pub fn dynamicModuleCallback(
|
|||||||
|
|
||||||
pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.c) void {
|
pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.c) void {
|
||||||
const self = fromC(c_context.?);
|
const self = fromC(c_context.?);
|
||||||
const m = v8.Module{ .handle = c_module.? };
|
const m = js.Module{ .handle = c_module.? };
|
||||||
const meta = v8.Object{ .handle = c_meta.? };
|
const meta = v8.Object{ .handle = c_meta.? };
|
||||||
|
|
||||||
const url = self.module_identifier.get(m.getIdentityHash()) orelse {
|
const url = self.module_identifier.get(m.getIdentityHash()) orelse {
|
||||||
@@ -1338,7 +1326,7 @@ pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _resolveModuleCallback(self: *Context, referrer: v8.Module, specifier: [:0]const u8) !?*const v8.C_Module {
|
fn _resolveModuleCallback(self: *Context, referrer: js.Module, specifier: [:0]const u8) !?*const v8.C_Module {
|
||||||
const referrer_path = self.module_identifier.get(referrer.getIdentityHash()) orelse {
|
const referrer_path = self.module_identifier.get(referrer.getIdentityHash()) orelse {
|
||||||
// Shouldn't be possible.
|
// Shouldn't be possible.
|
||||||
return error.UnknownModuleReferrer;
|
return error.UnknownModuleReferrer;
|
||||||
@@ -1365,7 +1353,8 @@ fn _resolveModuleCallback(self: *Context, referrer: v8.Module, specifier: [:0]co
|
|||||||
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
const mod = try compileModule(v8_isolate, source.src(), normalized_specifier);
|
const mod = try compileModule(v8_isolate, source.src(), normalized_specifier);
|
||||||
try self.postCompileModule(mod, normalized_specifier);
|
try self.postCompileModule(mod, normalized_specifier);
|
||||||
entry.module = PersistentModule.init(v8_isolate, mod);
|
const v8_module = v8.Module{ .handle = mod.handle };
|
||||||
|
entry.module = PersistentModule.init(v8_isolate, v8_module);
|
||||||
return entry.module.?.castToModule().handle;
|
return entry.module.?.castToModule().handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1381,7 +1370,7 @@ const DynamicModuleResolveState = struct {
|
|||||||
resolver: v8.Persistent(v8.PromiseResolver),
|
resolver: v8.Persistent(v8.PromiseResolver),
|
||||||
};
|
};
|
||||||
|
|
||||||
fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !v8.Promise {
|
fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !js.Promise {
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
const gop = try self.module_cache.getOrPut(self.arena, specifier);
|
const gop = try self.module_cache.getOrPut(self.arena, specifier);
|
||||||
@@ -1389,7 +1378,8 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
// This is easy, there's already something responsible
|
// This is easy, there's already something responsible
|
||||||
// for loading the module. Maybe it's still loading, maybe
|
// for loading the module. Maybe it's still loading, maybe
|
||||||
// it's complete. Whatever, we can just return that promise.
|
// it's complete. Whatever, we can just return that promise.
|
||||||
return gop.value_ptr.resolver_promise.?.castToPromise();
|
const v8_promise = gop.value_ptr.resolver_promise.?.castToPromise();
|
||||||
|
return .{ .handle = v8_promise.handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
const v8_context = v8.Context{ .handle = self.handle };
|
const v8_context = v8.Context{ .handle = self.handle };
|
||||||
@@ -1432,7 +1422,8 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
// For now, we're done. but this will be continued in
|
// For now, we're done. but this will be continued in
|
||||||
// `dynamicModuleSourceCallback`, once the source for the
|
// `dynamicModuleSourceCallback`, once the source for the
|
||||||
// moduel is loaded.
|
// moduel is loaded.
|
||||||
return promise;
|
const v8_promise = promise;
|
||||||
|
return .{ .handle = v8_promise.handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
// So we have a module, but no async resolver. This can only
|
// So we have a module, but no async resolver. This can only
|
||||||
@@ -1464,7 +1455,8 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
std.debug.assert(status == .kErrored);
|
std.debug.assert(status == .kErrored);
|
||||||
const error_msg = v8.String.initUtf8(v8_isolate, "Module evaluation failed");
|
const error_msg = v8.String.initUtf8(v8_isolate, "Module evaluation failed");
|
||||||
_ = resolver.reject(v8_context, error_msg.toValue());
|
_ = resolver.reject(v8_context, error_msg.toValue());
|
||||||
return promise;
|
const v8_promise = promise;
|
||||||
|
return .{ .handle = v8_promise.handle };
|
||||||
};
|
};
|
||||||
std.debug.assert(evaluated.isPromise());
|
std.debug.assert(evaluated.isPromise());
|
||||||
gop.value_ptr.module_promise = PersistentPromise.init(v8_isolate, .{ .handle = evaluated.handle });
|
gop.value_ptr.module_promise = PersistentPromise.init(v8_isolate, .{ .handle = evaluated.handle });
|
||||||
@@ -1479,7 +1471,8 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
// But we can skip direclty to `resolveDynamicModule` which is
|
// But we can skip direclty to `resolveDynamicModule` which is
|
||||||
// what the above callback will eventually do.
|
// what the above callback will eventually do.
|
||||||
self.resolveDynamicModule(state, gop.value_ptr.*);
|
self.resolveDynamicModule(state, gop.value_ptr.*);
|
||||||
return promise;
|
const v8_promise = promise;
|
||||||
|
return .{ .handle = v8_promise.handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptManager.ModuleSource) void {
|
fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptManager.ModuleSource) void {
|
||||||
@@ -1935,7 +1928,7 @@ fn jsUnsignedIntToZig(comptime T: type, max: comptime_int, maybe: u32) !T {
|
|||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compileScript(isolate: v8.Isolate, ctx: v8.Context, src: []const u8, name: ?[]const u8) !v8.Script {
|
fn compileScript(isolate: v8.Isolate, ctx: v8.Context, src: []const u8, name: ?[]const u8) !js.Script {
|
||||||
// compile
|
// compile
|
||||||
const script_name = v8.String.initUtf8(isolate, name orelse "anonymous");
|
const script_name = v8.String.initUtf8(isolate, name orelse "anonymous");
|
||||||
const script_source = v8.String.initUtf8(isolate, src);
|
const script_source = v8.String.initUtf8(isolate, src);
|
||||||
@@ -1946,15 +1939,16 @@ fn compileScript(isolate: v8.Isolate, ctx: v8.Context, src: []const u8, name: ?[
|
|||||||
v8.ScriptCompilerSource.init(&script_comp_source, script_source, origin, null);
|
v8.ScriptCompilerSource.init(&script_comp_source, script_source, origin, null);
|
||||||
defer script_comp_source.deinit();
|
defer script_comp_source.deinit();
|
||||||
|
|
||||||
return v8.ScriptCompiler.compile(
|
const v8_script = v8.ScriptCompiler.compile(
|
||||||
ctx,
|
ctx,
|
||||||
&script_comp_source,
|
&script_comp_source,
|
||||||
.kNoCompileOptions,
|
.kNoCompileOptions,
|
||||||
.kNoCacheNoReason,
|
.kNoCacheNoReason,
|
||||||
) catch return error.CompilationError;
|
) catch return error.CompilationError;
|
||||||
|
return .{ .handle = v8_script.handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compileModule(isolate: v8.Isolate, src: []const u8, name: []const u8) !v8.Module {
|
fn compileModule(isolate: v8.Isolate, src: []const u8, name: []const u8) !js.Module {
|
||||||
// compile
|
// compile
|
||||||
const script_name = v8.String.initUtf8(isolate, name);
|
const script_name = v8.String.initUtf8(isolate, name);
|
||||||
const script_source = v8.String.initUtf8(isolate, src);
|
const script_source = v8.String.initUtf8(isolate, src);
|
||||||
@@ -1976,12 +1970,13 @@ fn compileModule(isolate: v8.Isolate, src: []const u8, name: []const u8) !v8.Mod
|
|||||||
v8.ScriptCompilerSource.init(&script_comp_source, script_source, origin, null);
|
v8.ScriptCompilerSource.init(&script_comp_source, script_source, origin, null);
|
||||||
defer script_comp_source.deinit();
|
defer script_comp_source.deinit();
|
||||||
|
|
||||||
return v8.ScriptCompiler.compileModule(
|
const v8_module = v8.ScriptCompiler.compileModule(
|
||||||
isolate,
|
isolate,
|
||||||
&script_comp_source,
|
&script_comp_source,
|
||||||
.kNoCompileOptions,
|
.kNoCompileOptions,
|
||||||
.kNoCacheNoReason,
|
.kNoCacheNoReason,
|
||||||
) catch return error.CompilationError;
|
) catch return error.CompilationError;
|
||||||
|
return .{ .handle = v8_module.handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zigJsonToJs(isolate: v8.Isolate, v8_context: v8.Context, value: std.json.Value) !v8.Value {
|
fn zigJsonToJs(isolate: v8.Isolate, v8_context: v8.Context, value: std.json.Value) !v8.Value {
|
||||||
|
|||||||
82
src/browser/js/Module.zig
Normal file
82
src/browser/js/Module.zig
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("js.zig");
|
||||||
|
const v8 = js.v8;
|
||||||
|
|
||||||
|
const Module = @This();
|
||||||
|
|
||||||
|
pub const Status = enum(u32) {
|
||||||
|
kUninstantiated = v8.c.kUninstantiated,
|
||||||
|
kInstantiating = v8.c.kInstantiating,
|
||||||
|
kInstantiated = v8.c.kInstantiated,
|
||||||
|
kEvaluating = v8.c.kEvaluating,
|
||||||
|
kEvaluated = v8.c.kEvaluated,
|
||||||
|
kErrored = v8.c.kErrored,
|
||||||
|
};
|
||||||
|
|
||||||
|
handle: *const v8.c.Module,
|
||||||
|
|
||||||
|
pub fn getStatus(self: Module) Status {
|
||||||
|
return @enumFromInt(v8.c.v8__Module__GetStatus(self.handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getException(self: Module) v8.Value {
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__Module__GetException(self.handle).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getModuleRequests(self: Module) v8.FixedArray {
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__Module__GetModuleRequests(self.handle).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn instantiate(self: Module, ctx_handle: *const v8.c.Context, cb: v8.c.ResolveModuleCallback) !bool {
|
||||||
|
var out: v8.c.MaybeBool = undefined;
|
||||||
|
v8.c.v8__Module__InstantiateModule(self.handle, ctx_handle, cb, &out);
|
||||||
|
if (out.has_value) {
|
||||||
|
return out.value;
|
||||||
|
}
|
||||||
|
return error.JsException;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn evaluate(self: Module, ctx_handle: *const v8.c.Context) !v8.Value {
|
||||||
|
const res = v8.c.v8__Module__Evaluate(self.handle, ctx_handle) orelse return error.JsException;
|
||||||
|
|
||||||
|
if (self.getStatus() == .kErrored) {
|
||||||
|
return error.JsException;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .handle = res };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getIdentityHash(self: Module) u32 {
|
||||||
|
return @bitCast(v8.c.v8__Module__GetIdentityHash(self.handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getModuleNamespace(self: Module) v8.Value {
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__Module__GetModuleNamespace(self.handle).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getScriptId(self: Module) u32 {
|
||||||
|
return @intCast(v8.c.v8__Module__ScriptId(self.handle));
|
||||||
|
}
|
||||||
31
src/browser/js/Name.zig
Normal file
31
src/browser/js/Name.zig
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("js.zig");
|
||||||
|
const v8 = js.v8;
|
||||||
|
|
||||||
|
const Name = @This();
|
||||||
|
|
||||||
|
handle: *const v8.c.Name,
|
||||||
|
|
||||||
|
pub fn toValue(self: Name) js.Value {
|
||||||
|
return .{
|
||||||
|
.ctx = undefined, // Will be set by caller if needed
|
||||||
|
.handle = @ptrCast(self.handle),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -79,6 +79,13 @@ pub fn toString(self: Object) ![]const u8 {
|
|||||||
return self.ctx.valueToString(js_value, .{});
|
return self.ctx.valueToString(js_value, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toValue(self: Object) js.Value {
|
||||||
|
return .{
|
||||||
|
.ctx = self.ctx,
|
||||||
|
.handle = @ptrCast(self.handle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn format(self: Object, writer: *std.Io.Writer) !void {
|
pub fn format(self: Object, writer: *std.Io.Writer) !void {
|
||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
const js_value = v8.Value{ .handle = @ptrCast(self.handle) };
|
const js_value = v8.Value{ .handle = @ptrCast(self.handle) };
|
||||||
|
|||||||
49
src/browser/js/Promise.zig
Normal file
49
src/browser/js/Promise.zig
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("js.zig");
|
||||||
|
const v8 = js.v8;
|
||||||
|
|
||||||
|
const Promise = @This();
|
||||||
|
|
||||||
|
handle: *const v8.c.Promise,
|
||||||
|
|
||||||
|
pub fn toObject(self: Promise) js.Object {
|
||||||
|
return .{
|
||||||
|
.ctx = undefined, // Will be set by caller if needed
|
||||||
|
.handle = @ptrCast(self.handle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toValue(self: Promise) js.Value {
|
||||||
|
return .{
|
||||||
|
.ctx = undefined, // Will be set by caller if needed
|
||||||
|
.handle = @ptrCast(self.handle),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn thenAndCatch(self: Promise, ctx_handle: *const v8.c.Context, on_fulfilled: js.Function, on_rejected: js.Function) !Promise {
|
||||||
|
const v8_context = v8.Context{ .handle = ctx_handle };
|
||||||
|
const v8_on_fulfilled = v8.Function{ .handle = on_fulfilled.handle };
|
||||||
|
const v8_on_rejected = v8.Function{ .handle = on_rejected.handle };
|
||||||
|
|
||||||
|
if (v8.c.v8__Promise__Then2(self.handle, v8_context.handle, v8_on_fulfilled.handle, v8_on_rejected.handle)) |handle| {
|
||||||
|
return Promise{ .handle = handle };
|
||||||
|
}
|
||||||
|
return error.PromiseChainFailed;
|
||||||
|
}
|
||||||
75
src/browser/js/PromiseResolver.zig
Normal file
75
src/browser/js/PromiseResolver.zig
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("js.zig");
|
||||||
|
const v8 = js.v8;
|
||||||
|
const log = @import("../../log.zig");
|
||||||
|
|
||||||
|
const PromiseResolver = @This();
|
||||||
|
|
||||||
|
ctx: *const js.Context,
|
||||||
|
handle: *const v8.c.PromiseResolver,
|
||||||
|
|
||||||
|
pub fn init(ctx: *const js.Context) PromiseResolver {
|
||||||
|
return .{
|
||||||
|
.ctx = ctx,
|
||||||
|
.handle = v8.c.v8__Promise__Resolver__New(ctx.handle).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn promise(self: PromiseResolver) js.Promise {
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__Promise__Resolver__GetPromise(self.handle).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve(self: PromiseResolver, comptime source: []const u8, value: anytype) void {
|
||||||
|
self._resolve(value) catch |err| {
|
||||||
|
log.err(.bug, "resolve", .{ .source = source, .err = err, .persistent = false });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _resolve(self: PromiseResolver, value: anytype) !void {
|
||||||
|
const ctx: *js.Context = @constCast(self.ctx);
|
||||||
|
const js_value = try ctx.zigValueToJs(value, .{});
|
||||||
|
|
||||||
|
var out: v8.c.MaybeBool = undefined;
|
||||||
|
v8.c.v8__Promise__Resolver__Resolve(self.handle, self.ctx.handle, js_value.handle, &out);
|
||||||
|
if (!out.has_value or !out.value) {
|
||||||
|
return error.FailedToResolvePromise;
|
||||||
|
}
|
||||||
|
ctx.runMicrotasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reject(self: PromiseResolver, comptime source: []const u8, value: anytype) void {
|
||||||
|
self._reject(value) catch |err| {
|
||||||
|
log.err(.bug, "reject", .{ .source = source, .err = err, .persistent = false });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _reject(self: PromiseResolver, value: anytype) !void {
|
||||||
|
const ctx: *js.Context = @constCast(self.ctx);
|
||||||
|
const js_value = try ctx.zigValueToJs(value, .{});
|
||||||
|
|
||||||
|
var out: v8.c.MaybeBool = undefined;
|
||||||
|
v8.c.v8__Promise__Resolver__Reject(self.handle, self.ctx.handle, js_value.handle, &out);
|
||||||
|
if (!out.has_value or !out.value) {
|
||||||
|
return error.FailedToRejectPromise;
|
||||||
|
}
|
||||||
|
ctx.runMicrotasks();
|
||||||
|
}
|
||||||
38
src/browser/js/Script.zig
Normal file
38
src/browser/js/Script.zig
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||||
|
//
|
||||||
|
// Francis Bouvier <francis@lightpanda.io>
|
||||||
|
// Pierre Tachoire <pierre@lightpanda.io>
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as
|
||||||
|
// published by the Free Software Foundation, either version 3 of the
|
||||||
|
// License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
const js = @import("js.zig");
|
||||||
|
const v8 = js.v8;
|
||||||
|
|
||||||
|
const Script = @This();
|
||||||
|
|
||||||
|
handle: *const v8.c.Script,
|
||||||
|
|
||||||
|
pub fn compile(ctx_handle: *const v8.c.Context, src_handle: *const v8.c.String, origin: ?*const v8.c.ScriptOrigin) !Script {
|
||||||
|
if (v8.c.v8__Script__Compile(ctx_handle, src_handle, origin)) |handle| {
|
||||||
|
return .{ .handle = handle };
|
||||||
|
}
|
||||||
|
return error.JsException;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(self: Script, ctx_handle: *const v8.c.Context) !v8.Value {
|
||||||
|
if (v8.c.v8__Script__Run(self.handle, ctx_handle)) |value| {
|
||||||
|
return .{ .handle = value };
|
||||||
|
}
|
||||||
|
return error.JsException;
|
||||||
|
}
|
||||||
@@ -37,6 +37,12 @@ pub const String = @import("String.zig");
|
|||||||
pub const Object = @import("Object.zig");
|
pub const Object = @import("Object.zig");
|
||||||
pub const TryCatch = @import("TryCatch.zig");
|
pub const TryCatch = @import("TryCatch.zig");
|
||||||
pub const Function = @import("Function.zig");
|
pub const Function = @import("Function.zig");
|
||||||
|
pub const Promise = @import("Promise.zig");
|
||||||
|
pub const PromiseResolver = @import("PromiseResolver.zig");
|
||||||
|
pub const Module = @import("Module.zig");
|
||||||
|
pub const BigInt = @import("BigInt.zig");
|
||||||
|
pub const Name = @import("Name.zig");
|
||||||
|
pub const Script = @import("Script.zig");
|
||||||
|
|
||||||
pub const Integer = @import("Integer.zig");
|
pub const Integer = @import("Integer.zig");
|
||||||
pub const Global = @import("global.zig").Global;
|
pub const Global = @import("global.zig").Global;
|
||||||
@@ -72,47 +78,6 @@ pub const ArrayBuffer = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PromiseResolver = struct {
|
|
||||||
context: *Context,
|
|
||||||
resolver: v8.PromiseResolver,
|
|
||||||
|
|
||||||
pub fn promise(self: PromiseResolver) Promise {
|
|
||||||
return self.resolver.getPromise();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve(self: PromiseResolver, comptime source: []const u8, value: anytype) void {
|
|
||||||
self._resolve(value) catch |err| {
|
|
||||||
log.err(.bug, "resolve", .{ .source = source, .err = err, .persistent = false });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fn _resolve(self: PromiseResolver, value: anytype) !void {
|
|
||||||
const context = self.context;
|
|
||||||
const js_value = try context.zigValueToJs(value, .{});
|
|
||||||
|
|
||||||
const v8_context = v8.Context{ .handle = context.handle };
|
|
||||||
if (self.resolver.resolve(v8_context, js_value) == null) {
|
|
||||||
return error.FailedToResolvePromise;
|
|
||||||
}
|
|
||||||
self.context.runMicrotasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reject(self: PromiseResolver, comptime source: []const u8, value: anytype) void {
|
|
||||||
self._reject(value) catch |err| {
|
|
||||||
log.err(.bug, "reject", .{ .source = source, .err = err, .persistent = false });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
fn _reject(self: PromiseResolver, value: anytype) !void {
|
|
||||||
const context = self.context;
|
|
||||||
const js_value = try context.zigValueToJs(value);
|
|
||||||
|
|
||||||
const v8_context = v8.Context{ .handle = context.handle };
|
|
||||||
if (self.resolver.reject(v8_context, js_value) == null) {
|
|
||||||
return error.FailedToRejectPromise;
|
|
||||||
}
|
|
||||||
self.context.runMicrotasks();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PersistentPromiseResolver = struct {
|
pub const PersistentPromiseResolver = struct {
|
||||||
context: *Context,
|
context: *Context,
|
||||||
resolver: v8.Persistent(v8.PromiseResolver),
|
resolver: v8.Persistent(v8.PromiseResolver),
|
||||||
@@ -122,7 +87,8 @@ pub const PersistentPromiseResolver = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn promise(self: PersistentPromiseResolver) Promise {
|
pub fn promise(self: PersistentPromiseResolver) Promise {
|
||||||
return self.resolver.castToPromiseResolver().getPromise();
|
const v8_promise = self.resolver.castToPromiseResolver().getPromise();
|
||||||
|
return .{ .handle = v8_promise.handle };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve(self: PersistentPromiseResolver, comptime source: []const u8, value: anytype) void {
|
pub fn resolve(self: PersistentPromiseResolver, comptime source: []const u8, value: anytype) void {
|
||||||
@@ -160,8 +126,6 @@ pub const PersistentPromiseResolver = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Promise = v8.Promise;
|
|
||||||
|
|
||||||
pub const Exception = struct {
|
pub const Exception = struct {
|
||||||
inner: v8.Value,
|
inner: v8.Value,
|
||||||
context: *const Context,
|
context: *const Context,
|
||||||
|
|||||||
Reference in New Issue
Block a user