src/bun.js/node/types.zig (1,838 lines of code) (raw):
const std = @import("std");
const builtin = @import("builtin");
const bun = @import("root").bun;
const meta = bun.meta;
const windows = bun.windows;
const heap_allocator = bun.default_allocator;
const is_bindgen: bool = false;
const kernel32 = windows.kernel32;
const logger = bun.logger;
const posix = std.posix;
const path_handler = bun.path;
const strings = bun.strings;
const string = bun.string;
const C = bun.C;
const L = strings.literal;
const Environment = bun.Environment;
const Fs = @import("../../fs.zig");
const IdentityContext = @import("../../identity_context.zig").IdentityContext;
const JSC = bun.JSC;
const Mode = bun.Mode;
const Shimmer = @import("../bindings/shimmer.zig").Shimmer;
const Syscall = bun.sys;
const URL = @import("../../url.zig").URL;
const Value = std.json.Value;
pub const Path = @import("./path.zig");
fn typeBaseNameT(comptime T: type) []const u8 {
return meta.typeBaseName(@typeName(T));
}
pub const Buffer = JSC.MarkedArrayBuffer;
/// On windows, this is what libuv expects
/// On unix it is what the utimens api expects
pub const TimeLike = if (Environment.isWindows) f64 else std.posix.timespec;
pub const Flavor = enum {
sync,
promise,
callback,
pub fn Wrap(comptime this: Flavor, comptime T: type) type {
return comptime brk: {
switch (this) {
.sync => break :brk T,
// .callback => {
// const Callback = CallbackTask(Type);
// },
else => @compileError("Not implemented yet"),
}
};
}
};
/// Node.js expects the error to include contextual information
/// - "syscall"
/// - "path"
/// - "errno"
pub fn Maybe(comptime ReturnTypeT: type, comptime ErrorTypeT: type) type {
const hasRetry = @hasDecl(ErrorTypeT, "retry");
const hasTodo = @hasDecl(ErrorTypeT, "todo");
return union(Tag) {
pub const ErrorType = ErrorTypeT;
pub const ReturnType = ReturnTypeT;
err: ErrorType,
result: ReturnType,
pub const Tag = enum { err, result };
pub const retry: @This() = if (hasRetry) .{ .err = ErrorType.retry } else .{ .err = ErrorType{} };
pub const success: @This() = @This(){
.result = std.mem.zeroes(ReturnType),
};
pub fn assert(this: @This()) ReturnType {
switch (this) {
.err => |err| {
bun.Output.panic("Unexpected error\n{}", .{err});
},
.result => |result| return result,
}
}
pub inline fn todo() @This() {
if (Environment.allow_assert) {
if (comptime ReturnType == void) {
@panic("TODO called!");
}
@panic(comptime "TODO: Maybe(" ++ typeBaseNameT(ReturnType) ++ ")");
}
if (hasTodo) {
return .{ .err = ErrorType.todo() };
}
return .{ .err = ErrorType{} };
}
pub fn unwrap(this: @This()) !ReturnType {
return switch (this) {
.result => |r| r,
.err => |e| bun.errnoToZigErr(e.errno),
};
}
pub inline fn initErr(e: ErrorType) Maybe(ReturnType, ErrorType) {
return .{ .err = e };
}
pub inline fn initErrWithP(e: C.SystemErrno, syscall: Syscall.Tag, path: anytype) Maybe(ReturnType, ErrorType) {
return .{ .err = .{
.errno = @intFromEnum(e),
.syscall = syscall,
.path = path,
} };
}
pub inline fn asErr(this: *const @This()) ?ErrorType {
if (this.* == .err) return this.err;
return null;
}
pub inline fn initResult(result: ReturnType) Maybe(ReturnType, ErrorType) {
return .{ .result = result };
}
pub fn toJS(this: @This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return switch (this) {
.result => |r| switch (ReturnType) {
JSC.JSValue => r,
void => .undefined,
bool => JSC.JSValue.jsBoolean(r),
JSC.ArrayBuffer => r.toJS(globalObject, null),
[]u8 => JSC.ArrayBuffer.fromBytes(r, .ArrayBuffer).toJS(globalObject, null),
else => switch (@typeInfo(ReturnType)) {
.Int, .Float, .ComptimeInt, .ComptimeFloat => JSC.JSValue.jsNumber(r),
.Struct, .Enum, .Opaque, .Union => r.toJS(globalObject),
.Pointer => {
if (bun.trait.isZigString(ReturnType))
JSC.ZigString.init(bun.asByteSlice(r)).withEncoding().toJS(globalObject);
return r.toJS(globalObject);
},
},
},
.err => |e| e.toJSC(globalObject),
};
}
pub fn toArrayBuffer(this: @This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return switch (this) {
.result => |r| JSC.ArrayBuffer.fromBytes(r, .ArrayBuffer).toJS(globalObject, null),
.err => |e| e.toJSC(globalObject),
};
}
pub inline fn getErrno(this: @This()) posix.E {
return switch (this) {
.result => posix.E.SUCCESS,
.err => |e| @enumFromInt(e.errno),
};
}
pub inline fn errnoSys(rc: anytype, syscall: Syscall.Tag) ?@This() {
if (comptime Environment.isWindows) {
if (comptime @TypeOf(rc) == std.os.windows.NTSTATUS) {} else {
if (rc != 0) return null;
}
}
return switch (Syscall.getErrno(rc)) {
.SUCCESS => null,
else => |e| @This(){
// always truncate
.err = .{
.errno = translateToErrInt(e),
.syscall = syscall,
},
},
};
}
pub inline fn errno(err: anytype, syscall: Syscall.Tag) @This() {
return @This(){
// always truncate
.err = .{
.errno = translateToErrInt(err),
.syscall = syscall,
},
};
}
pub inline fn errnoSysFd(rc: anytype, syscall: Syscall.Tag, fd: bun.FileDescriptor) ?@This() {
if (comptime Environment.isWindows) {
if (comptime @TypeOf(rc) == std.os.windows.NTSTATUS) {} else {
if (rc != 0) return null;
}
}
return switch (Syscall.getErrno(rc)) {
.SUCCESS => null,
else => |e| @This(){
// Always truncate
.err = .{
.errno = translateToErrInt(e),
.syscall = syscall,
.fd = fd,
},
},
};
}
pub inline fn errnoSysP(rc: anytype, syscall: Syscall.Tag, path: anytype) ?@This() {
if (bun.meta.Item(@TypeOf(path)) == u16) {
@compileError("Do not pass WString path to errnoSysP, it needs the path encoded as utf8");
}
if (comptime Environment.isWindows) {
if (comptime @TypeOf(rc) == std.os.windows.NTSTATUS) {} else {
if (rc != 0) return null;
}
}
return switch (Syscall.getErrno(rc)) {
.SUCCESS => null,
else => |e| @This(){
// Always truncate
.err = .{
.errno = translateToErrInt(e),
.syscall = syscall,
.path = bun.asByteSlice(path),
},
},
};
}
};
}
fn translateToErrInt(err: anytype) bun.sys.Error.Int {
return switch (@TypeOf(err)) {
bun.windows.NTSTATUS => @intFromEnum(bun.windows.translateNTStatusToErrno(err)),
else => @truncate(@intFromEnum(err)),
};
}
pub const BlobOrStringOrBuffer = union(enum) {
blob: JSC.WebCore.Blob,
string_or_buffer: StringOrBuffer,
pub fn deinit(this: *const BlobOrStringOrBuffer) void {
switch (this.*) {
.blob => |blob| {
if (blob.store) |store| {
store.deref();
}
},
.string_or_buffer => |*str| {
str.deinit();
},
}
}
pub fn slice(this: *const BlobOrStringOrBuffer) []const u8 {
return switch (this.*) {
.blob => |*blob| blob.sharedView(),
.string_or_buffer => |*str| str.slice(),
};
}
pub fn protect(this: *const BlobOrStringOrBuffer) void {
switch (this.*) {
.string_or_buffer => |sob| {
sob.protect();
},
else => {},
}
}
pub fn deinitAndUnprotect(this: *BlobOrStringOrBuffer) void {
switch (this.*) {
.string_or_buffer => |sob| {
sob.deinitAndUnprotect();
},
.blob => |*blob| {
blob.deinit();
},
}
}
pub fn fromJS(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue) ?BlobOrStringOrBuffer {
if (value.as(JSC.WebCore.Blob)) |blob| {
if (blob.store) |store| {
store.ref();
}
return .{ .blob = blob.* };
}
return .{ .string_or_buffer = StringOrBuffer.fromJS(global, allocator, value) orelse return null };
}
pub fn fromJSWithEncodingValue(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue) ?BlobOrStringOrBuffer {
return fromJSWithEncodingValueMaybeAsync(global, allocator, value, encoding_value, false);
}
pub fn fromJSWithEncodingValueMaybeAsync(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue, is_async: bool) ?BlobOrStringOrBuffer {
if (value.as(JSC.WebCore.Blob)) |blob| {
if (blob.store) |store| {
store.ref();
}
return .{ .blob = blob.* };
}
return .{ .string_or_buffer = StringOrBuffer.fromJSWithEncodingValueMaybeAsync(global, allocator, value, encoding_value, is_async) orelse return null };
}
};
pub const StringOrBuffer = union(enum) {
string: bun.SliceWithUnderlyingString,
threadsafe_string: bun.SliceWithUnderlyingString,
encoded_slice: JSC.ZigString.Slice,
buffer: Buffer,
pub const empty = StringOrBuffer{ .encoded_slice = JSC.ZigString.Slice.empty };
pub fn toThreadSafe(this: *@This()) void {
switch (this.*) {
.string => {
this.string.toThreadSafe();
this.* = .{
.threadsafe_string = this.string,
};
},
.threadsafe_string => {},
.encoded_slice => {},
.buffer => {},
}
}
pub fn protect(this: *const StringOrBuffer) void {
switch (this.*) {
.buffer => |buf| {
buf.buffer.value.protect();
},
else => {},
}
}
pub fn fromJSToOwnedSlice(globalObject: *JSC.JSGlobalObject, value: JSC.JSValue, allocator: std.mem.Allocator) ![]u8 {
if (value.asArrayBuffer(globalObject)) |array_buffer| {
defer globalObject.vm().reportExtraMemory(array_buffer.len);
return try allocator.dupe(u8, array_buffer.byteSlice());
}
const str = bun.String.tryFromJS(value, globalObject) orelse return error.JSError;
defer str.deref();
const result = try str.toOwnedSlice(allocator);
defer globalObject.vm().reportExtraMemory(result.len);
return result;
}
pub fn toJS(this: *StringOrBuffer, ctx: JSC.C.JSContextRef) JSC.JSValue {
return switch (this.*) {
inline .threadsafe_string, .string => |*str| {
defer {
str.deinit();
str.* = .{};
}
return str.toJS(ctx);
},
.encoded_slice => {
defer {
this.encoded_slice.deinit();
this.encoded_slice = .{};
}
const str = bun.String.createUTF8(this.encoded_slice.slice());
defer str.deref();
return str.toJS(ctx);
},
.buffer => {
if (this.buffer.buffer.value != .zero) {
return this.buffer.buffer.value;
}
return this.buffer.toNodeBuffer(ctx);
},
};
}
pub fn slice(this: *const StringOrBuffer) []const u8 {
return switch (this.*) {
inline else => |*str| str.slice(),
};
}
pub fn deinit(this: *const StringOrBuffer) void {
switch (this.*) {
inline .threadsafe_string, .string => |*str| {
str.deinit();
},
.encoded_slice => |*encoded| {
encoded.deinit();
},
else => {},
}
}
pub fn deinitAndUnprotect(this: *const StringOrBuffer) void {
switch (this.*) {
inline .threadsafe_string, .string => |*str| {
str.deinit();
},
.buffer => |buffer| {
buffer.buffer.value.unprotect();
},
.encoded_slice => |*encoded| {
encoded.deinit();
},
}
}
pub fn fromJSMaybeAsync(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, is_async: bool) ?StringOrBuffer {
return switch (value.jsType()) {
JSC.JSValue.JSType.String, JSC.JSValue.JSType.StringObject, JSC.JSValue.JSType.DerivedStringObject, JSC.JSValue.JSType.Object => {
const str = bun.String.tryFromJS(value, global) orelse return null;
if (is_async) {
defer str.deref();
var possible_clone = str;
var sliced = possible_clone.toThreadSafeSlice(allocator);
sliced.reportExtraMemory(global.vm());
if (sliced.underlying.isEmpty()) {
return StringOrBuffer{ .encoded_slice = sliced.utf8 };
}
return StringOrBuffer{ .threadsafe_string = sliced };
} else {
return StringOrBuffer{ .string = str.toSlice(allocator) };
}
},
.ArrayBuffer,
.Int8Array,
.Uint8Array,
.Uint8ClampedArray,
.Int16Array,
.Uint16Array,
.Int32Array,
.Uint32Array,
.Float32Array,
.Float16Array,
.Float64Array,
.BigInt64Array,
.BigUint64Array,
.DataView,
=> StringOrBuffer{
.buffer = Buffer.fromArrayBuffer(global, value),
},
else => null,
};
}
pub fn fromJS(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue) ?StringOrBuffer {
return fromJSMaybeAsync(global, allocator, value, false);
}
pub fn fromJSWithEncoding(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding: Encoding) ?StringOrBuffer {
return fromJSWithEncodingMaybeAsync(global, allocator, value, encoding, false);
}
pub fn fromJSWithEncodingMaybeAsync(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding: Encoding, is_async: bool) ?StringOrBuffer {
if (value.isCell() and value.jsType().isTypedArray()) {
return StringOrBuffer{
.buffer = Buffer.fromTypedArray(global, value),
};
}
if (encoding == .utf8) {
return fromJSMaybeAsync(global, allocator, value, is_async);
}
var str = bun.String.tryFromJS(value, global) orelse return null;
defer str.deref();
if (str.isEmpty()) {
return fromJSMaybeAsync(global, allocator, value, is_async);
}
const out = str.encode(encoding);
defer global.vm().reportExtraMemory(out.len);
return .{
.encoded_slice = JSC.ZigString.Slice.init(bun.default_allocator, out),
};
}
pub fn fromJSWithEncodingValue(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue) ?StringOrBuffer {
const encoding: Encoding = brk: {
if (!encoding_value.isCell())
break :brk .utf8;
break :brk Encoding.fromJS(encoding_value, global) orelse .utf8;
};
return fromJSWithEncoding(global, allocator, value, encoding);
}
pub fn fromJSWithEncodingValueMaybeAsync(global: *JSC.JSGlobalObject, allocator: std.mem.Allocator, value: JSC.JSValue, encoding_value: JSC.JSValue, maybe_async: bool) ?StringOrBuffer {
const encoding: Encoding = brk: {
if (!encoding_value.isCell())
break :brk .utf8;
break :brk Encoding.fromJS(encoding_value, global) orelse .utf8;
};
return fromJSWithEncodingMaybeAsync(global, allocator, value, encoding, maybe_async);
}
};
pub const ErrorCode = @import("./nodejs_error_code.zig").Code;
// We can't really use Zig's error handling for syscalls because Node.js expects the "real" errno to be returned
// and various issues with std.posix that make it too unstable for arbitrary user input (e.g. how .BADF is marked as unreachable)
/// https://github.com/nodejs/node/blob/master/lib/buffer.js#L587
pub const Encoding = enum(u8) {
utf8,
ucs2,
utf16le,
latin1,
ascii,
base64,
base64url,
hex,
/// Refer to the buffer's encoding
buffer,
pub const map = bun.ComptimeStringMap(Encoding, .{
.{ "utf-8", Encoding.utf8 },
.{ "utf8", Encoding.utf8 },
.{ "ucs-2", Encoding.utf16le },
.{ "ucs2", Encoding.utf16le },
.{ "utf16-le", Encoding.utf16le },
.{ "utf16le", Encoding.utf16le },
.{ "binary", Encoding.latin1 },
.{ "latin1", Encoding.latin1 },
.{ "ascii", Encoding.ascii },
.{ "base64", Encoding.base64 },
.{ "hex", Encoding.hex },
.{ "buffer", Encoding.buffer },
.{ "base64url", Encoding.base64url },
});
pub fn isBinaryToText(this: Encoding) bool {
return switch (this) {
.hex, .base64, .base64url => true,
else => false,
};
}
/// Caller must verify the value is a string
pub fn fromJS(value: JSC.JSValue, global: *JSC.JSGlobalObject) ?Encoding {
return map.fromJSCaseInsensitive(global, value);
}
/// Caller must verify the value is a string
pub fn from(slice: []const u8) ?Encoding {
return strings.inMapCaseInsensitive(slice, map);
}
pub fn encodeWithSize(encoding: Encoding, globalObject: *JSC.JSGlobalObject, comptime size: usize, input: *const [size]u8) JSC.JSValue {
switch (encoding) {
.base64 => {
var buf: [std.base64.standard.Encoder.calcSize(size)]u8 = undefined;
const len = bun.base64.encode(&buf, input);
return JSC.ZigString.init(buf[0..len]).toJS(globalObject);
},
.base64url => {
var buf: [std.base64.url_safe_no_pad.Encoder.calcSize(size)]u8 = undefined;
const encoded = std.base64.url_safe_no_pad.Encoder.encode(&buf, input);
return JSC.ZigString.init(buf[0..encoded.len]).toJS(globalObject);
},
.hex => {
var buf: [size * 4]u8 = undefined;
const out = std.fmt.bufPrint(&buf, "{}", .{std.fmt.fmtSliceHexLower(input)}) catch bun.outOfMemory();
const result = JSC.ZigString.init(out).toJS(globalObject);
return result;
},
.buffer => {
return JSC.ArrayBuffer.createBuffer(globalObject, input);
},
inline else => |enc| {
const res = JSC.WebCore.Encoder.toString(input.ptr, size, globalObject, enc);
if (res.isError()) {
globalObject.throwValue(res);
return .zero;
}
return res;
},
}
}
pub fn encodeWithMaxSize(encoding: Encoding, globalObject: *JSC.JSGlobalObject, comptime max_size: usize, input: []const u8) JSC.JSValue {
switch (encoding) {
.base64 => {
var base64_buf: [std.base64.standard.Encoder.calcSize(max_size * 4)]u8 = undefined;
const encoded_len = bun.base64.encode(&base64_buf, input);
const encoded, const bytes = bun.String.createUninitialized(.latin1, encoded_len);
defer encoded.deref();
@memcpy(@constCast(bytes), base64_buf[0..encoded_len]);
return encoded.toJS(globalObject);
},
.base64url => {
var buf: [std.base64.url_safe_no_pad.Encoder.calcSize(max_size * 4)]u8 = undefined;
const encoded = std.base64.url_safe_no_pad.Encoder.encode(&buf, input);
return JSC.ZigString.init(buf[0..encoded.len]).toJS(globalObject);
},
.hex => {
var buf: [max_size * 4]u8 = undefined;
const out = std.fmt.bufPrint(&buf, "{}", .{std.fmt.fmtSliceHexLower(input)}) catch bun.outOfMemory();
const result = JSC.ZigString.init(out).toJS(globalObject);
return result;
},
.buffer => {
return JSC.ArrayBuffer.createBuffer(globalObject, input);
},
inline else => |enc| {
const res = JSC.WebCore.Encoder.toString(input.ptr, input.len, globalObject, enc);
if (res.isError()) {
globalObject.throwValue(res);
return .zero;
}
return res;
},
}
}
};
const PathOrBuffer = union(Tag) {
path: bun.PathString,
buffer: Buffer,
pub const Tag = enum { path, buffer };
pub inline fn slice(this: PathOrBuffer) []const u8 {
return this.path.slice();
}
};
pub fn CallbackTask(comptime Result: type) type {
return struct {
callback: JSC.C.JSObjectRef,
option: Option,
success: bool = false,
pub const Option = union {
err: JSC.SystemError,
result: Result,
};
};
}
pub const PathLike = union(enum) {
string: bun.PathString,
buffer: Buffer,
slice_with_underlying_string: bun.SliceWithUnderlyingString,
threadsafe_string: bun.SliceWithUnderlyingString,
encoded_slice: JSC.ZigString.Slice,
pub fn estimatedSize(this: *const PathLike) usize {
return switch (this.*) {
.string => this.string.estimatedSize(),
.buffer => this.buffer.slice().len,
.threadsafe_string, .slice_with_underlying_string => 0,
.encoded_slice => this.encoded_slice.slice().len,
};
}
pub fn deinit(this: *const PathLike) void {
switch (this.*) {
.string, .buffer => {},
inline else => |*str| {
str.deinit();
},
}
}
pub fn toThreadSafe(this: *PathLike) void {
switch (this.*) {
.slice_with_underlying_string => {
this.slice_with_underlying_string.toThreadSafe();
this.* = .{
.threadsafe_string = this.slice_with_underlying_string,
};
},
.buffer => {
this.buffer.buffer.value.protect();
},
else => {},
}
}
pub fn deinitAndUnprotect(this: *const PathLike) void {
switch (this.*) {
inline .encoded_slice, .threadsafe_string, .slice_with_underlying_string => |*val| {
val.deinit();
},
.buffer => |val| {
val.buffer.value.unprotect();
},
else => {},
}
}
pub inline fn slice(this: PathLike) string {
return switch (this) {
inline else => |*str| str.slice(),
};
}
pub fn sliceZWithForceCopy(this: PathLike, buf: *bun.PathBuffer, comptime force: bool) if (force) [:0]u8 else [:0]const u8 {
const sliced = this.slice();
if (Environment.isWindows) {
if (std.fs.path.isAbsolute(sliced)) {
return path_handler.PosixToWinNormalizer.resolveCWDWithExternalBufZ(buf, sliced) catch @panic("Error while resolving path.");
}
}
if (sliced.len == 0) {
if (comptime !force) return "";
buf[0] = 0;
return buf[0..0 :0];
}
if (comptime !force) {
if (sliced[sliced.len - 1] == 0) {
return sliced[0 .. sliced.len - 1 :0];
}
}
@memcpy(buf[0..sliced.len], sliced);
buf[sliced.len] = 0;
return buf[0..sliced.len :0];
}
pub inline fn sliceZ(this: PathLike, buf: *bun.PathBuffer) [:0]const u8 {
return sliceZWithForceCopy(this, buf, false);
}
pub inline fn sliceW(this: PathLike, buf: *bun.PathBuffer) [:0]const u16 {
return strings.toWPath(@alignCast(std.mem.bytesAsSlice(u16, buf)), this.slice());
}
pub inline fn osPath(this: PathLike, buf: *bun.PathBuffer) bun.OSPathSliceZ {
if (comptime Environment.isWindows) {
return sliceW(this, buf);
}
return sliceZWithForceCopy(this, buf, false);
}
pub fn toJS(this: *const PathLike, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return switch (this.*) {
.string => this.string.toJS(globalObject, null),
.buffer => this.buffer.toJS(globalObject),
inline .threadsafe_string, .slice_with_underlying_string => |*str| str.toJS(globalObject),
.encoded_slice => |encoded| {
if (this.encoded_slice.allocator.get()) |allocator| {
// Is this a globally-allocated slice?
if (allocator.vtable == bun.default_allocator.vtable) {}
}
const str = bun.String.createUTF8(encoded.slice());
defer str.deref();
return str.toJS(globalObject);
},
else => unreachable,
};
}
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, exception: JSC.C.ExceptionRef) ?PathLike {
return fromJSWithAllocator(ctx, arguments, bun.default_allocator, exception);
}
pub fn fromJSWithAllocator(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, allocator: std.mem.Allocator, exception: JSC.C.ExceptionRef) ?PathLike {
const arg = arguments.next() orelse return null;
switch (arg.jsType()) {
JSC.JSValue.JSType.Uint8Array,
JSC.JSValue.JSType.DataView,
=> {
const buffer = Buffer.fromTypedArray(ctx, arg);
if (exception.* != null) return null;
if (!Valid.pathBuffer(buffer, ctx, exception)) return null;
arguments.protectEat();
return PathLike{ .buffer = buffer };
},
JSC.JSValue.JSType.ArrayBuffer => {
const buffer = Buffer.fromArrayBuffer(ctx, arg);
if (exception.* != null) return null;
if (!Valid.pathBuffer(buffer, ctx, exception)) return null;
arguments.protectEat();
return PathLike{ .buffer = buffer };
},
JSC.JSValue.JSType.String,
JSC.JSValue.JSType.StringObject,
JSC.JSValue.JSType.DerivedStringObject,
=> {
var str = arg.toBunString(ctx);
defer str.deref();
arguments.eat();
if (!Valid.pathStringLength(str.length(), ctx, exception)) {
return null;
}
if (arguments.will_be_async) {
var sliced = str.toThreadSafeSlice(allocator);
sliced.reportExtraMemory(ctx.vm());
if (sliced.underlying.isEmpty()) {
return PathLike{ .encoded_slice = sliced.utf8 };
}
return PathLike{ .threadsafe_string = sliced };
} else {
var sliced = str.toSlice(allocator);
// Costs nothing to keep both around.
if (sliced.isWTFAllocated()) {
str.ref();
return PathLike{ .slice_with_underlying_string = sliced };
}
sliced.reportExtraMemory(ctx.vm());
// It is expensive to keep both around.
return PathLike{ .encoded_slice = sliced.utf8 };
}
},
else => {
if (arg.as(JSC.DOMURL)) |domurl| {
var str: bun.String = domurl.fileSystemPath();
defer str.deref();
if (str.isEmpty()) {
JSC.throwInvalidArguments("URL must be a non-empty \"file:\" path", .{}, ctx, exception);
return null;
}
arguments.eat();
if (!Valid.pathStringLength(str.length(), ctx, exception)) {
return null;
}
if (arguments.will_be_async) {
var sliced = str.toThreadSafeSlice(allocator);
sliced.reportExtraMemory(ctx.vm());
if (sliced.underlying.isEmpty()) {
return PathLike{ .encoded_slice = sliced.utf8 };
}
return PathLike{ .threadsafe_string = sliced };
} else {
var sliced = str.toSlice(allocator);
// Costs nothing to keep both around.
if (sliced.isWTFAllocated()) {
str.ref();
return PathLike{ .slice_with_underlying_string = sliced };
}
sliced.reportExtraMemory(ctx.vm());
// It is expensive to keep both around.
return PathLike{ .encoded_slice = sliced.utf8 };
}
}
return null;
},
}
}
};
pub const Valid = struct {
pub fn fileDescriptor(fd: i64, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) bool {
if (fd < 0) {
JSC.throwInvalidArguments("Invalid file descriptor, must not be negative number", .{}, ctx, exception);
return false;
}
return true;
}
pub fn pathSlice(zig_str: JSC.ZigString.Slice, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) bool {
switch (zig_str.len) {
0...bun.MAX_PATH_BYTES => return true,
else => {
// TODO: should this be an EINVAL?
var system_error = bun.sys.Error.fromCode(.NAMETOOLONG, .open).withPath(zig_str.slice()).toSystemError();
system_error.syscall = bun.String.dead;
exception.* = system_error.toErrorInstance(ctx).asObjectRef();
return false;
},
}
unreachable;
}
pub fn pathStringLength(len: usize, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) bool {
switch (len) {
0...bun.MAX_PATH_BYTES => return true,
else => {
// TODO: should this be an EINVAL?
var system_error = bun.sys.Error.fromCode(.NAMETOOLONG, .open).toSystemError();
system_error.syscall = bun.String.dead;
exception.* = system_error.toErrorInstance(ctx).asObjectRef();
return false;
},
}
unreachable;
}
pub fn pathString(zig_str: JSC.ZigString, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) bool {
return pathStringLength(zig_str.len, ctx, exception);
}
pub fn pathBuffer(buffer: Buffer, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) bool {
const slice = buffer.slice();
switch (slice.len) {
0 => {
JSC.throwInvalidArguments("Invalid path buffer: can't be empty", .{}, ctx, exception);
return false;
},
else => {
var system_error = bun.sys.Error.fromCode(.NAMETOOLONG, .open).toSystemError();
system_error.syscall = bun.String.dead;
exception.* = system_error.toErrorInstance(ctx).asObjectRef();
return false;
},
1...bun.MAX_PATH_BYTES => return true,
}
unreachable;
}
};
pub const VectorArrayBuffer = struct {
value: JSC.JSValue,
buffers: std.ArrayList(bun.PlatformIOVec),
pub fn toJS(this: VectorArrayBuffer, _: *JSC.JSGlobalObject) JSC.JSValue {
return this.value;
}
pub fn fromJS(globalObject: *JSC.JSGlobalObject, val: JSC.JSValue, exception: JSC.C.ExceptionRef, allocator: std.mem.Allocator) ?VectorArrayBuffer {
if (!val.jsType().isArrayLike()) {
JSC.throwInvalidArguments("Expected ArrayBufferView[]", .{}, globalObject, exception);
return null;
}
var bufferlist = std.ArrayList(bun.PlatformIOVec).init(allocator);
var i: usize = 0;
const len = val.getLength(globalObject);
bufferlist.ensureTotalCapacityPrecise(len) catch bun.outOfMemory();
while (i < len) {
const element = val.getIndex(globalObject, @as(u32, @truncate(i)));
if (!element.isCell()) {
JSC.throwInvalidArguments("Expected ArrayBufferView[]", .{}, globalObject, exception);
return null;
}
const array_buffer = element.asArrayBuffer(globalObject) orelse {
JSC.throwInvalidArguments("Expected ArrayBufferView[]", .{}, globalObject, exception);
return null;
};
const buf = array_buffer.byteSlice();
bufferlist.append(bun.platformIOVecCreate(buf)) catch bun.outOfMemory();
i += 1;
}
return VectorArrayBuffer{ .value = val, .buffers = bufferlist };
}
};
pub const ArgumentsSlice = struct {
remaining: []const JSC.JSValue,
vm: *JSC.VirtualMachine,
arena: bun.ArenaAllocator = bun.ArenaAllocator.init(bun.default_allocator),
all: []const JSC.JSValue,
threw: bool = false,
protected: std.bit_set.IntegerBitSet(32) = std.bit_set.IntegerBitSet(32).initEmpty(),
will_be_async: bool = false,
pub fn unprotect(this: *ArgumentsSlice) void {
var iter = this.protected.iterator(.{});
const ctx = this.vm.global;
while (iter.next()) |i| {
JSC.C.JSValueUnprotect(ctx, this.all[i].asObjectRef());
}
this.protected = std.bit_set.IntegerBitSet(32).initEmpty();
}
pub fn deinit(this: *ArgumentsSlice) void {
this.unprotect();
this.arena.deinit();
}
pub fn protectEat(this: *ArgumentsSlice) void {
if (this.remaining.len == 0) return;
const index = this.all.len - this.remaining.len;
this.protected.set(index);
JSC.C.JSValueProtect(this.vm.global, this.all[index].asObjectRef());
this.eat();
}
pub fn protectEatNext(this: *ArgumentsSlice) ?JSC.JSValue {
if (this.remaining.len == 0) return null;
return this.nextEat();
}
pub fn from(vm: *JSC.VirtualMachine, arguments: []const JSC.JSValueRef) ArgumentsSlice {
return init(vm, @as([*]const JSC.JSValue, @ptrCast(arguments.ptr))[0..arguments.len]);
}
pub fn init(vm: *JSC.VirtualMachine, arguments: []const JSC.JSValue) ArgumentsSlice {
return ArgumentsSlice{
.remaining = arguments,
.vm = vm,
.all = arguments,
.arena = bun.ArenaAllocator.init(vm.allocator),
};
}
pub fn initAsync(vm: *JSC.VirtualMachine, arguments: []const JSC.JSValue) ArgumentsSlice {
return ArgumentsSlice{
.remaining = bun.default_allocator.dupe(JSC.JSValue, arguments),
.vm = vm,
.all = arguments,
.arena = bun.ArenaAllocator.init(bun.default_allocator),
};
}
pub inline fn len(this: *const ArgumentsSlice) u16 {
return @as(u16, @truncate(this.remaining.len));
}
pub fn eat(this: *ArgumentsSlice) void {
if (this.remaining.len == 0) {
return;
}
this.remaining = this.remaining[1..];
}
pub fn next(this: *ArgumentsSlice) ?JSC.JSValue {
if (this.remaining.len == 0) {
return null;
}
return this.remaining[0];
}
pub fn nextEat(this: *ArgumentsSlice) ?JSC.JSValue {
if (this.remaining.len == 0) {
return null;
}
defer this.eat();
return this.remaining[0];
}
};
pub fn fileDescriptorFromJS(ctx: JSC.C.JSContextRef, value: JSC.JSValue, exception: JSC.C.ExceptionRef) ?bun.FileDescriptor {
return if (bun.FDImpl.fromJSValidated(value, ctx, exception) catch null) |fd|
fd.encode()
else
null;
}
// Node.js docs:
// > Values can be either numbers representing Unix epoch time in seconds, Dates, or a numeric string like '123456789.0'.
// > If the value can not be converted to a number, or is NaN, Infinity, or -Infinity, an Error will be thrown.
pub fn timeLikeFromJS(globalObject: *JSC.JSGlobalObject, value: JSC.JSValue, _: JSC.C.ExceptionRef) ?TimeLike {
if (value.jsType() == .JSDate) {
const milliseconds = value.getUnixTimestamp();
if (!std.math.isFinite(milliseconds)) {
return null;
}
if (comptime Environment.isWindows) {
return milliseconds / 1000.0;
}
return TimeLike{
.tv_sec = @intFromFloat(@divFloor(milliseconds, std.time.ms_per_s)),
.tv_nsec = @intFromFloat(@mod(milliseconds, std.time.ms_per_s) * std.time.ns_per_ms),
};
}
if (!value.isNumber() and !value.isString()) {
return null;
}
const seconds = value.coerce(f64, globalObject);
if (!std.math.isFinite(seconds)) {
return null;
}
if (comptime Environment.isWindows) {
return seconds;
}
return TimeLike{
.tv_sec = @intFromFloat(seconds),
.tv_nsec = @intFromFloat(@mod(seconds, 1.0) * std.time.ns_per_s),
};
}
pub fn modeFromJS(ctx: JSC.C.JSContextRef, value: JSC.JSValue, exception: JSC.C.ExceptionRef) ?Mode {
const mode_int = if (value.isNumber()) brk: {
if (!value.isUInt32AsAnyInt()) {
exception.* = ctx.ERR_OUT_OF_RANGE("The value of \"mode\" is out of range. It must be an integer. Received {d}", .{value.asNumber()}).toJS().asObjectRef();
return null;
}
break :brk @as(Mode, @truncate(value.to(Mode)));
} else brk: {
if (value.isUndefinedOrNull()) return null;
// An easier method of constructing the mode is to use a sequence of
// three octal digits (e.g. 765). The left-most digit (7 in the example),
// specifies the permissions for the file owner. The middle digit (6 in
// the example), specifies permissions for the group. The right-most
// digit (5 in the example), specifies the permissions for others.
var zig_str = JSC.ZigString.Empty;
value.toZigString(&zig_str, ctx.ptr());
var slice = zig_str.slice();
if (strings.hasPrefix(slice, "0o")) {
slice = slice[2..];
}
break :brk std.fmt.parseInt(Mode, slice, 8) catch {
JSC.throwInvalidArguments("Invalid mode string: must be an octal number", .{}, ctx, exception);
return null;
};
};
if (mode_int < 0) {
JSC.throwInvalidArguments("Invalid mode: must be greater than or equal to 0.", .{}, ctx, exception);
return null;
}
return mode_int & 0o777;
}
pub const PathOrFileDescriptor = union(Tag) {
fd: bun.FileDescriptor,
path: PathLike,
pub const Tag = enum { fd, path };
pub const SerializeTag = enum(u8) { fd, path };
/// This will unref() the path string if it is a PathLike.
/// Does nothing for file descriptors, **does not** close file descriptors.
pub fn deinit(this: PathOrFileDescriptor) void {
if (this == .path) {
this.path.deinit();
}
}
pub fn estimatedSize(this: *const PathOrFileDescriptor) usize {
return switch (this.*) {
.path => this.path.estimatedSize(),
.fd => 0,
};
}
pub fn toThreadSafe(this: *PathOrFileDescriptor) void {
if (this.* == .path) {
this.path.toThreadSafe();
}
}
pub fn deinitAndUnprotect(this: PathOrFileDescriptor) void {
if (this == .path) {
this.path.deinitAndUnprotect();
}
}
pub fn hash(this: JSC.Node.PathOrFileDescriptor) u64 {
return switch (this) {
.path => bun.hash(this.path.slice()),
.fd => bun.hash(std.mem.asBytes(&this.fd)),
};
}
pub fn format(this: JSC.Node.PathOrFileDescriptor, comptime fmt: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
if (fmt.len != 0 and fmt[0] != 's') {
@compileError("Unsupported format argument: '" ++ fmt ++ "'.");
}
switch (this) {
.path => |p| try writer.writeAll(p.slice()),
.fd => |fd| try writer.print("{}", .{fd}),
}
}
pub fn fromJS(ctx: JSC.C.JSContextRef, arguments: *ArgumentsSlice, allocator: std.mem.Allocator, exception: JSC.C.ExceptionRef) ?JSC.Node.PathOrFileDescriptor {
const first = arguments.next() orelse return null;
if (bun.FDImpl.fromJSValidated(first, ctx, exception) catch return null) |fd| {
arguments.eat();
return JSC.Node.PathOrFileDescriptor{ .fd = fd.encode() };
}
return JSC.Node.PathOrFileDescriptor{
.path = PathLike.fromJSWithAllocator(ctx, arguments, allocator, exception) orelse return null,
};
}
pub fn toJS(this: JSC.Node.PathOrFileDescriptor, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef {
return switch (this) {
.path => |path| path.toJS(ctx, exception),
.fd => |fd| bun.FDImpl.decode(fd).toJS(),
};
}
};
pub const FileSystemFlags = enum(Mode) {
/// Open file for appending. The file is created if it does not exist.
a = bun.O.APPEND | bun.O.WRONLY | bun.O.CREAT,
/// Like 'a' but fails if the path exists.
// @"ax" = bun.O.APPEND | bun.O.EXCL,
/// Open file for reading and appending. The file is created if it does not exist.
// @"a+" = bun.O.APPEND | bun.O.RDWR,
/// Like 'a+' but fails if the path exists.
// @"ax+" = bun.O.APPEND | bun.O.RDWR | bun.O.EXCL,
/// Open file for appending in synchronous mode. The file is created if it does not exist.
// @"as" = bun.O.APPEND,
/// Open file for reading and appending in synchronous mode. The file is created if it does not exist.
// @"as+" = bun.O.APPEND | bun.O.RDWR,
/// Open file for reading. An exception occurs if the file does not exist.
r = bun.O.RDONLY,
/// Open file for reading and writing. An exception occurs if the file does not exist.
// @"r+" = bun.O.RDWR,
/// Open file for reading and writing in synchronous mode. Instructs the operating system to bypass the local file system cache.
/// This is primarily useful for opening files on NFS mounts as it allows skipping the potentially stale local cache. It has a very real impact on I/O performance so using this flag is not recommended unless it is needed.
/// This doesn't turn fs.open() or fsPromises.open() into a synchronous blocking call. If synchronous operation is desired, something like fs.openSync() should be used.
// @"rs+" = bun.O.RDWR,
/// Open file for writing. The file is created (if it does not exist) or truncated (if it exists).
w = bun.O.WRONLY | bun.O.CREAT,
/// Like 'w' but fails if the path exists.
// @"wx" = bun.O.WRONLY | bun.O.TRUNC,
// /// Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists).
// @"w+" = bun.O.RDWR | bun.O.CREAT,
// /// Like 'w+' but fails if the path exists.
// @"wx+" = bun.O.RDWR | bun.O.EXCL,
_,
const O_RDONLY: Mode = bun.O.RDONLY;
const O_RDWR: Mode = bun.O.RDWR;
const O_APPEND: Mode = bun.O.APPEND;
const O_CREAT: Mode = bun.O.CREAT;
const O_WRONLY: Mode = bun.O.WRONLY;
const O_EXCL: Mode = bun.O.EXCL;
const O_SYNC: Mode = 0;
const O_TRUNC: Mode = bun.O.TRUNC;
const map = bun.ComptimeStringMap(Mode, .{
.{ "r", O_RDONLY },
.{ "rs", O_RDONLY | O_SYNC },
.{ "sr", O_RDONLY | O_SYNC },
.{ "r+", O_RDWR },
.{ "rs+", O_RDWR | O_SYNC },
.{ "sr+", O_RDWR | O_SYNC },
.{ "R", O_RDONLY },
.{ "RS", O_RDONLY | O_SYNC },
.{ "SR", O_RDONLY | O_SYNC },
.{ "R+", O_RDWR },
.{ "RS+", O_RDWR | O_SYNC },
.{ "SR+", O_RDWR | O_SYNC },
.{ "w", O_TRUNC | O_CREAT | O_WRONLY },
.{ "wx", O_TRUNC | O_CREAT | O_WRONLY | O_EXCL },
.{ "xw", O_TRUNC | O_CREAT | O_WRONLY | O_EXCL },
.{ "W", O_TRUNC | O_CREAT | O_WRONLY },
.{ "WX", O_TRUNC | O_CREAT | O_WRONLY | O_EXCL },
.{ "XW", O_TRUNC | O_CREAT | O_WRONLY | O_EXCL },
.{ "w+", O_TRUNC | O_CREAT | O_RDWR },
.{ "wx+", O_TRUNC | O_CREAT | O_RDWR | O_EXCL },
.{ "xw+", O_TRUNC | O_CREAT | O_RDWR | O_EXCL },
.{ "W+", O_TRUNC | O_CREAT | O_RDWR },
.{ "WX+", O_TRUNC | O_CREAT | O_RDWR | O_EXCL },
.{ "XW+", O_TRUNC | O_CREAT | O_RDWR | O_EXCL },
.{ "a", O_APPEND | O_CREAT | O_WRONLY },
.{ "ax", O_APPEND | O_CREAT | O_WRONLY | O_EXCL },
.{ "xa", O_APPEND | O_CREAT | O_WRONLY | O_EXCL },
.{ "as", O_APPEND | O_CREAT | O_WRONLY | O_SYNC },
.{ "sa", O_APPEND | O_CREAT | O_WRONLY | O_SYNC },
.{ "A", O_APPEND | O_CREAT | O_WRONLY },
.{ "AX", O_APPEND | O_CREAT | O_WRONLY | O_EXCL },
.{ "XA", O_APPEND | O_CREAT | O_WRONLY | O_EXCL },
.{ "AS", O_APPEND | O_CREAT | O_WRONLY | O_SYNC },
.{ "SA", O_APPEND | O_CREAT | O_WRONLY | O_SYNC },
.{ "a+", O_APPEND | O_CREAT | O_RDWR },
.{ "ax+", O_APPEND | O_CREAT | O_RDWR | O_EXCL },
.{ "xa+", O_APPEND | O_CREAT | O_RDWR | O_EXCL },
.{ "as+", O_APPEND | O_CREAT | O_RDWR | O_SYNC },
.{ "sa+", O_APPEND | O_CREAT | O_RDWR | O_SYNC },
.{ "A+", O_APPEND | O_CREAT | O_RDWR },
.{ "AX+", O_APPEND | O_CREAT | O_RDWR | O_EXCL },
.{ "XA+", O_APPEND | O_CREAT | O_RDWR | O_EXCL },
.{ "AS+", O_APPEND | O_CREAT | O_RDWR | O_SYNC },
.{ "SA+", O_APPEND | O_CREAT | O_RDWR | O_SYNC },
});
pub fn fromJS(ctx: JSC.C.JSContextRef, val: JSC.JSValue, exception: JSC.C.ExceptionRef) ?FileSystemFlags {
if (val.isNumber()) {
if (!val.isInt32()) {
exception.* = ctx.ERR_OUT_OF_RANGE("The value of \"flags\" is out of range. It must be an integer. Received {d}", .{val.asNumber()}).toJS().asObjectRef();
return null;
}
const number = val.coerce(i32, ctx);
return @as(FileSystemFlags, @enumFromInt(@as(Mode, @intCast(@max(number, 0)))));
}
const jsType = val.jsType();
if (jsType.isStringLike()) {
const str = val.getZigString(ctx);
if (str.isEmpty()) {
JSC.throwInvalidArguments(
"Expected flags to be a non-empty string. Learn more at https://nodejs.org/api/fs.html#fs_file_system_flags",
.{},
ctx,
exception,
);
return null;
}
// it's definitely wrong when the string is super long
else if (str.len > 12) {
JSC.throwInvalidArguments(
"Invalid flag '{any}'. Learn more at https://nodejs.org/api/fs.html#fs_file_system_flags",
.{str},
ctx,
exception,
);
return null;
}
const flags = brk: {
switch (str.is16Bit()) {
inline else => |is_16bit| {
const chars = if (is_16bit) str.utf16SliceAligned() else str.slice();
if (std.ascii.isDigit(@as(u8, @truncate(chars[0])))) {
// node allows "0o644" as a string :(
if (is_16bit) {
const slice = str.toSlice(bun.default_allocator);
defer slice.deinit();
break :brk std.fmt.parseInt(Mode, slice.slice(), 10) catch null;
} else {
break :brk std.fmt.parseInt(Mode, chars, 10) catch null;
}
}
},
}
break :brk map.getWithEql(str, JSC.ZigString.eqlComptime);
} orelse {
JSC.throwInvalidArguments(
"Invalid flag '{any}'. Learn more at https://nodejs.org/api/fs.html#fs_file_system_flags",
.{str},
ctx,
exception,
);
return null;
};
return @as(FileSystemFlags, @enumFromInt(@as(Mode, @intCast(flags))));
}
return null;
}
};
/// Stats and BigIntStats classes from node:fs
pub fn StatType(comptime Big: bool) type {
const Int = if (Big) i64 else i32;
const Float = if (Big) i64 else f64;
const Timestamp = if (Big) u64 else u0;
const Date = packed struct {
value: Float,
pub inline fn toJS(this: @This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
const milliseconds = JSC.JSValue.jsNumber(this.value);
const array: [1]JSC.C.JSValueRef = .{milliseconds.asObjectRef()};
return JSC.JSValue.c(JSC.C.JSObjectMakeDate(globalObject, 1, &array, null));
}
};
return extern struct {
pub usingnamespace if (Big) JSC.Codegen.JSBigIntStats else JSC.Codegen.JSStats;
pub usingnamespace bun.New(@This());
// Stats stores these as i32, but BigIntStats stores all of these as i64
// On windows, these two need to be u64 as the numbers are often very large.
dev: u64,
ino: u64,
mode: Int,
nlink: Int,
uid: Int,
gid: Int,
rdev: Int,
blksize: Int,
blocks: Int,
// Always store size as a 64-bit integer
size: i64,
// _ms is either a float if Small, or a 64-bit integer if Big
atime_ms: Float,
mtime_ms: Float,
ctime_ms: Float,
birthtime_ms: Float,
// _ns is a u64 storing nanosecond precision. it is a u0 when not BigIntStats
atime_ns: Timestamp = 0,
mtime_ns: Timestamp = 0,
ctime_ns: Timestamp = 0,
birthtime_ns: Timestamp = 0,
const This = @This();
const StatTimespec = if (Environment.isWindows) bun.windows.libuv.uv_timespec_t else std.posix.timespec;
inline fn toNanoseconds(ts: StatTimespec) Timestamp {
const tv_sec: i64 = @intCast(ts.tv_sec);
const tv_nsec: i64 = @intCast(ts.tv_nsec);
return @as(Timestamp, @intCast(tv_sec * 1_000_000_000)) + @as(Timestamp, @intCast(tv_nsec));
}
fn toTimeMS(ts: StatTimespec) Float {
if (Big) {
const tv_sec: i64 = @intCast(ts.tv_sec);
const tv_nsec: i64 = @intCast(ts.tv_nsec);
return @as(i64, @intCast(tv_sec * std.time.ms_per_s)) + @as(i64, @intCast(@divTrunc(tv_nsec, std.time.ns_per_ms)));
} else {
return (@as(f64, @floatFromInt(@max(ts.tv_sec, 0))) * std.time.ms_per_s) + (@as(f64, @floatFromInt(@as(usize, @intCast(@max(ts.tv_nsec, 0))))) / std.time.ns_per_ms);
}
}
const PropertyGetter = fn (this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue;
fn getter(comptime field: meta.FieldEnum(This)) PropertyGetter {
return struct {
pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
const value = @field(this, @tagName(field));
const Type = @TypeOf(value);
if (comptime Big and @typeInfo(Type) == .Int) {
if (Type == u64) {
return JSC.JSValue.fromUInt64NoTruncate(globalObject, value);
}
return JSC.JSValue.fromInt64NoTruncate(globalObject, value);
}
return JSC.JSValue.jsNumber(value);
}
}.callback;
}
fn dateGetter(comptime field: meta.FieldEnum(This)) PropertyGetter {
return struct {
pub fn callback(this: *This, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
const value = @field(this, @tagName(field));
// Doing `Date{ ... }` here shouldn't actually change the memory layout of `value`
// but it will tell comptime code how to convert the i64/f64 to a JS Date.
return globalObject.toJS(Date{ .value = value }, .temporary);
}
}.callback;
}
pub const isBlockDevice_ = JSC.wrapInstanceMethod(This, "isBlockDevice", false);
pub const isCharacterDevice_ = JSC.wrapInstanceMethod(This, "isCharacterDevice", false);
pub const isDirectory_ = JSC.wrapInstanceMethod(This, "isDirectory", false);
pub const isFIFO_ = JSC.wrapInstanceMethod(This, "isFIFO", false);
pub const isFile_ = JSC.wrapInstanceMethod(This, "isFile", false);
pub const isSocket_ = JSC.wrapInstanceMethod(This, "isSocket", false);
pub const isSymbolicLink_ = JSC.wrapInstanceMethod(This, "isSymbolicLink", false);
pub const isBlockDevice_WithoutTypeChecks = domCall(.isBlockDevice);
pub const isCharacterDevice_WithoutTypeChecks = domCall(.isCharacterDevice);
pub const isDirectory_WithoutTypeChecks = domCall(.isDirectory);
pub const isFIFO_WithoutTypeChecks = domCall(.isFIFO);
pub const isFile_WithoutTypeChecks = domCall(.isFile);
pub const isSocket_WithoutTypeChecks = domCall(.isSocket);
pub const isSymbolicLink_WithoutTypeChecks = domCall(.isSymbolicLink);
const DOMCallFn = fn (
*This,
*JSC.JSGlobalObject,
) callconv(JSC.conv) JSC.JSValue;
fn domCall(comptime decl: meta.DeclEnum(This)) DOMCallFn {
return struct {
pub fn run(
this: *This,
_: *JSC.JSGlobalObject,
) callconv(JSC.conv) JSC.JSValue {
return @field(This, @tagName(decl))(this);
}
}.run;
}
pub const dev = getter(.dev);
pub const ino = getter(.ino);
pub const mode = getter(.mode);
pub const nlink = getter(.nlink);
pub const uid = getter(.uid);
pub const gid = getter(.gid);
pub const rdev = getter(.rdev);
pub const size = getter(.size);
pub const blksize = getter(.blksize);
pub const blocks = getter(.blocks);
pub const atime = dateGetter(.atime_ms);
pub const mtime = dateGetter(.mtime_ms);
pub const ctime = dateGetter(.ctime_ms);
pub const birthtime = dateGetter(.birthtime_ms);
pub const atimeMs = getter(.atime_ms);
pub const mtimeMs = getter(.mtime_ms);
pub const ctimeMs = getter(.ctime_ms);
pub const birthtimeMs = getter(.birthtime_ms);
pub const atimeNs = getter(.atime_ns);
pub const mtimeNs = getter(.mtime_ns);
pub const ctimeNs = getter(.ctime_ns);
pub const birthtimeNs = getter(.birthtime_ns);
inline fn modeInternal(this: *This) i32 {
return @truncate(this.mode);
}
const S = if (Environment.isWindows) bun.C.S else posix.system.S;
pub fn isBlockDevice(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(S.ISBLK(@intCast(this.modeInternal())));
}
pub fn isCharacterDevice(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(S.ISCHR(@intCast(this.modeInternal())));
}
pub fn isDirectory(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(S.ISDIR(@intCast(this.modeInternal())));
}
pub fn isFIFO(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(S.ISFIFO(@intCast(this.modeInternal())));
}
pub fn isFile(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(bun.isRegularFile(this.modeInternal()));
}
pub fn isSocket(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(S.ISSOCK(@intCast(this.modeInternal())));
}
/// Node.js says this method is only valid on the result of lstat()
/// so it's fine if we just include it on stat() because it would
/// still just return false.
///
/// See https://nodejs.org/api/fs.html#statsissymboliclink
pub fn isSymbolicLink(this: *This) JSC.JSValue {
return JSC.JSValue.jsBoolean(S.ISLNK(@intCast(this.modeInternal())));
}
// TODO: BigIntStats includes a `_checkModeProperty` but I dont think anyone actually uses it.
pub fn finalize(this: *This) callconv(.C) void {
this.destroy();
}
pub fn init(stat_: bun.Stat) This {
const aTime = stat_.atime();
const mTime = stat_.mtime();
const cTime = stat_.ctime();
return .{
.dev = @intCast(@max(stat_.dev, 0)),
.ino = @intCast(@max(stat_.ino, 0)),
.mode = @truncate(@as(i64, @intCast(stat_.mode))),
.nlink = @truncate(@as(i64, @intCast(stat_.nlink))),
.uid = @truncate(@as(i64, @intCast(stat_.uid))),
.gid = @truncate(@as(i64, @intCast(stat_.gid))),
.rdev = @truncate(@as(i64, @intCast(stat_.rdev))),
.size = @truncate(@as(i64, @intCast(stat_.size))),
.blksize = @truncate(@as(i64, @intCast(stat_.blksize))),
.blocks = @truncate(@as(i64, @intCast(stat_.blocks))),
.atime_ms = toTimeMS(aTime),
.mtime_ms = toTimeMS(mTime),
.ctime_ms = toTimeMS(cTime),
.atime_ns = if (Big) toNanoseconds(aTime) else 0,
.mtime_ns = if (Big) toNanoseconds(mTime) else 0,
.ctime_ns = if (Big) toNanoseconds(cTime) else 0,
// Linux doesn't include this info in stat
// maybe it does in statx, but do you really need birthtime? If you do please file an issue.
.birthtime_ms = if (Environment.isLinux) 0 else toTimeMS(stat_.birthtime()),
.birthtime_ns = if (Big and !Environment.isLinux) toNanoseconds(stat_.birthtime()) else 0,
};
}
pub fn constructor(globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(JSC.conv) ?*This {
if (Big) {
globalObject.throwInvalidArguments("BigIntStats is not a constructor", .{});
return null;
}
// dev, mode, nlink, uid, gid, rdev, blksize, ino, size, blocks, atimeMs, mtimeMs, ctimeMs, birthtimeMs
var args = callFrame.argumentsPtr()[0..@min(callFrame.argumentsCount(), 14)];
const atime_ms: f64 = if (args.len > 10 and args[10].isNumber()) args[10].asNumber() else 0;
const mtime_ms: f64 = if (args.len > 11 and args[11].isNumber()) args[11].asNumber() else 0;
const ctime_ms: f64 = if (args.len > 12 and args[12].isNumber()) args[12].asNumber() else 0;
const birthtime_ms: f64 = if (args.len > 13 and args[13].isNumber()) args[13].asNumber() else 0;
const this = This.new(.{
.dev = if (args.len > 0 and args[0].isNumber()) @intCast(args[0].toInt32()) else 0,
.mode = if (args.len > 1 and args[1].isNumber()) args[1].toInt32() else 0,
.nlink = if (args.len > 2 and args[2].isNumber()) args[2].toInt32() else 0,
.uid = if (args.len > 3 and args[3].isNumber()) args[3].toInt32() else 0,
.gid = if (args.len > 4 and args[4].isNumber()) args[4].toInt32() else 0,
.rdev = if (args.len > 5 and args[5].isNumber()) args[5].toInt32() else 0,
.blksize = if (args.len > 6 and args[6].isNumber()) args[6].toInt32() else 0,
.ino = if (args.len > 7 and args[7].isNumber()) @intCast(args[7].toInt32()) else 0,
.size = if (args.len > 8 and args[8].isNumber()) args[8].toInt32() else 0,
.blocks = if (args.len > 9 and args[9].isNumber()) args[9].toInt32() else 0,
.atime_ms = atime_ms,
.mtime_ms = mtime_ms,
.ctime_ms = ctime_ms,
.birthtime_ms = birthtime_ms,
});
return this;
}
comptime {
_ = isBlockDevice_WithoutTypeChecks;
_ = isCharacterDevice_WithoutTypeChecks;
_ = isDirectory_WithoutTypeChecks;
_ = isFIFO_WithoutTypeChecks;
_ = isFile_WithoutTypeChecks;
_ = isSocket_WithoutTypeChecks;
_ = isSymbolicLink_WithoutTypeChecks;
}
};
}
pub const StatsSmall = StatType(false);
pub const StatsBig = StatType(true);
/// Union between `Stats` and `BigIntStats` where the type can be decided at runtime
pub const Stats = union(enum) {
big: StatsBig,
small: StatsSmall,
pub inline fn init(stat_: bun.Stat, big: bool) Stats {
if (big) {
return .{ .big = StatsBig.init(stat_) };
} else {
return .{ .small = StatsSmall.init(stat_) };
}
}
pub fn toJSNewlyCreated(this: *const Stats, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return switch (this.*) {
.big => StatsBig.new(this.big).toJS(globalObject),
.small => StatsSmall.new(this.small).toJS(globalObject),
};
}
pub inline fn toJS(this: *Stats, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = this;
_ = globalObject;
@compileError("Only use Stats.toJSNewlyCreated() or Stats.toJS() directly on a StatsBig or StatsSmall");
}
};
/// A class representing a directory stream.
///
/// Created by {@link opendir}, {@link opendirSync}, or `fsPromises.opendir()`.
///
/// ```js
/// import { opendir } from 'fs/promises';
///
/// try {
/// const dir = await opendir('./');
/// for await (const dirent of dir)
/// console.log(dirent.name);
/// } catch (err) {
/// console.error(err);
/// }
/// ```
///
/// When using the async iterator, the `fs.Dir` object will be automatically
/// closed after the iterator exits.
/// @since v12.12.0
pub const Dirent = struct {
name: bun.String,
path: bun.String,
// not publicly exposed
kind: Kind,
pub const Kind = std.fs.File.Kind;
pub usingnamespace JSC.Codegen.JSDirent;
pub usingnamespace bun.New(@This());
pub fn constructor(globalObject: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) ?*Dirent {
globalObject.throw("Dirent is not a constructor", .{});
return null;
}
pub fn toJS(this: *Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
const as_js = Dirent.toJSUnchecked(globalObject, this);
// Immediately create JSString* objects for the name and path
// So that the GC is aware of them and can collect them if necessary
Dirent.nameSetCached(as_js, globalObject, this.name.toJS(globalObject));
Dirent.pathSetCached(as_js, globalObject, this.path.toJS(globalObject));
return as_js;
}
pub fn toJSNewlyCreated(this: *const Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return toJS(Dirent.new(this.*), globalObject);
}
pub fn getName(this: *Dirent, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
return this.name.toJS(globalObject);
}
pub fn getPath(this: *Dirent, globalThis: *JSC.JSGlobalObject) JSC.JSValue {
return this.path.toJS(globalThis);
}
pub fn isBlockDevice(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.block_device);
}
pub fn isCharacterDevice(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.character_device);
}
pub fn isDirectory(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.directory);
}
pub fn isFIFO(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.named_pipe or this.kind == std.fs.File.Kind.event_port);
}
pub fn isFile(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.file);
}
pub fn isSocket(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.unix_domain_socket);
}
pub fn isSymbolicLink(
this: *Dirent,
_: *JSC.JSGlobalObject,
_: *JSC.CallFrame,
) JSC.JSValue {
return JSC.JSValue.jsBoolean(this.kind == std.fs.File.Kind.sym_link);
}
pub fn deref(this: *const Dirent) void {
this.name.deref();
this.path.deref();
}
pub fn finalize(this: *Dirent) void {
this.deref();
this.destroy();
}
};
pub const Emitter = struct {
pub const Listener = struct {
once: bool = false,
callback: JSC.JSValue,
pub const List = struct {
pub const ArrayList = std.MultiArrayList(Listener);
list: ArrayList = ArrayList{},
once_count: u32 = 0,
pub fn append(this: *List, allocator: std.mem.Allocator, ctx: JSC.C.JSContextRef, listener: Listener) !void {
JSC.C.JSValueProtect(ctx, listener.callback.asObjectRef());
try this.list.append(allocator, listener);
this.once_count +|= @as(u32, @intFromBool(listener.once));
}
pub fn prepend(this: *List, allocator: std.mem.Allocator, ctx: JSC.C.JSContextRef, listener: Listener) !void {
JSC.C.JSValueProtect(ctx, listener.callback.asObjectRef());
try this.list.ensureUnusedCapacity(allocator, 1);
this.list.insertAssumeCapacity(0, listener);
this.once_count +|= @as(u32, @intFromBool(listener.once));
}
// removeListener() will remove, at most, one instance of a listener from the
// listener array. If any single listener has been added multiple times to the
// listener array for the specified eventName, then removeListener() must be
// called multiple times to remove each instance.
pub fn remove(this: *List, ctx: JSC.C.JSContextRef, callback: JSC.JSValue) bool {
const callbacks = this.list.items(.callback);
for (callbacks, 0..) |item, i| {
if (callback.eqlValue(item)) {
JSC.C.JSValueUnprotect(ctx, callback.asObjectRef());
this.once_count -|= @as(u32, @intFromBool(this.list.items(.once)[i]));
this.list.orderedRemove(i);
return true;
}
}
return false;
}
pub fn emit(this: *List, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
var i: usize = 0;
outer: while (true) {
var slice = this.list.slice();
var callbacks = slice.items(.callback);
var once = slice.items(.once);
while (i < callbacks.len) : (i += 1) {
const callback = callbacks[i];
globalObject.enqueueMicrotask1(
callback,
value,
);
if (once[i]) {
this.once_count -= 1;
JSC.C.JSValueUnprotect(globalObject, callback.asObjectRef());
this.list.orderedRemove(i);
slice = this.list.slice();
callbacks = slice.items(.callback);
once = slice.items(.once);
continue :outer;
}
}
return;
}
}
};
};
pub fn New(comptime EventType: type) type {
return struct {
const EventEmitter = @This();
pub const Map = std.enums.EnumArray(EventType, Listener.List);
listeners: Map = Map.initFill(Listener.List{}),
pub fn addListener(this: *EventEmitter, ctx: JSC.C.JSContextRef, event: EventType, listener: Emitter.Listener) !void {
try this.listeners.getPtr(event).append(bun.default_allocator, ctx, listener);
}
pub fn prependListener(this: *EventEmitter, ctx: JSC.C.JSContextRef, event: EventType, listener: Emitter.Listener) !void {
try this.listeners.getPtr(event).prepend(bun.default_allocator, ctx, listener);
}
pub fn emit(this: *EventEmitter, event: EventType, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void {
this.listeners.getPtr(event).emit(globalObject, value);
}
pub fn removeListener(this: *EventEmitter, ctx: JSC.C.JSContextRef, event: EventType, callback: JSC.JSValue) bool {
return this.listeners.getPtr(event).remove(ctx, callback);
}
};
}
};
pub const Process = struct {
pub fn getArgv0(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
return JSC.ZigString.fromUTF8(bun.argv[0]).toJS(globalObject);
}
pub fn getExecPath(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
const out = bun.selfExePath() catch {
// if for any reason we are unable to get the executable path, we just return argv[0]
return getArgv0(globalObject);
};
return JSC.ZigString.fromUTF8(out).toJS(globalObject);
}
pub fn getExecArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
var sfb = std.heap.stackFallback(4096, globalObject.allocator());
const temp_alloc = sfb.get();
const vm = globalObject.bunVM();
if (vm.worker) |worker| {
// was explicitly overridden for the worker?
if (worker.execArgv) |execArgv| {
const array = JSC.JSValue.createEmptyArray(globalObject, execArgv.len);
for (0..execArgv.len) |i| {
array.putIndex(globalObject, @intCast(i), bun.String.init(execArgv[i]).toJS(globalObject));
}
return array;
}
}
var args = std.ArrayList(bun.String).initCapacity(temp_alloc, bun.argv.len - 1) catch bun.outOfMemory();
defer args.deinit();
var seen_run = false;
var prev: ?[]const u8 = null;
// we re-parse the process argv to extract execArgv, since this is a very uncommon operation
// it isn't worth doing this as a part of the CLI
for (bun.argv[@min(1, bun.argv.len)..]) |arg| {
defer prev = arg;
if (arg.len >= 1 and arg[0] == '-') {
args.append(bun.String.createUTF8(arg)) catch bun.outOfMemory();
continue;
}
if (!seen_run and bun.strings.eqlComptime(arg, "run")) {
seen_run = true;
continue;
}
// A set of execArgv args consume an extra argument, so we do not want to
// confuse these with script names.
const map = bun.ComptimeStringMap(void, comptime brk: {
const auto_params = bun.CLI.Arguments.auto_params;
const KV = struct { []const u8, void };
var entries: [auto_params.len]KV = undefined;
var i = 0;
for (auto_params) |param| {
if (param.takes_value != .none) {
if (param.names.long) |name| {
entries[i] = .{ "--" ++ name, {} };
i += 1;
}
if (param.names.short) |name| {
entries[i] = .{ &[_]u8{ '-', name }, {} };
i += 1;
}
}
}
var result: [i]KV = undefined;
@memcpy(&result, entries[0..i]);
break :brk result;
});
if (prev) |p| if (map.has(p)) {
args.append(bun.String.createUTF8(arg)) catch @panic("OOM");
continue;
};
// we hit the script name
break;
}
return bun.String.toJSArray(globalObject, args.items);
}
pub fn getArgv(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
const vm = globalObject.bunVM();
// Allocate up to 32 strings in stack
var stack_fallback_allocator = std.heap.stackFallback(
32 * @sizeOf(JSC.ZigString) + (bun.MAX_PATH_BYTES + 1) + 32,
heap_allocator,
);
const allocator = stack_fallback_allocator.get();
var args_count: usize = vm.argv.len;
if (vm.worker) |worker| {
args_count = if (worker.argv) |argv| argv.len else 0;
}
const args = allocator.alloc(
bun.String,
// argv omits "bun" because it could be "bun run" or "bun" and it's kind of ambiguous
// argv also omits the script name
args_count + 2,
) catch bun.outOfMemory();
var args_list = std.ArrayListUnmanaged(bun.String){ .items = args, .capacity = args.len };
args_list.items.len = 0;
if (vm.standalone_module_graph != null) {
// Don't break user's code because they did process.argv.slice(2)
// Even if they didn't type "bun", we still want to add it as argv[0]
args_list.appendAssumeCapacity(
bun.String.static("bun"),
);
} else {
const exe_path = bun.selfExePath() catch null;
args_list.appendAssumeCapacity(
if (exe_path) |str| bun.String.fromUTF8(str) else bun.String.static("bun"),
);
}
if (vm.main.len > 0)
args_list.appendAssumeCapacity(bun.String.fromUTF8(vm.main));
defer allocator.free(args);
if (vm.worker) |worker| {
if (worker.argv) |argv| {
for (argv) |arg| {
args_list.appendAssumeCapacity(bun.String.init(arg));
}
}
} else {
for (vm.argv) |arg| {
const str = bun.String.fromUTF8(arg);
// https://github.com/yargs/yargs/blob/adb0d11e02c613af3d9427b3028cc192703a3869/lib/utils/process-argv.ts#L1
args_list.appendAssumeCapacity(str);
}
}
return bun.String.toJSArray(globalObject, args_list.items);
}
pub fn getCwd(globalObject: *JSC.JSGlobalObject) callconv(.C) JSC.JSValue {
var buf: bun.PathBuffer = undefined;
return switch (Path.getCwd(&buf)) {
.result => |r| JSC.ZigString.init(r).withEncoding().toJS(globalObject),
.err => |e| e.toJSC(globalObject),
};
}
pub fn setCwd(globalObject: *JSC.JSGlobalObject, to: *JSC.ZigString) callconv(.C) JSC.JSValue {
if (to.len == 0) {
return JSC.toInvalidArguments("path is required", .{}, globalObject.ref());
}
var buf: bun.PathBuffer = undefined;
const slice = to.sliceZBuf(&buf) catch {
return JSC.toInvalidArguments("Invalid path", .{}, globalObject.ref());
};
switch (Syscall.chdir(slice)) {
.result => {
// When we update the cwd from JS, we have to update the bundler's version as well
// However, this might be called many times in a row, so we use a pre-allocated buffer
// that way we don't have to worry about garbage collector
const fs = JSC.VirtualMachine.get().bundler.fs;
fs.top_level_dir = switch (Path.getCwd(&fs.top_level_dir_buf)) {
.result => |r| r,
.err => {
_ = Syscall.chdir(@as([:0]const u8, @ptrCast(fs.top_level_dir)));
return JSC.toInvalidArguments("Invalid path", .{}, globalObject.ref());
},
};
const len = fs.top_level_dir.len;
fs.top_level_dir_buf[len] = std.fs.path.sep;
fs.top_level_dir_buf[len + 1] = 0;
fs.top_level_dir = fs.top_level_dir_buf[0 .. len + 1];
return .undefined;
},
.err => |e| return e.toJSC(globalObject),
}
}
pub fn exit(globalObject: *JSC.JSGlobalObject, code: u8) callconv(.C) void {
var vm = globalObject.bunVM();
if (vm.worker) |worker| {
vm.exit_handler.exit_code = code;
worker.requestTerminate();
return;
}
vm.exit_handler.exit_code = code;
vm.onExit();
vm.globalExit();
}
pub export const Bun__version: [*:0]const u8 = "v" ++ bun.Global.package_json_version;
pub export const Bun__versions_boringssl: [*:0]const u8 = bun.Global.versions.boringssl;
pub export const Bun__versions_libarchive: [*:0]const u8 = bun.Global.versions.libarchive;
pub export const Bun__versions_mimalloc: [*:0]const u8 = bun.Global.versions.mimalloc;
pub export const Bun__versions_picohttpparser: [*:0]const u8 = bun.Global.versions.picohttpparser;
pub export const Bun__versions_uws: [*:0]const u8 = bun.Environment.git_sha;
pub export const Bun__versions_webkit: [*:0]const u8 = bun.Global.versions.webkit;
pub export const Bun__versions_zig: [*:0]const u8 = bun.Global.versions.zig;
pub export const Bun__versions_zlib: [*:0]const u8 = bun.Global.versions.zlib;
pub export const Bun__versions_tinycc: [*:0]const u8 = bun.Global.versions.tinycc;
pub export const Bun__versions_lolhtml: [*:0]const u8 = bun.Global.versions.lolhtml;
pub export const Bun__versions_c_ares: [*:0]const u8 = bun.Global.versions.c_ares;
pub export const Bun__versions_libdeflate: [*:0]const u8 = bun.Global.versions.libdeflate;
pub export const Bun__versions_usockets: [*:0]const u8 = bun.Environment.git_sha;
pub export const Bun__version_sha: [*:0]const u8 = bun.Environment.git_sha;
pub export const Bun__versions_lshpack: [*:0]const u8 = bun.Global.versions.lshpack;
pub export const Bun__versions_zstd: [*:0]const u8 = bun.Global.versions.zstd;
};
comptime {
std.testing.refAllDecls(Process);
}