diff options
Diffstat (limited to 'src/bun.js/javascript.zig')
-rw-r--r-- | src/bun.js/javascript.zig | 141 |
1 files changed, 136 insertions, 5 deletions
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 3e5ac799d..a1dd77674 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -496,6 +496,8 @@ pub const VirtualMachine = struct { gc_controller: JSC.GarbageCollectionController = .{}, worker: ?*JSC.WebWorker = null, + debugger: ?Debugger = null, + pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; pub const OnException = fn (*ZigException) void; @@ -504,6 +506,10 @@ pub const VirtualMachine = struct { return this.worker == null; } + pub fn isInspectorEnabled(this: *const VirtualMachine) bool { + return this.debugger != null; + } + pub fn setOnException(this: *VirtualMachine, callback: *const OnException) void { this.on_exception = callback; } @@ -706,6 +712,106 @@ pub const VirtualMachine = struct { } } + pub fn nextAsyncTaskID(this: *VirtualMachine) u64 { + var debugger: *Debugger = &(this.debugger orelse return 0); + debugger.next_debugger_id +%= 1; + return debugger.next_debugger_id; + } + + pub const Debugger = struct { + path_or_port: []const u8 = "", + script_execution_context_id: u32 = 0, + next_debugger_id: u64 = 1, + poll_ref: JSC.PollRef = .{}, + auto_pause: bool = false, + const debug = Output.scoped(.DEBUGGER, false); + + extern "C" fn Bun__createJSDebugger(*JSC.JSGlobalObject) u32; + extern "C" fn Bun__ensureDebugger(u32, bool) void; + extern "C" fn Bun__startJSDebuggerThread(*JSC.JSGlobalObject, u32, *bun.String) bun.String; + var has_started_debugger_thread: bool = false; + var futex_atomic: std.atomic.Atomic(u32) = undefined; + + pub fn create(this: *VirtualMachine, globalObject: *JSGlobalObject) !void { + debug("create", .{}); + this.debugger.?.script_execution_context_id = Bun__createJSDebugger(globalObject); + if (!has_started_debugger_thread) { + has_started_debugger_thread = true; + futex_atomic = std.atomic.Atomic(u32).init(0); + var thread = try std.Thread.spawn(.{}, startJSDebuggerThread, .{this}); + thread.detach(); + } + this.eventLoop().ensureWaker(); + if (this.debugger.?.auto_pause) { + this.debugger.?.poll_ref.ref(this); + } + debug("spin", .{}); + while (futex_atomic.load(.Monotonic) > 0) std.Thread.Futex.wait(&futex_atomic, 1); + if (comptime Environment.allow_assert) + debug("waitForDebugger: {}", .{Output.ElapsedFormatter{ + .colors = Output.enable_ansi_colors_stderr, + .duration_ns = @truncate(@as(u128, @intCast(std.time.nanoTimestamp() - bun.CLI.start_time))), + }}); + + Bun__ensureDebugger(this.debugger.?.script_execution_context_id, this.debugger.?.auto_pause); + } + + pub fn startJSDebuggerThread(other_vm: *VirtualMachine) void { + var arena = bun.MimallocArena.init() catch unreachable; + Output.Source.configureNamedThread("Debugger"); + debug("startJSDebuggerThread", .{}); + + var vm = JSC.VirtualMachine.init(.{ + .allocator = arena.allocator(), + .args = std.mem.zeroes(Api.TransformOptions), + .store_fd = false, + }) catch @panic("Failed to create Debugger VM"); + vm.allocator = arena.allocator(); + vm.arena = &arena; + + vm.bundler.configureDefines() catch @panic("Failed to configure defines"); + vm.is_main_thread = false; + vm.eventLoop().ensureWaker(); + + vm.global.vm().holdAPILock(other_vm, @ptrCast(&start)); + } + + pub export var Bun__debugger_server_url: bun.String = undefined; + + fn start(other_vm: *VirtualMachine) void { + var this = VirtualMachine.get(); + var str = bun.String.create(other_vm.debugger.?.path_or_port); + Bun__debugger_server_url = Bun__startJSDebuggerThread(this.global, other_vm.debugger.?.script_execution_context_id, &str); + Bun__debugger_server_url.toThreadSafe(); + + this.global.handleRejectedPromises(); + + if (this.log.msgs.items.len > 0) { + if (Output.enable_ansi_colors) { + this.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), true) catch {}; + } else { + this.log.printForLogLevelWithEnableAnsiColors(Output.errorWriter(), false) catch {}; + } + Output.prettyErrorln("\n", .{}); + Output.flush(); + } + + futex_atomic.store(0, .Monotonic); + std.Thread.Futex.wake(&futex_atomic, 1); + debug("wake", .{}); + this.eventLoop().tick(); + + while (true) { + while (this.eventLoop().tasks.count > 0 or this.active_tasks > 0 or this.uws_event_loop.?.active > 0) { + this.tick(); + this.eventLoop().autoTickActive(); + } + + this.eventLoop().tickPossiblyForever(); + } + } + }; + pub inline fn enqueueTask(this: *VirtualMachine, task: Task) void { this.eventLoop().enqueueTask(task); } @@ -917,6 +1023,8 @@ pub const VirtualMachine = struct { source_code_printer.?.ctx.append_null_byte = false; } + vm.configureDebugger(opts.debugger); + return vm; } @@ -928,6 +1036,7 @@ pub const VirtualMachine = struct { store_fd: bool = false, smol: bool = false, graph: ?*bun.StandaloneModuleGraph = null, + debugger: bun.CLI.Command.Debugger = .{ .unspecified = {} }, }; pub fn init(opts: Options) !*VirtualMachine { @@ -1023,9 +1132,32 @@ pub const VirtualMachine = struct { source_code_printer.?.ctx.append_null_byte = false; } + vm.configureDebugger(opts.debugger); + return vm; } + fn configureDebugger(this: *VirtualMachine, debugger: bun.CLI.Command.Debugger) void { + switch (debugger) { + .unspecified => {}, + .enable => { + this.debugger = Debugger{}; + }, + .path_or_port => { + this.debugger = Debugger{ + .path_or_port = debugger.path_or_port, + }; + }, + } + + if (debugger != .unspecified) { + this.bundler.options.minify_identifiers = false; + this.bundler.options.minify_syntax = false; + this.bundler.options.minify_whitespace = false; + this.bundler.options.debugger = true; + } + } + pub fn initWorker( worker: *WebWorker, opts: Options, @@ -1126,11 +1258,6 @@ pub const VirtualMachine = struct { return vm; } - // dynamic import - // pub fn import(global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) ErrorableZigString { - - // } - pub threadlocal var source_code_printer: ?*js_printer.BufferPrinter = null; pub fn clearRefString(_: *anyopaque, ref_string: *JSC.RefString) void { @@ -1771,6 +1898,10 @@ pub const VirtualMachine = struct { var promise: *JSInternalPromise = undefined; + if (this.debugger != null) { + try Debugger.create(this, this.global); + } + if (!this.bundler.options.disable_transpilation) { { this.is_in_preload = true; |