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}; |