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