Compare commits

...

6 Commits

Author SHA1 Message Date
Jarred Sumner
9e59e40abc Try without SegmentHeap 2025-07-30 22:57:15 -07:00
Jarred Sumner
5a72185685 Fix 2025-07-30 22:42:48 -07:00
autofix-ci[bot]
94fa2020c1 [autofix.ci] apply automated fixes 2025-07-31 05:39:07 +00:00
Jarred Sumner
ab420e1f9c Reduce stack space utilization on Windows 2025-07-30 22:35:54 -07:00
Jarred Sumner
e12e23fc55 Add exceptions 2025-07-30 22:34:16 -07:00
Jarred Sumner
c3f02df42f Add a way to voluntarily crash on stack overflow in CI 2025-07-30 20:56:15 -07:00
17 changed files with 247 additions and 192 deletions

View File

@@ -263,15 +263,18 @@ function getTestExpectations() {
return expectations;
}
const skipArray = (() => {
const path = join(cwd, "test/no-validate-exceptions.txt");
function normalizeFilePathTextFile(path) {
if (!existsSync(path)) {
return [];
}
return readFileSync(path, "utf-8")
.split("\n")
.filter(line => !line.startsWith("#") && line.length > 0);
})();
.filter(line => !line.startsWith("#") && line.length > 0)
.map(line => line.replace(/\\/g, "/").trim());
}
const skipArray = normalizeFilePathTextFile(join(cwd, "test/no-validate-exceptions.txt"));
const noStackOverflowArray = normalizeFilePathTextFile(join(cwd, "test/stackoverflow-tests.txt"));
/**
* Returns whether we should validate exception checks running the given test
@@ -1026,7 +1029,7 @@ function getCombinedPath(execPath) {
* @param {SpawnOptions} options
* @returns {Promise<SpawnBunResult>}
*/
async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr, testPath }) {
const path = getCombinedPath(execPath);
const tmpdirPath = mkdtempSync(join(tmpdir(), "buntmp-"));
const { username, homedir } = userInfo();
@@ -1040,6 +1043,7 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
SHELL: shellPath,
FORCE_COLOR: "1",
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "1",
BUN_FEATURE_FLAG_CRASH_ON_STACK_OVERFLOW: "1",
BUN_DEBUG_QUIET_LOGS: "1",
BUN_GARBAGE_COLLECTOR_LEVEL: "1",
BUN_JSC_randomIntegrityAuditRate: "1.0",
@@ -1052,6 +1056,12 @@ async function spawnBun(execPath, { args, cwd, timeout, env, stdout, stderr }) {
: { BUN_ENABLE_CRASH_REPORTING: "0" }),
};
if (testPath) {
if (noStackOverflowArray.includes(testPath.replaceAll("\\", "/"))) {
delete bunEnv.BUN_FEATURE_FLAG_CRASH_ON_STACK_OVERFLOW;
}
}
if (basename(execPath).includes("asan")) {
bunEnv.ASAN_OPTIONS = "allow_user_segv_handler=1:disable_coredump=0";
}
@@ -1250,6 +1260,7 @@ async function spawnBunTest(execPath, testPath, options = { cwd }) {
}
const { ok, error, stdout, crashes } = await spawnBun(execPath, {
testPath,
args: isReallyTest ? testArgs : [...args, absPath],
cwd: options["cwd"],
timeout: isReallyTest ? timeout : 30_000,

View File

@@ -4,7 +4,6 @@
<asmv3:application>
<asmv3:windowsSettings>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
<heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

View File

@@ -141,6 +141,35 @@ pub const Run = struct {
return bun.shell.Interpreter.initAndRunFromFile(ctx, mini, entry_path);
}
fn resolveStdinEntryPath(ctx: Command.Context) !string {
const trigger = bun.pathLiteral("/[stdin]");
var entry_point_buf: [bun.MAX_PATH_BYTES + trigger.len]u8 = undefined;
const cwd = try std.posix.getcwd(&entry_point_buf);
@memcpy(entry_point_buf[cwd.len..][0..trigger.len], trigger);
const entry_path = entry_point_buf[0 .. cwd.len + trigger.len];
var passthrough_list = try std.ArrayList(string).initCapacity(ctx.allocator, ctx.passthrough.len + 1);
passthrough_list.appendAssumeCapacity("-");
passthrough_list.appendSliceAssumeCapacity(ctx.passthrough);
ctx.passthrough = passthrough_list.items;
return try bun.default_allocator.dupe(u8, entry_path);
}
pub fn bootFromStdin(ctx: Command.Context) !void {
var stdin_allocator = std.heap.stackFallback(2048, bun.default_allocator);
var script = std.ArrayList(u8).init(stdin_allocator.get());
_ = try bun.sys.File.readToEndWithArrayList(
.{ .handle = bun.FD.stdin() },
&script,
.{ .probably_small = false, .allow_pread = false },
).unwrap();
ctx.runtime_options.eval.script = script.items;
try Run.boot(ctx, try resolveStdinEntryPath(ctx), null);
}
pub fn boot(ctx: Command.Context, entry_path: string, loader: ?bun.options.Loader) !void {
jsc.markBinding(@src());

View File

@@ -4,6 +4,12 @@ pub const JSGlobalObject = opaque {
}
extern fn JSGlobalObject__throwStackOverflow(this: *JSGlobalObject) void;
pub fn throwStackOverflow(this: *JSGlobalObject) void {
if (comptime bun.Environment.is_canary) {
if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_CRASH_ON_STACK_OVERFLOW)) {
@panic("Stack overflow. If this was intentional, update test/stackoverflow-tests.txt");
}
}
JSGlobalObject__throwStackOverflow(this);
}
extern fn JSGlobalObject__throwOutOfMemoryError(this: *JSGlobalObject) void;

View File

@@ -64,7 +64,7 @@ fn cpusImplLinux(globalThis: *jsc.JSGlobalObject) !jsc.JSValue {
const file = try std.fs.openFileAbsolute("/proc/stat", .{});
defer file.close();
const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap();
const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, .{ .probably_small = true }).unwrap();
defer file_buf.clearRetainingCapacity();
const contents = file_buf.items[0..read];
@@ -104,7 +104,7 @@ fn cpusImplLinux(globalThis: *jsc.JSGlobalObject) !jsc.JSValue {
if (std.fs.openFileAbsolute("/proc/cpuinfo", .{})) |file| {
defer file.close();
const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap();
const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, .{ .probably_small = true }).unwrap();
defer file_buf.clearRetainingCapacity();
const contents = file_buf.items[0..read];
@@ -155,7 +155,7 @@ fn cpusImplLinux(globalThis: *jsc.JSGlobalObject) !jsc.JSValue {
if (std.fs.openFileAbsolute(path, .{})) |file| {
defer file.close();
const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, true).unwrap();
const read = try bun.sys.File.from(file).readToEndWithArrayList(&file_buf, .{ .probably_small = true }).unwrap();
defer file_buf.clearRetainingCapacity();
const contents = file_buf.items[0..read];

View File

@@ -3730,6 +3730,12 @@ pub const StackCheck = struct {
// Workaround for lack of branch hints.
pub noinline fn throwStackOverflow() StackOverflow!void {
@branchHint(.cold);
if (comptime bun.Environment.is_canary) {
if (bun.getRuntimeFeatureFlag(.BUN_FEATURE_FLAG_CRASH_ON_STACK_OVERFLOW)) {
@panic("Stack overflow. If this was intentional, update test/stackoverflow-tests.txt");
}
}
return error.StackOverflow;
}
const StackOverflow = error{StackOverflow};

View File

@@ -1222,79 +1222,76 @@ pub const RunCommand = struct {
};
return true;
}
// This is split into another function to ensure we don't keep all this stack space used for the entire lifetime of the application.
fn resolveAbsoluteScriptPath(script_name_to_search: string) !?string {
var file_path = script_name_to_search;
var script_name_buf: bun.PathBuffer = undefined;
const file = bun.FD.fromStdFile((brk: {
// This whole scope should be refactored.
if (std.fs.path.isAbsolute(script_name_to_search)) {
const win_resolver = resolve_path.PosixToWinNormalizer.get();
defer win_resolver.deinit();
var resolved = win_resolver.resolveCWD(script_name_to_search) catch @panic("Could not resolve path");
if (comptime Environment.isWindows) {
resolved = resolve_path.normalizeString(resolved, false, .windows);
}
break :brk bun.openFile(
resolved,
.{ .mode = .read_only },
);
} else if (!strings.hasPrefix(script_name_to_search, "..") and script_name_to_search[0] != '~') {
const file_pathZ = brk2: {
@memcpy(script_name_buf[0..file_path.len], file_path);
script_name_buf[file_path.len] = 0;
break :brk2 script_name_buf[0..file_path.len :0];
};
break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only });
} else {
var path_buf_2: bun.PathBuffer = undefined;
const cwd = bun.getcwd(&path_buf_2) catch return null;
path_buf_2[cwd.len] = std.fs.path.sep;
var parts = [_]string{script_name_to_search};
file_path = resolve_path.joinAbsStringBuf(
path_buf_2[0 .. cwd.len + 1],
&script_name_buf,
&parts,
.auto,
);
if (file_path.len == 0) return null;
script_name_buf[file_path.len] = 0;
const file_pathZ = script_name_buf[0..file_path.len :0];
break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only });
}
}) catch return null).makeLibUVOwnedForSyscall(.open, .close_on_fail).unwrap() catch return null;
defer file.close();
switch (bun.sys.fstat(file)) {
.result => |stat| {
// directories cannot be run. if only there was a faster way to check this
if (bun.S.ISDIR(@intCast(stat.mode))) return null;
},
.err => return null,
}
Global.configureAllocator(.{ .long_running = true });
return try bun.default_allocator.dupe(u8, try bun.getFdPath(file, &script_name_buf));
}
fn maybeOpenWithBunJS(ctx: Command.Context) bool {
if (ctx.args.entry_points.len == 0)
return false;
var script_name_buf: bun.PathBuffer = undefined;
const script_name_to_search = ctx.args.entry_points[0];
var absolute_script_path: ?string = null;
// TODO: optimize this pass for Windows. we can make better use of system apis available
var file_path = script_name_to_search;
{
const file = bun.FD.fromStdFile((brk: {
if (std.fs.path.isAbsolute(script_name_to_search)) {
var win_resolver = resolve_path.PosixToWinNormalizer{};
var resolved = win_resolver.resolveCWD(script_name_to_search) catch @panic("Could not resolve path");
if (comptime Environment.isWindows) {
resolved = resolve_path.normalizeString(resolved, false, .windows);
}
break :brk bun.openFile(
resolved,
.{ .mode = .read_only },
);
} else if (!strings.hasPrefix(script_name_to_search, "..") and script_name_to_search[0] != '~') {
const file_pathZ = brk2: {
@memcpy(script_name_buf[0..file_path.len], file_path);
script_name_buf[file_path.len] = 0;
break :brk2 script_name_buf[0..file_path.len :0];
};
break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only });
} else {
var path_buf_2: bun.PathBuffer = undefined;
const cwd = bun.getcwd(&path_buf_2) catch return false;
path_buf_2[cwd.len] = std.fs.path.sep;
var parts = [_]string{script_name_to_search};
file_path = resolve_path.joinAbsStringBuf(
path_buf_2[0 .. cwd.len + 1],
&script_name_buf,
&parts,
.auto,
);
if (file_path.len == 0) return false;
script_name_buf[file_path.len] = 0;
const file_pathZ = script_name_buf[0..file_path.len :0];
break :brk bun.openFileZ(file_pathZ, .{ .mode = .read_only });
}
}) catch return false).makeLibUVOwnedForSyscall(.open, .close_on_fail).unwrap() catch return false;
defer file.close();
switch (bun.sys.fstat(file)) {
.result => |stat| {
// directories cannot be run. if only there was a faster way to check this
if (bun.S.ISDIR(@intCast(stat.mode))) return false;
},
.err => return false,
}
Global.configureAllocator(.{ .long_running = true });
absolute_script_path = brk: {
if (comptime !Environment.isWindows) break :brk bun.getFdPath(file, &script_name_buf) catch return false;
var fd_path_buf: bun.PathBuffer = undefined;
break :brk bun.getFdPath(file, &fd_path_buf) catch return false;
};
}
const absolute_script_path = (resolveAbsoluteScriptPath(script_name_to_search) catch null) orelse return false;
if (!ctx.debug.loaded_bunfig) {
bun.cli.Arguments.loadConfigPath(ctx.allocator, true, "bunfig.toml", ctx, .RunCommand) catch {};
}
_ = _bootAndHandleError(ctx, absolute_script_path.?, null);
_ = _bootAndHandleError(ctx, absolute_script_path, null);
return true;
}
pub fn exec(
@@ -1373,26 +1370,7 @@ pub const RunCommand = struct {
if (target_name.len == 1 and target_name[0] == '-') {
log("Executing from stdin", .{});
// read from stdin
var stack_fallback = std.heap.stackFallback(2048, bun.default_allocator);
var list = std.ArrayList(u8).init(stack_fallback.get());
errdefer list.deinit();
std.io.getStdIn().reader().readAllArrayList(&list, 1024 * 1024 * 1024) catch return false;
ctx.runtime_options.eval.script = list.items;
const trigger = bun.pathLiteral("/[stdin]");
var entry_point_buf: [bun.MAX_PATH_BYTES + trigger.len]u8 = undefined;
const cwd = try std.posix.getcwd(&entry_point_buf);
@memcpy(entry_point_buf[cwd.len..][0..trigger.len], trigger);
const entry_path = entry_point_buf[0 .. cwd.len + trigger.len];
var passthrough_list = try std.ArrayList(string).initCapacity(ctx.allocator, ctx.passthrough.len + 1);
passthrough_list.appendAssumeCapacity("-");
passthrough_list.appendSliceAssumeCapacity(ctx.passthrough);
ctx.passthrough = passthrough_list.items;
Run.boot(ctx, ctx.allocator.dupe(u8, entry_path) catch return false, null) catch |err| {
Run.bootFromStdin(ctx) catch |err| {
ctx.log.print(Output.errorWriter()) catch {};
Output.prettyErrorln("<r><red>error<r>: Failed to run <b>{s}<r> due to error <b>{s}<r>", .{
@@ -1590,15 +1568,23 @@ pub const RunCommand = struct {
return false;
}
fn resolveEvalScriptPath() !string {
const trigger = bun.pathLiteral("/[eval]");
var entry_point_buf: [bun.MAX_PATH_BYTES + trigger.len]u8 = undefined;
const cwd = try std.posix.getcwd(&entry_point_buf);
@memcpy(entry_point_buf[cwd.len..][0..trigger.len], trigger);
return try bun.default_allocator.dupe(u8, entry_point_buf[0 .. cwd.len + trigger.len]);
}
fn execEvalScriptAsIfNode(ctx: Command.Context) !void {
try Run.boot(ctx, try resolveEvalScriptPath(), null);
}
pub fn execAsIfNode(ctx: Command.Context) !void {
bun.assert(CLI.pretend_to_be_node);
if (ctx.runtime_options.eval.script.len > 0) {
const trigger = bun.pathLiteral("/[eval]");
var entry_point_buf: [bun.MAX_PATH_BYTES + trigger.len]u8 = undefined;
const cwd = try std.posix.getcwd(&entry_point_buf);
@memcpy(entry_point_buf[cwd.len..][0..trigger.len], trigger);
try Run.boot(ctx, entry_point_buf[0 .. cwd.len + trigger.len], null);
try execEvalScriptAsIfNode(ctx);
return;
}
@@ -1607,16 +1593,9 @@ pub const RunCommand = struct {
Global.exit(1);
}
// TODO(@paperclover): merge windows branch
// var win_resolver = resolve_path.PosixToWinNormalizer{};
const filename = ctx.positionals[0];
const normalized_filename = if (std.fs.path.isAbsolute(filename))
// TODO(@paperclover): merge windows branch
// try win_resolver.resolveCWD("/dev/bun/test/etc.js");
filename
else brk: {
const normalized_filename = if (std.fs.path.isAbsolute(filename)) filename else brk: {
const cwd = try bun.getcwd(&path_buf);
path_buf[cwd.len] = std.fs.path.sep_posix;
var parts = [_]string{filename};

View File

@@ -40,6 +40,8 @@ pub const RuntimeFeatureFlag = enum {
BUN_NO_CODESIGN_MACHO_BINARY,
BUN_TRACE,
NODE_NO_WARNINGS,
BUN_FEATURE_FLAG_CRASH_ON_STACK_OVERFLOW,
};
/// Enable breaking changes for the next major release of Bun

View File

@@ -539,7 +539,8 @@ var ensureTempNodeGypScriptOnce = bun.once(struct {
fn httpThreadOnInitError(err: HTTP.InitError, opts: HTTP.HTTPThread.InitOpts) noreturn {
switch (err) {
error.LoadCAFile => {
var normalizer: bun.path.PosixToWinNormalizer = .{};
const normalizer = bun.path.PosixToWinNormalizer.get();
defer normalizer.deinit();
const normalized = normalizer.resolveZ(FileSystem.instance.top_level_dir, opts.abs_ca_file_name);
if (!bun.sys.existsZ(normalized)) {
Output.err("HTTPThread", "could not find CA file: '{s}'", .{opts.abs_ca_file_name});

View File

@@ -162,25 +162,16 @@ pub fn openGlobalDir(explicit_global_dir: string) !std.fs.Dir {
}
if (bun.getenvZ("BUN_INSTALL")) |home_dir| {
var buf: bun.PathBuffer = undefined;
var parts = [_]string{ "install", "global" };
const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{});
return try bun.path.makeOpen(home_dir, &.{ "install", "global" });
}
if (!Environment.isWindows) {
if (bun.getenvZ("XDG_CACHE_HOME") orelse bun.getenvZ("HOME")) |home_dir| {
var buf: bun.PathBuffer = undefined;
var parts = [_]string{ ".bun", "install", "global" };
const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{});
return try bun.path.makeOpen(home_dir, &.{ ".bun", "install", "global" });
}
} else {
if (bun.getenvZ("USERPROFILE")) |home_dir| {
var buf: bun.PathBuffer = undefined;
var parts = [_]string{ ".bun", "install", "global" };
const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{});
return try bun.path.makeOpen(home_dir, &.{ ".bun", "install", "global" });
}
}
@@ -201,22 +192,11 @@ pub fn openGlobalBinDir(opts_: ?*const Api.BunInstall) !std.fs.Dir {
}
if (bun.getenvZ("BUN_INSTALL")) |home_dir| {
var buf: bun.PathBuffer = undefined;
var parts = [_]string{
"bin",
};
const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{});
return try bun.path.makeOpen(home_dir, &.{"bin"});
}
if (bun.getenvZ("XDG_CACHE_HOME") orelse bun.getenvZ(bun.DotEnv.home_env)) |home_dir| {
var buf: bun.PathBuffer = undefined;
var parts = [_]string{
".bun",
"bin",
};
const path = Path.joinAbsStringBuf(home_dir, &buf, &parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{});
return try bun.path.makeOpen(home_dir, &.{ ".bun", "bin" });
}
return error.@"Missing global bin directory: try setting $BUN_INSTALL";
@@ -699,7 +679,6 @@ const Environment = bun.Environment;
const FD = bun.FD;
const OOM = bun.OOM;
const Output = bun.Output;
const Path = bun.path;
const URL = bun.URL;
const logger = bun.logger;
const strings = bun.strings;

View File

@@ -194,7 +194,7 @@ pub const FolderResolution = union(Tag) {
body.data.reset();
var man = body.data.list.toManaged(manager.allocator);
defer body.data.list = man.moveToUnmanaged();
_ = try file.readToEndWithArrayList(&man, true).unwrap();
_ = try file.readToEndWithArrayList(&man, .{ .probably_small = true }).unwrap();
}
break :brk logger.Source.initPathString(abs, body.data.list.items);

View File

@@ -198,7 +198,7 @@ const PosixBufferedReader = struct {
pub fn finalBuffer(this: *PosixBufferedReader) *std.ArrayList(u8) {
if (this.flags.memfd and this.handle == .fd) {
defer this.handle.close(null, {});
_ = bun.sys.File.readToEndWithArrayList(.{ .handle = this.handle.fd }, this.buffer(), false).unwrap() catch |err| {
_ = bun.sys.File.readToEndWithArrayList(.{ .handle = this.handle.fd }, this.buffer(), .{ .probably_small = false, .allow_pread = false }).unwrap() catch |err| {
bun.Output.debugWarn("error reading from memfd\n{}", .{err});
return this.buffer();
};

View File

@@ -1825,46 +1825,56 @@ pub fn nextDirname(path_: []const u8) ?[]const u8 {
///
/// This API does nothing on Linux (it has a size of zero)
pub const PosixToWinNormalizer = struct {
const Buf = if (bun.Environment.isWindows) bun.PathBuffer else void;
const Buf = if (bun.Environment.isWindows) *bun.PathBuffer else void;
_raw_bytes: Buf = undefined,
_raw_bytes: Buf,
pub inline fn get() PosixToWinNormalizer {
if (comptime bun.Environment.isWindows) {
return .{
._raw_bytes = bun.path_buffer_pool.get(),
};
}
return .{
._raw_bytes = {},
};
}
pub inline fn deinit(this: PosixToWinNormalizer) void {
if (comptime bun.Environment.isWindows) {
bun.path_buffer_pool.put(this._raw_bytes);
}
}
// methods on PosixToWinNormalizer, to be minimal yet stack allocate the PathBuffer
// these do not force inline of much code
pub inline fn resolve(
this: *PosixToWinNormalizer,
this: PosixToWinNormalizer,
source_dir: []const u8,
maybe_posix_path: []const u8,
) []const u8 {
return resolveWithExternalBuf(&this._raw_bytes, source_dir, maybe_posix_path);
return resolveWithExternalBuf(this._raw_bytes, source_dir, maybe_posix_path);
}
pub inline fn resolveZ(
this: *PosixToWinNormalizer,
this: PosixToWinNormalizer,
source_dir: []const u8,
maybe_posix_path: [:0]const u8,
) [:0]const u8 {
return resolveWithExternalBufZ(&this._raw_bytes, source_dir, maybe_posix_path);
return resolveWithExternalBufZ(this._raw_bytes, source_dir, maybe_posix_path);
}
pub inline fn resolveCWD(
this: *PosixToWinNormalizer,
this: PosixToWinNormalizer,
maybe_posix_path: []const u8,
) ![]const u8 {
return resolveCWDWithExternalBuf(&this._raw_bytes, maybe_posix_path);
}
pub inline fn resolveCWDZ(
this: *PosixToWinNormalizer,
maybe_posix_path: []const u8,
) ![:0]const u8 {
return resolveCWDWithExternalBufZ(&this._raw_bytes, maybe_posix_path);
return resolveCWDWithExternalBuf(this._raw_bytes, maybe_posix_path);
}
// underlying implementation:
fn resolveWithExternalBuf(
buf: *Buf,
buf: Buf,
source_dir: []const u8,
maybe_posix_path: []const u8,
) []const u8 {
@@ -1889,7 +1899,7 @@ pub const PosixToWinNormalizer = struct {
}
fn resolveWithExternalBufZ(
buf: *Buf,
buf: Buf,
source_dir: []const u8,
maybe_posix_path: [:0]const u8,
) [:0]const u8 {
@@ -1915,7 +1925,7 @@ pub const PosixToWinNormalizer = struct {
}
pub fn resolveCWDWithExternalBuf(
buf: *Buf,
buf: Buf,
maybe_posix_path: []const u8,
) ![]const u8 {
assert(std.fs.path.isAbsoluteWindows(maybe_posix_path));
@@ -2056,6 +2066,12 @@ pub fn posixToPlatformInPlace(comptime T: type, path_buffer: []T) void {
}
}
pub fn makeOpen(cwd: []const u8, parts: []const []const u8) !std.fs.Dir {
var buf: bun.PathBuffer = undefined;
const path = joinAbsStringBuf(cwd, &buf, parts, .auto);
return try std.fs.cwd().makeOpenPath(path, .{});
}
const Fs = @import("../fs.zig");
const std = @import("std");

View File

@@ -761,7 +761,8 @@ pub const Resolver = struct {
// It's always relative to the current working directory of the project root.
//
// ...unless you pass a relative path that exists in the standalone module graph executable.
var source_dir_resolver: bun.path.PosixToWinNormalizer = .{};
const source_dir_resolver: bun.path.PosixToWinNormalizer = bun.path.PosixToWinNormalizer.get();
defer source_dir_resolver.deinit();
const source_dir_normalized = brk: {
if (r.standalone_module_graph) |graph| {
if (bun.StandaloneModuleGraph.isBunStandaloneFilePath(import_path)) {
@@ -986,9 +987,10 @@ pub const Resolver = struct {
}
} else if (dir.abs_real_path.len > 0) {
var parts = [_]string{ dir.abs_real_path, query.entry.base() };
var buf: bun.PathBuffer = undefined;
var buf = bun.path_buffer_pool.get();
defer bun.path_buffer_pool.put(buf);
var out = r.fs.absBuf(&parts, &buf);
var out = r.fs.absBuf(&parts, buf);
const store_fd = r.store_fd;
@@ -1002,7 +1004,7 @@ pub const Resolver = struct {
if (!store_fd) {
assert(file.stdioTag() == null);
out = try file.getFdPath(&buf);
out = try file.getFdPath(buf);
file.close();
query.entry.cache.fd = .invalid;
} else {
@@ -1014,15 +1016,14 @@ pub const Resolver = struct {
defer {
if (r.fs.fs.needToCloseFiles()) {
if (query.entry.cache.fd.isValid()) {
var file = query.entry.cache.fd.stdFile();
file.close();
query.entry.cache.fd.close();
query.entry.cache.fd = .invalid;
}
}
}
if (store_fd) {
out = try bun.getFdPath(query.entry.cache.fd, &buf);
out = try bun.getFdPath(query.entry.cache.fd, buf);
}
const symlink = try Fs.FileSystem.FilenameStore.instance.append(@TypeOf(out), out);
@@ -1140,21 +1141,22 @@ pub const Resolver = struct {
}
// Run node's resolution rules (e.g. adding ".js")
var normalizer = ResolvePath.PosixToWinNormalizer{};
if (r.loadAsFileOrDirectory(normalizer.resolve(source_dir, import_path), kind)) |entry| {
return .{
.success = Result{
.dirname_fd = entry.dirname_fd,
.path_pair = entry.path_pair,
.diff_case = entry.diff_case,
.package_json = entry.package_json,
.file_fd = entry.file_fd,
.jsx = r.opts.jsx,
},
};
}
return .{ .not_found = {} };
const normalizer = ResolvePath.PosixToWinNormalizer.get();
defer normalizer.deinit();
const entry = r.loadAsFileOrDirectory(
normalizer.resolve(source_dir, import_path),
kind,
) orelse return .{ .not_found = {} };
return .{
.success = Result{
.dirname_fd = entry.dirname_fd,
.path_pair = entry.path_pair,
.diff_case = entry.diff_case,
.package_json = entry.package_json,
.file_fd = entry.file_fd,
.jsx = r.opts.jsx,
},
};
}
// Check both relative and package paths for CSS URL tokens, with relative
@@ -1287,6 +1289,7 @@ pub const Resolver = struct {
pub fn checkRelativePath(r: *ThisResolver, source_dir: string, import_path: string, kind: ast.ImportKind, global_cache: GlobalCache) Result.Union {
const parts = [_]string{ source_dir, import_path };
const abs_path = r.fs.absBuf(&parts, bufs(.relative_abs_path));
if (r.opts.external.abs_paths.count() > 0 and r.opts.external.abs_paths.contains(abs_path)) {

View File

@@ -533,17 +533,9 @@ pub const Mapping = struct {
return null;
const name = source_map.external_source_names[@intCast(index)];
var buf: bun.PathBuffer = undefined;
const normalized = bun.path.joinAbsStringBufZ(
bun.path.dirname(base_filename, .auto),
&buf,
&.{name},
.loose,
);
switch (bun.sys.File.readFrom(
switch (bun.sys.File.readFromJoinedPath(
std.fs.cwd(),
normalized,
&.{ bun.path.dirname(base_filename, .auto), name },
bun.default_allocator,
)) {
.result => |r| break :bytes r,

View File

@@ -4280,8 +4280,18 @@ pub const File = struct {
return .{ .result = buf[0..read_amount] };
}
pub fn readToEndWithArrayList(this: File, list: *std.ArrayList(u8), probably_small: bool) Maybe(usize) {
if (probably_small) {
pub const ReadToEndWithArrayListOptions = packed struct(u8) {
probably_small: bool = false,
allow_pread: bool = true,
_padding: u6 = 0,
};
pub fn readToEndWithArrayList(
this: File,
list: *std.ArrayList(u8),
opts: ReadToEndWithArrayListOptions,
) Maybe(usize) {
if (opts.probably_small) {
list.ensureUnusedCapacity(64) catch bun.outOfMemory();
} else {
list.ensureTotalCapacityPrecise(
@@ -4300,7 +4310,7 @@ pub const File = struct {
list.ensureUnusedCapacity(16) catch bun.outOfMemory();
}
switch (if (comptime Environment.isPosix)
switch (if ((comptime Environment.isPosix) and opts.allow_pread)
pread(this.handle, list.unusedCapacitySlice(), total)
else
sys.read(this.handle, list.unusedCapacitySlice())) {
@@ -4325,7 +4335,7 @@ pub const File = struct {
/// Calls fstat() on the file to get the size of the file and avoids reallocations + extra read() calls.
pub fn readToEnd(this: File, allocator: std.mem.Allocator) ReadToEndResult {
var list = std.ArrayList(u8).init(allocator);
return switch (readToEndWithArrayList(this, &list, false)) {
return switch (readToEndWithArrayList(this, &list, .{ .probably_small = false })) {
.err => |err| .{ .err = err, .bytes = list },
.result => .{ .err = null, .bytes = list },
};
@@ -4335,7 +4345,7 @@ pub const File = struct {
/// This will skip the fstat() call, preallocating 64 bytes instead of the file's size.
pub fn readToEndSmall(this: File, allocator: std.mem.Allocator) ReadToEndResult {
var list = std.ArrayList(u8).init(allocator);
return switch (readToEndWithArrayList(this, &list, true)) {
return switch (readToEndWithArrayList(this, &list, .{ .probably_small = true })) {
.err => |err| .{ .err = err, .bytes = list },
.result => .{ .err = null, .bytes = list },
};
@@ -4414,6 +4424,28 @@ pub const File = struct {
return .{ .result = bytes };
}
pub fn readFromJoinedPath(dir_fd: anytype, path_parts: []const []const u8, allocator: std.mem.Allocator) Maybe([]u8) {
const path = bun.path_buffer_pool.get();
defer bun.path_buffer_pool.put(path);
const file, const bytes = switch (readFileFrom(
dir_fd,
bun.path.joinAbsStringBufZ(
if (path_parts.len > 0 and std.fs.path.isAbsolute(path_parts[0])) path_parts[0] else bun.fs.FileSystem.instance.top_level_dir,
path,
path_parts,
.auto,
),
allocator,
)) {
.err => |err| return .{ .err = err },
.result => |result| result,
};
file.close();
return .{ .result = bytes };
}
const ToSourceOptions = struct {
convert_bom: bool = false,
};

View File

@@ -5,9 +5,9 @@
"!= allocator.ptr": 0,
".arguments_old(": 280,
".stdDir()": 40,
".stdFile()": 18,
".stdFile()": 17,
"// autofix": 168,
": [a-zA-Z0-9_\\.\\*\\?\\[\\]\\(\\)]+ = undefined,": 229,
": [a-zA-Z0-9_\\.\\*\\?\\[\\]\\(\\)]+ = undefined,": 228,
"== alloc.ptr": 0,
"== allocator.ptr": 0,
"@import(\"bun\").": 0,
@@ -30,7 +30,7 @@
"std.enums.tagName(": 2,
"std.fs.Dir": 170,
"std.fs.File": 62,
"std.fs.cwd": 103,
"std.fs.cwd": 99,
"std.log": 1,
"std.mem.indexOfAny(u8": 0,
"std.unicode": 30,