diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/bun.js/base.zig | 55 | ||||
| -rw-r--r-- | src/bun.js/bindings/BunString.cpp | 50 | ||||
| -rw-r--r-- | src/bun.js/node/node_fs.zig | 344 | ||||
| -rw-r--r-- | src/bun.js/node/node_fs_binding.zig | 57 | ||||
| -rw-r--r-- | src/bun.js/node/types.zig | 28 | ||||
| -rw-r--r-- | src/js/node/fs.js | 28 | ||||
| -rw-r--r-- | src/js/node/fs.promises.ts | 31 | ||||
| -rw-r--r-- | src/js/out/modules/node/fs.js | 17 | ||||
| -rw-r--r-- | src/js/out/modules/node/fs.promises.js | 2 | 
9 files changed, 380 insertions, 232 deletions
| diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index f7b2eb343..579a0975a 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -266,58 +266,21 @@ pub const To = struct {                      // Recursion can stack overflow here                      if (comptime std.meta.trait.isSlice(Type)) { -                        const Child = std.meta.Child(Type); - -                        const prefill = 32; -                        if (value.len <= prefill) { -                            var array: [prefill]JSC.C.JSValueRef = undefined; -                            var i: u8 = 0; -                            const len = @min(@as(u8, @intCast(value.len)), prefill); -                            while (i < len and exception.* == null) : (i += 1) { -                                array[i] = if (comptime Child == JSC.C.JSValueRef) -                                    value[i] -                                else -                                    To.JS.withType(Child, value[i], context, exception); -                            } - -                            if (exception.* != null) { -                                return null; -                            } - -                            // TODO: this function copies to a MarkedArgumentsBuffer -                            // That copy is unnecessary. -                            const obj = JSC.C.JSObjectMakeArray(context, len, &array, exception); - -                            if (exception.* != null) { -                                return null; -                            } -                            return obj; -                        } - -                        { -                            var array = bun.default_allocator.alloc(JSC.C.JSValueRef, value.len) catch unreachable; -                            defer bun.default_allocator.free(array); -                            var i: usize = 0; -                            while (i < value.len and exception.* == null) : (i += 1) { -                                array[i] = if (comptime Child == JSC.C.JSValueRef) -                                    value[i] -                                else -                                    To.JS.withType(Child, value[i], context, exception); -                            } +                        const Child = comptime std.meta.Child(Type); -                            if (exception.* != null) { -                                return null; -                            } +                        var array = JSC.JSValue.createEmptyArray(context, value.len); +                        for (value, 0..) |item, i| { +                            array.putIndex( +                                context, +                                @truncate(i), +                                JSC.JSValue.c(To.JS.withType(Child, item, context, exception)), +                            ); -                            // TODO: this function copies to a MarkedArgumentsBuffer -                            // That copy is unnecessary. -                            const obj = JSC.C.JSObjectMakeArray(context, value.len, array.ptr, exception);                              if (exception.* != null) {                                  return null;                              } - -                            return obj;                          } +                        return array.asObjectRef();                      }                      if (comptime std.meta.trait.isZigString(Type)) { diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 714f10080..e044730c4 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -256,31 +256,47 @@ extern "C" EncodedJSValue BunString__createArray(      auto& vm = globalObject->vm();      auto throwScope = DECLARE_THROW_SCOPE(vm); -    // We must do this or Bun.gc(true) in a loop creating large arrays of strings will crash due to GC'ing. -    MarkedArgumentBuffer arguments; -    JSC::ObjectInitializationScope scope(vm); -    GCDeferralContext context(vm); - -    arguments.fill(length, [&](JSC::JSValue* value) { -        const BunString* end = ptr + length; -        while (ptr != end) { -            *value++ = Bun::toJS(globalObject, *ptr++); -        } -    }); +    if (length < 64) { +        // We must do this or Bun.gc(true) in a loop creating large arrays of strings will crash due to GC'ing. +        MarkedArgumentBuffer arguments; + +        arguments.fill(length, [&](JSC::JSValue* value) { +            const BunString* end = ptr + length; +            while (ptr != end) { +                *value++ = Bun::toJS(globalObject, *ptr++); +            } +        }); + +        JSC::ObjectInitializationScope scope(vm); +        GCDeferralContext context(vm); -    if (JSC::JSArray* array = JSC::JSArray::tryCreateUninitializedRestricted( +        JSC::JSArray* array = JSC::JSArray::tryCreateUninitializedRestricted(              scope,              globalObject->arrayStructureForIndexingTypeDuringAllocation(JSC::ArrayWithContiguous), -            length)) { +            length); + +        if (array) { +            for (size_t i = 0; i < length; ++i) { +                array->initializeIndex(scope, i, arguments.at(i)); +            } +            return JSValue::encode(array); +        } + +        JSC::throwOutOfMemoryError(globalObject, throwScope); +        RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSValue())); +    } else { +        JSC::JSArray* array = constructEmptyArray(globalObject, nullptr, length); +        if (!array) { +            JSC::throwOutOfMemoryError(globalObject, throwScope); +            RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSValue())); +        }          for (size_t i = 0; i < length; ++i) { -            array->initializeIndex(scope, i, arguments.at(i)); +            array->putDirectIndex(globalObject, i, Bun::toJS(globalObject, *ptr++));          } +          return JSValue::encode(array);      } - -    JSC::throwOutOfMemoryError(globalObject, throwScope); -    RELEASE_AND_RETURN(throwScope, JSValue::encode(JSC::JSValue()));  }  extern "C" void BunString__toWTFString(BunString* bunString) diff --git a/src/bun.js/node/node_fs.zig b/src/bun.js/node/node_fs.zig index 314cd44bd..6d2ec4120 100644 --- a/src/bun.js/node/node_fs.zig +++ b/src/bun.js/node/node_fs.zig @@ -55,6 +55,160 @@ const ArrayBuffer = JSC.MarkedArrayBuffer;  const Buffer = JSC.Buffer;  const FileSystemFlags = JSC.Node.FileSystemFlags; +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 = .{}, + +    pub fn create(globalObject: *JSC.JSGlobalObject, readdir_args: Arguments.Readdir, vm: *JSC.VirtualMachine) 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, +        }; +        task.ref.ref(vm); + +        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); + +        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(); + +        this.deinit(); +        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.deinit(); +        this.promise.strong.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, + +    pub fn create( +        globalObject: *JSC.JSGlobalObject, +        readdir_args: Arguments.Stat, +        vm: *JSC.VirtualMachine, +        is_lstat: bool, +    ) 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, +        }; +        task.ref.ref(vm); + +        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(); + +        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.deinit(); +        this.promise.strong.deinit(); +        bun.default_allocator.destroy(this); +    } +}; +  // TODO: to improve performance for all of these  // The tagged unions for each type should become regular unions  // and the tags should be passed in as comptime arguments to the functions performing the syscalls @@ -2624,11 +2778,20 @@ const Return = struct {          };          pub fn toJS(this: Readdir, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef { -            return switch (this) { -                .with_file_types => JSC.To.JS.withType([]const Dirent, this.with_file_types, ctx, exception), -                .buffers => JSC.To.JS.withType([]const Buffer, this.buffers, ctx, exception), -                .files => JSC.To.JS.withType([]const bun.String, this.files, ctx, exception), -            }; +            switch (this) { +                .with_file_types => { +                    defer bun.default_allocator.free(this.with_file_types); +                    return JSC.To.JS.withType([]const Dirent, this.with_file_types, ctx, exception); +                }, +                .buffers => { +                    defer bun.default_allocator.free(this.buffers); +                    return JSC.To.JS.withType([]const Buffer, this.buffers, ctx, exception); +                }, +                .files => { +                    // automatically freed +                    return JSC.To.JS.withType([]const bun.String, this.files, ctx, exception); +                }, +            }          }      };      pub const ReadFile = JSC.Node.StringOrNodeBuffer; @@ -3145,28 +3308,22 @@ pub const NodeFS = struct {          return Maybe(Return.Link).todo;      }      pub fn lstat(this: *NodeFS, args: Arguments.Lstat, comptime flavor: Flavor) Maybe(Return.Lstat) { +        _ = flavor;          if (args.big_int) return Maybe(Return.Lstat).todo; -        switch (comptime flavor) { -            .sync => { -                return switch (Syscall.lstat( -                    args.path.sliceZ( -                        &this.sync_error_buf, -                    ), -                )) { -                    .result => |result| Maybe(Return.Lstat){ .result = .{ .stats = Stats.init(result, args.big_int) } }, -                    .err => |err| brk: { -                        if (!args.throw_if_no_entry and err.getErrno() == .NOENT) { -                            return Maybe(Return.Lstat){ .result = .{ .not_found = {} } }; -                        } -                        break :brk Maybe(Return.Lstat){ .err = err }; -                    }, -                }; +        return switch (Syscall.lstat( +            args.path.sliceZ( +                &this.sync_error_buf, +            ), +        )) { +            .result => |result| Maybe(Return.Lstat){ .result = .{ .stats = Stats.init(result, args.big_int) } }, +            .err => |err| brk: { +                if (!args.throw_if_no_entry and err.getErrno() == .NOENT) { +                    return Maybe(Return.Lstat){ .result = .{ .not_found = {} } }; +                } +                break :brk Maybe(Return.Lstat){ .err = err };              }, -            else => {}, -        } - -        return Maybe(Return.Lstat).todo; +        };      }      pub fn mkdir(this: *NodeFS, args: Arguments.Mkdir, comptime flavor: Flavor) Maybe(Return.Mkdir) { @@ -3575,7 +3732,7 @@ pub const NodeFS = struct {      pub fn readdir(this: *NodeFS, args: Arguments.Readdir, comptime flavor: Flavor) Maybe(Return.Readdir) {          return switch (args.encoding) {              .buffer => _readdir( -                this, +                &this.sync_error_buf,                  args,                  Buffer,                  flavor, @@ -3583,7 +3740,7 @@ pub const NodeFS = struct {              else => {                  if (!args.with_file_types) {                      return _readdir( -                        this, +                        &this.sync_error_buf,                          args,                          bun.String,                          flavor, @@ -3591,7 +3748,7 @@ pub const NodeFS = struct {                  }                  return _readdir( -                    this, +                    &this.sync_error_buf,                      args,                      Dirent,                      flavor, @@ -3601,10 +3758,10 @@ pub const NodeFS = struct {      }      pub fn _readdir( -        this: *NodeFS, +        buf: *[bun.MAX_PATH_BYTES]u8,          args: Arguments.Readdir,          comptime ExpectedType: type, -        comptime flavor: Flavor, +        comptime _: Flavor,      ) Maybe(Return.Readdir) {          const file_type = comptime switch (ExpectedType) {              Dirent => "with_file_types", @@ -3613,73 +3770,66 @@ pub const NodeFS = struct {              else => unreachable,          }; -        switch (comptime flavor) { -            .sync => { -                var path = args.path.sliceZ(&this.sync_error_buf); -                const flags = os.O.DIRECTORY | os.O.RDONLY; -                const fd = switch (Syscall.open(path, flags, 0)) { -                    .err => |err| return .{ -                        .err = err.withPath(args.path.slice()), -                    }, -                    .result => |fd_| fd_, -                }; -                defer { -                    _ = Syscall.close(fd); -                } - -                var entries = std.ArrayList(ExpectedType).init(bun.default_allocator); -                var dir = std.fs.Dir{ .fd = fd }; -                var iterator = DirIterator.iterate(dir); -                var entry = iterator.next(); -                while (switch (entry) { -                    .err => |err| { -                        for (entries.items) |*item| { -                            switch (comptime ExpectedType) { -                                Dirent => { -                                    item.name.deref(); -                                }, -                                Buffer => { -                                    item.destroy(); -                                }, -                                bun.String => { -                                    item.deref(); -                                }, -                                else => unreachable, -                            } -                        } - -                        entries.deinit(); +        var path = args.path.sliceZ(buf); +        const flags = os.O.DIRECTORY | os.O.RDONLY; +        const fd = switch (Syscall.open(path, flags, 0)) { +            .err => |err| return .{ +                .err = err.withPath(args.path.slice()), +            }, +            .result => |fd_| fd_, +        }; +        defer { +            _ = Syscall.close(fd); +        } -                        return .{ -                            .err = err.withPath(args.path.slice()), -                        }; -                    }, -                    .result => |ent| ent, -                }) |current| : (entry = iterator.next()) { -                    const utf8_name = current.name.slice(); +        var entries = std.ArrayList(ExpectedType).init(bun.default_allocator); +        var dir = std.fs.Dir{ .fd = fd }; +        var iterator = DirIterator.iterate(dir); +        var entry = iterator.next(); +        while (switch (entry) { +            .err => |err| { +                for (entries.items) |*item| {                      switch (comptime ExpectedType) {                          Dirent => { -                            entries.append(.{ -                                .name = bun.String.create(utf8_name), -                                .kind = current.kind, -                            }) catch unreachable; +                            item.name.deref();                          },                          Buffer => { -                            entries.append(Buffer.fromString(utf8_name, bun.default_allocator) catch unreachable) catch unreachable; +                            item.destroy();                          },                          bun.String => { -                            entries.append(bun.String.create(utf8_name)) catch unreachable; +                            item.deref();                          },                          else => unreachable,                      }                  } -                return .{ .result = @unionInit(Return.Readdir, file_type, entries.items) }; +                entries.deinit(); + +                return .{ +                    .err = err.withPath(args.path.slice()), +                };              }, -            else => {}, +            .result => |ent| ent, +        }) |current| : (entry = iterator.next()) { +            const utf8_name = current.name.slice(); +            switch (comptime ExpectedType) { +                Dirent => { +                    entries.append(.{ +                        .name = bun.String.create(utf8_name), +                        .kind = current.kind, +                    }) catch unreachable; +                }, +                Buffer => { +                    entries.append(Buffer.fromString(utf8_name, bun.default_allocator) catch unreachable) catch unreachable; +                }, +                bun.String => { +                    entries.append(bun.String.create(utf8_name)) catch unreachable; +                }, +                else => unreachable, +            }          } -        return Maybe(Return.Readdir).todo; +        return .{ .result = @unionInit(Return.Readdir, file_type, entries.items) };      }      pub const StringType = enum { @@ -4354,28 +4504,22 @@ pub const NodeFS = struct {          return Maybe(Return.Rm).todo;      }      pub fn stat(this: *NodeFS, args: Arguments.Stat, comptime flavor: Flavor) Maybe(Return.Stat) { +        _ = flavor;          if (args.big_int) return Maybe(Return.Stat).todo; -        switch (comptime flavor) { -            .sync => { -                return @as(Maybe(Return.Stat), switch (Syscall.stat( -                    args.path.sliceZ( -                        &this.sync_error_buf, -                    ), -                )) { -                    .result => |result| Maybe(Return.Stat){ .result = .{ .stats = Stats.init(result, args.big_int) } }, -                    .err => |err| brk: { -                        if (!args.throw_if_no_entry and err.getErrno() == .NOENT) { -                            return Maybe(Return.Stat){ .result = .{ .not_found = {} } }; -                        } -                        break :brk Maybe(Return.Stat){ .err = err }; -                    }, -                }); +        return @as(Maybe(Return.Stat), switch (Syscall.stat( +            args.path.sliceZ( +                &this.sync_error_buf, +            ), +        )) { +            .result => |result| Maybe(Return.Stat){ .result = .{ .stats = Stats.init(result, args.big_int) } }, +            .err => |err| brk: { +                if (!args.throw_if_no_entry and err.getErrno() == .NOENT) { +                    return Maybe(Return.Stat){ .result = .{ .not_found = {} } }; +                } +                break :brk Maybe(Return.Stat){ .err = err };              }, -            else => {}, -        } - -        return Maybe(Return.Stat).todo; +        });      }      pub fn symlink(this: *NodeFS, args: Arguments.Symlink, comptime flavor: Flavor) Maybe(Return.Symlink) { diff --git a/src/bun.js/node/node_fs_binding.zig b/src/bun.js/node/node_fs_binding.zig index a4cc62cd3..88e0b30e2 100644 --- a/src/bun.js/node/node_fs_binding.zig +++ b/src/bun.js/node/node_fs_binding.zig @@ -95,25 +95,54 @@ fn callSync(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction {      return NodeBindingClosure.bind;  } -fn call(comptime Function: NodeFSFunctionEnum) NodeFSFunction { -    // const FunctionType = @TypeOf(Function); -    _ = Function; - -    // const function: std.builtin.Type.Fn = comptime @typeInfo(FunctionType).Fn; -    // comptime if (function.args.len != 3) @compileError("Expected 3 arguments"); -    // const Arguments = comptime function.args[2].type orelse @compileError(std.fmt.comptimePrint("Function {s} expected to have an arg type at [2]", .{@typeName(FunctionType)})); -    // const Result = comptime function.return_type.?; -    // comptime if (Arguments != void and !fromJSTrait(Arguments)) @compileError(std.fmt.comptimePrint("{s} is missing fromJS()", .{@typeName(Arguments)})); -    // comptime if (Result != void and !toJSTrait(Result)) @compileError(std.fmt.comptimePrint("{s} is missing toJS()", .{@typeName(Result)})); +fn call(comptime FunctionEnum: NodeFSFunctionEnum) NodeFSFunction { +    const Function = @field(JSC.Node.NodeFS, @tagName(FunctionEnum)); +    const FunctionType = @TypeOf(Function); + +    const function: std.builtin.Type.Fn = comptime @typeInfo(FunctionType).Fn; +    comptime if (function.params.len != 3) @compileError("Expected 3 arguments"); +    const Arguments = comptime function.params[1].type.?;      const NodeBindingClosure = struct {          pub fn bind(              _: *JSC.Node.NodeJSFS,              globalObject: *JSC.JSGlobalObject, -            _: *JSC.CallFrame, +            callframe: *JSC.CallFrame,          ) callconv(.C) JSC.JSValue { -            globalObject.throw("Not implemented yet", .{}); -            return .zero; -            // var slice = ArgumentsSlice.init(arguments); +            if (comptime FunctionEnum != .readdir and FunctionEnum != .lstat and FunctionEnum != .stat) { +                globalObject.throw("Not implemented yet", .{}); +                return .zero; +            } + +            var arguments = callframe.arguments(8); + +            var slice = ArgumentsSlice.init(globalObject.bunVM(), arguments.ptr[0..arguments.len]); +            var exceptionref: JSC.C.JSValueRef = null; +            const args = if (comptime Arguments != void) +                (Arguments.fromJS(globalObject, &slice, &exceptionref) orelse { +                    // we might've already thrown +                    if (exceptionref != null) +                        globalObject.throwValue(JSC.JSValue.c(exceptionref)); +                    return .zero; +                }) +            else +                Arguments{}; + +            const exception1 = JSC.JSValue.c(exceptionref); + +            if (exception1 != .zero) { +                globalObject.throwValue(exception1); +                return .zero; +            } + +            // TODO: handle globalObject.throwValue + +            if (comptime FunctionEnum == .readdir) { +                return JSC.Node.AsyncReaddirTask.create(globalObject, args, slice.vm); +            } + +            if (comptime FunctionEnum == .stat or FunctionEnum == .lstat) { +                return JSC.Node.AsyncStatTask.create(globalObject, args, slice.vm, FunctionEnum == .lstat); +            }              // defer {              //     for (arguments.len) |arg| { diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 23d693d69..dadf28629 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1937,27 +1937,20 @@ pub const Path = struct {      ) callconv(.C) JSC.JSValue {          if (comptime is_bindgen) return JSC.JSValue.jsUndefined();          if (args_len == 0) return JSC.ZigString.init("").toValue(globalThis); - +        var arena = @import("root").bun.ArenaAllocator.init(heap_allocator); +        var arena_allocator = arena.allocator();          var stack_fallback_allocator = std.heap.stackFallback( -            (32 * @sizeOf(string)), -            heap_allocator, +            ((32 * @sizeOf(string)) + 1024), +            arena_allocator,          );          var allocator = stack_fallback_allocator.get(); -        var arena = @import("root").bun.ArenaAllocator.init(heap_allocator); -        var arena_allocator = arena.allocator(); +          defer arena.deinit();          var buf: [bun.MAX_PATH_BYTES]u8 = undefined;          var to_join = allocator.alloc(string, args_len) catch unreachable; -        var possibly_utf16 = false;          for (args_ptr[0..args_len], 0..) |arg, i| {              const zig_str: JSC.ZigString = arg.getZigString(globalThis); -            if (zig_str.is16Bit()) { -                // TODO: remove this string conversion -                to_join[i] = zig_str.toSlice(arena_allocator).slice(); -                possibly_utf16 = true; -            } else { -                to_join[i] = zig_str.slice(); -            } +            to_join[i] = zig_str.toSlice(allocator).slice();          }          const out = if (!isWindows) @@ -1965,12 +1958,9 @@ pub const Path = struct {          else              PathHandler.joinStringBuf(&buf, to_join, .windows); -        var out_str = JSC.ZigString.init(out); -        if (possibly_utf16) { -            out_str.setOutputEncoding(); -        } - -        return out_str.toValueGC(globalThis); +        var str = bun.String.create(out); +        defer str.deref(); +        return str.toJS(globalThis);      }      pub fn normalize(globalThis: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue {          if (comptime is_bindgen) return JSC.JSValue.jsUndefined(); diff --git a/src/js/node/fs.js b/src/js/node/fs.js index 5e72d6e27..f847cf179 100644 --- a/src/js/node/fs.js +++ b/src/js/node/fs.js @@ -122,9 +122,6 @@ export var access = function access(...args) {    link = function link(...args) {      callbackify(fs.linkSync, args);    }, -  lstat = function lstat(...args) { -    callbackify(fs.lstatSync, args); -  },    mkdir = function mkdir(...args) {      callbackify(fs.mkdirSync, args);    }, @@ -141,7 +138,13 @@ export var access = function access(...args) {      callbackify(fs.writeSync, args);    },    readdir = function readdir(...args) { -    callbackify(fs.readdirSync, args); +    const callback = args[args.length - 1]; +    if (typeof callback !== "function") { +      // TODO: set code +      throw new TypeError("Callback must be a function"); +    } + +    fs.readdir(...args).then(result => callback(null, result), callback);    },    readFile = function readFile(...args) {      callbackify(fs.readFileSync, args); @@ -158,8 +161,23 @@ export var access = function access(...args) {    rename = function rename(...args) {      callbackify(fs.renameSync, args);    }, +  lstat = function lstat(...args) { +    const callback = args[args.length - 1]; +    if (typeof callback !== "function") { +      // TODO: set code +      throw new TypeError("Callback must be a function"); +    } + +    fs.lstat(...args).then(result => callback(null, result), callback); +  },    stat = function stat(...args) { -    callbackify(fs.statSync, args); +    const callback = args[args.length - 1]; +    if (typeof callback !== "function") { +      // TODO: set code +      throw new TypeError("Callback must be a function"); +    } + +    fs.stat(...args).then(result => callback(null, result), callback);    },    symlink = function symlink(...args) {      callbackify(fs.symlinkSync, args); diff --git a/src/js/node/fs.promises.ts b/src/js/node/fs.promises.ts index 12278ef53..1826586ae 100644 --- a/src/js/node/fs.promises.ts +++ b/src/js/node/fs.promises.ts @@ -11,30 +11,9 @@ var fs = Bun.fs();  const notrace = "::bunternal::";  var promisify = {    [notrace]: fsFunction => { -    // TODO: remove variadic arguments -    // we can use new Function() here instead -    // based on fsFucntion.length -    var func = { -      [notrace]: function (resolve, reject, args) { -        var result; -        try { -          result = fsFunction.apply(fs, args); -          args = undefined; -        } catch (err) { -          args = undefined; -          reject(err); -          return; -        } - -        resolve(result); -      }, -    }[notrace]; -      return async function (...args) { -      // we await it so that the stack is captured -      return await new Promise((resolve, reject) => { -        process.nextTick(func, resolve, reject, args); -      }); +      await 1; +      return fsFunction.apply(fs, args);      };    },  }[notrace]; @@ -104,19 +83,19 @@ export var access = promisify(fs.accessSync),    lchmod = promisify(fs.lchmodSync),    lchown = promisify(fs.lchownSync),    link = promisify(fs.linkSync), -  lstat = promisify(fs.lstatSync), +  lstat = fs.lstat.bind(fs),    mkdir = promisify(fs.mkdirSync),    mkdtemp = promisify(fs.mkdtempSync),    open = promisify(fs.openSync),    read = promisify(fs.readSync),    write = promisify(fs.writeSync), -  readdir = promisify(fs.readdirSync), +  readdir = fs.readdir.bind(fs),    readFile = promisify(fs.readFileSync),    writeFile = promisify(fs.writeFileSync),    readlink = promisify(fs.readlinkSync),    realpath = promisify(fs.realpathSync),    rename = promisify(fs.renameSync), -  stat = promisify(fs.statSync), +  stat = fs.stat.bind(fs),    symlink = promisify(fs.symlinkSync),    truncate = promisify(fs.truncateSync),    unlink = promisify(fs.unlinkSync), diff --git a/src/js/out/modules/node/fs.js b/src/js/out/modules/node/fs.js index b7457f104..947eaf826 100644 --- a/src/js/out/modules/node/fs.js +++ b/src/js/out/modules/node/fs.js @@ -97,8 +97,6 @@ var access = function access2(...args) {    callbackify(fs.lchownSync, args);  }, link = function link2(...args) {    callbackify(fs.linkSync, args); -}, lstat = function lstat2(...args) { -  callbackify(fs.lstatSync, args);  }, mkdir = function mkdir2(...args) {    callbackify(fs.mkdirSync, args);  }, mkdtemp = function mkdtemp2(...args) { @@ -110,7 +108,10 @@ var access = function access2(...args) {  }, write = function write2(...args) {    callbackify(fs.writeSync, args);  }, readdir = function readdir2(...args) { -  callbackify(fs.readdirSync, args); +  const callback = args[args.length - 1]; +  if (typeof callback !== "function") +    throw new TypeError("Callback must be a function"); +  fs.readdir(...args).then((result) => callback(null, result), callback);  }, readFile = function readFile2(...args) {    callbackify(fs.readFileSync, args);  }, writeFile = function writeFile2(...args) { @@ -121,8 +122,16 @@ var access = function access2(...args) {    callbackify(fs.realpathSync, args);  }, rename = function rename2(...args) {    callbackify(fs.renameSync, args); +}, lstat = function lstat2(...args) { +  const callback = args[args.length - 1]; +  if (typeof callback !== "function") +    throw new TypeError("Callback must be a function"); +  fs.lstat(...args).then((result) => callback(null, result), callback);  }, stat = function stat2(...args) { -  callbackify(fs.statSync, args); +  const callback = args[args.length - 1]; +  if (typeof callback !== "function") +    throw new TypeError("Callback must be a function"); +  fs.stat(...args).then((result) => callback(null, result), callback);  }, symlink = function symlink2(...args) {    callbackify(fs.symlinkSync, args);  }, truncate = function truncate2(...args) { diff --git a/src/js/out/modules/node/fs.promises.js b/src/js/out/modules/node/fs.promises.js index 9ac3c6f65..185955dd5 100644 --- a/src/js/out/modules/node/fs.promises.js +++ b/src/js/out/modules/node/fs.promises.js @@ -1 +1 @@ -var r=(B)=>{return import.meta.require(B)};function J(B,C={}){const G=[];if(B instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(B))B=B.toString();else if(typeof B!=="string")throw new TypeError("Expected path to be a string or Buffer");let D=null;if(typeof C==="string")C={encoding:C};return S.watch(B,C||{},(z,A)=>{if(G.push({eventType:z,filename:A}),D){const H=D;D=null,H()}}),{async*[Symbol.asyncIterator](){let z=!1;while(!z){while(G.length){let A=G.shift();if(A.eventType==="close"){z=!0;break}if(A.eventType==="error")throw z=!0,A.filename;yield A}await new Promise((A)=>D=A)}}}}var S=Bun.fs(),I="::bunternal::",q={[I]:(B)=>{var C={[I]:function(G,D,z){var A;try{A=B.apply(S,z),z=void 0}catch(H){z=void 0,D(H);return}G(A)}}[I];return async function(...G){return await new Promise((D,z)=>{process.nextTick(C,D,z,G)})}}}[I],K=q(S.accessSync),L=q(S.appendFileSync),M=q(S.closeSync),N=q(S.copyFileSync),O=q(S.existsSync),P=q(S.chownSync),Q=q(S.chmodSync),U=q(S.fchmodSync),V=q(S.fchownSync),X=q(S.fstatSync),Y=q(S.fsyncSync),Z=q(S.ftruncateSync),_=q(S.futimesSync),$=q(S.lchmodSync),T=q(S.lchownSync),W=q(S.linkSync),k=q(S.lstatSync),E=q(S.mkdirSync),x=q(S.mkdtempSync),F=q(S.openSync),R=q(S.readSync),g=q(S.writeSync),h=q(S.readdirSync),j=q(S.readFileSync),w=q(S.writeFileSync),b=q(S.readlinkSync),u=q(S.realpathSync),d=q(S.renameSync),c=q(S.statSync),v=q(S.symlinkSync),a=q(S.truncateSync),y=q(S.unlinkSync),l=q(S.utimesSync),p=q(S.lutimesSync),m=q(S.rmSync),n=q(S.rmdirSync),t=(B,C,G)=>{return new Promise((D,z)=>{try{var A=S.writevSync(B,C,G)}catch(H){z(H);return}D({bytesWritten:A,buffers:C})})},o=(B,C,G)=>{return new Promise((D,z)=>{try{var A=S.readvSync(B,C,G)}catch(H){z(H);return}D({bytesRead:A,buffers:C})})},f={access:K,appendFile:L,close:M,copyFile:N,exists:O,chown:P,chmod:Q,fchmod:U,fchown:V,fstat:X,fsync:Y,ftruncate:Z,futimes:_,lchmod:$,lchown:T,link:W,lstat:k,mkdir:E,mkdtemp:x,open:F,read:R,write:g,readdir:h,readFile:j,writeFile:w,readlink:b,realpath:u,rename:d,stat:c,symlink:v,truncate:a,unlink:y,utimes:l,lutimes:p,rm:m,rmdir:n,watch:J,writev:t,readv:o,constants,[Symbol.for("CommonJS")]:0};export{t as writev,w as writeFile,g as write,J as watch,l as utimes,y as unlink,a as truncate,v as symlink,c as stat,n as rmdir,m as rm,d as rename,u as realpath,o as readv,b as readlink,h as readdir,j as readFile,R as read,F as open,x as mkdtemp,E as mkdir,p as lutimes,k as lstat,W as link,T as lchown,$ as lchmod,_ as futimes,Z as ftruncate,Y as fsync,X as fstat,V as fchown,U as fchmod,O as exists,f as default,N as copyFile,M as close,P as chown,Q as chmod,L as appendFile,K as access}; +var o=(g)=>{return import.meta.require(g)};function H(g,q={}){const C=[];if(g instanceof URL)throw new TypeError("Watch URLs are not supported yet");else if(Buffer.isBuffer(g))g=g.toString();else if(typeof g!=="string")throw new TypeError("Expected path to be a string or Buffer");let B=null;if(typeof q==="string")q={encoding:q};return P.watch(g,q||{},(A,z)=>{if(C.push({eventType:A,filename:z}),B){const D=B;B=null,D()}}),{async*[Symbol.asyncIterator](){let A=!1;while(!A){while(C.length){let z=C.shift();if(z.eventType==="close"){A=!0;break}if(z.eventType==="error")throw A=!0,z.filename;yield z}await new Promise((z)=>B=z)}}}}var P=Bun.fs(),G="::bunternal::",U={[G]:(g)=>{return async function(...q){return await 1,g.apply(P,q)}}}[G],I=U(P.accessSync),J=U(P.appendFileSync),K=U(P.closeSync),L=U(P.copyFileSync),M=U(P.existsSync),N=U(P.chownSync),O=U(P.chmodSync),Q=U(P.fchmodSync),S=U(P.fchownSync),V=U(P.fstatSync),X=U(P.fsyncSync),Y=U(P.ftruncateSync),Z=U(P.futimesSync),_=U(P.lchmodSync),$=U(P.lchownSync),T=U(P.linkSync),W=P.lstat.bind(P),j=U(P.mkdirSync),x=U(P.mkdtempSync),E=U(P.openSync),F=U(P.readSync),w=U(P.writeSync),k=P.readdir.bind(P),R=U(P.readFileSync),h=U(P.writeFileSync),b=U(P.readlinkSync),u=U(P.realpathSync),c=U(P.renameSync),d=P.stat.bind(P),v=U(P.symlinkSync),a=U(P.truncateSync),l=U(P.unlinkSync),y=U(P.utimesSync),p=U(P.lutimesSync),m=U(P.rmSync),n=U(P.rmdirSync),t=(g,q,C)=>{return new Promise((B,A)=>{try{var z=P.writevSync(g,q,C)}catch(D){A(D);return}B({bytesWritten:z,buffers:q})})},r=(g,q,C)=>{return new Promise((B,A)=>{try{var z=P.readvSync(g,q,C)}catch(D){A(D);return}B({bytesRead:z,buffers:q})})},f={access:I,appendFile:J,close:K,copyFile:L,exists:M,chown:N,chmod:O,fchmod:Q,fchown:S,fstat:V,fsync:X,ftruncate:Y,futimes:Z,lchmod:_,lchown:$,link:T,lstat:W,mkdir:j,mkdtemp:x,open:E,read:F,write:w,readdir:k,readFile:R,writeFile:h,readlink:b,realpath:u,rename:c,stat:d,symlink:v,truncate:a,unlink:l,utimes:y,lutimes:p,rm:m,rmdir:n,watch:H,writev:t,readv:r,constants,[Symbol.for("CommonJS")]:0};export{t as writev,h as writeFile,w as write,H as watch,y as utimes,l as unlink,a as truncate,v as symlink,d as stat,n as rmdir,m as rm,c as rename,u as realpath,r as readv,b as readlink,k as readdir,R as readFile,F as read,E as open,x as mkdtemp,j as mkdir,p as lutimes,W as lstat,T as link,$ as lchown,_ as lchmod,Z as futimes,Y as ftruncate,X as fsync,V as fstat,S as fchown,Q as fchmod,M as exists,f as default,L as copyFile,K as close,N as chown,O as chmod,J as appendFile,I as access}; | 
