Compare commits

...

5 Commits

Author SHA1 Message Date
dave caruso
92b97b15de Merge branch 'main' into dave/win/assertion-fix 2024-07-29 16:59:29 -07:00
dave caruso
c8504ba3c7 remove breakpoint 2024-07-16 16:49:05 -07:00
Dave Caruso
a503889a88 aaaaaaaaaa 2024-07-16 15:51:31 -07:00
dave caruso
ccb1835234 this doesnt fix it 2024-07-16 15:51:31 -07:00
dave caruso
b372971906 fix an assertion 2024-07-16 15:51:31 -07:00
7 changed files with 72 additions and 48 deletions

View File

@@ -2140,7 +2140,7 @@ pub const VirtualMachine = struct {
const buster_name = name: {
if (std.fs.path.isAbsolute(normalized_specifier)) {
if (std.fs.path.dirname(normalized_specifier)) |dir| {
// Normalized with trailing slash
// Normalized without trailing slash
break :name bun.strings.normalizeSlashesOnly(&specifier_cache_resolver_buf, dir, std.fs.path.sep);
}
}
@@ -2160,7 +2160,7 @@ pub const VirtualMachine = struct {
};
// Only re-query if we previously had something cached.
if (jsc_vm.bundler.resolver.bustDirCache(buster_name)) {
if (jsc_vm.bundler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) {
continue;
}
@@ -4089,7 +4089,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
// on windows we receive file events for all items affected by a directory change
// so we only need to clear the directory cache. all other effects will be handled
// by the file events
_ = resolver.bustDirCache(strings.pathWithoutTrailingSlashOne(file_path));
_ = resolver.bustDirCache(strings.withoutTrailingSlashWindowsPath(file_path));
continue;
}
var affected_buf: [128][]const u8 = undefined;
@@ -4139,7 +4139,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
}
}
_ = resolver.bustDirCache(strings.pathWithoutTrailingSlashOne(file_path));
_ = resolver.bustDirCache(strings.withoutTrailingSlashWindowsPath(file_path));
if (entries_option) |dir_ent| {
var last_file_hash: GenericWatcher.HashType = std.math.maxInt(GenericWatcher.HashType);

View File

@@ -420,7 +420,7 @@ pub const Bundler = struct {
};
// Only re-query if we previously had something cached.
if (bundler.resolver.bustDirCache(buster_name)) {
if (bundler.resolver.bustDirCache(bun.strings.withoutTrailingSlashWindowsPath(buster_name))) {
if (_resolveEntryPoint(bundler, entry_point)) |result|
return result
else |_| {

View File

@@ -1036,7 +1036,7 @@ pub const FileSystem = struct {
comptime Iterator: type,
iterator: Iterator,
) !*EntriesOption {
var dir = bun.strings.pathWithoutTrailingSlashOne(dir_maybe_trail_slash);
var dir = bun.strings.withoutTrailingSlashWindowsPath(dir_maybe_trail_slash);
bun.resolver.Resolver.assertValidCacheKey(dir);
var cache_result: ?allocators.Result = null;

View File

@@ -679,8 +679,8 @@ pub fn windowsFilesystemRootT(comptime T: type, path: []const T) []const T {
!Platform.windows.isSeparatorT(T, path[2]) and
path[2] != '.')
{
if (bun.strings.indexAnyComptimeT(T, path[3..], "/\\")) |idx| {
if (bun.strings.indexAnyComptimeT(T, path[4 + idx ..], "/\\")) |idx_second| {
if (bun.strings.indexOfAnyT(T, path[3..], "/\\")) |idx| {
if (bun.strings.indexOfAnyT(T, path[4 + idx ..], "/\\")) |idx_second| {
return path[0 .. idx + idx_second + 4];
}
return path[0..];
@@ -1915,9 +1915,11 @@ pub const PosixToWinNormalizer = struct {
@memcpy(buf[source_root.len..][0 .. maybe_posix_path.len - 1], maybe_posix_path[1..]);
const res = buf[0 .. source_root.len + maybe_posix_path.len - 1];
assert(!bun.strings.isWindowsAbsolutePathMissingDriveLetter(u8, res));
assert(std.fs.path.isAbsoluteWindows(res));
return res;
}
}
assert(!bun.strings.isWindowsAbsolutePathMissingDriveLetter(u8, maybe_posix_path));
}
return maybe_posix_path;
}
@@ -1940,9 +1942,11 @@ pub const PosixToWinNormalizer = struct {
@memcpy(buf[source_root.len..][0 .. maybe_posix_path.len - 1], maybe_posix_path[1..]);
const res = buf[0 .. source_root.len + maybe_posix_path.len - 1];
assert(!bun.strings.isWindowsAbsolutePathMissingDriveLetter(u8, res));
assert(std.fs.path.isAbsoluteWindows(res));
return res;
}
}
assert(!bun.strings.isWindowsAbsolutePathMissingDriveLetter(u8, maybe_posix_path));
}
return maybe_posix_path;
@@ -1967,9 +1971,12 @@ pub const PosixToWinNormalizer = struct {
buf[source_root.len + maybe_posix_path.len - 1] = 0;
const res = buf[0 .. source_root.len + maybe_posix_path.len - 1 :0];
assert(!bun.strings.isWindowsAbsolutePathMissingDriveLetter(u8, res));
assert(std.fs.path.isAbsoluteWindows(res));
return res;
}
}
assert(!bun.strings.isWindowsAbsolutePathMissingDriveLetter(u8, maybe_posix_path));
}
@memcpy(buf.ptr, maybe_posix_path);

View File

@@ -1644,8 +1644,8 @@ pub const Resolver = struct {
/// But drive roots MUST have a trailing slash ('/' and 'C:\')
/// UNC paths, even if the root, must not have the trailing slash.
///
/// The helper function bun.strings.pathWithoutTrailingSlashOne can be used to remove
/// the trailing slash from a path, but also note it will only remove a SINGLE slash.
/// The helper function bun.strings.withoutTrailingSlashWindowsPath can be used
/// to remove the trailing slash from a path
pub fn assertValidCacheKey(path: []const u8) void {
if (Environment.allow_assert) {
if (path.len > 1 and strings.charIsAnySlash(path[path.len - 1]) and !if (Environment.isWindows)
@@ -2127,7 +2127,7 @@ pub const Resolver = struct {
) !?*DirInfo {
assert(r.package_manager != null);
const dir_path = strings.pathWithoutTrailingSlashOne(dir_path_maybe_trail_slash);
const dir_path = strings.withoutTrailingSlashWindowsPath(dir_path_maybe_trail_slash);
assertValidCacheKey(dir_path);
var dir_cache_info_result = r.dir_cache.getOrPut(dir_path) catch bun.outOfMemory();
@@ -2624,7 +2624,7 @@ pub const Resolver = struct {
assert(std.fs.path.isAbsolute(input_path));
const path_without_trailing_slash = strings.pathWithoutTrailingSlashOne(input_path);
const path_without_trailing_slash = strings.withoutTrailingSlashWindowsPath(input_path);
assertValidCacheKey(path_without_trailing_slash);
const top_result = try r.dir_cache.getOrPut(path_without_trailing_slash);
if (top_result.status != .unknown) {
@@ -2646,7 +2646,7 @@ pub const Resolver = struct {
.status = .not_found,
};
const root_path = if (Environment.isWindows)
bun.strings.pathWithoutTrailingSlashOne(ResolvePath.windowsFilesystemRoot(path))
bun.strings.withoutTrailingSlashWindowsPath(ResolvePath.windowsFilesystemRoot(path))
else
// we cannot just use "/"
// we will write to the buffer past the ptr len so it must be a non-const buffer
@@ -3740,7 +3740,8 @@ pub const Resolver = struct {
}
}
const dir_path = bun.strings.pathWithoutTrailingSlashOne(Dirname.dirname(path));
const dir_path = bun.strings.withoutTrailingSlashWindowsPath(Dirname.dirname(path));
bun.strings.assertIsValidWindowsPath(u8, dir_path);
const dir_entry: *Fs.FileSystem.RealFS.EntriesOption = rfs.readDirectory(
dir_path,
@@ -4195,7 +4196,12 @@ pub const Dirname = struct {
if (Environment.isWindows) {
const root = ResolvePath.windowsFilesystemRoot(path);
assert(root.len > 0);
break :brk root;
// Preserve the trailing slash for UNC paths.
// Going from `\\server\share\folder` should end up
// at `\\server\share\`, not `\\server\share`
bun.unsafeAssert(root.ptr == path.ptr);
break :brk if (root.len >= 5) path[0 .. root.len + 1] else root;
}
break :brk "/";
};

View File

@@ -124,6 +124,7 @@ pub fn indexOfAny16(self: []const u16, comptime str: anytype) ?OptionalUsize {
pub fn indexOfAnyT(comptime T: type, str: []const T, comptime chars: anytype) ?OptionalUsize {
if (T == u8) return indexOfAny(str, chars);
for (str, 0..) |c, i| {
inline for (chars) |a| {
if (c == a) {
@@ -618,34 +619,24 @@ pub fn withoutTrailingSlash(this: string) []const u8 {
return href;
}
/// Does not strip the C:\
pub fn withoutTrailingSlashWindowsPath(this: string) []const u8 {
if (this.len < 3 or
this[1] != ':') return withoutTrailingSlash(this);
/// Does not strip the device root (C:\ or \\Server\Share\ portion off of the path)
pub fn withoutTrailingSlashWindowsPath(input: string) []const u8 {
if (Environment.isPosix or input.len < 3 or input[1] != ':')
return withoutTrailingSlash(input);
var href = this;
while (href.len > 3 and (switch (href[href.len - 1]) {
const root_len = bun.path.windowsFilesystemRoot(input).len + 1;
var path = input;
while (path.len > root_len and (switch (path[path.len - 1]) {
'/', '\\' => true,
else => false,
})) {
href.len -= 1;
path.len -= 1;
}
return href;
}
bun.assert(!isWindowsAbsolutePathMissingDriveLetter(u8, path));
/// This will remove ONE trailing slash at the end of a string,
/// but on Windows it will not remove the \ in "C:\"
pub fn pathWithoutTrailingSlashOne(str: []const u8) []const u8 {
return if (str.len > 0 and charIsAnySlash(str[str.len - 1]))
if (Environment.isWindows and str.len == 3 and str[1] == ':')
// Preserve "C:\"
str
else
// Remove one slash
str[0 .. str.len - 1]
else
str;
return path;
}
pub fn withoutLeadingSlash(this: string) []const u8 {
@@ -1600,25 +1591,21 @@ pub fn utf16Codepoint(comptime Type: type, input: Type) UTF16Replacement {
}
}
/// Checks if a path is missing a windows drive letter. Not a perfect check,
/// but it is good enough for most cases. For windows APIs, this is used for
/// an assertion, and PosixToWinNormalizer can help make an absolute path
/// contain a drive letter.
/// Checks if a path is missing a windows drive letter. For windows APIs,
/// this is used for an assertion, and PosixToWinNormalizer can help make
/// an absolute path contain a drive letter.
pub fn isWindowsAbsolutePathMissingDriveLetter(comptime T: type, chars: []const T) bool {
bun.unsafeAssert(bun.path.Platform.windows.isAbsoluteT(T, chars));
bun.unsafeAssert(chars.len > 0);
// 'C:\hello' -> false
// This is the most common situation, so we check it first
if (!(chars[0] == '/' or chars[0] == '\\')) {
bun.unsafeAssert(chars.len > 2);
bun.unsafeAssert(chars[1] == ':');
return false;
}
// '\\hello' -> false (probably a UNC path)
if (chars.len > 1 and
(chars[1] == '/' or chars[1] == '\\')) return false;
if (chars.len > 4) {
// '\??\hello' -> false (has the NT object prefix)
if (chars[1] == '?' and
@@ -1633,9 +1620,13 @@ pub fn isWindowsAbsolutePathMissingDriveLetter(comptime T: type, chars: []const
return false;
}
// oh no, '/hello/world'
// where is the drive letter!
return true;
// A path starting with `/` can be a UNC path with forward slashes,
// or actually just a posix path.
//
// '\\Server\Share' -> false (unc)
// '\\Server\\Share' -> true (not unc because extra slashes)
// '\Server\Share' -> true (posix path)
return bun.path.windowsFilesystemRootT(T, chars).len == 1;
}
pub fn fromWPath(buf: []u8, utf16: []const u16) [:0]const u8 {

View File

@@ -1,7 +1,7 @@
import { it, expect } from "bun:test";
import { mkdirSync, writeFileSync } from "fs";
import { join } from "path";
import { bunExe, bunEnv, tempDirWithFiles } from "harness";
import { bunExe, bunEnv, tempDirWithFiles, isWindows } from "harness";
import { pathToFileURL } from "bun";
import { sep } from "path";
@@ -312,3 +312,23 @@ it.todo("import override to bun:test", async () => {
// @ts-expect-error
expect(await import("#bun_test")).toBeDefined();
});
it.if(isWindows)("directory cache key computation", () => {
expect(import(`${process.cwd()}\\\\doesnotexist.ts`)).rejects.toThrow();
expect(import(`${process.cwd()}\\\\\\doesnotexist.ts`)).rejects.toThrow();
expect(import(`\\\\Test\\\\doesnotexist.ts\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\doesnotexist.ts\\\\\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\\\doesnotexist.ts` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\\\\\doesnotexist.ts` as any)).rejects.toThrow();
expect(import(`\\\\Test\\doesnotexist.ts` as any)).rejects.toThrow();
expect(import(`\\\\\\Test\\doesnotexist.ts` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\\\doesnotexist.ts\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\\\\\doesnotexist.ts\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\doesnotexist.ts\\` as any)).rejects.toThrow();
expect(import(`\\\\\\Test\\doesnotexist.ts\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\\\\\\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
expect(import(`\\\\Test\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
expect(import(`\\\\\\Test\\doesnotexist.ts\\\\` as any)).rejects.toThrow();
});