Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions src/jsc/VirtualMachine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ pub struct VirtualMachine {
/// `RawSlice` carries the BACKREF outlives-holder invariant — read via
/// `main()`.
main: bun_ptr::RawSlice<u8>,
/// `process.argv[1]` when the `node` shim resolved `main` from it. Node keeps the given path too.
main_for_argv: Option<&'static [u8]>,
pub main_is_html_entrypoint: bool,
pub main_resolved_path: bun_core::String,
pub main_hash: u32,
Expand Down Expand Up @@ -3120,6 +3122,7 @@ impl VirtualMachine {
// `log` is a fresh leaked Box; outlives the VM.
addr_of_mut!((*vm).log).write(NonNull::new(log));
addr_of_mut!((*vm).main).write(bun_ptr::RawSlice::EMPTY);
addr_of_mut!((*vm).main_for_argv).write(None);
addr_of_mut!((*vm).main_hash).write(0);
addr_of_mut!((*vm).main_resolved_path).write(bun_core::String::EMPTY);
addr_of_mut!((*vm).hide_bun_stackframes).write(true);
Expand Down Expand Up @@ -3308,6 +3311,17 @@ impl VirtualMachine {
self.main = bun_ptr::RawSlice::new(path);
}

/// `process.argv[1]`: the path from `set_main_for_argv`, else `main()`.
#[inline]
pub fn main_for_argv(&self) -> &[u8] {
self.main_for_argv.unwrap_or_else(|| self.main())
}

#[inline]
pub fn set_main_for_argv(&mut self, path: &'static [u8]) {
self.main_for_argv = Some(path);
}

/// `eventLoop().waitForPromise(promise)` — spin tick/auto_tick until
/// `promise` settles. Thin forwarder; body lives in
/// [`crate::event_loop::EventLoop::wait_for_promise`].
Expand Down
9 changes: 7 additions & 2 deletions src/runtime/cli/fuzzilli_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use bun_core::{Environment, Global};
use bun_sys::{self as sys, Fd, FdExt, O};

#[cfg(unix)]
use super::run_command::RunCommand;
use super::run_command::{EntryPath, RunCommand};
use crate::Command;

pub(crate) struct FuzzilliCommand;
Expand Down Expand Up @@ -92,7 +92,12 @@ impl FuzzilliCommand {
let temp_path: &[u8] = b"/tmp/bun-fuzzilli-reprl.js";
// The `Run.boot` entry point is hosted on `RunCommand` to avoid the
// higher-tier crate cycle (see run_command.rs §`Run`).
let result = RunCommand::boot(_ctx, temp_path.to_vec().into_boxed_slice(), None);
let result = RunCommand::boot(
_ctx,
temp_path.to_vec().into_boxed_slice(),
EntryPath::Resolved,
None,
);

// `defer fd.close()` — Fd is Copy and has no Drop; close explicitly.
temp_file_fd.close();
Expand Down
59 changes: 53 additions & 6 deletions src/runtime/cli/run_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ pub(crate) struct ConfigureEnvOptions {
pub(crate) store_root_fd: bool,
}

/// What the `entry_path` of [`RunCommand::boot`] names.
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) enum EntryPath {
/// The file to run.
Resolved,
/// A path as `node` takes it. `Run::start` resolves it and it stays `process.argv[1]` (Node: `resolveMainPath`).
Unresolved,
}

pub(crate) struct RunCommand;

impl RunCommand {
Expand Down Expand Up @@ -934,6 +943,7 @@ Full documentation is available at <magenta>https://bun.com/docs/cli/run<r>
pub(crate) fn boot(
ctx: &mut ContextData,
entry_path: Box<[u8]>,
entry_kind: EntryPath,
loader: Option<Loader>,
) -> crate::Result<()> {
if !ctx.debug.loaded_bunfig {
Expand Down Expand Up @@ -1111,10 +1121,29 @@ Full documentation is available at <magenta>https://bun.com/docs/cli/run<r>
ctx,
vm,
entry_path: run_entry,
entry_kind,
}
.start()
}

/// The module key of `entry`, for `vm.main()`. On `None` the module loader reports the failure.
fn resolve_entry_path(vm: &mut VirtualMachine, entry: &'static [u8]) -> Option<&'static [u8]> {
let top_level_dir = vm.top_level_dir();
// Like the module loader, keep the resolver's messages out of `vm.log`.
let mut log = bun_ast::Log::default();
let resolver = &raw mut vm.transpiler.resolver;
// SAFETY: `resolver` is a field of the live VM. `log` is declared
// before the guard, so the guard restores the log before `log` drops.
let _restore_log = unsafe {
bun_resolver::Resolver::scoped_log(resolver, ::core::ptr::NonNull::from(&mut log))
};
// SAFETY: `vm` is borrowed for this call, so nothing else uses its resolver.
let resolved = unsafe { &mut *resolver }
.resolve(top_level_dir, entry, bun_ast::ImportKind::EntryPointRun)
.ok()?;
Some(resolved.path_const()?.text)
}

/// Entry point for
/// `bun build --compile` executables. Mirrors [`boot`] but routes through
/// `VirtualMachine::init_with_module_graph` and applies the standalone
Expand Down Expand Up @@ -1230,6 +1259,7 @@ Full documentation is available at <magenta>https://bun.com/docs/cli/run<r>
ctx,
vm,
entry_path: entry,
entry_kind: EntryPath::Resolved,
}
.start()
}
Expand All @@ -1250,6 +1280,7 @@ pub struct Run<'a> {
/// reloader stores them too (`boot` leaks the `Box<[u8]>`, cron mode uses
/// the runner arena).
entry_path: &'static [u8],
entry_kind: EntryPath,
}

// `on_unhandled_rejection_before_close` is a plain fn pointer stored on the
Expand Down Expand Up @@ -1299,6 +1330,7 @@ impl Run<'_> {
ctx,
vm,
entry_path: mut entry,
entry_kind,
} = self;
let _api_lock = vm.global().vm().get_api_lock();

Expand Down Expand Up @@ -1431,6 +1463,16 @@ impl Run<'_> {
}
}

// Resolve last: `NODE_PRESERVE_SYMLINKS` and the `--watch`/`--hot` watcher must be on the resolver.
if entry_kind == EntryPath::Unresolved
&& vm.module_loader.eval_source.is_none()
&& let Some(resolved) = RunCommand::resolve_entry_path(vm, entry)
&& resolved != entry
{
vm.set_main_for_argv(entry);
entry = resolved;
}
Comment thread
robobun marked this conversation as resolved.

match vm.load_entry_point(entry) {
Ok(promise) => {
// SAFETY: `promise` is a live GC cell returned by the module loader.
Expand Down Expand Up @@ -1710,7 +1752,7 @@ impl RunCommand {
// owned copy by value.
let owned: Box<[u8]> = path.to_vec().into_boxed_slice();

if let Err(err) = Self::boot(ctx, owned, loader) {
if let Err(err) = Self::boot(ctx, owned, EntryPath::Resolved, loader) {
Self::boot_failed_exit(ctx, paths::basename(path), &err);
}
true
Expand Down Expand Up @@ -2869,7 +2911,7 @@ impl RunCommand {
// `basename(target_name)` (= "-"), not `basename(entry_path)`
// (= "[stdin]"), in the error message.
let owned: Box<[u8]> = entry_path.to_vec().into_boxed_slice();
if let Err(err) = Self::boot(ctx, owned, None) {
if let Err(err) = Self::boot(ctx, owned, EntryPath::Resolved, None) {
Self::boot_failed_exit(ctx, b"-", &err);
}
Ok(true)
Expand Down Expand Up @@ -2919,7 +2961,7 @@ impl RunCommand {
let entry: Box<[u8]> = entry_point_buf[..cwd_len + EVAL_TRIGGER.len()]
.to_vec()
.into_boxed_slice();
Self::boot(ctx, entry, None)
Self::boot(ctx, entry, EntryPath::Resolved, None)
}

/// `node` argv0 emulation. Port of `execAsIfNode`.
Expand Down Expand Up @@ -2954,7 +2996,7 @@ impl RunCommand {
let entry: Box<[u8]> = entry_point_buf[..cwd_len + EVAL_TRIGGER.len()]
.to_vec()
.into_boxed_slice();
return Self::boot(ctx, entry, None);
return Self::boot(ctx, entry, EntryPath::Resolved, None);
}

if ctx.positionals.is_empty() {
Expand Down Expand Up @@ -2997,7 +3039,7 @@ impl RunCommand {
// `Global::configure_allocator` and (b) uses the
// `Output.err(err, "Failed to run script \"...\"")` form.
let basename: Box<[u8]> = paths::basename(&normalized).to_vec().into_boxed_slice();
if let Err(err) = Self::boot(ctx, normalized, None) {
if let Err(err) = Self::boot(ctx, normalized, EntryPath::Unresolved, None) {
Self::exec_as_if_node_boot_failed(ctx, &basename, err);
}
Ok(())
Expand Down Expand Up @@ -4036,7 +4078,12 @@ impl BunXFastPath {
::core::slice::from_raw_parts_mut(raw.cast::<u8>(), bun_paths::PATH_MAX_WIDE * 2)
};
let utf8 = strings::convert_utf16_to_utf8_in_buffer(out_buf, wpath);
if let Err(err) = RunCommand::boot(ctx, utf8.to_vec().into_boxed_slice(), None) {
if let Err(err) = RunCommand::boot(
ctx,
utf8.to_vec().into_boxed_slice(),
EntryPath::Resolved,
None,
) {
// SAFETY: `ctx.log` was set in `create_context_data`.
let _ = unsafe { &mut *ctx.log }.print(std::ptr::from_mut(Output::error_writer()));
Output::err(
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/node/node_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ mod _impl {
if worker.is_some_and(|w| w.eval_mode()) {
args_list.push(BunString::static_("[worker eval]"));
} else {
args_list.push(BunString::borrow_utf8(vm.main()));
args_list.push(BunString::borrow_utf8(vm.main_for_argv()));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/shell/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1701,7 +1701,7 @@ impl Interpreter {
// SAFETY: `bun_vm()` on a JS event loop returns the live
// `*VirtualMachine` owning that loop.
let vm = unsafe { &*vm_ptr };
let main = vm.main();
let main = vm.main_for_argv();
if !main.is_empty() {
if int == 0 {
out.extend_from_slice(main);
Expand Down
Loading
Loading