From 5fa13625a1ca0ea1a3a1c5bb86d0880dcfac349f Mon Sep 17 00:00:00 2001 From: Dylan Conway <35280289+dylan-conway@users.noreply.github.com> Date: Wed, 21 Jun 2023 23:38:18 -0700 Subject: upgrade zig to `v0.11.0-dev.3737+9eb008717` (#3374) * progress * finish `@memset/@memcpy` update * Update build.zig * change `@enumToInt` to `@intFromEnum` and friends * update zig versions * it was 1 * add link to issue * add `compileError` reminder * fix merge * format * upgrade to llvm 16 * Revert "upgrade to llvm 16" This reverts commit cc930ceb1c5b4db9614a7638596948f704544ab8. --------- Co-authored-by: Jarred Sumner Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/node/syscall.zig | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'src/bun.js/node/syscall.zig') diff --git a/src/bun.js/node/syscall.zig b/src/bun.js/node/syscall.zig index 7b10a3028..77bd5b13d 100644 --- a/src/bun.js/node/syscall.zig +++ b/src/bun.js/node/syscall.zig @@ -202,7 +202,7 @@ pub fn openat(dirfd: bun.FileDescriptor, file_path: [:0]const u8, flags: JSC.Nod .SUCCESS => .{ .result = @intCast(bun.FileDescriptor, rc) }, else => |err| .{ .err = .{ - .errno = @truncate(Syscall.Error.Int, @enumToInt(err)), + .errno = @truncate(Syscall.Error.Int, @intFromEnum(err)), .syscall = .open, }, }, @@ -218,7 +218,7 @@ pub fn openat(dirfd: bun.FileDescriptor, file_path: [:0]const u8, flags: JSC.Nod else => |err| { return Maybe(std.os.fd_t){ .err = .{ - .errno = @truncate(Syscall.Error.Int, @enumToInt(err)), + .errno = @truncate(Syscall.Error.Int, @intFromEnum(err)), .syscall = .open, }, }; @@ -253,14 +253,14 @@ pub fn closeAllowingStdoutAndStderr(fd: std.os.fd_t) ?Syscall.Error { if (comptime Environment.isMac) { // This avoids the EINTR problem. return switch (system.getErrno(system.@"close$NOCANCEL"(fd))) { - .BADF => Syscall.Error{ .errno = @enumToInt(os.E.BADF), .syscall = .close }, + .BADF => Syscall.Error{ .errno = @intFromEnum(os.E.BADF), .syscall = .close }, else => null, }; } if (comptime Environment.isLinux) { return switch (linux.getErrno(linux.close(fd))) { - .BADF => Syscall.Error{ .errno = @enumToInt(os.E.BADF), .syscall = .close }, + .BADF => Syscall.Error{ .errno = @intFromEnum(os.E.BADF), .syscall = .close }, else => null, }; } @@ -546,7 +546,7 @@ pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) Maybe([]u8) { .macos, .ios, .watchos, .tvos => { // On macOS, we can use F.GETPATH fcntl command to query the OS for // the path to the file descriptor. - @memset(out_buffer, 0, MAX_PATH_BYTES); + @memset(out_buffer[0..MAX_PATH_BYTES], 0); if (Maybe([]u8).errnoSys(system.fcntl(fd, os.F.GETPATH, out_buffer), .fcntl)) |err| { return err; } @@ -594,7 +594,7 @@ fn mmap( const fail = std.c.MAP.FAILED; if (rc == fail) { return Maybe([]align(mem.page_size) u8){ - .err = .{ .errno = @truncate(Syscall.Error.Int, @enumToInt(std.c.getErrno(@bitCast(i64, @ptrToInt(fail))))), .syscall = .mmap }, + .err = .{ .errno = @truncate(Syscall.Error.Int, @intFromEnum(std.c.getErrno(@bitCast(i64, @intFromPtr(fail))))), .syscall = .mmap }, }; } @@ -643,16 +643,16 @@ pub fn munmap(memory: []align(mem.page_size) const u8) Maybe(void) { pub const Error = struct { const max_errno_value = brk: { const errno_values = std.enums.values(os.E); - var err = @enumToInt(os.E.SUCCESS); + var err = @intFromEnum(os.E.SUCCESS); for (errno_values) |errn| { - err = @max(err, @enumToInt(errn)); + err = @max(err, @intFromEnum(errn)); } break :brk err; }; pub const Int: type = std.math.IntFittingRange(0, max_errno_value + 5); errno: Int, - syscall: Syscall.Tag = @intToEnum(Syscall.Tag, 0), + syscall: Syscall.Tag = @enumFromInt(Syscall.Tag, 0), path: []const u8 = "", fd: i32 = -1, @@ -661,7 +661,7 @@ pub const Error = struct { } pub fn fromCode(errno: os.E, syscall: Syscall.Tag) Error { - return .{ .errno = @truncate(Int, @enumToInt(errno)), .syscall = syscall }; + return .{ .errno = @truncate(Int, @intFromEnum(errno)), .syscall = syscall }; } pub fn format(self: Error, comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void { @@ -672,16 +672,16 @@ pub const Error = struct { pub const retry = Error{ .errno = if (Environment.isLinux) - @intCast(Int, @enumToInt(os.E.AGAIN)) + @intCast(Int, @intFromEnum(os.E.AGAIN)) else if (Environment.isMac) - @intCast(Int, @enumToInt(os.E.WOULDBLOCK)) + @intCast(Int, @intFromEnum(os.E.WOULDBLOCK)) else - @intCast(Int, @enumToInt(os.E.INTR)), + @intCast(Int, @intFromEnum(os.E.INTR)), .syscall = .retry, }; pub inline fn getErrno(this: Error) os.E { - return @intToEnum(os.E, this.errno); + return @enumFromInt(os.E, this.errno); } pub inline fn withPath(this: Error, path: anytype) Error { @@ -726,7 +726,7 @@ pub const Error = struct { // errno label if (this.errno > 0 and this.errno < C.SystemErrno.max) { - const system_errno = @intToEnum(C.SystemErrno, this.errno); + const system_errno = @enumFromInt(C.SystemErrno, this.errno); err.code = JSC.ZigString.init(@tagName(system_errno)); if (C.SystemErrno.labels.get(system_errno)) |label| { err.message = JSC.ZigString.init(label); -- cgit v1.2.3 From 50e872fc761db50ae2804f780ea9cd655600a7e2 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Mon, 26 Jun 2023 11:56:53 -0700 Subject: Implement writev & readv (#3419) * [node:fs] Implement `writev` and `readv` * writev & readv tests * cast to const type * woops * cast --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/node/node_fs.zig | 235 +++++++++++++++++++++++++++++++++ src/bun.js/node/node_fs_binding.zig | 8 +- src/bun.js/node/syscall.zig | 152 +++++++++++++++++++++ src/bun.js/node/types.zig | 44 ++++++ src/io/io_darwin.zig | 4 + src/js/node/fs.js | 41 +++++- src/js/node/fs.promises.ts | 34 ++++- src/js/out/modules/node/fs.js | 38 +++++- src/js/out/modules/node/fs.promises.js | 2 +- test/js/node/fs/fs.test.ts | 65 +++++++++ 10 files changed, 611 insertions(+), 12 deletions(-) (limited to 'src/bun.js/node/syscall.zig') diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 13d785e97..fa33a575b 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -135,6 +135,154 @@ pub const Arguments = struct { } }; + pub const Writev = struct { + fd: FileDescriptor, + buffers: JSC.Node.VectorArrayBuffer, + position: ?u52 = 0, + + pub fn deinit(_: *const @This()) void {} + + pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Writev { + const fd_value = arguments.nextEat() orelse { + if (exception.* == null) { + JSC.throwInvalidArguments( + "file descriptor is required", + .{}, + ctx, + exception, + ); + } + return null; + }; + + const fd = JSC.Node.fileDescriptorFromJS(ctx, fd_value, exception) orelse { + if (exception.* == null) { + JSC.throwInvalidArguments( + "file descriptor must be a number", + .{}, + ctx, + exception, + ); + } + return null; + }; + + const buffers = JSC.Node.VectorArrayBuffer.fromJS( + ctx, + arguments.protectEatNext() orelse { + JSC.throwInvalidArguments("Expected an ArrayBufferView[]", .{}, ctx, exception); + return null; + }, + exception, + arguments.arena.allocator(), + ) orelse { + if (exception.* == null) { + JSC.throwInvalidArguments( + "buffers must be an array of TypedArray", + .{}, + ctx, + exception, + ); + } + return null; + }; + + var position: ?u52 = null; + + if (arguments.nextEat()) |pos_value| { + if (!pos_value.isUndefinedOrNull()) { + if (pos_value.isNumber()) { + position = pos_value.to(u52); + } else { + JSC.throwInvalidArguments( + "position must be a number", + .{}, + ctx, + exception, + ); + return null; + } + } + } + + return Writev{ .fd = fd, .buffers = buffers, .position = position }; + } + }; + + pub const Readv = struct { + fd: FileDescriptor, + buffers: JSC.Node.VectorArrayBuffer, + position: ?u52 = 0, + + pub fn deinit(_: *const @This()) void {} + + pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Readv { + const fd_value = arguments.nextEat() orelse { + if (exception.* == null) { + JSC.throwInvalidArguments( + "file descriptor is required", + .{}, + ctx, + exception, + ); + } + return null; + }; + + const fd = JSC.Node.fileDescriptorFromJS(ctx, fd_value, exception) orelse { + if (exception.* == null) { + JSC.throwInvalidArguments( + "file descriptor must be a number", + .{}, + ctx, + exception, + ); + } + return null; + }; + + const buffers = JSC.Node.VectorArrayBuffer.fromJS( + ctx, + arguments.protectEatNext() orelse { + JSC.throwInvalidArguments("Expected an ArrayBufferView[]", .{}, ctx, exception); + return null; + }, + exception, + arguments.arena.allocator(), + ) orelse { + if (exception.* == null) { + JSC.throwInvalidArguments( + "buffers must be an array of TypedArray", + .{}, + ctx, + exception, + ); + } + return null; + }; + + var position: ?u52 = null; + + if (arguments.nextEat()) |pos_value| { + if (!pos_value.isUndefinedOrNull()) { + if (pos_value.isNumber()) { + position = pos_value.to(u52); + } else { + JSC.throwInvalidArguments( + "position must be a number", + .{}, + ctx, + exception, + ); + return null; + } + } + } + + return Readv{ .fd = fd, .buffers = buffers, .position = position }; + } + }; + pub const FTruncate = struct { fd: FileDescriptor, len: ?JSC.WebCore.Blob.SizeType = null, @@ -2372,6 +2520,7 @@ const Return = struct { pub const Mkdtemp = JSC.ZigString; pub const Open = FileDescriptor; pub const WriteFile = void; + pub const Readv = Read; pub const Read = struct { bytes_read: u52, @@ -2480,6 +2629,8 @@ const Return = struct { pub const Chown = void; pub const Lutimes = void; + + pub const Writev = Write; }; /// Bun's implementation of the Node.js "fs" module @@ -3256,6 +3407,14 @@ pub const NodeFS = struct { ); } + pub fn readv(this: *NodeFS, args: Arguments.Readv, comptime flavor: Flavor) Maybe(Return.Read) { + return if (args.position != null) _preadv(this, args, flavor) else _readv(this, args, flavor); + } + + pub fn writev(this: *NodeFS, args: Arguments.Writev, comptime flavor: Flavor) Maybe(Return.Write) { + return if (args.position != null) _pwritev(this, args, flavor) else _writev(this, args, flavor); + } + pub fn write(this: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) { return if (args.position != null) _pwrite(this, args, flavor) else _write(this, args, flavor); } @@ -3307,6 +3466,82 @@ pub const NodeFS = struct { return Maybe(Return.Write).todo; } + fn _preadv(_: *NodeFS, args: Arguments.Readv, comptime flavor: Flavor) Maybe(Return.Readv) { + const position = args.position.?; + + switch (comptime flavor) { + .sync => { + return switch (Syscall.preadv(args.fd, args.buffers.buffers.items, position)) { + .err => |err| .{ + .err = err, + }, + .result => |amt| .{ .result = .{ + .bytes_read = @truncate(u52, amt), + } }, + }; + }, + else => {}, + } + + return Maybe(Return.Write).todo; + } + + fn _readv(_: *NodeFS, args: Arguments.Readv, comptime flavor: Flavor) Maybe(Return.Readv) { + switch (comptime flavor) { + .sync => { + return switch (Syscall.readv(args.fd, args.buffers.buffers.items)) { + .err => |err| .{ + .err = err, + }, + .result => |amt| .{ .result = .{ + .bytes_read = @truncate(u52, amt), + } }, + }; + }, + else => {}, + } + + return Maybe(Return.Write).todo; + } + + fn _pwritev(_: *NodeFS, args: Arguments.Writev, comptime flavor: Flavor) Maybe(Return.Write) { + const position = args.position.?; + + switch (comptime flavor) { + .sync => { + return switch (Syscall.pwritev(args.fd, args.buffers.buffers.items, position)) { + .err => |err| .{ + .err = err, + }, + .result => |amt| .{ .result = .{ + .bytes_written = @truncate(u52, amt), + } }, + }; + }, + else => {}, + } + + return Maybe(Return.Write).todo; + } + + fn _writev(_: *NodeFS, args: Arguments.Writev, comptime flavor: Flavor) Maybe(Return.Write) { + switch (comptime flavor) { + .sync => { + return switch (Syscall.writev(args.fd, args.buffers.buffers.items)) { + .err => |err| .{ + .err = err, + }, + .result => |amt| .{ .result = .{ + .bytes_written = @truncate(u52, amt), + } }, + }; + }, + else => {}, + } + + return Maybe(Return.Write).todo; + } + pub fn readdir(this: *NodeFS, args: Arguments.Readdir, comptime flavor: Flavor) Maybe(Return.Readdir) { return switch (args.encoding) { .buffer => _readdir( diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig index f178f0355..a4cc62cd3 100644 --- a/src/bun.js/node/node_fs_binding.zig +++ b/src/bun.js/node/node_fs_binding.zig @@ -229,6 +229,10 @@ pub const NodeJSFS = struct { pub const lutimesSync = callSync(.lutimes); pub const rmSync = callSync(.rm); pub const rmdirSync = callSync(.rmdir); + pub const writev = call(.writev); + pub const writevSync = callSync(.writev); + pub const readv = call(.readv); + pub const readvSync = callSync(.readv); pub const fdatasyncSync = callSync(.fdatasync); pub const fdatasync = call(.fdatasync); @@ -247,8 +251,4 @@ pub const NodeJSFS = struct { const notimpl = fdatasync; pub const opendir = notimpl; pub const opendirSync = notimpl; - pub const readv = notimpl; - pub const readvSync = notimpl; - pub const writev = notimpl; - pub const writevSync = notimpl; }; diff --git a/src/bun.js/node/syscall.zig b/src/bun.js/node/syscall.zig index 77bd5b13d..48c5b1305 100644 --- a/src/bun.js/node/syscall.zig +++ b/src/bun.js/node/syscall.zig @@ -106,6 +106,10 @@ pub const Tag = enum(u8) { waitpid, posix_spawn, getaddrinfo, + writev, + pwritev, + readv, + preadv, pub var strings = std.EnumMap(Tag, JSC.C.JSStringRef).initFull(null); }; const PathString = @import("root").bun.PathString; @@ -302,6 +306,154 @@ pub fn write(fd: os.fd_t, bytes: []const u8) Maybe(usize) { } } +fn veclen(buffers: anytype) usize { + var len: usize = 0; + for (buffers) |buffer| { + len += buffer.iov_len; + } + return len; +} + +pub fn writev(fd: os.fd_t, buffers: []std.os.iovec) Maybe(usize) { + if (comptime Environment.isMac) { + const rc = writev_sym(fd, @ptrCast([*]std.os.iovec_const, buffers.ptr), @intCast(i32, buffers.len)); + if (comptime Environment.allow_assert) + log("writev({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .writev, fd)) |err| { + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } else { + while (true) { + const rc = writev_sym(fd, @ptrCast([*]std.os.iovec_const, buffers.ptr), buffers.len); + if (comptime Environment.allow_assert) + log("writev({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .writev, fd)) |err| { + if (err.getErrno() == .INTR) continue; + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } + unreachable; + } +} + +pub fn pwritev(fd: os.fd_t, buffers: []std.os.iovec, position: isize) Maybe(usize) { + if (comptime Environment.isMac) { + const rc = pwritev_sym(fd, @ptrCast([*]std.os.iovec_const, buffers.ptr), @intCast(i32, buffers.len), position); + if (comptime Environment.allow_assert) + log("pwritev({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .pwritev, fd)) |err| { + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } else { + while (true) { + const rc = pwritev_sym(fd, @ptrCast([*]std.os.iovec_const, buffers.ptr), buffers.len, position); + if (comptime Environment.allow_assert) + log("pwritev({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .pwritev, fd)) |err| { + if (err.getErrno() == .INTR) continue; + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } + unreachable; + } +} + +pub fn readv(fd: os.fd_t, buffers: []std.os.iovec) Maybe(usize) { + if (comptime Environment.isMac) { + const rc = readv_sym(fd, buffers.ptr, @intCast(i32, buffers.len)); + if (comptime Environment.allow_assert) + log("readv({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .readv, fd)) |err| { + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } else { + while (true) { + const rc = readv_sym(fd, buffers.ptr, buffers.len); + if (comptime Environment.allow_assert) + log("readv({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .readv, fd)) |err| { + if (err.getErrno() == .INTR) continue; + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } + unreachable; + } +} + +pub fn preadv(fd: os.fd_t, buffers: []std.os.iovec, position: isize) Maybe(usize) { + if (comptime Environment.isMac) { + const rc = preadv_sym(fd, buffers.ptr, @intCast(i32, buffers.len), position); + if (comptime Environment.allow_assert) + log("preadv({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .preadv, fd)) |err| { + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } else { + while (true) { + const rc = preadv_sym(fd, buffers.ptr, buffers.len, position); + if (comptime Environment.allow_assert) + log("preadv({d}, {d}) = {d}", .{ fd, veclen(buffers), rc }); + + if (Maybe(usize).errnoSysFd(rc, .preadv, fd)) |err| { + if (err.getErrno() == .INTR) continue; + return err; + } + + return Maybe(usize){ .result = @intCast(usize, rc) }; + } + unreachable; + } +} + +const preadv_sym = if (builtin.os.tag == .linux and builtin.link_libc) + std.os.linux.preadv +else if (builtin.os.tag.isDarwin()) + system.@"preadv$NOCANCEL" +else + system.preadv; + +const readv_sym = if (builtin.os.tag == .linux and builtin.link_libc) + std.os.linux.readv +else if (builtin.os.tag.isDarwin()) + system.@"readv$NOCANCEL" +else + system.readv; + +const pwritev_sym = if (builtin.os.tag == .linux and builtin.link_libc) + std.os.linux.pwritev +else if (builtin.os.tag.isDarwin()) + system.@"pwritev$NOCANCEL" +else + system.pwritev; + +const writev_sym = if (builtin.os.tag == .linux and builtin.link_libc) + std.os.linux.writev +else if (builtin.os.tag.isDarwin()) + system.@"writev$NOCANCEL" +else + system.writev; + const pread_sym = if (builtin.os.tag == .linux and builtin.link_libc) sys.pread64 else if (builtin.os.tag.isDarwin()) diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 659ac31bb..b01eca8e0 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -813,6 +813,50 @@ pub const Valid = struct { } }; +pub const VectorArrayBuffer = struct { + value: JSC.JSValue, + buffers: std.ArrayList(std.os.iovec), + + pub fn toJS(this: VectorArrayBuffer, _: *JSC.JSGlobalObject) JSC.JSValue { + return this.value; + } + + pub fn fromJS(globalObject: *JSC.JSGlobalObject, val: JSC.JSValue, exception: JSC.C.ExceptionRef, allocator: std.mem.Allocator) ?VectorArrayBuffer { + if (!val.jsType().isArrayLike()) { + JSC.throwInvalidArguments("Expected ArrayBufferView[]", .{}, globalObject, exception); + return null; + } + + var bufferlist = std.ArrayList(std.os.iovec).init(allocator); + var i: usize = 0; + const len = val.getLength(globalObject); + bufferlist.ensureTotalCapacityPrecise(len) catch @panic("Failed to allocate memory for ArrayBuffer[]"); + + while (i < len) { + const element = val.getIndex(globalObject, @truncate(u32, i)); + + if (!element.isCell()) { + JSC.throwInvalidArguments("Expected ArrayBufferView[]", .{}, globalObject, exception); + return null; + } + + const array_buffer = element.asArrayBuffer(globalObject) orelse { + JSC.throwInvalidArguments("Expected ArrayBufferView[]", .{}, globalObject, exception); + return null; + }; + + var buf = array_buffer.byteSlice(); + bufferlist.append(std.os.iovec{ + .iov_base = buf.ptr, + .iov_len = buf.len, + }) catch @panic("Failed to allocate memory for ArrayBuffer[]"); + i += 1; + } + + return VectorArrayBuffer{ .value = val, .buffers = bufferlist }; + } +}; + pub const ArgumentsSlice = struct { remaining: []const JSC.JSValue, vm: *JSC.VirtualMachine, diff --git a/src/io/io_darwin.zig b/src/io/io_darwin.zig index 8045cbdf6..9ebc6f4d6 100644 --- a/src/io/io_darwin.zig +++ b/src/io/io_darwin.zig @@ -274,7 +274,11 @@ pub const darwin = struct { pub extern "c" fn @"openat$NOCANCEL"(fd: c.fd_t, path: [*:0]const u8, oflag: c_uint, ...) c_int; pub extern "c" fn @"read$NOCANCEL"(fd: c.fd_t, buf: [*]u8, nbyte: usize) isize; pub extern "c" fn @"pread$NOCANCEL"(fd: c.fd_t, buf: [*]u8, nbyte: usize, offset: c.off_t) isize; + pub extern "c" fn @"preadv$NOCANCEL"(fd: c.fd_t, uf: [*]std.os.iovec, count: i32, offset: c.off_t) isize; + pub extern "c" fn @"readv$NOCANCEL"(fd: c.fd_t, uf: [*]std.os.iovec, count: i32) isize; pub extern "c" fn @"write$NOCANCEL"(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize; + pub extern "c" fn @"writev$NOCANCEL"(fd: c.fd_t, buf: [*]std.os.iovec_const, count: i32) isize; + pub extern "c" fn @"pwritev$NOCANCEL"(fd: c.fd_t, buf: [*]std.os.iovec_const, count: i32, offset: c.off_t) isize; }; pub const OpenError = error{ /// In WASI, this error may occur when the file descriptor does diff --git a/src/js/node/fs.js b/src/js/node/fs.js index 8d9f0d235..072102c35 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -5,9 +5,8 @@ import { EventEmitter } from "node:events"; // Hardcoded module "node:fs" var { direct, isPromise, isCallable } = globalThis[Symbol.for("Bun.lazy")]("primordials"); -export { default as promises } from "node:fs/promises"; import promises from "node:fs/promises"; - +export { default as promises } from "node:fs/promises"; import * as Stream from "node:stream"; var fs = Bun.fs(); @@ -213,6 +212,40 @@ export var access = function access(...args) { lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), + writev = (fd, buffers, position, callback) => { + if (typeof position === "function") { + callback = position; + position = null; + } + + queueMicrotask(() => { + try { + var written = fs.writevSync(fd, buffers, position); + } catch (e) { + callback(e); + } + + callback(null, written, buffers); + }); + }, + writevSync = fs.writevSync.bind(fs), + readv = (fd, buffers, position, callback) => { + if (typeof position === "function") { + callback = position; + position = null; + } + + queueMicrotask(() => { + try { + var written = fs.readvSync(fd, buffers, position); + } catch (e) { + callback(e); + } + + callback(null, written, buffers); + }); + }, + readvSync = fs.readvSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, watch = function watch(path, options, listener) { @@ -1069,6 +1102,10 @@ export default { ReadStream, watch, FSWatcher, + writev, + writevSync, + readv, + readvSync, [Symbol.for("::bunternal::")]: { ReadStreamClass, WriteStreamClass, diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts index 7df446ccb..12278ef53 100644 --- a/src/js/node/fs.promises.ts +++ b/src/js/node/fs.promises.ts @@ -123,7 +123,37 @@ export var access = promisify(fs.accessSync), utimes = promisify(fs.utimesSync), lutimes = promisify(fs.lutimesSync), rm = promisify(fs.rmSync), - rmdir = promisify(fs.rmdirSync); + rmdir = promisify(fs.rmdirSync), + writev = (fd, buffers, position) => { + return new Promise((resolve, reject) => { + try { + var bytesWritten = fs.writevSync(fd, buffers, position); + } catch (err) { + reject(err); + return; + } + + resolve({ + bytesWritten, + buffers, + }); + }); + }, + readv = (fd, buffers, position) => { + return new Promise((resolve, reject) => { + try { + var bytesRead = fs.readvSync(fd, buffers, position); + } catch (err) { + reject(err); + return; + } + + resolve({ + bytesRead, + buffers, + }); + }); + }; export default { access, @@ -163,6 +193,8 @@ export default { rm, rmdir, watch, + writev, + readv, constants, [Symbol.for("CommonJS")]: 0, }; diff --git a/src/js/out/modules/node/fs.js b/src/js/out/modules/node/fs.js index 6c8269d59..b7457f104 100644 --- a/src/js/out/modules/node/fs.js +++ b/src/js/out/modules/node/fs.js @@ -1,6 +1,6 @@ import {EventEmitter} from "node:events"; -import {default as default2} from "node:fs/promises"; import promises2 from "node:fs/promises"; +import {default as default2} from "node:fs/promises"; import * as Stream from "node:stream"; var callbackify = function(fsFunction, args) { try { @@ -133,7 +133,29 @@ var access = function access2(...args) { callbackify(fs.utimesSync, args); }, lutimes = function lutimes2(...args) { callbackify(fs.lutimesSync, args); -}, accessSync = fs.accessSync.bind(fs), appendFileSync = fs.appendFileSync.bind(fs), closeSync = fs.closeSync.bind(fs), copyFileSync = fs.copyFileSync.bind(fs), existsSync = fs.existsSync.bind(fs), chownSync = fs.chownSync.bind(fs), chmodSync = fs.chmodSync.bind(fs), fchmodSync = fs.fchmodSync.bind(fs), fchownSync = fs.fchownSync.bind(fs), fstatSync = fs.fstatSync.bind(fs), fsyncSync = fs.fsyncSync.bind(fs), ftruncateSync = fs.ftruncateSync.bind(fs), futimesSync = fs.futimesSync.bind(fs), lchmodSync = fs.lchmodSync.bind(fs), lchownSync = fs.lchownSync.bind(fs), linkSync = fs.linkSync.bind(fs), lstatSync = fs.lstatSync.bind(fs), mkdirSync = fs.mkdirSync.bind(fs), mkdtempSync = fs.mkdtempSync.bind(fs), openSync = fs.openSync.bind(fs), readSync = fs.readSync.bind(fs), writeSync = fs.writeSync.bind(fs), readdirSync = fs.readdirSync.bind(fs), readFileSync = fs.readFileSync.bind(fs), writeFileSync = fs.writeFileSync.bind(fs), readlinkSync = fs.readlinkSync.bind(fs), realpathSync = fs.realpathSync.bind(fs), renameSync = fs.renameSync.bind(fs), statSync = fs.statSync.bind(fs), symlinkSync = fs.symlinkSync.bind(fs), truncateSync = fs.truncateSync.bind(fs), unlinkSync = fs.unlinkSync.bind(fs), utimesSync = fs.utimesSync.bind(fs), lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, watch = function watch2(path, options, listener) { +}, accessSync = fs.accessSync.bind(fs), appendFileSync = fs.appendFileSync.bind(fs), closeSync = fs.closeSync.bind(fs), copyFileSync = fs.copyFileSync.bind(fs), existsSync = fs.existsSync.bind(fs), chownSync = fs.chownSync.bind(fs), chmodSync = fs.chmodSync.bind(fs), fchmodSync = fs.fchmodSync.bind(fs), fchownSync = fs.fchownSync.bind(fs), fstatSync = fs.fstatSync.bind(fs), fsyncSync = fs.fsyncSync.bind(fs), ftruncateSync = fs.ftruncateSync.bind(fs), futimesSync = fs.futimesSync.bind(fs), lchmodSync = fs.lchmodSync.bind(fs), lchownSync = fs.lchownSync.bind(fs), linkSync = fs.linkSync.bind(fs), lstatSync = fs.lstatSync.bind(fs), mkdirSync = fs.mkdirSync.bind(fs), mkdtempSync = fs.mkdtempSync.bind(fs), openSync = fs.openSync.bind(fs), readSync = fs.readSync.bind(fs), writeSync = fs.writeSync.bind(fs), readdirSync = fs.readdirSync.bind(fs), readFileSync = fs.readFileSync.bind(fs), writeFileSync = fs.writeFileSync.bind(fs), readlinkSync = fs.readlinkSync.bind(fs), realpathSync = fs.realpathSync.bind(fs), renameSync = fs.renameSync.bind(fs), statSync = fs.statSync.bind(fs), symlinkSync = fs.symlinkSync.bind(fs), truncateSync = fs.truncateSync.bind(fs), unlinkSync = fs.unlinkSync.bind(fs), utimesSync = fs.utimesSync.bind(fs), lutimesSync = fs.lutimesSync.bind(fs), rmSync = fs.rmSync.bind(fs), rmdirSync = fs.rmdirSync.bind(fs), writev = (fd, buffers, position, callback) => { + if (typeof position === "function") + callback = position, position = null; + queueMicrotask(() => { + try { + var written = fs.writevSync(fd, buffers, position); + } catch (e) { + callback(e); + } + callback(null, written, buffers); + }); +}, writevSync = fs.writevSync.bind(fs), readv = (fd, buffers, position, callback) => { + if (typeof position === "function") + callback = position, position = null; + queueMicrotask(() => { + try { + var written = fs.readvSync(fd, buffers, position); + } catch (e) { + callback(e); + } + callback(null, written, buffers); + }); +}, readvSync = fs.readvSync.bind(fs), Dirent = fs.Dirent, Stats = fs.Stats, watch = function watch2(path, options, listener) { return new FSWatcher(path, options, listener); }, readStreamPathFastPathSymbol = Symbol.for("Bun.Node.readStreamPathFastPath"), readStreamSymbol = Symbol.for("Bun.NodeReadStream"), readStreamPathOrFdSymbol = Symbol.for("Bun.NodeReadStreamPathOrFd"), writeStreamSymbol = Symbol.for("Bun.NodeWriteStream"), writeStreamPathFastPathSymbol = Symbol.for("Bun.NodeWriteStreamFastPath"), writeStreamPathFastPathCallSymbol = Symbol.for("Bun.NodeWriteStreamFastPathCall"), kIoDone = Symbol.for("kIoDone"), defaultReadStreamOptions = { file: void 0, @@ -366,8 +388,8 @@ WriteStream = function(InternalWriteStream) { return WriteStreamClass = InternalWriteStream, Object.defineProperty(WriteStreamClass.prototype, Symbol.toStringTag, { value: "WritesStream", enumerable: !1 - }), Object.defineProperty(function WriteStream(options) { - return new InternalWriteStream(options); + }), Object.defineProperty(function WriteStream(path, options) { + return new InternalWriteStream(path, options); }, Symbol.hasInstance, { value(instance) { return instance instanceof InternalWriteStream; @@ -642,12 +664,18 @@ var fs_default = { ReadStream, watch, FSWatcher, + writev, + writevSync, + readv, + readvSync, [Symbol.for("::bunternal::")]: { ReadStreamClass, WriteStreamClass } }; export { + writevSync, + writev, writeSync, writeFileSync, writeFile, @@ -671,6 +699,8 @@ export { rename, realpathSync, realpath, + readvSync, + readv, readlinkSync, readlink, readdirSync, diff --git a/src/js/out/modules/node/fs.promises.js b/src/js/out/modules/node/fs.promises.js index ef3330771..549ba0c9c 100644 --- a/src/js/out/modules/node/fs.promises.js +++ b/src/js/out/modules/node/fs.promises.js @@ -1 +1 @@ -function H(S,C={}){const J=[];if(S instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(S))S=S.toString();else if(typeof S!=="string")throw new TypeError("Expected path to be a string or Buffer");let b=null;if(typeof C==="string")C={encoding:C};return D.watch(S,C||{},(q,z)=>{if(J.push({eventType:q,filename:z}),b){const A=b;b=null,A()}}),{async*[Symbol.asyncIterator](){let q=!1;while(!q){while(J.length){let z=J.shift();if(z.eventType==="close"){q=!0;break}if(z.eventType==="error")throw q=!0,z.filename;yield z}await new Promise((z)=>b=z)}}}}var D=Bun.fs(),B="::bunternal::",G={[B]:(S)=>{var C={[B]:function(J,b,q){var z;try{z=S.apply(D,q),q=void 0}catch(A){q=void 0,b(A);return}J(z)}}[B];return async function(...J){return await new Promise((b,q)=>{process.nextTick(C,b,q,J)})}}}[B],I=G(D.accessSync),K=G(D.appendFileSync),L=G(D.closeSync),M=G(D.copyFileSync),N=G(D.existsSync),O=G(D.chownSync),P=G(D.chmodSync),Q=G(D.fchmodSync),U=G(D.fchownSync),V=G(D.fstatSync),W=G(D.fsyncSync),X=G(D.ftruncateSync),Y=G(D.futimesSync),Z=G(D.lchmodSync),_=G(D.lchownSync),$=G(D.linkSync),T=G(D.lstatSync),E=G(D.mkdirSync),j=G(D.mkdtempSync),R=G(D.openSync),k=G(D.readSync),x=G(D.writeSync),F=G(D.readdirSync),u=G(D.readFileSync),w=G(D.writeFileSync),g=G(D.readlinkSync),h=G(D.realpathSync),d=G(D.renameSync),c=G(D.statSync),v=G(D.symlinkSync),a=G(D.truncateSync),y=G(D.unlinkSync),l=G(D.utimesSync),t=G(D.lutimesSync),p=G(D.rmSync),n=G(D.rmdirSync),m={access:I,appendFile:K,close:L,copyFile:M,exists:N,chown:O,chmod:P,fchmod:Q,fchown:U,fstat:V,fsync:W,ftruncate:X,futimes:Y,lchmod:Z,lchown:_,link:$,lstat:T,mkdir:E,mkdtemp:j,open:R,read:k,write:x,readdir:F,readFile:u,writeFile:w,readlink:g,realpath:h,rename:d,stat:c,symlink:v,truncate:a,unlink:y,utimes:l,lutimes:t,rm:p,rmdir:n,watch:H,constants,[Symbol.for("CommonJS")]:0};export{w as writeFile,x as write,H as watch,l as utimes,y as unlink,a as truncate,v as symlink,c as stat,n as rmdir,p as rm,d as rename,h as realpath,g as readlink,F as readdir,u as readFile,k as read,R as open,j as mkdtemp,E as mkdir,t as lutimes,T as lstat,$ as link,_ as lchown,Z as lchmod,Y as futimes,X as ftruncate,W as fsync,V as fstat,U as fchown,Q as fchmod,N as exists,m as default,M as copyFile,L as close,O as chown,P as chmod,K as appendFile,I as access}; +function J(S,q={}){const z=[];if(S instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(S))S=S.toString();else if(typeof S!=="string")throw new TypeError("Expected path to be a string or Buffer");let A=null;if(typeof q==="string")q={encoding:q};return H.watch(S,q||{},(B,C)=>{if(z.push({eventType:B,filename:C}),A){const D=A;A=null,D()}}),{async*[Symbol.asyncIterator](){let B=!1;while(!B){while(z.length){let C=z.shift();if(C.eventType==="close"){B=!0;break}if(C.eventType==="error")throw B=!0,C.filename;yield C}await new Promise((C)=>A=C)}}}}var H=Bun.fs(),G="::bunternal::",I={[G]:(S)=>{var q={[G]:function(z,A,B){var C;try{C=S.apply(H,B),B=void 0}catch(D){B=void 0,A(D);return}z(C)}}[G];return async function(...z){return await new Promise((A,B)=>{process.nextTick(q,A,B,z)})}}}[G],K=I(H.accessSync),L=I(H.appendFileSync),M=I(H.closeSync),N=I(H.copyFileSync),O=I(H.existsSync),P=I(H.chownSync),Q=I(H.chmodSync),U=I(H.fchmodSync),V=I(H.fchownSync),X=I(H.fstatSync),Y=I(H.fsyncSync),Z=I(H.ftruncateSync),_=I(H.futimesSync),$=I(H.lchmodSync),T=I(H.lchownSync),W=I(H.linkSync),k=I(H.lstatSync),E=I(H.mkdirSync),x=I(H.mkdtempSync),F=I(H.openSync),R=I(H.readSync),g=I(H.writeSync),h=I(H.readdirSync),j=I(H.readFileSync),w=I(H.writeFileSync),b=I(H.readlinkSync),u=I(H.realpathSync),d=I(H.renameSync),c=I(H.statSync),v=I(H.symlinkSync),a=I(H.truncateSync),y=I(H.unlinkSync),l=I(H.utimesSync),p=I(H.lutimesSync),m=I(H.rmSync),n=I(H.rmdirSync),t=(S,q,z)=>{return new Promise((A,B)=>{try{var C=H.writevSync(S,q,z)}catch(D){B(D);return}A({bytesWritten:C,buffers:q})})},o=(S,q,z)=>{return new Promise((A,B)=>{try{var C=H.readvSync(S,q,z)}catch(D){B(D);return}A({bytesRead:C,buffers:q})})},r={access:K,appendFile:L,close:M,copyFile:N,exists:O,chown:P,chmod:Q,fchmod:U,fchown:V,fstat:X,fsync:Y,ftruncate:Z,futimes:_,lchmod:$,lchown:T,link:W,lstat:k,mkdir:E,mkdtemp:x,open:F,read:R,write:g,readdir:h,readFile:j,writeFile:w,readlink:b,realpath:u,rename:d,stat:c,symlink:v,truncate:a,unlink:y,utimes:l,lutimes:p,rm:m,rmdir:n,watch:J,writev:t,readv:o,constants,[Symbol.for("CommonJS")]:0};export{t as writev,w as writeFile,g as write,J as watch,l as utimes,y as unlink,a as truncate,v as symlink,c as stat,n as rmdir,m as rm,d as rename,u as realpath,o as readv,b as readlink,h as readdir,j as readFile,R as read,F as open,x as mkdtemp,E as mkdir,p as lutimes,k as lstat,W as link,T as lchown,$ as lchmod,_ as futimes,Z as ftruncate,Y as fsync,X as fstat,V as fchown,U as fchmod,O as exists,r as default,N as copyFile,M as close,P as chown,Q as chmod,L as appendFile,K as access}; diff --git a/test/js/node/fs/fs.test.ts b/test/js/node/fs/fs.test.ts index 0353968fe..272522fc0 100644 --- a/test/js/node/fs/fs.test.ts +++ b/test/js/node/fs/fs.test.ts @@ -29,6 +29,8 @@ import fs, { realpathSync, readlinkSync, symlinkSync, + writevSync, + readvSync, } from "node:fs"; import _promises from "node:fs/promises"; @@ -301,6 +303,69 @@ describe("readSync", () => { }); }); +it("writevSync", () => { + var fd = openSync(`${tmpdir()}/writevSync.txt`, "w"); + fs.ftruncateSync(fd, 0); + const buffers = [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), new Uint8Array([7, 8, 9])]; + const result = writevSync(fd, buffers); + expect(result).toBe(9); + closeSync(fd); + + fd = openSync(`${tmpdir()}/writevSync.txt`, "r"); + const buf = new Uint8Array(9); + readSync(fd, buf, 0, 9, 0); + expect(buf).toEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])); +}); + +it("pwritevSync", () => { + var fd = openSync(`${tmpdir()}/pwritevSync.txt`, "w"); + fs.ftruncateSync(fd, 0); + writeSync(fd, "lalalala", 0); + const buffers = [new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6]), new Uint8Array([7, 8, 9])]; + const result = writevSync(fd, buffers, "lalalala".length); + expect(result).toBe(9); + closeSync(fd); + + const out = readFileSync(`${tmpdir()}/pwritevSync.txt`); + expect(out.slice(0, "lalalala".length).toString()).toBe("lalalala"); + expect(out.slice("lalalala".length)).toEqual(new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9])); +}); + +it("readvSync", () => { + var fd = openSync(`${tmpdir()}/readv.txt`, "w"); + fs.ftruncateSync(fd, 0); + + const buf = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9]); + writeSync(fd, buf, 0, 9, 0); + closeSync(fd); + + var fd = openSync(`${tmpdir()}/readv.txt`, "r"); + const buffers = [new Uint8Array(3), new Uint8Array(3), new Uint8Array(3)]; + const result = readvSync(fd, buffers); + expect(result).toBe(9); + expect(buffers[0]).toEqual(new Uint8Array([1, 2, 3])); + expect(buffers[1]).toEqual(new Uint8Array([4, 5, 6])); + expect(buffers[2]).toEqual(new Uint8Array([7, 8, 9])); + closeSync(fd); +}); + +it("preadv", () => { + var fd = openSync(`${tmpdir()}/preadv.txt`, "w"); + fs.ftruncateSync(fd, 0); + + const buf = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + writeSync(fd, buf, 0, buf.byteLength, 0); + closeSync(fd); + + var fd = openSync(`${tmpdir()}/preadv.txt`, "r"); + const buffers = [new Uint8Array(3), new Uint8Array(3), new Uint8Array(3)]; + const result = readvSync(fd, buffers, 3); + expect(result).toBe(9); + expect(buffers[0]).toEqual(new Uint8Array([4, 5, 6])); + expect(buffers[1]).toEqual(new Uint8Array([7, 8, 9])); + expect(buffers[2]).toEqual(new Uint8Array([10, 11, 12])); +}); + describe("writeSync", () => { it("works with a position set to 0", () => { const fd = openSync(import.meta.dir + "/writeFileSync.txt", "w+"); -- cgit v1.2.3 From f00e2be548da21b9feaef178bb0ac22230801d6f Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sat, 1 Jul 2023 17:37:44 -0700 Subject: Use `BunString` in `SystemError` (#3485) * Use `BunString` in SystemError * Use Bun::toStringRef when we will de-ref strings * Move `napi_create_error` to C++ to support `code` being a Symbol potentially * Update blob.zig * Make this test less flaky --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/api/bun/dns_resolver.zig | 8 ++-- src/bun.js/api/bun/socket.zig | 12 ++--- src/bun.js/api/ffi.zig | 6 +-- src/bun.js/api/server.zig | 12 ++--- src/bun.js/bindings/BunString.cpp | 48 +++++++++++++++++--- src/bun.js/bindings/bindings.cpp | 66 +++++++++++++--------------- src/bun.js/bindings/bindings.zig | 8 ++-- src/bun.js/bindings/headers-handwritten.h | 12 +++-- src/bun.js/bindings/helpers.h | 4 +- src/bun.js/bindings/napi.cpp | 29 +++++++++--- src/bun.js/bindings/webcore/JSCloseEvent.cpp | 2 +- src/bun.js/node/node_os.zig | 32 +++++++------- src/bun.js/node/syscall.zig | 8 ++-- src/bun.js/webcore/blob.zig | 55 +++++++++++------------ src/bun.js/webcore/response.zig | 14 +++--- src/bun.js/webcore/streams.zig | 6 +-- src/bundler/bundle_v2.zig | 16 +++++-- src/http.zig | 5 ++- src/napi/napi.zig | 11 +---- test/js/bun/util/error-gc-test.test.js | 41 ++++++++++++++++- 20 files changed, 242 insertions(+), 153 deletions(-) (limited to 'src/bun.js/node/syscall.zig') diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index fee834e5e..d0d4f5b7b 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -1925,8 +1925,8 @@ pub const DNSResolver = struct { .err => |err| { const system_error = JSC.SystemError{ .errno = -1, - .code = JSC.ZigString.init(err.code()), - .message = JSC.ZigString.init(err.label()), + .code = bun.String.static(err.code()), + .message = bun.String.static(err.label()), }; globalThis.throwValue(system_error.toErrorInstance(globalThis)); @@ -1972,8 +1972,8 @@ pub const DNSResolver = struct { .err => |err| { const system_error = JSC.SystemError{ .errno = -1, - .code = JSC.ZigString.init(err.code()), - .message = JSC.ZigString.init(err.label()), + .code = bun.String.static(err.code()), + .message = bun.String.static(err.label()), }; globalThis.throwValue(system_error.toErrorInstance(globalThis)); diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 00e34a77d..69d6611cb 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -1022,8 +1022,8 @@ fn NewSocket(comptime ssl: bool) type { var globalObject = handlers.globalObject; const err = JSC.SystemError{ .errno = errno, - .message = ZigString.init("Failed to connect"), - .syscall = ZigString.init("connect"), + .message = bun.String.static("Failed to connect"), + .syscall = bun.String.static("connect"), }; if (callback == .zero) { @@ -1232,8 +1232,8 @@ fn NewSocket(comptime ssl: bool) type { const reason = if (ssl_error.reason == null) "" else ssl_error.reason[0..bun.len(ssl_error.reason)]; const fallback = JSC.SystemError{ - .code = ZigString.init(code), - .message = ZigString.init(reason), + .code = bun.String.create(code), + .message = bun.String.create(reason), }; authorization_error = fallback.toErrorInstance(globalObject); @@ -1409,8 +1409,8 @@ fn NewSocket(comptime ssl: bool) type { const reason = if (ssl_error.reason == null) "" else ssl_error.reason[0..bun.len(ssl_error.reason)]; const fallback = JSC.SystemError{ - .code = ZigString.init(code), - .message = ZigString.init(reason), + .code = bun.String.create(code), + .message = bun.String.create(reason), }; return fallback.toErrorInstance(globalObject); diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index e46e054ec..ba31b67ed 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -311,9 +311,9 @@ pub const FFI = struct { break :brk std.DynLib.open(backup_name) catch { // Then, if that fails, report an error. const system_error = JSC.SystemError{ - .code = ZigString.init(@tagName(JSC.Node.ErrorCode.ERR_DLOPEN_FAILED)), - .message = ZigString.init("Failed to open library. This is usually caused by a missing library or an invalid library path."), - .syscall = ZigString.init("dlopen"), + .code = bun.String.create(@tagName(JSC.Node.ErrorCode.ERR_DLOPEN_FAILED)), + .message = bun.String.create("Failed to open library. This is usually caused by a missing library or an invalid library path."), + .syscall = bun.String.create("dlopen"), }; return system_error.toErrorInstance(global); }; diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 136737069..140e62ce4 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -1796,7 +1796,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp .syscall = .sendfile, }; var sys = err.withPathLike(file.pathlike).toSystemError(); - sys.message = ZigString.init("MacOS does not support sending non-regular files"); + sys.message = bun.String.static("MacOS does not support sending non-regular files"); this.runErrorHandler(sys.toErrorInstance( this.server.globalThis, )); @@ -1815,7 +1815,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp .syscall = .sendfile, }; var sys = err.withPathLike(file.pathlike).toSystemError(); - sys.message = ZigString.init("File must be regular or FIFO"); + sys.message = bun.String.static("File must be regular or FIFO"); this.runErrorHandler(sys.toErrorInstance( this.server.globalThis, )); @@ -2375,8 +2375,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } const fallback = JSC.SystemError{ - .code = ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_UNHANDLED_ERROR))), - .message = ZigString.init("Unhandled error in ReadableStream"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_UNHANDLED_ERROR))), + .message = bun.String.static("Unhandled error in ReadableStream"), }; req.handleReject(fallback.toErrorInstance(globalThis)); } @@ -2422,8 +2422,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp if (stream.isLocked(this.server.globalThis)) { streamLog("was locked but it shouldn't be", .{}); var err = JSC.SystemError{ - .code = ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE))), - .message = ZigString.init("Stream already used, please create a new one"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_STREAM_CANNOT_PIPE))), + .message = bun.String.static("Stream already used, please create a new one"), }; stream.value.unprotect(); this.runErrorHandler(err.toErrorInstance(this.server.globalThis)); diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index f590edd35..4c8ff384e 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -86,31 +86,69 @@ BunString toString(JSC::JSGlobalObject* globalObject, JSValue value) return fromJS(globalObject, value); } +BunString toStringRef(JSC::JSGlobalObject* globalObject, JSValue value) +{ + auto str = value.toWTFString(globalObject); + if (str.isEmpty()) { + return { BunStringTag::Empty }; + } + + str.impl()->ref(); + + return { BunStringTag::WTFStringImpl, { .wtf = str.impl() } }; +} + BunString toString(WTF::String& wtfString) { - if (wtfString.length() == 0) + if (wtfString.isEmpty()) return { BunStringTag::Empty }; return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; } BunString toString(const WTF::String& wtfString) { - if (wtfString.length() == 0) + if (wtfString.isEmpty()) return { BunStringTag::Empty }; return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; } BunString toString(WTF::StringImpl* wtfString) { - if (wtfString->length() == 0) + if (wtfString->isEmpty()) return { BunStringTag::Empty }; return { BunStringTag::WTFStringImpl, { .wtf = wtfString } }; } +BunString toStringRef(WTF::String& wtfString) +{ + if (wtfString.isEmpty()) + return { BunStringTag::Empty }; + + wtfString.impl()->ref(); + return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; +} +BunString toStringRef(const WTF::String& wtfString) +{ + if (wtfString.isEmpty()) + return { BunStringTag::Empty }; + + wtfString.impl()->ref(); + return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; +} +BunString toStringRef(WTF::StringImpl* wtfString) +{ + if (wtfString->isEmpty()) + return { BunStringTag::Empty }; + + wtfString->ref(); + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString } }; +} + BunString fromString(WTF::String& wtfString) { - if (wtfString.length() == 0) + if (wtfString.isEmpty()) return { BunStringTag::Empty }; return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; @@ -118,7 +156,7 @@ BunString fromString(WTF::String& wtfString) BunString fromString(WTF::StringImpl* wtfString) { - if (wtfString->length() == 0) + if (wtfString->isEmpty()) return { BunStringTag::Empty }; return { BunStringTag::WTFStringImpl, { .wtf = wtfString } }; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 96fcde303..9f9b20c1e 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -1279,15 +1279,14 @@ JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, JSC__JSGlobalObject* globalObject) { - static const char* system_error_name = "SystemError"; SystemError err = *arg0; JSC::VM& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSValue message = JSC::jsUndefined(); - if (err.message.len > 0) { - message = Zig::toJSString(err.message, globalObject); + if (err.message.tag != BunStringTag::Empty) { + message = Bun::toJS(globalObject, err.message); } JSC::JSValue options = JSC::jsUndefined(); @@ -1297,8 +1296,8 @@ JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, auto clientData = WebCore::clientData(vm); - if (err.code.len > 0 && !(err.code.len == 1 and err.code.ptr[0] == 0)) { - JSC::JSValue code = Zig::toJSStringGC(err.code, globalObject); + if (err.code.tag != BunStringTag::Empty) { + JSC::JSValue code = Bun::toJS(globalObject, err.code); result->putDirect(vm, clientData->builtinNames().codePublicName(), code, JSC::PropertyAttribute::DontDelete | 0); @@ -1307,13 +1306,12 @@ JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, result->putDirect( vm, vm.propertyNames->name, - JSC::JSValue(JSC::jsOwnedString( - vm, WTF::String(WTF::StringImpl::createWithoutCopying(system_error_name, 11)))), + JSC::JSValue(jsString(vm, String("SystemError"_s))), JSC::PropertyAttribute::DontEnum | 0); } - if (err.path.len > 0) { - JSC::JSValue path = JSC::JSValue(Zig::toJSStringGC(err.path, globalObject)); + if (err.path.tag != BunStringTag::Empty) { + JSC::JSValue path = Bun::toJS(globalObject, err.path); result->putDirect(vm, clientData->builtinNames().pathPublicName(), path, JSC::PropertyAttribute::DontDelete | 0); } @@ -1324,8 +1322,8 @@ JSC__JSValue SystemError__toErrorInstance(const SystemError* arg0, JSC::PropertyAttribute::DontDelete | 0); } - if (err.syscall.len > 0) { - JSC::JSValue syscall = JSC::JSValue(Zig::toJSString(err.syscall, globalObject)); + if (err.syscall.tag != BunStringTag::Empty) { + JSC::JSValue syscall = Bun::toJS(globalObject, err.syscall); result->putDirect(vm, clientData->builtinNames().syscallPublicName(), syscall, JSC::PropertyAttribute::DontDelete | 0); } @@ -3303,11 +3301,8 @@ bool JSC__JSValue__stringIncludes(JSC__JSValue value, JSC__JSGlobalObject* globa static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stackFrame, ZigStackFrame* frame) { - String str = stackFrame->sourceURL(vm); - if (!str.isEmpty()) - str.impl()->ref(); - frame->source_url = Bun::toString(str); + frame->source_url = Bun::toStringRef(stackFrame->sourceURL(vm)); if (stackFrame->isWasmFrame()) { frame->code_type = ZigStackFrameCodeWasm; @@ -3344,10 +3339,7 @@ static void populateStackFrameMetadata(JSC::VM& vm, const JSC::StackFrame* stack JSC::JSObject* callee = JSC::jsCast(calleeCell); - String displayName = JSC::getCalculatedDisplayName(vm, callee); - if (!displayName.isEmpty()) - displayName.impl()->ref(); - frame->function_name = Bun::toString(displayName); + frame->function_name = Bun::toStringRef(JSC::getCalculatedDisplayName(vm, callee)); } // Based on // https://github.com/mceSystems/node-jsc/blob/master/deps/jscshim/src/shim/JSCStackTrace.cpp#L298 @@ -3421,7 +3413,7 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr // Most of the time, when you look at a stack trace, you want a couple lines above - source_lines[0] = Bun::toString(sourceString.substring(lineStart, lineStop - lineStart).toStringWithoutCopying()); + source_lines[0] = Bun::toStringRef(sourceString.substring(lineStart, lineStop - lineStart).toStringWithoutCopying()); source_line_numbers[0] = line; if (lineStart > 0) { @@ -3438,7 +3430,7 @@ static void populateStackFramePosition(const JSC::StackFrame* stackFrame, BunStr } // We are at the beginning of the line - source_lines[source_line_i] = Bun::toString(sourceString.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1).toStringWithoutCopying()); + source_lines[source_line_i] = Bun::toStringRef(sourceString.substring(byte_offset_in_source_string, end_of_line_offset - byte_offset_in_source_string + 1).toStringWithoutCopying()); source_line_numbers[source_line_i] = line - source_line_i; source_line_i++; @@ -3526,30 +3518,32 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, except->code = 8; } if (except->code == SYNTAX_ERROR_CODE) { - except->message = Bun::toString(err->sanitizedMessageString(global)); + except->message = Bun::toStringRef(err->sanitizedMessageString(global)); } else if (JSC::JSValue message = obj->getIfPropertyExists(global, vm.propertyNames->message)) { - except->message = Bun::toString(global, message); + except->message = Bun::toStringRef(global, message); } else { - except->message = Bun::toString(err->sanitizedMessageString(global)); + except->message = Bun::toStringRef(err->sanitizedMessageString(global)); } - except->name = Bun::toString(err->sanitizedNameString(global)); + + except->name = Bun::toStringRef(err->sanitizedNameString(global)); + except->runtime_type = err->runtimeTypeForCause(); auto clientData = WebCore::clientData(vm); if (except->code != SYNTAX_ERROR_CODE) { if (JSC::JSValue syscall = obj->getIfPropertyExists(global, clientData->builtinNames().syscallPublicName())) { - except->syscall = Bun::toString(global, syscall); + except->syscall = Bun::toStringRef(global, syscall); } if (JSC::JSValue code = obj->getIfPropertyExists(global, clientData->builtinNames().codePublicName())) { - except->code_ = Bun::toString(global, code); + except->code_ = Bun::toStringRef(global, code); } if (JSC::JSValue path = obj->getIfPropertyExists(global, clientData->builtinNames().pathPublicName())) { - except->path = Bun::toString(global, path); + except->path = Bun::toStringRef(global, path); } if (JSC::JSValue fd = obj->getIfPropertyExists(global, Identifier::fromString(vm, "fd"_s))) { @@ -3565,7 +3559,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, if (getFromSourceURL) { if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, vm.propertyNames->sourceURL)) { - except->stack.frames_ptr[0].source_url = Bun::toString(global, sourceURL); + except->stack.frames_ptr[0].source_url = Bun::toStringRef(global, sourceURL); if (JSC::JSValue column = obj->getIfPropertyExists(global, vm.propertyNames->column)) { except->stack.frames_ptr[0].position.column_start = column.toInt32(global); @@ -3577,7 +3571,7 @@ static void fromErrorInstance(ZigException* except, JSC::JSGlobalObject* global, if (JSC::JSValue lineText = obj->getIfPropertyExists(global, JSC::Identifier::fromString(vm, "lineText"_s))) { if (JSC::JSString* jsStr = lineText.toStringOrNull(global)) { auto str = jsStr->value(global); - except->stack.source_lines_ptr[0] = Bun::toString(str); + except->stack.source_lines_ptr[0] = Bun::toStringRef(str); except->stack.source_lines_numbers[0] = except->stack.frames_ptr[0].position.line; except->stack.source_lines_len = 1; except->remapped = true; @@ -3600,7 +3594,7 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal if (JSC::JSObject* obj = JSC::jsDynamicCast(value)) { if (obj->hasProperty(global, global->vm().propertyNames->name)) { auto name_str = obj->getIfPropertyExists(global, global->vm().propertyNames->name).toWTFString(global); - except->name = Bun::toString(name_str); + except->name = Bun::toStringRef(name_str); if (name_str == "Error"_s) { except->code = JSErrorCodeError; } else if (name_str == "EvalError"_s) { @@ -3622,14 +3616,14 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal if (JSC::JSValue message = obj->getIfPropertyExists(global, global->vm().propertyNames->message)) { if (message) { - except->message = Bun::toString( + except->message = Bun::toStringRef( message.toWTFString(global)); } } if (JSC::JSValue sourceURL = obj->getIfPropertyExists(global, global->vm().propertyNames->sourceURL)) { if (sourceURL) { - except->stack.frames_ptr[0].source_url = Bun::toString( + except->stack.frames_ptr[0].source_url = Bun::toStringRef( sourceURL.toWTFString(global)); except->stack.frames_len = 1; } @@ -3658,7 +3652,7 @@ void exceptionFromString(ZigException* except, JSC::JSValue value, JSC::JSGlobal } scope.release(); - except->message = Bun::toString(str); + except->message = Bun::toStringRef(str); } void JSC__VM__releaseWeakRefs(JSC__VM* arg0) @@ -3768,8 +3762,8 @@ void JSC__JSValue__toZigException(JSC__JSValue JSValue0, JSC__JSGlobalObject* ar JSC::JSValue value = JSC::JSValue::decode(JSValue0); if (value == JSC::JSValue {}) { exception->code = JSErrorCodeError; - exception->name = Bun::toString("Error"_s); - exception->message = Bun::toString("Unknown error"_s); + exception->name = Bun::toStringRef("Error"_s); + exception->message = Bun::toStringRef("Unknown error"_s); return; } diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 1c09378a8..777860d3c 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -1561,10 +1561,10 @@ pub const FetchHeaders = opaque { pub const SystemError = extern struct { errno: c_int = 0, /// label for errno - code: ZigString = ZigString.init(""), - message: ZigString = ZigString.init(""), - path: ZigString = ZigString.init(""), - syscall: ZigString = ZigString.init(""), + code: String = String.empty, + message: String = String.empty, + path: String = String.empty, + syscall: String = String.empty, fd: i32 = -1, pub fn Maybe(comptime Result: type) type { diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index c7429b633..90c8f86d2 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -84,10 +84,10 @@ typedef struct ErrorableResolvedSource { typedef struct SystemError { int errno_; - ZigString code; - ZigString message; - ZigString path; - ZigString syscall; + BunString code; + BunString message; + BunString path; + BunString syscall; int fd; } SystemError; @@ -246,6 +246,10 @@ BunString toString(WTF::String& wtfString); BunString toString(const WTF::String& wtfString); BunString toString(WTF::StringImpl* wtfString); +BunString toStringRef(JSC::JSGlobalObject* globalObject, JSC::JSValue value); +BunString toStringRef(WTF::String& wtfString); +BunString toStringRef(const WTF::String& wtfString); +BunString toStringRef(WTF::StringImpl* wtfString); } using Uint8Array_alias = JSC::JSUint8Array; diff --git a/src/bun.js/bindings/helpers.h b/src/bun.js/bindings/helpers.h index 402807f3d..00777c304 100644 --- a/src/bun.js/bindings/helpers.h +++ b/src/bun.js/bindings/helpers.h @@ -342,10 +342,10 @@ static const WTF::String toStringStatic(ZigString str) } if (isTaggedUTF16Ptr(str.ptr)) { - return WTF::String(WTF::ExternalStringImpl::createStatic(reinterpret_cast(untag(str.ptr)), str.len)); + return WTF::String(AtomStringImpl::add(reinterpret_cast(untag(str.ptr)), str.len)); } - return WTF::String(WTF::ExternalStringImpl::createStatic( + return WTF::String(AtomStringImpl::add( reinterpret_cast(untag(str.ptr)), str.len)); } diff --git a/src/bun.js/bindings/napi.cpp b/src/bun.js/bindings/napi.cpp index a859e3ac5..bb62cb2a0 100644 --- a/src/bun.js/bindings/napi.cpp +++ b/src/bun.js/bindings/napi.cpp @@ -554,7 +554,6 @@ extern "C" napi_status napi_wrap(napi_env env, auto* globalObject = toJS(env); auto& vm = globalObject->vm(); - auto* val = jsDynamicCast(value); @@ -572,7 +571,7 @@ extern "C" napi_status napi_wrap(napi_env env, auto clientData = WebCore::clientData(vm); auto* ref = new NapiRef(globalObject, 1); - ref->strongRef.set(globalObject->vm(), value.getObject()); + ref->strongRef.set(globalObject->vm(), value.getObject()); if (finalize_cb) { ref->finalizer.finalize_cb = finalize_cb; @@ -816,7 +815,7 @@ extern "C" napi_status napi_create_reference(napi_env env, napi_value value, } } - if(object) { + if (object) { object->napiRef = ref; } @@ -1029,7 +1028,26 @@ extern "C" napi_status napi_create_type_error(napi_env env, napi_value code, auto error = JSC::createTypeError(globalObject, messageValue.toWTFString(globalObject)); if (codeValue) { - error->putDirect(vm, Identifier::fromString(vm, "code"_s), codeValue, 0); + error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0); + } + + *result = reinterpret_cast(JSC::JSValue::encode(error)); + return napi_ok; +} + +extern "C" napi_status napi_create_error(napi_env env, napi_value code, + napi_value msg, + napi_value* result) +{ + Zig::GlobalObject* globalObject = toJS(env); + JSC::VM& vm = globalObject->vm(); + + JSC::JSValue codeValue = JSC::JSValue::decode(reinterpret_cast(code)); + JSC::JSValue messageValue = JSC::JSValue::decode(reinterpret_cast(msg)); + + auto error = JSC::createError(globalObject, messageValue.toWTFString(globalObject)); + if (codeValue) { + error->putDirect(vm, WebCore::builtinNames(vm).codePublicName(), codeValue, 0); } *result = reinterpret_cast(JSC::JSValue::encode(error)); @@ -1474,7 +1492,8 @@ extern "C" napi_status napi_get_property_names(napi_env env, napi_value object, return napi_ok; } -extern "C" napi_status napi_create_object(napi_env env, napi_value* result){ +extern "C" napi_status napi_create_object(napi_env env, napi_value* result) +{ if (UNLIKELY(result == nullptr)) { return napi_invalid_arg; diff --git a/src/bun.js/bindings/webcore/JSCloseEvent.cpp b/src/bun.js/bindings/webcore/JSCloseEvent.cpp index be07cbcfe..ad7b6ed57 100644 --- a/src/bun.js/bindings/webcore/JSCloseEvent.cpp +++ b/src/bun.js/bindings/webcore/JSCloseEvent.cpp @@ -99,7 +99,7 @@ template<> CloseEvent::Init convertDictionary(JSGlobalObject& if (isNullOrUndefined) codeValue = jsUndefined(); else { - codeValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "code"_s)); + codeValue = object->get(&lexicalGlobalObject, WebCore::builtinNames(vm).codePublicName()); RETURN_IF_EXCEPTION(throwScope, {}); } if (!codeValue.isUndefined()) { diff --git a/src/bun.js/node/node_os.zig b/src/bun.js/node/node_os.zig index f71143315..483acb3e2 100644 --- a/src/bun.js/node/node_os.zig +++ b/src/bun.js/node/node_os.zig @@ -78,8 +78,8 @@ pub const Os = struct { return if (comptime Environment.isLinux) cpusImplLinux(globalThis) catch { const err = JSC.SystemError{ - .message = JSC.ZigString.init("Failed to get cpu information"), - .code = JSC.ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .message = bun.String.static("Failed to get cpu information"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); @@ -88,8 +88,8 @@ pub const Os = struct { else if (comptime Environment.isMac) cpusImplDarwin(globalThis) catch { const err = JSC.SystemError{ - .message = JSC.ZigString.init("Failed to get cpu information"), - .code = JSC.ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .message = bun.String.static("Failed to get cpu information"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); @@ -318,11 +318,11 @@ pub const Os = struct { //info.put(globalThis, JSC.ZigString.static("syscall"), JSC.ZigString.init("uv_os_getpriority").withEncoding().toValueGC(globalThis)); const err = JSC.SystemError{ - .message = JSC.ZigString.init("A system error occurred: uv_os_getpriority returned ESRCH (no such process)"), - .code = JSC.ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .message = bun.String.static("A system error occurred: uv_os_getpriority returned ESRCH (no such process)"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), //.info = info, .errno = -3, - .syscall = JSC.ZigString.init("uv_os_getpriority"), + .syscall = bun.String.static("uv_os_getpriority"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); @@ -377,10 +377,10 @@ pub const Os = struct { const rc = C.getifaddrs(&interface_start); if (rc != 0) { const err = JSC.SystemError{ - .message = JSC.ZigString.init("A system error occurred: getifaddrs returned an error"), - .code = JSC.ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .message = bun.String.static("A system error occurred: getifaddrs returned an error"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), .errno = @intFromEnum(std.os.errno(rc)), - .syscall = JSC.ZigString.init("getifaddrs"), + .syscall = bun.String.static("getifaddrs"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); @@ -591,11 +591,11 @@ pub const Os = struct { switch (errcode) { .SRCH => { const err = JSC.SystemError{ - .message = JSC.ZigString.init("A system error occurred: uv_os_setpriority returned ESRCH (no such process)"), - .code = JSC.ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .message = bun.String.static("A system error occurred: uv_os_setpriority returned ESRCH (no such process)"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), //.info = info, .errno = -3, - .syscall = JSC.ZigString.init("uv_os_setpriority"), + .syscall = bun.String.static("uv_os_setpriority"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); @@ -603,11 +603,11 @@ pub const Os = struct { }, .ACCES => { const err = JSC.SystemError{ - .message = JSC.ZigString.init("A system error occurred: uv_os_setpriority returned EACCESS (permission denied)"), - .code = JSC.ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), + .message = bun.String.static("A system error occurred: uv_os_setpriority returned EACCESS (permission denied)"), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_SYSTEM_ERROR))), //.info = info, .errno = -13, - .syscall = JSC.ZigString.init("uv_os_setpriority"), + .syscall = bun.String.static("uv_os_setpriority"), }; globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); diff --git a/src/bun.js/node/syscall.zig b/src/bun.js/node/syscall.zig index 48c5b1305..5ff0b2f44 100644 --- a/src/bun.js/node/syscall.zig +++ b/src/bun.js/node/syscall.zig @@ -873,20 +873,20 @@ pub const Error = struct { pub fn toSystemError(this: Error) SystemError { var err = SystemError{ .errno = @as(c_int, this.errno) * -1, - .syscall = JSC.ZigString.init(@tagName(this.syscall)), + .syscall = bun.String.static(@tagName(this.syscall)), }; // errno label if (this.errno > 0 and this.errno < C.SystemErrno.max) { const system_errno = @enumFromInt(C.SystemErrno, this.errno); - err.code = JSC.ZigString.init(@tagName(system_errno)); + err.code = bun.String.static(@tagName(system_errno)); if (C.SystemErrno.labels.get(system_errno)) |label| { - err.message = JSC.ZigString.init(label); + err.message = bun.String.static(label); } } if (this.path.len > 0) { - err.path = JSC.ZigString.init(this.path); + err.path = bun.String.create(this.path); } if (this.fd != -1) { diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index faf503a3f..86b5414e3 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -1194,9 +1194,6 @@ pub const Blob = struct { .syscall = .open, }).toSystemError(); - // assert we never end up reusing the memory - std.debug.assert(@intFromPtr(this.system_error.?.path.slice().ptr) != @intFromPtr(path_buffer)); - callback(this, null_fd); return; }; @@ -1359,12 +1356,13 @@ pub const Blob = struct { return; } else if (this.store == null) { bun.default_allocator.destroy(this); - cb(cb_ctx, ResultType{ .err = SystemError{ - .code = ZigString.init("INTERNAL_ERROR"), - .path = ZigString.Empty, - .message = ZigString.init("assertion failure - store should not be null"), - .syscall = ZigString.init("read"), - } }); + cb(cb_ctx, ResultType{ + .err = SystemError{ + .code = bun.String.static("INTERNAL_ERROR"), + .message = bun.String.static("assertion failure - store should not be null"), + .syscall = bun.String.static("read"), + }, + }); return; } @@ -1396,12 +1394,12 @@ pub const Blob = struct { }).toSystemError(); } else { this.system_error = JSC.SystemError{ - .code = ZigString.init(bun.asByteSlice(@errorName(err))), + .code = bun.String.static(bun.asByteSlice(@errorName(err))), .path = if (this.file_store.pathlike == .path) - ZigString.init(this.file_store.pathlike.path.slice()) + bun.String.create(this.file_store.pathlike.path.slice()) else - ZigString.Empty, - .syscall = ZigString.init("read"), + bun.String.empty, + .syscall = bun.String.static("read"), }; this.errno = err; @@ -1458,13 +1456,13 @@ pub const Blob = struct { if (std.os.S.ISDIR(stat.mode)) { this.errno = error.EISDIR; this.system_error = JSC.SystemError{ - .code = ZigString.init("EISDIR"), + .code = bun.String.static("EISDIR"), .path = if (this.file_store.pathlike == .path) - ZigString.init(this.file_store.pathlike.path.slice()) + bun.String.create(this.file_store.pathlike.path.slice()) else - ZigString.Empty, - .message = ZigString.init("Directories cannot be read like files"), - .syscall = ZigString.init("read"), + bun.String.empty, + .message = bun.String.static("Directories cannot be read like files"), + .syscall = bun.String.static("read"), }; return; } @@ -1643,8 +1641,8 @@ pub const Blob = struct { this.wrote += @truncate(SizeType, result catch |errno| { this.errno = errno; this.system_error = this.system_error orelse JSC.SystemError{ - .code = ZigString.init(bun.asByteSlice(@errorName(errno))), - .syscall = ZigString.init("write"), + .code = bun.String.static(bun.asByteSlice(@errorName(errno))), + .syscall = bun.String.static("write"), }; this.wrote = 0; @@ -1703,13 +1701,13 @@ pub const Blob = struct { const unsupported_directory_error = SystemError{ .errno = @intCast(c_int, @intFromEnum(bun.C.SystemErrno.EISDIR)), - .message = ZigString.init("That doesn't work on folders"), - .syscall = ZigString.init("fstat"), + .message = bun.String.static("That doesn't work on folders"), + .syscall = bun.String.static("fstat"), }; const unsupported_non_regular_file_error = SystemError{ .errno = @intCast(c_int, @intFromEnum(bun.C.SystemErrno.ENOTSUP)), - .message = ZigString.init("Non-regular files aren't supported yet"), - .syscall = ZigString.init("fstat"), + .message = bun.String.static("Non-regular files aren't supported yet"), + .syscall = bun.String.static("fstat"), }; // blocking, but off the main thread @@ -1777,13 +1775,12 @@ pub const Blob = struct { pub fn reject(this: *CopyFile, promise: *JSC.JSPromise) void { var globalThis = this.globalThis; var system_error: SystemError = this.system_error orelse SystemError{}; - if (this.source_file_store.pathlike == .path and system_error.path.len == 0) { - system_error.path = ZigString.init(this.source_file_store.pathlike.path.slice()); - system_error.path.mark(); + if (this.source_file_store.pathlike == .path and system_error.path.isEmpty()) { + system_error.path = bun.String.create(this.source_file_store.pathlike.path.slice()); } - if (system_error.message.len == 0) { - system_error.message = ZigString.init("Failed to copy file"); + if (system_error.message.isEmpty()) { + system_error.message = bun.String.static("Failed to copy file"); } var instance = system_error.toErrorInstance(this.globalThis); diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index b4ea08579..e888ffa5a 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -777,15 +777,15 @@ pub const Fetch = struct { } const fetch_error = JSC.SystemError{ - .code = ZigString.init(@errorName(this.result.fail)), + .code = bun.String.static(@errorName(this.result.fail)), .message = switch (this.result.fail) { - error.ConnectionClosed => ZigString.init("The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()"), - error.FailedToOpenSocket => ZigString.init("Was there a typo in the url or port?"), - error.TooManyRedirects => ZigString.init("The response redirected too many times. For more information, pass `verbose: true` in the second argument to fetch()"), - error.ConnectionRefused => ZigString.init("Unable to connect. Is the computer able to access the url?"), - else => ZigString.init("fetch() failed. For more information, pass `verbose: true` in the second argument to fetch()"), + error.ConnectionClosed => bun.String.static("The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()"), + error.FailedToOpenSocket => bun.String.static("Was there a typo in the url or port?"), + error.TooManyRedirects => bun.String.static("The response redirected too many times. For more information, pass `verbose: true` in the second argument to fetch()"), + error.ConnectionRefused => bun.String.static("Unable to connect. Is the computer able to access the url?"), + else => bun.String.static("fetch() failed. For more information, pass `verbose: true` in the second argument to fetch()"), }, - .path = ZigString.init(this.http.?.url.href), + .path = bun.String.create(this.http.?.url.href), }; return fetch_error.toErrorInstance(this.global_this); diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 5986afac7..343ce37ab 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -1964,10 +1964,10 @@ pub fn NewJSSink(comptime SinkType: type, comptime name_: []const u8) type { pub const message = std.fmt.comptimePrint("{s} is not constructable", .{SinkType.name}); }; const err = JSC.SystemError{ - .message = ZigString.init(Static.message), - .code = ZigString.init(@as(string, @tagName(JSC.Node.ErrorCode.ERR_ILLEGAL_CONSTRUCTOR))), + .message = bun.String.static(Static.message), + .code = bun.String.static(@as(string, @tagName(JSC.Node.ErrorCode.ERR_ILLEGAL_CONSTRUCTOR))), }; - globalThis.vm().throwError(globalThis, err.toErrorInstance(globalThis)); + globalThis.throwValue(err.toErrorInstance(globalThis)); return JSC.JSValue.jsUndefined(); } diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 814e49a20..f534e4184 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -9174,8 +9174,10 @@ const LinkerContext = struct { }, )) { .err => |err| { + var message = err.toSystemError().message.toUTF8(bun.default_allocator); + defer message.deinit(); c.log.addErrorFmt(null, Logger.Loc.Empty, bun.default_allocator, "{} writing sourcemap for chunk {}", .{ - bun.fmt.quote(err.toSystemError().message.slice()), + bun.fmt.quote(message.slice()), bun.fmt.quote(chunk.final_rel_path), }) catch unreachable; return error.WriteFailed; @@ -9242,8 +9244,10 @@ const LinkerContext = struct { }, )) { .err => |err| { + var message = err.toSystemError().message.toUTF8(bun.default_allocator); + defer message.deinit(); c.log.addErrorFmt(null, Logger.Loc.Empty, bun.default_allocator, "{} writing chunk {}", .{ - bun.fmt.quote(err.toSystemError().message.slice()), + bun.fmt.quote(message.slice()), bun.fmt.quote(chunk.final_rel_path), }) catch unreachable; return error.WriteFailed; @@ -9309,8 +9313,10 @@ const LinkerContext = struct { }, )) { .err => |err| { + const utf8 = err.toSystemError().message.toUTF8(bun.default_allocator); + defer utf8.deinit(); c.log.addErrorFmt(null, Logger.Loc.Empty, bun.default_allocator, "{} writing chunk {}", .{ - bun.fmt.quote(err.toSystemError().message.slice()), + bun.fmt.quote(utf8.slice()), bun.fmt.quote(components_manifest_path), }) catch unreachable; return error.WriteFailed; @@ -9383,8 +9389,10 @@ const LinkerContext = struct { }, )) { .err => |err| { + const utf8 = err.toSystemError().message.toUTF8(bun.default_allocator); + defer utf8.deinit(); c.log.addErrorFmt(null, Logger.Loc.Empty, bun.default_allocator, "{} writing file {}", .{ - bun.fmt.quote(err.toSystemError().message.slice()), + bun.fmt.quote(utf8.slice()), bun.fmt.quote(src.src_path.text), }) catch unreachable; return error.WriteFailed; diff --git a/src/http.zig b/src/http.zig index 80718db2f..b1d97c382 100644 --- a/src/http.zig +++ b/src/http.zig @@ -684,8 +684,9 @@ pub const RequestContext = struct { if (erro == error.EBADF or erro == error.ECONNABORTED or erro == error.ECONNREFUSED) { return error.SocketClosed; } - - Output.prettyErrorln("send() error: {s}", .{err.toSystemError().message.slice()}); + const msg = err.toSystemError().message.toUTF8(bun.default_allocator); + defer msg.deinit(); + Output.prettyErrorln("send() error: {s}", .{msg.slice()}); return erro; }, diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 0973ca559..439319489 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -303,16 +303,7 @@ pub export fn napi_create_string_utf16(env: napi_env, str: [*]const char16_t, le return .ok; } pub extern fn napi_create_symbol(env: napi_env, description: napi_value, result: *napi_value) napi_status; -pub export fn napi_create_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status { - log("napi_create_error: \"{any}\"", .{msg.getZigString(env)}); - const system_error = JSC.SystemError{ - .code = if (!code.isEmptyOrUndefinedOrNull()) code.getZigString(env) else ZigString.Empty, - .message = msg.getZigString(env), - }; - result.* = system_error.toErrorInstance(env); - return .ok; -} - +pub extern fn napi_create_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; pub extern fn napi_create_type_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; pub extern fn napi_create_range_error(env: napi_env, code: napi_value, msg: napi_value, result: *napi_value) napi_status; pub extern fn napi_typeof(env: napi_env, value: napi_value, result: *napi_valuetype) napi_status; diff --git a/test/js/bun/util/error-gc-test.test.js b/test/js/bun/util/error-gc-test.test.js index 247bd68ef..4a45346b6 100644 --- a/test/js/bun/util/error-gc-test.test.js +++ b/test/js/bun/util/error-gc-test.test.js @@ -1,5 +1,5 @@ import { test, expect } from "bun:test"; - +import { readFileSync } from "fs"; // This test checks that printing stack traces increments and decrements // reference-counted strings test("error gc test", () => { @@ -34,7 +34,7 @@ test("error gc test #2", () => { } }); -test("error gc test #2", () => { +test("error gc test #3", () => { for (let i = 0; i < 1000; i++) { var err = new Error(); Error.captureStackTrace(err); @@ -42,3 +42,40 @@ test("error gc test #2", () => { Bun.gc(); } }); + +// This test fails if: +// - it crashes +// - The test failure message gets a non-sensical error +test("error gc test #4", () => { + for (let i = 0; i < 1000; i++) { + let path = + // Use a long-enough string for it to be obvious if we leak memory + "/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/ii/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/ii/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i/don/t/exist/tmp/i"; + try { + readFileSync(path); + throw new Error("unreachable"); + } catch (e) { + if (e.message === "unreachable") { + throw e; + } + + const inspected = Bun.inspect(e); + Bun.gc(true); + + // Deliberately avoid using .toContain() directly to avoid + // BunString shenanigins. + // + // Only JSC builtin functions to operate on the string after inspecting it. + // + if (!inspected.includes(path)) { + expect(inspected).toContain(path); + } + + if (!inspected.includes("ENOENT")) { + expect(inspected).toContain("ENOENT"); + } + } finally { + Bun.gc(true); + } + } +}); -- cgit v1.2.3