### What does this PR do?

Fixes #23177

### How did you verify your code works?
This commit is contained in:
Jarred Sumner
2026-01-09 15:14:00 -08:00
committed by GitHub
parent 1879b7eeca
commit 367eeb308e

View File

@@ -444,10 +444,10 @@ pub const Interpreter = struct {
if (comptime free_buffered_io) { if (comptime free_buffered_io) {
if (this._buffered_stdout == .owned) { if (this._buffered_stdout == .owned) {
this._buffered_stdout.owned.deinit(bun.default_allocator); this._buffered_stdout.owned.clearAndFree(bun.default_allocator);
} }
if (this._buffered_stderr == .owned) { if (this._buffered_stderr == .owned) {
this._buffered_stderr.owned.deinit(bun.default_allocator); this._buffered_stderr.owned.clearAndFree(bun.default_allocator);
} }
} }
@@ -994,7 +994,7 @@ pub const Interpreter = struct {
interp.exit_code = exit_code; interp.exit_code = exit_code;
switch (try interp.run()) { switch (try interp.run()) {
.err => |e| { .err => |e| {
interp.deinitEverything(); interp.#deinitFromExec();
bun.Output.err(e, "Failed to run script <b>{s}<r>", .{std.fs.path.basename(path)}); bun.Output.err(e, "Failed to run script <b>{s}<r>", .{std.fs.path.basename(path)});
bun.Global.exit(1); bun.Global.exit(1);
return 1; return 1;
@@ -1003,7 +1003,7 @@ pub const Interpreter = struct {
} }
mini.tick(&is_done, @as(fn (*anyopaque) bool, IsDone.isDone)); mini.tick(&is_done, @as(fn (*anyopaque) bool, IsDone.isDone));
const code = interp.exit_code.?; const code = interp.exit_code.?;
interp.deinitEverything(); interp.#deinitFromExec();
return code; return code;
} }
@@ -1061,7 +1061,7 @@ pub const Interpreter = struct {
interp.exit_code = exit_code; interp.exit_code = exit_code;
switch (try interp.run()) { switch (try interp.run()) {
.err => |e| { .err => |e| {
interp.deinitEverything(); interp.#deinitFromExec();
bun.Output.err(e, "Failed to run script <b>{s}<r>", .{path_for_errors}); bun.Output.err(e, "Failed to run script <b>{s}<r>", .{path_for_errors});
bun.Global.exit(1); bun.Global.exit(1);
return 1; return 1;
@@ -1070,7 +1070,7 @@ pub const Interpreter = struct {
} }
mini.tick(&is_done, @as(fn (*anyopaque) bool, IsDone.isDone)); mini.tick(&is_done, @as(fn (*anyopaque) bool, IsDone.isDone));
const code = interp.exit_code.?; const code = interp.exit_code.?;
interp.deinitEverything(); interp.#deinitFromExec();
return code; return code;
} }
@@ -1142,7 +1142,7 @@ pub const Interpreter = struct {
_ = callframe; // autofix _ = callframe; // autofix
if (this.setupIOBeforeRun().asErr()) |e| { if (this.setupIOBeforeRun().asErr()) |e| {
defer this.deinitEverything(); defer this.#deinitFromExec();
const shellerr = bun.shell.ShellErr.newSys(e); const shellerr = bun.shell.ShellErr.newSys(e);
return try throwShellErr(&shellerr, .{ .js = globalThis.bunVM().event_loop }); return try throwShellErr(&shellerr, .{ .js = globalThis.bunVM().event_loop });
} }
@@ -1191,20 +1191,21 @@ pub const Interpreter = struct {
defer decrPendingActivityFlag(&this.has_pending_activity); defer decrPendingActivityFlag(&this.has_pending_activity);
if (this.event_loop == .js) { if (this.event_loop == .js) {
defer this.deinitAfterJSRun();
this.exit_code = exit_code; this.exit_code = exit_code;
const this_jsvalue = this.this_jsvalue; const this_jsvalue = this.this_jsvalue;
if (this_jsvalue != .zero) { if (this_jsvalue != .zero) {
if (jsc.Codegen.JSShellInterpreter.resolveGetCached(this_jsvalue)) |resolve| { if (jsc.Codegen.JSShellInterpreter.resolveGetCached(this_jsvalue)) |resolve| {
const loop = this.event_loop.js; const loop = this.event_loop.js;
const globalThis = this.globalThis; const globalThis = this.globalThis;
this.this_jsvalue = .zero; const buffered_stdout = this.getBufferedStdout(globalThis);
const buffered_stderr = this.getBufferedStderr(globalThis);
this.keep_alive.disable(); this.keep_alive.disable();
this.#derefRootShellAndIOIfNeeded(true);
loop.enter(); loop.enter();
_ = resolve.call(globalThis, .js_undefined, &.{ _ = resolve.call(globalThis, .js_undefined, &.{
JSValue.jsNumberFromU16(exit_code), JSValue.jsNumberFromU16(exit_code),
this.getBufferedStdout(globalThis), buffered_stdout,
this.getBufferedStderr(globalThis), buffered_stderr,
}) catch |err| globalThis.reportActiveExceptionAsUnhandled(err); }) catch |err| globalThis.reportActiveExceptionAsUnhandled(err);
jsc.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .js_undefined); jsc.Codegen.JSShellInterpreter.resolveSetCached(this_jsvalue, globalThis, .js_undefined);
jsc.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .js_undefined); jsc.Codegen.JSShellInterpreter.rejectSetCached(this_jsvalue, globalThis, .js_undefined);
@@ -1219,35 +1220,45 @@ pub const Interpreter = struct {
return .done; return .done;
} }
fn deinitAfterJSRun(this: *ThisInterpreter) void { fn #derefRootShellAndIOIfNeeded(this: *ThisInterpreter, free_buffered_io: bool) void {
log("Interpreter(0x{x}) deinitAfterJSRun", .{@intFromPtr(this)}); if (free_buffered_io) {
this.root_io.deref(); // Can safely be called multiple times.
this.keep_alive.disable(); if (this.root_shell._buffered_stderr == .owned) {
this.root_shell.deinitImpl(false, false); this.root_shell._buffered_stderr.owned.clearAndFree(bun.default_allocator);
}
if (this.root_shell._buffered_stdout == .owned) {
this.root_shell._buffered_stdout.owned.clearAndFree(bun.default_allocator);
}
}
// Has this already been finalized?
if (this.this_jsvalue != .zero) {
// Cannot be safely called multiple times.
this.root_io.deref();
this.root_shell.deinitImpl(false, false);
}
this.this_jsvalue = .zero; this.this_jsvalue = .zero;
} }
fn deinitFromFinalizer(this: *ThisInterpreter) void { fn deinitFromFinalizer(this: *ThisInterpreter) void {
if (this.root_shell._buffered_stderr == .owned) { this.#derefRootShellAndIOIfNeeded(true);
this.root_shell._buffered_stderr.owned.deinit(bun.default_allocator); this.keep_alive.disable();
}
if (this.root_shell._buffered_stdout == .owned) {
this.root_shell._buffered_stdout.owned.deinit(bun.default_allocator);
}
this.this_jsvalue = .zero;
this.args.deinit(); this.args.deinit();
this.allocator.destroy(this); this.allocator.destroy(this);
} }
fn deinitEverything(this: *ThisInterpreter) void { fn #deinitFromExec(this: *ThisInterpreter) void {
log("deinit interpreter", .{}); log("deinit interpreter", .{});
this.this_jsvalue = .zero;
this.root_io.deref(); this.root_io.deref();
this.root_shell.deinitImpl(false, true); this.root_shell.deinitImpl(false, true);
for (this.vm_args_utf8.items[0..]) |str| { for (this.vm_args_utf8.items[0..]) |str| {
str.deinit(); str.deinit();
} }
this.vm_args_utf8.deinit(); this.vm_args_utf8.deinit();
this.this_jsvalue = .zero;
this.allocator.destroy(this); this.allocator.destroy(this);
} }