diff --git a/src/bun.js/api/BunObject.zig b/src/bun.js/api/BunObject.zig index 2107cae783..d6ec510af9 100644 --- a/src/bun.js/api/BunObject.zig +++ b/src/bun.js/api/BunObject.zig @@ -1408,11 +1408,7 @@ pub fn indexOfLine(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) c continue; } - if (byte == '\r') { - if (i + 1 < bytes.len and bytes[i + 1] == '\n') { - return JSC.JSValue.jsNumber(i + 1); - } - } else if (byte == '\n') { + if (byte == '\n') { return JSC.JSValue.jsNumber(i); } diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index 9e1c3cd37e..3dd2291cf5 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -679,8 +679,8 @@ pub const Subprocess = struct { sig = arguments.ptr[0].coerce(i32, globalThis); } - if (!(sig > -1 and sig < std.math.maxInt(u8))) { - globalThis.throwInvalidArguments("Invalid signal: must be > -1 and < 255", .{}); + if (!(sig >= 0 and sig <= std.math.maxInt(u8))) { + globalThis.throwInvalidArguments("Invalid signal: must be >= 0 and <= 255", .{}); return .zero; } @@ -730,8 +730,13 @@ pub const Subprocess = struct { } if (comptime Environment.isWindows) { if (std.os.windows.kernel32.TerminateProcess(this.pid.process_handle, @intCast(sig)) == 0) { - const err = @as(bun.C.E, @enumFromInt(@intFromEnum(bun.windows.GetLastError()))); - if (err != .SRCH) + const err = bun.windows.getLastErrno(); + // if the process was already killed don't throw + // + // "After a process has terminated, call to TerminateProcess with open + // handles to the process fails with ERROR_ACCESS_DENIED (5) error code." + // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminateprocess + if (err != .PERM) return .{ .err = bun.sys.Error.fromCode(err, .kill) }; } diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index fb1ca16b0d..a1db89e25a 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -1483,6 +1483,8 @@ pub fn NewFileSink(comptime EventLoop: JSC.EventLoopKind) type { while (remain.len > 0) { const write_buf = remain[0..@min(remain.len, max_to_write)]; const res = bun.sys.write(fd, write_buf); + // this does not fix the issue with writes not showing up + // const res = bun.sys.sys_uv.write(fd, write_buf); if (res == .err) { const retry = @@ -4541,7 +4543,7 @@ pub const File = struct { var fd = if (file.pathlike != .path) // We will always need to close the file descriptor. switch (Syscall.dup(file.pathlike.fd)) { - .result => |_fd| _fd, + .result => |_fd| if (Environment.isWindows) bun.toLibUVOwnedFD(_fd) else _fd, .err => |err| { return .{ .err = err.withFd(file.pathlike.fd) }; }, @@ -4563,28 +4565,26 @@ pub const File = struct { } if (file.pathlike != .path and !(file.is_atty orelse false)) { - if (comptime Environment.isWindows) { - @panic("TODO on Windows"); - } + if (comptime !Environment.isWindows) { + // ensure we have non-blocking IO set + switch (Syscall.fcntl(fd, std.os.F.GETFL, 0)) { + .err => return .{ .err = Syscall.Error.fromCode(E.BADF, .fcntl) }, + .result => |flags| { + // if we do not, clone the descriptor and set non-blocking + // it is important for us to clone it so we don't cause Weird Things to happen + if ((flags & std.os.O.NONBLOCK) == 0) { + fd = switch (Syscall.fcntl(fd, std.os.F.DUPFD, 0)) { + .result => |_fd| bun.toFD(_fd), + .err => |err| return .{ .err = err }, + }; - // ensure we have non-blocking IO set - switch (Syscall.fcntl(fd, std.os.F.GETFL, 0)) { - .err => return .{ .err = Syscall.Error.fromCode(E.BADF, .fcntl) }, - .result => |flags| { - // if we do not, clone the descriptor and set non-blocking - // it is important for us to clone it so we don't cause Weird Things to happen - if ((flags & std.os.O.NONBLOCK) == 0) { - fd = switch (Syscall.fcntl(fd, std.os.F.DUPFD, 0)) { - .result => |_fd| bun.toFD(_fd), - .err => |err| return .{ .err = err }, - }; - - switch (Syscall.fcntl(fd, std.os.F.SETFL, flags | std.os.O.NONBLOCK)) { - .err => |err| return .{ .err = err }, - .result => |_| {}, + switch (Syscall.fcntl(fd, std.os.F.SETFL, flags | std.os.O.NONBLOCK)) { + .err => |err| return .{ .err = err }, + .result => |_| {}, + } } - } - }, + }, + } } } var size: Blob.SizeType = 0; @@ -4609,6 +4609,11 @@ pub const File = struct { file.seekable = this.seekable; size = @intCast(stat.size); } else if (comptime Environment.isWindows) outer: { + // without this check the getEndPos call fails unpredictably + if (bun.windows.GetFileType(fd.cast()) != bun.windows.FILE_TYPE_DISK) { + this.seekable = false; + break :outer; + } size = @intCast(fd.asFile().getEndPos() catch { this.seekable = false; break :outer; diff --git a/src/js/builtins/ConsoleObject.ts b/src/js/builtins/ConsoleObject.ts index e0812e10e1..c3ad5610f3 100644 --- a/src/js/builtins/ConsoleObject.ts +++ b/src/js/builtins/ConsoleObject.ts @@ -38,7 +38,12 @@ export function asyncIterator(this: Console) { // TODO: "\r", 0x4048, 0x4049, 0x404A, 0x404B, 0x404C, 0x404D, 0x404E, 0x404F i = indexOf(actualChunk, last); while (i !== -1) { - yield decoder.decode(actualChunk.subarray(last, i)); + yield decoder.decode( + actualChunk.subarray( + last, + process.platform === "win32" ? (actualChunk[i - 1] === 0x0d /* \r */ ? i - 1 : i) : i, + ), + ); last = i + 1; i = indexOf(actualChunk, last); } @@ -78,7 +83,12 @@ export function asyncIterator(this: Console) { while (i !== -1) { // This yield may end the function, in that case we need to be able to recover state // if the iterator was fired up again. - yield decoder.decode(actualChunk.subarray(last, i)); + yield decoder.decode( + actualChunk.subarray( + last, + process.platform === "win32" ? (actualChunk[i - 1] === 0x0d /* \r */ ? i - 1 : i) : i, + ), + ); last = i + 1; i = indexOf(actualChunk, last); } diff --git a/src/sys.zig b/src/sys.zig index c16bfd9aab..99c3274dca 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -750,6 +750,7 @@ pub fn write(fd: bun.FileDescriptor, bytes: []const u8) Maybe(usize) { &bytes_written, null, ); + log("WriteFile({d}, {d}) = {d} (written: {d})", .{ @intFromPtr(fd.cast()), adjusted_len, rc, bytes_written }); if (rc == 0) { return .{ .err = Syscall.Error{ @@ -1751,13 +1752,13 @@ pub fn pipe() Maybe([2]bun.FileDescriptor) { pub fn dup(fd: bun.FileDescriptor) Maybe(bun.FileDescriptor) { if (comptime Environment.isWindows) { - const target: *windows.HANDLE = undefined; + var target: windows.HANDLE = undefined; const process = kernel32.GetCurrentProcess(); const out = kernel32.DuplicateHandle( process, fd.cast(), process, - target, + &target, 0, w.TRUE, w.DUPLICATE_SAME_ACCESS, @@ -1767,7 +1768,7 @@ pub fn dup(fd: bun.FileDescriptor) Maybe(bun.FileDescriptor) { return err; } } - return Maybe(bun.FileDescriptor){ .result = bun.toFD(out) }; + return Maybe(bun.FileDescriptor){ .result = bun.toFD(target) }; } const out = std.c.dup(fd.cast()); diff --git a/src/windows.zig b/src/windows.zig index 5e6f6adf28..38d5801c17 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -76,6 +76,17 @@ pub extern fn CommandLineToArgvW( pNumArgs: *c_int, ) [*]win32.LPWSTR; +pub extern fn GetFileType( + hFile: win32.HANDLE, +) callconv(windows.WINAPI) win32.DWORD; + +/// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfiletype#return-value +pub const FILE_TYPE_UNKNOWN = 0x0000; +pub const FILE_TYPE_DISK = 0x0001; +pub const FILE_TYPE_CHAR = 0x0002; +pub const FILE_TYPE_PIPE = 0x0003; +pub const FILE_TYPE_REMOTE = 0x8000; + pub const LPDWORD = *win32.DWORD; pub extern "kernel32" fn GetBinaryTypeW(