mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-02-04 06:23:45 +00:00
Cleanup js -> string
Converting a JS value to a string is a bit messy right now. There's duplication between string helpers in js.Local, and what js.String and js.Value provide. Now, all stringifying functions are in js.String, with some helpers in js.Value. Also tried to streamline the APIs around most common-cases (e.g. js.String -> []u8 using call_arena). js.String now also implements format, so it can be used as-is in some cases.
This commit is contained in:
@@ -276,7 +276,6 @@ fn reset(self: *Page, comptime initializing: bool) !void {
|
|||||||
self._arena_pool_leak_track.clearRetainingCapacity();
|
self._arena_pool_leak_track.clearRetainingCapacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// We force a garbage collection between page navigations to keep v8
|
// We force a garbage collection between page navigations to keep v8
|
||||||
// memory usage as low as possible.
|
// memory usage as low as possible.
|
||||||
self._session.browser.env.memoryPressureNotification(.moderate);
|
self._session.browser.env.memoryPressureNotification(.moderate);
|
||||||
|
|||||||
@@ -314,14 +314,14 @@ fn isInErrorSet(err: anyerror, comptime T: type) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn nameToString(self: *const Caller, comptime T: type, name: *const v8.Name) !T {
|
fn nameToString(self: *const Caller, comptime T: type, name: *const v8.Name) !T {
|
||||||
const v8_string = @as(*const v8.String, @ptrCast(name));
|
const handle = @as(*const v8.String, @ptrCast(name));
|
||||||
if (T == string.String) {
|
if (T == string.String) {
|
||||||
return self.local.jsStringToStringSSO(v8_string, .{});
|
return js.String.toSSO(.{ .local = &self.local, .handle = handle }, false);
|
||||||
}
|
}
|
||||||
if (T == string.Global) {
|
if (T == string.Global) {
|
||||||
return self.local.jsStringToStringSSO(v8_string, .{ .allocator = self.local.ctx.allocator });
|
return js.String.toSSO(.{ .local = &self.local, .handle = handle }, true);
|
||||||
}
|
}
|
||||||
return try self.local.valueHandleToString(v8_string, .{});
|
return try js.String.toSlice(.{ .local = &self.local, .handle = handle });
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
|||||||
@@ -357,9 +357,13 @@ pub fn module(self: *Context, comptime want_result: bool, local: *const js.Local
|
|||||||
|
|
||||||
// Some module-loading errors aren't handled by TryCatch. We need to
|
// Some module-loading errors aren't handled by TryCatch. We need to
|
||||||
// get the error from the module itself.
|
// get the error from the module itself.
|
||||||
|
const message = blk: {
|
||||||
|
const e = mod.getException().toString() catch break :blk "???";
|
||||||
|
break :blk e.toSlice() catch "???";
|
||||||
|
};
|
||||||
log.warn(.js, "evaluate module", .{
|
log.warn(.js, "evaluate module", .{
|
||||||
|
.message = message,
|
||||||
.specifier = owned_url,
|
.specifier = owned_url,
|
||||||
.message = mod.getException().toString(.{}) catch "???",
|
|
||||||
});
|
});
|
||||||
return error.EvaluationError;
|
return error.EvaluationError;
|
||||||
};
|
};
|
||||||
@@ -457,11 +461,11 @@ fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: *
|
|||||||
const request_len = requests.len();
|
const request_len = requests.len();
|
||||||
const script_manager = self.script_manager.?;
|
const script_manager = self.script_manager.?;
|
||||||
for (0..request_len) |i| {
|
for (0..request_len) |i| {
|
||||||
const specifier = try local.jsStringToZigZ(requests.get(i).specifier(), .{});
|
const specifier = requests.get(i).specifier(local);
|
||||||
const normalized_specifier = try script_manager.resolveSpecifier(
|
const normalized_specifier = try script_manager.resolveSpecifier(
|
||||||
self.call_arena,
|
self.call_arena,
|
||||||
url,
|
url,
|
||||||
specifier,
|
try specifier.toSliceZ(),
|
||||||
);
|
);
|
||||||
const nested_gop = try self.module_cache.getOrPut(self.arena, normalized_specifier);
|
const nested_gop = try self.module_cache.getOrPut(self.arena, normalized_specifier);
|
||||||
if (!nested_gop.found_existing) {
|
if (!nested_gop.found_existing) {
|
||||||
@@ -494,14 +498,14 @@ fn resolveModuleCallback(
|
|||||||
_ = import_attributes;
|
_ = import_attributes;
|
||||||
|
|
||||||
const self = fromC(c_context.?);
|
const self = fromC(c_context.?);
|
||||||
var local = js.Local{
|
const local = js.Local{
|
||||||
.ctx = self,
|
.ctx = self,
|
||||||
.handle = c_context.?,
|
.handle = c_context.?,
|
||||||
.isolate = self.isolate,
|
.isolate = self.isolate,
|
||||||
.call_arena = self.call_arena,
|
.call_arena = self.call_arena,
|
||||||
};
|
};
|
||||||
|
|
||||||
const specifier = local.jsStringToZigZ(c_specifier.?, .{}) catch |err| {
|
const specifier = js.String.toSliceZ(.{ .local = &local, .handle = c_specifier.? }) catch |err| {
|
||||||
log.err(.js, "resolve module", .{ .err = err });
|
log.err(.js, "resolve module", .{ .err = err });
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@@ -527,19 +531,19 @@ pub fn dynamicModuleCallback(
|
|||||||
_ = import_attrs;
|
_ = import_attrs;
|
||||||
|
|
||||||
const self = fromC(c_context.?);
|
const self = fromC(c_context.?);
|
||||||
var local = js.Local{
|
const local = js.Local{
|
||||||
.ctx = self,
|
.ctx = self,
|
||||||
.handle = c_context.?,
|
.handle = c_context.?,
|
||||||
.call_arena = self.call_arena,
|
.call_arena = self.call_arena,
|
||||||
.isolate = self.isolate,
|
.isolate = self.isolate,
|
||||||
};
|
};
|
||||||
|
|
||||||
const resource = local.jsStringToZigZ(resource_name.?, .{}) catch |err| {
|
const resource = js.String.toSliceZ(.{ .local = &local, .handle = resource_name.? }) catch |err| {
|
||||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback1" });
|
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback1" });
|
||||||
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
||||||
};
|
};
|
||||||
|
|
||||||
const specifier = local.jsStringToZigZ(v8_specifier.?, .{}) catch |err| {
|
const specifier = js.String.toSliceZ(.{ .local = &local, .handle = v8_specifier.? }) catch |err| {
|
||||||
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback2" });
|
log.err(.app, "OOM", .{ .err = err, .src = "dynamicModuleCallback2" });
|
||||||
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
return @constCast((local.rejectPromise("Out of memory") catch return null).handle);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -254,8 +254,7 @@ fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) v
|
|||||||
|
|
||||||
const value =
|
const value =
|
||||||
if (v8.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value|
|
if (v8.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value|
|
||||||
// @HandleScope - no reason to create a js.Context here
|
js.Value.toStringSlice(.{ .local = &local, .handle = v8_value }) catch |err| @errorName(err)
|
||||||
local.valueHandleToString(v8_value, .{}) catch |err| @errorName(err)
|
|
||||||
else
|
else
|
||||||
"no value";
|
"no value";
|
||||||
|
|
||||||
|
|||||||
@@ -473,10 +473,10 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T {
|
|||||||
if (ptr.child == u8) {
|
if (ptr.child == u8) {
|
||||||
if (ptr.sentinel()) |s| {
|
if (ptr.sentinel()) |s| {
|
||||||
if (comptime s == 0) {
|
if (comptime s == 0) {
|
||||||
return self.valueToStringZ(js_val, .{});
|
return try js_val.toStringSliceZ();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return self.valueToString(js_val, .{});
|
return try js_val.toStringSlice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,10 +549,8 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T {
|
|||||||
},
|
},
|
||||||
.@"enum" => |e| {
|
.@"enum" => |e| {
|
||||||
if (@hasDecl(T, "js_enum_from_string")) {
|
if (@hasDecl(T, "js_enum_from_string")) {
|
||||||
if (!js_val.isString()) {
|
const js_str = js_val.isString() orelse return error.InvalidArgument;
|
||||||
return error.InvalidArgument;
|
return std.meta.stringToEnum(T, try js_str.toSlice()) orelse return error.InvalidArgument;
|
||||||
}
|
|
||||||
return std.meta.stringToEnum(T, try self.valueToString(js_val, .{})) orelse return error.InvalidArgument;
|
|
||||||
}
|
}
|
||||||
switch (@typeInfo(e.tag_type)) {
|
switch (@typeInfo(e.tag_type)) {
|
||||||
.int => return std.meta.intToEnum(T, try jsIntToZig(e.tag_type, js_val)),
|
.int => return std.meta.intToEnum(T, try jsIntToZig(e.tag_type, js_val)),
|
||||||
@@ -625,17 +623,12 @@ fn jsValueToStruct(self: *const Local, comptime T: type, js_val: js.Value) !?T {
|
|||||||
return try promise.persist();
|
return try promise.persist();
|
||||||
},
|
},
|
||||||
string.String => {
|
string.String => {
|
||||||
if (!js_val.isString()) {
|
const js_str = js_val.isString() orelse return null;
|
||||||
return null;
|
return try js_str.toSSO(false);
|
||||||
}
|
|
||||||
return try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.call_arena });
|
|
||||||
},
|
},
|
||||||
string.Global => {
|
string.Global => {
|
||||||
if (!js_val.isString()) {
|
const js_str = js_val.isString() orelse return null;
|
||||||
return null;
|
return try js_str.toSSO(true);
|
||||||
}
|
|
||||||
// Use arena for persistent strings
|
|
||||||
return .{ .str = try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.arena }) };
|
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
if (!js_val.isObject()) {
|
if (!js_val.isObject()) {
|
||||||
@@ -883,7 +876,7 @@ fn probeJsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ptr.child == u8) {
|
if (ptr.child == u8) {
|
||||||
if (js_val.isString()) {
|
if (v8.v8__Value__IsString(js_val.handle)) {
|
||||||
return .{ .ok = {} };
|
return .{ .ok = {} };
|
||||||
}
|
}
|
||||||
// anything can be coerced into a string
|
// anything can be coerced into a string
|
||||||
@@ -931,10 +924,11 @@ fn probeJsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !Pr
|
|||||||
if (js_arr.len() == arr.len) {
|
if (js_arr.len() == arr.len) {
|
||||||
return .{ .ok = {} };
|
return .{ .ok = {} };
|
||||||
}
|
}
|
||||||
} else if (js_val.isString() and arr.child == u8) {
|
} else if (arr.child == u8) {
|
||||||
const str = try js_val.toString(self.local);
|
if (js_val.isString()) |js_str| {
|
||||||
if (str.lenUtf8(self.isolate) == arr.len) {
|
if (js_str.lenUtf8(self.isolate) == arr.len) {
|
||||||
return .{ .ok = {} };
|
return .{ .ok = {} };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return .{ .invalid = {} };
|
return .{ .invalid = {} };
|
||||||
@@ -947,7 +941,7 @@ fn probeJsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !Pr
|
|||||||
.@"struct" => {
|
.@"struct" => {
|
||||||
// Handle string.String and string.Global specially
|
// Handle string.String and string.Global specially
|
||||||
if (T == string.String or T == string.Global) {
|
if (T == string.String or T == string.Global) {
|
||||||
if (js_val.isString()) {
|
if (v8.v8__Value__IsString(js_val.handle)) {
|
||||||
return .{ .ok = {} };
|
return .{ .ok = {} };
|
||||||
}
|
}
|
||||||
// Anything can be coerced to a string
|
// Anything can be coerced to a string
|
||||||
@@ -1080,14 +1074,15 @@ pub fn stackTrace(self: *const Local) !?[]const u8 {
|
|||||||
const frame_count = v8.v8__StackTrace__GetFrameCount(stack_trace_handle);
|
const frame_count = v8.v8__StackTrace__GetFrameCount(stack_trace_handle);
|
||||||
|
|
||||||
if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate.handle)) |script| {
|
if (v8.v8__StackTrace__CurrentScriptNameOrSourceURL__STATIC(isolate.handle)) |script| {
|
||||||
try writer.print("{s}<{s}>", .{ separator, try self.jsStringToZig(script, .{}) });
|
const stack = js.String{ .local = self, .handle = script };
|
||||||
|
try writer.print("{s}<{f}>", .{ separator, stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
for (0..@intCast(frame_count)) |i| {
|
for (0..@intCast(frame_count)) |i| {
|
||||||
const frame_handle = v8.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?;
|
const frame_handle = v8.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?;
|
||||||
if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| {
|
if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| {
|
||||||
const script = try self.jsStringToZig(name, .{});
|
const script = js.String{ .local = self, .handle = name };
|
||||||
try writer.print("{s}{s}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) });
|
try writer.print("{s}{f}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) });
|
||||||
} else {
|
} else {
|
||||||
try writer.print("{s}<anonymous>:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) });
|
try writer.print("{s}<anonymous>:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) });
|
||||||
}
|
}
|
||||||
@@ -1095,100 +1090,6 @@ pub fn stackTrace(self: *const Local) !?[]const u8 {
|
|||||||
return buf.items;
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
// == Stringifiers ==
|
|
||||||
const ToStringOpts = struct {
|
|
||||||
allocator: ?Allocator = null,
|
|
||||||
};
|
|
||||||
pub fn valueToString(self: *const Local, js_val: js.Value, opts: ToStringOpts) ![]u8 {
|
|
||||||
return self.valueHandleToString(js_val.handle, opts);
|
|
||||||
}
|
|
||||||
pub fn valueToStringZ(self: *const Local, js_val: js.Value, opts: ToStringOpts) ![:0]u8 {
|
|
||||||
return self.valueHandleToStringZ(js_val.handle, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn valueHandleToString(self: *const Local, js_val: *const v8.Value, opts: ToStringOpts) ![]u8 {
|
|
||||||
return self._valueToString(false, js_val, opts);
|
|
||||||
}
|
|
||||||
pub fn valueHandleToStringZ(self: *const Local, js_val: *const v8.Value, opts: ToStringOpts) ![:0]u8 {
|
|
||||||
return self._valueToString(true, js_val, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _valueToString(self: *const Local, comptime null_terminate: bool, value_handle: *const v8.Value, opts: ToStringOpts) !(if (null_terminate) [:0]u8 else []u8) {
|
|
||||||
var resolved_value_handle = value_handle;
|
|
||||||
if (v8.v8__Value__IsSymbol(value_handle)) {
|
|
||||||
const symbol_handle = v8.v8__Symbol__Description(@ptrCast(value_handle), self.isolate.handle).?;
|
|
||||||
resolved_value_handle = @ptrCast(symbol_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
const string_handle = v8.v8__Value__ToString(resolved_value_handle, self.handle) orelse {
|
|
||||||
return error.JsException;
|
|
||||||
};
|
|
||||||
|
|
||||||
return self._jsStringToZig(null_terminate, string_handle, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn jsStringToZig(self: *const Local, str: anytype, opts: ToStringOpts) ![]u8 {
|
|
||||||
return self._jsStringToZig(false, str, opts);
|
|
||||||
}
|
|
||||||
pub fn jsStringToZigZ(self: *const Local, str: anytype, opts: ToStringOpts) ![:0]u8 {
|
|
||||||
return self._jsStringToZig(true, str, opts);
|
|
||||||
}
|
|
||||||
fn _jsStringToZig(self: *const Local, comptime null_terminate: bool, str: anytype, opts: ToStringOpts) !(if (null_terminate) [:0]u8 else []u8) {
|
|
||||||
const handle = if (@TypeOf(str) == js.String) str.handle else str;
|
|
||||||
|
|
||||||
const len = v8.v8__String__Utf8Length(handle, self.isolate.handle);
|
|
||||||
const allocator = opts.allocator orelse self.call_arena;
|
|
||||||
const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(len), 0) else allocator.alloc(u8, @intCast(len)));
|
|
||||||
const n = v8.v8__String__WriteUtf8(handle, self.isolate.handle, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
|
||||||
std.debug.assert(n == len);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert JS string to string.String with SSO
|
|
||||||
pub fn valueToStringSSO(self: *const Local, js_val: js.Value, opts: ToStringOpts) !string.String {
|
|
||||||
const string_handle = v8.v8__Value__ToString(js_val.handle, self.handle) orelse {
|
|
||||||
return error.JsException;
|
|
||||||
};
|
|
||||||
return self.jsStringToStringSSO(string_handle, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn jsStringToStringSSO(self: *const Local, str: anytype, opts: ToStringOpts) !string.String {
|
|
||||||
const handle = if (@TypeOf(str) == js.String) str.handle else str;
|
|
||||||
const len: usize = @intCast(v8.v8__String__Utf8Length(handle, self.isolate.handle));
|
|
||||||
|
|
||||||
if (len <= 12) {
|
|
||||||
var content: [12]u8 = undefined;
|
|
||||||
const n = v8.v8__String__WriteUtf8(handle, self.isolate.handle, &content[0], content.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
|
||||||
if (comptime IS_DEBUG) {
|
|
||||||
std.debug.assert(n == len);
|
|
||||||
}
|
|
||||||
// Weird that we do this _after_, but we have to..I've seen weird issues
|
|
||||||
// in ReleaseMode where v8 won't write to content if it starts off zero
|
|
||||||
// initiated
|
|
||||||
@memset(content[len..], 0);
|
|
||||||
return .{ .len = @intCast(len), .payload = .{ .content = content } };
|
|
||||||
}
|
|
||||||
|
|
||||||
const allocator = opts.allocator orelse self.call_arena;
|
|
||||||
const buf = try allocator.alloc(u8, len);
|
|
||||||
const n = v8.v8__String__WriteUtf8(handle, self.isolate.handle, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
|
||||||
if (comptime IS_DEBUG) {
|
|
||||||
std.debug.assert(n == len);
|
|
||||||
}
|
|
||||||
|
|
||||||
var prefix: [4]u8 = @splat(0);
|
|
||||||
@memcpy(&prefix, buf[0..4]);
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.len = @intCast(len),
|
|
||||||
.payload = .{ .heap = .{
|
|
||||||
.prefix = prefix,
|
|
||||||
.ptr = buf.ptr,
|
|
||||||
} },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// == Promise Helpers ==
|
// == Promise Helpers ==
|
||||||
pub fn rejectPromise(self: *const Local, value: anytype) !js.Promise {
|
pub fn rejectPromise(self: *const Local, value: anytype) !js.Promise {
|
||||||
var resolver = js.PromiseResolver.init(self);
|
var resolver = js.PromiseResolver.init(self);
|
||||||
@@ -1233,18 +1134,16 @@ fn _debugValue(self: *const Local, js_val: js.Value, seen: *std.AutoHashMapUnman
|
|||||||
|
|
||||||
if (js_val.isSymbol()) {
|
if (js_val.isSymbol()) {
|
||||||
const symbol_handle = v8.v8__Symbol__Description(@ptrCast(js_val.handle), self.isolate.handle).?;
|
const symbol_handle = v8.v8__Symbol__Description(@ptrCast(js_val.handle), self.isolate.handle).?;
|
||||||
const js_sym_str = try self.valueToString(.{ .local = self, .handle = symbol_handle }, .{});
|
return writer.print("{f} (symbol)", .{js.String{ .local = self, .handle = @ptrCast(symbol_handle) }});
|
||||||
return writer.print("{s} (symbol)", .{js_sym_str});
|
|
||||||
}
|
}
|
||||||
const js_type = try self.jsStringToZig(js_val.typeOf(), .{});
|
const js_val_str = try js_val.toStringSlice();
|
||||||
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]);
|
||||||
try writer.writeAll(" ... (truncated)");
|
try writer.writeAll(" ... (truncated)");
|
||||||
} else {
|
} else {
|
||||||
try writer.writeAll(js_val_str);
|
try writer.writeAll(js_val_str);
|
||||||
}
|
}
|
||||||
return writer.print(" ({s})", .{js_type});
|
return writer.print(" ({f})", .{js_val.typeOf()});
|
||||||
}
|
}
|
||||||
|
|
||||||
const js_obj = js_val.toObject();
|
const js_obj = js_val.toObject();
|
||||||
@@ -1266,7 +1165,7 @@ fn _debugValue(self: *const Local, js_val: js.Value, seen: *std.AutoHashMapUnman
|
|||||||
}
|
}
|
||||||
const own_len = js_obj.getOwnPropertyNames().len();
|
const own_len = js_obj.getOwnPropertyNames().len();
|
||||||
if (own_len == 0) {
|
if (own_len == 0) {
|
||||||
const js_val_str = try self.valueToString(js_val, .{});
|
const js_val_str = try js_val.toStringSlice();
|
||||||
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]);
|
||||||
return writer.writeAll(" ... (truncated)");
|
return writer.writeAll(" ... (truncated)");
|
||||||
@@ -1281,10 +1180,11 @@ fn _debugValue(self: *const Local, js_val: js.Value, seen: *std.AutoHashMapUnman
|
|||||||
try writer.writeByte('\n');
|
try writer.writeByte('\n');
|
||||||
}
|
}
|
||||||
const field_name = try names_arr.get(@intCast(i));
|
const field_name = try names_arr.get(@intCast(i));
|
||||||
const name = try self.valueToString(field_name, .{});
|
const name = try field_name.toStringSlice();
|
||||||
try writer.splatByteAll(' ', depth);
|
try writer.splatByteAll(' ', depth);
|
||||||
try writer.writeAll(name);
|
try writer.writeAll(name);
|
||||||
try writer.writeAll(": ");
|
try writer.writeAll(": ");
|
||||||
|
|
||||||
const field_val = try js_obj.get(name);
|
const field_val = try js_obj.get(name);
|
||||||
try self._debugValue(field_val, seen, depth + 1, writer);
|
try self._debugValue(field_val, seen, depth + 1, writer);
|
||||||
if (i != len - 1) {
|
if (i != len - 1) {
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ const Requests = struct {
|
|||||||
const Request = struct {
|
const Request = struct {
|
||||||
handle: *const v8.ModuleRequest,
|
handle: *const v8.ModuleRequest,
|
||||||
|
|
||||||
pub fn specifier(self: Request) *const v8.String {
|
pub fn specifier(self: Request, local: *const js.Local) js.String {
|
||||||
return v8.v8__ModuleRequest__GetSpecifier(self.handle).?;
|
return .{ .local = local, .handle = v8.v8__ModuleRequest__GetSpecifier(self.handle).? };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -201,8 +201,8 @@ pub const NameIterator = struct {
|
|||||||
}
|
}
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
|
|
||||||
const js_val_handle = v8.v8__Object__GetIndex(@ptrCast(self.handle), self.local.handle, idx) orelse return error.JsException;
|
const local = self.local;
|
||||||
const js_val = js.Value{ .local = self.local, .handle = js_val_handle };
|
const js_val_handle = v8.v8__Object__GetIndex(@ptrCast(self.handle), local.handle, idx) orelse return error.JsException;
|
||||||
return try self.local.valueToString(js_val, .{});
|
return try js.Value.toStringSlice(.{ .local = local, .handle = js_val_handle });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,8 +18,10 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const js = @import("js.zig");
|
const js = @import("js.zig");
|
||||||
|
const SSO = @import("../../string.zig").String;
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
const IS_DEBUG = @import("builtin").mode == .Debug;
|
||||||
|
|
||||||
const v8 = js.v8;
|
const v8 = js.v8;
|
||||||
|
|
||||||
@@ -28,26 +30,82 @@ const String = @This();
|
|||||||
local: *const js.Local,
|
local: *const js.Local,
|
||||||
handle: *const v8.String,
|
handle: *const v8.String,
|
||||||
|
|
||||||
pub const ToZigOpts = struct {
|
pub fn toSlice(self: String) ![]u8 {
|
||||||
allocator: ?Allocator = null,
|
return self._toSlice(false, self.local.call_arena);
|
||||||
};
|
|
||||||
|
|
||||||
pub fn toZig(self: String, opts: ToZigOpts) ![]u8 {
|
|
||||||
return self._toZig(false, opts);
|
|
||||||
}
|
}
|
||||||
|
pub fn toSliceZ(self: String) ![:0]u8 {
|
||||||
pub fn toZigZ(self: String, opts: ToZigOpts) ![:0]u8 {
|
return self._toSlice(true, self.local.call_arena);
|
||||||
return self._toZig(true, opts);
|
|
||||||
}
|
}
|
||||||
|
pub fn toSliceWithAlloc(self: String, allocator: Allocator) ![]u8 {
|
||||||
|
return self._toSlice(false, allocator);
|
||||||
|
}
|
||||||
|
fn _toSlice(self: String, comptime null_terminate: bool, allocator: Allocator) !(if (null_terminate) [:0]u8 else []u8) {
|
||||||
|
const local = self.local;
|
||||||
|
const handle = self.handle;
|
||||||
|
const isolate = local.isolate.handle;
|
||||||
|
|
||||||
fn _toZig(self: String, comptime null_terminate: bool, opts: ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) {
|
const len = v8.v8__String__Utf8Length(handle, isolate);
|
||||||
const isolate = self.local.isolate.handle;
|
const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(len), 0) else allocator.alloc(u8, @intCast(len)));
|
||||||
const allocator = opts.allocator orelse self.local.ctx.call_arena;
|
const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||||
const len: u32 = @intCast(v8.v8__String__Utf8Length(self.handle, isolate));
|
if (comptime IS_DEBUG) {
|
||||||
const buf = if (null_terminate) try allocator.allocSentinel(u8, len, 0) else try allocator.alloc(u8, len);
|
std.debug.assert(n == len);
|
||||||
|
}
|
||||||
|
|
||||||
const options = v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8;
|
|
||||||
const n = v8.v8__String__WriteUtf8(self.handle, isolate, buf.ptr, buf.len, options);
|
|
||||||
std.debug.assert(n == len);
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn toSSO(self: String, comptime global: bool) !(if (global) SSO.Global else SSO) {
|
||||||
|
if (comptime global) {
|
||||||
|
return .{ .str = try self.toSSOWithAlloc(self.local.ctx.arena) };
|
||||||
|
}
|
||||||
|
return self.toSSOWithAlloc(self.local.call_arena);
|
||||||
|
}
|
||||||
|
pub fn toSSOWithAlloc(self: String, allocator: Allocator) !SSO {
|
||||||
|
const handle = self.handle;
|
||||||
|
const isolate = self.local.isolate.handle;
|
||||||
|
|
||||||
|
const len: usize = @intCast(v8.v8__String__Utf8Length(handle, isolate));
|
||||||
|
|
||||||
|
if (len <= 12) {
|
||||||
|
var content: [12]u8 = undefined;
|
||||||
|
const n = v8.v8__String__WriteUtf8(handle, isolate, &content[0], content.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||||
|
if (comptime IS_DEBUG) {
|
||||||
|
std.debug.assert(n == len);
|
||||||
|
}
|
||||||
|
// Weird that we do this _after_, but we have to..I've seen weird issues
|
||||||
|
// in ReleaseMode where v8 won't write to content if it starts off zero
|
||||||
|
// initiated
|
||||||
|
@memset(content[len..], 0);
|
||||||
|
return .{ .len = @intCast(len), .payload = .{ .content = content } };
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = try allocator.alloc(u8, len);
|
||||||
|
const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||||
|
if (comptime IS_DEBUG) {
|
||||||
|
std.debug.assert(n == len);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix: [4]u8 = @splat(0);
|
||||||
|
@memcpy(&prefix, buf[0..4]);
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.len = @intCast(len),
|
||||||
|
.payload = .{ .heap = .{
|
||||||
|
.prefix = prefix,
|
||||||
|
.ptr = buf.ptr,
|
||||||
|
} },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(self: String, writer: *std.Io.Writer) !void {
|
||||||
|
const local = self.local;
|
||||||
|
const handle = self.handle;
|
||||||
|
const isolate = local.isolate.handle;
|
||||||
|
|
||||||
|
var small: [1024]u8 = undefined;
|
||||||
|
const len = v8.v8__String__Utf8Length(handle, isolate);
|
||||||
|
var buf = if (len < 1024) &small else local.call_arena.alloc(u8, @intCast(len)) catch return error.WriteFailed;
|
||||||
|
|
||||||
|
const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8);
|
||||||
|
return writer.writeAll(buf[0..n]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,12 +46,12 @@ pub fn caught(self: TryCatch, allocator: Allocator) ?Caught {
|
|||||||
|
|
||||||
const exception: ?[]const u8 = blk: {
|
const exception: ?[]const u8 = blk: {
|
||||||
const handle = v8.v8__TryCatch__Exception(&self.handle) orelse break :blk null;
|
const handle = v8.v8__TryCatch__Exception(&self.handle) orelse break :blk null;
|
||||||
break :blk l.valueHandleToString(@ptrCast(handle), .{ .allocator = allocator }) catch |err| @errorName(err);
|
break :blk js.String.toSliceWithAlloc(.{ .local = l, .handle = @ptrCast(handle) }, allocator) catch |err| @errorName(err);
|
||||||
};
|
};
|
||||||
|
|
||||||
const stack: ?[]const u8 = blk: {
|
const stack: ?[]const u8 = blk: {
|
||||||
const handle = v8.v8__TryCatch__StackTrace(&self.handle, l.handle) orelse break :blk null;
|
const handle = v8.v8__TryCatch__StackTrace(&self.handle, l.handle) orelse break :blk null;
|
||||||
break :blk l.valueHandleToString(@ptrCast(handle), .{ .allocator = allocator }) catch |err| @errorName(err);
|
break :blk js.String.toSliceWithAlloc(.{ .local = l, .handle = @ptrCast(handle) }, allocator) catch |err| @errorName(err);
|
||||||
};
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const js = @import("js.zig");
|
const js = @import("js.zig");
|
||||||
|
const SSO = @import("../../string.zig").String;
|
||||||
|
|
||||||
const v8 = js.v8;
|
const v8 = js.v8;
|
||||||
|
|
||||||
@@ -34,8 +35,12 @@ pub fn isObject(self: Value) bool {
|
|||||||
return v8.v8__Value__IsObject(self.handle);
|
return v8.v8__Value__IsObject(self.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isString(self: Value) bool {
|
pub fn isString(self: Value) ?js.String {
|
||||||
return v8.v8__Value__IsString(self.handle);
|
const handle = self.handle;
|
||||||
|
if (!v8.v8__Value__IsString(handle)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return .{ .local = self.local, .handle = @ptrCast(handle) };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn isArray(self: Value) bool {
|
pub fn isArray(self: Value) bool {
|
||||||
@@ -204,35 +209,40 @@ pub fn toPromise(self: Value) js.Promise {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toString(self: Value, opts: js.String.ToZigOpts) ![]u8 {
|
pub fn toString(self: Value) !js.String {
|
||||||
return self._toString(false, opts);
|
const l = self.local;
|
||||||
|
const value_handle: *const v8.Value = blk: {
|
||||||
|
if (self.isSymbol()) {
|
||||||
|
break :blk @ptrCast(v8.v8__Symbol__Description(@ptrCast(self.handle), l.isolate.handle).?);
|
||||||
|
}
|
||||||
|
break :blk self.handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
const str_handle = v8.v8__Value__ToString(value_handle, l.handle) orelse return error.JsException;
|
||||||
|
return .{ .local = self.local, .handle = str_handle };
|
||||||
}
|
}
|
||||||
pub fn toStringZ(self: Value, opts: js.String.ToZigOpts) ![:0]u8 {
|
|
||||||
return self._toString(true, opts);
|
pub fn toSSO(self: Value, comptime global: bool) !(if (global) SSO.Global else SSO) {
|
||||||
|
return (try self.toString()).toSSO(global);
|
||||||
|
}
|
||||||
|
pub fn toSSOWithAlloc(self: Value, allocator: Allocator) !SSO {
|
||||||
|
return (try self.toString()).toSSOWithAlloc(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toStringSlice(self: Value) ![]u8 {
|
||||||
|
return (try self.toString()).toSlice();
|
||||||
|
}
|
||||||
|
pub fn toStringSliceZ(self: Value) ![:0]u8 {
|
||||||
|
return (try self.toString()).toSliceZ();
|
||||||
|
}
|
||||||
|
pub fn toStringSliceWithAlloc(self: Value, allocator: Allocator) ![]u8 {
|
||||||
|
return (try self.toString()).toSliceWithAlloc(allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn toJson(self: Value, allocator: Allocator) ![]u8 {
|
pub fn toJson(self: Value, allocator: Allocator) ![]u8 {
|
||||||
const json_str_handle = v8.v8__JSON__Stringify(self.local.handle, self.handle, null) orelse return error.JsException;
|
const local = self.local;
|
||||||
return self.local.jsStringToZig(json_str_handle, .{ .allocator = allocator });
|
const str_handle = v8.v8__JSON__Stringify(local.handle, self.handle, null) orelse return error.JsException;
|
||||||
}
|
return js.String.toSliceWithAlloc(.{ .local = local, .handle = str_handle }, allocator);
|
||||||
|
|
||||||
fn _toString(self: Value, comptime null_terminate: bool, opts: js.String.ToZigOpts) !(if (null_terminate) [:0]u8 else []u8) {
|
|
||||||
const l = self.local;
|
|
||||||
|
|
||||||
if (self.isSymbol()) {
|
|
||||||
const sym_handle = v8.v8__Symbol__Description(@ptrCast(self.handle), l.isolate.handle).?;
|
|
||||||
return _toString(.{ .handle = @ptrCast(sym_handle), .local = l }, null_terminate, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
const str_handle = v8.v8__Value__ToString(self.handle, l.handle) orelse {
|
|
||||||
return error.JsException;
|
|
||||||
};
|
|
||||||
|
|
||||||
const str = js.String{ .local = l, .handle = str_handle };
|
|
||||||
if (comptime null_terminate) {
|
|
||||||
return js.String.toZigZ(str, opts);
|
|
||||||
}
|
|
||||||
return js.String.toZig(str, opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn persist(self: Value) !Global {
|
pub fn persist(self: Value) !Global {
|
||||||
@@ -296,8 +306,8 @@ pub fn format(self: Value, writer: *std.Io.Writer) !void {
|
|||||||
if (comptime IS_DEBUG) {
|
if (comptime IS_DEBUG) {
|
||||||
return self.local.debugValue(self, writer);
|
return self.local.debugValue(self, writer);
|
||||||
}
|
}
|
||||||
const str = self.toString(.{}) catch return error.WriteFailed;
|
const js_str = self.toString() catch return error.WriteFailed;
|
||||||
return writer.writeAll(str);
|
return js_str.format(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const Temp = G(0);
|
pub const Temp = G(0);
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8.Prope
|
|||||||
hs.init(local.isolate);
|
hs.init(local.isolate);
|
||||||
defer hs.deinit();
|
defer hs.deinit();
|
||||||
|
|
||||||
const property: []const u8 = local.valueHandleToString(@ptrCast(c_name.?), .{}) catch {
|
const property: []const u8 = js.String.toSlice(.{ .local = local, .handle = @ptrCast(c_name.?) }) catch {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ pub fn throwIfAborted(self: *const AbortSignal, page: *Page) !ThrowIfAborted {
|
|||||||
if (self._aborted) {
|
if (self._aborted) {
|
||||||
const exception = switch (self._reason) {
|
const exception = switch (self._reason) {
|
||||||
.string => |str| local.throw(str),
|
.string => |str| local.throw(str),
|
||||||
.js_val => |js_val| local.throw(try local.toLocal(js_val).toString(.{ .allocator = page.call_arena })),
|
.js_val => |js_val| local.throw(try local.toLocal(js_val).toStringSlice()),
|
||||||
.undefined => local.throw("AbortError"),
|
.undefined => local.throw("AbortError"),
|
||||||
};
|
};
|
||||||
return .{ .exception = exception };
|
return .{ .exception = exception };
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ const ValueWriter = struct {
|
|||||||
var buf: [32]u8 = undefined;
|
var buf: [32]u8 = undefined;
|
||||||
for (self.values, 0..) |value, i| {
|
for (self.values, 0..) |value, i| {
|
||||||
const name = try std.fmt.bufPrint(&buf, "param.{d}", .{i});
|
const name = try std.fmt.bufPrint(&buf, "param.{d}", .{i});
|
||||||
try writer.write(name, try value.toString(.{}));
|
try writer.write(name, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,9 +73,8 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu
|
|||||||
var js_arr = observed_attrs.toArray();
|
var js_arr = observed_attrs.toArray();
|
||||||
for (0..js_arr.len()) |i| {
|
for (0..js_arr.len()) |i| {
|
||||||
const attr_val = js_arr.get(@intCast(i)) catch continue;
|
const attr_val = js_arr.get(@intCast(i)) catch continue;
|
||||||
const attr_name = attr_val.toString(.{ .allocator = page.arena }) catch continue;
|
const attr_name = attr_val.toStringSliceWithAlloc(page.arena) catch continue;
|
||||||
const owned_attr = page.dupeString(attr_name) catch continue;
|
definition.observed_attributes.put(page.arena, attr_name, {}) catch continue;
|
||||||
definition.observed_attributes.put(page.arena, owned_attr, {}) catch continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,12 +68,11 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?N
|
|||||||
|
|
||||||
while (try it.next()) |name| {
|
while (try it.next()) |name| {
|
||||||
const js_value = try js_obj.get(name);
|
const js_value = try js_obj.get(name);
|
||||||
const value = try js_value.toString(.{});
|
|
||||||
const normalized = if (comptime normalizer) |n| n(name, page) else name;
|
const normalized = if (comptime normalizer) |n| n(name, page) else name;
|
||||||
|
|
||||||
list._entries.appendAssumeCapacity(.{
|
list._entries.appendAssumeCapacity(.{
|
||||||
.name = try String.init(arena, normalized, .{}),
|
.name = try String.init(arena, normalized, .{}),
|
||||||
.value = try String.init(arena, value, .{}),
|
.value = try js_value.toSSOWithAlloc(arena),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ pub fn cancelIdleCallback(self: *Window, id: u32) void {
|
|||||||
pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
pub fn reportError(self: *Window, err: js.Value, page: *Page) !void {
|
||||||
const error_event = try ErrorEvent.initTrusted("error", .{
|
const error_event = try ErrorEvent.initTrusted("error", .{
|
||||||
.@"error" = try err.persist(),
|
.@"error" = try err.persist(),
|
||||||
.message = err.toString(.{}) catch "Unknown error",
|
.message = err.toStringSlice() catch "Unknown error",
|
||||||
.bubbles = false,
|
.bubbles = false,
|
||||||
.cancelable = true,
|
.cancelable = true,
|
||||||
}, page);
|
}, page);
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*URLSearchParams {
|
|||||||
if (js_val.isObject()) {
|
if (js_val.isObject()) {
|
||||||
break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, page);
|
break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, page);
|
||||||
}
|
}
|
||||||
if (js_val.isString()) {
|
if (js_val.isString()) |js_str| {
|
||||||
break :blk try paramsFromString(arena, try js_val.toString(.{ .allocator = arena }), &page.buf);
|
break :blk try paramsFromString(arena, try js_str.toSliceWithAlloc(arena), &page.buf);
|
||||||
}
|
}
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ fn run(
|
|||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
|
|
||||||
return value.toString(.{ .allocator = arena });
|
return value.toStringSliceWithAlloc(arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Writer = struct {
|
const Writer = struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user