Files
bun.sh/src/bun.js/node/node_fs_binding.zig
2024-11-01 18:16:04 -07:00

294 lines
10 KiB
Zig

const bun = @import("root").bun;
const JSC = bun.JSC;
const std = @import("std");
const Flavor = JSC.Node.Flavor;
const ArgumentsSlice = JSC.Node.ArgumentsSlice;
const system = std.posix.system;
const Maybe = JSC.Maybe;
const Encoding = JSC.Node.Encoding;
const FeatureFlags = bun.FeatureFlags;
const Args = JSC.Node.NodeFS.Arguments;
const d = JSC.d;
const NodeFSFunction = fn (
this: *JSC.Node.NodeJSFS,
globalObject: *JSC.JSGlobalObject,
callframe: *JSC.CallFrame,
) JSC.JSValue;
const NodeFSFunctionEnum = std.meta.DeclEnum(JSC.Node.NodeFS);
fn callSync(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 FormattedName = comptime [1]u8{std.ascii.toUpper(@tagName(FunctionEnum)[0])} ++ @tagName(FunctionEnum)[1..];
const Result = comptime JSC.Maybe(@field(JSC.Node.NodeFS.ReturnType, FormattedName));
_ = Result;
const NodeBindingClosure = struct {
pub fn bind(
this: *JSC.Node.NodeJSFS,
globalObject: *JSC.JSGlobalObject,
callframe: *JSC.CallFrame,
) JSC.JSValue {
var exceptionref: JSC.C.JSValueRef = null;
var arguments = callframe.arguments(8);
var slice = ArgumentsSlice.init(globalObject.bunVM(), arguments.slice());
defer slice.deinit();
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{};
defer {
if (comptime Arguments != void and @hasDecl(Arguments, "deinit")) args.deinit();
}
const exception1 = JSC.JSValue.c(exceptionref);
if (exception1 != .zero) {
globalObject.throwValue(exception1);
return .zero;
} else if (globalObject.hasException()) {
return .zero;
}
var result = Function(
&this.node_fs,
args,
comptime Flavor.sync,
);
switch (result) {
.err => |err| {
globalObject.throwValue(JSC.JSValue.c(err.toJS(globalObject)));
return .zero;
},
.result => |*res| {
return globalObject.toJS(res, .temporary);
},
}
}
};
return NodeBindingClosure.bind;
}
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(this: *JSC.Node.NodeJSFS, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
var arguments = callframe.arguments(8);
var slice = ArgumentsSlice.init(globalObject.bunVM(), arguments.slice());
slice.will_be_async = true;
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));
slice.deinit();
return .zero;
})
else
Arguments{};
const exception1 = JSC.JSValue.c(exceptionref);
if (exception1 != .zero) {
globalObject.throwValue(exception1);
slice.deinit();
return .zero;
} else if (globalObject.hasException()) {
slice.deinit();
return .zero;
}
const Task = @field(JSC.Node.Async, @tagName(FunctionEnum));
if (comptime FunctionEnum == .cp) {
return Task.create(globalObject, this, args, globalObject.bunVM(), slice.arena);
} else {
if (comptime FunctionEnum == .readdir) {
if (args.recursive) {
return JSC.Node.Async.readdir_recursive.create(globalObject, args, globalObject.bunVM());
}
}
return Task.create(globalObject, this, args, globalObject.bunVM());
}
}
};
return NodeBindingClosure.bind;
}
pub const NodeJSFS = struct {
node_fs: JSC.Node.NodeFS = .{},
pub usingnamespace JSC.Codegen.JSNodeJSFS;
pub usingnamespace bun.New(@This());
pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*@This() {
globalObject.throw("Not a constructor", .{});
return null;
}
pub fn finalize(this: *JSC.Node.NodeJSFS) void {
if (this.node_fs.vm) |vm| {
if (vm.node_fs == &this.node_fs) {
return;
}
}
this.destroy();
}
pub const access = call(.access);
pub const appendFile = call(.appendFile);
pub const close = call(.close);
pub const copyFile = call(.copyFile);
pub const cp = call(.cp);
pub const exists = call(.exists);
pub const chown = call(.chown);
pub const chmod = call(.chmod);
pub const fchmod = call(.fchmod);
pub const fchown = call(.fchown);
pub const fstat = call(.fstat);
pub const fsync = call(.fsync);
pub const ftruncate = call(.ftruncate);
pub const futimes = call(.futimes);
pub const lchmod = call(.lchmod);
pub const lchown = call(.lchown);
pub const link = call(.link);
pub const lstat = call(.lstat);
pub const mkdir = call(.mkdir);
pub const mkdtemp = call(.mkdtemp);
pub const open = call(.open);
pub const read = call(.read);
pub const write = call(.write);
pub const readdir = call(.readdir);
pub const readFile = call(.readFile);
pub const writeFile = call(.writeFile);
pub const readlink = call(.readlink);
pub const rm = call(.rm);
pub const rmdir = call(.rmdir);
pub const realpath = call(.realpath);
pub const rename = call(.rename);
pub const stat = call(.stat);
pub const symlink = call(.symlink);
pub const truncate = call(.truncate);
pub const unlink = call(.unlink);
pub const utimes = call(.utimes);
pub const lutimes = call(.lutimes);
pub const accessSync = callSync(.access);
pub const appendFileSync = callSync(.appendFile);
pub const closeSync = callSync(.close);
pub const cpSync = callSync(.cp);
pub const copyFileSync = callSync(.copyFile);
pub const existsSync = callSync(.exists);
pub const chownSync = callSync(.chown);
pub const chmodSync = callSync(.chmod);
pub const fchmodSync = callSync(.fchmod);
pub const fchownSync = callSync(.fchown);
pub const fstatSync = callSync(.fstat);
pub const fsyncSync = callSync(.fsync);
pub const ftruncateSync = callSync(.ftruncate);
pub const futimesSync = callSync(.futimes);
pub const lchmodSync = callSync(.lchmod);
pub const lchownSync = callSync(.lchown);
pub const linkSync = callSync(.link);
pub const lstatSync = callSync(.lstat);
pub const mkdirSync = callSync(.mkdir);
pub const mkdtempSync = callSync(.mkdtemp);
pub const openSync = callSync(.open);
pub const readSync = callSync(.read);
pub const writeSync = callSync(.write);
pub const readdirSync = callSync(.readdir);
pub const readFileSync = callSync(.readFile);
pub const writeFileSync = callSync(.writeFile);
pub const readlinkSync = callSync(.readlink);
pub const realpathSync = callSync(.realpath);
pub const renameSync = callSync(.rename);
pub const statSync = callSync(.stat);
pub const symlinkSync = callSync(.symlink);
pub const truncateSync = callSync(.truncate);
pub const unlinkSync = callSync(.unlink);
pub const utimesSync = callSync(.utimes);
pub const lutimesSync = callSync(.lutimes);
pub const rmSync = callSync(.rm);
pub const rmdirSync = callSync(.rmdir);
pub const writev = call(.writev);
pub const writevSync = callSync(.writev);
pub const readv = call(.readv);
pub const readvSync = callSync(.readv);
pub const fdatasyncSync = callSync(.fdatasync);
pub const fdatasync = call(.fdatasync);
pub fn getDirent(_: *NodeJSFS, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
return JSC.Node.Dirent.getConstructor(globalThis);
}
pub fn getStats(_: *NodeJSFS, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
return JSC.Node.StatsSmall.getConstructor(globalThis);
}
pub const watch = callSync(.watch);
pub const watchFile = callSync(.watchFile);
pub const unwatchFile = callSync(.unwatchFile);
// Not implemented yet:
const notimpl = fdatasync;
pub const opendir = notimpl;
pub const opendirSync = notimpl;
};
pub fn createBinding(globalObject: *JSC.JSGlobalObject) JSC.JSValue {
const module = NodeJSFS.new(.{});
const vm = globalObject.bunVM();
if (vm.standalone_module_graph != null)
module.node_fs.vm = vm;
return module.toJS(globalObject);
}
pub fn createMemfdForTesting(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) JSC.JSValue {
const arguments = callFrame.arguments(1);
if (arguments.len < 1) {
return .undefined;
}
if (comptime !bun.Environment.isLinux) {
globalObject.throw("memfd_create is not implemented on this platform", .{});
return .zero;
}
const size = arguments.ptr[0].toInt64();
switch (bun.sys.memfd_create("my_memfd", std.os.linux.MFD.CLOEXEC)) {
.result => |fd| {
_ = bun.sys.ftruncate(fd, size);
return JSC.JSValue.jsNumber(fd.cast());
},
.err => |err| {
globalObject.throwValue(err.toJSC(globalObject));
return .zero;
},
}
}