diff --git a/src/runtime/cli/pack_command.rs b/src/runtime/cli/pack_command.rs index 855960f9d37e..7a2b23173949 100644 --- a/src/runtime/cli/pack_command.rs +++ b/src/runtime/cli/pack_command.rs @@ -69,21 +69,12 @@ fn file_to_source_at(dir: &Dir, path: &ZStr) -> bun_sys::Maybe )) } -/// `manager.log` deref — set once at `init()`. -/// Raw-pointer receiver so the borrow doesn't conflict with the simultaneous -/// `&mut workspace_package_json_cache` borrow at the call site. -#[inline] -fn pm_log<'a>(m: *mut PackageManager) -> &'a mut bun_ast::Log { - // SAFETY: `m` came from `&mut PackageManager`; `log` is non-null after - // `PackageManager::init()`. - unsafe { &mut *(*m).log } -} -/// `manager.workspace_package_json_cache` field projection via raw pointer. +/// Raw projection: `pack()` holds the `&mut MapEntry` this yields across other `ctx.manager` uses. #[inline] fn pm_workspace_cache<'a>( m: *mut PackageManager, ) -> &'a mut WorkspacePackageJSONCache::WorkspacePackageJSONCache { - // SAFETY: `m` came from `&mut PackageManager`; field disjoint from `log`. + // SAFETY: `m` is `pack()`'s live `ctx.manager`; `pack()` touches the cache only through here. unsafe { &mut (*m).workspace_package_json_cache } } #[inline] @@ -106,7 +97,8 @@ pub(crate) struct PackCommand; // Context // ─────────────────────────────────────────────────────────────────────────── -pub(crate) struct Context<'a> { +/// `'l` is the caller-local lockfile; `'a` flows on into the `Publish::Context` `pack()` returns. +pub(crate) struct Context<'a, 'l> { pub(crate) manager: &'a mut PackageManager, // allocator param dropped — global mimalloc (see PORTING.md §Allocators) pub(crate) command_ctx: Command::Context<'a>, @@ -115,7 +107,7 @@ pub(crate) struct Context<'a> { /// it's possible we will need it for finding /// workspace versions. This is the only valid lockfile /// pointer in this file. `manager.lockfile` is incorrect - pub(crate) lockfile: Option<&'a Lockfile>, + pub(crate) lockfile: Option<&'l Lockfile>, pub(crate) bundled_deps: Vec, @@ -130,7 +122,7 @@ pub struct Stats { pub(crate) bundled_deps: usize, } -impl<'a> Context<'a> { +impl Context<'_, '_> { pub(crate) fn print_summary( stats: Stats, maybe_shasum: Option<&[u8; sha::SHA1::DIGEST]>, @@ -215,14 +207,8 @@ impl PackCommand { } let mut lockfile = Lockfile::default(); - // `log` is non-null after `PackageManager::init()`. - let log_ptr: *mut bun_ast::Log = manager.log; - let manager_ptr: *mut PackageManager = manager; - // SAFETY: `manager_ptr`/`log_ptr` came from live `&mut`; reborrowed - // disjointly (`log` is a separate allocation from the manager fields - // `load_from_cwd` touches). - let load_from_disk_result = lockfile - .load_from_cwd::(Some(unsafe { &mut *manager_ptr }), unsafe { &mut *log_ptr }); + let log = manager.log_mut(); + let load_from_disk_result = lockfile.load_from_cwd::(Some(&mut *manager), log); let lockfile_ref: Option<&Lockfile> = match load_from_disk_result { LoadResult::Ok(ok) => Some(&*ok.lockfile), @@ -256,8 +242,8 @@ impl PackCommand { ); } } - if pm_log(manager_ptr).has_errors() { - let _ = pm_log(manager_ptr).print(std::ptr::from_mut(Output::error_writer())); + if log.has_errors() { + let _ = log.print(std::ptr::from_mut(Output::error_writer())); } Global::crash(); } @@ -269,7 +255,7 @@ impl PackCommand { // package.json path before constructing `Context`. let abs_pkg_json = ZBox::from_bytes(manager.original_package_json_path.as_bytes()); - let mut pack_ctx = Context { + let pack_ctx = Context { manager, command_ctx: ctx, lockfile: lockfile_ref, @@ -278,7 +264,7 @@ impl PackCommand { }; // just pack the current workspace - if let Err(err) = pack::(&mut pack_ctx, &abs_pkg_json) { + if let Err(err) = pack::(pack_ctx, &abs_pkg_json) { match err { PackError::OutOfMemory => bun_core::out_of_memory(), PackError::MissingPackageName | PackError::MissingPackageVersion => { @@ -1896,23 +1882,16 @@ fn opt_pack_gzip_level(m: &PackageManager) -> Option<&[u8]> { // Const generics cannot vary the // return type directly, so both instantiations return an Option that is // `Some` only when FOR_PUBLISH == true. -pub(crate) type PackReturn<'a, const FOR_PUBLISH: bool> = Option>; - -pub(crate) fn pack( - ctx: &mut Context<'_>, +pub(crate) fn pack<'a, const FOR_PUBLISH: bool>( + mut ctx: Context<'a, '_>, abs_package_json_path: &ZStr, -) -> Result, PackError> { - // Raw pointer for the `pm_workspace_cache`/`pm_log` disjoint-field - // projections and the `'static` lifetime extension when returning - // `Publish::Context`. +) -> Result>, PackError> { let manager_ptr: *mut PackageManager = &raw mut *ctx.manager; let log_level = ctx.manager.options.log_level; let bump = pack_bump(); - // Note: `workspace_package_json_cache` and `log` are disjoint fields on - // `PackageManager`; route through raw-pointer field projections so the - // two `&mut` borrows don't conflict. + let log = ctx.manager.log_mut(); let mut json = match pm_workspace_cache(manager_ptr).get_with_path( - pm_log(manager_ptr), + log, abs_package_json_path.as_bytes(), WorkspacePackageJSONCache::GetJSONOptions { guess_indentation: true, @@ -1933,7 +1912,7 @@ pub(crate) fn pack( "failed to parse package.json: {}", format_args!("{}", bstr::BStr::new(abs_package_json_path.as_bytes())), ); - let _ = pm_log(manager_ptr).print(std::ptr::from_mut(Output::error_writer())); + let _ = log.print(std::ptr::from_mut(Output::error_writer())); Global::crash(); } WorkspacePackageJSONCache::GetResult::Entry(entry) => entry, @@ -2163,8 +2142,9 @@ pub(crate) fn pack( let _ = pm_workspace_cache(manager_ptr).map.remove(cache_key); // Re-read package.json from disk + let log = ctx.manager.log_mut(); json = match pm_workspace_cache(manager_ptr).get_with_path( - pm_log(manager_ptr), + log, abs_package_json_path.as_bytes(), WorkspacePackageJSONCache::GetJSONOptions { guess_indentation: true, @@ -2185,7 +2165,7 @@ pub(crate) fn pack( "failed to parse package.json: {}", format_args!("{}", bstr::BStr::new(abs_package_json_path.as_bytes())), ); - let _ = pm_log(manager_ptr).print(std::ptr::from_mut(Output::error_writer())); + let _ = log.print(std::ptr::from_mut(Output::error_writer())); Global::crash(); } WorkspacePackageJSONCache::GetResult::Entry(entry) => entry, @@ -2376,7 +2356,7 @@ pub(crate) fn pack( let mut bundled_pack_queue = iterate_bundled_deps( &mut ctx.bundled_deps, &mut ctx.stats, - pm_log(manager_ptr), + ctx.manager.log_mut(), &root_dir, log_level, )?; @@ -2388,7 +2368,7 @@ pub(crate) fn pack( // don't create the tarball, but run scripts if they exist print_archived_files_and_packages::( - ctx, + &mut ctx, &root_dir, PackListOrQueue::Queue(&mut pack_queue), 0, @@ -2446,17 +2426,9 @@ pub(crate) fn pack( package_version, &mut dest_buf[..], ); - // Note: `manager`/`command_ctx` reborrowed via raw pointer — - // both are process-lifetime - // singletons (see `cli::command::GLOBAL_CLI_CTX`). return Ok(Some(Publish::Context { - // SAFETY: `manager_ptr` was derived from `&mut *ctx.manager`; the - // process-lifetime singleton outlives the returned `Publish::Context`. - manager: unsafe { &mut *manager_ptr }, - // SAFETY: `ctx.command_ctx` aliases the process-lifetime - // `GLOBAL_CLI_CTX` singleton (see note above); reborrowed - // disjointly from `manager`. - command_ctx: unsafe { &mut *std::ptr::from_mut(ctx.command_ctx) }, + manager: ctx.manager, + command_ctx: ctx.command_ctx, package_name: package_name.into(), package_version: package_version.into(), abs_tarball_path: ZStr::boxed(abs_tarball_dest.as_bytes()), @@ -2599,7 +2571,7 @@ pub(crate) fn pack( // exit and `end()` once after the loops. entry = archive_package_json( - ctx, + &mut ctx, // SAFETY: `archive` is the non-null `*mut Archive` returned by // `Archive::write_new()` above; only this thread accesses it. unsafe { &mut *archive }, @@ -2674,7 +2646,7 @@ pub(crate) fn pack( }); entry = add_archive_entry( - ctx, + &mut ctx, fd, &stat, &item.path, @@ -2729,7 +2701,7 @@ pub(crate) fn pack( }; entry = add_archive_entry( - ctx, + &mut ctx, file.handle, &stat, &item.path, @@ -2885,7 +2857,7 @@ pub(crate) fn pack( }; print_archived_files_and_packages::( - ctx, + &mut ctx, &root_dir, PackListOrQueue::List(&pack_list), edited_package_json.len(), @@ -2923,13 +2895,8 @@ pub(crate) fn pack( if FOR_PUBLISH { return Ok(Some(Publish::Context { - // SAFETY: `manager_ptr` was derived from `&mut *ctx.manager`; the - // process-lifetime singleton outlives the returned `Publish::Context`. - manager: unsafe { &mut *manager_ptr }, - // SAFETY: `ctx.command_ctx` aliases the process-lifetime - // `GLOBAL_CLI_CTX` singleton (see dry-run note above); - // reborrowed disjointly from `manager`. - command_ctx: unsafe { &mut *std::ptr::from_mut(ctx.command_ctx) }, + manager: ctx.manager, + command_ctx: ctx.command_ctx, package_name: package_name.into(), package_version: package_version.into(), abs_tarball_path: ZStr::boxed(abs_tarball_dest.as_bytes()), @@ -3143,7 +3110,7 @@ impl<'a> fmt::Display for TarballNameFormatter<'a> { } fn archive_package_json( - ctx: &mut Context<'_>, + ctx: &mut Context<'_, '_>, archive: &mut Archive, entry: *mut ArchiveEntry, root_dir: &Dir, @@ -3193,7 +3160,7 @@ fn archive_package_json( } fn add_archive_entry( - ctx: &mut Context<'_>, + ctx: &mut Context<'_, '_>, file: Fd, stat: &bun_sys::Stat, filename: &ZStr, @@ -3816,7 +3783,7 @@ enum PackListOrQueue<'a> { } fn print_archived_files_and_packages( - ctx: &mut Context<'_>, + ctx: &mut Context<'_, '_>, root_dir_std: &Dir, pack_list: PackListOrQueue<'_>, package_json_len: usize, diff --git a/src/runtime/cli/publish_command.rs b/src/runtime/cli/publish_command.rs index ac54ef762f7f..dc64fb63e852 100644 --- a/src/runtime/cli/publish_command.rs +++ b/src/runtime/cli/publish_command.rs @@ -452,22 +452,13 @@ impl<'a, const DIRECTORY_PUBLISH: bool> Context<'a, DIRECTORY_PUBLISH> { /// `bun publish` without a tarball path. Automatically pack the current workspace and get /// information required for publishing - // Note: the return type is pinned to `Context<'static, true>`, the only - // valid shape. `'static` matches `pack::pack`'s return — - // the embedded `&mut PackageManager` / `Command::Context` are process- - // lifetime singletons reborrowed through raw pointers there. pub(crate) fn from_workspace( ctx: Command::Context<'a>, manager: &'a mut PackageManager, - ) -> Result, FromWorkspaceError> { + ) -> Result, FromWorkspaceError> { let mut lockfile = Lockfile::default(); - let manager_ptr: *mut PackageManager = manager; let log: &mut bun_ast::Log = manager.log_mut(); - // SAFETY: `manager_ptr` was just derived from `manager: &'a mut PackageManager`; - // `log` borrows the disjoint `.log` field, so the re-derived `&mut` - // never touches memory the live `log` borrow covers. - let load_from_disk_result = - lockfile.load_from_cwd::(Some(unsafe { &mut *manager_ptr }), log); + let load_from_disk_result = lockfile.load_from_cwd::(Some(&mut *manager), log); let lockfile_ref: Option<&Lockfile> = match load_from_disk_result { LoadResult::Ok(ok) => Some(&*ok.lockfile), @@ -504,18 +495,11 @@ impl<'a, const DIRECTORY_PUBLISH: bool> Context<'a, DIRECTORY_PUBLISH> { // Note: capture the package.json path before constructing // `pack::Context` so the `&mut PackageManager` borrow doesn't conflict. - // SAFETY: `manager_ptr` came from `&'a mut PackageManager`. - let abs_pkg_json = bun_core::ZBox::from_bytes( - unsafe { &*manager_ptr } - .original_package_json_path - .as_bytes(), - ); + let abs_pkg_json = + bun_core::ZBox::from_bytes(manager.original_package_json_path.as_bytes()); - let mut pack_ctx = pack::Context { - // SAFETY: `manager_ptr` came from `&'a mut PackageManager`; - // `lockfile_ref` borrows the local `lockfile`, not the manager, - // so the re-derived `&mut` is the only live manager borrow. - manager: unsafe { &mut *manager_ptr }, + let pack_ctx = pack::Context { + manager, command_ctx: ctx, lockfile: lockfile_ref, bundled_deps: Vec::new(), @@ -523,7 +507,7 @@ impl<'a, const DIRECTORY_PUBLISH: bool> Context<'a, DIRECTORY_PUBLISH> { }; // `pack::` returns `Some(Context)` on success. - Ok(pack::pack::(&mut pack_ctx, &abs_pkg_json)? + Ok(pack::pack::(pack_ctx, &abs_pkg_json)? .expect("pack:: always yields a publish context")) } } @@ -552,7 +536,6 @@ impl PublishCommand { } }; drop(original_cwd); - let manager_ptr: *mut PackageManager = manager; if cli.positionals.len() > 1 { let context = match Context::::from_tarball_path( @@ -584,8 +567,8 @@ impl PublishCommand { ); } FromTarballError::InvalidPackageJSON => { - // SAFETY: `manager.log` is set once at init. - let _ = unsafe { &mut *(*manager_ptr).log } + let _ = manager + .log_mut() .print(std::ptr::from_mut(Output::error_writer())); Output::err_generic("failed to parse tarball package.json", ()); } @@ -711,23 +694,18 @@ impl PublishCommand { .put(b"npm_command", b"publish") .map_err(|_| crate::Error::Alloc(bun_alloc::AllocError))?; - // Note: reshaped for borrowck — `command_ctx: &mut ContextData` - // is held by `context`; `run_package_script_foreground` needs - // `&mut ContextData` too. Re-derive from the raw pointer. - let cmd_ctx_ptr: *mut crate::cli::command::ContextData = context.command_ctx; + let use_system_shell = context.command_ctx.debug.use_system_shell; if let Some(publish_script) = &context.publish_script { if let Err(e) = Run::run_package_script_foreground( - // SAFETY: see above. - unsafe { &mut *cmd_ctx_ptr }, + context.command_ctx, publish_script, b"publish", &abs_workspace_path, script_env, &[], context.manager.options.log_level == LogLevel::Silent, - // SAFETY: see above. - unsafe { &*cmd_ctx_ptr }.debug.use_system_shell, + use_system_shell, ) { if matches!(e, crate::Error::MissingShell) { Output::err_generic( @@ -742,16 +720,14 @@ impl PublishCommand { if let Some(postpublish_script) = &context.postpublish_script { if let Err(e) = Run::run_package_script_foreground( - // SAFETY: see above. - unsafe { &mut *cmd_ctx_ptr }, + context.command_ctx, postpublish_script, b"postpublish", &abs_workspace_path, script_env, &[], context.manager.options.log_level == LogLevel::Silent, - // SAFETY: see above. - unsafe { &*cmd_ctx_ptr }.debug.use_system_shell, + use_system_shell, ) { if matches!(e, crate::Error::MissingShell) { Output::err_generic(