Compare commits

...

3 Commits

Author SHA1 Message Date
Jarred Sumner
b2ddca54d7 band-aid 2025-10-21 21:20:35 -07:00
Jarred Sumner
91c0fafa04 Update path.zig 2025-10-21 19:43:30 -07:00
Jarred Sumner
f827b67fe1 Micro-optimize some of the path functions 2025-10-21 19:41:12 -07:00
2 changed files with 310 additions and 228 deletions

View File

@@ -35,11 +35,9 @@ static inline JSC::EncodedJSValue createZigFunction(JSGlobalObject* globalObject
{
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
MarkedArgumentBufferWithSize<8> args = MarkedArgumentBufferWithSize<8>();
for (unsigned i = 0, size = callFrame->argumentCount(); i < size; ++i) {
args.append(callFrame->argument(i));
}
const auto result = Function(globalObject, isWindows, args.data(), args.size());
auto args = ArgList(callFrame);
const auto result = Function(globalObject, isWindows, args.data(), static_cast<uint16_t>(args.size()));
RETURN_IF_EXCEPTION(scope, {});
return result;
}

View File

@@ -1969,35 +1969,52 @@ pub fn parse(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]js
/// Based on Node v21.6.1 path.posix.relative:
/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1193
pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) MaybeSlice(T) {
pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T), buf3: *std.ArrayList(T)) MaybeSlice(T) {
comptime validatePathT(T, "relativePosixT");
// validateString of `from` and `to` are performed in pub fn relative.
if (std.mem.eql(T, from, to)) {
return MaybeSlice(T){ .result = &.{} };
return .{ .result = &.{} };
}
// Trim leading forward slashes.
// Backed by expandable buf2 because fromOrig may be long.
// We need to save the resolved paths because they'll be backed by temporary buffers
// Use buf2 and buf3 as scratch buffers for resolving from
const fromOrig = switch (resolvePosixT(T, &.{from}, buf2, buf3)) {
.result => |r| r,
.err => |e| return MaybeSlice(T){ .err = e },
.err => |e| return .{ .err = e },
};
const fromOrigLen = fromOrig.len;
// Backed by buf.
const toOrig = switch (resolvePosixT(T, &.{to}, buf, buf3)) {
.result => |r| r,
.err => |e| return MaybeSlice(T){ .err = e },
};
if (std.mem.eql(T, fromOrig, toOrig)) {
return MaybeSlice(T){ .result = &.{} };
// Save fromOrig into buf (we'll use buf for the final result later)
buf.clearRetainingCapacity();
buf.ensureTotalCapacity(fromOrigLen) catch return .{ .err = Syscall.Error.oom };
const oldLen = buf.items.len;
buf.items.len = oldLen + fromOrigLen;
bun.memmove(buf.allocatedSlice()[oldLen .. oldLen + fromOrigLen], fromOrig);
const fromOrigSaved = buf.items[0..fromOrigLen];
// Now use buf2 and buf3 as scratch buffers for resolving to
const toOrig = switch (resolvePosixT(T, &.{to}, buf2, buf3)) {
.result => |r| r,
.err => |e| return .{ .err = e },
};
const toOrigLen = toOrig.len;
// Save toOrig into buf3
buf3.clearRetainingCapacity();
buf3.ensureTotalCapacity(toOrigLen) catch return .{ .err = Syscall.Error.oom };
const oldLen3 = buf3.items.len;
buf3.items.len = oldLen3 + toOrigLen;
bun.memmove(buf3.allocatedSlice()[oldLen3 .. oldLen3 + toOrigLen], toOrig);
const toOrigSaved = buf3.items[0..toOrigLen];
if (std.mem.eql(T, fromOrigSaved, toOrigSaved)) {
return .{ .result = &.{} };
}
const fromStart = 1;
const fromEnd = fromOrigLen;
const fromLen = fromEnd - fromStart;
const toOrigLen = toOrig.len;
var toStart: usize = 1;
const toLen = toOrigLen - toStart;
@@ -2011,8 +2028,8 @@ pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T
{
var i: usize = 0;
while (i < smallestLength) : (i += 1) {
const fromByte = fromOrig[fromStart + i];
if (fromByte != toOrig[toStart + i]) {
const fromByte = fromOrigSaved[fromStart + i];
if (fromByte != toOrigSaved[toStart + i]) {
break;
} else if (fromByte == CHAR_FORWARD_SLASH) {
lastCommonSep = i;
@@ -2022,18 +2039,33 @@ pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T
}
if (matchesAllOfSmallest) {
if (toLen > smallestLength) {
if (toOrig[toStart + smallestLength] == CHAR_FORWARD_SLASH) {
if (toOrigSaved[toStart + smallestLength] == CHAR_FORWARD_SLASH) {
// We get here if `from` is the exact base path for `to`.
// For example: from='/foo/bar'; to='/foo/bar/baz'
return MaybeSlice(T){ .result = toOrig[toStart + smallestLength + 1 .. toOrigLen :0] };
// Return a slice from toOrigSaved, adding null terminator
const resultStart = toStart + smallestLength + 1;
const resultLen = toOrigLen - resultStart;
buf2.clearRetainingCapacity();
buf2.ensureTotalCapacity(resultLen + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = resultLen + 1;
bun.memmove(buf2.items[0..resultLen], toOrigSaved[resultStart..toOrigLen]);
buf2.items[resultLen] = 0;
return .{ .result = buf2.items[0..resultLen :0] };
}
if (smallestLength == 0) {
// We get here if `from` is the root
// For example: from='/'; to='/foo'
return MaybeSlice(T){ .result = toOrig[toStart + smallestLength .. toOrigLen :0] };
const resultStart = toStart + smallestLength;
const resultLen = toOrigLen - resultStart;
buf2.clearRetainingCapacity();
buf2.ensureTotalCapacity(resultLen + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = resultLen + 1;
bun.memmove(buf2.items[0..resultLen], toOrigSaved[resultStart..toOrigLen]);
buf2.items[resultLen] = 0;
return .{ .result = buf2.items[0..resultLen :0] };
}
} else if (fromLen > smallestLength) {
if (fromOrig[fromStart + smallestLength] == CHAR_FORWARD_SLASH) {
if (fromOrigSaved[fromStart + smallestLength] == CHAR_FORWARD_SLASH) {
// We get here if `to` is the exact base path for `from`.
// For example: from='/foo/bar/baz'; to='/foo/bar'
lastCommonSep = smallestLength;
@@ -2045,11 +2077,9 @@ pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T
}
}
var bufOffset: usize = 0;
var bufSize: usize = 0;
// Clear buf2 for building the final result
buf2.clearRetainingCapacity();
// Backed by buf3.
var out: []const T = &.{};
// Add a block to isolate `i`.
{
// Generate the relative path based on the path difference between `to`
@@ -2059,21 +2089,19 @@ pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T
// for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) {
var i: usize = fromStart + (if (lastCommonSep != null) lastCommonSep.? + 1 else 0);
while (i <= fromEnd) : (i += 1) {
if (i == fromEnd or fromOrig[i] == CHAR_FORWARD_SLASH) {
if (i == fromEnd or fromOrigSaved[i] == CHAR_FORWARD_SLASH) {
// Translated from the following JS code:
// out += out.length === 0 ? '..' : '/..';
if (out.len > 0) {
bufOffset = bufSize;
bufSize += 3;
buf3[bufOffset] = CHAR_FORWARD_SLASH;
buf3[bufOffset + 1] = CHAR_DOT;
buf3[bufOffset + 2] = CHAR_DOT;
if (buf2.items.len > 0) {
buf2.ensureTotalCapacity(buf2.items.len + 3) catch return .{ .err = Syscall.Error.oom };
buf2.appendAssumeCapacity(CHAR_FORWARD_SLASH);
buf2.appendAssumeCapacity(CHAR_DOT);
buf2.appendAssumeCapacity(CHAR_DOT);
} else {
bufSize = 2;
buf3[0] = CHAR_DOT;
buf3[1] = CHAR_DOT;
buf2.ensureTotalCapacity(2) catch return .{ .err = Syscall.Error.oom };
buf2.appendAssumeCapacity(CHAR_DOT);
buf2.appendAssumeCapacity(CHAR_DOT);
}
out = buf3[0..bufSize];
}
}
}
@@ -2085,55 +2113,71 @@ pub fn relativePosixT(comptime T: type, from: []const T, to: []const T, buf: []T
// return `${out}${StringPrototypeSlice(to, toStart + lastCommonSep)}`;
toStart = if (lastCommonSep != null) toStart + lastCommonSep.? else 0;
const sliceSize = toOrigLen - toStart;
const outLen = out.len;
bufSize = outLen;
if (sliceSize > 0) {
bufOffset = bufSize;
bufSize += sliceSize;
// Use bun.copy because toOrig and buf overlap.
bun.copy(T, buf[bufOffset..bufSize], toOrig[toStart..toOrigLen]);
const currentLen = buf2.items.len;
buf2.ensureTotalCapacity(currentLen + sliceSize + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = currentLen + sliceSize;
bun.memmove(buf2.allocatedSlice()[currentLen .. currentLen + sliceSize], toOrigSaved[toStart..toOrigLen]);
}
if (outLen > 0) {
bun.memmove(buf[0..outLen], out);
}
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
const finalLen = buf2.items.len;
buf2.ensureTotalCapacity(finalLen + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = finalLen + 1;
buf2.items[finalLen] = 0;
return .{ .result = buf2.items[0..finalLen :0] };
}
/// Based on Node v21.6.1 path.win32.relative:
/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L500
pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) MaybeSlice(T) {
pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T), buf3: *std.ArrayList(T)) MaybeSlice(T) {
comptime validatePathT(T, "relativeWindowsT");
// validateString of `from` and `to` are performed in pub fn relative.
if (std.mem.eql(T, from, to)) {
return MaybeSlice(T){ .result = &.{} };
return .{ .result = &.{} };
}
// Backed by expandable buf2 because fromOrig may be long.
// Resolve fromOrig into buf2
buf2.clearRetainingCapacity();
const fromOrig = switch (resolveWindowsT(T, &.{from}, buf2, buf3)) {
.result => |r| r,
.err => |e| return MaybeSlice(T){ .err = e },
.err => |e| return .{ .err = e },
};
const fromOrigLen = fromOrig.len;
// Backed by buf.
const toOrig = switch (resolveWindowsT(T, &.{to}, buf, buf3)) {
// Save fromOrig to buf3 to avoid aliasing when resolving toOrig
buf3.clearRetainingCapacity();
buf3.ensureTotalCapacity(fromOrigLen) catch return .{ .err = Syscall.Error.oom };
buf3.items.len = fromOrigLen;
bun.memmove(buf3.items[0..fromOrigLen], fromOrig);
const fromOrigSaved = buf3.items[0..fromOrigLen];
// Resolve toOrig into buf
buf.clearRetainingCapacity();
const toOrig = switch (resolveWindowsT(T, &.{to}, buf, buf2)) {
.result => |r| r,
.err => |e| return MaybeSlice(T){ .err = e },
.err => |e| return .{ .err = e },
};
if (std.mem.eql(T, fromOrig, toOrig) or
eqlIgnoreCaseT(T, fromOrig, toOrig))
{
return MaybeSlice(T){ .result = &.{} };
}
const toOrigLen = toOrig.len;
// Save toOrig to the end of buf3, including the sentinel
buf3.ensureTotalCapacity(fromOrigLen + toOrigLen + 1) catch return .{ .err = Syscall.Error.oom };
buf3.items.len = fromOrigLen + toOrigLen + 1;
bun.memmove(buf3.items[fromOrigLen .. fromOrigLen + toOrigLen], toOrig);
buf3.items[fromOrigLen + toOrigLen] = 0;
const toOrigSaved = buf3.items[fromOrigLen .. fromOrigLen + toOrigLen :0];
if (std.mem.eql(T, fromOrigSaved, toOrigSaved) or
eqlIgnoreCaseT(T, fromOrigSaved, toOrigSaved))
{
return .{ .result = &.{} };
}
// Trim leading backslashes
var fromStart: usize = 0;
while (fromStart < fromOrigLen and
fromOrig[fromStart] == CHAR_BACKWARD_SLASH)
fromOrigSaved[fromStart] == CHAR_BACKWARD_SLASH)
{
fromStart += 1;
}
@@ -2141,7 +2185,7 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
// Trim trailing backslashes (applicable to UNC paths only)
var fromEnd = fromOrigLen;
while (fromEnd - 1 > fromStart and
fromOrig[fromEnd - 1] == CHAR_BACKWARD_SLASH)
fromOrigSaved[fromEnd - 1] == CHAR_BACKWARD_SLASH)
{
fromEnd -= 1;
}
@@ -2151,7 +2195,7 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
// Trim leading backslashes
var toStart: usize = 0;
while (toStart < toOrigLen and
toOrig[toStart] == CHAR_BACKWARD_SLASH)
toOrigSaved[toStart] == CHAR_BACKWARD_SLASH)
{
toStart = toStart + 1;
}
@@ -2159,7 +2203,7 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
// Trim trailing backslashes (applicable to UNC paths only)
var toEnd = toOrigLen;
while (toEnd - 1 > toStart and
toOrig[toEnd - 1] == CHAR_BACKWARD_SLASH)
toOrigSaved[toEnd - 1] == CHAR_BACKWARD_SLASH)
{
toEnd -= 1;
}
@@ -2176,8 +2220,8 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
{
var i: usize = 0;
while (i < smallestLength) : (i += 1) {
const fromByte = fromOrig[fromStart + i];
if (toLowerT(T, fromByte) != toLowerT(T, toOrig[toStart + i])) {
const fromByte = fromOrigSaved[fromStart + i];
if (toLowerT(T, fromByte) != toLowerT(T, toOrigSaved[toStart + i])) {
break;
} else if (fromByte == CHAR_BACKWARD_SLASH) {
lastCommonSep = i;
@@ -2190,23 +2234,23 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
// return the original `to`.
if (!matchesAllOfSmallest) {
if (lastCommonSep == null) {
return MaybeSlice(T){ .result = toOrig };
return .{ .result = toOrigSaved };
}
} else {
if (toLen > smallestLength) {
if (toOrig[toStart + smallestLength] == CHAR_BACKWARD_SLASH) {
if (toOrigSaved[toStart + smallestLength] == CHAR_BACKWARD_SLASH) {
// We get here if `from` is the exact base path for `to`.
// For example: from='C:\foo\bar'; to='C:\foo\bar\baz'
return MaybeSlice(T){ .result = toOrig[toStart + smallestLength + 1 .. toOrigLen :0] };
return .{ .result = toOrigSaved[toStart + smallestLength + 1 .. toOrigLen :0] };
}
if (smallestLength == 2) {
// We get here if `from` is the device root.
// For example: from='C:\'; to='C:\foo'
return MaybeSlice(T){ .result = toOrig[toStart + smallestLength .. toOrigLen :0] };
return .{ .result = toOrigSaved[toStart + smallestLength .. toOrigLen :0] };
}
}
if (fromLen > smallestLength) {
if (fromOrig[fromStart + smallestLength] == CHAR_BACKWARD_SLASH) {
if (fromOrigSaved[fromStart + smallestLength] == CHAR_BACKWARD_SLASH) {
// We get here if `to` is the exact base path for `from`.
// For example: from='C:\foo\bar'; to='C:\foo'
lastCommonSep = smallestLength;
@@ -2221,10 +2265,8 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
}
}
var bufOffset: usize = 0;
var bufSize: usize = 0;
// Backed by buf3.
// Build "out" using buf2
buf2.clearRetainingCapacity();
var out: []const T = &.{};
// Add a block to isolate `i`.
{
@@ -2232,21 +2274,20 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
// and `from`.
var i: usize = fromStart + (if (lastCommonSep != null) lastCommonSep.? + 1 else 0);
while (i <= fromEnd) : (i += 1) {
if (i == fromEnd or fromOrig[i] == CHAR_BACKWARD_SLASH) {
if (i == fromEnd or fromOrigSaved[i] == CHAR_BACKWARD_SLASH) {
// Translated from the following JS code:
// out += out.length === 0 ? '..' : '\\..';
if (out.len > 0) {
bufOffset = bufSize;
bufSize += 3;
buf3[bufOffset] = CHAR_BACKWARD_SLASH;
buf3[bufOffset + 1] = CHAR_DOT;
buf3[bufOffset + 2] = CHAR_DOT;
buf2.ensureTotalCapacity(buf2.items.len + 3) catch return .{ .err = Syscall.Error.oom };
buf2.appendAssumeCapacity(CHAR_BACKWARD_SLASH);
buf2.appendAssumeCapacity(CHAR_DOT);
buf2.appendAssumeCapacity(CHAR_DOT);
} else {
bufSize = 2;
buf3[0] = CHAR_DOT;
buf3[1] = CHAR_DOT;
buf2.ensureTotalCapacity(2) catch return .{ .err = Syscall.Error.oom };
buf2.appendAssumeCapacity(CHAR_DOT);
buf2.appendAssumeCapacity(CHAR_DOT);
}
out = buf3[0..bufSize];
out = buf2.items[0..buf2.items.len];
}
}
}
@@ -2266,32 +2307,34 @@ pub fn relativeWindowsT(comptime T: type, from: []const T, to: []const T, buf: [
const outLen = out.len;
if (outLen > 0) {
const sliceSize = toEnd - toStart;
bufSize = outLen;
const totalLen = outLen + sliceSize;
buf.clearRetainingCapacity();
buf.ensureTotalCapacity(totalLen + 1) catch return .{ .err = Syscall.Error.oom };
buf.items.len = totalLen + 1;
// Copy "out" first
bun.memmove(buf.items[0..outLen], out);
// Then append the slice from toOrig
if (sliceSize > 0) {
bufOffset = bufSize;
bufSize += sliceSize;
// Use bun.copy because toOrig and buf overlap.
bun.copy(T, buf[bufOffset..bufSize], toOrig[toStart..toEnd]);
bun.memmove(buf.items[outLen .. outLen + sliceSize], toOrigSaved[toStart..toEnd]);
}
bun.memmove(buf[0..outLen], out);
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
buf.items[totalLen] = 0;
return .{ .result = buf.items[0..totalLen :0] };
}
if (toOrig[toStart] == CHAR_BACKWARD_SLASH) {
if (toOrigSaved[toStart] == CHAR_BACKWARD_SLASH) {
toStart += 1;
}
return MaybeSlice(T){ .result = toOrig[toStart..toEnd :0] };
return .{ .result = toOrigSaved[toStart..toEnd :0] };
}
pub fn relativePosixJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) bun.JSError!jsc.JSValue {
pub fn relativePosixJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, from: []const T, to: []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T), buf3: *std.ArrayList(T)) bun.JSError!jsc.JSValue {
return switch (relativePosixT(T, from, to, buf, buf2, buf3)) {
.result => |r| bun.String.createUTF8ForJS(globalObject, r),
.err => |e| e.toJS(globalObject),
};
}
pub fn relativeWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, from: []const T, to: []const T, buf: []T, buf2: []T, buf3: []T) bun.JSError!jsc.JSValue {
pub fn relativeWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, from: []const T, to: []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T), buf3: *std.ArrayList(T)) bun.JSError!jsc.JSValue {
return switch (relativeWindowsT(T, from, to, buf, buf2, buf3)) {
.result => |r| bun.String.createUTF8ForJS(globalObject, r),
.err => |e| e.toJS(globalObject),
@@ -2300,14 +2343,13 @@ pub fn relativeWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject,
pub fn relativeJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, allocator: std.mem.Allocator, isWindows: bool, from: []const T, to: []const T) bun.JSError!jsc.JSValue {
const bufLen = @max(from.len + to.len, PATH_SIZE(T));
// +1 for null terminator
const buf = bun.handleOom(allocator.alloc(T, bufLen + 1));
defer allocator.free(buf);
const buf2 = bun.handleOom(allocator.alloc(T, bufLen + 1));
defer allocator.free(buf2);
const buf3 = bun.handleOom(allocator.alloc(T, bufLen + 1));
defer allocator.free(buf3);
return if (isWindows) relativeWindowsJS_T(T, globalObject, from, to, buf, buf2, buf3) else relativePosixJS_T(T, globalObject, from, to, buf, buf2, buf3);
var buf = try std.ArrayList(T).initCapacity(allocator, bufLen + 1);
defer buf.deinit();
var buf2 = try std.ArrayList(T).initCapacity(allocator, bufLen + 1);
defer buf2.deinit();
var buf3 = try std.ArrayList(T).initCapacity(allocator, bufLen + 1);
defer buf3.deinit();
return if (isWindows) relativeWindowsJS_T(T, globalObject, from, to, &buf, &buf2, &buf3) else relativePosixJS_T(T, globalObject, from, to, &buf, &buf2, &buf3);
}
pub fn relative(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]jsc.JSValue, args_len: u16) bun.JSError!jsc.JSValue {
@@ -2318,36 +2360,31 @@ pub fn relative(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*
// Supress exeption in zig. It does globalThis.vm().throwError() in JS land.
try validateString(globalObject, to_ptr, "to", .{});
const fromZigStr = try from_ptr.getZigString(globalObject);
const toZigStr = try to_ptr.getZigString(globalObject);
if ((fromZigStr.len + toZigStr.len) == 0) return from_ptr;
const fromZigStr = try from_ptr.toJSString(globalObject);
const toZigStr = try to_ptr.toJSString(globalObject);
if (fromZigStr.length() == 0 and toZigStr.length() == 0) return from_ptr;
var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, bun.default_allocator);
const allocator = stack_fallback.get();
var fromZigSlice = fromZigStr.toSlice(allocator);
var fromZigSlice = fromZigStr.toSlice(globalObject, allocator);
defer fromZigSlice.deinit();
var toZigSlice = toZigStr.toSlice(allocator);
var toZigSlice = toZigStr.toSlice(globalObject, allocator);
defer toZigSlice.deinit();
return relativeJS_T(u8, globalObject, allocator, isWindows, fromZigSlice.slice(), toZigSlice.slice());
}
/// Based on Node v21.6.1 path.posix.resolve:
/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L1095
pub fn resolvePosixT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) MaybeSlice(T) {
pub fn resolvePosixT(comptime T: type, paths: []const []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T)) MaybeSlice(T) {
comptime validatePathT(T, "resolvePosixT");
// Backed by expandable buf2 because resolvedPath may be long.
// We use buf2 here because resolvePosixT is called by other methods and using
// buf2 here avoids stepping on others' toes.
var resolvedPath: [:0]const T = undefined;
resolvedPath.len = 0;
var resolvedPathLen: usize = 0;
buf2.clearRetainingCapacity();
var resolvedAbsolute: bool = false;
var bufOffset: usize = 0;
var bufSize: usize = 0;
var i_i64: i64 = if (paths.len == 0) -1 else @as(i64, @intCast(paths.len - 1));
while (i_i64 > -2 and !resolvedAbsolute) : (i_i64 -= 1) {
var path: []const T = &.{};
@@ -2358,7 +2395,7 @@ pub fn resolvePosixT(comptime T: type, paths: []const []const T, buf: []T, buf2:
var tmpBuf: [MAX_PATH_SIZE(T)]T = undefined;
path = switch (posixCwdT(T, &tmpBuf)) {
.result => |r| r,
.err => |e| return MaybeSlice(T){ .err = e },
.err => |e| return .{ .err = e },
};
}
// validateString of `path` is performed in pub fn resolve.
@@ -2371,58 +2408,69 @@ pub fn resolvePosixT(comptime T: type, paths: []const []const T, buf: []T, buf2:
// Translated from the following JS code:
// resolvedPath = `${path}/${resolvedPath}`;
if (resolvedPathLen > 0) {
bufOffset = len + 1;
bufSize = bufOffset + resolvedPathLen;
// Move all bytes to the right by path.len + 1 for the separator.
// Use bun.copy because resolvedPath and buf2 overlap.
bun.copy(u8, buf2[bufOffset..bufSize], resolvedPath);
const oldLen = buf2.items.len;
if (oldLen > 0) {
const newSize = len + 1 + oldLen;
buf2.ensureTotalCapacity(newSize + 1) catch return .{ .err = Syscall.Error.oom };
// Move existing content to the right
bun.memmove(buf2.allocatedSlice()[len + 1 .. newSize], buf2.items[0..oldLen]);
// Copy new path at the beginning
bun.memmove(buf2.allocatedSlice()[0..len], path);
// Add separator
buf2.allocatedSlice()[len] = CHAR_FORWARD_SLASH;
buf2.items.len = newSize;
} else {
// First path
buf2.ensureTotalCapacity(len) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = len;
bun.memmove(buf2.allocatedSlice()[0..len], path);
}
bufSize = len;
bun.memmove(buf2[0..bufSize], path);
bufSize += 1;
buf2[len] = CHAR_FORWARD_SLASH;
bufSize += resolvedPathLen;
buf2[bufSize] = 0;
resolvedPath = buf2[0..bufSize :0];
resolvedPathLen = bufSize;
resolvedAbsolute = path[0] == CHAR_FORWARD_SLASH;
}
// Exit early for empty path.
if (resolvedPathLen == 0) {
return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) };
if (buf2.items.len == 0) {
return .{ .result = comptime L(T, CHAR_STR_DOT) };
}
// At this point the path should be resolved to a full absolute path, but
// handle relative paths to be safe (might happen when process.cwd() fails)
// Normalize the path
resolvedPath = normalizeStringT(T, resolvedPath, !resolvedAbsolute, CHAR_FORWARD_SLASH, .posix, buf);
// resolvedPath is now backed by buf.
resolvedPathLen = resolvedPath.len;
// Normalize the path using buf for the normalized result
// We need a separate buffer for normalization to avoid aliasing
buf.clearRetainingCapacity();
buf.ensureTotalCapacity(buf2.items.len + 2) catch return .{ .err = Syscall.Error.oom };
buf.items.len = buf.capacity;
const normalized = normalizeStringT(T, buf2.items, !resolvedAbsolute, CHAR_FORWARD_SLASH, .posix, buf.allocatedSlice());
// Translated from the following JS code:
// if (resolvedAbsolute) {
// return `/${resolvedPath}`;
// }
if (resolvedAbsolute) {
bufSize = resolvedPathLen + 1;
// Use bun.copy because resolvedPath and buf overlap.
bun.copy(T, buf[1..bufSize], resolvedPath);
buf[0] = CHAR_FORWARD_SLASH;
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
// normalized is already in buf, we just need to prepend '/' and add null terminator
const normalizedLen = normalized.len;
const resultLen = normalizedLen + 1;
// Ensure capacity for the result
buf.ensureTotalCapacity(resultLen + 1) catch return .{ .err = Syscall.Error.oom };
// Move normalized content to make room for leading slash
if (normalizedLen > 0 and normalized.ptr != buf.allocatedSlice().ptr + 1) {
bun.memmove(buf.allocatedSlice()[1..resultLen], normalized);
}
buf.items.len = resultLen + 1;
buf.items[0] = CHAR_FORWARD_SLASH;
buf.items[resultLen] = 0;
return .{ .result = buf.items[0..resultLen :0] };
}
// Translated from the following JS code:
// return resolvedPath.length > 0 ? resolvedPath : '.';
return MaybeSlice(T){ .result = if (resolvedPathLen > 0) resolvedPath else comptime L(T, CHAR_STR_DOT) };
return .{ .result = if (normalized.len > 0) normalized else comptime L(T, CHAR_STR_DOT) };
}
/// Based on Node v21.6.1 path.win32.resolve:
/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L162
pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf2: []T) MaybeSlice(T) {
pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T)) MaybeSlice(T) {
comptime validatePathT(T, "resolveWindowsT");
const isSepT = isSepWindowsT;
@@ -2481,14 +2529,19 @@ pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf
}
bufSize = 1;
// Reuse buf2 for the env key because it's used to get the path.
buf2[0] = '=';
buf2.clearRetainingCapacity();
buf2.ensureTotalCapacity(1 + resolvedDeviceLen + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = 1;
buf2.items[0] = '=';
bufOffset = bufSize;
bufSize += resolvedDeviceLen;
bun.memmove(buf2[bufOffset..bufSize], resolvedDevice);
buf2.ensureTotalCapacity(bufSize + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = bufSize;
bun.memmove(buf2.items[bufOffset..bufSize], resolvedDevice);
if (T == u16) {
break :brk buf2[0..bufSize];
break :brk buf2.items[0..bufSize];
} else {
bufSize = std.unicode.wtf16LeToWtf8(buf2[0..bufSize], &u16Buf);
bufSize = std.unicode.wtf16LeToWtf8(buf2.items[0..bufSize], &u16Buf);
break :brk u16Buf[0..bufSize :0];
}
};
@@ -2500,12 +2553,18 @@ pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf
if (std.process.getenvW(key_w)) |r| {
if (T == u16) {
bufSize = r.len;
bun.memmove(buf2[0..bufSize], r);
buf2.clearRetainingCapacity();
buf2.ensureTotalCapacity(bufSize) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = bufSize;
bun.memmove(buf2.items[0..bufSize], r);
} else {
// Reuse buf2 because it's used for path.
bufSize = std.unicode.wtf16LeToWtf8(buf2, r);
buf2.clearRetainingCapacity();
buf2.ensureTotalCapacity(r.len * 3) catch return .{ .err = Syscall.Error.oom }; // wtf16 to utf8 can expand
bufSize = std.unicode.wtf16LeToWtf8(buf2.allocatedSlice(), r);
buf2.items.len = bufSize;
}
envPath = buf2[0..bufSize];
envPath = buf2.items[0..bufSize];
}
}
if (envPath) |_envPath| {
@@ -2535,11 +2594,14 @@ pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf
// Translated from the following JS code:
// path = `${resolvedDevice}\\`;
bufSize = resolvedDeviceLen;
bun.memmove(buf2[0..bufSize], resolvedDevice);
buf2.clearRetainingCapacity();
buf2.ensureTotalCapacity(bufSize + 1) catch return .{ .err = Syscall.Error.oom };
buf2.items.len = bufSize + 1;
bun.memmove(buf2.items[0..bufSize], resolvedDevice);
bufOffset = bufSize;
bufSize += 1;
buf2[bufOffset] = CHAR_BACKWARD_SLASH;
path = buf2[0..bufSize];
buf2.items[bufOffset] = CHAR_BACKWARD_SLASH;
path = buf2.items[0..bufSize];
}
}
@@ -2668,23 +2730,27 @@ pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf
// Translated from the following JS code:
// resolvedTail = `${StringPrototypeSlice(path, rootEnd)}\\${resolvedTail}`;
const sliceLen = len - rootEnd;
const newTailLen = sliceLen + 1 + resolvedTailLen;
buf2.ensureTotalCapacity(newTailLen) catch return .{ .err = Syscall.Error.oom };
if (resolvedTailLen > 0) {
bufOffset = sliceLen + 1;
bufSize = bufOffset + resolvedTailLen;
// Move all bytes to the right by path slice.len + 1 for the separator
// Use bun.copy because resolvedTail and buf2 overlap.
bun.copy(u8, buf2[bufOffset..bufSize], resolvedTail);
bun.copy(T, buf2.allocatedSlice()[bufOffset..bufSize], resolvedTail);
}
bufSize = sliceLen;
if (sliceLen > 0) {
bun.memmove(buf2[0..bufSize], path[rootEnd..len]);
bun.memmove(buf2.allocatedSlice()[0..bufSize], path[rootEnd..len]);
}
bufOffset = bufSize;
bufSize += 1;
buf2[bufOffset] = CHAR_BACKWARD_SLASH;
buf2.allocatedSlice()[bufOffset] = CHAR_BACKWARD_SLASH;
bufSize += resolvedTailLen;
buf2.items.len = bufSize;
resolvedTail = buf2[0..bufSize];
resolvedTail = buf2.items[0..bufSize];
resolvedTailLen = bufSize;
resolvedAbsolute = _isAbsolute;
@@ -2704,7 +2770,8 @@ pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf
// fails)
// Normalize the tail path
resolvedTail = normalizeStringT(T, resolvedTail, !resolvedAbsolute, CHAR_BACKWARD_SLASH, .windows, buf);
buf.ensureTotalCapacity(resolvedTailLen + 1) catch return .{ .err = Syscall.Error.oom };
resolvedTail = normalizeStringT(T, resolvedTail, !resolvedAbsolute, CHAR_BACKWARD_SLASH, .windows, buf.allocatedSlice());
// resolvedTail is now backed by buf.
resolvedTailLen = resolvedTail.len;
@@ -2713,35 +2780,39 @@ pub fn resolveWindowsT(comptime T: type, paths: []const []const T, buf: []T, buf
if (resolvedAbsolute) {
bufOffset = resolvedDeviceLen + 1;
bufSize = bufOffset + resolvedTailLen;
buf.ensureTotalCapacity(bufSize + 1) catch return .{ .err = Syscall.Error.oom };
// Use bun.copy because resolvedTail and buf overlap.
bun.copy(T, buf[bufOffset..bufSize], resolvedTail);
buf[resolvedDeviceLen] = CHAR_BACKWARD_SLASH;
bun.memmove(buf[0..resolvedDeviceLen], resolvedDevice);
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
bun.copy(T, buf.allocatedSlice()[bufOffset..bufSize], resolvedTail);
buf.allocatedSlice()[resolvedDeviceLen] = CHAR_BACKWARD_SLASH;
bun.memmove(buf.allocatedSlice()[0..resolvedDeviceLen], resolvedDevice);
buf.items.len = bufSize + 1;
buf.items[bufSize] = 0;
return .{ .result = buf.items[0..bufSize :0] };
}
// Translated from the following JS code:
// : `${resolvedDevice}${resolvedTail}` || '.'
if ((resolvedDeviceLen + resolvedTailLen) > 0) {
bufOffset = resolvedDeviceLen;
bufSize = bufOffset + resolvedTailLen;
buf.ensureTotalCapacity(bufSize + 1) catch return .{ .err = Syscall.Error.oom };
// Use bun.copy because resolvedTail and buf overlap.
bun.copy(T, buf[bufOffset..bufSize], resolvedTail);
bun.memmove(buf[0..resolvedDeviceLen], resolvedDevice);
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
bun.copy(T, buf.allocatedSlice()[bufOffset..bufSize], resolvedTail);
bun.memmove(buf.allocatedSlice()[0..resolvedDeviceLen], resolvedDevice);
buf.items.len = bufSize + 1;
buf.items[bufSize] = 0;
return .{ .result = buf.items[0..bufSize :0] };
}
return MaybeSlice(T){ .result = comptime L(T, CHAR_STR_DOT) };
return .{ .result = comptime L(T, CHAR_STR_DOT) };
}
pub fn resolvePosixJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) bun.JSError!jsc.JSValue {
pub fn resolvePosixJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, paths: []const []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T)) bun.JSError!jsc.JSValue {
return switch (resolvePosixT(T, paths, buf, buf2)) {
.result => |r| bun.String.createUTF8ForJS(globalObject, r),
.err => |e| e.toJS(globalObject),
};
}
pub fn resolveWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, paths: []const []const T, buf: []T, buf2: []T) bun.JSError!jsc.JSValue {
pub fn resolveWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, paths: []const []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T)) bun.JSError!jsc.JSValue {
return switch (resolveWindowsT(T, paths, buf, buf2)) {
.result => |r| bun.String.createUTF8ForJS(globalObject, r),
.err => |e| e.toJS(globalObject),
@@ -2754,11 +2825,11 @@ pub fn resolveJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, allocato
for (paths) |path| bufLen += if (bufLen > 0 and path.len > 0) path.len + 1 else path.len;
bufLen = @max(bufLen, PATH_SIZE(T));
// +2 to account for separator and null terminator during path resolution
const buf = try allocator.alloc(T, bufLen + 2);
defer allocator.free(buf);
const buf2 = try allocator.alloc(T, bufLen + 2);
defer allocator.free(buf2);
return if (isWindows) resolveWindowsJS_T(T, globalObject, paths, buf, buf2) else resolvePosixJS_T(T, globalObject, paths, buf, buf2);
var buf = try std.ArrayList(T).initCapacity(allocator, bufLen + 2);
defer buf.deinit();
var buf2 = try std.ArrayList(T).initCapacity(allocator, bufLen + 2);
defer buf2.deinit();
return if (isWindows) resolveWindowsJS_T(T, globalObject, paths, &buf, &buf2) else resolvePosixJS_T(T, globalObject, paths, &buf, &buf2);
}
extern "c" fn Process__getCachedCwd(*jsc.JSGlobalObject) jsc.JSValue;
@@ -2772,6 +2843,15 @@ pub fn resolve(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]
var paths_buf = try allocator.alloc(string, args_len);
defer allocator.free(paths_buf);
var slices = try std.ArrayList(jsc.ZigString.Slice).initCapacity(allocator, args_len);
defer {
for (slices.items) |*slice| {
slice.deinit();
}
slices.deinit();
}
var paths_offset: usize = args_len;
var resolved_root = false;
@@ -2785,18 +2865,20 @@ pub fn resolve(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]
const path = args_ptr[i];
try validateString(globalObject, path, "paths[{d}]", .{i});
const path_str = try path.toBunString(globalObject);
defer path_str.deref();
const path_str = try path.toJSString(globalObject);
if (path_str.length() == 0) {
continue;
}
paths_offset -= 1;
paths_buf[paths_offset] = try path_str.toOwnedSlice(allocator);
// This lets us avoid joining substring internally when possible.
const slice = path_str.toSlice(globalObject, allocator);
slices.appendAssumeCapacity(slice);
paths_buf[paths_offset] = slice.slice();
if (!isWindows) {
if (path_str.charAt(0) == CHAR_FORWARD_SLASH) {
if (slice.slice()[0] == CHAR_FORWARD_SLASH) {
resolved_root = true;
}
}
@@ -2823,26 +2905,27 @@ pub fn resolve(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]
/// Based on Node v21.6.1 path.win32.toNamespacedPath:
/// https://github.com/nodejs/node/blob/6ae20aa63de78294b18d5015481485b7cd8fbb60/lib/path.js#L622
pub fn toNamespacedPathWindowsT(comptime T: type, path: []const T, buf: []T, buf2: []T) MaybeSlice(T) {
pub fn toNamespacedPathWindowsT(comptime T: type, path: []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T)) MaybeSlice(T) {
comptime validatePathT(T, "toNamespacedPathWindowsT");
// validateString of `path` is performed in pub fn toNamespacedPath.
// Backed by buf.
// Resolve path into buf
buf.clearRetainingCapacity();
const resolvedPath = switch (resolveWindowsT(T, &.{path}, buf, buf2)) {
.result => |r| r,
.err => |e| return MaybeSlice(T){ .err = e },
.err => |e| return .{ .err = e },
};
const len = resolvedPath.len;
if (len <= 2) {
@memcpy(buf[0..path.len], path);
buf[path.len] = 0;
return MaybeSlice(T){ .result = buf[0..path.len :0] };
buf.clearRetainingCapacity();
buf.ensureTotalCapacity(path.len + 1) catch return .{ .err = Syscall.Error.oom };
buf.items.len = path.len + 1;
bun.memmove(buf.items[0..path.len], path);
buf.items[path.len] = 0;
return .{ .result = buf.items[0..path.len :0] };
}
var bufOffset: usize = 0;
var bufSize: usize = 0;
const byte0 = resolvedPath[0];
if (byte0 == CHAR_BACKWARD_SLASH) {
// Possible UNC root
@@ -2853,24 +2936,25 @@ pub fn toNamespacedPathWindowsT(comptime T: type, path: []const T, buf: []T, buf
// Translated from the following JS code:
// return `\\\\?\\UNC\\${StringPrototypeSlice(resolvedPath, 2)}`;
bufOffset = 6;
bufSize = len + 6;
const newLen = len + 6;
buf.ensureTotalCapacity(newLen + 1) catch return .{ .err = Syscall.Error.oom };
// Move all bytes to the right by 6 so that the first two bytes are
// overwritten by "\\\\?\\UNC\\" which is 8 bytes long.
// Use bun.copy because resolvedPath and buf overlap.
bun.copy(T, buf[bufOffset..bufSize], resolvedPath);
bun.copy(T, buf.allocatedSlice()[6..newLen], resolvedPath);
// Equiv to std.os.windows.NamespacePrefix.verbatim
// https://github.com/ziglang/zig/blob/dcaf43674e35372e1d28ab12c4c4ff9af9f3d646/lib/std/os/windows.zig#L2358-L2374
buf[0] = CHAR_BACKWARD_SLASH;
buf[1] = CHAR_BACKWARD_SLASH;
buf[2] = CHAR_QUESTION_MARK;
buf[3] = CHAR_BACKWARD_SLASH;
buf[4] = 'U';
buf[5] = 'N';
buf[6] = 'C';
buf[7] = CHAR_BACKWARD_SLASH;
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
buf.items.len = newLen + 1;
buf.items[0] = CHAR_BACKWARD_SLASH;
buf.items[1] = CHAR_BACKWARD_SLASH;
buf.items[2] = CHAR_QUESTION_MARK;
buf.items[3] = CHAR_BACKWARD_SLASH;
buf.items[4] = 'U';
buf.items[5] = 'N';
buf.items[6] = 'C';
buf.items[7] = CHAR_BACKWARD_SLASH;
buf.items[newLen] = 0;
return .{ .result = buf.items[0..newLen :0] };
}
}
} else if (isWindowsDeviceRootT(T, byte0) and
@@ -2881,24 +2965,25 @@ pub fn toNamespacedPathWindowsT(comptime T: type, path: []const T, buf: []T, buf
// Translated from the following JS code:
// return `\\\\?\\${resolvedPath}`
bufOffset = 4;
bufSize = len + 4;
const newLen = len + 4;
buf.ensureTotalCapacity(newLen + 1) catch return .{ .err = Syscall.Error.oom };
// Move all bytes to the right by 4
// Use bun.copy because resolvedPath and buf overlap.
bun.copy(T, buf[bufOffset..bufSize], resolvedPath);
bun.copy(T, buf.allocatedSlice()[4..newLen], resolvedPath);
// Equiv to std.os.windows.NamespacePrefix.verbatim
// https://github.com/ziglang/zig/blob/dcaf43674e35372e1d28ab12c4c4ff9af9f3d646/lib/std/os/windows.zig#L2358-L2374
buf[0] = CHAR_BACKWARD_SLASH;
buf[1] = CHAR_BACKWARD_SLASH;
buf[2] = CHAR_QUESTION_MARK;
buf[3] = CHAR_BACKWARD_SLASH;
buf[bufSize] = 0;
return MaybeSlice(T){ .result = buf[0..bufSize :0] };
buf.items.len = newLen + 1;
buf.items[0] = CHAR_BACKWARD_SLASH;
buf.items[1] = CHAR_BACKWARD_SLASH;
buf.items[2] = CHAR_QUESTION_MARK;
buf.items[3] = CHAR_BACKWARD_SLASH;
buf.items[newLen] = 0;
return .{ .result = buf.items[0..newLen :0] };
}
return MaybeSlice(T){ .result = resolvedPath };
return .{ .result = resolvedPath };
}
pub fn toNamespacedPathWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, path: []const T, buf: []T, buf2: []T) bun.JSError!jsc.JSValue {
pub fn toNamespacedPathWindowsJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject, path: []const T, buf: *std.ArrayList(T), buf2: *std.ArrayList(T)) bun.JSError!jsc.JSValue {
return switch (toNamespacedPathWindowsT(T, path, buf, buf2)) {
.result => |r| bun.String.createUTF8ForJS(globalObject, r),
.err => |e| e.toJS(globalObject),
@@ -2909,11 +2994,11 @@ pub fn toNamespacedPathJS_T(comptime T: type, globalObject: *jsc.JSGlobalObject,
if (!isWindows or path.len == 0) return bun.String.createUTF8ForJS(globalObject, path);
const bufLen = @max(path.len, PATH_SIZE(T));
// +8 for possible UNC prefix, +1 for null terminator
const buf = try allocator.alloc(T, bufLen + 8 + 1);
defer allocator.free(buf);
const buf2 = try allocator.alloc(T, bufLen + 8 + 1);
defer allocator.free(buf2);
return toNamespacedPathWindowsJS_T(T, globalObject, path, buf, buf2);
var buf = try std.ArrayList(T).initCapacity(allocator, bufLen + 8 + 1);
defer buf.deinit();
var buf2 = try std.ArrayList(T).initCapacity(allocator, bufLen + 8 + 1);
defer buf2.deinit();
return toNamespacedPathWindowsJS_T(T, globalObject, path, &buf, &buf2);
}
pub fn toNamespacedPath(globalObject: *jsc.JSGlobalObject, isWindows: bool, args_ptr: [*]jsc.JSValue, args_len: u16) bun.JSError!jsc.JSValue {
@@ -2926,14 +3011,13 @@ pub fn toNamespacedPath(globalObject: *jsc.JSGlobalObject, isWindows: bool, args
//
// Act as an identity function for non-string values and non-Windows platforms.
if (!isWindows or !path_ptr.isString()) return path_ptr;
const pathZStr = try path_ptr.getZigString(globalObject);
const len = pathZStr.len;
if (len == 0) return path_ptr;
const path_jsstring = try path_ptr.toJSString(globalObject);
if (path_jsstring.length() == 0) return path_ptr;
var stack_fallback = std.heap.stackFallback(stack_fallback_size_small, bun.default_allocator);
const allocator = stack_fallback.get();
const pathZSlice = pathZStr.toSlice(allocator);
const pathZSlice = path_jsstring.toSlice(globalObject, allocator);
defer pathZSlice.deinit();
return toNamespacedPathJS_T(u8, globalObject, allocator, isWindows, pathZSlice.slice());
}