aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/event_loop.zig241
-rw-r--r--src/bun.js/node/node_fs.zig2222
-rw-r--r--src/bun.js/node/node_fs_binding.zig62
-rw-r--r--src/bun.js/node/types.zig80
4 files changed, 1263 insertions, 1342 deletions
diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig
index c7de557f4..ed9d5b195 100644
--- a/src/bun.js/event_loop.zig
+++ b/src/bun.js/event_loop.zig
@@ -303,6 +303,48 @@ const PollPendingModulesTask = JSC.ModuleLoader.AsyncModule.Queue;
// const PromiseTask = JSInternalPromise.Completion.PromiseTask;
const GetAddrInfoRequestTask = JSC.DNS.GetAddrInfoRequest.Task;
const JSCDeferredWorkTask = JSCScheduler.JSCDeferredWorkTask;
+
+const Stat = JSC.Node.Async.stat;
+const Lstat = JSC.Node.Async.lstat;
+const Fstat = JSC.Node.Async.fstat;
+const Open = JSC.Node.Async.open;
+const ReadFile = JSC.Node.Async.readFile;
+const WriteFile = JSC.Node.Async.writeFile;
+const CopyFile = JSC.Node.Async.copyFile;
+const Read = JSC.Node.Async.read;
+const Write = JSC.Node.Async.write;
+const Truncate = JSC.Node.Async.truncate;
+const FTruncate = JSC.Node.Async.ftruncate;
+const Readdir = JSC.Node.Async.readdir;
+const Readv = JSC.Node.Async.readv;
+const Writev = JSC.Node.Async.writev;
+const Close = JSC.Node.Async.close;
+const Rm = JSC.Node.Async.rm;
+const Rmdir = JSC.Node.Async.rmdir;
+const Chown = JSC.Node.Async.chown;
+const FChown = JSC.Node.Async.fchown;
+const Utimes = JSC.Node.Async.utimes;
+const Lutimes = JSC.Node.Async.lutimes;
+const Chmod = JSC.Node.Async.chmod;
+const Fchmod = JSC.Node.Async.fchmod;
+const Link = JSC.Node.Async.link;
+const Symlink = JSC.Node.Async.symlink;
+const Readlink = JSC.Node.Async.readlink;
+const Realpath = JSC.Node.Async.realpath;
+const Mkdir = JSC.Node.Async.mkdir;
+const Fsync = JSC.Node.Async.fsync;
+const Rename = JSC.Node.Async.rename;
+const Fdatasync = JSC.Node.Async.fdatasync;
+const Access = JSC.Node.Async.access;
+const AppendFile = JSC.Node.Async.appendFile;
+const Mkdtemp = JSC.Node.Async.mkdtemp;
+const Exists = JSC.Node.Async.exists;
+const Futimes = JSC.Node.Async.futimes;
+const Lchmod = JSC.Node.Async.lchmod;
+const Lchown = JSC.Node.Async.lchown;
+const Unlink = JSC.Node.Async.unlink;
+
+// Task.get(ReadFileTask) -> ?ReadFileTask
pub const Task = TaggedPointerUnion(.{
FetchTasklet,
Microtask,
@@ -321,9 +363,45 @@ pub const Task = TaggedPointerUnion(.{
GetAddrInfoRequestTask,
FSWatchTask,
JSCDeferredWorkTask,
-
- // PromiseTask,
- // TimeoutTasklet,
+ Stat,
+ Lstat,
+ Fstat,
+ Open,
+ ReadFile,
+ WriteFile,
+ CopyFile,
+ Read,
+ Write,
+ Truncate,
+ FTruncate,
+ Readdir,
+ Close,
+ Rm,
+ Rmdir,
+ Chown,
+ FChown,
+ Utimes,
+ Lutimes,
+ Chmod,
+ Fchmod,
+ Link,
+ Symlink,
+ Readlink,
+ Realpath,
+ Mkdir,
+ Fsync,
+ Fdatasync,
+ Writev,
+ Readv,
+ Rename,
+ Access,
+ AppendFile,
+ Mkdtemp,
+ Exists,
+ Futimes,
+ Lchmod,
+ Lchown,
+ Unlink,
});
const UnboundedQueue = @import("./unbounded_queue.zig").UnboundedQueue;
pub const ConcurrentTask = struct {
@@ -535,7 +613,6 @@ pub const EventLoop = struct {
}
extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void;
fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalObject) void {
- JSC.markBinding(@src());
JSC__JSGlobalObject__drainMicrotasks(globalObject);
this.drainDeferredTasks();
}
@@ -662,6 +739,162 @@ pub const EventLoop = struct {
any.runFromJS();
any.deinit();
},
+ @field(Task.Tag, typeBaseName(@typeName(Stat))) => {
+ var any: *Stat = task.get(Stat).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Lstat))) => {
+ var any: *Lstat = task.get(Lstat).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Fstat))) => {
+ var any: *Fstat = task.get(Fstat).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Open))) => {
+ var any: *Open = task.get(Open).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(ReadFile))) => {
+ var any: *ReadFile = task.get(ReadFile).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(WriteFile))) => {
+ var any: *WriteFile = task.get(WriteFile).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(CopyFile))) => {
+ var any: *CopyFile = task.get(CopyFile).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Read))) => {
+ var any: *Read = task.get(Read).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Write))) => {
+ var any: *Write = task.get(Write).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Truncate))) => {
+ var any: *Truncate = task.get(Truncate).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Writev))) => {
+ var any: *Writev = task.get(Writev).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Readv))) => {
+ var any: *Readv = task.get(Readv).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Rename))) => {
+ var any: *Rename = task.get(Rename).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(FTruncate))) => {
+ var any: *FTruncate = task.get(FTruncate).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Readdir))) => {
+ var any: *Readdir = task.get(Readdir).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Close))) => {
+ var any: *Close = task.get(Close).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Rm))) => {
+ var any: *Rm = task.get(Rm).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Rmdir))) => {
+ var any: *Rmdir = task.get(Rmdir).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Chown))) => {
+ var any: *Chown = task.get(Chown).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(FChown))) => {
+ var any: *FChown = task.get(FChown).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Utimes))) => {
+ var any: *Utimes = task.get(Utimes).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Lutimes))) => {
+ var any: *Lutimes = task.get(Lutimes).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Chmod))) => {
+ var any: *Chmod = task.get(Chmod).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Fchmod))) => {
+ var any: *Fchmod = task.get(Fchmod).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Link))) => {
+ var any: *Link = task.get(Link).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Symlink))) => {
+ var any: *Symlink = task.get(Symlink).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Readlink))) => {
+ var any: *Readlink = task.get(Readlink).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Realpath))) => {
+ var any: *Realpath = task.get(Realpath).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Mkdir))) => {
+ var any: *Mkdir = task.get(Mkdir).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Fsync))) => {
+ var any: *Fsync = task.get(Fsync).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Fdatasync))) => {
+ var any: *Fdatasync = task.get(Fdatasync).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Access))) => {
+ var any: *Access = task.get(Access).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(AppendFile))) => {
+ var any: *AppendFile = task.get(AppendFile).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Mkdtemp))) => {
+ var any: *Mkdtemp = task.get(Mkdtemp).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Exists))) => {
+ var any: *Exists = task.get(Exists).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Futimes))) => {
+ var any: *Futimes = task.get(Futimes).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Lchmod))) => {
+ var any: *Lchmod = task.get(Lchmod).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Lchown))) => {
+ var any: *Lchown = task.get(Lchown).?;
+ any.runFromJSThread();
+ },
+ @field(Task.Tag, typeBaseName(@typeName(Unlink))) => {
+ var any: *Unlink = task.get(Unlink).?;
+ any.runFromJSThread();
+ },
else => if (Environment.allow_assert) {
bun.Output.prettyln("\nUnexpected tag: {s}\n", .{@tagName(task.tag())});
} else {
diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig
index d9e3ed119..752d0e2fb 100644
--- a/src/bun.js/node/node_fs.zig
+++ b/src/bun.js/node/node_fs.zig
@@ -55,468 +55,147 @@ else
// TODO:
0;
+const SliceWithUnderlyingStringOrBuffer = JSC.Node.SliceWithUnderlyingStringOrBuffer;
const ArrayBuffer = JSC.MarkedArrayBuffer;
const Buffer = JSC.Buffer;
const FileSystemFlags = JSC.Node.FileSystemFlags;
+pub const Async = struct {
+ pub const access = NewAsyncFSTask(Return.Access, Arguments.Access, NodeFS.access);
+ pub const appendFile = NewAsyncFSTask(Return.AppendFile, Arguments.AppendFile, NodeFS.appendFile);
+ pub const chmod = NewAsyncFSTask(Return.Chmod, Arguments.Chmod, NodeFS.chmod);
+ pub const chown = NewAsyncFSTask(Return.Chown, Arguments.Chown, NodeFS.chown);
+ pub const close = NewAsyncFSTask(Return.Close, Arguments.Close, NodeFS.close);
+ pub const copyFile = NewAsyncFSTask(Return.CopyFile, Arguments.CopyFile, NodeFS.copyFile);
+ pub const exists = NewAsyncFSTask(Return.Exists, Arguments.Exists, NodeFS.exists);
+ pub const fchmod = NewAsyncFSTask(Return.Fchmod, Arguments.FChmod, NodeFS.fchmod);
+ pub const fchown = NewAsyncFSTask(Return.Fchown, Arguments.Fchown, NodeFS.fchown);
+ pub const fdatasync = NewAsyncFSTask(Return.Fdatasync, Arguments.FdataSync, NodeFS.fdatasync);
+ pub const fstat = NewAsyncFSTask(Return.Fstat, Arguments.Fstat, NodeFS.fstat);
+ pub const fsync = NewAsyncFSTask(Return.Fsync, Arguments.Fsync, NodeFS.fsync);
+ pub const ftruncate = NewAsyncFSTask(Return.Ftruncate, Arguments.FTruncate, NodeFS.ftruncate);
+ pub const futimes = NewAsyncFSTask(Return.Futimes, Arguments.Futimes, NodeFS.futimes);
+ pub const lchmod = NewAsyncFSTask(Return.Lchmod, Arguments.LCHmod, NodeFS.lchmod);
+ pub const lchown = NewAsyncFSTask(Return.Lchown, Arguments.LChown, NodeFS.lchown);
+ pub const link = NewAsyncFSTask(Return.Link, Arguments.Link, NodeFS.link);
+ pub const lstat = NewAsyncFSTask(Return.Stat, Arguments.Stat, NodeFS.lstat);
+ pub const lutimes = NewAsyncFSTask(Return.Lutimes, Arguments.Lutimes, NodeFS.lutimes);
+ pub const mkdir = NewAsyncFSTask(Return.Mkdir, Arguments.Mkdir, NodeFS.mkdir);
+ pub const mkdtemp = NewAsyncFSTask(Return.Mkdtemp, Arguments.MkdirTemp, NodeFS.mkdtemp);
+ pub const open = NewAsyncFSTask(Return.Open, Arguments.Open, NodeFS.open);
+ pub const read = NewAsyncFSTask(Return.Read, Arguments.Read, NodeFS.read);
+ pub const readdir = NewAsyncFSTask(Return.Readdir, Arguments.Readdir, NodeFS.readdir);
+ pub const readFile = NewAsyncFSTask(Return.ReadFile, Arguments.ReadFile, NodeFS.readFile);
+ pub const readlink = NewAsyncFSTask(Return.Readlink, Arguments.Readlink, NodeFS.readlink);
+ pub const readv = NewAsyncFSTask(Return.Readv, Arguments.Readv, NodeFS.readv);
+ pub const realpath = NewAsyncFSTask(Return.Realpath, Arguments.Realpath, NodeFS.realpath);
+ pub const rename = NewAsyncFSTask(Return.Rename, Arguments.Rename, NodeFS.rename);
+ pub const rm = NewAsyncFSTask(Return.Rm, Arguments.Rm, NodeFS.rm);
+ pub const rmdir = NewAsyncFSTask(Return.Rmdir, Arguments.RmDir, NodeFS.rmdir);
+ pub const stat = NewAsyncFSTask(Return.Stat, Arguments.Stat, NodeFS.stat);
+ pub const symlink = NewAsyncFSTask(Return.Symlink, Arguments.Symlink, NodeFS.symlink);
+ pub const truncate = NewAsyncFSTask(Return.Truncate, Arguments.Truncate, NodeFS.truncate);
+ pub const unlink = NewAsyncFSTask(Return.Unlink, Arguments.Unlink, NodeFS.unlink);
+ pub const utimes = NewAsyncFSTask(Return.Utimes, Arguments.Utimes, NodeFS.utimes);
+ pub const write = NewAsyncFSTask(Return.Write, Arguments.Write, NodeFS.write);
+ pub const writeFile = NewAsyncFSTask(Return.WriteFile, Arguments.WriteFile, NodeFS.writeFile);
+ pub const writev = NewAsyncFSTask(Return.Writev, Arguments.Writev, NodeFS.writev);
+
+ pub const cp = AsyncCpTask;
+
+ fn NewAsyncFSTask(comptime ReturnType: type, comptime ArgumentType: type, comptime Function: anytype) type {
+ return struct {
+ promise: JSC.JSPromise.Strong,
+ args: ArgumentType,
+ globalObject: *JSC.JSGlobalObject,
+ task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
+ result: JSC.Maybe(ReturnType),
+ ref: JSC.PollRef = .{},
+ tracker: JSC.AsyncTaskTracker,
+
+ pub const Task = @This();
+
+ pub fn create(
+ globalObject: *JSC.JSGlobalObject,
+ args: ArgumentType,
+ vm: *JSC.VirtualMachine,
+ ) JSC.JSValue {
+ var task = bun.default_allocator.create(Task) catch @panic("out of memory");
+ task.* = Task{
+ .promise = JSC.JSPromise.Strong.init(globalObject),
+ .args = args,
+ .result = undefined,
+ .globalObject = globalObject,
+ .tracker = JSC.AsyncTaskTracker.init(vm),
+ };
+ task.ref.ref(vm);
+ task.args.toThreadSafe();
+ task.tracker.didSchedule(globalObject);
+ JSC.WorkPool.schedule(&task.task);
-pub const AsyncReaddirTask = struct {
- promise: JSC.JSPromise.Strong,
- args: Arguments.Readdir,
- globalObject: *JSC.JSGlobalObject,
- task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
- result: JSC.Maybe(Return.Readdir),
- ref: JSC.PollRef = .{},
- arena: bun.ArenaAllocator,
- tracker: JSC.AsyncTaskTracker,
-
- pub fn create(globalObject: *JSC.JSGlobalObject, readdir_args: Arguments.Readdir, vm: *JSC.VirtualMachine, arena: bun.ArenaAllocator) JSC.JSValue {
- var task = bun.default_allocator.create(AsyncReaddirTask) catch @panic("out of memory");
- task.* = AsyncReaddirTask{
- .promise = JSC.JSPromise.Strong.init(globalObject),
- .args = readdir_args,
- .result = undefined,
- .globalObject = globalObject,
- .arena = arena,
- .tracker = JSC.AsyncTaskTracker.init(vm),
- };
- task.ref.ref(vm);
- task.args.path.toThreadSafe();
- task.tracker.didSchedule(globalObject);
- JSC.WorkPool.schedule(&task.task);
-
- return task.promise.value();
- }
-
- fn workPoolCallback(task: *JSC.WorkPoolTask) void {
- var this: *AsyncReaddirTask = @fieldParentPtr(AsyncReaddirTask, "task", task);
-
- var node_fs = NodeFS{};
- this.result = node_fs.readdir(this.args, .promise);
-
- if (this.result == .err) {
- this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch "";
- }
-
- this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
- }
-
- fn runFromJSThread(this: *AsyncReaddirTask) void {
- var globalObject = this.globalObject;
-
- var success = @as(JSC.Maybe(Return.Readdir).Tag, this.result) == .result;
- const result = switch (this.result) {
- .err => |err| err.toJSC(globalObject),
- .result => |res| brk: {
- var exceptionref: JSC.C.JSValueRef = null;
- const out = JSC.JSValue.c(JSC.To.JS.withType(Return.Readdir, res, globalObject, &exceptionref));
- const exception = JSC.JSValue.c(exceptionref);
- if (exception != .zero) {
- success = false;
- break :brk exception;
- }
-
- break :brk out;
- },
- };
- var promise_value = this.promise.value();
- var promise = this.promise.get();
- promise_value.ensureStillAlive();
-
- const tracker = this.tracker;
- this.deinit();
-
- tracker.willDispatch(globalObject);
- defer tracker.didDispatch(globalObject);
- switch (success) {
- false => {
- promise.reject(globalObject, result);
- },
- true => {
- promise.resolve(globalObject, result);
- },
- }
- }
-
- pub fn deinit(this: *AsyncReaddirTask) void {
- this.ref.unref(this.globalObject.bunVM());
- this.args.deinitAndUnprotect();
- this.promise.strong.deinit();
- this.arena.deinit();
- bun.default_allocator.destroy(this);
- }
-};
-
-pub const AsyncStatTask = struct {
- promise: JSC.JSPromise.Strong,
- args: Arguments.Stat,
- globalObject: *JSC.JSGlobalObject,
- task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
- result: JSC.Maybe(Return.Stat),
- ref: JSC.PollRef = .{},
- is_lstat: bool = false,
- arena: bun.ArenaAllocator,
- tracker: JSC.AsyncTaskTracker,
-
- pub fn create(
- globalObject: *JSC.JSGlobalObject,
- readdir_args: Arguments.Stat,
- vm: *JSC.VirtualMachine,
- is_lstat: bool,
- arena: bun.ArenaAllocator,
- ) JSC.JSValue {
- var task = bun.default_allocator.create(AsyncStatTask) catch @panic("out of memory");
- task.* = AsyncStatTask{
- .promise = JSC.JSPromise.Strong.init(globalObject),
- .args = readdir_args,
- .result = undefined,
- .globalObject = globalObject,
- .is_lstat = is_lstat,
- .tracker = JSC.AsyncTaskTracker.init(vm),
- .arena = arena,
- };
- task.ref.ref(vm);
- task.args.path.toThreadSafe();
- task.tracker.didSchedule(globalObject);
-
- JSC.WorkPool.schedule(&task.task);
-
- return task.promise.value();
- }
-
- fn workPoolCallback(task: *JSC.WorkPoolTask) void {
- var this: *AsyncStatTask = @fieldParentPtr(AsyncStatTask, "task", task);
-
- var node_fs = NodeFS{};
- this.result = if (this.is_lstat)
- node_fs.lstat(this.args, .promise)
- else
- node_fs.stat(this.args, .promise);
-
- this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
- }
-
- fn runFromJSThread(this: *AsyncStatTask) void {
- var globalObject = this.globalObject;
- var success = @as(JSC.Maybe(Return.Lstat).Tag, this.result) == .result;
- const result = switch (this.result) {
- .err => |err| err.toJSC(globalObject),
- .result => |res| brk: {
- var exceptionref: JSC.C.JSValueRef = null;
- const out = JSC.JSValue.c(JSC.To.JS.withType(Return.Lstat, res, globalObject, &exceptionref));
- const exception = JSC.JSValue.c(exceptionref);
- if (exception != .zero) {
- success = false;
- break :brk exception;
- }
-
- break :brk out;
- },
- };
- var promise_value = this.promise.value();
- var promise = this.promise.get();
- promise_value.ensureStillAlive();
-
- const tracker = this.tracker;
- tracker.willDispatch(globalObject);
- defer tracker.didDispatch(globalObject);
-
- this.deinit();
- switch (success) {
- false => {
- promise.reject(globalObject, result);
- },
- true => {
- promise.resolve(globalObject, result);
- },
- }
- }
-
- pub fn deinit(this: *AsyncStatTask) void {
- this.ref.unref(this.globalObject.bunVM());
- this.args.deinitAndUnprotect();
- this.promise.strong.deinit();
- this.arena.deinit();
- bun.default_allocator.destroy(this);
- }
-};
-
-pub const AsyncRealpathTask = struct {
- promise: JSC.JSPromise.Strong,
- args: Arguments.Realpath,
- globalObject: *JSC.JSGlobalObject,
- task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
- result: JSC.Maybe(Return.Realpath),
- ref: JSC.PollRef = .{},
- arena: bun.ArenaAllocator,
- tracker: JSC.AsyncTaskTracker,
-
- pub fn create(
- globalObject: *JSC.JSGlobalObject,
- args: Arguments.Realpath,
- vm: *JSC.VirtualMachine,
- arena: bun.ArenaAllocator,
- ) JSC.JSValue {
- var task = bun.default_allocator.create(AsyncRealpathTask) catch @panic("out of memory");
- task.* = AsyncRealpathTask{
- .promise = JSC.JSPromise.Strong.init(globalObject),
- .args = args,
- .result = undefined,
- .globalObject = globalObject,
- .arena = arena,
- .tracker = JSC.AsyncTaskTracker.init(vm),
- };
- task.ref.ref(vm);
- task.args.path.toThreadSafe();
- task.tracker.didSchedule(globalObject);
- JSC.WorkPool.schedule(&task.task);
-
- return task.promise.value();
- }
-
- fn workPoolCallback(task: *JSC.WorkPoolTask) void {
- var this: *AsyncRealpathTask = @fieldParentPtr(AsyncRealpathTask, "task", task);
+ return task.promise.value();
+ }
- var node_fs = NodeFS{};
- this.result = node_fs.realpath(this.args, .promise);
+ fn workPoolCallback(task: *JSC.WorkPoolTask) void {
+ var this: *Task = @fieldParentPtr(Task, "task", task);
- if (this.result == .err) {
- this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch "";
- }
+ var node_fs = NodeFS{};
+ this.result = Function(&node_fs, this.args, .promise);
- this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
- }
-
- fn runFromJSThread(this: *AsyncRealpathTask) void {
- var globalObject = this.globalObject;
- var success = @as(JSC.Maybe(Return.Realpath).Tag, this.result) == .result;
- const result = switch (this.result) {
- .err => |err| err.toJSC(globalObject),
- .result => |res| brk: {
- var exceptionref: JSC.C.JSValueRef = null;
- const out = JSC.JSValue.c(JSC.To.JS.withType(Return.Realpath, res, globalObject, &exceptionref));
- const exception = JSC.JSValue.c(exceptionref);
- if (exception != .zero) {
- success = false;
- break :brk exception;
+ if (this.result == .err) {
+ this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch "";
+ std.mem.doNotOptimizeAway(&node_fs);
}
- break :brk out;
- },
- };
- var promise_value = this.promise.value();
- var promise = this.promise.get();
- promise_value.ensureStillAlive();
-
- const tracker = this.tracker;
- tracker.willDispatch(globalObject);
- defer tracker.didDispatch(globalObject);
-
- this.deinit();
- switch (success) {
- false => {
- promise.reject(globalObject, result);
- },
- true => {
- promise.resolve(globalObject, result);
- },
- }
- }
-
- pub fn deinit(this: *AsyncRealpathTask) void {
- if (this.result == .err) {
- bun.default_allocator.free(this.result.err.path);
- }
-
- this.ref.unref(this.globalObject.bunVM());
- this.args.deinitAndUnprotect();
- this.promise.strong.deinit();
- this.arena.deinit();
- bun.default_allocator.destroy(this);
- }
-};
-
-pub const AsyncReadFileTask = struct {
- promise: JSC.JSPromise.Strong,
- args: Arguments.ReadFile,
- globalObject: *JSC.JSGlobalObject,
- task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
- result: JSC.Maybe(Return.ReadFile),
- ref: JSC.PollRef = .{},
- arena: bun.ArenaAllocator,
- tracker: JSC.AsyncTaskTracker,
-
- pub fn create(
- globalObject: *JSC.JSGlobalObject,
- args: Arguments.ReadFile,
- vm: *JSC.VirtualMachine,
- arena: bun.ArenaAllocator,
- ) JSC.JSValue {
- var task = bun.default_allocator.create(AsyncReadFileTask) catch @panic("out of memory");
- task.* = AsyncReadFileTask{
- .promise = JSC.JSPromise.Strong.init(globalObject),
- .args = args,
- .result = undefined,
- .globalObject = globalObject,
- .arena = arena,
- .tracker = JSC.AsyncTaskTracker.init(vm),
- };
- task.ref.ref(vm);
- task.args.path.toThreadSafe();
- task.tracker.didSchedule(globalObject);
- JSC.WorkPool.schedule(&task.task);
-
- return task.promise.value();
- }
-
- fn workPoolCallback(task: *JSC.WorkPoolTask) void {
- var this: *AsyncReadFileTask = @fieldParentPtr(AsyncReadFileTask, "task", task);
-
- var node_fs = NodeFS{};
- this.result = node_fs.readFile(this.args, .promise);
-
- if (this.result == .err) {
- this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch "";
- }
-
- this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
- }
+ this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this)));
+ }
- fn runFromJSThread(this: *AsyncReadFileTask) void {
- var globalObject = this.globalObject;
+ pub fn runFromJSThread(this: *Task) void {
+ var globalObject = this.globalObject;
+ var success = @as(JSC.Maybe(ReturnType).Tag, this.result) == .result;
+ const result = switch (this.result) {
+ .err => |err| err.toJSC(globalObject),
+ .result => |res| brk: {
+ var exceptionref: JSC.C.JSValueRef = null;
+ const out = JSC.JSValue.c(JSC.To.JS.withType(ReturnType, res, globalObject, &exceptionref));
+ const exception = JSC.JSValue.c(exceptionref);
+ if (exception != .zero) {
+ success = false;
+ break :brk exception;
+ }
- var success = @as(JSC.Maybe(Return.ReadFile).Tag, this.result) == .result;
- const result = switch (this.result) {
- .err => |err| err.toJSC(globalObject),
- .result => |res| brk: {
- var exceptionref: JSC.C.JSValueRef = null;
- const out = JSC.JSValue.c(JSC.To.JS.withType(Return.ReadFile, res, globalObject, &exceptionref));
- const exception = JSC.JSValue.c(exceptionref);
- if (exception != .zero) {
- success = false;
- break :brk exception;
+ break :brk out;
+ },
+ };
+ var promise_value = this.promise.value();
+ var promise = this.promise.get();
+ promise_value.ensureStillAlive();
+
+ const tracker = this.tracker;
+ tracker.willDispatch(globalObject);
+ defer tracker.didDispatch(globalObject);
+
+ this.deinit();
+ switch (success) {
+ false => {
+ promise.reject(globalObject, result);
+ },
+ true => {
+ promise.resolve(globalObject, result);
+ },
}
+ }
- break :brk out;
- },
- };
- var promise_value = this.promise.value();
- var promise = this.promise.get();
- promise_value.ensureStillAlive();
-
- const tracker = this.tracker;
- tracker.willDispatch(globalObject);
- defer tracker.didDispatch(globalObject);
-
- this.deinit();
- switch (success) {
- false => {
- promise.reject(globalObject, result);
- },
- true => {
- promise.resolve(globalObject, result);
- },
- }
- }
-
- pub fn deinit(this: *AsyncReadFileTask) void {
- this.ref.unref(this.globalObject.bunVM());
- this.args.deinitAndUnprotect();
- this.promise.strong.deinit();
- this.arena.deinit();
- bun.default_allocator.destroy(this);
- }
-};
-
-pub const AsyncCopyFileTask = struct {
- promise: JSC.JSPromise.Strong,
- args: Arguments.CopyFile,
- globalObject: *JSC.JSGlobalObject,
- task: JSC.WorkPoolTask = .{ .callback = &workPoolCallback },
- result: JSC.Maybe(Return.CopyFile),
- ref: JSC.PollRef = .{},
- arena: bun.ArenaAllocator,
- tracker: JSC.AsyncTaskTracker,
-
- pub fn create(
- globalObject: *JSC.JSGlobalObject,
- copyfile_args: Arguments.CopyFile,
- vm: *JSC.VirtualMachine,
- arena: bun.ArenaAllocator,
- ) JSC.JSValue {
- var task = bun.default_allocator.create(AsyncCopyFileTask) catch @panic("out of memory");
- task.* = AsyncCopyFileTask{
- .promise = JSC.JSPromise.Strong.init(globalObject),
- .args = copyfile_args,
- .result = undefined,
- .globalObject = globalObject,
- .tracker = JSC.AsyncTaskTracker.init(vm),
- .arena = arena,
- };
- task.ref.ref(vm);
- task.args.src.toThreadSafe();
- task.args.dest.toThreadSafe();
- task.tracker.didSchedule(globalObject);
-
- JSC.WorkPool.schedule(&task.task);
-
- return task.promise.value();
- }
-
- fn workPoolCallback(task: *JSC.WorkPoolTask) void {
- var this: *AsyncCopyFileTask = @fieldParentPtr(AsyncCopyFileTask, "task", task);
-
- var node_fs = NodeFS{};
- this.result = node_fs.copyFile(this.args, .promise);
-
- if (this.result == .err) {
- this.result.err.path = bun.default_allocator.dupe(u8, this.result.err.path) catch "";
- }
-
- this.globalObject.bunVMConcurrently().eventLoop().enqueueTaskConcurrent(JSC.ConcurrentTask.fromCallback(this, runFromJSThread));
- }
-
- fn runFromJSThread(this: *AsyncCopyFileTask) void {
- var globalObject = this.globalObject;
- var success = @as(JSC.Maybe(Return.CopyFile).Tag, this.result) == .result;
- const result = switch (this.result) {
- .err => |err| err.toJSC(globalObject),
- .result => |res| brk: {
- var exceptionref: JSC.C.JSValueRef = null;
- const out = JSC.JSValue.c(JSC.To.JS.withType(Return.CopyFile, res, globalObject, &exceptionref));
- const exception = JSC.JSValue.c(exceptionref);
- if (exception != .zero) {
- success = false;
- break :brk exception;
+ pub fn deinit(this: *Task) void {
+ if (this.result == .err) {
+ bun.default_allocator.free(this.result.err.path);
}
- break :brk out;
- },
+ this.ref.unref(this.globalObject.bunVM());
+ this.args.deinit();
+ this.promise.strong.deinit();
+ bun.default_allocator.destroy(this);
+ }
};
- var promise_value = this.promise.value();
- var promise = this.promise.get();
- promise_value.ensureStillAlive();
-
- const tracker = this.tracker;
- tracker.willDispatch(globalObject);
- defer tracker.didDispatch(globalObject);
-
- this.deinit();
- switch (success) {
- false => {
- promise.reject(globalObject, result);
- },
- true => {
- promise.resolve(globalObject, result);
- },
- }
- }
-
- pub fn deinit(this: *AsyncCopyFileTask) void {
- this.ref.unref(this.globalObject.bunVM());
- this.args.deinit();
- this.promise.strong.deinit();
- this.arena.deinit();
- bun.default_allocator.destroy(this);
}
};
@@ -710,6 +389,16 @@ pub const Arguments = struct {
this.new_path.deinit();
}
+ pub fn deinitAndUnprotect(this: @This()) void {
+ this.old_path.deinitAndUnprotect();
+ this.new_path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.old_path.toThreadSafe();
+ this.new_path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Rename {
const old_path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -748,8 +437,16 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: *@This()) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Truncate {
- const path = PathOrFileDescriptor.fromJS(ctx, arguments, arguments.arena.allocator(), exception) orelse {
+ const path = PathOrFileDescriptor.fromJS(ctx, arguments, bun.default_allocator, exception) orelse {
if (exception.* == null) {
JSC.throwInvalidArguments(
"path must be a string or TypedArray",
@@ -783,6 +480,21 @@ pub const Arguments = struct {
pub fn deinit(_: *const @This()) void {}
+ pub fn deinitAndUnprotect(this: *const @This()) void {
+ this.buffers.value.unprotect();
+ this.buffers.buffers.deinit();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.buffers.value.protect();
+
+ var clone = bun.default_allocator.dupe(std.os.iovec, this.buffers.buffers.items) catch @panic("out of memory");
+ this.buffers.buffers.deinit();
+ this.buffers.buffers.items = clone;
+ this.buffers.buffers.capacity = clone.len;
+ this.buffers.buffers.allocator = bun.default_allocator;
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Writev {
const fd_value = arguments.nextEat() orelse {
if (exception.* == null) {
@@ -855,7 +567,23 @@ pub const Arguments = struct {
buffers: JSC.Node.VectorArrayBuffer,
position: ?u52 = 0,
- pub fn deinit(_: *const @This()) void {}
+ pub fn deinit(this: *const @This()) void {
+ _ = this;
+ }
+
+ pub fn deinitAndUnprotect(this: *const @This()) void {
+ this.buffers.value.unprotect();
+ this.buffers.buffers.deinit();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.buffers.value.protect();
+ var clone = bun.default_allocator.dupe(std.os.iovec, this.buffers.buffers.items) catch @panic("out of memory");
+ this.buffers.buffers.deinit();
+ this.buffers.buffers.items = clone;
+ this.buffers.buffers.capacity = clone.len;
+ this.buffers.buffers.allocator = bun.default_allocator;
+ }
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Readv {
const fd_value = arguments.nextEat() orelse {
@@ -932,6 +660,14 @@ pub const Arguments = struct {
_ = this;
}
+ pub fn deinitAndUnprotect(this: *@This()) void {
+ _ = this;
+ }
+
+ pub fn toThreadSafe(this: *const @This()) void {
+ _ = this;
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FTruncate {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -982,6 +718,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: *@This()) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Chown {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1040,6 +784,8 @@ pub const Arguments = struct {
pub fn deinit(_: @This()) void {}
+ pub fn toThreadSafe(_: *const @This()) void {}
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Fchown {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -1114,6 +860,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: *@This()) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Lutimes {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1189,6 +943,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn toThreadSafe(this: *@This()) void {
+ this.path.toThreadSafe();
+ }
+
+ pub fn deinitAndUnprotect(this: *@This()) void {
+ this.path.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Chmod {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1234,6 +996,10 @@ pub const Arguments = struct {
fd: FileDescriptor,
mode: Mode = 0x777,
+ pub fn deinit(_: *const @This()) void {}
+
+ pub fn toThreadSafe(_: *const @This()) void {}
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FChmod {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -1303,6 +1069,10 @@ pub const Arguments = struct {
this.path.deinitAndUnprotect();
}
+ pub fn toThreadSafe(this: *Stat) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Stat {
const path = PathLike.fromJSWithAllocator(ctx, arguments, bun.default_allocator, exception) orelse {
if (exception.* == null) {
@@ -1356,6 +1126,8 @@ pub const Arguments = struct {
pub fn deinit(_: @This()) void {}
+ pub fn toThreadSafe(_: *@This()) void {}
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Fstat {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -1412,6 +1184,16 @@ pub const Arguments = struct {
this.new_path.deinit();
}
+ pub fn deinitAndUnprotect(this: *Link) void {
+ this.old_path.deinitAndUnprotect();
+ this.new_path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *Link) void {
+ this.old_path.toThreadSafe();
+ this.new_path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Link {
const old_path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1454,6 +1236,16 @@ pub const Arguments = struct {
this.new_path.deinit();
}
+ pub fn deinitAndUnprotect(this: Symlink) void {
+ this.old_path.deinitAndUnprotect();
+ this.new_path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *@This()) void {
+ this.old_path.toThreadSafe();
+ this.new_path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Symlink {
const old_path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1512,6 +1304,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: *Readlink) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *Readlink) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Readlink {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1560,6 +1360,10 @@ pub const Arguments = struct {
this.path.deinitAndUnprotect();
}
+ pub fn toThreadSafe(this: *Realpath) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Realpath {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1599,6 +1403,18 @@ pub const Arguments = struct {
pub const Unlink = struct {
path: PathLike,
+ pub fn deinit(this: Unlink) void {
+ this.path.deinit();
+ }
+
+ pub fn deinitAndUnprotect(this: *Unlink) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *Unlink) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Unlink {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1620,13 +1436,7 @@ pub const Arguments = struct {
}
};
- pub const Rm = struct {
- path: PathLike,
- force: bool = false,
- max_retries: u32 = 0,
- recursive: bool = false,
- retry_delay: c_uint = 100,
- };
+ pub const Rm = RmDir;
pub const RmDir = struct {
path: PathLike,
@@ -1637,6 +1447,14 @@ pub const Arguments = struct {
recursive: bool = false,
retry_delay: c_uint = 100,
+ pub fn deinitAndUnprotect(this: *RmDir) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *RmDir) void {
+ this.path.toThreadSafe();
+ }
+
pub fn deinit(this: RmDir) void {
this.path.deinit();
}
@@ -1701,6 +1519,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: *Mkdir) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *Mkdir) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: *JSC.JSGlobalObject, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Mkdir {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1752,10 +1578,18 @@ pub const Arguments = struct {
this.prefix.deinit();
}
+ pub fn deinitAndUnprotect(this: *MkdirTemp) void {
+ this.prefix.deinit();
+ }
+
+ pub fn toThreadSafe(this: *MkdirTemp) void {
+ this.prefix.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?MkdirTemp {
const prefix_value = arguments.next() orelse return MkdirTemp{};
- var prefix = JSC.Node.SliceOrBuffer.fromJS(ctx, arguments.arena.allocator(), prefix_value) orelse {
+ var prefix = JSC.Node.SliceOrBuffer.fromJS(ctx, bun.default_allocator, prefix_value) orelse {
if (exception.* == null) {
JSC.throwInvalidArguments(
"prefix must be a string or TypedArray",
@@ -1810,6 +1644,10 @@ pub const Arguments = struct {
this.path.deinitAndUnprotect();
}
+ pub fn toThreadSafe(this: *Readdir) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Readdir {
const path = PathLike.fromJSWithAllocator(ctx, arguments, bun.default_allocator, exception) orelse {
if (exception.* == null) {
@@ -1864,6 +1702,7 @@ pub const Arguments = struct {
fd: FileDescriptor,
pub fn deinit(_: Close) void {}
+ pub fn toThreadSafe(_: Close) void {}
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Close {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
@@ -1905,6 +1744,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn deinitAndUnprotect(this: Open) void {
+ this.path.deinitAndUnprotect();
+ }
+
+ pub fn toThreadSafe(this: *Open) void {
+ this.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Open {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -1971,6 +1818,10 @@ pub const Arguments = struct {
pub fn deinit(_: Futimes) void {}
+ pub fn toThreadSafe(self: *const @This()) void {
+ _ = self;
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Futimes {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -2052,42 +1903,6 @@ pub const Arguments = struct {
}
};
- pub const FSync = struct {
- fd: FileDescriptor,
-
- pub fn deinit(_: FSync) void {}
-
- pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FSync {
- const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
- if (exception.* == null) {
- JSC.throwInvalidArguments(
- "File descriptor is required",
- .{},
- ctx,
- exception,
- );
- }
- return null;
- }, exception) orelse {
- if (exception.* == null) {
- JSC.throwInvalidArguments(
- "fd must be a number",
- .{},
- ctx,
- exception,
- );
- }
- return null;
- };
-
- if (exception.* != null) return null;
-
- return FSync{
- .fd = fd,
- };
- }
- };
-
/// Write `buffer` to the file specified by `fd`. If `buffer` is a normal object, it
/// must have an own `toString` function property.
///
@@ -2114,7 +1929,7 @@ pub const Arguments = struct {
///
pub const Write = struct {
fd: FileDescriptor,
- buffer: StringOrBuffer,
+ buffer: JSC.Node.SliceWithUnderlyingStringOrBuffer,
// buffer_val: JSC.JSValue = JSC.JSValue.zero,
offset: u64 = 0,
length: u64 = std.math.maxInt(u64),
@@ -2123,6 +1938,10 @@ pub const Arguments = struct {
pub fn deinit(_: Write) void {}
+ pub fn toThreadSafe(self: *@This()) void {
+ self.buffer.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Write {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -2150,7 +1969,7 @@ pub const Arguments = struct {
if (exception.* != null) return null;
- const buffer = StringOrBuffer.fromJS(ctx.ptr(), arguments.arena.allocator(), arguments.next() orelse {
+ const buffer = SliceWithUnderlyingStringOrBuffer.fromJS(ctx.ptr(), bun.default_allocator, arguments.next() orelse {
if (exception.* == null) {
JSC.throwInvalidArguments(
"data is required",
@@ -2177,7 +1996,7 @@ pub const Arguments = struct {
.fd = fd,
.buffer = buffer,
.encoding = switch (buffer) {
- .string => Encoding.utf8,
+ .SliceWithUnderlyingString => Encoding.utf8,
.buffer => Encoding.buffer,
},
};
@@ -2190,7 +2009,7 @@ pub const Arguments = struct {
var current = current_;
switch (buffer) {
// fs.write(fd, string[, position[, encoding]], callback)
- .string => {
+ .SliceWithUnderlyingString => {
if (current.isNumber()) {
args.position = current.to(i52);
arguments.eat();
@@ -2239,6 +2058,14 @@ pub const Arguments = struct {
pub fn deinit(_: Read) void {}
+ pub fn toThreadSafe(this: Read) void {
+ this.buffer.buffer.value.protect();
+ }
+
+ pub fn deinitAndUnprotect(this: *Read) void {
+ this.buffer.buffer.value.unprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Read {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -2377,6 +2204,10 @@ pub const Arguments = struct {
self.path.deinitAndUnprotect();
}
+ pub fn toThreadSafe(self: *ReadFile) void {
+ self.path.toThreadSafe();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?ReadFile {
const path = PathOrFileDescriptor.fromJS(ctx, arguments, bun.default_allocator, exception) orelse {
if (exception.* == null) {
@@ -2463,8 +2294,16 @@ pub const Arguments = struct {
self.file.deinit();
}
+ pub fn toThreadSafe(self: *WriteFile) void {
+ self.file.toThreadSafe();
+ }
+
+ pub fn deinitAndUnprotect(self: *WriteFile) void {
+ self.file.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?WriteFile {
- const file = PathOrFileDescriptor.fromJS(ctx, arguments, arguments.arena.allocator(), exception) orelse {
+ const file = PathOrFileDescriptor.fromJS(ctx, arguments, bun.default_allocator, exception) orelse {
if (exception.* == null) {
JSC.throwInvalidArguments(
"path must be a string or a file descriptor",
@@ -2478,7 +2317,7 @@ pub const Arguments = struct {
if (exception.* != null) return null;
- const data = StringOrBuffer.fromJS(ctx.ptr(), arguments.arena.allocator(), arguments.next() orelse {
+ const data = StringOrBuffer.fromJS(ctx.ptr(), bun.default_allocator, arguments.next() orelse {
if (exception.* == null) {
JSC.throwInvalidArguments(
"data is required",
@@ -2673,6 +2512,18 @@ pub const Arguments = struct {
}
}
+ pub fn toThreadSafe(this: *Exists) void {
+ if (this.path) |*path| {
+ path.toThreadSafe();
+ }
+ }
+
+ pub fn deinitAndUnprotect(this: *Exists) void {
+ if (this.path) |*path| {
+ path.deinitAndUnprotect();
+ }
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Exists {
return Exists{
.path = PathLike.fromJS(ctx, arguments, exception),
@@ -2688,6 +2539,14 @@ pub const Arguments = struct {
this.path.deinit();
}
+ pub fn toThreadSafe(this: *Access) void {
+ this.path.toThreadSafe();
+ }
+
+ pub fn deinitAndUnprotect(this: *Access) void {
+ this.path.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Access {
const path = PathLike.fromJS(ctx, arguments, exception) orelse {
if (exception.* == null) {
@@ -3002,6 +2861,9 @@ pub const Arguments = struct {
fd: FileDescriptor,
pub fn deinit(_: FdataSync) void {}
+ pub fn toThreadSafe(self: *const @This()) void {
+ _ = self;
+ }
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?FdataSync {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
@@ -3039,11 +2901,21 @@ pub const Arguments = struct {
dest: PathLike,
mode: Constants.Copyfile,
- fn deinit(this: CopyFile) void {
+ pub fn deinit(this: CopyFile) void {
this.src.deinit();
this.dest.deinit();
}
+ pub fn toThreadSafe(this: *CopyFile) void {
+ this.src.toThreadSafe();
+ this.dest.toThreadSafe();
+ }
+
+ pub fn deinitAndUnprotect(this: *CopyFile) void {
+ this.src.deinitAndUnprotect();
+ this.dest.deinitAndUnprotect();
+ }
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?CopyFile {
const src = PathLike.fromJSWithAllocator(ctx, arguments, bun.default_allocator, exception) orelse {
if (exception.* == null) {
@@ -3196,6 +3068,9 @@ pub const Arguments = struct {
pub const Fsync = struct {
fd: FileDescriptor,
+ pub fn deinit(_: Fsync) void {}
+ pub fn toThreadSafe(_: *const @This()) void {}
+
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?Fsync {
const fd = JSC.Node.fileDescriptorFromJS(ctx, arguments.next() orelse {
if (exception.* == null) {
@@ -3411,69 +3286,49 @@ pub const NodeFS = struct {
}
pub fn appendFile(this: *NodeFS, args: Arguments.AppendFile, comptime flavor: Flavor) Maybe(Return.AppendFile) {
+ _ = flavor;
var data = args.data.slice();
switch (args.file) {
.fd => |fd| {
- switch (comptime flavor) {
- .sync => {
- while (data.len > 0) {
- const written = switch (Syscall.write(fd, data)) {
- .result => |result| result,
- .err => |err| return .{ .err = err },
- };
- data = data[written..];
- }
-
- return Maybe(Return.AppendFile).success;
- },
- else => {
- @compileError("Not implemented yet");
- },
+ while (data.len > 0) {
+ const written = switch (Syscall.write(fd, data)) {
+ .result => |result| result,
+ .err => |err| return .{ .err = err },
+ };
+ data = data[written..];
}
+
+ return Maybe(Return.AppendFile).success;
},
.path => |path_| {
const path = path_.sliceZ(&this.sync_error_buf);
- switch (comptime flavor) {
- .sync => {
- const fd = switch (Syscall.open(path, @intFromEnum(FileSystemFlags.a), 0o000666)) {
- .result => |result| result,
- .err => |err| return .{ .err = err },
- };
- defer {
- _ = Syscall.close(fd);
- }
+ const fd = switch (Syscall.open(path, @intFromEnum(FileSystemFlags.a), 0o000666)) {
+ .result => |result| result,
+ .err => |err| return .{ .err = err },
+ };
- while (data.len > 0) {
- const written = switch (Syscall.write(fd, data)) {
- .result => |result| result,
- .err => |err| return .{ .err = err },
- };
- data = data[written..];
- }
+ defer {
+ _ = Syscall.close(fd);
+ }
- return Maybe(Return.AppendFile).success;
- },
- else => {
- @compileError("Not implemented yet");
- },
+ while (data.len > 0) {
+ const written = switch (Syscall.write(fd, data)) {
+ .result => |result| result,
+ .err => |err| return .{ .err = err },
+ };
+ data = data[written..];
}
+
+ return Maybe(Return.AppendFile).success;
},
}
-
- return Maybe(Return.AppendFile).todo;
}
pub fn close(_: *NodeFS, args: Arguments.Close, comptime flavor: Flavor) Maybe(Return.Close) {
- switch (comptime flavor) {
- .sync => {
- return if (Syscall.close(args.fd)) |err| .{ .err = err } else Maybe(Return.Close).success;
- },
- else => {},
- }
-
- return .{ .err = Syscall.Error.todo };
+ _ = flavor;
+ return if (Syscall.close(args.fd)) |err| .{ .err = err } else Maybe(Return.Close).success;
}
// since we use a 64 KB stack buffer, we should not let this function get inlined
@@ -3714,132 +3569,92 @@ pub const NodeFS = struct {
}
pub fn exists(this: *NodeFS, args: Arguments.Exists, comptime flavor: Flavor) Maybe(Return.Exists) {
+ _ = flavor;
const Ret = Maybe(Return.Exists);
- switch (comptime flavor) {
- .sync => {
- const path = args.path orelse return Ret{ .result = false };
- const slice = path.sliceZ(&this.sync_error_buf);
- // access() may not work correctly on NFS file systems with UID
- // mapping enabled, because UID mapping is done on the server and
- // hidden from the client, which checks permissions. Similar
- // problems can occur to FUSE mounts.
- const rc = (system.access(slice, std.os.F_OK));
- return Ret{ .result = rc == 0 };
- },
- else => {},
- }
-
- return Ret.todo;
+ const path = args.path orelse return Ret{ .result = false };
+ const slice = path.sliceZ(&this.sync_error_buf);
+ // access() may not work correctly on NFS file systems with UID
+ // mapping enabled, because UID mapping is done on the server and
+ // hidden from the client, which checks permissions. Similar
+ // problems can occur to FUSE mounts.
+ const rc = (system.access(slice, std.os.F_OK));
+ return Ret{ .result = rc == 0 };
}
pub fn chown(this: *NodeFS, args: Arguments.Chown, comptime flavor: Flavor) Maybe(Return.Chown) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fchmod).todo;
}
const path = args.path.sliceZ(&this.sync_error_buf);
- switch (comptime flavor) {
- .sync => return Syscall.chown(path, args.uid, args.gid),
- else => {},
- }
-
- return Maybe(Return.Chown).todo;
+ return Syscall.chown(path, args.uid, args.gid);
}
/// This should almost never be async
pub fn chmod(this: *NodeFS, args: Arguments.Chmod, comptime flavor: Flavor) Maybe(Return.Chmod) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fchmod).todo;
}
const path = args.path.sliceZ(&this.sync_error_buf);
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Chmod).errnoSysP(C.chmod(path, args.mode), .chmod, path) orelse
- Maybe(Return.Chmod).success;
- },
- else => {},
- }
-
- return Maybe(Return.Chmod).todo;
+ return Maybe(Return.Chmod).errnoSysP(C.chmod(path, args.mode), .chmod, path) orelse
+ Maybe(Return.Chmod).success;
}
/// This should almost never be async
pub fn fchmod(_: *NodeFS, args: Arguments.FChmod, comptime flavor: Flavor) Maybe(Return.Fchmod) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fchmod).todo;
}
- switch (comptime flavor) {
- .sync => {
- return Syscall.fchmod(args.fd, args.mode);
- },
- else => {},
- }
-
- return Maybe(Return.Fchmod).todo;
+ return Syscall.fchmod(args.fd, args.mode);
}
pub fn fchown(_: *NodeFS, args: Arguments.Fchown, comptime flavor: Flavor) Maybe(Return.Fchown) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fchown).todo;
}
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Fchown).errnoSys(C.fchown(args.fd, args.uid, args.gid), .fchown) orelse
- Maybe(Return.Fchown).success;
- },
- else => {},
- }
-
- return Maybe(Return.Fchown).todo;
+ return Maybe(Return.Fchown).errnoSys(C.fchown(args.fd, args.uid, args.gid), .fchown) orelse
+ Maybe(Return.Fchown).success;
}
pub fn fdatasync(_: *NodeFS, args: Arguments.FdataSync, comptime flavor: Flavor) Maybe(Return.Fdatasync) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fdatasync).todo;
}
- switch (comptime flavor) {
- .sync => return Maybe(Return.Fdatasync).errnoSys(system.fdatasync(args.fd), .fdatasync) orelse
- Maybe(Return.Fdatasync).success,
- else => {},
- }
-
- return Maybe(Return.Fdatasync).todo;
+ return Maybe(Return.Fdatasync).errnoSys(system.fdatasync(args.fd), .fdatasync) orelse
+ Maybe(Return.Fdatasync).success;
}
pub fn fstat(_: *NodeFS, args: Arguments.Fstat, comptime flavor: Flavor) Maybe(Return.Fstat) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fstat).todo;
}
- switch (comptime flavor) {
- .sync => {
- if (comptime Environment.isPosix) {
- return switch (Syscall.fstat(args.fd)) {
- .result => |result| Maybe(Return.Fstat){ .result = Stats.init(result, false) },
- .err => |err| Maybe(Return.Fstat){ .err = err },
- };
- }
- },
- else => {},
+ if (comptime Environment.isPosix) {
+ return switch (Syscall.fstat(args.fd)) {
+ .result => |result| Maybe(Return.Fstat){ .result = Stats.init(result, false) },
+ .err => |err| Maybe(Return.Fstat){ .err = err },
+ };
}
return Maybe(Return.Fstat).todo;
}
pub fn fsync(_: *NodeFS, args: Arguments.Fsync, comptime flavor: Flavor) Maybe(Return.Fsync) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Fsync).todo;
}
- switch (comptime flavor) {
- .sync => return Maybe(Return.Fsync).errnoSys(system.fsync(args.fd), .fsync) orelse
- Maybe(Return.Fsync).success,
- else => {},
- }
-
- return Maybe(Return.Fsync).todo;
+ return Maybe(Return.Fsync).errnoSys(system.fsync(args.fd), .fsync) orelse
+ Maybe(Return.Fsync).success;
}
pub fn ftruncateSync(args: Arguments.FTruncate) Maybe(Return.Ftruncate) {
@@ -3847,14 +3662,11 @@ pub const NodeFS = struct {
}
pub fn ftruncate(_: *NodeFS, args: Arguments.FTruncate, comptime flavor: Flavor) Maybe(Return.Ftruncate) {
- switch (comptime flavor) {
- .sync => return ftruncateSync(args),
- else => {},
- }
-
- return Maybe(Return.Ftruncate).todo;
+ _ = flavor;
+ return ftruncateSync(args);
}
pub fn futimes(_: *NodeFS, args: Arguments.Futimes, comptime flavor: Flavor) Maybe(Return.Futimes) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Futimes).todo;
}
@@ -3870,66 +3682,43 @@ pub const NodeFS = struct {
},
};
- switch (comptime flavor) {
- .sync => return if (Maybe(Return.Futimes).errnoSys(system.futimens(args.fd, &times), .futimens)) |err|
- err
- else
- Maybe(Return.Futimes).success,
- else => {},
- }
-
- return Maybe(Return.Futimes).todo;
+ return if (Maybe(Return.Futimes).errnoSys(system.futimens(args.fd, &times), .futimens)) |err|
+ err
+ else
+ Maybe(Return.Futimes).success;
}
pub fn lchmod(this: *NodeFS, args: Arguments.LCHmod, comptime flavor: Flavor) Maybe(Return.Lchmod) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Lchmod).todo;
}
const path = args.path.sliceZ(&this.sync_error_buf);
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Lchmod).errnoSysP(C.lchmod(path, args.mode), .lchmod, path) orelse
- Maybe(Return.Lchmod).success;
- },
- else => {},
- }
-
- return Maybe(Return.Lchmod).todo;
+ return Maybe(Return.Lchmod).errnoSysP(C.lchmod(path, args.mode), .lchmod, path) orelse
+ Maybe(Return.Lchmod).success;
}
pub fn lchown(this: *NodeFS, args: Arguments.LChown, comptime flavor: Flavor) Maybe(Return.Lchown) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Lchown).todo;
}
const path = args.path.sliceZ(&this.sync_error_buf);
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Lchown).errnoSysP(C.lchown(path, args.uid, args.gid), .lchown, path) orelse
- Maybe(Return.Lchown).success;
- },
- else => {},
- }
-
- return Maybe(Return.Lchown).todo;
+ return Maybe(Return.Lchown).errnoSysP(C.lchown(path, args.uid, args.gid), .lchown, path) orelse
+ Maybe(Return.Lchown).success;
}
pub fn link(this: *NodeFS, args: Arguments.Link, comptime flavor: Flavor) Maybe(Return.Link) {
+ _ = flavor;
var new_path_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
const from = args.old_path.sliceZ(&this.sync_error_buf);
const to = args.new_path.sliceZ(&new_path_buf);
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Link).errnoSysP(system.link(from, to, 0), .link, from) orelse
- Maybe(Return.Link).success;
- },
- else => {},
- }
-
- return Maybe(Return.Link).todo;
+ return Maybe(Return.Link).errnoSysP(system.link(from, to, 0), .link, from) orelse
+ Maybe(Return.Link).success;
}
pub fn lstat(this: *NodeFS, args: Arguments.Lstat, comptime flavor: Flavor) Maybe(Return.Lstat) {
if (comptime Environment.isWindows) {
@@ -3957,158 +3746,146 @@ pub const NodeFS = struct {
}
// Node doesn't absolute the path so we don't have to either
fn mkdirNonRecursive(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) {
- switch (comptime flavor) {
- .sync => {
- const path = args.path.sliceZ(&this.sync_error_buf);
- return switch (Syscall.mkdir(path, args.mode)) {
- .result => Maybe(Return.Mkdir){ .result = bun.String.empty },
- .err => |err| Maybe(Return.Mkdir){ .err = err },
- };
- },
- else => {},
- }
+ _ = flavor;
- return Maybe(Return.Mkdir).todo;
+ const path = args.path.sliceZ(&this.sync_error_buf);
+ return switch (Syscall.mkdir(path, args.mode)) {
+ .result => Maybe(Return.Mkdir){ .result = bun.String.empty },
+ .err => |err| Maybe(Return.Mkdir){ .err = err },
+ };
}
// TODO: windows
// TODO: verify this works correctly with unicode codepoints
pub fn mkdirRecursive(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) {
+ _ = flavor;
const Option = Maybe(Return.Mkdir);
if (comptime Environment.isWindows) return Option.todo;
- switch (comptime flavor) {
- // The sync version does no allocation except when returning the path
- .sync => {
- var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
- const path = args.path.sliceZWithForceCopy(&buf, true);
- const len = @as(u16, @truncate(path.len));
-
- // First, attempt to create the desired directory
- // If that fails, then walk back up the path until we have a match
- switch (Syscall.mkdir(path, args.mode)) {
- .err => |err| {
- switch (err.getErrno()) {
- else => {
- @memcpy(this.sync_error_buf[0..len], path[0..len]);
- return .{ .err = err.withPath(this.sync_error_buf[0..len]) };
- },
+ var buf: [bun.MAX_PATH_BYTES]u8 = undefined;
+ const path = args.path.sliceZWithForceCopy(&buf, true);
+ const len = @as(u16, @truncate(path.len));
- .EXIST => {
- return Option{ .result = bun.String.empty };
- },
- // continue
- .NOENT => {},
- }
+ // First, attempt to create the desired directory
+ // If that fails, then walk back up the path until we have a match
+ switch (Syscall.mkdir(path, args.mode)) {
+ .err => |err| {
+ switch (err.getErrno()) {
+ else => {
+ @memcpy(this.sync_error_buf[0..len], path[0..len]);
+ return .{ .err = err.withPath(this.sync_error_buf[0..len]) };
},
- .result => {
- return Option{
- .result = if (args.path == .slice_with_underlying_string)
- args.path.slice_with_underlying_string.underlying
- else
- bun.String.create(args.path.slice()),
- };
+
+ .EXIST => {
+ return Option{ .result = bun.String.empty };
},
+ // continue
+ .NOENT => {},
}
+ },
+ .result => {
+ return Option{
+ .result = if (args.path == .slice_with_underlying_string)
+ args.path.slice_with_underlying_string.underlying
+ else
+ bun.String.create(args.path.slice()),
+ };
+ },
+ }
- var working_mem = &this.sync_error_buf;
- @memcpy(working_mem[0..len], path[0..len]);
+ var working_mem = &this.sync_error_buf;
+ @memcpy(working_mem[0..len], path[0..len]);
- var i: u16 = len - 1;
+ var i: u16 = len - 1;
- // iterate backwards until creating the directory works successfully
- while (i > 0) : (i -= 1) {
- if (path[i] == std.fs.path.sep) {
- working_mem[i] = 0;
- var parent: [:0]u8 = working_mem[0..i :0];
+ // iterate backwards until creating the directory works successfully
+ while (i > 0) : (i -= 1) {
+ if (path[i] == std.fs.path.sep) {
+ working_mem[i] = 0;
+ var parent: [:0]u8 = working_mem[0..i :0];
- switch (Syscall.mkdir(parent, args.mode)) {
- .err => |err| {
- working_mem[i] = std.fs.path.sep;
- switch (err.getErrno()) {
- .EXIST => {
- // Handle race condition
- break;
- },
- .NOENT => {
- continue;
- },
- else => return .{ .err = err.withPath(parent) },
- }
- },
- .result => {
- // We found a parent that worked
- working_mem[i] = std.fs.path.sep;
+ switch (Syscall.mkdir(parent, args.mode)) {
+ .err => |err| {
+ working_mem[i] = std.fs.path.sep;
+ switch (err.getErrno()) {
+ .EXIST => {
+ // Handle race condition
break;
},
- }
- }
- }
- var first_match: u16 = i;
- i += 1;
- // after we find one that works, we go forward _after_ the first working directory
- while (i < len) : (i += 1) {
- if (path[i] == std.fs.path.sep) {
- working_mem[i] = 0;
- var parent: [:0]u8 = working_mem[0..i :0];
-
- switch (Syscall.mkdir(parent, args.mode)) {
- .err => |err| {
- working_mem[i] = std.fs.path.sep;
- switch (err.getErrno()) {
- .EXIST => {
- if (Environment.allow_assert) std.debug.assert(false);
- continue;
- },
- else => return .{ .err = err },
- }
- },
-
- .result => {
- working_mem[i] = std.fs.path.sep;
+ .NOENT => {
+ continue;
},
+ else => return .{ .err = err.withPath(parent) },
}
- }
+ },
+ .result => {
+ // We found a parent that worked
+ working_mem[i] = std.fs.path.sep;
+ break;
+ },
}
+ }
+ }
+ var first_match: u16 = i;
+ i += 1;
+ // after we find one that works, we go forward _after_ the first working directory
+ while (i < len) : (i += 1) {
+ if (path[i] == std.fs.path.sep) {
+ working_mem[i] = 0;
+ var parent: [:0]u8 = working_mem[0..i :0];
- working_mem[len] = 0;
-
- // Our final directory will not have a trailing separator
- // so we have to create it once again
- switch (Syscall.mkdir(working_mem[0..len :0], args.mode)) {
+ switch (Syscall.mkdir(parent, args.mode)) {
.err => |err| {
+ working_mem[i] = std.fs.path.sep;
switch (err.getErrno()) {
- // handle the race condition
.EXIST => {
- var display_path = bun.String.empty;
- if (first_match != std.math.maxInt(u16)) {
- display_path = bun.String.create(working_mem[0..first_match]);
- }
- return Option{ .result = display_path };
- },
-
- // NOENT shouldn't happen here
- else => return .{
- .err = err.withPath(path),
+ if (Environment.allow_assert) std.debug.assert(false);
+ continue;
},
+ else => return .{ .err = err },
}
},
+
.result => {
- return Option{
- .result = if (first_match != std.math.maxInt(u16))
- bun.String.create(working_mem[0..first_match])
- else if (args.path == .slice_with_underlying_string)
- args.path.slice_with_underlying_string.underlying
- else
- bun.String.create(args.path.slice()),
- };
+ working_mem[i] = std.fs.path.sep;
},
}
- },
- else => {},
+ }
}
- return Maybe(Return.Mkdir).todo;
+ working_mem[len] = 0;
+
+ // Our final directory will not have a trailing separator
+ // so we have to create it once again
+ switch (Syscall.mkdir(working_mem[0..len :0], args.mode)) {
+ .err => |err| {
+ switch (err.getErrno()) {
+ // handle the race condition
+ .EXIST => {
+ var display_path = bun.String.empty;
+ if (first_match != std.math.maxInt(u16)) {
+ display_path = bun.String.create(working_mem[0..first_match]);
+ }
+ return Option{ .result = display_path };
+ },
+
+ // NOENT shouldn't happen here
+ else => return .{
+ .err = err.withPath(path),
+ },
+ }
+ },
+ .result => {
+ return Option{
+ .result = if (first_match != std.math.maxInt(u16))
+ bun.String.create(working_mem[0..first_match])
+ else if (args.path == .slice_with_underlying_string)
+ args.path.slice_with_underlying_string.underlying
+ else
+ bun.String.create(args.path.slice()),
+ };
+ },
+ }
}
pub fn mkdtemp(this: *NodeFS, args: Arguments.MkdirTemp, comptime _: Flavor) Maybe(Return.Mkdtemp) {
@@ -4136,75 +3913,54 @@ pub const NodeFS = struct {
return .{ .err = Syscall.Error{ .errno = @as(Syscall.Error.Int, @truncate(@intFromEnum(errno))), .syscall = .mkdtemp } };
}
pub fn open(this: *NodeFS, args: Arguments.Open, comptime flavor: Flavor) Maybe(Return.Open) {
- switch (comptime flavor) {
- // The sync version does no allocation except when returning the path
- .sync => {
- const path = args.path.sliceZ(&this.sync_error_buf);
- return switch (Syscall.open(path, @intFromEnum(args.flags), args.mode)) {
- .err => |err| .{
- .err = err.withPath(args.path.slice()),
- },
- .result => |fd| .{ .result = fd },
- };
+ _ = flavor;
+ const path = args.path.sliceZ(&this.sync_error_buf);
+ return switch (Syscall.open(path, @intFromEnum(args.flags), args.mode)) {
+ .err => |err| .{
+ .err = err.withPath(args.path.slice()),
},
- else => {},
- }
-
- return Maybe(Return.Open).todo;
+ .result => |fd| .{ .result = fd },
+ };
}
pub fn openDir(_: *NodeFS, _: Arguments.OpenDir, comptime _: Flavor) Maybe(Return.OpenDir) {
return Maybe(Return.OpenDir).todo;
}
fn _read(_: *NodeFS, args: Arguments.Read, comptime flavor: Flavor) Maybe(Return.Read) {
+ _ = flavor;
if (Environment.allow_assert) std.debug.assert(args.position == null);
+ var buf = args.buffer.slice();
+ buf = buf[@min(args.offset, buf.len)..];
+ buf = buf[0..@min(buf.len, args.length)];
- switch (comptime flavor) {
- // The sync version does no allocation except when returning the path
- .sync => {
- var buf = args.buffer.slice();
- buf = buf[@min(args.offset, buf.len)..];
- buf = buf[0..@min(buf.len, args.length)];
-
- return switch (Syscall.read(args.fd, buf)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{
- .result = .{
- .bytes_read = @as(u52, @truncate(amt)),
- },
- },
- };
+ return switch (Syscall.read(args.fd, buf)) {
+ .err => |err| .{
+ .err = err,
},
- else => {},
- }
-
- return Maybe(Return.Read).todo;
+ .result => |amt| .{
+ .result = .{
+ .bytes_read = @as(u52, @truncate(amt)),
+ },
+ },
+ };
}
fn _pread(_: *NodeFS, args: Arguments.Read, comptime flavor: Flavor) Maybe(Return.Read) {
- switch (comptime flavor) {
- .sync => {
- var buf = args.buffer.slice();
- buf = buf[@min(args.offset, buf.len)..];
- buf = buf[0..@min(buf.len, args.length)];
-
- return switch (Syscall.pread(args.fd, buf, args.position.?)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{
- .result = .{
- .bytes_read = @as(u52, @truncate(amt)),
- },
- },
- };
- },
- else => {},
- }
+ _ = flavor;
+ var buf = args.buffer.slice();
+ buf = buf[@min(args.offset, buf.len)..];
+ buf = buf[0..@min(buf.len, args.length)];
- return Maybe(Return.Read).todo;
+ return switch (Syscall.pread(args.fd, buf, args.position.?)) {
+ .err => |err| .{
+ .err = err,
+ },
+ .result => |amt| .{
+ .result = .{
+ .bytes_read = @as(u52, @truncate(amt)),
+ },
+ },
+ };
}
pub fn read(this: *NodeFS, args: Arguments.Read, comptime flavor: Flavor) Maybe(Return.Read) {
@@ -4236,127 +3992,91 @@ pub const NodeFS = struct {
return if (args.position != null) _pwrite(this, args, flavor) else _write(this, args, flavor);
}
fn _write(_: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) {
- switch (comptime flavor) {
- .sync => {
- var buf = args.buffer.slice();
- buf = buf[@min(args.offset, buf.len)..];
- buf = buf[0..@min(buf.len, args.length)];
-
- return switch (Syscall.write(args.fd, buf)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{
- .result = .{
- .bytes_written = @as(u52, @truncate(amt)),
- },
- },
- };
- },
- else => {},
- }
+ _ = flavor;
+
+ var buf = args.buffer.slice();
+ buf = buf[@min(args.offset, buf.len)..];
+ buf = buf[0..@min(buf.len, args.length)];
- return Maybe(Return.Write).todo;
+ return switch (Syscall.write(args.fd, buf)) {
+ .err => |err| .{
+ .err = err,
+ },
+ .result => |amt| .{
+ .result = .{
+ .bytes_written = @as(u52, @truncate(amt)),
+ },
+ },
+ };
}
fn _pwrite(_: *NodeFS, args: Arguments.Write, comptime flavor: Flavor) Maybe(Return.Write) {
+ _ = flavor;
const position = args.position.?;
- switch (comptime flavor) {
- .sync => {
- var buf = args.buffer.slice();
- buf = buf[@min(args.offset, buf.len)..];
- buf = buf[0..@min(args.length, buf.len)];
+ var buf = args.buffer.slice();
+ buf = buf[@min(args.offset, buf.len)..];
+ buf = buf[0..@min(args.length, buf.len)];
- return switch (Syscall.pwrite(args.fd, buf, position)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{ .result = .{
- .bytes_written = @as(u52, @truncate(amt)),
- } },
- };
+ return switch (Syscall.pwrite(args.fd, buf, position)) {
+ .err => |err| .{
+ .err = err,
},
- else => {},
- }
-
- return Maybe(Return.Write).todo;
+ .result => |amt| .{ .result = .{
+ .bytes_written = @as(u52, @truncate(amt)),
+ } },
+ };
}
fn _preadv(_: *NodeFS, args: Arguments.Readv, comptime flavor: Flavor) Maybe(Return.Readv) {
+ _ = flavor;
const position = args.position.?;
- switch (comptime flavor) {
- .sync => {
- return switch (Syscall.preadv(args.fd, args.buffers.buffers.items, position)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{ .result = .{
- .bytes_read = @as(u52, @truncate(amt)),
- } },
- };
+ return switch (Syscall.preadv(args.fd, args.buffers.buffers.items, position)) {
+ .err => |err| .{
+ .err = err,
},
- else => {},
- }
-
- return Maybe(Return.Write).todo;
+ .result => |amt| .{ .result = .{
+ .bytes_read = @as(u52, @truncate(amt)),
+ } },
+ };
}
fn _readv(_: *NodeFS, args: Arguments.Readv, comptime flavor: Flavor) Maybe(Return.Readv) {
- switch (comptime flavor) {
- .sync => {
- return switch (Syscall.readv(args.fd, args.buffers.buffers.items)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{ .result = .{
- .bytes_read = @as(u52, @truncate(amt)),
- } },
- };
+ _ = flavor;
+ return switch (Syscall.readv(args.fd, args.buffers.buffers.items)) {
+ .err => |err| .{
+ .err = err,
},
- else => {},
- }
-
- return Maybe(Return.Write).todo;
+ .result => |amt| .{ .result = .{
+ .bytes_read = @as(u52, @truncate(amt)),
+ } },
+ };
}
fn _pwritev(_: *NodeFS, args: Arguments.Writev, comptime flavor: Flavor) Maybe(Return.Write) {
+ _ = flavor;
const position = args.position.?;
-
- switch (comptime flavor) {
- .sync => {
- return switch (Syscall.pwritev(args.fd, args.buffers.buffers.items, position)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{ .result = .{
- .bytes_written = @as(u52, @truncate(amt)),
- } },
- };
+ return switch (Syscall.pwritev(args.fd, args.buffers.buffers.items, position)) {
+ .err => |err| .{
+ .err = err,
},
- else => {},
- }
-
- return Maybe(Return.Write).todo;
+ .result => |amt| .{ .result = .{
+ .bytes_written = @as(u52, @truncate(amt)),
+ } },
+ };
}
fn _writev(_: *NodeFS, args: Arguments.Writev, comptime flavor: Flavor) Maybe(Return.Write) {
- switch (comptime flavor) {
- .sync => {
- return switch (Syscall.writev(args.fd, args.buffers.buffers.items)) {
- .err => |err| .{
- .err = err,
- },
- .result => |amt| .{ .result = .{
- .bytes_written = @as(u52, @truncate(amt)),
- } },
- };
+ _ = flavor;
+ return switch (Syscall.writev(args.fd, args.buffers.buffers.items)) {
+ .err => |err| .{
+ .err = err,
},
- else => {},
- }
-
- return Maybe(Return.Write).todo;
+ .result => |amt| .{ .result = .{
+ .bytes_written = @as(u52, @truncate(amt)),
+ } },
+ };
}
pub fn readdir(this: *NodeFS, args: Arguments.Readdir, comptime flavor: Flavor) Maybe(Return.Readdir) {
@@ -4751,50 +4471,39 @@ pub const NodeFS = struct {
return Maybe(Return.WriteFile).success;
}
- pub fn writeFile(this: *NodeFS, args: Arguments.WriteFile, comptime flavor: Flavor) Maybe(Return.WriteFile) {
- switch (comptime flavor) {
- .sync => return writeFileWithPathBuffer(&this.sync_error_buf, args),
- else => {},
- }
-
- return Maybe(Return.WriteFile).todo;
+ pub fn writeFile(this: *NodeFS, args: Arguments.WriteFile, comptime _: Flavor) Maybe(Return.WriteFile) {
+ return writeFileWithPathBuffer(&this.sync_error_buf, args);
}
- pub fn readlink(this: *NodeFS, args: Arguments.Readlink, comptime flavor: Flavor) Maybe(Return.Readlink) {
+ pub fn readlink(this: *NodeFS, args: Arguments.Readlink, comptime _: Flavor) Maybe(Return.Readlink) {
var outbuf: [bun.MAX_PATH_BYTES]u8 = undefined;
var inbuf = &this.sync_error_buf;
- switch (comptime flavor) {
- .sync => {
- const path = args.path.sliceZ(inbuf);
- const len = switch (Syscall.readlink(path, &outbuf)) {
- .err => |err| return .{
- .err = err.withPath(args.path.slice()),
- },
- .result => |buf_| buf_,
- };
+ const path = args.path.sliceZ(inbuf);
- return .{
- .result = switch (args.encoding) {
- .buffer => .{
- .buffer = Buffer.fromString(outbuf[0..len], bun.default_allocator) catch unreachable,
- },
- else => if (args.path == .slice_with_underlying_string and
- strings.eqlLong(args.path.slice_with_underlying_string.slice(), outbuf[0..len], true))
- .{
- .BunString = args.path.slice_with_underlying_string.underlying.dupeRef(),
- }
- else
- .{
- .BunString = bun.String.create(outbuf[0..len]),
- },
- },
- };
+ const len = switch (Syscall.readlink(path, &outbuf)) {
+ .err => |err| return .{
+ .err = err.withPath(args.path.slice()),
},
- else => {},
- }
+ .result => |buf_| buf_,
+ };
- return Maybe(Return.Readlink).todo;
+ return .{
+ .result = switch (args.encoding) {
+ .buffer => .{
+ .buffer = Buffer.fromString(outbuf[0..len], bun.default_allocator) catch unreachable,
+ },
+ else => if (args.path == .slice_with_underlying_string and
+ strings.eqlLong(args.path.slice_with_underlying_string.slice(), outbuf[0..len], true))
+ .{
+ .BunString = args.path.slice_with_underlying_string.underlying.dupeRef(),
+ }
+ else
+ .{
+ .BunString = bun.String.create(outbuf[0..len]),
+ },
+ },
+ };
}
pub fn realpath(this: *NodeFS, args: Arguments.Realpath, comptime _: Flavor) Maybe(Return.Realpath) {
var outbuf: [bun.MAX_PATH_BYTES]u8 = undefined;
@@ -4857,325 +4566,309 @@ pub const NodeFS = struct {
// return error.NotImplementedYet;
// }
pub fn rename(this: *NodeFS, args: Arguments.Rename, comptime flavor: Flavor) Maybe(Return.Rename) {
+ _ = flavor;
var from_buf = &this.sync_error_buf;
var to_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
- switch (comptime flavor) {
- .sync => {
- var from = args.old_path.sliceZ(from_buf);
- var to = args.new_path.sliceZ(&to_buf);
- return Syscall.rename(from, to);
- },
- else => {},
- }
-
- return Maybe(Return.Rename).todo;
+ var from = args.old_path.sliceZ(from_buf);
+ var to = args.new_path.sliceZ(&to_buf);
+ return Syscall.rename(from, to);
}
pub fn rmdir(this: *NodeFS, args: Arguments.RmDir, comptime flavor: Flavor) Maybe(Return.Rmdir) {
- switch (comptime flavor) {
- .sync => {
- if (comptime Environment.isMac) {
- if (args.recursive) {
- var dest = args.path.sliceZ(&this.sync_error_buf);
-
- var flags: u32 = bun.C.darwin.RemoveFileFlags.cross_mount |
- bun.C.darwin.RemoveFileFlags.allow_long_paths |
- bun.C.darwin.RemoveFileFlags.recursive;
-
- while (true) {
- if (Maybe(Return.Rmdir).errnoSys(bun.C.darwin.removefileat(std.os.AT.FDCWD, dest, null, flags), .rmdir)) |errno| {
- switch (@as(os.E, @enumFromInt(errno.err.errno))) {
+ _ = flavor;
+
+ if (comptime Environment.isMac) {
+ if (args.recursive) {
+ var dest = args.path.sliceZ(&this.sync_error_buf);
+
+ var flags: u32 = bun.C.darwin.RemoveFileFlags.cross_mount |
+ bun.C.darwin.RemoveFileFlags.allow_long_paths |
+ bun.C.darwin.RemoveFileFlags.recursive;
+
+ while (true) {
+ if (Maybe(Return.Rmdir).errnoSys(bun.C.darwin.removefileat(std.os.AT.FDCWD, dest, null, flags), .rmdir)) |errno| {
+ switch (@as(os.E, @enumFromInt(errno.err.errno))) {
+ .AGAIN, .INTR => continue,
+ .NOENT => return Maybe(Return.Rmdir).success,
+ .MLINK => {
+ var copy: [bun.MAX_PATH_BYTES]u8 = undefined;
+ @memcpy(copy[0..dest.len], dest);
+ copy[dest.len] = 0;
+ var dest_copy = copy[0..dest.len :0];
+ switch (Syscall.unlink(dest_copy).getErrno()) {
.AGAIN, .INTR => continue,
- .NOENT => return Maybe(Return.Rmdir).success,
- .MLINK => {
- var copy: [bun.MAX_PATH_BYTES]u8 = undefined;
- @memcpy(copy[0..dest.len], dest);
- copy[dest.len] = 0;
- var dest_copy = copy[0..dest.len :0];
- switch (Syscall.unlink(dest_copy).getErrno()) {
- .AGAIN, .INTR => continue,
- .NOENT => return errno,
- .SUCCESS => continue,
- else => return errno,
- }
- },
- .SUCCESS => unreachable,
+ .NOENT => return errno,
+ .SUCCESS => continue,
else => return errno,
}
- }
-
- return Maybe(Return.Rmdir).success;
+ },
+ .SUCCESS => unreachable,
+ else => return errno,
}
}
- return Maybe(Return.Rmdir).errnoSysP(system.rmdir(args.path.sliceZ(&this.sync_error_buf)), .rmdir, args.path.slice()) orelse
- Maybe(Return.Rmdir).success;
- } else if (comptime Environment.isLinux) {
- if (args.recursive) {
- std.fs.cwd().deleteTree(args.path.slice()) catch |err| {
- const errno: std.os.E = switch (err) {
- error.InvalidHandle => .BADF,
- error.AccessDenied => .PERM,
- error.FileTooBig => .FBIG,
- error.SymLinkLoop => .LOOP,
- error.ProcessFdQuotaExceeded => .NFILE,
- error.NameTooLong => .NAMETOOLONG,
- error.SystemFdQuotaExceeded => .MFILE,
- error.SystemResources => .NOMEM,
- error.ReadOnlyFileSystem => .ROFS,
- error.FileSystem => .IO,
- error.FileBusy => .BUSY,
- error.DeviceBusy => .BUSY,
-
- // One of the path components was not a directory.
- // This error is unreachable if `sub_path` does not contain a path separator.
- error.NotDir => .NOTDIR,
- // On Windows, file paths must be valid Unicode.
- error.InvalidUtf8 => .INVAL,
-
- // On Windows, file paths cannot contain these characters:
- // '/', '*', '?', '"', '<', '>', '|'
- error.BadPathName => .INVAL,
-
- else => .FAULT,
- };
- return Maybe(Return.Rm){
- .err = bun.sys.Error.fromCode(errno, .rmdir),
- };
- };
+ return Maybe(Return.Rmdir).success;
+ }
+ }
- return Maybe(Return.Rmdir).success;
- }
+ return Maybe(Return.Rmdir).errnoSysP(system.rmdir(args.path.sliceZ(&this.sync_error_buf)), .rmdir, args.path.slice()) orelse
+ Maybe(Return.Rmdir).success;
+ } else if (comptime Environment.isLinux) {
+ if (args.recursive) {
+ std.fs.cwd().deleteTree(args.path.slice()) catch |err| {
+ const errno: std.os.E = switch (err) {
+ error.InvalidHandle => .BADF,
+ error.AccessDenied => .PERM,
+ error.FileTooBig => .FBIG,
+ error.SymLinkLoop => .LOOP,
+ error.ProcessFdQuotaExceeded => .NFILE,
+ error.NameTooLong => .NAMETOOLONG,
+ error.SystemFdQuotaExceeded => .MFILE,
+ error.SystemResources => .NOMEM,
+ error.ReadOnlyFileSystem => .ROFS,
+ error.FileSystem => .IO,
+ error.FileBusy => .BUSY,
+ error.DeviceBusy => .BUSY,
+
+ // One of the path components was not a directory.
+ // This error is unreachable if `sub_path` does not contain a path separator.
+ error.NotDir => .NOTDIR,
+ // On Windows, file paths must be valid Unicode.
+ error.InvalidUtf8 => .INVAL,
+
+ // On Windows, file paths cannot contain these characters:
+ // '/', '*', '?', '"', '<', '>', '|'
+ error.BadPathName => .INVAL,
+
+ else => .FAULT,
+ };
+ return Maybe(Return.Rm){
+ .err = bun.sys.Error.fromCode(errno, .rmdir),
+ };
+ };
- return Maybe(Return.Rmdir).errnoSysP(system.rmdir(args.path.sliceZ(&this.sync_error_buf)), .rmdir, args.path.slice()) orelse
- Maybe(Return.Rmdir).success;
- }
- },
- else => {},
- }
+ return Maybe(Return.Rmdir).success;
+ }
- return Maybe(Return.Rmdir).todo;
+ return Maybe(Return.Rmdir).errnoSysP(system.rmdir(args.path.sliceZ(&this.sync_error_buf)), .rmdir, args.path.slice()) orelse
+ Maybe(Return.Rmdir).success;
+ }
}
pub fn rm(this: *NodeFS, args: Arguments.RmDir, comptime flavor: Flavor) Maybe(Return.Rm) {
- switch (comptime flavor) {
- .sync => {
- if (comptime Environment.isMac) {
- var dest = args.path.sliceZ(&this.sync_error_buf);
-
- while (true) {
- var flags: u32 = 0;
- if (args.recursive) {
- flags |= bun.C.darwin.RemoveFileFlags.cross_mount;
- flags |= bun.C.darwin.RemoveFileFlags.allow_long_paths;
- flags |= bun.C.darwin.RemoveFileFlags.recursive;
- }
+ _ = flavor;
+
+ if (comptime Environment.isMac) {
+ var dest = args.path.sliceZ(&this.sync_error_buf);
- if (Maybe(Return.Rm).errnoSys(bun.C.darwin.removefileat(std.os.AT.FDCWD, dest, null, flags), .unlink)) |errno| {
- switch (@as(os.E, @enumFromInt(errno.err.errno))) {
+ while (true) {
+ var flags: u32 = 0;
+ if (args.recursive) {
+ flags |= bun.C.darwin.RemoveFileFlags.cross_mount;
+ flags |= bun.C.darwin.RemoveFileFlags.allow_long_paths;
+ flags |= bun.C.darwin.RemoveFileFlags.recursive;
+ }
+
+ if (Maybe(Return.Rm).errnoSys(bun.C.darwin.removefileat(std.os.AT.FDCWD, dest, null, flags), .unlink)) |errno| {
+ switch (@as(os.E, @enumFromInt(errno.err.errno))) {
+ .AGAIN, .INTR => continue,
+ .NOENT => {
+ if (args.force) {
+ return Maybe(Return.Rm).success;
+ }
+
+ return errno;
+ },
+
+ .MLINK => {
+ var copy: [bun.MAX_PATH_BYTES]u8 = undefined;
+ @memcpy(copy[0..dest.len], dest);
+ copy[dest.len] = 0;
+ var dest_copy = copy[0..dest.len :0];
+ switch (Syscall.unlink(dest_copy).getErrno()) {
.AGAIN, .INTR => continue,
.NOENT => {
if (args.force) {
- return Maybe(Return.Rm).success;
+ continue;
}
return errno;
},
-
- .MLINK => {
- var copy: [bun.MAX_PATH_BYTES]u8 = undefined;
- @memcpy(copy[0..dest.len], dest);
- copy[dest.len] = 0;
- var dest_copy = copy[0..dest.len :0];
- switch (Syscall.unlink(dest_copy).getErrno()) {
- .AGAIN, .INTR => continue,
- .NOENT => {
- if (args.force) {
- continue;
- }
-
- return errno;
- },
- .SUCCESS => continue,
- else => return errno,
- }
- },
- .SUCCESS => unreachable,
+ .SUCCESS => continue,
else => return errno,
}
- }
-
- return Maybe(Return.Rm).success;
- }
- } else if (comptime Environment.isLinux or Environment.isWindows) {
- if (args.recursive) {
- std.fs.cwd().deleteTree(args.path.slice()) catch |err| {
- const errno: E = switch (err) {
- error.InvalidHandle => .BADF,
- error.AccessDenied => .PERM,
- error.FileTooBig => .FBIG,
- error.SymLinkLoop => .LOOP,
- error.ProcessFdQuotaExceeded => .NFILE,
- error.NameTooLong => .NAMETOOLONG,
- error.SystemFdQuotaExceeded => .MFILE,
- error.SystemResources => .NOMEM,
- error.ReadOnlyFileSystem => .ROFS,
- error.FileSystem => .IO,
- error.FileBusy => .BUSY,
- error.DeviceBusy => .BUSY,
-
- // One of the path components was not a directory.
- // This error is unreachable if `sub_path` does not contain a path separator.
- error.NotDir => .NOTDIR,
- // On Windows, file paths must be valid Unicode.
- error.InvalidUtf8 => .INVAL,
-
- // On Windows, file paths cannot contain these characters:
- // '/', '*', '?', '"', '<', '>', '|'
- error.BadPathName => .INVAL,
-
- else => .FAULT,
- };
- if (args.force) {
- return Maybe(Return.Rm).success;
- }
- return Maybe(Return.Rm){
- .err = bun.sys.Error.fromCode(errno, .unlink),
- };
- };
- return Maybe(Return.Rm).success;
+ },
+ .SUCCESS => unreachable,
+ else => return errno,
}
}
- if (comptime Environment.isPosix) {
- var dest = args.path.osPath(&this.sync_error_buf);
- std.os.unlinkZ(dest) catch |er| {
- // empircally, it seems to return AccessDenied when the
- // file is actually a directory on macOS.
- if (args.recursive and
- (er == error.IsDir or er == error.NotDir or er == error.AccessDenied))
- {
- std.os.rmdirZ(dest) catch |err| {
- if (args.force) {
- return Maybe(Return.Rm).success;
- }
-
- const code: E = switch (err) {
- error.AccessDenied => .PERM,
- error.SymLinkLoop => .LOOP,
- error.NameTooLong => .NAMETOOLONG,
- error.SystemResources => .NOMEM,
- error.ReadOnlyFileSystem => .ROFS,
- error.FileBusy => .BUSY,
- error.FileNotFound => .NOENT,
- error.InvalidUtf8 => .INVAL,
- error.BadPathName => .INVAL,
- else => .FAULT,
- };
-
- return .{
- .err = bun.sys.Error.fromCode(
- code,
- .rmdir,
- ),
- };
- };
-
- return Maybe(Return.Rm).success;
- }
+ return Maybe(Return.Rm).success;
+ }
+ } else if (comptime Environment.isLinux or Environment.isWindows) {
+ if (args.recursive) {
+ std.fs.cwd().deleteTree(args.path.slice()) catch |err| {
+ const errno: E = switch (err) {
+ error.InvalidHandle => .BADF,
+ error.AccessDenied => .PERM,
+ error.FileTooBig => .FBIG,
+ error.SymLinkLoop => .LOOP,
+ error.ProcessFdQuotaExceeded => .NFILE,
+ error.NameTooLong => .NAMETOOLONG,
+ error.SystemFdQuotaExceeded => .MFILE,
+ error.SystemResources => .NOMEM,
+ error.ReadOnlyFileSystem => .ROFS,
+ error.FileSystem => .IO,
+ error.FileBusy => .BUSY,
+ error.DeviceBusy => .BUSY,
+
+ // One of the path components was not a directory.
+ // This error is unreachable if `sub_path` does not contain a path separator.
+ error.NotDir => .NOTDIR,
+ // On Windows, file paths must be valid Unicode.
+ error.InvalidUtf8 => .INVAL,
+
+ // On Windows, file paths cannot contain these characters:
+ // '/', '*', '?', '"', '<', '>', '|'
+ error.BadPathName => .INVAL,
+
+ else => .FAULT,
+ };
+ if (args.force) {
+ return Maybe(Return.Rm).success;
+ }
+ return Maybe(Return.Rm){
+ .err = bun.sys.Error.fromCode(errno, .unlink),
+ };
+ };
+ return Maybe(Return.Rm).success;
+ }
+ }
+ if (comptime Environment.isPosix) {
+ var dest = args.path.osPath(&this.sync_error_buf);
+ std.os.unlinkZ(dest) catch |er| {
+ // empircally, it seems to return AccessDenied when the
+ // file is actually a directory on macOS.
+ if (args.recursive and
+ (er == error.IsDir or er == error.NotDir or er == error.AccessDenied))
+ {
+ std.os.rmdirZ(dest) catch |err| {
if (args.force) {
return Maybe(Return.Rm).success;
}
- {
- const code: E = switch (er) {
- error.AccessDenied => .PERM,
- error.SymLinkLoop => .LOOP,
- error.NameTooLong => .NAMETOOLONG,
- error.SystemResources => .NOMEM,
- error.ReadOnlyFileSystem => .ROFS,
- error.FileBusy => .BUSY,
- error.InvalidUtf8 => .INVAL,
- error.BadPathName => .INVAL,
- error.FileNotFound => .NOENT,
- else => .FAULT,
- };
+ const code: E = switch (err) {
+ error.AccessDenied => .PERM,
+ error.SymLinkLoop => .LOOP,
+ error.NameTooLong => .NAMETOOLONG,
+ error.SystemResources => .NOMEM,
+ error.ReadOnlyFileSystem => .ROFS,
+ error.FileBusy => .BUSY,
+ error.FileNotFound => .NOENT,
+ error.InvalidUtf8 => .INVAL,
+ error.BadPathName => .INVAL,
+ else => .FAULT,
+ };
- return .{
- .err = bun.sys.Error.fromCode(
- code,
- .unlink,
- ),
- };
- }
+ return .{
+ .err = bun.sys.Error.fromCode(
+ code,
+ .rmdir,
+ ),
+ };
};
return Maybe(Return.Rm).success;
}
- if (comptime Environment.isWindows) {
- var dest = args.path.osPath(&this.sync_error_buf);
- std.os.windows.DeleteFile(dest, .{
- .dir = null,
- .remove_dir = brk: {
- const file_attrs = std.os.windows.GetFileAttributesW(dest.ptr) catch |err| {
- if (args.force) {
- return Maybe(Return.Rm).success;
- }
+ if (args.force) {
+ return Maybe(Return.Rm).success;
+ }
- const code: E = switch (err) {
- error.FileNotFound => .NOENT,
- error.PermissionDenied => .PERM,
- else => .INVAL,
- };
+ {
+ const code: E = switch (er) {
+ error.AccessDenied => .PERM,
+ error.SymLinkLoop => .LOOP,
+ error.NameTooLong => .NAMETOOLONG,
+ error.SystemResources => .NOMEM,
+ error.ReadOnlyFileSystem => .ROFS,
+ error.FileBusy => .BUSY,
+ error.InvalidUtf8 => .INVAL,
+ error.BadPathName => .INVAL,
+ error.FileNotFound => .NOENT,
+ else => .FAULT,
+ };
- return .{
- .err = bun.sys.Error.fromCode(
- code,
- .unlink,
- ),
- };
- };
- // TODO: check FILE_ATTRIBUTE_INVALID
- break :brk (file_attrs & std.os.windows.FILE_ATTRIBUTE_DIRECTORY) != 0;
- },
- }) catch |er| {
- // empircally, it seems to return AccessDenied when the
- // file is actually a directory on macOS.
+ return .{
+ .err = bun.sys.Error.fromCode(
+ code,
+ .unlink,
+ ),
+ };
+ }
+ };
+
+ return Maybe(Return.Rm).success;
+ }
+ if (comptime Environment.isWindows) {
+ var dest = args.path.osPath(&this.sync_error_buf);
+ std.os.windows.DeleteFile(dest, .{
+ .dir = null,
+ .remove_dir = brk: {
+ const file_attrs = std.os.windows.GetFileAttributesW(dest.ptr) catch |err| {
if (args.force) {
return Maybe(Return.Rm).success;
}
- {
- const code: E = switch (er) {
- error.FileNotFound => .NOENT,
- error.AccessDenied => .PERM,
- error.NameTooLong => .INVAL,
- error.FileBusy => .BUSY,
- error.NotDir => .NOTDIR,
- error.IsDir => .ISDIR,
- error.DirNotEmpty => .INVAL,
- error.NetworkNotFound => .NOENT,
- else => .UNKNOWN,
- };
+ const code: E = switch (err) {
+ error.FileNotFound => .NOENT,
+ error.PermissionDenied => .PERM,
+ else => .INVAL,
+ };
- return .{
- .err = bun.sys.Error.fromCode(
- code,
- .unlink,
- ),
- };
- }
+ return .{
+ .err = bun.sys.Error.fromCode(
+ code,
+ .unlink,
+ ),
+ };
};
+ // TODO: check FILE_ATTRIBUTE_INVALID
+ break :brk (file_attrs & std.os.windows.FILE_ATTRIBUTE_DIRECTORY) != 0;
+ },
+ }) catch |er| {
+ // empircally, it seems to return AccessDenied when the
+ // file is actually a directory on macOS.
+ if (args.force) {
return Maybe(Return.Rm).success;
}
- },
- else => {},
- }
- return Maybe(Return.Rm).todo;
+ {
+ const code: E = switch (er) {
+ error.FileNotFound => .NOENT,
+ error.AccessDenied => .PERM,
+ error.NameTooLong => .INVAL,
+ error.FileBusy => .BUSY,
+ error.NotDir => .NOTDIR,
+ error.IsDir => .ISDIR,
+ error.DirNotEmpty => .INVAL,
+ error.NetworkNotFound => .NOENT,
+ else => .UNKNOWN,
+ };
+
+ return .{
+ .err = bun.sys.Error.fromCode(
+ code,
+ .unlink,
+ ),
+ };
+ }
+ };
+
+ return Maybe(Return.Rm).success;
+ }
}
pub fn stat(this: *NodeFS, args: Arguments.Stat, comptime flavor: Flavor) Maybe(Return.Stat) {
if (comptime Environment.isWindows) {
@@ -5199,38 +4892,26 @@ pub const NodeFS = struct {
}
pub fn symlink(this: *NodeFS, args: Arguments.Symlink, comptime flavor: Flavor) Maybe(Return.Symlink) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Symlink).todo;
}
var to_buf: [bun.MAX_PATH_BYTES]u8 = undefined;
- switch (comptime flavor) {
- .sync => {
- return Syscall.symlink(
- args.old_path.sliceZ(&this.sync_error_buf),
- args.new_path.sliceZ(&to_buf),
- );
- },
- else => {},
- }
-
- return Maybe(Return.Symlink).todo;
+ return Syscall.symlink(
+ args.old_path.sliceZ(&this.sync_error_buf),
+ args.new_path.sliceZ(&to_buf),
+ );
}
fn _truncate(this: *NodeFS, path: PathLike, len: JSC.WebCore.Blob.SizeType, comptime flavor: Flavor) Maybe(Return.Truncate) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Truncate).todo;
}
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Truncate).errno(C.truncate(path.sliceZ(&this.sync_error_buf), len)) orelse
- Maybe(Return.Truncate).success;
- },
- else => {},
- }
-
- return Maybe(Return.Truncate).todo;
+ return Maybe(Return.Truncate).errno(C.truncate(path.sliceZ(&this.sync_error_buf), len)) orelse
+ Maybe(Return.Truncate).success;
}
pub fn truncate(this: *NodeFS, args: Arguments.Truncate, comptime flavor: Flavor) Maybe(Return.Truncate) {
return switch (args.path) {
@@ -5246,19 +4927,13 @@ pub const NodeFS = struct {
};
}
pub fn unlink(this: *NodeFS, args: Arguments.Unlink, comptime flavor: Flavor) Maybe(Return.Unlink) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Unlink).todo;
}
- switch (comptime flavor) {
- .sync => {
- return Maybe(Return.Unlink).errnoSysP(system.unlink(args.path.sliceZ(&this.sync_error_buf)), .unlink, args.path.slice()) orelse
- Maybe(Return.Unlink).success;
- },
- else => {},
- }
-
- return Maybe(Return.Unlink).todo;
+ return Maybe(Return.Unlink).errnoSysP(system.unlink(args.path.sliceZ(&this.sync_error_buf)), .unlink, args.path.slice()) orelse
+ Maybe(Return.Unlink).success;
}
pub fn watchFile(_: *NodeFS, args: Arguments.WatchFile, comptime flavor: Flavor) Maybe(Return.WatchFile) {
std.debug.assert(flavor == .sync);
@@ -5283,6 +4958,7 @@ pub const NodeFS = struct {
return Maybe(Return.UnwatchFile).todo;
}
pub fn utimes(this: *NodeFS, args: Arguments.Utimes, comptime flavor: Flavor) Maybe(Return.Utimes) {
+ _ = flavor;
if (comptime Environment.isWindows) {
return Maybe(Return.Utimes).todo;
}
@@ -5300,21 +4976,13 @@ pub const NodeFS = struct {
},
};
- switch (comptime flavor) {
- // futimes uses the syscall version
- // we use libc because here, not for a good reason
- // just missing from the linux syscall interface in zig and I don't want to modify that right now
- .sync => return if (Maybe(Return.Utimes).errnoSysP(std.c.utimes(args.path.sliceZ(&this.sync_error_buf), &times), .utimes, args.path.slice())) |err|
- err
- else
- Maybe(Return.Utimes).success,
- else => {},
- }
-
- return Maybe(Return.Utimes).todo;
+ return if (Maybe(Return.Utimes).errnoSysP(std.c.utimes(args.path.sliceZ(&this.sync_error_buf), &times), .utimes, args.path.slice())) |err|
+ err
+ else
+ Maybe(Return.Utimes).success;
}
- pub fn lutimes(this: *NodeFS, args: Arguments.Lutimes, comptime flavor: Flavor) Maybe(Return.Lutimes) {
+ pub fn lutimes(this: *NodeFS, args: Arguments.Lutimes, comptime _: Flavor) Maybe(Return.Lutimes) {
if (comptime Environment.isWindows) {
return Maybe(Return.Lutimes).todo;
}
@@ -5332,18 +5000,10 @@ pub const NodeFS = struct {
},
};
- switch (comptime flavor) {
- // futimes uses the syscall version
- // we use libc because here, not for a good reason
- // just missing from the linux syscall interface in zig and I don't want to modify that right now
- .sync => return if (Maybe(Return.Lutimes).errnoSysP(C.lutimes(args.path.sliceZ(&this.sync_error_buf), &times), .lutimes, args.path.slice())) |err|
- err
- else
- Maybe(Return.Lutimes).success,
- else => {},
- }
-
- return Maybe(Return.Lutimes).todo;
+ return if (Maybe(Return.Lutimes).errnoSysP(C.lutimes(args.path.sliceZ(&this.sync_error_buf), &times), .lutimes, args.path.slice())) |err|
+ err
+ else
+ Maybe(Return.Lutimes).success;
}
pub fn watch(_: *NodeFS, args: Arguments.Watch, comptime _: Flavor) Maybe(Return.Watch) {
if (comptime Environment.isWindows) {
diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig
index b3866b557..db4d79fc9 100644
--- a/src/bun.js/node/node_fs_binding.zig
+++ b/src/bun.js/node/node_fs_binding.zig
@@ -67,6 +67,7 @@ fn callSync(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {
args,
comptime Flavor.sync,
);
+
switch (result) {
.err => |err| {
globalObject.throwValue(JSC.JSValue.c(err.toJS(globalObject)));
@@ -108,14 +109,6 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {
globalObject: *JSC.JSGlobalObject,
callframe: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
- switch (comptime FunctionEnum) {
- .readdir, .lstat, .stat, .readFile, .realpath, .copyFile, .cp => {},
- else => {
- globalObject.throw("Not implemented yet", .{});
- return .zero;
- },
- }
-
var arguments = callframe.arguments(8);
var slice = ArgumentsSlice.init(globalObject.bunVM(), arguments.ptr[0..arguments.len]);
@@ -142,57 +135,12 @@ fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {
// TODO: handle globalObject.throwValue
- if (comptime FunctionEnum == .readdir) {
- return JSC.Node.AsyncReaddirTask.create(globalObject, args, slice.vm, slice.arena);
- }
-
- if (comptime FunctionEnum == .readFile) {
- return JSC.Node.AsyncReadFileTask.create(globalObject, args, slice.vm, slice.arena);
- }
-
- if (comptime FunctionEnum == .realpath) {
- return JSC.Node.AsyncRealpathTask.create(globalObject, args, slice.vm, slice.arena);
- }
-
- if (comptime FunctionEnum == .stat or FunctionEnum == .lstat) {
- return JSC.Node.AsyncStatTask.create(globalObject, args, slice.vm, FunctionEnum == .lstat, slice.arena);
- }
-
- if (comptime FunctionEnum == .copyFile) {
- return JSC.Node.AsyncCopyFileTask.create(globalObject, args, slice.vm, slice.arena);
- }
-
+ const Task = @field(JSC.Node.Async, @tagName(FunctionEnum));
if (comptime FunctionEnum == .cp) {
- return JSC.Node.AsyncCpTask.create(globalObject, args, slice.vm, slice.arena);
+ return Task.create(globalObject, args, globalObject.bunVM(), slice.arena);
+ } else {
+ return Task.create(globalObject, args, globalObject.bunVM());
}
-
- // defer {
- // for (arguments.len) |arg| {
- // JSC.C.JSValueUnprotect(ctx, arg);
- // }
- // slice.arena.deinit();
- // }
-
- // const args = if (comptime Arguments != void)
- // Arguments.fromJS(ctx, &slice, exception)
- // else
- // Arguments{};
- // if (exception.* != null) return null;
-
- // const result: Maybe(Result) = Function(this, comptime Flavor.sync, args);
- // switch (result) {
- // .err => |err| {
- // exception.* = err.toJS(ctx);
- // return null;
- // },
- // .result => |res| {
- // return switch (comptime Result) {
- // void => JSC.JSValue.jsUndefined().asRef(),
- // else => res.toJS(ctx),
- // };
- // },
- // }
- // unreachable;
}
};
return NodeBindingClosure.bind;
diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig
index 35daea52c..84dc394d0 100644
--- a/src/bun.js/node/types.zig
+++ b/src/bun.js/node/types.zig
@@ -315,6 +315,68 @@ pub const StringOrBunStringOrBuffer = union(enum) {
}
};
+pub const SliceWithUnderlyingStringOrBuffer = union(enum) {
+ SliceWithUnderlyingString: bun.SliceWithUnderlyingString,
+ buffer: Buffer,
+
+ pub fn toThreadSafe(this: *@This()) void {
+ switch (this.*) {
+ .SliceWithUnderlyingString => this.SliceWithUnderlyingString.toThreadSafe(),
+ else => {},
+ }
+ }
+
+ pub fn toJS(this: *SliceWithUnderlyingStringOrBuffer, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef {
+ return switch (this) {
+ .SliceWithUnderlyingStringOrBuffer => {
+ defer {
+ this.SliceWithUnderlyingString.deinit();
+ this.SliceWithUnderlyingString.underlying = bun.String.empty;
+ this.SliceWithUnderlyingString.utf8 = .{};
+ }
+
+ return this.SliceWithUnderlyingString.underlying.toJS(ctx);
+ },
+ .buffer => this.buffer.toJSObjectRef(ctx, exception),
+ };
+ }
+
+ pub fn slice(this: *const SliceWithUnderlyingStringOrBuffer) []const u8 {
+ return switch (this.*) {
+ .SliceWithUnderlyingString => this.SliceWithUnderlyingString.slice(),
+ .buffer => this.buffer.slice(),
+ };
+ }
+
+ pub fn fromJS(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, exception: JSC.C.ExceptionRef) ?SliceWithUnderlyingStringOrBuffer {
+ _ = exception;
+ return switch (value.jsType()) {
+ JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject, JSC.JSValue.JSType.Object => {
+ var str = bun.String.tryFromJS(value, global) orelse return null;
+ return SliceWithUnderlyingStringOrBuffer{ .SliceWithUnderlyingString = str.toSlice(allocator) };
+ },
+
+ .ArrayBuffer,
+ .Int8Array,
+ .Uint8Array,
+ .Uint8ClampedArray,
+ .Int16Array,
+ .Uint16Array,
+ .Int32Array,
+ .Uint32Array,
+ .Float32Array,
+ .Float64Array,
+ .BigInt64Array,
+ .BigUint64Array,
+ .DataView,
+ => SliceWithUnderlyingStringOrBuffer{
+ .buffer = Buffer.fromArrayBuffer(global, value),
+ },
+ else => null,
+ };
+ }
+};
+
/// Like StringOrBuffer but actually returns a Node.js Buffer
pub const StringOrNodeBuffer = union(Tag) {
string: string,
@@ -401,6 +463,20 @@ pub const SliceOrBuffer = union(Tag) {
}
}
+ pub fn toThreadSafe(this: *SliceOrBuffer) void {
+ var buffer: ?Buffer = null;
+ if (this.* == .buffer) {
+ buffer = this.buffer;
+ this.buffer.buffer.value.ensureStillAlive();
+ }
+ defer {
+ if (buffer) |buf| {
+ buf.buffer.value.unprotect();
+ }
+ }
+ this.ensureCloned(bun.default_allocator) catch unreachable;
+ }
+
pub const Tag = enum { string, buffer };
pub fn slice(this: SliceOrBuffer) []const u8 {
@@ -650,6 +726,10 @@ pub const PathLike = union(Tag) {
if (this.* == .slice_with_underlying_string) {
this.slice_with_underlying_string.toThreadSafe();
}
+
+ if (this.* == .buffer) {
+ this.buffer.buffer.value.protect();
+ }
}
pub fn deinitAndUnprotect(this: *const PathLike) void {