diff --git a/CMakeLists.txt b/CMakeLists.txt index b2c423ddda..73813a0d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1230,17 +1230,15 @@ else() target_link_libraries(${bun} PRIVATE base64::base64) endif() -if(NOT WIN32) - if(USE_CUSTOM_TINYCC) - if(WIN32) - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/tcc.lib") - else() - target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libtcc.a") - endif() +if(USE_CUSTOM_TINYCC) + if(WIN32) + target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/tcc.lib") else() - find_package(tinycc REQUIRED) - target_link_libraries(${bun} PRIVATE tinycc::tinycc) + target_link_libraries(${bun} PRIVATE "${BUN_DEPS_OUT_DIR}/libtcc.a") endif() +else() + find_package(tinycc REQUIRED) + target_link_libraries(${bun} PRIVATE tinycc::tinycc) endif() if(USE_CUSTOM_LOLHTML) diff --git a/scripts/all-dependencies.ps1 b/scripts/all-dependencies.ps1 index 67ddb6ef94..f4426e4709 100644 --- a/scripts/all-dependencies.ps1 +++ b/scripts/all-dependencies.ps1 @@ -65,9 +65,9 @@ Build-Dependency ` Build-Dependency ` -Script "mimalloc" ` -Outputs @("mimalloc.lib") -# Build-Dependency ` -# -Script "tinycc" ` -# -Outputs @("tcc.lib") +Build-Dependency ` + -Script "tinycc" ` + -Outputs @("tcc.lib") Build-Dependency ` -Script "zlib" ` -Outputs @("zlib.lib") diff --git a/scripts/build-tinycc.ps1 b/scripts/build-tinycc.ps1 new file mode 100644 index 0000000000..a671be2ddf --- /dev/null +++ b/scripts/build-tinycc.ps1 @@ -0,0 +1,30 @@ +$ErrorActionPreference = 'Stop' # Setting strict mode, similar to 'set -euo pipefail' in bash +. (Join-Path $PSScriptRoot "env.ps1") + +Push-Location (Join-Path $BUN_DEPS_DIR 'tinycc') +try { + cd win32 + Run .\build-tcc.bat -clean + cd .. + + Set-Content -Path config.h -Value @" +#define TCC_VERSION "$(Get-Content VERSION)" +#define TCC_GITHASH "$(git rev-parse --short HEAD)" +#define CONFIG_TCCDIR "$((Get-Location).Path.Replace('\', '/'))" +#define CONFIG_TCC_PREDEFS 1 +#ifdef TCC_TARGET_X86_64 +#define CONFIG_TCC_CROSSPREFIX "$PX%-" +#endif +"@ + + Run clang-cl -DTCC_TARGET_PE -DTCC_TARGET_X86_64 config.h -DC2STR -o c2str.exe conftest.c + Run .\c2str.exe .\include\tccdefs.h tccdefs_.h + + # TODO: -MT + Run clang-cl libtcc.c -o tcc.obj "-DTCC_TARGET_PE" "-DTCC_TARGET_X86_64" "-O2" "-W2" "-Zi" "-MD" "-GS-" "-c" + Run lib "tcc.obj" "-OUT:tcc.lib" + + Copy-Item tcc.obj $BUN_DEPS_OUT_DIR/tcc.lib + + Write-Host "-> tcc.lib" +} finally { Pop-Location } diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit index b4de09f41b..347037014a 160000 --- a/src/bun.js/WebKit +++ b/src/bun.js/WebKit @@ -1 +1 @@ -Subproject commit b4de09f41b83e9e5c0e43ef414f1aee5968b6f7c +Subproject commit 347037014ae069eed1c4f4687001a256949b124e diff --git a/src/bun.js/api/FFI.h b/src/bun.js/api/FFI.h index dfe6395106..effe7dc13a 100644 --- a/src/bun.js/api/FFI.h +++ b/src/bun.js/api/FFI.h @@ -1,11 +1,11 @@ // This file is part of Bun! // You can find the original source: -// https://github.com/oven-sh/bun/blob/main/src/bun.js/api/FFI.h#L2 +// https://github.com/oven-sh/bun/blob/main/src/bun.js/api/FFI.h // // clang-format off // This file is only compatible with 64 bit CPUs // It must be kept in sync with JSCJSValue.h -// https://github.com/oven-sh/WebKit/blob/72c2052b781cbfd4af867ae79ac9de460e392fba/Source/JavaScriptCore/runtime/JSCJSValue.h#L455-L458 +// https://github.com/oven-sh/WebKit/blob/main/Source/JavaScriptCore/runtime/JSCJSValue.h #ifdef IS_CALLBACK #define INJECT_BEFORE int c = 500; // This is a callback, so we need to inject code before the call #endif diff --git a/src/bun.js/api/ffi.zig b/src/bun.js/api/ffi.zig index ad38445d53..44b0ac3827 100644 --- a/src/bun.js/api/ffi.zig +++ b/src/bun.js/api/ffi.zig @@ -670,9 +670,7 @@ pub const FFI = struct { val.arg_types.clearAndFree(allocator); if (val.state) |state| { - if (comptime !Environment.isWindows) { - TCC.tcc_delete(state); - } + TCC.tcc_delete(state); val.state = null; } @@ -772,9 +770,6 @@ pub const FFI = struct { this: *Function, allocator: std.mem.Allocator, ) !void { - if (comptime Environment.isWindows) { - return; - } var source_code = std.ArrayList(u8).init(allocator); var source_code_writer = source_code.writer(); try this.printSourceCode(&source_code_writer); @@ -789,9 +784,7 @@ pub const FFI = struct { this.state = state; defer { if (this.step == .failed) { - if (comptime !Environment.isWindows) { - TCC.tcc_delete(state); - } + TCC.tcc_delete(state); this.state = null; } } @@ -900,9 +893,6 @@ pub const FFI = struct { } pub fn inject(state: *TCC.TCCState) void { - if (comptime Environment.isWindows) { - return; - } JSC.markBinding(@src()); _ = TCC.tcc_add_symbol(state, "memset", &memset); _ = TCC.tcc_add_symbol(state, "memcpy", &memcpy); @@ -943,9 +933,6 @@ pub const FFI = struct { js_function: JSValue, is_threadsafe: bool, ) !void { - if (comptime Environment.isWindows) { - return; - } JSC.markBinding(@src()); var source_code = std.ArrayList(u8).init(allocator); var source_code_writer = source_code.writer(); @@ -969,9 +956,7 @@ pub const FFI = struct { this.state = state; defer { if (this.step == .failed) { - if (comptime !Environment.isWindows) { - TCC.tcc_delete(state); - } + TCC.tcc_delete(state); this.state = null; } } @@ -1256,22 +1241,7 @@ pub const FFI = struct { // -- Generate the FFI function symbol try writer.writeAll("\n \n/* --- The Callback Function */\n"); - try writer.writeAll("/* --- The Callback Function */\n"); - try this.return_type.typename(writer); - try writer.writeAll(" my_callback_function"); - try writer.writeAll("("); var first = true; - for (this.arg_types.items, 0..) |arg, i| { - if (!first) { - try writer.writeAll(", "); - } - first = false; - try arg.typename(writer); - try writer.print(" arg{d}", .{i}); - } - try writer.writeAll(");\n\n"); - - first = true; try this.return_type.typename(writer); try writer.writeAll(" my_callback_function"); diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index c785163470..55e75cc09a 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -5226,43 +5226,6 @@ pub const NodeFS = struct { _ = bun.sys.close(fd); } - if (Environment.isWindows) { - const native_fd = bun.fdcast(fd); - - var data = args.data.slice(); - - // "WriteFile sets this value to zero before doing any work or error checking." - var bytes_written: u32 = undefined; - - while (data.len > 0) { - const adjusted_len = @min(data.len, std.math.maxInt(u32)); - const rc = std.os.windows.kernel32.WriteFile(native_fd, data.ptr, adjusted_len, &bytes_written, null); - if (rc == 0) { - return .{ - .err = Syscall.Error{ - .errno = @intFromEnum(std.os.windows.kernel32.GetLastError()), - .syscall = .WriteFile, - .fd = fd, - }, - }; - } - data = data[bytes_written..]; - } - - const rc = std.os.windows.kernel32.SetEndOfFile(bun.fdcast(fd)); - if (rc == 0) { - return .{ - .err = Syscall.Error{ - .errno = @intFromEnum(std.os.windows.kernel32.GetLastError()), - .syscall = .WriteFile, - .fd = fd, - }, - }; - } - - return Maybe(Return.WriteFile).success; - } - var buf = args.data.slice(); var written: usize = 0; @@ -5298,7 +5261,7 @@ pub const NodeFS = struct { } while (buf.len > 0) { - switch (Syscall.write(fd, buf)) { + switch (bun.sys.write(fd, buf)) { .err => |err| return .{ .err = err, }, @@ -5312,9 +5275,22 @@ pub const NodeFS = struct { } } - // https://github.com/oven-sh/bun/issues/2931 - if ((@intFromEnum(args.flag) & std.os.O.APPEND) == 0) { - _ = ftruncateSync(.{ .fd = fd, .len = @as(JSC.WebCore.Blob.SizeType, @truncate(written)) }); + if (Environment.isWindows) { + const rc = std.os.windows.kernel32.SetEndOfFile(bun.fdcast(fd)); + if (rc == 0) { + return .{ + .err = Syscall.Error{ + .errno = @intFromEnum(std.os.windows.kernel32.GetLastError()), + .syscall = .SetEndOfFile, + .fd = fd, + }, + }; + } + } else { + // https://github.com/oven-sh/bun/issues/2931 + if ((@intFromEnum(args.flag) & std.os.O.APPEND) == 0) { + _ = ftruncateSync(.{ .fd = fd, .len = @as(JSC.WebCore.Blob.SizeType, @truncate(written)) }); + } } return Maybe(Return.WriteFile).success; diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index bab81a6ca5..e9e5808f5d 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -1192,18 +1192,23 @@ pub const Blob = struct { var file_path: [bun.MAX_PATH_BYTES]u8 = undefined; switch (bun.sys.open( pathlike.path.sliceZ(&file_path), - // we deliberately don't use O_TRUNC here - // it's a perf optimization - std.os.O.WRONLY | std.os.O.CREAT | std.os.O.NONBLOCK, + if (!Environment.isWindows) + // we deliberately don't use O_TRUNC here + // it's a perf optimization + std.os.O.WRONLY | std.os.O.CREAT | std.os.O.NONBLOCK + else + std.os.O.WRONLY | std.os.O.CREAT, write_permissions, )) { .result => |result| { break :brk result; }, .err => |err| { - if (err.getErrno() == .NOENT) { - needs_async.* = true; - return .zero; + if (!Environment.isWindows) { + if (err.getErrno() == .NOENT) { + needs_async.* = true; + return .zero; + } } return JSC.JSPromise.rejectedPromiseValue( @@ -1212,16 +1217,13 @@ pub const Blob = struct { ); }, } - unreachable; }; - var truncate = needs_open or bytes.len == 0; + // TODO: on windows this is always synchronous + + const truncate = needs_open or bytes.len == 0; var written: usize = 0; defer { - if (truncate) { - _ = bun.sys.ftruncate(fd, @as(i64, @intCast(written))); - } - if (needs_open) { _ = bun.sys.close(fd); } @@ -1239,13 +1241,17 @@ pub const Blob = struct { if (res == 0) break; }, .err => |err| { - truncate = false; - if (err.getErrno() == .AGAIN) { - needs_async.* = true; - return .zero; + if (!Environment.isWindows) { + if (err.getErrno() == .AGAIN) { + needs_async.* = true; + return .zero; + } } if (comptime !needs_open) { - return JSC.JSPromise.rejectedPromiseValue(globalThis, err.toJSC(globalThis)); + return JSC.JSPromise.rejectedPromiseValue( + globalThis, + err.toJSC(globalThis), + ); } return JSC.JSPromise.rejectedPromiseValue( globalThis, @@ -1255,6 +1261,14 @@ pub const Blob = struct { } } + if (truncate) { + if (Environment.isWindows) { + _ = std.os.windows.kernel32.SetEndOfFile(bun.fdcast(fd)); + } else { + _ = bun.sys.ftruncate(fd, @as(i64, @intCast(written))); + } + } + return JSC.JSPromise.resolvedPromiseValue(globalThis, JSC.JSValue.jsNumber(written)); } diff --git a/src/deps/tinycc b/src/deps/tinycc index 2d3ad9e0d3..ab631362d8 160000 --- a/src/deps/tinycc +++ b/src/deps/tinycc @@ -1 +1 @@ -Subproject commit 2d3ad9e0d32194ad7fd867b66ebe218dcc8cb5cd +Subproject commit ab631362d839333660a265d3084d8ff060b96753 diff --git a/src/sys.zig b/src/sys.zig index 14bd23b046..d49ca15687 100644 --- a/src/sys.zig +++ b/src/sys.zig @@ -132,6 +132,7 @@ pub const Tag = enum(u8) { GetFinalPathNameByHandle, CloseHandle, SetFilePointerEx, + SetEndOfFile, pub fn isWindows(this: Tag) bool { return @intFromEnum(this) > @intFromEnum(Tag.WriteFile); @@ -681,6 +682,7 @@ pub fn closeAllowingStdoutAndStderr(fd: bun.FileDescriptor) ?Syscall.Error { pub const max_count = switch (builtin.os.tag) { .linux => 0x7ffff000, .macos, .ios, .watchos, .tvos => std.math.maxInt(i32), + .windows => std.math.maxInt(u32), else => std.math.maxInt(isize), }; @@ -696,7 +698,7 @@ pub fn write(fd: bun.FileDescriptor, bytes: []const u8) Maybe(usize) { return err; } - return Maybe(usize){ .result = @as(usize, @intCast(rc)) }; + return Maybe(usize){ .result = @intCast(rc) }; }, .linux => { while (true) { @@ -708,10 +710,31 @@ pub fn write(fd: bun.FileDescriptor, bytes: []const u8) Maybe(usize) { return err; } - return Maybe(usize){ .result = @as(usize, @intCast(rc)) }; + return Maybe(usize){ .result = @intCast(rc) }; } }, - .windows => sys_uv.write(fd, bytes), + .windows => { + // "WriteFile sets this value to zero before doing any work or error checking." + var bytes_written: u32 = undefined; + std.debug.assert(bytes.len > 0); + const rc = std.os.windows.kernel32.WriteFile( + bun.fdcast(fd), + bytes.ptr, + adjusted_len, + &bytes_written, + null, + ); + if (rc == 0) { + return .{ + .err = Syscall.Error{ + .errno = @intFromEnum(bun.windows.getLastErrno()), + .syscall = .WriteFile, + .fd = fd, + }, + }; + } + return Maybe(usize){ .result = bytes_written }; + }, else => @compileError("Not implemented yet"), }; } diff --git a/src/sys_uv.zig b/src/sys_uv.zig index c319c7f3c5..d4bd93f004 100644 --- a/src/sys_uv.zig +++ b/src/sys_uv.zig @@ -354,7 +354,7 @@ pub fn pwritev(fd: FileDescriptor, bufs: []const bun.PlatformIOVec, position: i6 } if (rc.errno()) |errno| { - return .{ .err = .{ .errno = errno, .fd = fd, .syscall = .read } }; + return .{ .err = .{ .errno = errno, .fd = fd, .syscall = .write } }; } else { return .{ .result = @as(usize, @intCast(rc.value)) }; } diff --git a/src/windows.zig b/src/windows.zig index da8d69c0ab..857b935564 100644 --- a/src/windows.zig +++ b/src/windows.zig @@ -2968,3 +2968,24 @@ pub extern "kernel32" fn SetFileInformationByHandle( fileInformation: LPVOID, bufferSize: DWORD, ) BOOL; + +pub fn getLastErrno() bun.C.E { + return switch (bun.windows.kernel32.GetLastError()) { + .SUCCESS => .SUCCESS, + .FILE_NOT_FOUND => .NOENT, + .PATH_NOT_FOUND => .NOENT, + .TOO_MANY_OPEN_FILES => .NOMEM, + .ACCESS_DENIED => .PERM, + .INVALID_HANDLE => .BADF, + .NOT_ENOUGH_MEMORY => .NOMEM, + .OUTOFMEMORY => .NOMEM, + .INVALID_PARAMETER => .INVAL, + + else => |t| { + if (bun.Environment.isDebug) { + bun.Output.warn("Called getLastErrno with {s} which does not have a mapping to errno", .{@tagName(t)}); + } + return .UNKNOWN; + }, + }; +}