diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 2f2a3d4f80..bafedf0888 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -649,8 +649,12 @@ pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) b .transpiler = undefined, .scan_pass_result = ScanPassResult.init(bun.default_allocator), }); - errdefer bun.destroy(this); - errdefer this.arena.deinit(); + errdefer { + this.config.log.deinit(); + this.arena.deinit(); + this.ref_count.clearWithoutDestructor(); + bun.destroy(this); + } const config_arg = if (arguments.len > 0) arguments.ptr[0] else .js_undefined; const allocator = this.arena.allocator(); diff --git a/src/ptr/ref_count.zig b/src/ptr/ref_count.zig index c0433d5da4..afa7d6a8b6 100644 --- a/src/ptr/ref_count.zig +++ b/src/ptr/ref_count.zig @@ -175,11 +175,21 @@ pub fn RefCount(T: type, field_name: []const u8, destructor: anytype, options: O /// The count is 0 after the destructor is called. pub fn assertNoRefs(count: *const @This()) void { - if (enable_debug) { + if (comptime bun.Environment.ci_assert) { bun.assert(count.raw_count == 0); } } + /// Sets the ref count to 0 without running the destructor. + /// + /// Only use this if you're about to free the object (e.g., with `bun.destroy`). + /// + /// Don't modify the ref count or create any `RefPtr`s after calling this method. + pub fn clearWithoutDestructor(count: *@This()) void { + count.assertSingleThreaded(); + count.raw_count = 0; + } + fn assertSingleThreaded(count: *@This()) void { count.thread.lockOrAssert(); } @@ -282,11 +292,23 @@ pub fn ThreadSafeRefCount(T: type, field_name: []const u8, destructor: fn (*T) v /// The count is 0 after the destructor is called. pub fn assertNoRefs(count: *const @This()) void { - if (enable_debug) { + if (comptime bun.Environment.ci_assert) { bun.assert(count.raw_count.load(.seq_cst) == 0); } } + /// Sets the ref count to 0 without running the destructor. + /// + /// Only use this if you're about to free the object (e.g., with `bun.destroy`). + /// + /// Don't modify the ref count or create any `RefPtr`s after calling this method. + pub fn clearWithoutDestructor(count: *@This()) void { + // This method should only be used if you're about the free the object. You shouldn't + // be freeing the object if other threads might be using it, and no memory order can + // help with that, so .monotonic is sufficient. + count.raw_count.store(0, .monotonic); + } + fn getRefCount(self: *T) *@This() { return &@field(self, field_name); }