aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/javascript.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/bun.js/javascript.zig')
-rw-r--r--src/bun.js/javascript.zig189
1 files changed, 159 insertions, 30 deletions
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig
index e2e44e63a..1c996039d 100644
--- a/src/bun.js/javascript.zig
+++ b/src/bun.js/javascript.zig
@@ -294,13 +294,8 @@ pub export fn Bun__Process__send(
return .zero;
}
var vm = globalObject.bunVM();
- if (vm.ipc) |ipc| {
- const fd = ipc.socket.fd();
- const success = IPC.serializeJSValueForSubprocess(
- globalObject,
- callFrame.argument(0),
- fd,
- );
+ if (vm.ipc) |ipc_instance| {
+ const success = ipc_instance.ipc.serializeAndSend(globalObject, callFrame.argument(0));
return if (success) .undefined else .zero;
} else {
globalObject.throw("IPC Socket is no longer open.", .{});
@@ -308,10 +303,15 @@ pub export fn Bun__Process__send(
}
}
+pub export fn Bun__isBunMain(globalObject: *JSGlobalObject, input_ptr: [*]const u8, input_len: usize) bool {
+ return strings.eql(globalObject.bunVM().main, input_ptr[0..input_len]);
+}
+
pub export fn Bun__Process__disconnect(
globalObject: *JSGlobalObject,
callFrame: *JSC.CallFrame,
) JSValue {
+ JSC.markBinding(@src());
_ = callFrame;
_ = globalObject;
return .undefined;
@@ -320,14 +320,20 @@ pub export fn Bun__Process__disconnect(
/// This function is called on the main thread
/// The bunVM() call will assert this
pub export fn Bun__queueTask(global: *JSGlobalObject, task: *JSC.CppTask) void {
+ JSC.markBinding(@src());
+
global.bunVM().eventLoop().enqueueTask(Task.init(task));
}
pub export fn Bun__queueTaskWithTimeout(global: *JSGlobalObject, task: *JSC.CppTask, milliseconds: i32) void {
+ JSC.markBinding(@src());
+
global.bunVM().eventLoop().enqueueTaskWithTimeout(Task.init(task), milliseconds);
}
pub export fn Bun__reportUnhandledError(globalObject: *JSGlobalObject, value: JSValue) callconv(.C) JSValue {
+ JSC.markBinding(@src());
+
var jsc_vm = globalObject.bunVM();
jsc_vm.onUnhandledError(globalObject, value);
return JSC.JSValue.jsUndefined();
@@ -337,6 +343,8 @@ pub export fn Bun__reportUnhandledError(globalObject: *JSGlobalObject, value: JS
/// The main difference: we need to allocate the task & wakeup the thread
/// We can avoid that if we run it from the main thread.
pub export fn Bun__queueTaskConcurrently(global: *JSGlobalObject, task: *JSC.CppTask) void {
+ JSC.markBinding(@src());
+
var concurrent = bun.default_allocator.create(JSC.ConcurrentTask) catch unreachable;
concurrent.* = JSC.ConcurrentTask{
.task = Task.init(task),
@@ -346,6 +354,8 @@ pub export fn Bun__queueTaskConcurrently(global: *JSGlobalObject, task: *JSC.Cpp
}
pub export fn Bun__handleRejectedPromise(global: *JSGlobalObject, promise: *JSC.JSPromise) void {
+ JSC.markBinding(@src());
+
const result = promise.result(global.vm());
var jsc_vm = global.bunVM();
@@ -369,6 +379,14 @@ pub export fn Bun__onDidAppendPlugin(jsc_vm: *VirtualMachine, globalObject: *JSG
jsc_vm.bundler.linker.plugin_runner = &jsc_vm.plugin_runner.?;
}
+// pub fn getGlobalExitCodeForPipeFailure() u8 {
+// if (VirtualMachine.is_main_thread_vm) {
+// return VirtualMachine.get().exit_handler.exit_code;
+// }
+
+// return 0;
+// }
+
pub const ExitHandler = struct {
exit_code: u8 = 0,
@@ -401,6 +419,50 @@ pub const ExitHandler = struct {
pub const WebWorker = @import("./web_worker.zig").WebWorker;
+pub const ImportWatcher = union(enum) {
+ none: void,
+ hot: *HotReloader.Watcher,
+ watch: *WatchReloader.Watcher,
+
+ pub fn start(this: ImportWatcher) !void {
+ switch (this) {
+ inline .hot => |watcher| try watcher.start(),
+ inline .watch => |watcher| try watcher.start(),
+ else => {},
+ }
+ }
+
+ pub inline fn watchlist(this: ImportWatcher) Watcher.WatchListArray {
+ return switch (this) {
+ inline .hot, .watch => |wacher| wacher.watchlist,
+ else => .{},
+ };
+ }
+
+ pub inline fn indexOf(this: ImportWatcher, hash: Watcher.HashType) ?u32 {
+ return switch (this) {
+ inline .hot, .watch => |wacher| wacher.indexOf(hash),
+ else => null,
+ };
+ }
+
+ pub inline fn addFile(
+ this: ImportWatcher,
+ fd: StoredFileDescriptorType,
+ file_path: string,
+ hash: Watcher.HashType,
+ loader: options.Loader,
+ dir_fd: StoredFileDescriptorType,
+ package_json: ?*PackageJSON,
+ comptime copy_file_path: bool,
+ ) !void {
+ switch (this) {
+ inline .hot, .watch => |wacher| try wacher.addFile(fd, file_path, hash, loader, dir_fd, package_json, copy_file_path),
+ else => {},
+ }
+ }
+};
+
/// TODO: rename this to ScriptExecutionContext
/// This is the shared global state for a single JS instance execution
/// Today, Bun is one VM per thread, so the name "VirtualMachine" sort of makes sense
@@ -410,8 +472,7 @@ pub const VirtualMachine = struct {
allocator: std.mem.Allocator,
has_loaded_constructors: bool = false,
bundler: Bundler,
- bun_dev_watcher: ?*http.Watcher = null,
- bun_watcher: ?*JSC.Watcher = null,
+ bun_watcher: ImportWatcher = .{ .none = {} },
console: *ZigConsoleClient,
log: *logger.Log,
main: string = "",
@@ -980,6 +1041,8 @@ pub const VirtualMachine = struct {
pub const MacroMap = std.AutoArrayHashMap(i32, js.JSObjectRef);
pub fn enableMacroMode(this: *VirtualMachine) void {
+ JSC.markBinding(@src());
+
if (!this.has_enabled_macro_mode) {
this.has_enabled_macro_mode = true;
this.macro_event_loop.tasks = EventLoop.Queue.init(default_allocator);
@@ -1006,7 +1069,7 @@ pub const VirtualMachine = struct {
}
pub fn isWatcherEnabled(this: *VirtualMachine) bool {
- return this.bun_dev_watcher != null or this.bun_watcher != null;
+ return this.bun_watcher != .none;
}
/// Instead of storing timestamp as a i128, we store it as a u64.
@@ -1033,6 +1096,7 @@ pub const VirtualMachine = struct {
pub fn initWithModuleGraph(
opts: Options,
) !*VirtualMachine {
+ JSC.markBinding(@src());
const allocator = opts.allocator;
VMHolder.vm = try allocator.create(VirtualMachine);
var console = try allocator.create(ZigConsoleClient);
@@ -1129,6 +1193,7 @@ pub const VirtualMachine = struct {
};
pub fn init(opts: Options) !*VirtualMachine {
+ JSC.markBinding(@src());
const allocator = opts.allocator;
var log: *logger.Log = undefined;
if (opts.log) |__log| {
@@ -1258,6 +1323,7 @@ pub const VirtualMachine = struct {
worker: *WebWorker,
opts: Options,
) anyerror!*VirtualMachine {
+ JSC.markBinding(@src());
var log: *logger.Log = undefined;
const allocator = opts.allocator;
if (opts.log) |__log| {
@@ -1372,6 +1438,7 @@ pub const VirtualMachine = struct {
pub fn refCountedStringWithWasNew(this: *VirtualMachine, new: *bool, input_: []const u8, hash_: ?u32, comptime dupe: bool) *JSC.RefString {
JSC.markBinding(@src());
+ std.debug.assert(input_.len > 0);
const hash = hash_ orelse JSC.RefString.computeHash(input_);
this.ref_strings_mutex.lock();
defer this.ref_strings_mutex.unlock();
@@ -1400,6 +1467,7 @@ pub const VirtualMachine = struct {
}
pub fn refCountedString(this: *VirtualMachine, input_: []const u8, hash_: ?u32, comptime dupe: bool) *JSC.RefString {
+ std.debug.assert(input_.len > 0);
var _was_new = false;
return this.refCountedStringWithWasNew(&_was_new, input_, hash_, comptime dupe);
}
@@ -1476,7 +1544,6 @@ pub const VirtualMachine = struct {
fn normalizeSpecifierForResolution(specifier_: []const u8, query_string: *[]const u8) []const u8 {
var specifier = specifier_;
- if (strings.hasPrefixComptime(specifier, "file://")) specifier = specifier["file://".len..];
if (strings.indexOfChar(specifier, '?')) |i| {
query_string.* = specifier[i..];
@@ -1673,7 +1740,7 @@ pub const VirtualMachine = struct {
printed,
),
};
- res.* = ErrorableString.err(error.NameTooLong, ResolveMessage.create(global, VirtualMachine.get().allocator, msg, source.utf8()).asVoid());
+ res.* = ErrorableString.err(error.NameTooLong, ResolveMessage.create(global, VirtualMachine.get().allocator, msg, source_utf8.slice()).asVoid());
return;
}
@@ -1987,7 +2054,7 @@ pub const VirtualMachine = struct {
try this.entry_point.generate(
this.allocator,
- this.bun_watcher != null,
+ this.bun_watcher != .none,
Fs.PathName.init(entry_path),
main_file_name,
);
@@ -2044,7 +2111,7 @@ pub const VirtualMachine = struct {
defer JSValue.fromCell(promise).unprotect();
// pending_internal_promise can change if hot module reloading is enabled
- if (this.bun_watcher != null) {
+ if (this.isWatcherEnabled()) {
this.eventLoop().performGC();
switch (this.pending_internal_promise.status(this.global.vm())) {
JSC.JSPromise.Status.Pending => {
@@ -2099,7 +2166,7 @@ pub const VirtualMachine = struct {
var promise = try this.reloadEntryPoint(entry_path);
// pending_internal_promise can change if hot module reloading is enabled
- if (this.bun_watcher != null) {
+ if (this.isWatcherEnabled()) {
this.eventLoop().performGC();
switch (this.pending_internal_promise.status(this.global.vm())) {
JSC.JSPromise.Status.Pending => {
@@ -2125,6 +2192,21 @@ pub const VirtualMachine = struct {
return this.pending_internal_promise;
}
+ pub fn addListeningSocketForWatchMode(this: *VirtualMachine, socket: bun.FileDescriptor) void {
+ if (this.hot_reload != .watch) {
+ return;
+ }
+
+ this.rareData().addListeningSocketForWatchMode(socket);
+ }
+ pub fn removeListeningSocketForWatchMode(this: *VirtualMachine, socket: bun.FileDescriptor) void {
+ if (this.hot_reload != .watch) {
+ return;
+ }
+
+ this.rareData().removeListeningSocketForWatchMode(socket);
+ }
+
pub fn loadMacroEntryPoint(this: *VirtualMachine, entry_path: string, function_name: string, specifier: string, hash: i32) !*JSInternalPromise {
var entry_point_entry = try this.macro_entry_points.getOrPut(hash);
@@ -2742,9 +2824,8 @@ pub const VirtualMachine = struct {
pub const IPCInstance = struct {
globalThis: ?*JSGlobalObject,
- socket: IPC.Socket,
uws_context: *uws.SocketContext,
- ipc_buffer: bun.ByteList,
+ ipc: IPC.IPCData,
pub fn handleIPCMessage(
this: *IPCInstance,
@@ -2794,13 +2875,13 @@ pub const VirtualMachine = struct {
var instance = bun.default_allocator.create(IPCInstance) catch @panic("OOM");
instance.* = .{
.globalThis = this.global,
- .socket = socket,
.uws_context = context,
- .ipc_buffer = bun.ByteList{},
+ .ipc = .{ .socket = socket },
};
var ptr = socket.ext(*IPCInstance);
ptr.?.* = instance;
this.ipc = instance;
+ instance.ipc.writeVersionPacket();
}
comptime {
if (!JSC.is_bindgen)
@@ -2809,6 +2890,7 @@ pub const VirtualMachine = struct {
};
pub const HotReloader = NewHotReloader(VirtualMachine, JSC.EventLoop, false);
+pub const WatchReloader = NewHotReloader(VirtualMachine, JSC.EventLoop, true);
pub const Watcher = HotReloader.Watcher;
extern fn BunDebugger__willHotReload() void;
@@ -2835,6 +2917,8 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
this.eventLoop().enqueueTaskConcurrent(task);
}
+ pub var clear_screen = false;
+
pub const HotReloadTask = struct {
reloader: *Reloader,
count: u8 = 0,
@@ -2860,11 +2944,16 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
}
pub fn enqueue(this: *HotReloadTask) void {
+ JSC.markBinding(@src());
if (this.count == 0)
return;
if (comptime reload_immediately) {
- bun.reloadProcess(bun.default_allocator, Output.enable_ansi_colors);
+ Output.flush();
+ if (comptime Ctx == ImportWatcher) {
+ this.reloader.ctx.rareData().closeAllListenSocketsForWatchMode();
+ }
+ bun.reloadProcess(bun.default_allocator, clear_screen);
unreachable;
}
@@ -2898,23 +2987,51 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
) void);
pub fn enableHotModuleReloading(this: *Ctx) void {
- if (this.bun_watcher != null)
- return;
+ if (comptime @TypeOf(this.bun_watcher) == ImportWatcher) {
+ if (this.bun_watcher != .none)
+ return;
+ } else {
+ if (this.bun_watcher != null)
+ return;
+ }
var reloader = bun.default_allocator.create(Reloader) catch @panic("OOM");
reloader.* = .{
.ctx = this,
.verbose = if (@hasField(Ctx, "log")) this.log.level.atLeast(.info) else false,
};
- this.bun_watcher = @This().Watcher.init(
- reloader,
- this.bundler.fs,
- bun.default_allocator,
- ) catch @panic("Failed to enable File Watcher");
- this.bundler.resolver.watcher = Resolver.ResolveWatcher(*@This().Watcher, onMaybeWatchDirectory).init(this.bun_watcher.?);
+ if (comptime @TypeOf(this.bun_watcher) == ImportWatcher) {
+ this.bun_watcher = if (reload_immediately)
+ .{ .watch = @This().Watcher.init(
+ reloader,
+ this.bundler.fs,
+ bun.default_allocator,
+ ) catch @panic("Failed to enable File Watcher") }
+ else
+ .{ .hot = @This().Watcher.init(
+ reloader,
+ this.bundler.fs,
+ bun.default_allocator,
+ ) catch @panic("Failed to enable File Watcher") };
+
+ if (reload_immediately) {
+ this.bundler.resolver.watcher = Resolver.ResolveWatcher(*@This().Watcher, onMaybeWatchDirectory).init(this.bun_watcher.watch);
+ } else {
+ this.bundler.resolver.watcher = Resolver.ResolveWatcher(*@This().Watcher, onMaybeWatchDirectory).init(this.bun_watcher.hot);
+ }
+ } else {
+ this.bun_watcher = @This().Watcher.init(
+ reloader,
+ this.bundler.fs,
+ bun.default_allocator,
+ ) catch @panic("Failed to enable File Watcher");
+ this.bundler.resolver.watcher = Resolver.ResolveWatcher(*@This().Watcher, onMaybeWatchDirectory).init(this.bun_watcher.?);
+ }
+
+ clear_screen = Output.enable_ansi_colors and !strings.eqlComptime(this.bundler.env.map.get("BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD") orelse "0", "true");
- this.bun_watcher.?.start() catch @panic("Failed to start File Watcher");
+ reloader.getContext().start() catch @panic("Failed to start File Watcher");
}
pub fn onMaybeWatchDirectory(watch: *@This().Watcher, file_path: string, dir_fd: StoredFileDescriptorType) void {
@@ -2941,6 +3058,18 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
Output.prettyErrorln("<r>Watcher crashed: <red><b>{s}<r>", .{@errorName(err)});
}
+ pub fn getContext(this: *@This()) *@This().Watcher {
+ if (comptime @TypeOf(this.ctx.bun_watcher) == ImportWatcher) {
+ if (reload_immediately) {
+ return this.ctx.bun_watcher.watch;
+ } else {
+ return this.ctx.bun_watcher.hot;
+ }
+ } else {
+ return this.ctx.bun_watcher.?;
+ }
+ }
+
pub fn onFileUpdate(
this: *@This(),
events: []watcher.WatchEvent,
@@ -2954,7 +3083,7 @@ pub fn NewHotReloader(comptime Ctx: type, comptime EventLoopType: type, comptime
const hashes = slice.items(.hash);
const parents = slice.items(.parent_hash);
var file_descriptors = slice.items(.fd);
- var ctx = this.ctx.bun_watcher.?;
+ var ctx = this.getContext();
defer ctx.flushEvictions();
defer Output.flush();