diff --git a/src/Watcher.zig b/src/Watcher.zig index 1a04beafa2..5c50433b61 100644 --- a/src/Watcher.zig +++ b/src/Watcher.zig @@ -164,12 +164,13 @@ pub const WatchEvent = struct { }; } - pub const Op = packed struct { + pub const Op = packed struct(u8) { delete: bool = false, metadata: bool = false, rename: bool = false, write: bool = false, move_to: bool = false, + _padding: u3 = 0, pub fn merge(before: Op, after: Op) Op { return .{ @@ -185,6 +186,7 @@ pub const WatchEvent = struct { try w.writeAll("{"); var first = true; inline for (comptime std.meta.fieldNames(Op)) |name| { + if (comptime std.mem.eql(u8, name, "_padding")) continue; if (@field(op, name)) { if (!first) { try w.writeAll(","); diff --git a/src/bake/DevServer.zig b/src/bake/DevServer.zig index 59757996db..4265020e37 100644 --- a/src/bake/DevServer.zig +++ b/src/bake/DevServer.zig @@ -1917,9 +1917,8 @@ fn startAsyncBundle( errdefer heap.deinit(); const allocator = heap.allocator(); const ast_memory_allocator = try allocator.create(bun.JSAst.ASTMemoryAllocator); - ast_memory_allocator.* = .{ .allocator = allocator }; - ast_memory_allocator.reset(); - ast_memory_allocator.push(); + var ast_scope = ast_memory_allocator.enter(allocator); + defer ast_scope.exit(); const bv2 = try BundleV2.init( &dev.server_transpiler, diff --git a/src/bake/bake.zig b/src/bake/bake.zig index b3b7fd0423..834ca1ff27 100644 --- a/src/bake/bake.zig +++ b/src/bake/bake.zig @@ -595,14 +595,15 @@ pub const Framework = struct { bundler_options: *const BuildConfigSubset, ) !void { const JSAst = bun.JSAst; - const prev_alloc_stmt = JSAst.Stmt.Data.Store.memory_allocator; - const prev_alloc_expr = JSAst.Expr.Data.Store.memory_allocator; - defer JSAst.Stmt.Data.Store.memory_allocator = prev_alloc_stmt; - defer JSAst.Expr.Data.Store.memory_allocator = prev_alloc_expr; + var ast_memory_allocator: JSAst.ASTMemoryAllocator = undefined; ast_memory_allocator.initWithoutStack(arena); - JSAst.Stmt.Data.Store.memory_allocator = &ast_memory_allocator; - JSAst.Expr.Data.Store.memory_allocator = &ast_memory_allocator; + var ast_scope = JSAst.ASTMemoryAllocator.Scope{ + .previous = JSAst.Stmt.Data.Store.memory_allocator, + .current = &ast_memory_allocator, + }; + ast_scope.enter(); + defer ast_scope.exit(); out.* = try bun.Transpiler.init( arena, diff --git a/src/bun.js/ModuleLoader.zig b/src/bun.js/ModuleLoader.zig index 806e9822b4..48845dbe55 100644 --- a/src/bun.js/ModuleLoader.zig +++ b/src/bun.js/ModuleLoader.zig @@ -845,6 +845,11 @@ pub fn transpileSourceCode( switch (loader) { .js, .jsx, .ts, .tsx, .json, .jsonc, .toml, .text => { + // Ensure that if there was an ASTMemoryAllocator in use, it's not used anymore. + var ast_scope = js_ast.ASTMemoryAllocator.Scope{}; + ast_scope.enter(); + defer ast_scope.exit(); + jsc_vm.transpiled_count += 1; jsc_vm.transpiler.resetStore(); const hash = bun.Watcher.getHash(path.text); @@ -2320,9 +2325,8 @@ pub const RuntimeTranspilerStore = struct { }; } - ast_memory_store.?.allocator = allocator; - ast_memory_store.?.reset(); - ast_memory_store.?.push(); + var ast_scope = ast_memory_store.?.enter(allocator); + defer ast_scope.exit(); const path = this.path; const specifier = this.path.text; diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index bcbdb68117..6ad30f7e39 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -117,30 +117,13 @@ pub const TransformTask = struct { const name = this.loader.stdinName(); const source = logger.Source.initPathString(name, this.input_code.slice()); - 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; + defer arena.deinit(); 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; - - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - arena.deinit(); - } + var ast_scope = ast_memory_allocator.enter(allocator); + defer ast_scope.exit(); this.transpiler.setAllocator(allocator); this.transpiler.setLog(&this.log); @@ -847,7 +830,8 @@ pub fn scan(this: *JSTranspiler, globalThis: *JSC.JSGlobalObject, callframe: *JS var arena = Mimalloc.Arena.init() catch unreachable; const prev_allocator = this.transpiler.allocator; - this.transpiler.setAllocator(arena.allocator()); + const allocator = arena.allocator(); + this.transpiler.setAllocator(allocator); var log = logger.Log.init(arena.backingAllocator()); defer log.deinit(); this.transpiler.setLog(&log); @@ -856,13 +840,11 @@ pub fn scan(this: *JSTranspiler, globalThis: *JSC.JSGlobalObject, callframe: *JS this.transpiler.setAllocator(prev_allocator); arena.deinit(); } + var ast_memory_allocator = allocator.create(JSAst.ASTMemoryAllocator) catch bun.outOfMemory(); + var ast_scope = ast_memory_allocator.enter(allocator); + defer ast_scope.exit(); - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - } - - var parse_result = getParseResult(this, arena.allocator(), code, loader, Transpiler.MacroJSValueType.zero) orelse { + var parse_result = getParseResult(this, allocator, code, loader, Transpiler.MacroJSValueType.zero) orelse { if ((this.transpiler.log.warnings + this.transpiler.log.errors) > 0) { return globalThis.throwValue(try this.transpiler.log.toJS(globalThis, globalThis.allocator(), "Parse error")); } @@ -991,15 +973,14 @@ pub fn transformSync( } } - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - } + const allocator = arena.allocator(); + + var ast_memory_allocator = allocator.create(JSAst.ASTMemoryAllocator) catch bun.outOfMemory(); + var ast_scope = ast_memory_allocator.enter(allocator); + defer ast_scope.exit(); const prev_bundler = this.transpiler; - this.transpiler.setAllocator(arena.allocator()); + this.transpiler.setAllocator(allocator); this.transpiler.macro_context = null; var log = logger.Log.init(arena.backingAllocator()); log.level = this.transpiler_options.log.level; @@ -1010,7 +991,7 @@ pub fn transformSync( } const parse_result = getParseResult( this, - arena.allocator(), + allocator, code, loader, js_ctx_value, @@ -1132,7 +1113,12 @@ pub fn scanImports(this: *JSTranspiler, globalThis: *JSC.JSGlobalObject, callfra var arena = Mimalloc.Arena.init() catch unreachable; const prev_allocator = this.transpiler.allocator; - this.transpiler.setAllocator(arena.allocator()); + const allocator = arena.allocator(); + var ast_memory_allocator = allocator.create(JSAst.ASTMemoryAllocator) catch bun.outOfMemory(); + var ast_scope = ast_memory_allocator.enter(allocator); + defer ast_scope.exit(); + + this.transpiler.setAllocator(allocator); var log = logger.Log.init(arena.backingAllocator()); defer log.deinit(); this.transpiler.setLog(&log); @@ -1155,14 +1141,6 @@ pub fn scanImports(this: *JSTranspiler, globalThis: *JSC.JSGlobalObject, callfra } opts.macro_context = &this.transpiler.macro_context.?; - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - - defer { - JSAst.Stmt.Data.Store.reset(); - JSAst.Expr.Data.Store.reset(); - } - transpiler.resolver.caches.js.scan( transpiler.allocator, &this.scan_pass_result, diff --git a/src/js_ast.zig b/src/js_ast.zig index 3c8c1abdf1..9f50df2886 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -119,7 +119,7 @@ pub fn NewStore(comptime types: []const type, comptime count: usize) type { log("deinit", .{}); var it = store.firstBlock().next; // do not free `store.head` while (it) |next| { - if (Environment.isDebug) + if (Environment.isDebug or Environment.enable_asan) @memset(next.buffer, undefined); it = next.next; backing_allocator.destroy(next); @@ -133,7 +133,7 @@ pub fn NewStore(comptime types: []const type, comptime count: usize) type { pub fn reset(store: *Store) void { log("reset", .{}); - if (Environment.isDebug) { + if (Environment.isDebug or Environment.enable_asan) { var it: ?*Block = store.firstBlock(); while (it) |next| : (it = next.next) { next.bytes_used = undefined; @@ -3214,6 +3214,18 @@ pub const Stmt = struct { instance = StoreType.init(); } + /// create || reset + pub fn begin() void { + if (memory_allocator != null) return; + if (instance == null) { + create(); + return; + } + + if (!disable_reset) + instance.?.reset(); + } + pub fn reset() void { if (disable_reset or memory_allocator != null) return; instance.?.reset(); @@ -6322,12 +6334,24 @@ pub const Expr = struct { } pub inline fn assert() void { - if (comptime Environment.allow_assert) { + if (comptime Environment.isDebug or Environment.enable_asan) { if (instance == null and memory_allocator == null) bun.unreachablePanic("Store must be init'd", .{}); } } + /// create || reset + pub fn begin() void { + if (memory_allocator != null) return; + if (instance == null) { + create(); + return; + } + + if (!disable_reset) + instance.?.reset(); + } + pub fn append(comptime T: type, value: T) *T { if (memory_allocator) |allocator| { return allocator.append(T, value); @@ -8554,6 +8578,48 @@ pub const ASTMemoryAllocator = struct { allocator: std.mem.Allocator, previous: ?*ASTMemoryAllocator = null, + pub fn enter(this: *ASTMemoryAllocator, allocator: std.mem.Allocator) ASTMemoryAllocator.Scope { + this.allocator = allocator; + this.stack_allocator = SFA{ + .buffer = undefined, + .fallback_allocator = allocator, + .fixed_buffer_allocator = undefined, + }; + this.bump_allocator = this.stack_allocator.get(); + this.previous = null; + var ast_scope = ASTMemoryAllocator.Scope{ + .current = this, + .previous = Stmt.Data.Store.memory_allocator, + }; + ast_scope.enter(); + return ast_scope; + } + pub const Scope = struct { + current: ?*ASTMemoryAllocator = null, + previous: ?*ASTMemoryAllocator = null, + + pub fn enter(this: *@This()) void { + bun.debugAssert(Expr.Data.Store.memory_allocator == Stmt.Data.Store.memory_allocator); + + this.previous = Expr.Data.Store.memory_allocator; + + const current = this.current; + + Expr.Data.Store.memory_allocator = current; + Stmt.Data.Store.memory_allocator = current; + + if (current == null) { + Stmt.Data.Store.begin(); + Expr.Data.Store.begin(); + } + } + + pub fn exit(this: *const @This()) void { + Expr.Data.Store.memory_allocator = this.previous; + Stmt.Data.Store.memory_allocator = this.previous; + } + }; + pub fn reset(this: *ASTMemoryAllocator) void { this.stack_allocator = SFA{ .buffer = undefined, diff --git a/src/watcher/INotifyWatcher.zig b/src/watcher/INotifyWatcher.zig index d5ed05aa80..cba39c4e6c 100644 --- a/src/watcher/INotifyWatcher.zig +++ b/src/watcher/INotifyWatcher.zig @@ -292,7 +292,8 @@ pub fn watchLoopCycle(this: *bun.Watcher) bun.JSC.Maybe(void) { this.mutex.lock(); defer this.mutex.unlock(); if (this.running) { - this.onFileUpdate(this.ctx, all_events[0 .. last_event_index + 1], this.changed_filepaths[0 .. name_off + 1], this.watchlist); + // all_events.len == 0 is checked above, so last_event_index + 1 is safe + this.onFileUpdate(this.ctx, all_events[0 .. last_event_index + 1], this.changed_filepaths[0..name_off], this.watchlist); } else { break; }