diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 074a47e1558..fad25b92439 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -504,6 +504,10 @@ pub const TransformTask = struct { this.transpiler.setLog(&this.log); this.log.msgs.allocator = bun.default_allocator; + defer for (this.log.msgs.items) |*msg| { + msg.* = bun.handleOom(msg.clone(bun.default_allocator)); + }; + const jsx = if (this.tsconfig != null) this.tsconfig.?.mergeJSX(this.transpiler.options.jsx) else @@ -587,6 +591,7 @@ pub const TransformTask = struct { } pub fn deinit(this: *TransformTask) void { + for (this.log.msgs.items) |*msg| msg.deinit(bun.default_allocator); this.log.deinit(); this.input_code.deinitAndUnprotect(); this.output_code.deref(); diff --git a/test/js/bun/transpiler/transpiler-transform-error-uaf.test.ts b/test/js/bun/transpiler/transpiler-transform-error-uaf.test.ts new file mode 100644 index 00000000000..2cb5c4f827c --- /dev/null +++ b/test/js/bun/transpiler/transpiler-transform-error-uaf.test.ts @@ -0,0 +1,29 @@ +import { expect, test } from "bun:test"; + +test("concurrent async transform() with parse errors produces correct error messages", async () => { + const transpiler = new Bun.Transpiler(); + + const promises: Promise[] = []; + for (let i = 0; i < 100; i++) { + promises.push(transpiler.transform("const x = ;", "js").catch(e => e)); + } + + const errors = await Promise.all(promises); + for (const err of errors) { + expect(String(err)).toContain("Unexpected"); + } +}); + +test("concurrent async transform() with redeclaration errors produces correct error messages", async () => { + const transpiler = new Bun.Transpiler(); + + const promises: Promise[] = []; + for (let i = 0; i < 100; i++) { + promises.push(transpiler.transform("const x = 1; const x = 2;", "js").catch(e => e)); + } + + const errors = await Promise.all(promises); + for (const err of errors) { + expect(String(err)).toContain(`"x"`); + } +});