mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 14:33:47 +00:00
Isolate and HandleScope
This commit is contained in:
@@ -39,15 +39,16 @@ const CALL_ARENA_RETAIN = 1024 * 16;
|
|||||||
const Caller = @This();
|
const Caller = @This();
|
||||||
context: *Context,
|
context: *Context,
|
||||||
v8_context: v8.Context,
|
v8_context: v8.Context,
|
||||||
isolate: v8.Isolate,
|
isolate: js.Isolate,
|
||||||
call_arena: Allocator,
|
call_arena: Allocator,
|
||||||
|
|
||||||
// info is a v8.PropertyCallbackInfo or a v8.FunctionCallback
|
// info is a v8.PropertyCallbackInfo or a v8.FunctionCallback
|
||||||
// All we really want from it is the isolate.
|
// All we really want from it is the isolate.
|
||||||
// executor = Isolate -> getCurrentContext -> getEmbedderData()
|
// executor = Isolate -> getCurrentContext -> getEmbedderData()
|
||||||
pub fn init(info: anytype) Caller {
|
pub fn init(info: anytype) Caller {
|
||||||
const isolate = info.getIsolate();
|
const v8_isolate = info.getIsolate();
|
||||||
const v8_context = isolate.getCurrentContext();
|
const isolate = js.Isolate{ .handle = v8_isolate.handle };
|
||||||
|
const v8_context = v8_isolate.getCurrentContext();
|
||||||
const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
const context: *Context = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||||
|
|
||||||
context.call_depth += 1;
|
context.call_depth += 1;
|
||||||
@@ -133,7 +134,7 @@ pub fn method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionC
|
|||||||
|
|
||||||
pub fn _method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) !void {
|
pub fn _method(self: *Caller, comptime T: type, func: anytype, info: v8.FunctionCallbackInfo, comptime opts: CallOpts) !void {
|
||||||
const F = @TypeOf(func);
|
const F = @TypeOf(func);
|
||||||
var handle_scope: v8.HandleScope = undefined;
|
var handle_scope: js.HandleScope = undefined;
|
||||||
handle_scope.init(self.isolate);
|
handle_scope.init(self.isolate);
|
||||||
defer handle_scope.deinit();
|
defer handle_scope.deinit();
|
||||||
|
|
||||||
@@ -316,6 +317,7 @@ fn assertIsPageArg(comptime T: type, comptime F: type, index: comptime_int) void
|
|||||||
|
|
||||||
fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void {
|
fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror, info: anytype, comptime opts: CallOpts) void {
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
|
|
||||||
if (comptime @import("builtin").mode == .Debug and @hasDecl(@TypeOf(info), "length")) {
|
if (comptime @import("builtin").mode == .Debug and @hasDecl(@TypeOf(info), "length")) {
|
||||||
if (log.enabled(.js, .warn)) {
|
if (log.enabled(.js, .warn)) {
|
||||||
@@ -324,21 +326,21 @@ fn handleError(self: *Caller, comptime T: type, comptime F: type, err: anyerror,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var js_err: ?v8.Value = switch (err) {
|
var js_err: ?v8.Value = switch (err) {
|
||||||
error.InvalidArgument => createTypeException(isolate, "invalid argument"),
|
error.InvalidArgument => createTypeException(v8_isolate, "invalid argument"),
|
||||||
error.OutOfMemory => js._createException(isolate, "out of memory"),
|
error.OutOfMemory => js._createException(v8_isolate, "out of memory"),
|
||||||
error.IllegalConstructor => js._createException(isolate, "Illegal Contructor"),
|
error.IllegalConstructor => js._createException(v8_isolate, "Illegal Contructor"),
|
||||||
else => blk: {
|
else => blk: {
|
||||||
if (!comptime opts.dom_exception) {
|
if (!comptime opts.dom_exception) {
|
||||||
break :blk null;
|
break :blk null;
|
||||||
}
|
}
|
||||||
const DOMException = @import("../webapi/DOMException.zig");
|
const DOMException = @import("../webapi/DOMException.zig");
|
||||||
const ex = DOMException.fromError(err) orelse break :blk null;
|
const ex = DOMException.fromError(err) orelse break :blk null;
|
||||||
break :blk self.context.zigValueToJs(ex, .{}) catch js._createException(isolate, "internal error");
|
break :blk self.context.zigValueToJs(ex, .{}) catch js._createException(v8_isolate, "internal error");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (js_err == null) {
|
if (js_err == null) {
|
||||||
js_err = js._createException(isolate, @errorName(err));
|
js_err = js._createException(v8_isolate, @errorName(err));
|
||||||
}
|
}
|
||||||
const js_exception = isolate.throwException(js_err.?);
|
const js_exception = isolate.throwException(js_err.?);
|
||||||
info.getReturnValue().setValueHandle(js_exception.handle);
|
info.getReturnValue().setValueHandle(js_exception.handle);
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ const Context = @This();
|
|||||||
|
|
||||||
id: usize,
|
id: usize,
|
||||||
page: *Page,
|
page: *Page,
|
||||||
isolate: v8.Isolate,
|
isolate: js.Isolate,
|
||||||
// This context is a persistent object. The persistent needs to be recovered and reset.
|
// This context is a persistent object. The persistent needs to be recovered and reset.
|
||||||
v8_context: v8.Context,
|
v8_context: v8.Context,
|
||||||
handle_scope: ?v8.HandleScope,
|
handle_scope: ?js.HandleScope,
|
||||||
|
|
||||||
cpu_profiler: ?v8.CpuProfiler = null,
|
cpu_profiler: ?v8.CpuProfiler = null,
|
||||||
|
|
||||||
@@ -128,9 +128,10 @@ pub fn fromC(c_context: *const v8.C_Context) *Context {
|
|||||||
return @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
return @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fromIsolate(isolate: v8.Isolate) *Context {
|
pub fn fromIsolate(isolate: js.Isolate) *Context {
|
||||||
const v8_context = isolate.getCurrentContext();
|
const v8_context = v8.c.v8__Isolate__GetCurrentContext(isolate.handle).?;
|
||||||
return @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
const ctx = v8.Context{ .handle = v8_context };
|
||||||
|
return @ptrFromInt(ctx.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setupGlobal(self: *Context) !void {
|
pub fn setupGlobal(self: *Context) !void {
|
||||||
@@ -202,8 +203,9 @@ pub fn eval(self: *Context, src: []const u8, name: ?[]const u8) !void {
|
|||||||
|
|
||||||
pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value {
|
pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value {
|
||||||
const v8_context = self.v8_context;
|
const v8_context = self.v8_context;
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
|
||||||
const scr = try compileScript(self.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) catch {
|
||||||
return error.ExecutionError;
|
return error.ExecutionError;
|
||||||
@@ -213,6 +215,7 @@ pub fn exec(self: *Context, src: []const u8, name: ?[]const u8) !js.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url: []const u8, cacheable: bool) !(if (want_result) ModuleEntry else void) {
|
pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url: []const u8, cacheable: bool) !(if (want_result) ModuleEntry else void) {
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
const mod, const owned_url = blk: {
|
const mod, const owned_url = blk: {
|
||||||
const arena = self.arena;
|
const arena = self.arena;
|
||||||
|
|
||||||
@@ -231,13 +234,13 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const owned_url = try arena.dupeZ(u8, url);
|
const owned_url = try arena.dupeZ(u8, url);
|
||||||
const m = try compileModule(self.isolate, src, owned_url);
|
const m = try compileModule(v8_isolate, src, owned_url);
|
||||||
|
|
||||||
if (cacheable) {
|
if (cacheable) {
|
||||||
// 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(self.isolate, m);
|
gop.value_ptr.module = PersistentModule.init(v8_isolate, m);
|
||||||
if (!gop.found_existing) {
|
if (!gop.found_existing) {
|
||||||
gop.key_ptr.* = owned_url;
|
gop.key_ptr.* = owned_url;
|
||||||
}
|
}
|
||||||
@@ -290,7 +293,7 @@ pub fn module(self: *Context, comptime want_result: bool, src: []const u8, url:
|
|||||||
std.debug.assert(entry.module != null);
|
std.debug.assert(entry.module != null);
|
||||||
std.debug.assert(entry.module_promise == null);
|
std.debug.assert(entry.module_promise == null);
|
||||||
|
|
||||||
entry.module_promise = PersistentPromise.init(self.isolate, .{ .handle = evaluated.handle });
|
entry.module_promise = PersistentPromise.init(v8_isolate, .{ .handle = evaluated.handle });
|
||||||
return if (comptime want_result) entry.* else {};
|
return if (comptime want_result) entry.* else {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +312,8 @@ pub fn stringToFunction(self: *Context, str: []const u8) !js.Function {
|
|||||||
const full = try std.fmt.allocPrintSentinel(self.call_arena, "(function(e) {{ {s}{s} }})", .{ normalized, extra }, 0);
|
const full = try std.fmt.allocPrintSentinel(self.call_arena, "(function(e) {{ {s}{s} }})", .{ normalized, extra }, 0);
|
||||||
|
|
||||||
const v8_context = self.v8_context;
|
const v8_context = self.v8_context;
|
||||||
const script = try compileScript(self.isolate, v8_context, full, null);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
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) catch {
|
||||||
return error.ExecutionError;
|
return error.ExecutionError;
|
||||||
};
|
};
|
||||||
@@ -391,17 +395,19 @@ pub fn createFunction(self: *Context, js_value: v8.Value) !js.Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn throw(self: *Context, err: []const u8) js.Exception {
|
pub fn throw(self: *Context, err: []const u8) js.Exception {
|
||||||
const js_value = js._createException(self.isolate, err);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const js_value = js._createException(v8_isolate, err);
|
||||||
return self.createException(js_value);
|
return self.createException(js_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOpts) !v8.Value {
|
pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOpts) !v8.Value {
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
|
|
||||||
// Check if it's a "simple" type. This is extracted so that it can be
|
// Check if it's a "simple" type. This is extracted so that it can be
|
||||||
// reused by other parts of the code. "simple" types only require an
|
// reused by other parts of the code. "simple" types only require an
|
||||||
// isolate to create (specifically, they don't our templates array)
|
// isolate to create (specifically, they don't our templates array)
|
||||||
if (js.simpleZigValueToJs(isolate, value, false, opts.null_as_undefined)) |js_value| {
|
if (js.simpleZigValueToJs(v8_isolate, value, false, opts.null_as_undefined)) |js_value| {
|
||||||
return js_value;
|
return js_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,7 +420,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
|
|||||||
unreachable;
|
unreachable;
|
||||||
},
|
},
|
||||||
.array => {
|
.array => {
|
||||||
var js_arr = v8.Array.init(isolate, value.len);
|
var js_arr = v8.Array.init(v8_isolate, value.len);
|
||||||
var js_obj = js_arr.castTo(v8.Object);
|
var js_obj = js_arr.castTo(v8.Object);
|
||||||
for (value, 0..) |v, i| {
|
for (value, 0..) |v, i| {
|
||||||
const js_val = try self.zigValueToJs(v, opts);
|
const js_val = try self.zigValueToJs(v, opts);
|
||||||
@@ -453,7 +459,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
|
|||||||
// have handled it
|
// have handled it
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
var js_arr = v8.Array.init(isolate, @intCast(value.len));
|
var js_arr = v8.Array.init(v8_isolate, @intCast(value.len));
|
||||||
var js_obj = js_arr.castTo(v8.Object);
|
var js_obj = js_arr.castTo(v8.Object);
|
||||||
|
|
||||||
for (value, 0..) |v, i| {
|
for (value, 0..) |v, i| {
|
||||||
@@ -504,7 +510,7 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
|
|||||||
|
|
||||||
if (s.is_tuple) {
|
if (s.is_tuple) {
|
||||||
// return the tuple struct as an array
|
// return the tuple struct as an array
|
||||||
var js_arr = v8.Array.init(isolate, @intCast(s.fields.len));
|
var js_arr = v8.Array.init(v8_isolate, @intCast(s.fields.len));
|
||||||
var js_obj = js_arr.castTo(v8.Object);
|
var js_obj = js_arr.castTo(v8.Object);
|
||||||
inline for (s.fields, 0..) |f, i| {
|
inline for (s.fields, 0..) |f, i| {
|
||||||
const js_val = try self.zigValueToJs(@field(value, f.name), opts);
|
const js_val = try self.zigValueToJs(@field(value, f.name), opts);
|
||||||
@@ -516,10 +522,10 @@ pub fn zigValueToJs(self: *Context, value: anytype, comptime opts: Caller.CallOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return the struct as a JS object
|
// return the struct as a JS object
|
||||||
const js_obj = v8.Object.init(isolate);
|
const js_obj = v8.Object.init(v8_isolate);
|
||||||
inline for (s.fields) |f| {
|
inline for (s.fields) |f| {
|
||||||
const js_val = try self.zigValueToJs(@field(value, f.name), opts);
|
const js_val = try self.zigValueToJs(@field(value, f.name), opts);
|
||||||
const key = v8.String.initUtf8(isolate, f.name);
|
const key = v8.String.initUtf8(v8_isolate, f.name);
|
||||||
if (!js_obj.setValue(v8_context, key, js_val)) {
|
if (!js_obj.setValue(v8_context, key, js_val)) {
|
||||||
return error.CreateObjectFailure;
|
return error.CreateObjectFailure;
|
||||||
}
|
}
|
||||||
@@ -590,6 +596,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
const JsApi = bridge.Struct(ptr.child).JsApi;
|
const JsApi = bridge.Struct(ptr.child).JsApi;
|
||||||
|
|
||||||
// Sometimes we're creating a new v8.Object, like when
|
// Sometimes we're creating a new v8.Object, like when
|
||||||
@@ -619,7 +626,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
|
|||||||
// Skip setting internal field for the global object (Window)
|
// Skip setting internal field for the global object (Window)
|
||||||
// Window accessors get the instance from context.page.window instead
|
// Window accessors get the instance from context.page.window instead
|
||||||
if (resolved.class_id != @import("../webapi/Window.zig").JsApi.Meta.class_id) {
|
if (resolved.class_id != @import("../webapi/Window.zig").JsApi.Meta.class_id) {
|
||||||
js_obj.setInternalField(0, v8.External.init(isolate, tao));
|
js_obj.setInternalField(0, v8.External.init(v8_isolate, tao));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If the struct is empty, we don't need to do all
|
// If the struct is empty, we don't need to do all
|
||||||
@@ -629,7 +636,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
|
|||||||
// the type is empty and can create an empty instance.
|
// the type is empty and can create an empty instance.
|
||||||
}
|
}
|
||||||
|
|
||||||
const js_persistent = PersistentObject.init(isolate, js_obj);
|
const js_persistent = PersistentObject.init(v8_isolate, js_obj);
|
||||||
gop.value_ptr.* = js_persistent;
|
gop.value_ptr.* = js_persistent;
|
||||||
return js_persistent;
|
return js_persistent;
|
||||||
},
|
},
|
||||||
@@ -638,6 +645,7 @@ pub fn mapZigInstanceToJs(self: *Context, js_obj_: ?v8.Object, value: anytype) !
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T {
|
pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T {
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
switch (@typeInfo(T)) {
|
switch (@typeInfo(T)) {
|
||||||
.optional => |o| {
|
.optional => |o| {
|
||||||
// If type type is a ?js.Value or a ?js.Object, then we want to pass
|
// If type type is a ?js.Value or a ?js.Object, then we want to pass
|
||||||
@@ -680,7 +688,7 @@ pub fn jsValueToZig(self: *Context, comptime T: type, js_value: v8.Value) !T {
|
|||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
.int => return jsIntToZig(T, js_value, self.v8_context),
|
.int => return jsIntToZig(T, js_value, self.v8_context),
|
||||||
.bool => return js_value.toBool(self.isolate),
|
.bool => return js_value.toBool(v8_isolate),
|
||||||
.pointer => |ptr| switch (ptr.size) {
|
.pointer => |ptr| switch (ptr.size) {
|
||||||
.one => {
|
.one => {
|
||||||
if (!js_value.isObject()) {
|
if (!js_value.isObject()) {
|
||||||
@@ -845,11 +853,12 @@ fn jsValueToStruct(self: *Context, comptime T: type, js_value: v8.Value) !?T {
|
|||||||
const js_obj = js_value.castTo(v8.Object);
|
const js_obj = js_value.castTo(v8.Object);
|
||||||
const v8_context = self.v8_context;
|
const v8_context = self.v8_context;
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
|
|
||||||
var value: T = undefined;
|
var value: T = undefined;
|
||||||
inline for (@typeInfo(T).@"struct".fields) |field| {
|
inline for (@typeInfo(T).@"struct".fields) |field| {
|
||||||
const name = field.name;
|
const name = field.name;
|
||||||
const key = v8.String.initUtf8(isolate, name);
|
const key = v8.String.initUtf8(v8_isolate, name);
|
||||||
if (js_obj.has(v8_context, key.toValue())) {
|
if (js_obj.has(v8_context, key.toValue())) {
|
||||||
@field(value, name) = try self.jsValueToZig(field.type, try js_obj.getValue(v8_context, key));
|
@field(value, name) = try self.jsValueToZig(field.type, try js_obj.getValue(v8_context, key));
|
||||||
} else if (@typeInfo(field.type) == .optional) {
|
} else if (@typeInfo(field.type) == .optional) {
|
||||||
@@ -1009,7 +1018,8 @@ pub fn valueToString(self: *const Context, js_val: v8.Value, opts: valueToString
|
|||||||
const allocator = opts.allocator orelse self.call_arena;
|
const allocator = opts.allocator orelse self.call_arena;
|
||||||
if (js_val.isSymbol()) {
|
if (js_val.isSymbol()) {
|
||||||
const js_sym = v8.Symbol{ .handle = js_val.handle };
|
const js_sym = v8.Symbol{ .handle = js_val.handle };
|
||||||
const js_sym_desc = js_sym.getDescription(self.isolate);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const js_sym_desc = js_sym.getDescription(v8_isolate);
|
||||||
return self.valueToString(js_sym_desc, .{});
|
return self.valueToString(js_sym_desc, .{});
|
||||||
}
|
}
|
||||||
const str = try js_val.toString(self.v8_context);
|
const str = try js_val.toString(self.v8_context);
|
||||||
@@ -1020,7 +1030,8 @@ pub fn valueToStringZ(self: *const Context, js_val: v8.Value, opts: valueToStrin
|
|||||||
const allocator = opts.allocator orelse self.call_arena;
|
const allocator = opts.allocator orelse self.call_arena;
|
||||||
if (js_val.isSymbol()) {
|
if (js_val.isSymbol()) {
|
||||||
const js_sym = v8.Symbol{ .handle = js_val.handle };
|
const js_sym = v8.Symbol{ .handle = js_val.handle };
|
||||||
const js_sym_desc = js_sym.getDescription(self.isolate);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const js_sym_desc = js_sym.getDescription(v8_isolate);
|
||||||
return self.valueToStringZ(js_sym_desc, .{});
|
return self.valueToStringZ(js_sym_desc, .{});
|
||||||
}
|
}
|
||||||
const str = try js_val.toString(self.v8_context);
|
const str = try js_val.toString(self.v8_context);
|
||||||
@@ -1032,18 +1043,20 @@ const JsStringToZigOpts = struct {
|
|||||||
};
|
};
|
||||||
pub fn jsStringToZig(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![]u8 {
|
pub fn jsStringToZig(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![]u8 {
|
||||||
const allocator = opts.allocator orelse self.call_arena;
|
const allocator = opts.allocator orelse self.call_arena;
|
||||||
const len = str.lenUtf8(self.isolate);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const len = str.lenUtf8(v8_isolate);
|
||||||
const buf = try allocator.alloc(u8, len);
|
const buf = try allocator.alloc(u8, len);
|
||||||
const n = str.writeUtf8(self.isolate, buf);
|
const n = str.writeUtf8(v8_isolate, buf);
|
||||||
std.debug.assert(n == len);
|
std.debug.assert(n == len);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jsStringToZigZ(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![:0]u8 {
|
pub fn jsStringToZigZ(self: *const Context, str: v8.String, opts: JsStringToZigOpts) ![:0]u8 {
|
||||||
const allocator = opts.allocator orelse self.call_arena;
|
const allocator = opts.allocator orelse self.call_arena;
|
||||||
const len = str.lenUtf8(self.isolate);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const len = str.lenUtf8(v8_isolate);
|
||||||
const buf = try allocator.allocSentinel(u8, len, 0);
|
const buf = try allocator.allocSentinel(u8, len, 0);
|
||||||
const n = str.writeUtf8(self.isolate, buf);
|
const n = str.writeUtf8(v8_isolate, buf);
|
||||||
std.debug.assert(n == len);
|
std.debug.assert(n == len);
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
@@ -1073,13 +1086,14 @@ fn _debugValue(self: *const Context, js_val: v8.Value, seen: *std.AutoHashMapUnm
|
|||||||
return writer.writeAll("false");
|
return writer.writeAll("false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
if (js_val.isSymbol()) {
|
if (js_val.isSymbol()) {
|
||||||
const js_sym = v8.Symbol{ .handle = js_val.handle };
|
const js_sym = v8.Symbol{ .handle = js_val.handle };
|
||||||
const js_sym_desc = js_sym.getDescription(self.isolate);
|
const js_sym_desc = js_sym.getDescription(v8_isolate);
|
||||||
const js_sym_str = try self.valueToString(js_sym_desc, .{});
|
const js_sym_str = try self.valueToString(js_sym_desc, .{});
|
||||||
return writer.print("{s} (symbol)", .{js_sym_str});
|
return writer.print("{s} (symbol)", .{js_sym_str});
|
||||||
}
|
}
|
||||||
const js_type = try self.jsStringToZig(try js_val.typeOf(self.isolate), .{});
|
const js_type = try self.jsStringToZig(try js_val.typeOf(v8_isolate), .{});
|
||||||
const js_val_str = try self.valueToString(js_val, .{});
|
const js_val_str = try self.valueToString(js_val, .{});
|
||||||
if (js_val_str.len > 2000) {
|
if (js_val_str.len > 2000) {
|
||||||
try writer.writeAll(js_val_str[0..2000]);
|
try writer.writeAll(js_val_str[0..2000]);
|
||||||
@@ -1142,20 +1156,21 @@ pub fn stackTrace(self: *const Context) !?[]const u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
const separator = log.separator();
|
const separator = log.separator();
|
||||||
|
|
||||||
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
var buf: std.ArrayListUnmanaged(u8) = .empty;
|
||||||
var writer = buf.writer(self.call_arena);
|
var writer = buf.writer(self.call_arena);
|
||||||
|
|
||||||
const stack_trace = v8.StackTrace.getCurrentStackTrace(isolate, 30);
|
const stack_trace = v8.StackTrace.getCurrentStackTrace(v8_isolate, 30);
|
||||||
const frame_count = stack_trace.getFrameCount();
|
const frame_count = stack_trace.getFrameCount();
|
||||||
|
|
||||||
if (v8.StackTrace.getCurrentScriptNameOrSourceUrl(isolate)) |script| {
|
if (v8.StackTrace.getCurrentScriptNameOrSourceUrl(v8_isolate)) |script| {
|
||||||
try writer.print("{s}<{s}>", .{ separator, try self.jsStringToZig(script, .{}) });
|
try writer.print("{s}<{s}>", .{ separator, try self.jsStringToZig(script, .{}) });
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0..frame_count) |i| {
|
for (0..frame_count) |i| {
|
||||||
const frame = stack_trace.getFrame(isolate, @intCast(i));
|
const frame = stack_trace.getFrame(v8_isolate, @intCast(i));
|
||||||
if (frame.getScriptName()) |name| {
|
if (frame.getScriptName()) |name| {
|
||||||
const script = try self.jsStringToZig(name, .{});
|
const script = try self.jsStringToZig(name, .{});
|
||||||
try writer.print("{s}{s}:{d}", .{ separator, script, frame.getLineNumber() });
|
try writer.print("{s}{s}:{d}", .{ separator, script, frame.getLineNumber() });
|
||||||
@@ -1214,7 +1229,8 @@ pub fn createPromiseResolver(self: *Context, comptime lifetime: PromiseResolverL
|
|||||||
return .{ .context = self, .resolver = resolver };
|
return .{ .context = self, .resolver = resolver };
|
||||||
}
|
}
|
||||||
|
|
||||||
const persisted = v8.Persistent(v8.PromiseResolver).init(self.isolate, resolver);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const persisted = v8.Persistent(v8.PromiseResolver).init(v8_isolate, resolver);
|
||||||
|
|
||||||
if (comptime lifetime == .page) {
|
if (comptime lifetime == .page) {
|
||||||
try self.persisted_promise_resolvers.append(self.arena, persisted);
|
try self.persisted_promise_resolvers.append(self.arena, persisted);
|
||||||
@@ -1305,7 +1321,8 @@ pub fn metaObjectCallback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const js_key = v8.String.initUtf8(self.isolate, "url");
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const js_key = v8.String.initUtf8(v8_isolate, "url");
|
||||||
const js_value = try self.zigValueToJs(url, .{});
|
const js_value = try self.zigValueToJs(url, .{});
|
||||||
const res = meta.defineOwnProperty(self.v8_context, js_key.toName(), js_value, 0) orelse false;
|
const res = meta.defineOwnProperty(self.v8_context, js_key.toName(), js_value, 0) orelse false;
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@@ -1337,9 +1354,10 @@ fn _resolveModuleCallback(self: *Context, referrer: v8.Module, specifier: [:0]co
|
|||||||
try_catch.init(self);
|
try_catch.init(self);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
const mod = try compileModule(self.isolate, source.src(), normalized_specifier);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
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(self.isolate, mod);
|
entry.module = PersistentModule.init(v8_isolate, mod);
|
||||||
return entry.module.?.castToModule().handle;
|
return entry.module.?.castToModule().handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1357,6 +1375,7 @@ const DynamicModuleResolveState = struct {
|
|||||||
|
|
||||||
fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !v8.Promise {
|
fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []const u8) !v8.Promise {
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
|
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);
|
||||||
if (gop.found_existing and gop.value_ptr.resolver_promise != null) {
|
if (gop.found_existing and gop.value_ptr.resolver_promise != null) {
|
||||||
// This is easy, there's already something responsible
|
// This is easy, there's already something responsible
|
||||||
@@ -1365,7 +1384,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
return gop.value_ptr.resolver_promise.?.castToPromise();
|
return gop.value_ptr.resolver_promise.?.castToPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
const persistent_resolver = v8.Persistent(v8.PromiseResolver).init(isolate, v8.PromiseResolver.init(self.v8_context));
|
const persistent_resolver = v8.Persistent(v8.PromiseResolver).init(v8_isolate, v8.PromiseResolver.init(self.v8_context));
|
||||||
try self.persisted_promise_resolvers.append(self.arena, persistent_resolver);
|
try self.persisted_promise_resolvers.append(self.arena, persistent_resolver);
|
||||||
var resolver = persistent_resolver.castToPromiseResolver();
|
var resolver = persistent_resolver.castToPromiseResolver();
|
||||||
|
|
||||||
@@ -1379,7 +1398,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
.resolver = persistent_resolver,
|
.resolver = persistent_resolver,
|
||||||
};
|
};
|
||||||
|
|
||||||
const persisted_promise = PersistentPromise.init(self.isolate, resolver.getPromise());
|
const persisted_promise = PersistentPromise.init(v8_isolate, resolver.getPromise());
|
||||||
const promise = persisted_promise.castToPromise();
|
const promise = persisted_promise.castToPromise();
|
||||||
|
|
||||||
if (!gop.found_existing) {
|
if (!gop.found_existing) {
|
||||||
@@ -1397,7 +1416,7 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
|
|
||||||
// Next, we need to actually load it.
|
// Next, we need to actually load it.
|
||||||
self.script_manager.?.getAsyncImport(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| {
|
self.script_manager.?.getAsyncImport(specifier, dynamicModuleSourceCallback, state, referrer) catch |err| {
|
||||||
const error_msg = v8.String.initUtf8(isolate, @errorName(err));
|
const error_msg = v8.String.initUtf8(v8_isolate, @errorName(err));
|
||||||
_ = resolver.reject(self.v8_context, error_msg.toValue());
|
_ = resolver.reject(self.v8_context, error_msg.toValue());
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1425,21 +1444,21 @@ fn _dynamicModuleCallback(self: *Context, specifier: [:0]const u8, referrer: []c
|
|||||||
if (status == .kEvaluated or status == .kEvaluating) {
|
if (status == .kEvaluated or status == .kEvaluating) {
|
||||||
// Module was already evaluated (shouldn't normally happen, but handle it).
|
// Module was already evaluated (shouldn't normally happen, but handle it).
|
||||||
// Create a pre-resolved promise with the module namespace.
|
// Create a pre-resolved promise with the module namespace.
|
||||||
const persisted_module_resolver = v8.Persistent(v8.PromiseResolver).init(isolate, v8.PromiseResolver.init(self.v8_context));
|
const persisted_module_resolver = v8.Persistent(v8.PromiseResolver).init(v8_isolate, v8.PromiseResolver.init(self.v8_context));
|
||||||
try self.persisted_promise_resolvers.append(self.arena, persisted_module_resolver);
|
try self.persisted_promise_resolvers.append(self.arena, persisted_module_resolver);
|
||||||
var module_resolver = persisted_module_resolver.castToPromiseResolver();
|
var module_resolver = persisted_module_resolver.castToPromiseResolver();
|
||||||
_ = module_resolver.resolve(self.v8_context, mod.getModuleNamespace());
|
_ = module_resolver.resolve(self.v8_context, mod.getModuleNamespace());
|
||||||
gop.value_ptr.module_promise = PersistentPromise.init(self.isolate, module_resolver.getPromise());
|
gop.value_ptr.module_promise = PersistentPromise.init(v8_isolate, module_resolver.getPromise());
|
||||||
} else {
|
} else {
|
||||||
// the module was loaded, but not evaluated, we _have_ to evaluate it now
|
// the module was loaded, but not evaluated, we _have_ to evaluate it now
|
||||||
const evaluated = mod.evaluate(self.v8_context) catch {
|
const evaluated = mod.evaluate(self.v8_context) catch {
|
||||||
std.debug.assert(status == .kErrored);
|
std.debug.assert(status == .kErrored);
|
||||||
const error_msg = v8.String.initUtf8(isolate, "Module evaluation failed");
|
const error_msg = v8.String.initUtf8(v8_isolate, "Module evaluation failed");
|
||||||
_ = resolver.reject(self.v8_context, error_msg.toValue());
|
_ = resolver.reject(self.v8_context, error_msg.toValue());
|
||||||
return promise;
|
return promise;
|
||||||
};
|
};
|
||||||
std.debug.assert(evaluated.isPromise());
|
std.debug.assert(evaluated.isPromise());
|
||||||
gop.value_ptr.module_promise = PersistentPromise.init(self.isolate, .{ .handle = evaluated.handle });
|
gop.value_ptr.module_promise = PersistentPromise.init(v8_isolate, .{ .handle = evaluated.handle });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1459,7 +1478,8 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
|
|||||||
var self = state.context;
|
var self = state.context;
|
||||||
|
|
||||||
var ms = module_source_ catch |err| {
|
var ms = module_source_ catch |err| {
|
||||||
const error_msg = v8.String.initUtf8(self.isolate, @errorName(err));
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const error_msg = v8.String.initUtf8(v8_isolate, @errorName(err));
|
||||||
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
|
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -1479,7 +1499,8 @@ fn dynamicModuleSourceCallback(ctx: *anyopaque, module_source_: anyerror!ScriptM
|
|||||||
.stack = try_catch.stack(self.call_arena) catch null,
|
.stack = try_catch.stack(self.call_arena) catch null,
|
||||||
.line = try_catch.sourceLineNumber() orelse 0,
|
.line = try_catch.sourceLineNumber() orelse 0,
|
||||||
});
|
});
|
||||||
const error_msg = v8.String.initUtf8(self.isolate, ex);
|
const v8_isolate = v8.Isolate{ .handle = self.isolate.handle };
|
||||||
|
const error_msg = v8.String.initUtf8(v8_isolate, ex);
|
||||||
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
|
_ = state.resolver.castToPromiseResolver().reject(self.v8_context, error_msg.toValue());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -1492,7 +1513,8 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
|||||||
defer self.runMicrotasks();
|
defer self.runMicrotasks();
|
||||||
const ctx = self.v8_context;
|
const ctx = self.v8_context;
|
||||||
const isolate = self.isolate;
|
const isolate = self.isolate;
|
||||||
const external = v8.External.init(self.isolate, @ptrCast(state));
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
|
const external = v8.External.init(v8_isolate, @ptrCast(state));
|
||||||
|
|
||||||
// we can only be here if the module has been evaluated and if
|
// we can only be here if the module has been evaluated and if
|
||||||
// we have a resolve loading this asynchronously.
|
// we have a resolve loading this asynchronously.
|
||||||
@@ -1551,7 +1573,7 @@ fn resolveDynamicModule(self: *Context, state: *DynamicModuleResolveState, modul
|
|||||||
.err = err,
|
.err = err,
|
||||||
.specifier = state.specifier,
|
.specifier = state.specifier,
|
||||||
});
|
});
|
||||||
const error_msg = v8.String.initUtf8(isolate, "Failed to evaluate promise");
|
const error_msg = v8.String.initUtf8(v8_isolate, "Failed to evaluate promise");
|
||||||
_ = state.resolver.castToPromiseResolver().reject(ctx, error_msg.toValue());
|
_ = state.resolver.castToPromiseResolver().reject(ctx, error_msg.toValue());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1582,7 +1604,8 @@ pub fn typeTaggedAnyOpaque(comptime R: type, js_obj: v8.Object) !R {
|
|||||||
// Normally, this would be an error. All JsObject that map to a Zig type
|
// Normally, this would be an error. All JsObject that map to a Zig type
|
||||||
// are either `empty_with_no_proto` (handled above) or have an
|
// are either `empty_with_no_proto` (handled above) or have an
|
||||||
// interalFieldCount. The only exception to that is the Window...
|
// interalFieldCount. The only exception to that is the Window...
|
||||||
const isolate = js_obj.getIsolate();
|
const v8_isolate = js_obj.getIsolate();
|
||||||
|
const isolate = js.Isolate{ .handle = v8_isolate.handle };
|
||||||
const context = fromIsolate(isolate);
|
const context = fromIsolate(isolate);
|
||||||
|
|
||||||
const Window = @import("../webapi/Window.zig");
|
const Window = @import("../webapi/Window.zig");
|
||||||
@@ -2024,7 +2047,7 @@ pub fn queueSlotchangeDelivery(self: *Context) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void {
|
pub fn queueMicrotaskFunc(self: *Context, cb: js.Function) void {
|
||||||
self.isolate.enqueueMicrotaskFunc(.{ .handle = cb.handle });
|
self.isolate.enqueueMicrotaskFunc(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// == Misc ==
|
// == Misc ==
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ allocator: Allocator,
|
|||||||
platform: *const Platform,
|
platform: *const Platform,
|
||||||
|
|
||||||
// the global isolate
|
// the global isolate
|
||||||
isolate: v8.Isolate,
|
isolate: js.Isolate,
|
||||||
|
|
||||||
// just kept around because we need to free it on deinit
|
// just kept around because we need to free it on deinit
|
||||||
isolate_params: *v8.CreateParams,
|
isolate_params: *v8.CreateParams,
|
||||||
@@ -67,28 +67,30 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot
|
|||||||
|
|
||||||
params.external_references = &snapshot.external_references;
|
params.external_references = &snapshot.external_references;
|
||||||
|
|
||||||
var isolate = v8.Isolate.init(params);
|
var v8_isolate = v8.Isolate.init(params);
|
||||||
errdefer isolate.deinit();
|
errdefer v8_isolate.deinit();
|
||||||
|
|
||||||
// This is the callback that runs whenever a module is dynamically imported.
|
// This is the callback that runs whenever a module is dynamically imported.
|
||||||
isolate.setHostImportModuleDynamicallyCallback(Context.dynamicModuleCallback);
|
v8_isolate.setHostImportModuleDynamicallyCallback(Context.dynamicModuleCallback);
|
||||||
isolate.setPromiseRejectCallback(promiseRejectCallback);
|
v8_isolate.setPromiseRejectCallback(promiseRejectCallback);
|
||||||
isolate.setMicrotasksPolicy(v8.c.kExplicit);
|
v8_isolate.setMicrotasksPolicy(v8.c.kExplicit);
|
||||||
|
|
||||||
isolate.enter();
|
v8_isolate.enter();
|
||||||
errdefer isolate.exit();
|
errdefer v8_isolate.exit();
|
||||||
|
|
||||||
isolate.setHostInitializeImportMetaObjectCallback(Context.metaObjectCallback);
|
v8_isolate.setHostInitializeImportMetaObjectCallback(Context.metaObjectCallback);
|
||||||
|
|
||||||
|
const isolate = js.Isolate{ .handle = v8_isolate.handle };
|
||||||
|
|
||||||
// Allocate templates array dynamically to avoid comptime dependency on JsApis.len
|
// Allocate templates array dynamically to avoid comptime dependency on JsApis.len
|
||||||
const templates = try allocator.alloc(v8.FunctionTemplate, JsApis.len);
|
const templates = try allocator.alloc(v8.FunctionTemplate, JsApis.len);
|
||||||
errdefer allocator.free(templates);
|
errdefer allocator.free(templates);
|
||||||
|
|
||||||
{
|
{
|
||||||
var temp_scope: v8.HandleScope = undefined;
|
var temp_scope: js.HandleScope = undefined;
|
||||||
v8.HandleScope.init(&temp_scope, isolate);
|
temp_scope.init(isolate);
|
||||||
defer temp_scope.deinit();
|
defer temp_scope.deinit();
|
||||||
const context = v8.Context.init(isolate, null, null);
|
const context = v8.Context.init(v8_isolate, null, null);
|
||||||
|
|
||||||
context.enter();
|
context.enter();
|
||||||
defer context.exit();
|
defer context.exit();
|
||||||
@@ -97,7 +99,7 @@ pub fn init(allocator: Allocator, platform: *const Platform, snapshot: *Snapshot
|
|||||||
JsApi.Meta.class_id = i;
|
JsApi.Meta.class_id = i;
|
||||||
const data = context.getDataFromSnapshotOnce(snapshot.data_start + i);
|
const data = context.getDataFromSnapshotOnce(snapshot.data_start + i);
|
||||||
const function = v8.FunctionTemplate{ .handle = @ptrCast(data) };
|
const function = v8.FunctionTemplate{ .handle = @ptrCast(data) };
|
||||||
templates[i] = v8.Persistent(v8.FunctionTemplate).init(isolate, function).castToFunctionTemplate();
|
templates[i] = v8.Persistent(v8.FunctionTemplate).init(v8_isolate, function).castToFunctionTemplate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,8 +151,8 @@ pub fn newExecutionWorld(self: *Env) !ExecutionWorld {
|
|||||||
// `lowMemoryNotification` call on the isolate to encourage v8 to free
|
// `lowMemoryNotification` call on the isolate to encourage v8 to free
|
||||||
// any contexts which have been freed.
|
// any contexts which have been freed.
|
||||||
pub fn lowMemoryNotification(self: *Env) void {
|
pub fn lowMemoryNotification(self: *Env) void {
|
||||||
var handle_scope: v8.HandleScope = undefined;
|
var handle_scope: js.HandleScope = undefined;
|
||||||
v8.HandleScope.init(&handle_scope, self.isolate);
|
handle_scope.init(self.isolate);
|
||||||
defer handle_scope.deinit();
|
defer handle_scope.deinit();
|
||||||
self.isolate.lowMemoryNotification();
|
self.isolate.lowMemoryNotification();
|
||||||
}
|
}
|
||||||
@@ -178,8 +180,9 @@ pub fn dumpMemoryStats(self: *Env) void {
|
|||||||
|
|
||||||
fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void {
|
fn promiseRejectCallback(v8_msg: v8.C_PromiseRejectMessage) callconv(.c) void {
|
||||||
const msg = v8.PromiseRejectMessage.initFromC(v8_msg);
|
const msg = v8.PromiseRejectMessage.initFromC(v8_msg);
|
||||||
const isolate = msg.getPromise().toObject().getIsolate();
|
const v8_isolate = msg.getPromise().toObject().getIsolate();
|
||||||
const context = Context.fromIsolate(isolate);
|
const js_isolate = js.Isolate{ .handle = v8_isolate.handle };
|
||||||
|
const context = Context.fromIsolate(js_isolate);
|
||||||
|
|
||||||
const value =
|
const value =
|
||||||
if (msg.getValue()) |v8_value|
|
if (msg.getValue()) |v8_value|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ pub fn deinit(self: *ExecutionWorld) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only the top Context in the Main ExecutionWorld should hold a handle_scope.
|
// Only the top Context in the Main ExecutionWorld should hold a handle_scope.
|
||||||
// A v8.HandleScope is like an arena. Once created, any "Local" that
|
// A js.HandleScope is like an arena. Once created, any "Local" that
|
||||||
// v8 creates will be released (or at least, releasable by the v8 GC)
|
// v8 creates will be released (or at least, releasable by the v8 GC)
|
||||||
// when the handle_scope is freed.
|
// when the handle_scope is freed.
|
||||||
// We also maintain our own "context_arena" which allows us to have
|
// We also maintain our own "context_arena" which allows us to have
|
||||||
@@ -77,19 +77,20 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context
|
|||||||
const arena = self.context_arena.allocator();
|
const arena = self.context_arena.allocator();
|
||||||
|
|
||||||
var v8_context: v8.Context = blk: {
|
var v8_context: v8.Context = blk: {
|
||||||
var temp_scope: v8.HandleScope = undefined;
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
v8.HandleScope.init(&temp_scope, isolate);
|
var temp_scope: js.HandleScope = undefined;
|
||||||
|
temp_scope.init(isolate);
|
||||||
defer temp_scope.deinit();
|
defer temp_scope.deinit();
|
||||||
|
|
||||||
// Creates a global template that inherits from Window.
|
// Creates a global template that inherits from Window.
|
||||||
const global_template = @import("Snapshot.zig").createGlobalTemplate(isolate, env.templates);
|
const global_template = @import("Snapshot.zig").createGlobalTemplate(isolate, env.templates);
|
||||||
|
|
||||||
// Add the named property handler
|
// Add the named property handler
|
||||||
global_template.setNamedProperty(v8.NamedPropertyHandlerConfiguration{
|
global_template.setNamedProperty(v8.NamedPropertyHandlerConfiguration{
|
||||||
.getter = unknownPropertyCallback,
|
.getter = unknownPropertyCallback,
|
||||||
.flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings,
|
.flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings,
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
|
|
||||||
const context_local = v8.Context.init(isolate, global_template, null);
|
const context_local = v8.Context.init(isolate, global_template, null);
|
||||||
const v8_context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext();
|
const v8_context = v8.Persistent(v8.Context).init(isolate, context_local).castToContext();
|
||||||
break :blk v8_context;
|
break :blk v8_context;
|
||||||
@@ -98,10 +99,10 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context
|
|||||||
// For a Page we only create one HandleScope, it is stored in the main World (enter==true). A page can have multple contexts, 1 for each World.
|
// For a Page we only create one HandleScope, it is stored in the main World (enter==true). A page can have multple contexts, 1 for each World.
|
||||||
// The main Context that enters and holds the HandleScope should therefore always be created first. Following other worlds for this page
|
// The main Context that enters and holds the HandleScope should therefore always be created first. Following other worlds for this page
|
||||||
// like isolated Worlds, will thereby place their objects on the main page's HandleScope. Note: In the furure the number of context will multiply multiple frames support
|
// like isolated Worlds, will thereby place their objects on the main page's HandleScope. Note: In the furure the number of context will multiply multiple frames support
|
||||||
var handle_scope: ?v8.HandleScope = null;
|
var handle_scope: ?js.HandleScope = null;
|
||||||
if (enter) {
|
if (enter) {
|
||||||
handle_scope = @as(v8.HandleScope, undefined);
|
handle_scope = @as(js.HandleScope, undefined);
|
||||||
v8.HandleScope.init(&handle_scope.?, isolate);
|
handle_scope.?.init(isolate);
|
||||||
v8_context.enter();
|
v8_context.enter();
|
||||||
}
|
}
|
||||||
errdefer if (enter) {
|
errdefer if (enter) {
|
||||||
@@ -127,7 +128,8 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool) !*Context
|
|||||||
var context = &self.context.?;
|
var context = &self.context.?;
|
||||||
// Store a pointer to our context inside the v8 context so that, given
|
// Store a pointer to our context inside the v8 context so that, given
|
||||||
// a v8 context, we can get our context out
|
// a v8 context, we can get our context out
|
||||||
const data = isolate.initBigIntU64(@intCast(@intFromPtr(context)));
|
const v8_isolate = v8.Isolate{ .handle = isolate.handle };
|
||||||
|
const data = v8_isolate.initBigIntU64(@intCast(@intFromPtr(context)));
|
||||||
v8_context.setEmbedderData(1, data);
|
v8_context.setEmbedderData(1, data);
|
||||||
|
|
||||||
try context.setupGlobal();
|
try context.setupGlobal();
|
||||||
@@ -156,8 +158,8 @@ pub fn resumeExecution(self: *const ExecutionWorld) void {
|
|||||||
|
|
||||||
pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 {
|
pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 {
|
||||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||||
|
|
||||||
const context = Context.fromIsolate(info.getIsolate());
|
const context = Context.fromIsolate(info.getIsolate());
|
||||||
|
|
||||||
const maybe_property: ?[]u8 = context.valueToString(.{ .handle = c_name.? }, .{}) catch null;
|
const maybe_property: ?[]u8 = context.valueToString(.{ .handle = c_name.? }, .{}) catch null;
|
||||||
|
|
||||||
const ignored = std.StaticStringMap(void).initComptime(.{
|
const ignored = std.StaticStringMap(void).initComptime(.{
|
||||||
@@ -196,6 +198,7 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C
|
|||||||
return v8.Intercepted.Yes;
|
return v8.Intercepted.Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (comptime IS_DEBUG) {
|
||||||
log.debug(.unknown_prop, "unknown global property", .{
|
log.debug(.unknown_prop, "unknown global property", .{
|
||||||
.info = "but the property can exist in pure JS",
|
.info = "but the property can exist in pure JS",
|
||||||
.stack = context.stackTrace() catch "???",
|
.stack = context.stackTrace() catch "???",
|
||||||
@@ -203,6 +206,7 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return v8.Intercepted.No;
|
return v8.Intercepted.No;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,8 @@ pub fn src(self: *const Function) ![]const u8 {
|
|||||||
|
|
||||||
pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value {
|
pub fn getPropertyValue(self: *const Function, name: []const u8) !?js.Value {
|
||||||
const ctx = self.ctx;
|
const ctx = self.ctx;
|
||||||
const key = v8.String.initUtf8(ctx.isolate, name);
|
const v8_isolate = v8.Isolate{ .handle = ctx.isolate.handle };
|
||||||
|
const key = v8.String.initUtf8(v8_isolate, name);
|
||||||
const handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, key.handle) orelse {
|
const handle = v8.c.v8__Object__Get(self.handle, ctx.v8_context.handle, key.handle) orelse {
|
||||||
return error.JsException;
|
return error.JsException;
|
||||||
};
|
};
|
||||||
|
|||||||
32
src/browser/js/HandleScope.zig
Normal file
32
src/browser/js/HandleScope.zig
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// 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 HandleScope = @This();
|
||||||
|
|
||||||
|
inner: v8.c.HandleScope,
|
||||||
|
|
||||||
|
pub fn init(self: *HandleScope, isolate: js.Isolate) void {
|
||||||
|
v8.c.v8__HandleScope__CONSTRUCT(&self.inner, isolate.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: *HandleScope) void {
|
||||||
|
v8.c.v8__HandleScope__DESTRUCT(&self.inner);
|
||||||
|
}
|
||||||
65
src/browser/js/Isolate.zig
Normal file
65
src/browser/js/Isolate.zig
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// 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 Isolate = @This();
|
||||||
|
|
||||||
|
handle: *v8.c.Isolate,
|
||||||
|
|
||||||
|
pub fn deinit(self: Isolate) void {
|
||||||
|
v8.c.v8__Isolate__Dispose(self.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit(self: Isolate) void {
|
||||||
|
v8.c.v8__Isolate__Exit(self.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn performMicrotasksCheckpoint(self: Isolate) void {
|
||||||
|
v8.c.v8__Isolate__PerformMicrotaskCheckpoint(self.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enqueueMicrotask(self: Isolate, callback: anytype, data: anytype) void {
|
||||||
|
const v8_isolate = v8.Isolate{ .handle = self.handle };
|
||||||
|
v8_isolate.enqueueMicrotask(callback, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enqueueMicrotaskFunc(self: Isolate, function: js.Function) void {
|
||||||
|
v8.c.v8__Isolate__EnqueueMicrotaskFunc(self.handle, function.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lowMemoryNotification(self: Isolate) void {
|
||||||
|
v8.c.v8__Isolate__LowMemoryNotification(self.handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getHeapStatistics(self: Isolate) v8.c.HeapStatistics {
|
||||||
|
var res: v8.c.HeapStatistics = undefined;
|
||||||
|
v8.c.v8__Isolate__GetHeapStatistics(self.handle, &res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn throwException(self: Isolate, value: anytype) v8.Value {
|
||||||
|
const handle = switch (@TypeOf(value)) {
|
||||||
|
v8.Value => value.handle,
|
||||||
|
else => @compileError("Unsupported type for throwException"),
|
||||||
|
};
|
||||||
|
return .{
|
||||||
|
.handle = v8.c.v8__Isolate__ThrowException(self.handle, handle).?,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -85,7 +85,8 @@ pub fn toBool(self: Value) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn fromJson(ctx: *js.Context, json: []const u8) !Value {
|
pub fn fromJson(ctx: *js.Context, json: []const u8) !Value {
|
||||||
const json_string = v8.String.initUtf8(ctx.isolate, json);
|
const v8_isolate = v8.Isolate{ .handle = ctx.isolate.handle };
|
||||||
|
const json_string = v8.String.initUtf8(v8_isolate, json);
|
||||||
const value = try v8.Json.parse(ctx.v8_context, json_string);
|
const value = try v8.Json.parse(ctx.v8_context, json_string);
|
||||||
return .{ .ctx = ctx, .handle = value.handle };
|
return .{ .ctx = ctx, .handle = value.handle };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ pub const Context = @import("Context.zig");
|
|||||||
pub const Inspector = @import("Inspector.zig");
|
pub const Inspector = @import("Inspector.zig");
|
||||||
pub const Snapshot = @import("Snapshot.zig");
|
pub const Snapshot = @import("Snapshot.zig");
|
||||||
pub const Platform = @import("Platform.zig");
|
pub const Platform = @import("Platform.zig");
|
||||||
|
pub const Isolate = @import("Isolate.zig");
|
||||||
|
pub const HandleScope = @import("HandleScope.zig");
|
||||||
|
|
||||||
pub const Value = @import("Value.zig");
|
pub const Value = @import("Value.zig");
|
||||||
pub const Array = @import("Array.zig");
|
pub const Array = @import("Array.zig");
|
||||||
|
|||||||
Reference in New Issue
Block a user