diff options
author | 2023-06-13 09:15:05 -0700 | |
---|---|---|
committer | 2023-06-13 09:15:05 -0700 | |
commit | bdb1b7124aec3ca42a13dd13309df4c8e4e3cc64 (patch) | |
tree | 57a7a278699999521f561959204a533ea9906f8e /src | |
parent | b93bdbb124fc7b1b4a09d414158e0107e8d66b92 (diff) | |
download | bun-bdb1b7124aec3ca42a13dd13309df4c8e4e3cc64.tar.gz bun-bdb1b7124aec3ca42a13dd13309df4c8e4e3cc64.tar.zst bun-bdb1b7124aec3ca42a13dd13309df4c8e4e3cc64.zip |
Fix crash in CJS (#3294)bun-v0.6.9
* Fix crash in CJS
* Add std.heap.ArenaAllocator
* Use our arena allocator
* Reduce JS parser memory usage and make HMR faster
* Write some comments
* fix test failure & clean up this code
* Update javascript.zig
* make arena usage safer
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src')
36 files changed, 705 insertions, 272 deletions
diff --git a/src/ArenaAllocator.zig b/src/ArenaAllocator.zig new file mode 100644 index 000000000..31e0cf5b9 --- /dev/null +++ b/src/ArenaAllocator.zig @@ -0,0 +1,287 @@ +/// TODO: delete this once we've upgraded Zig and https://github.com/ziglang/zig/pull/15985 is merged. +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const Allocator = std.mem.Allocator; + +/// This allocator takes an existing allocator, wraps it, and provides an interface +/// where you can allocate without freeing, and then free it all together. +pub const ArenaAllocator = struct { + child_allocator: Allocator, + state: State, + + /// Inner state of ArenaAllocator. Can be stored rather than the entire ArenaAllocator + /// as a memory-saving optimization. + pub const State = struct { + buffer_list: std.SinglyLinkedList(usize) = .{}, + end_index: usize = 0, + + pub fn promote(self: State, child_allocator: Allocator) ArenaAllocator { + return .{ + .child_allocator = child_allocator, + .state = self, + }; + } + }; + + pub fn allocator(self: *ArenaAllocator) Allocator { + return .{ + .ptr = self, + .vtable = &.{ + .alloc = alloc, + .resize = resize, + .free = free, + }, + }; + } + + const BufNode = std.SinglyLinkedList(usize).Node; + + pub fn init(child_allocator: Allocator) ArenaAllocator { + return (State{}).promote(child_allocator); + } + + pub fn deinit(self: ArenaAllocator) void { + // NOTE: When changing this, make sure `reset()` is adjusted accordingly! + + var it = self.state.buffer_list.first; + while (it) |node| { + // this has to occur before the free because the free frees node + const next_it = node.next; + const align_bits = std.math.log2_int(usize, @alignOf(BufNode)); + const alloc_buf = @ptrCast([*]u8, node)[0..node.data]; + self.child_allocator.rawFree(alloc_buf, align_bits, @returnAddress()); + it = next_it; + } + } + + pub const ResetMode = union(enum) { + /// Releases all allocated memory in the arena. + free_all, + /// This will pre-heat the arena for future allocations by allocating a + /// large enough buffer for all previously done allocations. + /// Preheating will speed up the allocation process by invoking the backing allocator + /// less often than before. If `reset()` is used in a loop, this means that after the + /// biggest operation, no memory allocations are performed anymore. + retain_capacity, + /// This is the same as `retain_capacity`, but the memory will be shrunk to + /// this value if it exceeds the limit. + retain_with_limit: usize, + }; + /// Queries the current memory use of this arena. + /// This will **not** include the storage required for internal keeping. + pub fn queryCapacity(self: ArenaAllocator) usize { + var size: usize = 0; + var it = self.state.buffer_list.first; + while (it) |node| : (it = node.next) { + // Compute the actually allocated size excluding the + // linked list node. + size += node.data - @sizeOf(BufNode); + } + return size; + } + /// Resets the arena allocator and frees all allocated memory. + /// + /// `mode` defines how the currently allocated memory is handled. + /// See the variant documentation for `ResetMode` for the effects of each mode. + /// + /// The function will return whether the reset operation was successful or not. + /// If the reallocation failed `false` is returned. The arena will still be fully + /// functional in that case, all memory is released. Future allocations just might + /// be slower. + /// + /// NOTE: If `mode` is `free_mode`, the function will always return `true`. + pub fn reset(self: *ArenaAllocator, mode: ResetMode) bool { + // Some words on the implementation: + // The reset function can be implemented with two basic approaches: + // - Counting how much bytes were allocated since the last reset, and storing that + // information in State. This will make reset fast and alloc only a teeny tiny bit + // slower. + // - Counting how much bytes were allocated by iterating the chunk linked list. This + // will make reset slower, but alloc() keeps the same speed when reset() as if reset() + // would not exist. + // + // The second variant was chosen for implementation, as with more and more calls to reset(), + // the function will get faster and faster. At one point, the complexity of the function + // will drop to amortized O(1), as we're only ever having a single chunk that will not be + // reallocated, and we're not even touching the backing allocator anymore. + // + // Thus, only the first hand full of calls to reset() will actually need to iterate the linked + // list, all future calls are just taking the first node, and only resetting the `end_index` + // value. + const requested_capacity = switch (mode) { + .retain_capacity => self.queryCapacity(), + .retain_with_limit => |limit| std.math.min(limit, self.queryCapacity()), + .free_all => 0, + }; + if (requested_capacity == 0) { + // just reset when we don't have anything to reallocate + self.deinit(); + self.state = State{}; + return true; + } + const total_size = requested_capacity + @sizeOf(BufNode); + const align_bits = std.math.log2_int(usize, @alignOf(BufNode)); + // Free all nodes except for the last one + var it = self.state.buffer_list.first; + const maybe_first_node = while (it) |node| { + // this has to occur before the free because the free frees node + const next_it = node.next; + if (next_it == null) + break node; + const alloc_buf = @ptrCast([*]u8, node)[0..node.data]; + self.child_allocator.rawFree(alloc_buf, align_bits, @returnAddress()); + it = next_it; + } else null; + std.debug.assert(maybe_first_node == null or maybe_first_node.?.next == null); + // reset the state before we try resizing the buffers, so we definitely have reset the arena to 0. + self.state.end_index = 0; + if (maybe_first_node) |first_node| { + self.state.buffer_list.first = first_node; + // perfect, no need to invoke the child_allocator + if (first_node.data == total_size) + return true; + const first_alloc_buf = @ptrCast([*]u8, first_node)[0..first_node.data]; + if (self.child_allocator.rawResize(first_alloc_buf, align_bits, total_size, @returnAddress())) { + // successful resize + first_node.data = total_size; + } else { + // manual realloc + const new_ptr = self.child_allocator.rawAlloc(total_size, align_bits, @returnAddress()) orelse { + // we failed to preheat the arena properly, signal this to the user. + return false; + }; + self.child_allocator.rawFree(first_alloc_buf, align_bits, @returnAddress()); + const node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), new_ptr)); + node.* = .{ .data = total_size }; + self.state.buffer_list.first = node; + } + } + return true; + } + + fn createNode(self: *ArenaAllocator, prev_len: usize, minimum_size: usize) ?*BufNode { + const actual_min_size = minimum_size + (@sizeOf(BufNode) + 16); + const big_enough_len = prev_len + actual_min_size; + const len = big_enough_len + big_enough_len / 2; + const log2_align = comptime std.math.log2_int(usize, @alignOf(BufNode)); + const ptr = self.child_allocator.rawAlloc(len, log2_align, @returnAddress()) orelse + return null; + const buf_node = @ptrCast(*BufNode, @alignCast(@alignOf(BufNode), ptr)); + buf_node.* = .{ .data = len }; + self.state.buffer_list.prepend(buf_node); + self.state.end_index = 0; + return buf_node; + } + + fn alloc(ctx: *anyopaque, n: usize, log2_ptr_align: u8, ra: usize) ?[*]u8 { + const self = @ptrCast(*ArenaAllocator, @alignCast(@alignOf(ArenaAllocator), ctx)); + _ = ra; + + const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align); + var cur_node = if (self.state.buffer_list.first) |first_node| + first_node + else + (self.createNode(0, n + ptr_align) orelse return null); + while (true) { + const cur_alloc_buf = @ptrCast([*]u8, cur_node)[0..cur_node.data]; + const cur_buf = cur_alloc_buf[@sizeOf(BufNode)..]; + const addr = @ptrToInt(cur_buf.ptr) + self.state.end_index; + const adjusted_addr = mem.alignForward(addr, ptr_align); + const adjusted_index = self.state.end_index + (adjusted_addr - addr); + const new_end_index = adjusted_index + n; + + if (new_end_index <= cur_buf.len) { + const result = cur_buf[adjusted_index..new_end_index]; + self.state.end_index = new_end_index; + return result.ptr; + } + + const bigger_buf_size = @sizeOf(BufNode) + new_end_index; + const log2_align = comptime std.math.log2_int(usize, @alignOf(BufNode)); + if (self.child_allocator.rawResize(cur_alloc_buf, log2_align, bigger_buf_size, @returnAddress())) { + cur_node.data = bigger_buf_size; + } else { + // Allocate a new node if that's not possible + cur_node = self.createNode(cur_buf.len, n + ptr_align) orelse return null; + } + } + } + + fn resize(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, new_len: usize, ret_addr: usize) bool { + const self = @ptrCast(*ArenaAllocator, @alignCast(@alignOf(ArenaAllocator), ctx)); + _ = log2_buf_align; + _ = ret_addr; + + const cur_node = self.state.buffer_list.first orelse return false; + const cur_buf = @ptrCast([*]u8, cur_node)[@sizeOf(BufNode)..cur_node.data]; + if (@ptrToInt(cur_buf.ptr) + self.state.end_index != @ptrToInt(buf.ptr) + buf.len) { + // It's not the most recent allocation, so it cannot be expanded, + // but it's fine if they want to make it smaller. + return new_len <= buf.len; + } + + if (buf.len >= new_len) { + self.state.end_index -= buf.len - new_len; + return true; + } else if (cur_buf.len - self.state.end_index >= new_len - buf.len) { + self.state.end_index += new_len - buf.len; + return true; + } else { + return false; + } + } + + fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void { + _ = log2_buf_align; + _ = ret_addr; + + const self = @ptrCast(*ArenaAllocator, @alignCast(@alignOf(ArenaAllocator), ctx)); + + const cur_node = self.state.buffer_list.first orelse return; + const cur_buf = @ptrCast([*]u8, cur_node)[@sizeOf(BufNode)..cur_node.data]; + + if (@ptrToInt(cur_buf.ptr) + self.state.end_index == @ptrToInt(buf.ptr) + buf.len) { + self.state.end_index -= buf.len; + } + } +}; + +test "ArenaAllocator (reset with preheating)" { + var arena_allocator = ArenaAllocator.init(std.testing.allocator); + defer arena_allocator.deinit(); + // provides some variance in the allocated data + var rng_src = std.rand.DefaultPrng.init(19930913); + const random = rng_src.random(); + var rounds: usize = 25; + while (rounds > 0) { + rounds -= 1; + _ = arena_allocator.reset(.retain_capacity); + var alloced_bytes: usize = 0; + var total_size: usize = random.intRangeAtMost(usize, 256, 16384); + while (alloced_bytes < total_size) { + const size = random.intRangeAtMost(usize, 16, 256); + const alignment = 32; + const slice = try arena_allocator.allocator().alignedAlloc(u8, alignment, size); + try std.testing.expect(std.mem.isAligned(@ptrToInt(slice.ptr), alignment)); + try std.testing.expectEqual(size, slice.len); + alloced_bytes += slice.len; + } + } +} + +test "ArenaAllocator (reset while retaining a buffer)" { + var arena_allocator = ArenaAllocator.init(std.testing.allocator); + defer arena_allocator.deinit(); + const a = arena_allocator.allocator(); + + // Create two internal buffers + _ = try a.alloc(u8, 1); + _ = try a.alloc(u8, 1000); + + // Check that we have at least two buffers + try std.testing.expect(arena_allocator.state.buffer_list.first.?.next != null); + + // This retains the first allocated buffer + try std.testing.expect(arena_allocator.reset(.{ .retain_with_limit = 1 })); +} diff --git a/src/bun.js/api/JSTranspiler.zig b/src/bun.js/api/JSTranspiler.zig index 83e3b741f..4cfc02e94 100644 --- a/src/bun.js/api/JSTranspiler.zig +++ b/src/bun.js/api/JSTranspiler.zig @@ -46,7 +46,7 @@ const Expr = JSAst.Expr; pub usingnamespace JSC.Codegen.JSTranspiler; bundler: Bundler.Bundler, -arena: std.heap.ArenaAllocator, +arena: @import("root").bun.ArenaAllocator, transpiler_options: TranspilerOptions, scan_pass_result: ScanPassResult, buffer_writer: ?JSPrinter.BufferWriter = null, @@ -726,7 +726,7 @@ pub fn constructor( globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, ) callconv(.C) ?*Transpiler { - var temp = std.heap.ArenaAllocator.init(getAllocator(globalThis)); + var temp = @import("root").bun.ArenaAllocator.init(getAllocator(globalThis)); const arguments = callframe.arguments(3); var args = JSC.Node.ArgumentsSlice.init( globalThis.bunVM(), diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index 380ca9cbf..d3ef6919a 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -3602,7 +3602,7 @@ pub const TOML = struct { arguments: []const js.JSValueRef, exception: js.ExceptionRef, ) js.JSValueRef { - var arena = std.heap.ArenaAllocator.init(getAllocator(ctx)); + var arena = @import("root").bun.ArenaAllocator.init(getAllocator(ctx)); var allocator = arena.allocator(); defer arena.deinit(); var log = logger.Log.init(default_allocator); diff --git a/src/bun.js/api/bun/dns_resolver.zig b/src/bun.js/api/bun/dns_resolver.zig index bec7e4d64..82411701d 100644 --- a/src/bun.js/api/bun/dns_resolver.zig +++ b/src/bun.js/api/bun/dns_resolver.zig @@ -241,7 +241,7 @@ pub fn addrInfoToJSArray( globalThis: *JSC.JSGlobalObject, ) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); const array = JSC.JSValue.createEmptyArray( globalThis, addrInfoCount(addr_info), @@ -553,7 +553,7 @@ pub const GetAddrInfo = struct { .addrinfo => |addrinfo| addrInfoToJSArray(globalThis.allocator(), addrinfo orelse return null, globalThis), .list => |list| brk: { var stack = std.heap.stackFallback(2048, globalThis.allocator()); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); const array = JSC.JSValue.createEmptyArray(globalThis, @truncate(u32, list.items.len)); var i: u32 = 0; const items: []const Result = list.items; diff --git a/src/bun.js/api/bun/subprocess.zig b/src/bun.js/api/bun/subprocess.zig index a996f863b..832afac78 100644 --- a/src/bun.js/api/bun/subprocess.zig +++ b/src/bun.js/api/bun/subprocess.zig @@ -1031,7 +1031,7 @@ pub const Subprocess = struct { secondaryArgsValue: ?JSValue, comptime is_sync: bool, ) JSValue { - var arena = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena = @import("root").bun.ArenaAllocator.init(bun.default_allocator); defer arena.deinit(); var allocator = arena.allocator(); diff --git a/src/bun.js/api/filesystem_router.zig b/src/bun.js/api/filesystem_router.zig index d926ca7b3..216f66b7f 100644 --- a/src/bun.js/api/filesystem_router.zig +++ b/src/bun.js/api/filesystem_router.zig @@ -153,7 +153,7 @@ pub const FileSystemRouter = struct { origin: ?*JSC.RefString = null, base_dir: ?*JSC.RefString = null, router: Router, - arena: *std.heap.ArenaAllocator = undefined, + arena: *@import("root").bun.ArenaAllocator = undefined, allocator: std.mem.Allocator = undefined, asset_prefix: ?*JSC.RefString = null, @@ -210,8 +210,8 @@ pub const FileSystemRouter = struct { globalThis.throwInvalidArguments("Expected dir to be a string", .{}); return null; } - var arena = globalThis.allocator().create(std.heap.ArenaAllocator) catch unreachable; - arena.* = std.heap.ArenaAllocator.init(globalThis.allocator()); + var arena = globalThis.allocator().create(@import("root").bun.ArenaAllocator) catch unreachable; + arena.* = @import("root").bun.ArenaAllocator.init(globalThis.allocator()); var allocator = arena.allocator(); var extensions = std.ArrayList(string).init(allocator); if (argument.get(globalThis, "fileExtensions")) |file_extensions| { @@ -324,8 +324,8 @@ pub const FileSystemRouter = struct { pub fn reload(this: *FileSystemRouter, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { var this_value = callframe.this(); - var arena = globalThis.allocator().create(std.heap.ArenaAllocator) catch unreachable; - arena.* = std.heap.ArenaAllocator.init(globalThis.allocator()); + var arena = globalThis.allocator().create(@import("root").bun.ArenaAllocator) catch unreachable; + arena.* = @import("root").bun.ArenaAllocator.init(globalThis.allocator()); var allocator = arena.allocator(); var vm = globalThis.bunVM(); diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 0f967785e..8fd9acde7 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -296,7 +296,7 @@ pub const ServerConfig = struct { var i: u32 = 0; var valid_count: u32 = 0; - var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator); while (i < count) : (i += 1) { const item = js_obj.getIndex(global, i); if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item, exception)) |sb| { @@ -351,7 +351,7 @@ pub const ServerConfig = struct { } } else { const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable; - var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator); if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj, exception)) |sb| { const sliced = sb.slice(); if (sliced.len > 0) { @@ -398,7 +398,7 @@ pub const ServerConfig = struct { var i: u32 = 0; var valid_count: u32 = 0; - var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator); while (i < count) : (i += 1) { const item = js_obj.getIndex(global, i); if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item, exception)) |sb| { @@ -453,7 +453,7 @@ pub const ServerConfig = struct { } } else { const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable; - var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator); if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj, exception)) |sb| { const sliced = sb.slice(); if (sliced.len > 0) { @@ -513,7 +513,7 @@ pub const ServerConfig = struct { var i: u32 = 0; var valid_count: u32 = 0; - var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator); while (i < count) : (i += 1) { const item = js_obj.getIndex(global, i); if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), item, exception)) |sb| { @@ -568,7 +568,7 @@ pub const ServerConfig = struct { } } else { const native_array = bun.default_allocator.alloc([*c]const u8, 1) catch unreachable; - var arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator); + var arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator); if (JSC.Node.StringOrBuffer.fromJS(global, arena.allocator(), js_obj, exception)) |sb| { const sliced = sb.slice(); if (sliced.len > 0) { diff --git a/src/bun.js/base.zig b/src/bun.js/base.zig index cd0684398..4e3e83bd7 100644 --- a/src/bun.js/base.zig +++ b/src/bun.js/base.zig @@ -2042,8 +2042,8 @@ pub const RefString = struct { ptr: [*]const u8 = undefined, len: usize = 0, hash: Hash = 0, + impl: bun.WTF.StringImpl, - count: u32 = 0, allocator: std.mem.Allocator, ctx: ?*anyopaque = null, @@ -2053,17 +2053,13 @@ pub const RefString = struct { pub const Map = std.HashMap(Hash, *JSC.RefString, IdentityContext(Hash), 80); pub fn toJS(this: *RefString, global: *JSC.JSGlobalObject) JSValue { - return JSC.ZigString.init(this.slice()).external(global, this, RefString__external); + return bun.String.init(this.impl).toJS(global); } pub const Callback = fn (ctx: *anyopaque, str: *RefString) void; pub fn computeHash(input: []const u8) u32 { - return @truncate(u32, std.hash.Wyhash.hash(0, input)); - } - - pub fn ref(this: *RefString) void { - this.count += 1; + return std.hash.XxHash32.hash(input); } pub fn slice(this: *RefString) []const u8 { @@ -2072,25 +2068,21 @@ pub const RefString = struct { return this.leak(); } + pub fn ref(this: *RefString) void { + this.impl.ref(); + } + pub fn leak(this: RefString) []const u8 { @setRuntimeSafety(false); return this.ptr[0..this.len]; } pub fn deref(this: *RefString) void { - this.count -|= 1; - - if (this.count == 0) { - this.deinit(); - } - } - - pub export fn RefString__free(this: *RefString, _: [*]const u8, _: usize) void { - this.deref(); + this.impl.deref(); } - pub export fn RefString__external(this: ?*anyopaque, _: ?*anyopaque, _: usize) void { - bun.cast(*RefString, this.?).deref(); + pub export fn RefString__free(this: *anyopaque, _: *anyopaque, _: u32) void { + bun.cast(*RefString, this).deinit(); } pub fn deinit(this: *RefString) void { diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 797f66545..249f68435 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -2,7 +2,8 @@ #include "headers-handwritten.h" #include "JavaScriptCore/JSCJSValueInlines.h" #include "helpers.h" - +#include "simdutf.h" +#include "wtf/text/ExternalStringImpl.h" using namespace JSC; extern "C" void Bun__WTFStringImpl__deref(WTF::StringImpl* impl) @@ -124,6 +125,30 @@ extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject return JSValue::encode(Bun::toJS(globalObject, *bunString)); } +extern "C" BunString BunString__fromLatin1(const char* bytes, size_t length) +{ + return { BunStringTag::WTFStringImpl, { .wtf = &WTF::StringImpl::create(bytes, length).leakRef() } }; +} + +extern "C" BunString BunString__fromBytes(const char* bytes, size_t length) +{ + if (simdutf::validate_ascii(bytes, length)) { + return BunString__fromLatin1(bytes, length); + } + + auto str = WTF::String::fromUTF8ReplacingInvalidSequences(reinterpret_cast<const LChar*>(bytes), length); + return Bun::fromString(str); +} + +extern "C" BunString BunString__createExternal(const char* bytes, size_t length, bool isLatin1, void* ctx, void (*callback)(void* arg0, void* arg1, size_t arg2)) +{ + Ref<WTF::ExternalStringImpl> impl = isLatin1 ? WTF::ExternalStringImpl::create(reinterpret_cast<const LChar*>(bytes), length, ctx, callback) : + + WTF::ExternalStringImpl::create(reinterpret_cast<const UChar*>(bytes), length, ctx, callback); + + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + extern "C" void BunString__toWTFString(BunString* bunString) { if (bunString->tag == BunStringTag::ZigString) { diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp index fcf3dfe8c..31c24bb66 100644 --- a/src/bun.js/bindings/CommonJSModuleRecord.cpp +++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp @@ -62,6 +62,8 @@ #include <JavaScriptCore/JSMap.h> #include <JavaScriptCore/JSMapInlines.h> +#include <JavaScriptCore/GetterSetter.h> +#include "ZigSourceProvider.h" namespace Bun { using namespace JSC; @@ -76,13 +78,15 @@ public: mutable JSC::WriteBarrier<JSC::Unknown> m_exportsObject; mutable JSC::WriteBarrier<JSC::JSString> m_id; + mutable JSC::WriteBarrier<JSC::EvalExecutable> m_executable; - void finishCreation(JSC::VM& vm, JSC::JSValue exportsObject, JSC::JSString* id) + void finishCreation(JSC::VM& vm, JSC::JSValue exportsObject, JSC::JSString* id, JSC::JSString* filename, JSC::JSValue requireFunction, JSC::EvalExecutable* executable) { Base::finishCreation(vm); ASSERT(inherits(vm, info())); m_exportsObject.set(vm, this, exportsObject); m_id.set(vm, this, id); + m_executable.set(vm, this, executable); this->putDirectOffset( vm, @@ -98,16 +102,27 @@ public: vm, 2, id); + this->putDirectOffset( + vm, + 3, + filename); + this->putDirectOffset( + vm, + 4, + requireFunction); } static JSCommonJSModule* create( JSC::VM& vm, JSC::Structure* structure, JSC::JSValue exportsObject, - JSC::JSString* id) + JSC::JSString* id, + JSC::JSString* filename, + JSC::JSValue requireFunction, + JSC::EvalExecutable* executable) { JSCommonJSModule* cell = new (NotNull, JSC::allocateCell<JSCommonJSModule>(vm)) JSCommonJSModule(vm, structure); - cell->finishCreation(vm, exportsObject, id); + cell->finishCreation(vm, exportsObject, id, filename, requireFunction, executable); return cell; } @@ -240,6 +255,7 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor) Base::visitChildren(thisObject, visitor); visitor.append(thisObject->m_exportsObject); visitor.append(thisObject->m_id); + visitor.append(thisObject->m_executable); } DEFINE_VISIT_CHILDREN(JSCommonJSModule); @@ -250,7 +266,9 @@ JSCommonJSModule* createCommonJSModuleObject( const ResolvedSource& source, const WTF::String& sourceURL, JSC::JSValue exportsObjectValue, - JSC::JSValue requireFunctionValue) + JSC::JSValue requireFunctionValue, + JSC::EvalExecutable* executable, + JSC::JSString* filename) { auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); @@ -260,12 +278,7 @@ JSCommonJSModule* createCommonJSModuleObject( vm, globalObject->CommonJSModuleObjectStructure(), exportsObjectValue, - jsSourceURL); - - moduleObject->putDirectOffset( - vm, - 3, - requireFunctionValue); + jsSourceURL, filename, requireFunctionValue, executable); return moduleObject; } @@ -291,25 +304,24 @@ JSC::SourceCode createCommonJSModule( Zig::GlobalObject* globalObject, ResolvedSource source) { - auto sourceURL = Zig::toStringCopy(source.source_url); + auto sourceProvider = Zig::SourceProvider::create(globalObject, source, JSC::SourceProviderSourceType::Program); return JSC::SourceCode( JSC::SyntheticSourceProvider::create( - [source, sourceURL](JSC::JSGlobalObject* lexicalGlobalObject, + [source, sourceProvider = WTFMove(sourceProvider), sourceURL](JSC::JSGlobalObject* lexicalGlobalObject, JSC::Identifier moduleKey, Vector<JSC::Identifier, 4>& exportNames, JSC::MarkedArgumentBuffer& exportValues) -> void { - Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject); + auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); auto& vm = globalObject->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); - auto sourceCodeString = Zig::toString(source.source_code); auto* requireMapKey = jsString(vm, sourceURL); JSC::JSObject* exportsObject = source.commonJSExportsLen < 64 ? JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), source.commonJSExportsLen) : JSC::constructEmptyObject(globalObject, globalObject->objectPrototype()); - auto index = sourceURL.reverseFind('/', sourceURL.length()); JSString* dirname = jsEmptyString(vm); JSString* filename = requireMapKey; @@ -318,11 +330,8 @@ JSC::SourceCode createCommonJSModule( } globalObject->requireMap()->set(globalObject, requireMapKey, exportsObject); - JSC::SourceCode inputSource( - JSC::StringSourceProvider::create(sourceCodeString, - JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(sourceURL)), - sourceURL, TextPosition())); + WTFMove(sourceProvider)); JSC::Structure* scopeExtensionObjectStructure = globalObject->commonJSFunctionArgumentsStructure(); JSC::JSObject* scopeExtensionObject = JSC::constructEmptyObject( @@ -330,12 +339,27 @@ JSC::SourceCode createCommonJSModule( scopeExtensionObjectStructure); auto* requireFunction = Zig::ImportMetaObject::createRequireFunction(vm, globalObject, sourceURL); + auto* executable = JSC::DirectEvalExecutable::create( + globalObject, inputSource, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, + false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy()); + + if (UNLIKELY(!executable && !throwScope.exception())) { + // I'm not sure if this case happens, but it's better to be safe than sorry. + throwSyntaxError(globalObject, throwScope, "Failed to compile CommonJS module."_s); + } + + if (UNLIKELY(throwScope.exception())) { + globalObject->requireMap()->remove(globalObject, requireMapKey); + throwScope.release(); + return; + } auto* moduleObject = createCommonJSModuleObject(globalObject, source, sourceURL, exportsObject, - requireFunction); + requireFunction, executable, filename); + scopeExtensionObject->putDirectOffset( vm, 0, @@ -361,15 +385,6 @@ JSC::SourceCode createCommonJSModule( 4, requireFunction); - auto* executable = JSC::DirectEvalExecutable::create( - globalObject, inputSource, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None, - false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy()); - - if (UNLIKELY(!executable && !throwScope.exception())) { - // I'm not sure if this case happens, but it's better to be safe than sorry. - throwSyntaxError(globalObject, throwScope, "Failed to compile CommonJS module."_s); - } - if (UNLIKELY(throwScope.exception())) { globalObject->requireMap()->remove(globalObject, requireMapKey); throwScope.release(); @@ -432,7 +447,12 @@ JSC::SourceCode createCommonJSModule( // - Object.defineProperty(module, 'exports', {get: getter}) // - delete module.exports // - result = moduleObject->getIfPropertyExists(globalObject, clientData->builtinNames().exportsPublicName()); + if (result.isGetterSetter()) { + JSC::GetterSetter* getter = jsCast<JSC::GetterSetter*>(result); + result = getter->callGetter(globalObject, moduleObject); + } else { + result = moduleObject->getIfPropertyExists(globalObject, clientData->builtinNames().exportsPublicName()); + } if (UNLIKELY(throwScope.exception())) { // Unlike getters on properties of the exports object @@ -454,17 +474,15 @@ JSC::SourceCode createCommonJSModule( exportNames.append(Identifier::fromUid(vm.symbolRegistry().symbolForKey("CommonJS"_s))); exportValues.append(jsNumber(0)); - // This strong reference exists because otherwise it will crash when the finalizer runs. - exportNames.append(Identifier::fromUid(vm.symbolRegistry().symbolForKey("module"_s))); - exportValues.append(moduleObject); + moduleObject->m_executable.clear(); if (result.isObject()) { auto* exports = asObject(result); auto* structure = exports->structure(); uint32_t size = structure->inlineSize() + structure->outOfLineSize(); - exportNames.reserveCapacity(size + 3); - exportValues.ensureCapacity(size + 3); + exportNames.reserveCapacity(size + 2); + exportValues.ensureCapacity(size + 2); if (canPerformFastEnumeration(structure)) { exports->structure()->forEachProperty(vm, [&](const PropertyTableEntry& entry) -> bool { diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 035cd15c8..ed1e5702b 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -282,7 +282,7 @@ static JSValue handleVirtualModuleResult( return reject(JSValue::decode(reinterpret_cast<EncodedJSValue>(res->result.err.ptr))); } - auto provider = Zig::SourceProvider::create(res->result.value); + auto provider = Zig::SourceProvider::create(globalObject, res->result.value); return resolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); } case OnLoadResultTypeError: { @@ -346,7 +346,7 @@ extern "C" void Bun__onFulfillAsyncModule( return promise->reject(promise->globalObject(), exception); } - auto provider = Zig::SourceProvider::create(res->result.value); + auto provider = Zig::SourceProvider::create(jsDynamicCast<Zig::GlobalObject*>(globalObject), res->result.value); promise->resolve(promise->globalObject(), JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); } @@ -457,8 +457,8 @@ static JSValue fetchSourceCode( return rejectOrResolve(JSSourceCode::create(vm, WTFMove(source))); } default: { - auto provider = Zig::SourceProvider::create(res->result.value); - return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider)))); + auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value); + return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); } } } @@ -488,8 +488,8 @@ static JSValue fetchSourceCode( return reject(exception); } - auto provider = Zig::SourceProvider::create(res->result.value); - return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider)))); + auto&& provider = Zig::SourceProvider::create(globalObject, res->result.value); + return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); } extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 8926be6bb..f2f5a372b 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -4044,7 +4044,12 @@ void GlobalObject::reload() registry->clear(this->vm()); this->requireMap()->clear(this->vm()); - this->vm().heap.collectSync(); + + // If we run the GC every time, we will never get the SourceProvider cache hit. + // So we run the GC every other time. + if ((this->reloadCount++ + 1) % 2 == 0) { + this->vm().heap.collectSync(); + } } extern "C" void JSC__JSGlobalObject__reload(JSC__JSGlobalObject* arg0) diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 3cf88bf29..d5f933540 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -388,6 +388,12 @@ public: BunPlugin::OnResolve onResolvePlugins[BunPluginTargetMax + 1] {}; BunPluginTarget defaultBunPluginTarget = BunPluginTargetBun; + // This increases the cache hit rate for JSC::VM's SourceProvider cache + // It also avoids an extra allocation for the SourceProvider + // The key is a pointer to the source code + WTF::HashMap<uintptr_t, Ref<JSC::SourceProvider>> sourceProviderMap; + size_t reloadCount = 0; + void reload(); JSC::Structure* pendingVirtualModuleResultStructure() { return m_pendingVirtualModuleResultStructure.get(this); } diff --git a/src/bun.js/bindings/ZigSourceProvider.cpp b/src/bun.js/bindings/ZigSourceProvider.cpp index 7b3a8ffbc..d42d6b445 100644 --- a/src/bun.js/bindings/ZigSourceProvider.cpp +++ b/src/bun.js/bindings/ZigSourceProvider.cpp @@ -5,6 +5,7 @@ #include "ZigSourceProvider.h" #include "JavaScriptCore/BytecodeCacheError.h" +#include "ZigGlobalObject.h" #include "JavaScriptCore/Completion.h" #include "wtf/Scope.h" @@ -26,67 +27,73 @@ using SourceOrigin = JSC::SourceOrigin; using String = WTF::String; using SourceProviderSourceType = JSC::SourceProviderSourceType; -Ref<SourceProvider> SourceProvider::create(ResolvedSource resolvedSource) +static uintptr_t getSourceProviderMapKey(ResolvedSource& resolvedSource) { - void* allocator = resolvedSource.allocator; + switch (resolvedSource.source_code.tag) { + case BunStringTag::WTFStringImpl: { + return (uintptr_t)resolvedSource.source_code.impl.wtf->characters8(); + } + case BunStringTag::StaticZigString: + case BunStringTag::ZigString: { + return (uintptr_t)Zig::untag(resolvedSource.source_code.impl.zig.ptr); + } + default: { + return 0; + } + } +} - JSC::SourceProviderSourceType sourceType = JSC::SourceProviderSourceType::Module; +Ref<SourceProvider> SourceProvider::create(Zig::GlobalObject* globalObject, ResolvedSource resolvedSource, JSC::SourceProviderSourceType sourceType) +{ - // // JSC owns the memory - // if (resolvedSource.hash == 1) { - // return adoptRef(*new SourceProvider( - // resolvedSource, WTF::StringImpl::create(resolvedSource.source_code.ptr, resolvedSource.source_code.len), - // JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))), - // toStringNotConst(resolvedSource.source_url).isolatedCopy(), TextPosition(), - // sourceType)); - // } + uintptr_t providerKey = 0; + if (globalObject->isThreadLocalDefaultGlobalObject) { + auto& sourceProviderMap = globalObject->sourceProviderMap; + providerKey = getSourceProviderMapKey(resolvedSource); + if (providerKey) { + auto sourceProvider = sourceProviderMap.get(providerKey); + if (sourceProvider != nullptr) { + sourceProvider->ref(); + return adoptRef(*reinterpret_cast<Zig::SourceProvider*>(sourceProvider)); + } + } + } + auto stringImpl = Bun::toWTFString(resolvedSource.source_code); + + if (stringImpl.impl()->refCount() > 1) + // Deref because we don't call a destructor for BunString + stringImpl.impl()->deref(); - if (allocator) { - Ref<WTF::ExternalStringImpl> stringImpl_ = WTF::ExternalStringImpl::create( - resolvedSource.source_code.ptr, resolvedSource.source_code.len, - allocator, - RefString__free); - return adoptRef(*new SourceProvider( - resolvedSource, stringImpl_, - JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))), - toStringNotConst(resolvedSource.source_url), TextPosition(), - sourceType)); - } else { - Ref<WTF::ExternalStringImpl> stringImpl_ = WTF::ExternalStringImpl::createStatic( - resolvedSource.source_code.ptr, resolvedSource.source_code.len); - return adoptRef(*new SourceProvider( - resolvedSource, stringImpl_, - JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))), - toStringNotConst(resolvedSource.source_url), TextPosition(), - sourceType)); + auto provider = adoptRef(*new SourceProvider( + globalObject->isThreadLocalDefaultGlobalObject ? globalObject : nullptr, + resolvedSource, stringImpl.releaseImpl().releaseNonNull(), + JSC::SourceOrigin(WTF::URL::fileURLWithFileSystemPath(toString(resolvedSource.source_url))), + toStringNotConst(resolvedSource.source_url), TextPosition(), + sourceType)); + + if (providerKey) { + globalObject->sourceProviderMap.set(providerKey, provider.copyRef()); } + + return provider; } -unsigned SourceProvider::getHash() +unsigned SourceProvider::hash() const { if (m_hash) { return m_hash; } - m_hash = WTF::StringHash::hash(m_source.get()); - return m_hash; + return m_source->hash(); } void SourceProvider::freeSourceCode() { - if (did_free_source_code) { - return; + if (m_globalObjectForSourceProviderMap) { + m_globalObjectForSourceProviderMap->sourceProviderMap.remove((uintptr_t)m_source.get().characters8()); } - did_free_source_code = true; - if (m_resolvedSource.allocator != 0) { // // WTF::ExternalStringImpl::destroy(m_source.ptr()); - this->m_source = WTF::StringImpl::empty()->isolatedCopy(); - this->m_hash = 0; - m_resolvedSource.allocator = 0; - } - // if (m_resolvedSource.allocator != 0) { - // ZigString__free(m_resolvedSource.source_code.ptr, m_resolvedSource.source_code.len, - // m_resolvedSource.allocator); - // } + + m_source = *WTF::StringImpl::empty(); } void SourceProvider::updateCache(const UnlinkedFunctionExecutable* executable, const SourceCode&, diff --git a/src/bun.js/bindings/ZigSourceProvider.h b/src/bun.js/bindings/ZigSourceProvider.h index 5219bd2c1..dd78b20ae 100644 --- a/src/bun.js/bindings/ZigSourceProvider.h +++ b/src/bun.js/bindings/ZigSourceProvider.h @@ -20,6 +20,8 @@ class SourceProvider; namespace Zig { +class GlobalObject; + class SourceProvider final : public JSC::SourceProvider { WTF_MAKE_FAST_ALLOCATED; using Base = JSC::SourceProvider; @@ -32,7 +34,7 @@ class SourceProvider final : public JSC::SourceProvider { using SourceOrigin = JSC::SourceOrigin; public: - static Ref<SourceProvider> create(ResolvedSource resolvedSource); + static Ref<SourceProvider> create(Zig::GlobalObject*, ResolvedSource resolvedSource, JSC::SourceProviderSourceType sourceType = JSC::SourceProviderSourceType::Module); ~SourceProvider() { freeSourceCode(); @@ -40,8 +42,8 @@ public: commitCachedBytecode(); } - unsigned hash() const { return m_hash; }; - StringView source() const { return StringView(m_source.get()); } + unsigned hash() const override; + StringView source() const override { return StringView(m_source.get()); } RefPtr<JSC::CachedBytecode> cachedBytecode() { // if (m_resolvedSource.bytecodecache_fd == 0) { @@ -62,7 +64,7 @@ public: void freeSourceCode(); private: - SourceProvider(ResolvedSource resolvedSource, WTF::StringImpl& sourceImpl, + SourceProvider(Zig::GlobalObject* globalObject, ResolvedSource resolvedSource, Ref<WTF::StringImpl>&& sourceImpl, const SourceOrigin& sourceOrigin, WTF::String&& sourceURL, const TextPosition& startPosition, JSC::SourceProviderSourceType sourceType) : Base(sourceOrigin, WTFMove(sourceURL), String(), startPosition, sourceType) @@ -70,17 +72,14 @@ private: { m_resolvedSource = resolvedSource; - - m_hash = resolvedSource.hash; - - getHash(); } - unsigned m_hash; - unsigned getHash(); RefPtr<JSC::CachedBytecode> m_cachedBytecode; Ref<WTF::StringImpl> m_source; bool did_free_source_code = false; + Zig::GlobalObject* m_globalObjectForSourceProviderMap; + unsigned m_hash; + // JSC::SourceCodeKey key; }; diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index e48acb8c4..6ea1eba60 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -203,7 +203,7 @@ pub const ResolvedSource = extern struct { pub const namespace = shim.namespace; specifier: bun.String, - source_code: ZigString, + source_code: bun.String, source_url: ZigString, commonjs_exports: ?[*]ZigString = null, commonjs_exports_len: u32 = 0, diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 9a8cb1ef3..b845c4e00 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -64,7 +64,7 @@ typedef struct ErrorableString { } ErrorableString; typedef struct ResolvedSource { BunString specifier; - ZigString source_code; + BunString source_code; ZigString source_url; ZigString* commonJSExports; uint32_t commonJSExportsLen; diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 5bd066a9a..63d024ad7 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -449,6 +449,8 @@ pub const VirtualMachine = struct { modules: ModuleLoader.AsyncModule.Queue = .{}, aggressive_garbage_collection: GCLevel = GCLevel.none, + parser_arena: ?@import("root").bun.ArenaAllocator = null, + gc_controller: JSC.GarbageCollectionController = .{}, pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; @@ -780,6 +782,7 @@ pub const VirtualMachine = struct { .ref_strings = JSC.RefString.Map.init(allocator), .file_blobs = JSC.WebCore.Blob.Store.Map.init(allocator), .standalone_module_graph = graph, + .parser_arena = @import("root").bun.ArenaAllocator.init(allocator), }; vm.source_mappings = .{ .map = &vm.saved_source_map_table }; vm.regular_event_loop.tasks = EventLoop.Queue.init( @@ -876,6 +879,7 @@ pub const VirtualMachine = struct { .origin_timestamp = getOriginTimestamp(), .ref_strings = JSC.RefString.Map.init(allocator), .file_blobs = JSC.WebCore.Blob.Store.Map.init(allocator), + .parser_arena = @import("root").bun.ArenaAllocator.init(allocator), }; vm.source_mappings = .{ .map = &vm.saved_source_map_table }; vm.regular_event_loop.tasks = EventLoop.Queue.init( @@ -936,11 +940,15 @@ pub const VirtualMachine = struct { _ = VirtualMachine.get().ref_strings.remove(ref_string.hash); } - pub fn refCountedResolvedSource(this: *VirtualMachine, code: []const u8, specifier: bun.String, source_url: []const u8, hash_: ?u32) ResolvedSource { - var source = this.refCountedString(code, hash_, true); + pub fn refCountedResolvedSource(this: *VirtualMachine, code: []const u8, specifier: bun.String, source_url: []const u8, hash_: ?u32, comptime add_double_ref: bool) ResolvedSource { + var source = this.refCountedString(code, hash_, !add_double_ref); + if (add_double_ref) { + source.ref(); + source.ref(); + } return ResolvedSource{ - .source_code = ZigString.init(source.slice()), + .source_code = bun.String.init(source.impl), .specifier = specifier, .source_url = ZigString.init(source_url), .hash = source.hash, @@ -963,6 +971,7 @@ pub const VirtualMachine = struct { .allocator = this.allocator, .ptr = input.ptr, .len = input.len, + .impl = bun.String.createExternal(input, true, ref, &JSC.RefString.RefString__free).value.WTFStringImpl, .hash = hash, .ctx = this, .onBeforeDeinit = VirtualMachine.clearRefString, @@ -1432,9 +1441,9 @@ pub const VirtualMachine = struct { }; if (vm.has_loaded) { - blobs.temporary.put(specifier_blob, .{ .ptr = result.source_code._unsafe_ptr_do_not_use, .len = result.source_code.len }) catch {}; + blobs.temporary.put(specifier_blob, .{ .ptr = result.source_code.byteSlice().ptr, .len = result.source_code.length() }) catch {}; } else { - blobs.persistent.put(specifier_blob, .{ .ptr = result.source_code._unsafe_ptr_do_not_use, .len = result.source_code.len }) catch {}; + blobs.persistent.put(specifier_blob, .{ .ptr = result.source_code.byteSlice().ptr, .len = result.source_code.length() }) catch {}; } } @@ -2033,7 +2042,9 @@ pub const VirtualMachine = struct { var log = logger.Log.init(default_allocator); var errorable: ErrorableResolvedSource = undefined; var original_source = fetchWithoutOnLoadPlugins(this, this.global, bun.String.init(top.source_url), bun.String.empty, &log, &errorable, .print_source) catch return; - const code = original_source.source_code.slice(); + const code = original_source.source_code.toUTF8(bun.default_allocator); + defer code.deinit(); + top.position.line = mapping.original.lines; top.position.line_start = mapping.original.lines; top.position.line_stop = mapping.original.lines + 1; @@ -2046,7 +2057,7 @@ pub const VirtualMachine = struct { top.position.expression_stop = mapping.original.columns + 1; if (strings.getLinesInText( - code, + code.slice(), @intCast(u32, top.position.line), JSC.ZigException.Holder.source_lines_count, )) |lines| { diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index 4611adea4..d115573be 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -142,7 +142,7 @@ fn jsModuleFromFile(from_path: string, comptime input: string) string { inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag, specifier: String) ResolvedSource { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(""), + .source_code = bun.String.empty, .specifier = specifier, .source_url = ZigString.init(@tagName(name)), .hash = 0, @@ -185,6 +185,7 @@ pub const ModuleLoader = struct { loader: Api.Loader, hash: u32 = std.math.maxInt(u32), globalThis: *JSC.JSGlobalObject = undefined, + arena: bun.ArenaAllocator, // This is the specific state for making it async poll_ref: JSC.PollRef = .{}, @@ -515,6 +516,7 @@ pub const ModuleLoader = struct { // .stmt_blocks = stmt_blocks, // .expr_blocks = expr_blocks, .globalThis = globalObject, + .arena = opts.arena, }; } @@ -809,7 +811,7 @@ pub const ModuleLoader = struct { } if (jsc_vm.isWatcherEnabled()) { - var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, bun.String.init(specifier), path.text, null); + var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, bun.String.init(specifier), path.text, null, false); if (parse_result.input_fd) |fd_| { if (jsc_vm.bun_watcher != null and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { @@ -841,7 +843,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), + .source_code = bun.String.createLatin1(printer.ctx.getWritten()), .specifier = String.init(specifier), .source_url = ZigString.init(path.text), .commonjs_exports = if (commonjs_exports.len > 0) @@ -863,10 +865,12 @@ pub const ModuleLoader = struct { } pub fn deinit(this: *AsyncModule) void { + this.promise.deinit(); this.parse_result.deinit(); + this.arena.deinit(); // bun.default_allocator.free(this.stmt_blocks); // bun.default_allocator.free(this.expr_blocks); - this.promise.deinit(); + bun.default_allocator.free(this.string_buf); } @@ -914,7 +918,32 @@ pub const ModuleLoader = struct { jsc_vm.bundler.resetStore(); const hash = http.Watcher.getHash(path.text); - var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; + var arena: bun.ArenaAllocator = undefined; + + // Attempt to reuse the Arena from the parser when we can + // This code is potentially re-entrant, so only one Arena can be reused at a time + // That's why we have to check if the Arena is null + // + // Using an Arena here is a significant memory optimization when loading many files + if (jsc_vm.parser_arena) |shared| { + arena = shared; + jsc_vm.parser_arena = null; + _ = arena.reset(.retain_capacity); + } else { + arena = bun.ArenaAllocator.init(jsc_vm.allocator); + } + var give_back_arena = true; + defer { + if (give_back_arena) { + if (jsc_vm.parser_arena == null) { + jsc_vm.parser_arena = arena; + } else { + arena.deinit(); + } + } + } + + var allocator = arena.allocator(); var fd: ?StoredFileDescriptorType = null; var package_json: ?*PackageJSON = null; @@ -1014,7 +1043,7 @@ pub const ModuleLoader = struct { }; if (parse_result.loader == .wasm) { - const wasm_result = transpileSourceCode( + return transpileSourceCode( jsc_vm, specifier, display_specifier, @@ -1030,7 +1059,6 @@ pub const ModuleLoader = struct { globalObject, flags, ); - return wasm_result; } if (comptime !disable_transpilying) { @@ -1059,8 +1087,8 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, .source_code = switch (comptime flags) { - .print_source_and_clone => ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), - .print_source => ZigString.init(parse_result.source.contents), + .print_source_and_clone => bun.String.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), + .print_source => bun.String.static(parse_result.source.contents), else => unreachable, }, .specifier = input_specifier, @@ -1072,7 +1100,7 @@ pub const ModuleLoader = struct { if (parse_result.already_bundled) { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(try default_allocator.dupe(u8, parse_result.source.contents)), + .source_code = bun.String.createLatin1(parse_result.source.contents), .specifier = input_specifier, .source_url = ZigString.init(path.text), // // TODO: change hash to a bitfield @@ -1126,8 +1154,11 @@ pub const ModuleLoader = struct { .promise_ptr = promise_ptr, .specifier = specifier, .referrer = referrer, + .arena = arena, }, ); + arena = bun.ArenaAllocator.init(bun.default_allocator); + give_back_arena = false; return error.AsyncModule; } @@ -1150,12 +1181,13 @@ pub const ModuleLoader = struct { }; if (written == 0) { + // if it's an empty file but there were plugins // we don't want it to break if you try to import from it if (has_bun_plugin) { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init("// auto-generated plugin stub\nexport default undefined\n"), + .source_code = String.static("// auto-generated plugin stub\nexport default undefined\n"), .specifier = input_specifier, .source_url = ZigString.init(path.text), // // TODO: change hash to a bitfield @@ -1178,7 +1210,7 @@ pub const ModuleLoader = struct { } if (jsc_vm.isWatcherEnabled()) { - var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, input_specifier, path.text, null); + var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, input_specifier, path.text, null, false); resolved_source.commonjs_exports = if (commonjs_exports.len > 0) commonjs_exports.ptr @@ -1195,7 +1227,7 @@ pub const ModuleLoader = struct { return .{ .allocator = null, - .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), + .source_code = bun.String.createLatin1(printer.ctx.getWritten()), .specifier = input_specifier, .source_url = ZigString.init(path.text), .commonjs_exports = if (commonjs_exports.len > 0) @@ -1276,7 +1308,7 @@ pub const ModuleLoader = struct { } return ResolvedSource{ .allocator = null, - .source_code = ZigString.init( + .source_code = bun.String.static( strings.append3( bun.default_allocator, JSC.Node.fs.constants_string, @@ -1326,7 +1358,7 @@ pub const ModuleLoader = struct { writer.writeAll(";\n") catch unreachable; } - const public_url = ZigString.fromUTF8(jsc_vm.allocator.dupe(u8, buf.toOwnedSliceLeaky()) catch @panic("out of memory")); + const public_url = bun.String.create(buf.toOwnedSliceLeaky()); return ResolvedSource{ .allocator = &jsc_vm.allocator, .source_code = public_url, @@ -1614,7 +1646,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(code), + .source_code = bun.String.init(code), .specifier = bun.String.init(JSC.bun_file_import_path), .source_url = ZigString.init(JSC.bun_file_import_path[1..]), .hash = 0, // TODO @@ -1622,7 +1654,7 @@ pub const ModuleLoader = struct { } else if (jsc_vm.node_modules == null and specifier.eqlComptime(Runtime.Runtime.Imports.Name)) { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(Runtime.Runtime.sourceContentBun()), + .source_code = bun.String.init(Runtime.Runtime.sourceContentBun()), .specifier = bun.String.init(Runtime.Runtime.Imports.Name), .source_url = ZigString.init(Runtime.Runtime.Imports.Name), .hash = Runtime.Runtime.versionHash(), @@ -1636,13 +1668,35 @@ pub const ModuleLoader = struct { if (comptime disable_transpilying) { return ResolvedSource{ .allocator = null, - .source_code = ZigString.fromBytes(jsc_vm.entry_point.source.contents), + .source_code = bun.String.init(jsc_vm.entry_point.source.contents), .specifier = bun.String.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .source_url = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .hash = 0, }; } defer jsc_vm.transpiled_count += 1; + var arena: bun.ArenaAllocator = undefined; + + // Attempt to reuse the Arena from the parser when we can + // This code is potentially re-entrant, so only one Arena can be reused at a time + // That's why we have to check if the Arena is null + // + // Using an Arena here is a significant memory optimization when loading many files + if (jsc_vm.parser_arena) |shared| { + arena = shared; + jsc_vm.parser_arena = null; + _ = arena.reset(.retain_capacity); + } else { + arena = bun.ArenaAllocator.init(jsc_vm.allocator); + } + + defer { + if (jsc_vm.parser_arena == null) { + jsc_vm.parser_arena = arena; + } else { + arena.deinit(); + } + } var bundler = &jsc_vm.bundler; var old = jsc_vm.bundler.log; @@ -1670,7 +1724,7 @@ pub const ModuleLoader = struct { opts.filepath_hash_for_hmr = 0; opts.warn_about_unbundled_modules = false; opts.macro_context = &jsc_vm.bundler.macro_context.?; - const main_ast = ((bundler.resolver.caches.js.parse(jsc_vm.allocator, opts, bundler.options.define, bundler.log, &jsc_vm.entry_point.source) catch null) orelse { + const main_ast = ((bundler.resolver.caches.js.parse(arena.allocator(), opts, bundler.options.define, bundler.log, &jsc_vm.entry_point.source) catch null) orelse { return error.ParseError; }).ast; var parse_result = ParseResult{ .source = jsc_vm.entry_point.source, .ast = main_ast, .loader = .js, .input_fd = null }; @@ -1708,7 +1762,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable), + .source_code = bun.String.createLatin1(printer.ctx.written), .specifier = specifier, .source_url = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .hash = 0, @@ -1725,7 +1779,7 @@ pub const ModuleLoader = struct { .@"node:fs/promises" => { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(JSC.Node.fs.constants_string ++ @embedFile("../js/out/modules/node/fs.promises.js")), + .source_code = bun.String.static(comptime JSC.Node.fs.constants_string ++ @embedFile("../js/out/modules/node/fs.promises.js")), .specifier = specifier, .source_url = ZigString.init("node:fs/promises"), .hash = 0, @@ -1735,8 +1789,8 @@ pub const ModuleLoader = struct { const shared_library_suffix = if (Environment.isMac) "dylib" else if (Environment.isLinux) "so" else if (Environment.isWindows) "dll" else ""; return ResolvedSource{ .allocator = null, - .source_code = ZigString.init( - "export const FFIType=" ++ + .source_code = bun.String.static( + comptime "export const FFIType=" ++ JSC.FFI.ABIType.map_to_js_object ++ ";export const suffix='" ++ shared_library_suffix ++ "';" ++ @embedFile("../js/out/modules/bun/ffi.js"), @@ -1747,60 +1801,60 @@ pub const ModuleLoader = struct { }; }, - .@"bun:jsc" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"bun:jsc", "bun/jsc.js", specifier), - .@"bun:sqlite" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"bun:sqlite", "bun/sqlite.js", specifier), - - .@"node:assert" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:assert", "node/assert.js", specifier), - .@"node:assert/strict" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:assert/strict", "node/assert.strict.js", specifier), - .@"node:async_hooks" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:async_hooks", "node/async_hooks.js", specifier), - .@"node:child_process" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:child_process", "node/child_process.js", specifier), - .@"node:crypto" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:crypto", "node/crypto.js", specifier), - .@"node:dns" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dns", "node/dns.js", specifier), - .@"node:dns/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dns/promises", "node/dns.promises.js", specifier), - .@"node:events" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:child_process", "node/events.js", specifier), - .@"node:fs" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:fs", "node/fs.js", specifier), - .@"node:http" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:http", "node/http.js", specifier), - .@"node:https" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:https", "node/https.js", specifier), - .@"node:net" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:net", "node/net.js", specifier), - .@"node:os" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:os", "node/os.js", specifier), - .@"node:path" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path", "node/path.js", specifier), - .@"node:path/posix" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path/posix", "node/path.posix.js", specifier), - .@"node:path/win32" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path/win32", "node/path.win32.js", specifier), - .@"node:perf_hooks" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:perf_hooks", "node/perf_hooks.js", specifier), - .@"node:readline" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:readline", "node/readline.js", specifier), - .@"node:readline/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:readline/promises", "node/readline.promises.js", specifier), - .@"node:stream" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream", "node/stream.js", specifier), - .@"node:stream/consumers" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/consumers", "node/stream.consumers.js", specifier), - .@"node:stream/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/promises", "node/stream.promises.js", specifier), - .@"node:stream/web" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/web", "node/stream.web.js", specifier), - .@"node:timers" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:timers", "node/timers.js", specifier), - .@"node:timers/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:timers/promises", "node/timers.promises.js", specifier), - .@"node:tls" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:tls", "node/tls.js", specifier), - .@"node:url" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:url", "node/url.js", specifier), - .@"node:util" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:util", "node/util.js", specifier), - .@"node:vm" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:vm", "node/vm.js", specifier), - .@"node:wasi" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:wasi", "node/wasi.js", specifier), - .@"node:zlib" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:zlib", "node/zlib.js", specifier), - - .@"detect-libc" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .depd, if (Environment.isLinux) "thirdparty/detect-libc.linux.js" else "thirdparty/detect-libc.js", specifier), - .depd => return jsResolvedSource(jsc_vm.load_builtins_from_path, .depd, "thirdparty/depd.js", specifier), - .undici => return jsResolvedSource(jsc_vm.load_builtins_from_path, .undici, "thirdparty/undici.js", specifier), - .ws => return jsResolvedSource(jsc_vm.load_builtins_from_path, .ws, "thirdparty/ws.js", specifier), - - .@"node:cluster" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:cluster", "node/cluster.js", specifier), - .@"node:dgram" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dgram", "node/dgram.js", specifier), - .@"node:diagnostics_channel" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:diagnostics_channel", "node/diagnostics_channel.js", specifier), - .@"node:http2" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:http2", "node/http2.js", specifier), - .@"node:inspector" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:inspector", "node/inspector.js", specifier), - .@"node:repl" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:repl", "node/repl.js", specifier), - .@"node:trace_events" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:trace_events", "node/trace_events.js", specifier), - .@"node:v8" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:v8", "node/v8.js", specifier), + .@"bun:jsc" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"bun:jsc", "bun/jsc.js", specifier), + .@"bun:sqlite" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"bun:sqlite", "bun/sqlite.js", specifier), + + .@"node:assert" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:assert", "node/assert.js", specifier), + .@"node:assert/strict" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:assert/strict", "node/assert.strict.js", specifier), + .@"node:async_hooks" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:async_hooks", "node/async_hooks.js", specifier), + .@"node:child_process" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:child_process", "node/child_process.js", specifier), + .@"node:crypto" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:crypto", "node/crypto.js", specifier), + .@"node:dns" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:dns", "node/dns.js", specifier), + .@"node:dns/promises" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:dns/promises", "node/dns.promises.js", specifier), + .@"node:events" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:child_process", "node/events.js", specifier), + .@"node:fs" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:fs", "node/fs.js", specifier), + .@"node:http" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:http", "node/http.js", specifier), + .@"node:https" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:https", "node/https.js", specifier), + .@"node:net" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:net", "node/net.js", specifier), + .@"node:os" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:os", "node/os.js", specifier), + .@"node:path" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:path", "node/path.js", specifier), + .@"node:path/posix" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:path/posix", "node/path.posix.js", specifier), + .@"node:path/win32" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:path/win32", "node/path.win32.js", specifier), + .@"node:perf_hooks" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:perf_hooks", "node/perf_hooks.js", specifier), + .@"node:readline" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:readline", "node/readline.js", specifier), + .@"node:readline/promises" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:readline/promises", "node/readline.promises.js", specifier), + .@"node:stream" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:stream", "node/stream.js", specifier), + .@"node:stream/consumers" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:stream/consumers", "node/stream.consumers.js", specifier), + .@"node:stream/promises" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:stream/promises", "node/stream.promises.js", specifier), + .@"node:stream/web" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:stream/web", "node/stream.web.js", specifier), + .@"node:timers" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:timers", "node/timers.js", specifier), + .@"node:timers/promises" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:timers/promises", "node/timers.promises.js", specifier), + .@"node:tls" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:tls", "node/tls.js", specifier), + .@"node:url" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:url", "node/url.js", specifier), + .@"node:util" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:util", "node/util.js", specifier), + .@"node:vm" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:vm", "node/vm.js", specifier), + .@"node:wasi" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:wasi", "node/wasi.js", specifier), + .@"node:zlib" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:zlib", "node/zlib.js", specifier), + + .@"detect-libc" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .depd, if (Environment.isLinux) "thirdparty/detect-libc.linux.js" else "thirdparty/detect-libc.js", specifier), + .depd => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .depd, "thirdparty/depd.js", specifier), + .undici => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .undici, "thirdparty/undici.js", specifier), + .ws => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .ws, "thirdparty/ws.js", specifier), + + .@"node:cluster" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:cluster", "node/cluster.js", specifier), + .@"node:dgram" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:dgram", "node/dgram.js", specifier), + .@"node:diagnostics_channel" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:diagnostics_channel", "node/diagnostics_channel.js", specifier), + .@"node:http2" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:http2", "node/http2.js", specifier), + .@"node:inspector" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:inspector", "node/inspector.js", specifier), + .@"node:repl" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:repl", "node/repl.js", specifier), + .@"node:trace_events" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:trace_events", "node/trace_events.js", specifier), + .@"node:v8" => return jsResolvedSource(jsc_vm, jsc_vm.load_builtins_from_path, .@"node:v8", "node/v8.js", specifier), } } else if (specifier.hasPrefixComptime(js_ast.Macro.namespaceWithColon)) { if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(specifier.byteSlice()))) |entry| { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(entry.source.contents), + .source_code = bun.String.create(entry.source.contents), .specifier = specifier, .source_url = specifier.toZigString(), .hash = 0, @@ -1809,7 +1863,7 @@ pub const ModuleLoader = struct { } else if (DisabledModule.getWithEql(specifier, bun.String.eqlComptime) != null) { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init( + .source_code = bun.String.static( \\const symbol = Symbol.for("CommonJS"); \\const lazy = globalThis[Symbol.for("Bun.lazy")]; \\var masqueradesAsUndefined = lazy("masqueradesAsUndefined"); @@ -1827,7 +1881,7 @@ pub const ModuleLoader = struct { if (graph.files.get(specifier_utf8.slice())) |file| { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(file.contents), + .source_code = bun.String.static(file.contents), .specifier = specifier, .source_url = specifier.toZigString(), .hash = 0, @@ -2199,12 +2253,16 @@ pub const DisabledModule = bun.ComptimeStringMap( }, ); -inline fn jsResolvedSource(builtins: []const u8, comptime module: HardcodedModule, comptime input: []const u8, specifier: bun.String) ResolvedSource { - return ResolvedSource{ - .allocator = null, - .source_code = ZigString.init(jsModuleFromFile(builtins, input)), - .specifier = specifier, - .source_url = ZigString.init(@tagName(module)), - .hash = 0, - }; +fn jsResolvedSource(vm: *JSC.VirtualMachine, builtins: []const u8, comptime module: HardcodedModule, comptime input: []const u8, specifier: bun.String) ResolvedSource { + // We use RefCountedResolvedSource because we want a stable StringImpl* + // pointer so that the SourceProviderCache has the maximum hit rate + return vm.refCountedResolvedSource( + jsModuleFromFile(builtins, input), + specifier, + @tagName(module), + null, + + // we never want to free these + true, + ); } diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 23af9cc7c..95a2270a5 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -754,7 +754,7 @@ pub const Valid = struct { pub const ArgumentsSlice = struct { remaining: []const JSC.JSValue, vm: *JSC.VirtualMachine, - arena: std.heap.ArenaAllocator = std.heap.ArenaAllocator.init(bun.default_allocator), + arena: @import("root").bun.ArenaAllocator = @import("root").bun.ArenaAllocator.init(bun.default_allocator), all: []const JSC.JSValue, threw: bool = false, protected: std.bit_set.IntegerBitSet(32) = std.bit_set.IntegerBitSet(32).initEmpty(), @@ -794,7 +794,7 @@ pub const ArgumentsSlice = struct { .remaining = arguments, .vm = vm, .all = arguments, - .arena = std.heap.ArenaAllocator.init(vm.allocator), + .arena = @import("root").bun.ArenaAllocator.init(vm.allocator), }; } @@ -1733,7 +1733,7 @@ pub const Path = struct { heap_allocator, ); var allocator = stack_fallback_allocator.get(); - var arena = std.heap.ArenaAllocator.init(heap_allocator); + var arena = @import("root").bun.ArenaAllocator.init(heap_allocator); var arena_allocator = arena.allocator(); defer arena.deinit(); var buf: [bun.MAX_PATH_BYTES]u8 = undefined; @@ -1870,7 +1870,7 @@ pub const Path = struct { var parts = allocator.alloc(string, args_len) catch unreachable; defer allocator.free(parts); - var arena = std.heap.ArenaAllocator.init(heap_allocator); + var arena = @import("root").bun.ArenaAllocator.init(heap_allocator); var arena_allocator = arena.allocator(); defer arena.deinit(); diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index 13b086541..9b3ddb8df 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -242,7 +242,7 @@ pub const Blob = struct { allocator: std.mem.Allocator, form_data: *JSC.DOMFormData, ) Blob { - var arena = std.heap.ArenaAllocator.init(allocator); + var arena = @import("root").bun.ArenaAllocator.init(allocator); defer arena.deinit(); var stack_allocator = std.heap.stackFallback(1024, arena.allocator()); var stack_mem_all = stack_allocator.get(); diff --git a/src/bun.zig b/src/bun.zig index 9ca01f2b8..ab3b3e18c 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -1530,3 +1530,5 @@ pub const WTF = struct { /// The String type from WebKit's WTF library. pub const StringImpl = @import("./string.zig").WTFStringImpl; }; + +pub const ArenaAllocator = @import("./ArenaAllocator.zig").ArenaAllocator; diff --git a/src/bundler.zig b/src/bundler.zig index 9a2ae2355..3c796b576 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -192,9 +192,9 @@ pub const PluginRunner = struct { return null; } - var file_path = path_value.getZigString(global); + var file_path = path_value.toBunString(global); - if (file_path.len == 0) { + if (file_path.length() == 0) { log.addError( null, loc, diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index c62be6153..a03847079 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -217,7 +217,7 @@ pub const ThreadPool = struct { deinit_task: ThreadPoolLib.Task = .{ .callback = deinitCallback }, - temporary_arena: std.heap.ArenaAllocator = undefined, + temporary_arena: @import("root").bun.ArenaAllocator = undefined, stmt_list: LinkerContext.StmtList = undefined, pub fn deinitCallback(task: *ThreadPoolLib.Task) void { @@ -301,7 +301,7 @@ pub const ThreadPool = struct { this.data.bundler.linker.resolver = &this.data.bundler.resolver; this.data.bundler.macro_context = js_ast.Macro.MacroContext.init(&this.data.bundler); this.data.macro_context = this.data.bundler.macro_context.?; - this.temporary_arena = std.heap.ArenaAllocator.init(this.allocator); + this.temporary_arena = @import("root").bun.ArenaAllocator.init(this.allocator); this.stmt_list = LinkerContext.StmtList.init(this.allocator); const CacheSet = @import("../cache.zig"); @@ -3793,7 +3793,7 @@ const LinkerContext = struct { var stack_fallback = std.heap.stackFallback(4096, this.allocator); var stack_all = stack_fallback.get(); - var arena = std.heap.ArenaAllocator.init(stack_all); + var arena = @import("root").bun.ArenaAllocator.init(stack_all); defer arena.deinit(); var temp_allocator = arena.allocator(); @@ -6407,7 +6407,7 @@ const LinkerContext = struct { defer chunk.renamer.deinit(bun.default_allocator); - var arena = std.heap.ArenaAllocator.init(worker.allocator); + var arena = @import("root").bun.ArenaAllocator.init(worker.allocator); defer arena.deinit(); // Also generate the cross-chunk binding code diff --git a/src/deps/c_ares.zig b/src/deps/c_ares.zig index 06c9dfbf1..4539358d0 100644 --- a/src/deps/c_ares.zig +++ b/src/deps/c_ares.zig @@ -309,7 +309,7 @@ pub const AddrInfo = extern struct { globalThis: *JSC.JSGlobalObject, ) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); var node = addr_info.node.?; const array = JSC.JSValue.createEmptyArray( globalThis, @@ -627,7 +627,7 @@ pub const struct_ares_caa_reply = extern struct { pub fn toJSReponse(this: *struct_ares_caa_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); defer arena.deinit(); var allocator = arena.allocator(); @@ -706,7 +706,7 @@ pub const struct_ares_srv_reply = extern struct { pub fn toJSReponse(this: *struct_ares_srv_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); defer arena.deinit(); var allocator = arena.allocator(); @@ -791,7 +791,7 @@ pub const struct_ares_mx_reply = extern struct { pub fn toJSReponse(this: *struct_ares_mx_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); defer arena.deinit(); var allocator = arena.allocator(); @@ -867,7 +867,7 @@ pub const struct_ares_txt_reply = extern struct { pub fn toJSReponse(this: *struct_ares_txt_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); defer arena.deinit(); var allocator = arena.allocator(); @@ -949,7 +949,7 @@ pub const struct_ares_naptr_reply = extern struct { pub fn toJSReponse(this: *struct_ares_naptr_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); defer arena.deinit(); var allocator = arena.allocator(); @@ -1043,7 +1043,7 @@ pub const struct_ares_soa_reply = extern struct { pub fn toJSReponse(this: *struct_ares_soa_reply, parent_allocator: std.mem.Allocator, globalThis: *JSC.JSGlobalObject, comptime _: []const u8) JSC.JSValue { var stack = std.heap.stackFallback(2048, parent_allocator); - var arena = std.heap.ArenaAllocator.init(stack.get()); + var arena = @import("root").bun.ArenaAllocator.init(stack.get()); defer arena.deinit(); var allocator = arena.allocator(); diff --git a/src/deps/diffz/DiffMatchPatch.zig b/src/deps/diffz/DiffMatchPatch.zig index f46d88cd5..7e545f364 100644 --- a/src/deps/diffz/DiffMatchPatch.zig +++ b/src/deps/diffz/DiffMatchPatch.zig @@ -1398,7 +1398,7 @@ fn diffCommonOverlap(text1_in: []const u8, text2_in: []const u8) usize { } // pub fn main() void { -// var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); +// var arena = @import("root").bun.ArenaAllocator.init(std.heap.page_allocator); // defer arena.deinit(); // var bruh = default.diff(arena.allocator(), "Hello World.", "Goodbye World.", true); @@ -1406,7 +1406,7 @@ fn diffCommonOverlap(text1_in: []const u8, text2_in: []const u8) usize { // } // test { -// var arena = std.heap.ArenaAllocator.init(testing.allocator); +// var arena = @import("root").bun.ArenaAllocator.init(testing.allocator); // defer arena.deinit(); // var bruh = try default.diff(arena.allocator(), "Hello World.", "Goodbye World.", true); @@ -1455,7 +1455,7 @@ test diffCommonOverlap { } test diffHalfMatch { - var arena = std.heap.ArenaAllocator.init(testing.allocator); + var arena = @import("root").bun.ArenaAllocator.init(testing.allocator); defer arena.deinit(); var one_timeout = DiffMatchPatch{}; @@ -1549,7 +1549,7 @@ test diffHalfMatch { } test diffLinesToChars { - var arena = std.heap.ArenaAllocator.init(testing.allocator); + var arena = @import("root").bun.ArenaAllocator.init(testing.allocator); defer arena.deinit(); // Convert lines down to characters. @@ -1611,7 +1611,7 @@ test diffLinesToChars { } test diffCharsToLines { - var arena = std.heap.ArenaAllocator.init(testing.allocator); + var arena = @import("root").bun.ArenaAllocator.init(testing.allocator); defer arena.deinit(); try testing.expect((Diff.init(.equal, "a")).eql(Diff.init(.equal, "a"))); @@ -1640,7 +1640,7 @@ test diffCharsToLines { } test diffCleanupMerge { - var arena = std.heap.ArenaAllocator.init(testing.allocator); + var arena = @import("root").bun.ArenaAllocator.init(testing.allocator); defer arena.deinit(); // Cleanup a messy diff. @@ -1828,7 +1828,7 @@ test diffCleanupMerge { } test diffCleanupSemanticLossless { - var arena = std.heap.ArenaAllocator.init(testing.allocator); + var arena = @import("root").bun.ArenaAllocator.init(testing.allocator); defer arena.deinit(); var diffs = DiffList{}; @@ -1953,7 +1953,7 @@ fn rebuildtexts(allocator: std.mem.Allocator, diffs: DiffList) ![2][]const u8 { } test diffBisect { - var arena = std.heap.ArenaAllocator.init(talloc); + var arena = @import("root").bun.ArenaAllocator.init(talloc); defer arena.deinit(); // Normal. @@ -1987,7 +1987,7 @@ test diffBisect { const talloc = testing.allocator; test diff { - var arena = std.heap.ArenaAllocator.init(talloc); + var arena = @import("root").bun.ArenaAllocator.init(talloc); defer arena.deinit(); // Perform a trivial diff. @@ -2094,7 +2094,7 @@ test diff { } test diffCleanupSemantic { - var arena = std.heap.ArenaAllocator.init(talloc); + var arena = @import("root").bun.ArenaAllocator.init(talloc); defer arena.deinit(); // Cleanup semantically trivial equalities. diff --git a/src/deps/zig-clap/clap.zig b/src/deps/zig-clap/clap.zig index a21a1cb1a..16824e788 100644 --- a/src/deps/zig-clap/clap.zig +++ b/src/deps/zig-clap/clap.zig @@ -241,7 +241,7 @@ fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) void { pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type { return struct { - arena: std.heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, clap: ComptimeClap(Id, params), exe_arg: ?[]const u8, diff --git a/src/deps/zig-clap/clap/args.zig b/src/deps/zig-clap/clap/args.zig index a1fa3773a..d315582bb 100644 --- a/src/deps/zig-clap/clap/args.zig +++ b/src/deps/zig-clap/clap/args.zig @@ -48,7 +48,7 @@ test "SliceIterator" { pub const OsIterator = struct { const Error = process.ArgIterator.InitError; - arena: heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, args: process.ArgIterator, /// The executable path (this is the first argument passed to the program) @@ -58,7 +58,7 @@ pub const OsIterator = struct { pub fn init(allocator: mem.Allocator) OsIterator { var res = OsIterator{ - .arena = heap.ArenaAllocator.init(allocator), + .arena = @import("root").bun.ArenaAllocator.init(allocator), .args = process.args(), .exe_arg = undefined, }; @@ -83,12 +83,12 @@ pub const ShellIterator = struct { QuoteNotClosed, } || mem.Allocator.Error; - arena: heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, str: []const u8, pub fn init(allocator: mem.Allocator, str: []const u8) ShellIterator { return .{ - .arena = heap.ArenaAllocator.init(allocator), + .arena = @import("root").bun.ArenaAllocator.init(allocator), .str = str, }; } diff --git a/src/http.zig b/src/http.zig index f26a0e985..d2924b804 100644 --- a/src/http.zig +++ b/src/http.zig @@ -34,7 +34,7 @@ const DotEnv = @import("./env_loader.zig"); const mimalloc = @import("./allocators/mimalloc.zig"); const MacroMap = @import("./resolver/package_json.zig").MacroMap; const Analytics = @import("./analytics/analytics_thread.zig"); -const Arena = std.heap.ArenaAllocator; +const Arena = @import("root").bun.ArenaAllocator; const ThreadlocalArena = @import("./mimalloc_arena.zig").Arena; const JSON = bun.JSON; const DateTime = bun.DateTime; diff --git a/src/install/install.zig b/src/install/install.zig index cfbb0cb7f..f4695cc34 100644 --- a/src/install/install.zig +++ b/src/install/install.zig @@ -2293,7 +2293,7 @@ pub const PackageManager = struct { return null; } - var arena = std.heap.ArenaAllocator.init(this.allocator); + var arena = @import("root").bun.ArenaAllocator.init(this.allocator); defer arena.deinit(); var arena_alloc = arena.allocator(); var stack_fallback = std.heap.stackFallback(4096, arena_alloc); diff --git a/src/install/npm.zig b/src/install/npm.zig index 283d20b39..c01e6ee1f 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -781,7 +781,7 @@ pub const PackageManifest = struct { const source = logger.Source.initPathString(expected_name, json_buffer); initializeStore(); defer bun.JSAst.Stmt.Data.Store.memory_allocator.?.pop(); - var arena = std.heap.ArenaAllocator.init(allocator); + var arena = @import("root").bun.ArenaAllocator.init(allocator); defer arena.deinit(); const json = json_parser.ParseJSONUTF8( &source, diff --git a/src/main.zig b/src/main.zig index 0c7cf9584..f4df0814e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -24,7 +24,7 @@ pub fn main() void { // The memory allocator makes a massive difference. // std.heap.raw_c_allocator and default_allocator perform similarly. // std.heap.GeneralPurposeAllocator makes this about 3x _slower_ than esbuild. - // var root_alloc = std.heap.ArenaAllocator.init(std.heap.raw_c_allocator); + // var root_alloc = @import("root").bun.ArenaAllocator.init(std.heap.raw_c_allocator); // var root_alloc_ = &root_alloc.allocator; var stdout = std.io.getStdOut(); diff --git a/src/main_wasm.zig b/src/main_wasm.zig index b1cb14dc5..8958d7cd4 100644 --- a/src/main_wasm.zig +++ b/src/main_wasm.zig @@ -202,7 +202,7 @@ const Arena = @import("./mimalloc_arena.zig").Arena; var log: Logger.Log = undefined; export fn transform(opts_array: u64) u64 { - // var arena = std.heap.ArenaAllocator.init(default_allocator); + // var arena = @import("root").bun.ArenaAllocator.init(default_allocator); var arena = Arena.init() catch unreachable; var allocator = arena.allocator(); defer arena.deinit(); @@ -274,7 +274,7 @@ export fn transform(opts_array: u64) u64 { } export fn scan(opts_array: u64) u64 { - // var arena = std.heap.ArenaAllocator.init(default_allocator); + // var arena = @import("root").bun.ArenaAllocator.init(default_allocator); var arena = Arena.init() catch unreachable; var allocator = arena.allocator(); defer arena.deinit(); diff --git a/src/renamer.zig b/src/renamer.zig index 3b32511f6..0177725c6 100644 --- a/src/renamer.zig +++ b/src/renamer.zig @@ -470,7 +470,7 @@ pub const NumberRenamer = struct { allocator: std.mem.Allocator, temp_allocator: std.mem.Allocator, number_scope_pool: bun.HiveArray(NumberScope, 128).Fallback, - arena: std.heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, root: NumberScope = .{}, name_stack_fallback: std.heap.StackFallbackAllocator(512) = undefined, name_temp_allocator: std.mem.Allocator = undefined, @@ -538,7 +538,7 @@ pub const NumberRenamer = struct { .temp_allocator = temp_allocator, .names = try allocator.alloc(bun.BabyList(string), symbols.symbols_for_source.len), .number_scope_pool = undefined, - .arena = std.heap.ArenaAllocator.init(temp_allocator), + .arena = @import("root").bun.ArenaAllocator.init(temp_allocator), }; renamer.name_stack_fallback = .{ .buffer = undefined, diff --git a/src/string.zig b/src/string.zig index cfa50f792..62cdc5462 100644 --- a/src/string.zig +++ b/src/string.zig @@ -239,6 +239,17 @@ pub const String = extern struct { pub const dead = String{ .tag = .Dead, .value = .{ .Dead = {} } }; pub const StringImplAllocator = Parent.StringImplAllocator; + extern fn BunString__fromLatin1(bytes: [*]const u8, len: usize) String; + extern fn BunString__fromBytes(bytes: [*]const u8, len: usize) String; + + pub fn createLatin1(bytes: []const u8) String { + return BunString__fromLatin1(bytes.ptr, bytes.len); + } + + pub fn create(bytes: []const u8) String { + return BunString__fromBytes(bytes.ptr, bytes.len); + } + pub fn initWithType(comptime Type: type, value: Type) String { switch (comptime Type) { ZigString => return String{ .tag = .ZigString, .value = .{ .ZigString = value } }, @@ -273,6 +284,18 @@ pub const String = extern struct { return initWithType(@TypeOf(value), value); } + extern fn BunString__createExternal( + bytes: [*]const u8, + len: usize, + isLatin1: bool, + ptr: ?*anyopaque, + callback: ?*const fn (*anyopaque, *anyopaque, u32) callconv(.C) void, + ) String; + + pub fn createExternal(bytes: []const u8, isLatin1: bool, ctx: ?*anyopaque, callback: ?*const fn (*anyopaque, *anyopaque, u32) callconv(.C) void) String { + return BunString__createExternal(bytes.ptr, bytes.len, isLatin1, ctx, callback); + } + pub fn fromUTF8(value: []const u8) String { return String.initWithType(ZigString, ZigString.initUTF8(value)); } @@ -339,7 +362,7 @@ pub const String = extern struct { if (self.tag == .Empty) return &[_]u16{}; std.debug.assert(self.tag == .WTFStringImpl); - return self.value.WTFStringImpl.utf16(); + return self.value.WTFStringImpl.utf16Slice(); } pub inline fn latin1(self: String) []const u8 { @@ -347,7 +370,7 @@ pub const String = extern struct { return &[_]u8{}; std.debug.assert(self.tag == .WTFStringImpl); - return self.value.WTFStringImpl.latin1(); + return self.value.WTFStringImpl.latin1Slice(); } pub fn isUTF8(self: String) bool { diff --git a/src/zlib.zig b/src/zlib.zig index 4a3901a14..b85ddf431 100644 --- a/src/zlib.zig +++ b/src/zlib.zig @@ -238,7 +238,7 @@ pub fn NewZlibReader(comptime Writer: type, comptime buffer_size: usize) type { buf: [buffer_size]u8, zlib: zStream_struct, allocator: std.mem.Allocator, - arena: std.heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, state: State = State.Uninitialized, pub fn alloc(ctx: *anyopaque, items: uInt, len: uInt) callconv(.C) *anyopaque { @@ -272,7 +272,7 @@ pub fn NewZlibReader(comptime Writer: type, comptime buffer_size: usize) type { .buf = std.mem.zeroes([buffer_size]u8), .allocator = allocator, .zlib = undefined, - .arena = std.heap.ArenaAllocator.init(allocator), + .arena = @import("root").bun.ArenaAllocator.init(allocator), }; zlib_reader.zlib = zStream_struct{ @@ -422,7 +422,7 @@ pub const ZlibReaderArrayList = struct { list_ptr: *std.ArrayListUnmanaged(u8), zlib: zStream_struct, allocator: std.mem.Allocator, - arena: std.heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, state: State = State.Uninitialized, pub fn alloc(ctx: *anyopaque, items: uInt, len: uInt) callconv(.C) *anyopaque { @@ -473,7 +473,7 @@ pub const ZlibReaderArrayList = struct { .list_ptr = list, .allocator = allocator, .zlib = undefined, - .arena = std.heap.ArenaAllocator.init(allocator), + .arena = @import("root").bun.ArenaAllocator.init(allocator), }; zlib_reader.zlib = zStream_struct{ @@ -829,7 +829,7 @@ pub const ZlibCompressorArrayList = struct { list_ptr: *std.ArrayListUnmanaged(u8), zlib: zStream_struct, allocator: std.mem.Allocator, - arena: std.heap.ArenaAllocator, + arena: @import("root").bun.ArenaAllocator, state: State = State.Uninitialized, pub fn alloc(ctx: *anyopaque, items: uInt, len: uInt) callconv(.C) *anyopaque { @@ -868,7 +868,7 @@ pub const ZlibCompressorArrayList = struct { .list_allocator = list_allocator, .allocator = allocator, .zlib = undefined, - .arena = std.heap.ArenaAllocator.init(allocator), + .arena = @import("root").bun.ArenaAllocator.init(allocator), }; zlib_reader.zlib = zStream_struct{ |