Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
160 commits
Select commit Hold shift + click to select a range
1e9243e
output: `QuietWriter` holds an `Fd`, `SCOPED_FILE_WRITER` is an `Atom…
claude Oct 29, 2026
542872c
paths: `join_string_buf` calls `join_string_buf_t_same::<u8, P>`
claude Oct 29, 2026
d1fe00e
sys: `futex_wake` and `futex_wait` are safe fns that pass their own c…
claude Oct 29, 2026
a98b0ee
sys, dns, os: `net::Address` users read the fields and call `from_ip`…
claude Oct 29, 2026
c2b5560
sys: `flock` and `futimens` are `safe fn` imports; `sendfile` uses th…
claude Oct 29, 2026
14f372f
dgram, exe_format: `fstat`, `isatty` and the page size come from `bun…
claude Oct 29, 2026
d551cf6
`geteuid`, `listen`, `fallocate` and `socketpair` go through `safe fn…
claude Oct 29, 2026
63419aa
dgram: `if_indextoname` takes the name buffer as `&mut [u8; IF_NAMESI…
claude Oct 29, 2026
4e71e6e
sys: drop `unsafe impl Send` and `Sync` for `DynLib`
claude Oct 29, 2026
c1cf6e8
sys: drop `unsafe impl Send` and `Sync` for `dir_iterator::Name`
claude Oct 29, 2026
5fe1fa5
sys, jsc: drop `unsafe impl Zeroable for PlatformIoVecConst`
claude Oct 29, 2026
0c3423b
sys: `linux_syscall::getdents64` takes `&mut [MaybeUninit<u8>]`
claude Oct 29, 2026
9704e54
sys: the POSIX arm of `exists_os_path` calls `exists_z`
claude Oct 29, 2026
7bde157
paths: `LazyPathBuf` is a `OnceCell<Box<UnsafeCell<PathBuffer>>>`
claude Oct 29, 2026
b9e5831
standalone_graph: `File::module_info` is `&'static [u8]`
claude Oct 29, 2026
e9d8878
standalone_graph: the parsed source map goes in a `OnceLock`, the gra…
claude Oct 29, 2026
921de41
standalone_graph: the ELF payload getter returns `&'static u64`; `get…
claude Oct 29, 2026
8e130ad
standalone_graph: an entry point record is read with `read_prefix`
claude Oct 29, 2026
f51b92d
bundler_jsc: one `safe fn` import, and the module loader arrives as `…
claude Oct 29, 2026
745f507
highway: `highway_xxhash64_reset` and `highway_xxhash64_digest` are `…
claude Oct 29, 2026
4043933
crash_handler, trim: two statics that `global_asm!` defines in the sa…
claude Oct 29, 2026
206c4de
base64: `Base64Decoder::decode` indexes its 256-entry table with the …
claude Oct 29, 2026
08e7990
zlib_sys: drop the unused `unsafe impl Zeroable for zStream_struct`
claude Oct 29, 2026
0c8c5dc
crash_handler: rlimits go through `bun_sys::posix::getrlimit` and `se…
claude Oct 29, 2026
9324fa5
crash_handler: `sigemptyset` and `pthread_sigmask` are `safe fn`s ove…
claude Oct 29, 2026
6b30f29
glob: the four callers of `handle_sys_err_with_path` call `Error::wit…
claude Oct 29, 2026
ca42ad3
semver: `parse` appends through a by-value cursor, not through `tail`…
claude Oct 29, 2026
91aab23
test: `Jest::RUNNER` and `WORKER_CMDS` are `AtomicPtr`
claude Oct 29, 2026
7015c1f
io: `io_request` parents are recovered with `bun_core::from_field_ptr!`
claude Oct 29, 2026
a6b0482
router: `HashedString<'a>` holds a `&'a [u8]`
claude Oct 29, 2026
921ae76
tty: `Bun__ttySetMode` is a `safe fn` that takes `&mut State`
claude Oct 29, 2026
9a1bb3f
ptr: remove `Interned::assume`
claude Oct 29, 2026
b90d361
collections: drop the unused `unsafe impl Send for DynamicBitSetList`
claude Oct 29, 2026
4b27a23
collections: `empty_masks_ptr` steps into the static with `wrapping_add`
claude Oct 29, 2026
6212695
`AtomicPtr` and `AtomicBool` instead of `AtomicCell` for pointers and…
claude Oct 29, 2026
f75403a
bun_core: `AtomicCell<T>` is an `AtomicU8` and a `PhantomData<T>`
claude Oct 29, 2026
4814500
Five exported C strings are `&[u8; N]`; remove `SyncCStr` and `CStrPtr`
claude Oct 29, 2026
cc221c4
uws_sys: response callbacks get `*mut U` and `AnyResponse` on every t…
claude Oct 29, 2026
e6684bb
uws: the four `us_ssl_wrapper_*` exports take `&CallbackState`
claude Oct 29, 2026
63ec041
uws_sys: `Response<SSL>`, `App<SSL>` and `ListenSocket` hold the C ha…
claude Oct 29, 2026
565a53a
uws_sys: `AnyResponse` derefs its handles with `bun_opaque::opaque_de…
claude Oct 29, 2026
c35c24e
uws_sys: `AnyWebSocket::publish_with_options` calls `App::publish`
claude Oct 29, 2026
9462f85
uws_sys: `digest`'s `feed_arr` feeds each string with `feed_z`
claude Oct 29, 2026
c760006
uws_sys: `us_fault_hit` is a `safe fn` that takes `&mut` out-parameters
claude Oct 29, 2026
ac0f5e9
uws_sys: `sni_resolve` takes `Option<OwnedSslCtx>`
claude Oct 29, 2026
ae6ce5f
uws_sys: `us_quic_stream_header` returns `Option<&Header>`
claude Oct 29, 2026
472037e
uws_sys: route and upgrade thunks take `&mut` handles; `thunk::handle…
claude Oct 29, 2026
24d14bf
uws_sys: the UDP data callback gets `&PacketBuffer`
claude Oct 29, 2026
7d4138b
socket: the hand-written `UpgradedDuplex__*` and `WindowsNamedPipe__*…
claude Oct 29, 2026
fbf3709
http_jsc: `Bun__Fetch{Redirect,RequestMode,CacheMode}__toJS` are `saf…
claude Oct 29, 2026
937263d
http: `take_protos`, `take_server_name` and `get_tls_hostname` use `S…
claude Oct 29, 2026
77ea355
http: `preconnect` takes the href it has to free as a `Vec<u8>`
claude Oct 29, 2026
e50ae81
http: `us_socket_session_sink` is a `safe fn`, `on_new_session` borro…
claude Oct 29, 2026
1cdd454
http: `ProxyTunnel` keeps what changes in cells and is reached throug…
claude Oct 29, 2026
05405f8
dns: the four `*Request::init` return the promise with the request
claude Oct 29, 2026
49349f6
ipc: `SendQueue::run_deferred` takes the ref of its task as a `RefPtr…
claude Oct 29, 2026
9053db0
jsc: the `stop_active_handles_for_vm_teardown` hook takes `&mut Virtu…
claude Oct 29, 2026
8498522
dns: the out-parameter of `Bun__addrinfo_get` is a `&mut MaybeUninit<…
claude Oct 29, 2026
c68c879
server: `ServerConfig::from_js` passes on the object pointer it has
claude Oct 29, 2026
0790d11
socket: `select_alpn_callback` makes its `&SSL` once
claude Oct 29, 2026
3c53d7b
dispatch: the two reload arms consume their box like the arms around …
claude Oct 29, 2026
64e3b9f
server: `ServerAllConnectionsClosedTask::run_from_js_thread` takes th…
claude Oct 29, 2026
5ab1645
css: `Dependency<'bump>` borrows its placeholder from the printer's a…
claude Oct 29, 2026
c30d143
parsers: the JSON and XML tapes are set up through the reference at hand
claude Oct 29, 2026
be85b61
ast: the stores' allocator handle is passed as the `Option<BackRef>` …
claude Oct 29, 2026
061224e
react_compiler: `maybe_compile_pending` takes the arena as an argument
claude Oct 29, 2026
7c02158
js_parser: `next_codepoint_with` reads the byte with `get`
claude Oct 29, 2026
8024d7c
sourcemap_jsc: `find` takes the thread's map and returns a borrow of …
claude Oct 29, 2026
6b2dc2d
sema: the `mi_process_info` stand-in has the signature of its import
claude Oct 29, 2026
fb88bac
sourcemap: `release_value` calls `Arc::decrement_strong_count` itself
claude Oct 29, 2026
4c24912
js_printer: `print_commonjs_export_identifier` keeps the key as a pla…
claude Oct 29, 2026
ade482f
uws_sys: `BodyReaderMixin` owns its handler
claude Oct 29, 2026
0bb9d34
bun_core: `intrusive_field!` checks the type of the field it names
claude Oct 29, 2026
c31ff38
threading: `UnboundedQueue` finds the link through `IntrusiveField<Li…
claude Oct 29, 2026
3f79bd0
ptr: `WeakPtr<T>` finds its count through `IntrusiveField<WeakPtrData>`
claude Oct 29, 2026
47076ef
image: `jpeg::Handle` holds the TurboJPEG instance as `&'static mut T…
claude Oct 29, 2026
55495b5
image: the drop guards own the libwebp demuxer and mux as `&mut`
claude Oct 29, 2026
56e7dea
uws_sys: `on_websocket_upgrade` takes `res: &mut NewAppResponse<SSL>`
claude Oct 29, 2026
3adda2d
shell: `PipeReaderState::Done` and `BufferedIoState::Closed` carry no…
claude Oct 29, 2026
3ef02f8
shell: `PipeReader::process` is a `Cell`
claude Oct 29, 2026
d55df10
timer: `TimerHeap::peek_next` and `find_max_next` lend the deadline, …
claude Oct 29, 2026
fba101e
bake: `BakeProdLoad` takes `&PerThread`, `BakeRenderRoutesForProdStat…
claude Oct 29, 2026
865a06a
bake: `on_file_update` borrows the acquired event once
claude Oct 29, 2026
41d37ed
subprocess: `take_pending_start_writer` returns the claimed ref as a …
claude Oct 29, 2026
60c27c8
timer: the two empty uv close callbacks are safe fns
claude Oct 29, 2026
b702369
install: split `manager.lockfile` off with `&raw mut *manager.lockfile`
claude Oct 29, 2026
d9de508
install: `NetworkTask::write_init` takes the `HiveSlot` claim
claude Oct 29, 2026
4cb43b1
install: `PackageInstaller` reaches the progress bar through `manager`
claude Oct 29, 2026
4a502b9
install: the isolated install borrows `manager.progress` where it use…
claude Oct 29, 2026
fbe770c
install: `read_package_json_from_disk` splits off only the lockfile
claude Oct 29, 2026
d7658b9
install: `PackageManager::init` reborrows the new manager once
claude Oct 29, 2026
207bcd9
cli: the Fuzzilli compile loop stops with `std::process::abort()`
claude Oct 29, 2026
99342ea
cli: the upgrade test helpers keep the temp dir HANDLE in an `AtomicPtr`
claude Oct 29, 2026
523f1f3
uws_sys: the five IP address getters take `&mut [u8; 16]`
claude Oct 29, 2026
b816e5f
cares_sys: `ares_set_local_ip6` and `ares_get_servers_ports` are `saf…
claude Oct 29, 2026
f234f0a
socket: `Bun__WebSocket__freeSSLConfig` takes the `Box`
claude Oct 29, 2026
a9cff79
http: drop `unsafe impl Send for HpackHandle`
claude Oct 29, 2026
8a72be1
libuv_sys: `fs_t::ptr_as` is a safe fn
claude Oct 29, 2026
ab4b57d
http: the H3 client's quic callbacks take `&mut quic::Socket` / `&mut…
claude Oct 29, 2026
eb52c3f
jsc: `JsHeap::bun_vm()` for bindgen bodies that want only the VM
claude Oct 29, 2026
818ba56
jsc: a Worker's `argv` and `execArgv` entries are `Option<&WTFStringI…
claude Oct 29, 2026
d153d65
jsc: `RefString` owns its bytes as a `Box<[u8]>`
claude Oct 29, 2026
c2653a5
jsc: `VirtualMachine::bun_watcher` points at the `Watcher`
claude Oct 29, 2026
893625e
jsc: the `disarm_all_timers_for_vm_teardown` hook is a `fn()`
claude Oct 29, 2026
74dd73c
jsc: the drop check of `Completion` is a zero-sized field
claude Oct 29, 2026
9cf03f1
jsc: `promise_ptr` is an `Option<&Cell<*mut JSInternalPromise>>`
claude Oct 29, 2026
57dfa5d
jsc: a Worker's VM owns its env loader
claude Oct 29, 2026
34f3a44
jsc: `Bun__AOT__tryReservePagesForNewThread` is a `safe fn` that take…
claude Oct 29, 2026
0c14421
jsc: `RuntimeTranspilerStore::run_from_js_thread` takes the `&VM` it …
claude Oct 29, 2026
8bb23aa
jsc: `Bun__TestReporterAgentEnable` takes `&mut TestReporterHandle`
claude Oct 29, 2026
d9b1412
jsc: the compile cache hashes with `hashers::SHA256::hash`
claude Oct 29, 2026
42a87a1
io: `HeapContext::less` takes `&T`
claude Oct 29, 2026
820f01b
threading: `WorkPool::go` schedules its task with `schedule_new`
claude Oct 29, 2026
dc3ad14
bun_core: `errno` is a `Cell<c_int>` lent by `with_errno`
claude Oct 29, 2026
f856ffb
threading: the IO thread pool is a `OnceLock<ThreadPool>`
claude Oct 29, 2026
15410a1
mimalloc_sys: `mi_heap_visit_blocks` names what its visitor is given
claude Oct 29, 2026
5aa9743
io: `start_with_pipe` and `set_pipe` take the `Box<uv::Pipe>` they adopt
claude Oct 29, 2026
b555c95
sys: drop two Windows statics that nothing reads, and `take_environ`
claude Oct 29, 2026
749bf83
jsc: a new `FetchHeaders` comes back from C++ as the `HeadersRef` tha…
claude Oct 29, 2026
83a7a09
webcore: delete the unused export `BlobArrayBuffer_deallocator`
claude Oct 29, 2026
35ff658
jsc: `jsc_host_abi!` takes a plain `fn`
claude Oct 29, 2026
b077079
webcore: `CompressionStreamCoder__destroy` takes the ref it releases
claude Oct 29, 2026
a8c72d4
webcore: the wasm streaming export takes `&JSGlobalObject` and `&Stre…
claude Oct 29, 2026
66d2648
sys: `splice(2)` is a `safe fn` import
claude Oct 29, 2026
bc46867
webcore: `S3BlobStatTask::start` borrows from the caller's blob
claude Oct 29, 2026
65cfe08
s3: `report_progress` is handed what it reports
claude Oct 29, 2026
b800394
webcore: `Request::clone_into` becomes `clone_with_url`, which return…
claude Oct 29, 2026
fc78227
webcore: a file read's bytes reach `ReadFileToJs` as the `Box<[u8]>` …
claude Oct 29, 2026
b77626a
webcore: a file read's text is made from the `Box<[u8]>`
claude Oct 29, 2026
2127bbe
node: whoever holds a ref on a `StatWatcher` holds a `RefPtr`
claude Oct 29, 2026
c4fdf5b
node: `StatWatcherScheduler`'s pool pass and shutdown read through th…
claude Oct 29, 2026
ce8a913
runtime, jsc: ten timer owners drop their unused `from_timer_ptr`
claude Oct 29, 2026
c225b58
node: `FSWatchTaskPosix` batches `Event`s without a `needs_free` flag
claude Oct 29, 2026
23b298a
node: `NewAsyncCpTask::run_from_js_thread` takes the dispatcher's global
claude Oct 29, 2026
067427a
node: `CompressionStreamImpl` finds its task through `IntrusiveField<…
claude Oct 29, 2026
d64180e
node: `Bun__mkdirp` makes its own `NodeFS`
claude Oct 29, 2026
27488aa
node:quic: the lsquic vtable names its context types
claude Oct 29, 2026
d4199a0
node:quic: the connection and stream callbacks take `&QuicSession` / …
claude Oct 29, 2026
fc83674
node: the FSEvents callbacks take `&FSEventsLoop`
claude Oct 29, 2026
af58eb2
node: the FSEvents thread handle lives in the mutex that guards it
claude Oct 29, 2026
b296347
node: `UVFSRequest::run_from_js_thread` takes `self: Box<Self>`, and …
claude Oct 29, 2026
cd899af
node: the Windows `PathWatcher::emit` takes `&self`
claude Oct 29, 2026
fe6c424
resolver: a stale `DirEntry` is refreshed through the `&'static mut` …
claude Oct 29, 2026
2eefb10
bundler: `Worker::get` takes the `ThreadPool`
claude Oct 29, 2026
1e9d0be
bundler: `get_ast` takes `&BundleOptions`, `get_empty_ast` and `get_e…
claude Oct 29, 2026
92e6893
ast: `Symbol.import_item_status` is a `Cell`
claude Oct 29, 2026
7af1e5b
js_parser: `ScopeOrder` and `Part.scopes` hold the `StoreRef<Scope>` …
claude Oct 29, 2026
1ec744d
bundler: `relative_paths_list_ptr` is declared with `bss_string_list!`
claude Oct 29, 2026
e11be19
ast: `ImportRecord` is `Clone`, and the CSS import order clones its l…
claude Oct 29, 2026
8123fd8
bundler, resolver: delete six `Send`/`Sync` impls that nothing asks f…
claude Oct 29, 2026
d77d905
Say why `Relaxed` is enough for four new atomics
claude Nov 5, 2026
9614e6f
Revert "node: `NewAsyncCpTask::run_from_js_thread` takes the dispatch…
claude Nov 5, 2026
0ca2d30
Merge branch 'main' into claude/remove-unsafe-batch-13
claude Nov 7, 2026
3a3faf6
Merge branch 'main' into claude/remove-unsafe-batch-13
claude Nov 10, 2026
6a53a5a
uws_sys: `AnyResponse` panics in place on a null handle again
claude Nov 10, 2026
2b0d810
bundler: the pool's exclusive borrow is written in its one caller
claude Nov 10, 2026
607944e
parsers: the JSON tape's box is released, not leaked
claude Nov 10, 2026
7b1aa72
Comments of the last three commits, and `heap::take` for the JSON tape
claude Nov 10, 2026
fef501f
Revert "node: `Bun__mkdirp` makes its own `NodeFS`"
claude Nov 11, 2026
1566ff6
Merge branch 'main' into claude/remove-unsafe-batch-13
claude Nov 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion docs/project/bindgen.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ and `&mut self` for its object, because nothing can invalidate either while it
runs.

The compiler holds a body to its capability as far as this API goes. The older API has
other ways to a `&JSGlobalObject` (`VirtualMachine::get().global()`, a field of the struct), and a
other ways to a `&JSGlobalObject` (`VirtualMachine::get().global()`, `heap.bun_vm().global()`, a field of the struct), and a
body that takes one of those to run JavaScript has stepped around the check.

Much of the older API takes a `&JSGlobalObject` only to allocate or to throw. A `"heap"`
Expand Down
35 changes: 15 additions & 20 deletions src/ast/ast_memory_allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use core::ptr;

use bun_alloc::Arena;
use bun_alloc::ast_alloc::{self, AstAllocState};
use bun_ptr::{BackRef, Mut};

use crate::expr;
use crate::stmt;
Expand Down Expand Up @@ -64,7 +65,6 @@ pub struct ASTMemoryAllocator {
ast_state: Option<Box<AstAllocState>>,
/// `true` while `ast_state` is installed in the `AST_ALLOC` thread-local.
ast_pushed: bool,
previous: *mut ASTMemoryAllocator,
previous_logger: *const Arena,
/// The `AST_ALLOC` occupant displaced by `push()`, restored by `pop()`.
previous_ast_state: Option<Box<AstAllocState>>,
Expand All @@ -78,7 +78,6 @@ impl Default for ASTMemoryAllocator {
arena_dirty: false,
ast_state: None,
ast_pushed: false,
previous: ptr::null_mut(),
previous_logger: ptr::null(),
previous_ast_state: None,
}
Expand Down Expand Up @@ -138,7 +137,6 @@ impl ASTMemoryAllocator {
arena_dirty: false,
ast_state: None,
ast_pushed: false,
previous: ptr::null_mut(),
previous_logger: ptr::null(),
previous_ast_state: None,
}
Expand Down Expand Up @@ -221,10 +219,9 @@ impl ASTMemoryAllocator {
}
}
self.arena_dirty = true;
self.previous = ptr::null_mut();
let mut ast_scope = Scope {
current: Some(self),
previous: Some(stmt::data::Store::memory_allocator()),
previous: None,
previous_logger: ptr::null(),
previous_ast_state: None,
entered: false,
Expand Down Expand Up @@ -272,8 +269,9 @@ impl ASTMemoryAllocator {
// directly into it across many modules with no intervening `reset()`).
self.arena_dirty = true;
let arena: *const Arena = self.arena_raw();
stmt::data::Store::set_memory_allocator(std::ptr::from_mut::<Self>(self));
expr::data::Store::set_memory_allocator(std::ptr::from_mut::<Self>(self));
let this = Some(BackRef::new_mut(self));
stmt::data::Store::set_memory_allocator(this);
expr::data::Store::set_memory_allocator(this);
let spill = self.arena().heap_ptr();
if !self.ast_pushed {
// Capture the outer override only on the first (un-popped) push so
Expand All @@ -298,10 +296,8 @@ impl ASTMemoryAllocator {
}

pub fn pop(&mut self) {
let prev = self.previous;
debug_assert!(prev != std::ptr::from_mut::<Self>(self));
stmt::data::Store::set_memory_allocator(prev);
expr::data::Store::set_memory_allocator(prev);
stmt::data::Store::set_memory_allocator(None);
expr::data::Store::set_memory_allocator(None);
crate::set_data_store_override(self.previous_logger);
if self.ast_pushed {
// Take the state back; its contents stay alive until the owner
Expand All @@ -313,7 +309,6 @@ impl ASTMemoryAllocator {
"ASTMemoryAllocator::pop: the pushed AstAllocState was uninstalled by someone else"
);
}
self.previous = ptr::null_mut();
self.previous_logger = ptr::null();
}

Expand All @@ -327,7 +322,7 @@ impl ASTMemoryAllocator {

pub struct Scope<'a> {
current: Option<&'a mut ASTMemoryAllocator>,
previous: Option<*mut ASTMemoryAllocator>,
previous: Option<BackRef<ASTMemoryAllocator, Mut>>,
previous_logger: *const Arena,
/// The `AST_ALLOC` occupant displaced by `enter()`, restored by `exit()`.
previous_ast_state: Option<Box<AstAllocState>>,
Expand All @@ -351,15 +346,15 @@ impl<'a> Default for Scope<'a> {
impl<'a> Scope<'a> {
pub fn enter(&mut self) {
debug_assert!(
expr::data::Store::memory_allocator() == stmt::data::Store::memory_allocator()
expr::data::Store::MEMORY_ALLOCATOR.get() == stmt::data::Store::MEMORY_ALLOCATOR.get()
);
debug_assert!(!self.entered);

self.previous = Some(expr::data::Store::memory_allocator());
self.previous = expr::data::Store::MEMORY_ALLOCATOR.get();
self.previous_logger = crate::data_store_override();
self.entered = true;

let (current, arena): (*mut ASTMemoryAllocator, *const Arena) = match &mut self.current {
let (current, arena) = match &mut self.current {
Some(r) => {
debug_assert!(
!r.ast_pushed,
Expand All @@ -371,21 +366,21 @@ impl<'a> Scope<'a> {
state.set_spill_heap(r.arena().heap_ptr());
self.previous_ast_state = ast_alloc::swap_state(Some(state));
r.ast_pushed = true;
(std::ptr::from_mut::<ASTMemoryAllocator>(*r), arena)
(Some(BackRef::new_mut(*r)), arena)
}
None => {
// Block-store scope with no `ASTMemoryAllocator`: detach
// `AstAlloc` to the global-mimalloc fallback.
self.previous_ast_state = ast_alloc::swap_state(None);
(ptr::null_mut(), ptr::null())
(None, ptr::null())
}
};

expr::data::Store::set_memory_allocator(current);
stmt::data::Store::set_memory_allocator(current);
crate::set_data_store_override(arena);

if current.is_null() {
if current.is_none() {
stmt::data::Store::begin();
expr::data::Store::begin();
}
Expand All @@ -396,7 +391,7 @@ impl<'a> Scope<'a> {
return;
}
self.entered = false;
let prev = self.previous.unwrap_or(ptr::null_mut());
let prev = self.previous;
expr::data::Store::set_memory_allocator(prev);
stmt::data::Store::set_memory_allocator(prev);
crate::set_data_store_override(self.previous_logger);
Expand Down
1 change: 1 addition & 0 deletions src/ast/import_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use bun_paths::fs::Path;
// `bun_ast::import_record::{ImportKind, Index, Loader}` keep resolving.
pub use crate::{ImportKind, Index, Loader};

#[derive(Clone)]
pub struct ImportRecord<'a> {
pub range: Range,
pub path: Path<'a>,
Expand Down
24 changes: 22 additions & 2 deletions src/ast/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,8 +1232,7 @@ pub enum Level {
Warn, // 3
Err, // 4
}
// SAFETY: `#[repr(i8)]`, five variants, no payload — 1 byte, no padding.
bun_core::unsafe_impl_atom!(Level);
bun_core::impl_no_uninit!(Level);

impl Level {
pub fn at_least(self, other: Level) -> bool {
Expand Down Expand Up @@ -3069,6 +3068,27 @@ mod range_of_identifier_tests {
}
}

#[cfg(test)]
mod level_tests {
use super::*;

#[test]
fn every_level_survives_the_atomic_cell() {
let cell = bun_core::AtomicCell::new(Level::Warn);
assert_eq!(cell.load(), Level::Warn);
for level in [
Level::Verbose,
Level::Debug,
Level::Info,
Level::Warn,
Level::Err,
] {
cell.store(level);
assert_eq!(cell.load(), level);
}
}
}

#[cfg(test)]
mod location_tests {
use super::*;
Expand Down
26 changes: 12 additions & 14 deletions src/ast/new_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ macro_rules! thread_local_ast_store {
use ::core::cell::Cell;
type Backing = $Backing;

// `#[thread_local]` (bare `__thread` slot) — `memory_allocator()` is
// `#[thread_local]` (bare `__thread` slot) — `MEMORY_ALLOCATOR` is
// read on every node `alloc` (the hottest TLS in the parser), and
// the `thread_local!` macro's `LocalKey` wrapper showed up in
// next-lint profiles. All three are `Cell<ptr|bool>` (no destructor,
Expand Down Expand Up @@ -391,28 +391,26 @@ macro_rules! thread_local_ast_store {
unsafe { INSTANCE.get().as_mut() }
}
#[inline]
pub fn memory_allocator() -> *mut $crate::ASTMemoryAllocator {
MEMORY_ALLOCATOR
.get()
.map_or(::core::ptr::null_mut(), ::bun_ptr::BackRef::as_ptr)
pub fn has_memory_allocator() -> bool {
MEMORY_ALLOCATOR.get().is_some()
}
#[inline]
pub(crate) fn set_memory_allocator(p: *mut $crate::ASTMemoryAllocator) {
MEMORY_ALLOCATOR.set(::core::ptr::NonNull::new(p).map(|nn|
// SAFETY: `p` is the caller's live allocator with write provenance.
unsafe { ::bun_ptr::BackRef::from_raw_mut(nn.as_ptr()) }));
pub(crate) fn set_memory_allocator(
p: Option<::bun_ptr::BackRef<$crate::ASTMemoryAllocator, ::bun_ptr::Mut>>,
) {
MEMORY_ALLOCATOR.set(p);
}

pub fn create() {
if !instance().is_null() || !memory_allocator().is_null() {
if !instance().is_null() || has_memory_allocator() {
return;
}
INSTANCE.set(Backing::init());
}

/// create || reset
pub(crate) fn begin() {
if !memory_allocator().is_null() {
if has_memory_allocator() {
return;
}
match instance_mut() {
Expand All @@ -426,7 +424,7 @@ macro_rules! thread_local_ast_store {
}

pub fn reset() {
if DISABLE_RESET.get() || !memory_allocator().is_null() {
if DISABLE_RESET.get() || has_memory_allocator() {
return;
}
// Caller contract — instance is set when reset() is called.
Expand All @@ -448,7 +446,7 @@ macro_rules! thread_local_ast_store {
}

pub fn deinit() {
if instance().is_null() || !memory_allocator().is_null() {
if instance().is_null() || has_memory_allocator() {
return;
}
// SAFETY: checked non-null above; `destroy` frees the `Store`
Expand All @@ -460,7 +458,7 @@ macro_rules! thread_local_ast_store {
#[inline]
pub(crate) fn assert() {
if cfg!(debug_assertions) {
if instance().is_null() && memory_allocator().is_null() {
if instance().is_null() && !has_memory_allocator() {
unreachable!("Store must be init'd");
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/nodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -997,7 +997,7 @@ pub type DependencyList = bun_alloc::AstVec<Dependency>;
/// splitting.
pub struct Part {
pub stmts: StoreSlice<Stmt>,
pub scopes: StoreSlice<*mut Scope>, // TODO: &'bump mut [&'bump mut Scope]
pub scopes: StoreSlice<StoreRef<Scope>>, // TODO: &'bump mut [&'bump mut Scope]

/// Each is an index into the file-level import record list
pub import_record_indices: PartImportRecordIndices,
Expand Down
7 changes: 4 additions & 3 deletions src/ast/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ pub struct Symbol {
/// compile-time error like we do for real import items. This status lets us
/// avoid this. We also need to be able to replace such import items with
/// undefined, which this status is also used for.
pub import_item_status: ImportItemStatus,
/// `Cell`: the linker, whose graph is shared across threads, sets it in a serial step.
pub import_item_status: Cell<ImportItemStatus>,

/// Packed boolean state — see [`SymbolFlags`].
pub flags: SymbolFlags,
Expand Down Expand Up @@ -177,7 +178,7 @@ impl Default for Symbol {
chunk_index: AtomicU32::new(INVALID_CHUNK_INDEX),
nested_scope_slot: INVALID_NESTED_SCOPE_SLOT,
kind: Kind::Other,
import_item_status: ImportItemStatus::None,
import_item_status: Cell::new(ImportItemStatus::None),
flags: SymbolFlags::empty(),
}
}
Expand Down Expand Up @@ -244,7 +245,7 @@ impl Symbol {
/// that a hoisting merge links has no import item status.
#[inline]
pub fn is_bound_import_item(&self) -> bool {
self.import_item_status != ImportItemStatus::None && self.has_link()
self.import_item_status.get() != ImportItemStatus::None && self.has_link()
}
}

Expand Down
3 changes: 1 addition & 2 deletions src/base64/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,8 +592,7 @@ pub mod zig_base64 {
let mut dest_idx: usize = 0;
let mut leftover_idx: Option<usize> = None;
for (src_idx, &c) in source.iter().enumerate() {
// SAFETY: `c: u8` so `c as usize` is in 0..=255, and `char_to_index` is `[u8; 256]`.
let d = unsafe { *self.char_to_index.get_unchecked(c as usize) };
let d = self.char_to_index[c as usize];
if d == Self::INVALID_CHAR {
if self.pad_char.is_none() || c != self.pad_char.unwrap() {
return Err(Error::InvalidCharacter);
Expand Down
18 changes: 4 additions & 14 deletions src/bun_alloc/MimallocArena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,28 +565,18 @@ impl MimallocArena {
pub fn allocated_bytes(&self) -> usize {
extern "C" fn visit(
_heap: *const mimalloc::Heap,
area: *const mimalloc::mi_heap_area_t,
area: &mimalloc::mi_heap_area_t,
_block: *mut c_void,
_block_size: usize,
arg: *mut c_void,
total: &mut usize,
) -> bool {
// SAFETY: mimalloc passes a valid `area` for each heap area when
// `visit_all_blocks == false`; `arg` is the `&mut usize` we passed.
unsafe {
let total = &mut *arg.cast::<usize>();
*total += (*area).used.saturating_mul((*area).full_block_size);
}
*total += area.used.saturating_mul(area.full_block_size);
true
}
let mut total: usize = 0;
// SAFETY: `self.heap` is live; `visit` upholds the callback contract.
unsafe {
mimalloc::mi_heap_visit_blocks(
self.heap_ptr(),
false,
Some(visit),
(&raw mut total).cast(),
);
mimalloc::mi_heap_visit_blocks(self.heap_ptr(), false, Some(visit), &mut total);
}
total
}
Expand Down
Loading
Loading