diff --git a/src/browser/js/Caller.zig b/src/browser/js/Caller.zig index 7d0b5186..9f48d345 100644 --- a/src/browser/js/Caller.zig +++ b/src/browser/js/Caller.zig @@ -312,14 +312,14 @@ fn isInErrorSet(err: anyerror, comptime T: type) bool { } 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) { - return self.local.jsStringToStringSSO(v8_string, .{}); + return js.String.toSSO(.{ .local = &self.local, .handle = handle }, false); } 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 { diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index e4f90d6f..119dbf23 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -359,9 +359,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 // 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", .{ + .message = message, .specifier = owned_url, - .message = mod.getException().toString(.{}) catch "???", }); return error.EvaluationError; }; @@ -459,11 +463,11 @@ fn postCompileModule(self: *Context, mod: js.Module, url: [:0]const u8, local: * const request_len = requests.len(); const script_manager = self.script_manager.?; 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( self.call_arena, url, - specifier, + try specifier.toSliceZ(), ); const nested_gop = try self.module_cache.getOrPut(self.arena, normalized_specifier); if (!nested_gop.found_existing) { @@ -496,14 +500,14 @@ fn resolveModuleCallback( _ = import_attributes; const self = fromC(c_context.?); - var local = js.Local{ + const local = js.Local{ .ctx = self, .handle = c_context.?, .isolate = self.isolate, .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 }); return null; }; @@ -529,19 +533,19 @@ pub fn dynamicModuleCallback( _ = import_attrs; const self = fromC(c_context.?); - var local = js.Local{ + const local = js.Local{ .ctx = self, .handle = c_context.?, .call_arena = self.call_arena, .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" }); 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" }); return @constCast((local.rejectPromise("Out of memory") catch return null).handle); }; diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index b181048c..e0e2d2be 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -335,8 +335,7 @@ fn promiseRejectCallback(message_handle: v8.PromiseRejectMessage) callconv(.c) v const value = if (v8.v8__PromiseRejectMessage__GetValue(&message_handle)) |v8_value| - // @HandleScope - no reason to create a js.Context here - local.valueHandleToString(v8_value, .{}) catch |err| @errorName(err) + js.Value.toStringSlice(.{ .local = &local, .handle = v8_value }) catch |err| @errorName(err) else "no value"; diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index c47a71a7..2d20e7ab 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -473,10 +473,10 @@ pub fn jsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !T { if (ptr.child == u8) { if (ptr.sentinel()) |s| { if (comptime s == 0) { - return self.valueToStringZ(js_val, .{}); + return try js_val.toStringSliceZ(); } } 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| { if (@hasDecl(T, "js_enum_from_string")) { - if (!js_val.isString()) { - return error.InvalidArgument; - } - return std.meta.stringToEnum(T, try self.valueToString(js_val, .{})) orelse return error.InvalidArgument; + const js_str = js_val.isString() orelse return error.InvalidArgument; + return std.meta.stringToEnum(T, try js_str.toSlice()) orelse return error.InvalidArgument; } switch (@typeInfo(e.tag_type)) { .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(); }, string.String => { - if (!js_val.isString()) { - return null; - } - return try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.call_arena }); + const js_str = js_val.isString() orelse return null; + return try js_str.toSSO(false); }, string.Global => { - if (!js_val.isString()) { - return null; - } - // Use arena for persistent strings - return .{ .str = try self.valueToStringSSO(js_val, .{ .allocator = self.ctx.arena }) }; + const js_str = js_val.isString() orelse return null; + return try js_str.toSSO(true); }, else => { 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 (js_val.isString()) { + if (v8.v8__Value__IsString(js_val.handle)) { return .{ .ok = {} }; } // 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) { return .{ .ok = {} }; } - } else if (js_val.isString() and arr.child == u8) { - const str = try js_val.toString(self.local); - if (str.lenUtf8(self.isolate) == arr.len) { - return .{ .ok = {} }; + } else if (arr.child == u8) { + if (js_val.isString()) |js_str| { + if (js_str.lenUtf8(self.isolate) == arr.len) { + return .{ .ok = {} }; + } } } return .{ .invalid = {} }; @@ -947,7 +941,7 @@ fn probeJsValueToZig(self: *const Local, comptime T: type, js_val: js.Value) !Pr .@"struct" => { // Handle string.String and string.Global specially if (T == string.String or T == string.Global) { - if (js_val.isString()) { + if (v8.v8__Value__IsString(js_val.handle)) { return .{ .ok = {} }; } // 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); 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| { const frame_handle = v8.v8__StackTrace__GetFrame(stack_trace_handle, isolate.handle, @intCast(i)).?; if (v8.v8__StackFrame__GetFunctionName(frame_handle)) |name| { - const script = try self.jsStringToZig(name, .{}); - try writer.print("{s}{s}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) }); + const script = js.String{ .local = self, .handle = name }; + try writer.print("{s}{f}:{d}", .{ separator, script, v8.v8__StackFrame__GetLineNumber(frame_handle) }); } else { try writer.print("{s}:{d}", .{ separator, v8.v8__StackFrame__GetLineNumber(frame_handle) }); } @@ -1095,100 +1090,6 @@ pub fn stackTrace(self: *const Local) !?[]const u8 { 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 == pub fn rejectPromise(self: *const Local, value: anytype) !js.Promise { 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()) { 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("{s} (symbol)", .{js_sym_str}); + return writer.print("{f} (symbol)", .{js.String{ .local = self, .handle = @ptrCast(symbol_handle) }}); } - const js_type = try self.jsStringToZig(js_val.typeOf(), .{}); - const js_val_str = try self.valueToString(js_val, .{}); + const js_val_str = try js_val.toStringSlice(); if (js_val_str.len > 2000) { try writer.writeAll(js_val_str[0..2000]); try writer.writeAll(" ... (truncated)"); } else { 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(); @@ -1266,7 +1165,7 @@ fn _debugValue(self: *const Local, js_val: js.Value, seen: *std.AutoHashMapUnman } const own_len = js_obj.getOwnPropertyNames().len(); 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) { try writer.writeAll(js_val_str[0..2000]); return writer.writeAll(" ... (truncated)"); @@ -1281,10 +1180,11 @@ fn _debugValue(self: *const Local, js_val: js.Value, seen: *std.AutoHashMapUnman try writer.writeByte('\n'); } 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.writeAll(name); try writer.writeAll(": "); + const field_val = try js_obj.get(name); try self._debugValue(field_val, seen, depth + 1, writer); if (i != len - 1) { diff --git a/src/browser/js/Module.zig b/src/browser/js/Module.zig index c77c46fa..1e290d2d 100644 --- a/src/browser/js/Module.zig +++ b/src/browser/js/Module.zig @@ -131,7 +131,7 @@ const Requests = struct { const Request = struct { handle: *const v8.ModuleRequest, - pub fn specifier(self: Request) *const v8.String { - return v8.v8__ModuleRequest__GetSpecifier(self.handle).?; + pub fn specifier(self: Request, local: *const js.Local) js.String { + return .{ .local = local, .handle = v8.v8__ModuleRequest__GetSpecifier(self.handle).? }; } }; diff --git a/src/browser/js/Object.zig b/src/browser/js/Object.zig index 2cd7d243..4035eee7 100644 --- a/src/browser/js/Object.zig +++ b/src/browser/js/Object.zig @@ -197,8 +197,8 @@ pub const NameIterator = struct { } self.idx += 1; - const js_val_handle = v8.v8__Object__GetIndex(@ptrCast(self.handle), self.local.handle, idx) orelse return error.JsException; - const js_val = js.Value{ .local = self.local, .handle = js_val_handle }; - return try self.local.valueToString(js_val, .{}); + const local = self.local; + const js_val_handle = v8.v8__Object__GetIndex(@ptrCast(self.handle), local.handle, idx) orelse return error.JsException; + return try js.Value.toStringSlice(.{ .local = local, .handle = js_val_handle }); } }; diff --git a/src/browser/js/String.zig b/src/browser/js/String.zig index 3d0ad6ad..04eda642 100644 --- a/src/browser/js/String.zig +++ b/src/browser/js/String.zig @@ -18,8 +18,10 @@ const std = @import("std"); const js = @import("js.zig"); +const SSO = @import("../../string.zig").String; const Allocator = std.mem.Allocator; +const IS_DEBUG = @import("builtin").mode == .Debug; const v8 = js.v8; @@ -28,26 +30,82 @@ const String = @This(); local: *const js.Local, handle: *const v8.String, -pub const ToZigOpts = struct { - allocator: ?Allocator = null, -}; - -pub fn toZig(self: String, opts: ToZigOpts) ![]u8 { - return self._toZig(false, opts); +pub fn toSlice(self: String) ![]u8 { + return self._toSlice(false, self.local.call_arena); } - -pub fn toZigZ(self: String, opts: ToZigOpts) ![:0]u8 { - return self._toZig(true, opts); +pub fn toSliceZ(self: String) ![:0]u8 { + return self._toSlice(true, self.local.call_arena); } +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 isolate = self.local.isolate.handle; - const allocator = opts.allocator orelse self.local.ctx.call_arena; - const len: u32 = @intCast(v8.v8__String__Utf8Length(self.handle, isolate)); - const buf = if (null_terminate) try allocator.allocSentinel(u8, len, 0) else try allocator.alloc(u8, len); + const len = v8.v8__String__Utf8Length(handle, isolate); + 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, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); + if (comptime IS_DEBUG) { + 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; } + +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]); +} diff --git a/src/browser/js/TryCatch.zig b/src/browser/js/TryCatch.zig index 8554e312..3b2e24f2 100644 --- a/src/browser/js/TryCatch.zig +++ b/src/browser/js/TryCatch.zig @@ -46,12 +46,12 @@ pub fn caught(self: TryCatch, allocator: Allocator) ?Caught { const exception: ?[]const u8 = blk: { 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 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 .{ diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index d76313d5..7963ae7c 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -18,6 +18,7 @@ const std = @import("std"); const js = @import("js.zig"); +const SSO = @import("../../string.zig").String; const v8 = js.v8; @@ -34,8 +35,12 @@ pub fn isObject(self: Value) bool { return v8.v8__Value__IsObject(self.handle); } -pub fn isString(self: Value) bool { - return v8.v8__Value__IsString(self.handle); +pub fn isString(self: Value) ?js.String { + 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 { @@ -204,35 +209,40 @@ pub fn toPromise(self: Value) js.Promise { }; } -pub fn toString(self: Value, opts: js.String.ToZigOpts) ![]u8 { - return self._toString(false, opts); +pub fn toString(self: Value) !js.String { + 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 { - const json_str_handle = v8.v8__JSON__Stringify(self.local.handle, self.handle, null) orelse return error.JsException; - return self.local.jsStringToZig(json_str_handle, .{ .allocator = 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); + const local = self.local; + 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); } pub fn persist(self: Value) !Global { @@ -296,8 +306,8 @@ pub fn format(self: Value, writer: *std.Io.Writer) !void { if (comptime IS_DEBUG) { return self.local.debugValue(self, writer); } - const str = self.toString(.{}) catch return error.WriteFailed; - return writer.writeAll(str); + const js_str = self.toString() catch return error.WriteFailed; + return js_str.format(writer); } pub const Temp = G(0); diff --git a/src/browser/js/bridge.zig b/src/browser/js/bridge.zig index 9860d98f..a5ce1fae 100644 --- a/src/browser/js/bridge.zig +++ b/src/browser/js/bridge.zig @@ -422,7 +422,7 @@ pub fn unknownPropertyCallback(c_name: ?*const v8.Name, handle: ?*const v8.Prope hs.init(local.isolate); 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; }; diff --git a/src/browser/webapi/AbortSignal.zig b/src/browser/webapi/AbortSignal.zig index 8db60a70..0bb93886 100644 --- a/src/browser/webapi/AbortSignal.zig +++ b/src/browser/webapi/AbortSignal.zig @@ -116,7 +116,7 @@ pub fn throwIfAborted(self: *const AbortSignal, page: *Page) !ThrowIfAborted { if (self._aborted) { const exception = switch (self._reason) { .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"), }; return .{ .exception = exception }; diff --git a/src/browser/webapi/Console.zig b/src/browser/webapi/Console.zig index ab1fc12f..3318dc2c 100644 --- a/src/browser/webapi/Console.zig +++ b/src/browser/webapi/Console.zig @@ -146,7 +146,7 @@ const ValueWriter = struct { var buf: [32]u8 = undefined; for (self.values, 0..) |value, i| { const name = try std.fmt.bufPrint(&buf, "param.{d}", .{i}); - try writer.write(name, try value.toString(.{})); + try writer.write(name, value); } } diff --git a/src/browser/webapi/CustomElementRegistry.zig b/src/browser/webapi/CustomElementRegistry.zig index 461e7c6a..077d229b 100644 --- a/src/browser/webapi/CustomElementRegistry.zig +++ b/src/browser/webapi/CustomElementRegistry.zig @@ -73,9 +73,8 @@ pub fn define(self: *CustomElementRegistry, name: []const u8, constructor: js.Fu var js_arr = observed_attrs.toArray(); for (0..js_arr.len()) |i| { const attr_val = js_arr.get(@intCast(i)) catch continue; - const attr_name = attr_val.toString(.{ .allocator = page.arena }) catch continue; - const owned_attr = page.dupeString(attr_name) catch continue; - definition.observed_attributes.put(page.arena, owned_attr, {}) catch continue; + const attr_name = attr_val.toStringSliceWithAlloc(page.arena) catch continue; + definition.observed_attributes.put(page.arena, attr_name, {}) catch continue; } } } diff --git a/src/browser/webapi/KeyValueList.zig b/src/browser/webapi/KeyValueList.zig index 27ce6f7b..e4c55c49 100644 --- a/src/browser/webapi/KeyValueList.zig +++ b/src/browser/webapi/KeyValueList.zig @@ -68,12 +68,11 @@ pub fn fromJsObject(arena: Allocator, js_obj: js.Object, comptime normalizer: ?N while (try it.next()) |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; list._entries.appendAssumeCapacity(.{ .name = try String.init(arena, normalized, .{}), - .value = try String.init(arena, value, .{}), + .value = try js_value.toSSOWithAlloc(arena), }); } diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 3e96cc89..8b275c86 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -277,7 +277,7 @@ pub fn cancelIdleCallback(self: *Window, id: u32) void { pub fn reportError(self: *Window, err: js.Value, page: *Page) !void { const error_event = try ErrorEvent.initTrusted("error", .{ .@"error" = try err.persist(), - .message = err.toString(.{}) catch "Unknown error", + .message = err.toStringSlice() catch "Unknown error", .bubbles = false, .cancelable = true, }, page); diff --git a/src/browser/webapi/net/URLSearchParams.zig b/src/browser/webapi/net/URLSearchParams.zig index e9384d28..cf13b81e 100644 --- a/src/browser/webapi/net/URLSearchParams.zig +++ b/src/browser/webapi/net/URLSearchParams.zig @@ -49,8 +49,8 @@ pub fn init(opts_: ?InitOpts, page: *Page) !*URLSearchParams { if (js_val.isObject()) { break :blk try KeyValueList.fromJsObject(arena, js_val.toObject(), null, page); } - if (js_val.isString()) { - break :blk try paramsFromString(arena, try js_val.toString(.{ .allocator = arena }), &page.buf); + if (js_val.isString()) |js_str| { + break :blk try paramsFromString(arena, try js_str.toSliceWithAlloc(arena), &page.buf); } return error.InvalidArgument; }, diff --git a/src/main_wpt.zig b/src/main_wpt.zig index a70aacc6..dbf2eea9 100644 --- a/src/main_wpt.zig +++ b/src/main_wpt.zig @@ -140,7 +140,7 @@ fn run( return err; }; - return value.toString(.{ .allocator = arena }); + return value.toStringSliceWithAlloc(arena); } const Writer = struct {