diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 54f091c2f3..e5c1d53874 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -86,7 +86,7 @@ const TranspilerOptions = struct { // This is going to be hard to not leak pub const TransformTask = struct { input_code: JSC.Node.StringOrBuffer = JSC.Node.StringOrBuffer{ .buffer = .{} }, - output_code: ZigString = ZigString.init(""), + output_code: bun.String = bun.String.empty, bundler: Bundler.Bundler = undefined, log: logger.Log, err: ?anyerror = null, @@ -96,12 +96,13 @@ pub const TransformTask = struct { global: *JSGlobalObject, replace_exports: Runtime.Features.ReplaceableExport.Map = .{}, + pub usingnamespace bun.New(@This()); + pub const AsyncTransformTask = JSC.ConcurrentPromiseTask(TransformTask); pub const AsyncTransformEventLoopTask = AsyncTransformTask.EventLoopTask; pub fn create(transpiler: *Transpiler, input_code: bun.JSC.Node.StringOrBuffer, globalThis: *JSGlobalObject, loader: Loader) !*AsyncTransformTask { - var transform_task = try bun.default_allocator.create(TransformTask); - transform_task.* = .{ + var transform_task = TransformTask.new(.{ .input_code = input_code, .bundler = undefined, .global = globalThis, @@ -110,7 +111,7 @@ pub const TransformTask = struct { .log = logger.Log.init(bun.default_allocator), .loader = loader, .replace_exports = transpiler.transpiler_options.runtime.replace_exports, - }; + }); transform_task.log.level = transpiler.transpiler_options.log.level; transform_task.bundler = transpiler.bundler; transform_task.bundler.linker.resolver = &transform_task.bundler.resolver; @@ -124,21 +125,36 @@ pub const TransformTask = struct { const name = this.loader.stdinName(); const source = logger.Source.initPathString(name, this.input_code.slice()); - JSAst.Stmt.Data.Store.create(bun.default_allocator); - JSAst.Expr.Data.Store.create(bun.default_allocator); + const prev_memory_allocators = .{ JSAst.Stmt.Data.Store.memory_allocator, JSAst.Expr.Data.Store.memory_allocator }; + defer { + JSAst.Stmt.Data.Store.memory_allocator = prev_memory_allocators[0]; + JSAst.Expr.Data.Store.memory_allocator = prev_memory_allocators[1]; + } var arena = Mimalloc.Arena.init() catch unreachable; const allocator = arena.allocator(); + var ast_memory_allocator = allocator.create(JSAst.ASTMemoryAllocator) catch bun.outOfMemory(); + ast_memory_allocator.* = .{ + .allocator = allocator, + }; + ast_memory_allocator.reset(); + JSAst.Stmt.Data.Store.memory_allocator = ast_memory_allocator; + JSAst.Expr.Data.Store.memory_allocator = ast_memory_allocator; + JSAst.Stmt.Data.Store.create(bun.default_allocator); + JSAst.Expr.Data.Store.create(bun.default_allocator); + defer { - this.input_code.deinitAndUnprotect(); JSAst.Stmt.Data.Store.reset(); JSAst.Expr.Data.Store.reset(); arena.deinit(); } this.bundler.setAllocator(allocator); + this.bundler.setLog(&this.log); + this.log.msgs.allocator = bun.default_allocator; + const jsx = if (this.tsconfig != null) this.tsconfig.?.mergeJSX(this.bundler.options.jsx) else @@ -163,16 +179,15 @@ pub const TransformTask = struct { }; if (parse_result.empty) { - this.output_code = ZigString.init(""); + this.output_code = bun.String.empty; return; } - const global_allocator = arena.backingAllocator(); - var buffer_writer = JSPrinter.BufferWriter.init(global_allocator) catch |err| { + var buffer_writer = JSPrinter.BufferWriter.init(allocator) catch |err| { this.err = err; return; }; - buffer_writer.buffer.list.ensureTotalCapacity(global_allocator, 512) catch unreachable; + buffer_writer.buffer.list.ensureTotalCapacity(allocator, 512) catch unreachable; buffer_writer.reset(); // defer { @@ -188,12 +203,9 @@ pub const TransformTask = struct { if (printed > 0) { buffer_writer = printer.ctx; buffer_writer.buffer.list.items = buffer_writer.written; - - var output = JSC.ZigString.init(buffer_writer.written); - output.mark(); - this.output_code = output; + this.output_code = bun.String.createLatin1(buffer_writer.written); } else { - this.output_code = ZigString.init(""); + this.output_code = bun.String.empty; } } @@ -219,28 +231,25 @@ pub const TransformTask = struct { return; } - finish(this.output_code, this.global, promise); - + const global = this.global; + const code = this.output_code; + this.output_code = bun.String.empty; this.deinit(); + + finish(code, global, promise); } - noinline fn finish(code: ZigString, global: *JSGlobalObject, promise: *JSC.JSPromise) void { - promise.resolve(global, code.toValueGC(global)); + noinline fn finish(code: bun.String, global: *JSGlobalObject, promise: *JSC.JSPromise) void { + promise.resolve(global, code.toJS(global)); + code.deref(); } pub fn deinit(this: *TransformTask) void { - var should_cleanup = false; - defer if (should_cleanup) bun.Global.mimalloc_cleanup(false); - this.log.deinit(); this.input_code.deinitAndUnprotect(); + this.output_code.deref(); - if (this.output_code.isGloballyAllocated()) { - should_cleanup = this.output_code.len > 512_000; - this.output_code.deinitGlobal(); - } - - bun.default_allocator.destroy(this); + this.destroy(); } }; @@ -1008,7 +1017,7 @@ pub fn transform( return .zero; }; - var code = JSC.Node.StringOrBuffer.fromJS(globalThis, this.arena.allocator(), code_arg) orelse { + var code = JSC.Node.StringOrBuffer.fromJSWithEncodingMaybeAsync(globalThis, bun.default_allocator, code_arg, .utf8, true) orelse { globalThis.throwInvalidArgumentType("transform", "code", "string or Uint8Array"); return .zero; }; @@ -1024,17 +1033,23 @@ pub fn transform( }; if (exception.* != null) { + code.deinit(); globalThis.throwValue(JSC.JSValue.c(exception.*)); return .zero; } - code.toThreadSafe(); + if (code == .buffer) { + code_arg.protect(); + } var task = TransformTask.create( this, code, globalThis, loader orelse this.transpiler_options.default_loader, ) catch { + if (code == .buffer) { + code_arg.unprotect(); + } globalThis.throw("Out of memory", .{}); return .zero; }; diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index cf618e3172..c345948d9a 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -42,8 +42,10 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { // This is a poll because we want it to enter the uSockets loop ref: Async.KeepAlive = .{}, + pub usingnamespace bun.New(@This()); + pub fn createOnJSThread(allocator: std.mem.Allocator, globalThis: *JSGlobalObject, value: *Context) !*This { - var this = bun.new(This, .{ + var this = This.new(.{ .event_loop = VirtualMachine.get().event_loop, .ctx = value, .allocator = allocator, @@ -80,7 +82,8 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { } pub fn deinit(this: *This) void { - bun.destroy(this); + this.promise.strong.deinit(); + this.destroy(); } }; } diff --git a/test/transpiler/09748.test.ts b/test/transpiler/09748.test.ts new file mode 100644 index 0000000000..fef8528eaf --- /dev/null +++ b/test/transpiler/09748.test.ts @@ -0,0 +1,236 @@ +import { test, expect } from "bun:test"; + +// a somewhat long source code string +const source = ` +// @pragma jsx foo +import { Foo } from './foo'; +const foo = new Foo(); +foo.bar(); +export default foo; +export const abc_1 = "abc_1" + 123 * 2 + [foo]; +export const abc_2 = "abc_2" + 123 * 2 + [foo]; +export const abc_3 = "abc_3" + 123 * 2 + [foo]; +export const abc_4 = "abc_4" + 123 * 2 + [foo]; +export const abc_5 = "abc_5" + 123 * 2 + [foo]; +export const abc_6 = "abc_6" + 123 * 2 + [foo]; +export const abc_7 = "abc_7" + 123 * 2 + [foo]; +export const abc_8 = "abc_8" + 123 * 2 + [foo]; +export const abc_9 = "abc_9" + 123 * 2 + [foo]; +export const abc_10 = "abc_10" + 123 * 2 + [foo]; +export const abc_11 = "abc_11" + 123 * 2 + [foo]; +export const abc_12 = "abc_12" + 123 * 2 + [foo]; +export const abc_13 = "abc_13" + 123 * 2 + [foo]; +export const abc_14 = "abc_14" + 123 * 2 + [foo]; +export const abc_15 = "abc_15" + 123 * 2 + [foo]; +export const abc_16 = "abc_16" + 123 * 2 + [foo]; +export const abc_17 = "abc_17" + 123 * 2 + [foo]; +export const abc_18 = "abc_18" + 123 * 2 + [foo]; +export const abc_19 = "abc_19" + 123 * 2 + [foo]; +export const abc_20 = "abc_20" + 123 * 2 + [foo]; +export const abc_21 = "abc_21" + 123 * 2 + [foo]; +export const abc_22 = "abc_22" + 123 * 2 + [foo]; +export const abc_23 = "abc_23" + 123 * 2 + [foo]; +export const abc_24 = "abc_24" + 123 * 2 + [foo]; +export const abc_25 = "abc_25" + 123 * 2 + [foo]; +export const abc_26 = "abc_26" + 123 * 2 + [foo]; +export const abc_27 = "abc_27" + 123 * 2 + [foo]; +export const abc_28 = "abc_28" + 123 * 2 + [foo]; +export const abc_29 = "abc_29" + 123 * 2 + [foo]; +export const abc_30 = "abc_30" + 123 * 2 + [foo]; +export const abc_31 = "abc_31" + 123 * 2 + [foo]; +export const abc_32 = "abc_32" + 123 * 2 + [foo]; +export const abc_33 = "abc_33" + 123 * 2 + [foo]; +export const abc_34 = "abc_34" + 123 * 2 + [foo]; +export const abc_35 = "abc_35" + 123 * 2 + [foo]; +export const abc_36 = "abc_36" + 123 * 2 + [foo]; +export const abc_37 = "abc_37" + 123 * 2 + [foo]; +export const abc_38 = "abc_38" + 123 * 2 + [foo]; +export const abc_39 = "abc_39" + 123 * 2 + [foo]; +export const abc_40 = "abc_40" + 123 * 2 + [foo]; +export const abc_41 = "abc_41" + 123 * 2 + [foo]; +export const abc_42 = "abc_42" + 123 * 2 + [foo]; +export const abc_43 = "abc_43" + 123 * 2 + [foo]; +export const abc_44 = "abc_44" + 123 * 2 + [foo]; +export const abc_45 = "abc_45" + 123 * 2 + [foo]; +export const abc_46 = "abc_46" + 123 * 2 + [foo]; +export const abc_47 = "abc_47" + 123 * 2 + [foo]; +export const abc_48 = "abc_48" + 123 * 2 + [foo]; +export const abc_49 = "abc_49" + 123 * 2 + [foo]; +export const abc_50 = "abc_50" + 123 * 2 + [foo]; +export const abc_51 = "abc_51" + 123 * 2 + [foo]; +export const abc_52 = "abc_52" + 123 * 2 + [foo]; +export const abc_53 = "abc_53" + 123 * 2 + [foo]; +export const abc_54 = "abc_54" + 123 * 2 + [foo]; +export const abc_55 = "abc_55" + 123 * 2 + [foo]; +export const abc_56 = "abc_56" + 123 * 2 + [foo]; +export const abc_57 = "abc_57" + 123 * 2 + [foo]; +export const abc_58 = "abc_58" + 123 * 2 + [foo]; +export const abc_59 = "abc_59" + 123 * 2 + [foo]; +export const abc_60 = "abc_60" + 123 * 2 + [foo]; +export const abc_61 = "abc_61" + 123 * 2 + [foo]; +export const abc_62 = "abc_62" + 123 * 2 + [foo]; +export const abc_63 = "abc_63" + 123 * 2 + [foo]; +export const abc_64 = "abc_64" + 123 * 2 + [foo]; +export const abc_65 = "abc_65" + 123 * 2 + [foo]; +export const abc_66 = "abc_66" + 123 * 2 + [foo]; +export const abc_67 = "abc_67" + 123 * 2 + [foo]; +export const abc_68 = "abc_68" + 123 * 2 + [foo]; +export const abc_69 = "abc_69" + 123 * 2 + [foo]; +export const abc_70 = "abc_70" + 123 * 2 + [foo]; +export const abc_71 = "abc_71" + 123 * 2 + [foo]; +export const abc_72 = "abc_72" + 123 * 2 + [foo]; +export const abc_73 = "abc_73" + 123 * 2 + [foo]; +export const abc_74 = "abc_74" + 123 * 2 + [foo]; +export const abc_75 = "abc_75" + 123 * 2 + [foo]; +export const abc_76 = "abc_76" + 123 * 2 + [foo]; +export const abc_77 = "abc_77" + 123 * 2 + [foo]; +export const abc_78 = "abc_78" + 123 * 2 + [foo]; +export const abc_79 = "abc_79" + 123 * 2 + [foo]; +export const abc_80 = "abc_80" + 123 * 2 + [foo]; +export const abc_81 = "abc_81" + 123 * 2 + [foo]; +export const abc_82 = "abc_82" + 123 * 2 + [foo]; +export const abc_83 = "abc_83" + 123 * 2 + [foo]; +export const abc_84 = "abc_84" + 123 * 2 + [foo]; +export const abc_85 = "abc_85" + 123 * 2 + [foo]; +export const abc_86 = "abc_86" + 123 * 2 + [foo]; +export const abc_87 = "abc_87" + 123 * 2 + [foo]; +export const abc_88 = "abc_88" + 123 * 2 + [foo]; +export const abc_89 = "abc_89" + 123 * 2 + [foo]; +export const abc_90 = "abc_90" + 123 * 2 + [foo]; +export const abc_91 = "abc_91" + 123 * 2 + [foo]; +export const abc_92 = "abc_92" + 123 * 2 + [foo]; +export const abc_93 = "abc_93" + 123 * 2 + [foo]; +export const abc_94 = "abc_94" + 123 * 2 + [foo]; +export const abc_95 = "abc_95" + 123 * 2 + [foo]; +export const abc_96 = "abc_96" + 123 * 2 + [foo]; +export const abc_97 = "abc_97" + 123 * 2 + [foo]; +export const abc_98 = "abc_98" + 123 * 2 + [foo]; +export const abc_99 = "abc_99" + 123 * 2 + [foo]; +export const abc_100 = "abc_100" + 123 * 2 + [foo]; +export const abc_101 = "abc_101" + 123 * 2 + [foo]; +export const abc_102 = "abc_102" + 123 * 2 + [foo]; +export const abc_103 = "abc_103" + 123 * 2 + [foo]; +export const abc_104 = "abc_104" + 123 * 2 + [foo]; +export const abc_105 = "abc_105" + 123 * 2 + [foo]; +export const abc_106 = "abc_106" + 123 * 2 + [foo]; +export const abc_107 = "abc_107" + 123 * 2 + [foo]; +export const abc_108 = "abc_108" + 123 * 2 + [foo]; +export const abc_109 = "abc_109" + 123 * 2 + [foo]; +export const abc_110 = "abc_110" + 123 * 2 + [foo]; +export const abc_111 = "abc_111" + 123 * 2 + [foo]; +export const abc_112 = "abc_112" + 123 * 2 + [foo]; +export const abc_113 = "abc_113" + 123 * 2 + [foo]; +export const abc_114 = "abc_114" + 123 * 2 + [foo]; +export const abc_115 = "abc_115" + 123 * 2 + [foo]; +export const abc_116 = "abc_116" + 123 * 2 + [foo]; +export const abc_117 = "abc_117" + 123 * 2 + [foo]; +export const abc_118 = "abc_118" + 123 * 2 + [foo]; +export const abc_119 = "abc_119" + 123 * 2 + [foo]; +export const abc_120 = "abc_120" + 123 * 2 + [foo]; +export const abc_121 = "abc_121" + 123 * 2 + [foo]; +export const abc_122 = "abc_122" + 123 * 2 + [foo]; +export const abc_123 = "abc_123" + 123 * 2 + [foo]; +export const abc_124 = "abc_124" + 123 * 2 + [foo]; +export const abc_125 = "abc_125" + 123 * 2 + [foo]; +export const abc_126 = "abc_126" + 123 * 2 + [foo]; +export const abc_127 = "abc_127" + 123 * 2 + [foo]; +export const abc_128 = "abc_128" + 123 * 2 + [foo]; +export const abc_129 = "abc_129" + 123 * 2 + [foo]; +export const abc_130 = "abc_130" + 123 * 2 + [foo]; +export const abc_131 = "abc_131" + 123 * 2 + [foo]; +export const abc_132 = "abc_132" + 123 * 2 + [foo]; +export const abc_133 = "abc_133" + 123 * 2 + [foo]; +export const abc_134 = "abc_134" + 123 * 2 + [foo]; +export const abc_135 = "abc_135" + 123 * 2 + [foo]; +export const abc_136 = "abc_136" + 123 * 2 + [foo]; +export const abc_137 = "abc_137" + 123 * 2 + [foo]; +export const abc_138 = "abc_138" + 123 * 2 + [foo]; +export const abc_139 = "abc_139" + 123 * 2 + [foo]; +export const abc_140 = "abc_140" + 123 * 2 + [foo]; +export const abc_141 = "abc_141" + 123 * 2 + [foo]; +export const abc_142 = "abc_142" + 123 * 2 + [foo]; +export const abc_143 = "abc_143" + 123 * 2 + [foo]; +export const abc_144 = "abc_144" + 123 * 2 + [foo]; +export const abc_145 = "abc_145" + 123 * 2 + [foo]; +export const abc_146 = "abc_146" + 123 * 2 + [foo]; +export const abc_147 = "abc_147" + 123 * 2 + [foo]; +export const abc_148 = "abc_148" + 123 * 2 + [foo]; +export const abc_149 = "abc_149" + 123 * 2 + [foo]; +export const abc_150 = "abc_150" + 123 * 2 + [foo]; +export const abc_151 = "abc_151" + 123 * 2 + [foo]; +export const abc_152 = "abc_152" + 123 * 2 + [foo]; +export const abc_153 = "abc_153" + 123 * 2 + [foo]; +export const abc_154 = "abc_154" + 123 * 2 + [foo]; +export const abc_155 = "abc_155" + 123 * 2 + [foo]; +export const abc_156 = "abc_156" + 123 * 2 + [foo]; +export const abc_157 = "abc_157" + 123 * 2 + [foo]; +export const abc_158 = "abc_158" + 123 * 2 + [foo]; +export const abc_159 = "abc_159" + 123 * 2 + [foo]; +export const abc_160 = "abc_160" + 123 * 2 + [foo]; +export const abc_161 = "abc_161" + 123 * 2 + [foo]; +export const abc_162 = "abc_162" + 123 * 2 + [foo]; +export const abc_163 = "abc_163" + 123 * 2 + [foo]; +export const abc_164 = "abc_164" + 123 * 2 + [foo]; +export const abc_165 = "abc_165" + 123 * 2 + [foo]; +export const abc_166 = "abc_166" + 123 * 2 + [foo]; +export const abc_167 = "abc_167" + 123 * 2 + [foo]; +export const abc_168 = "abc_168" + 123 * 2 + [foo]; +export const abc_169 = "abc_169" + 123 * 2 + [foo]; +export const abc_170 = "abc_170" + 123 * 2 + [foo]; +export const abc_171 = "abc_171" + 123 * 2 + [foo]; +export const abc_172 = "abc_172" + 123 * 2 + [foo]; +export const abc_173 = "abc_173" + 123 * 2 + [foo]; +export const abc_174 = "abc_174" + 123 * 2 + [foo]; +export const abc_175 = "abc_175" + 123 * 2 + [foo]; +export const abc_176 = "abc_176" + 123 * 2 + [foo]; +export const abc_177 = "abc_177" + 123 * 2 + [foo]; +export const abc_178 = "abc_178" + 123 * 2 + [foo]; +export const abc_179 = "abc_179" + 123 * 2 + [foo]; +export const abc_180 = "abc_180" + 123 * 2 + [foo]; +export const abc_181 = "abc_181" + 123 * 2 + [foo]; +export const abc_182 = "abc_182" + 123 * 2 + [foo]; +export const abc_183 = "abc_183" + 123 * 2 + [foo]; +export const abc_184 = "abc_184" + 123 * 2 + [foo]; +export const abc_185 = "abc_185" + 123 * 2 + [foo]; +export const abc_186 = "abc_186" + 123 * 2 + [foo]; +export const abc_187 = "abc_187" + 123 * 2 + [foo]; +export const abc_188 = "abc_188" + 123 * 2 + [foo]; +export const abc_189 = "abc_189" + 123 * 2 + [foo]; +export const abc_190 = "abc_190" + 123 * 2 + [foo]; +export const abc_191 = "abc_191" + 123 * 2 + [foo]; +export const abc_192 = "abc_192" + 123 * 2 + [foo]; +export const abc_193 = "abc_193" + 123 * 2 + [foo]; +export const abc_194 = "abc_194" + 123 * 2 + [foo]; +export const abc_195 = "abc_195" + 123 * 2 + [foo]; +export const abc_196 = "abc_196" + 123 * 2 + [foo]; +export const abc_197 = "abc_197" + 123 * 2 + [foo]; +export const abc_198 = "abc_198" + 123 * 2 + [foo]; +export const abc_199 = "abc_199" + 123 * 2 + [foo]; +export const abc_200 = "abc_200" + 123 * 2 + [foo]; +export const abc_201 = "abc_201" + 123 * 2 + [foo]; +export const abc_202 = "abc_202" + 123 * 2 + [foo]; +export const abc_203 = "abc_203" + 123 * 2 + [foo]; +`; + +test("09748", async () => { + const concurrency = 100; + let strings; + + async function iter() { + const promises: Promise[] = []; + for (let c = 0; c < concurrency; c++) { + promises.push(new Bun.Transpiler().transform(source)); + } + + const results = await Promise.all(promises); + if (!strings) { + strings = Array.from({ length: concurrency }, () => results[0]); + } + expect(results).toStrictEqual(strings); + } + + for (let i = 0; i < 20; i++) { + await iter(); + Bun.gc(); + } +});