bun_runtime 0.1.2

Bao runtime integration — JS engine + Bun API + event loop
// @trace REQ-ENG-001 [entity:BaoRuntime] REQ-ENG-006 REQ-IMPL-01 REQ-IMPL-02 REQ-IMPL-03 REQ-IMPL-04 REQ-IMPL-05 REQ-PURE-010 [level:library] [entity:BaoRuntime]
#![allow(unsafe_op_in_unsafe_fn)]
#![allow(unused_imports)]
// @trace REQ-PURE-010: bao_runtime (Rust) replaces deleted bun_runtime (Zig) — zero Zig deps, zero JSC refs
// @trace REQ-IMPL-01: Phase 1 SpiderMonkey engine replacement (completed)
// @trace REQ-IMPL-02: Phase 2 servo engine integration + rendering (completed)
// @trace REQ-IMPL-03: Phase 3 CDP Server implementation (completed)
// @trace REQ-IMPL-04: Phase 4 Stealth anti-fingerprinting (completed)
// @trace REQ-IMPL-05: Phase 5 Integration testing and release (completed)

pub mod bao_browser_global;
pub mod bun_api;
pub mod bun_build;
pub mod bun_builtins;
// @trace REQ-ENG-006 [api:Bun.* Bun-face completion wave] — per-domain Bun
// API modules mounted by populate_bun_object (bun_api.rs).
pub mod bun_glob_api;
pub mod bun_hash_api;
pub mod bun_inspect_api;
pub mod bun_mime_api;
pub mod bun_serde_api;
pub mod bun_spawn_sync;
pub mod bun_util_api;
pub mod bun_ffi;
pub mod bun_sqlite;
pub mod bun_test;
pub mod dispatch;
pub mod fetch_api;
// @trace REQ-ENG-006 [entity:IpcChannel] — child_process IPC + cluster fd passing (SCM_RIGHTS)
pub mod ipc_channel;
pub mod workflow_host_global;
// @trace REQ-ENG-010 [entity:FetchTasklet] — async HTTP integration helper
// (BCE-20260618-007). Shared by node_http/node_https/node_tls JS-native entries.
pub mod bun_listen;
pub mod bun_password;
pub mod bun_shell;
pub mod bun_udp;
pub mod fetch_async;
pub mod gc_store;
pub mod globals;
pub mod h3_fetch;
pub mod http_client;
pub mod install;
pub mod node_async_hooks;
pub mod node_buffer;
pub mod node_child_process;
pub mod node_cluster;
pub mod node_console;
pub mod node_constants;
pub mod node_crypto;
pub mod node_dgram;
pub mod node_diagnostics_channel;
pub mod node_dns;
pub mod node_domain;
pub mod node_events;
pub mod node_fs;
pub mod node_http;
pub mod node_http2;
pub mod node_http2_upgrade;
pub mod node_https;
pub mod node_inspector;
pub mod node_inspector_promises;
pub mod node_internal_http;
pub mod node_internal_streams;
pub mod node_module;
pub mod node_net;
pub mod node_os;
pub mod node_path;
pub mod node_perf_hooks;
pub mod node_punycode;
pub mod node_querystring;
pub mod node_readline;
pub mod node_repl;
pub mod node_stream;
pub mod node_stream_consumers;
pub mod node_stream_web;
pub mod node_string_decoder;
pub mod node_stubs;
pub mod node_subpath_aliases;
pub mod node_sys;
pub mod node_test;
pub mod node_timers_module;
pub mod node_tls;
pub mod node_tls_common;
pub mod node_tls_wrap;
pub mod node_trace_events;
pub mod node_tty;
pub mod node_url;
pub mod node_util;
pub mod node_util_types;
pub mod node_vm;
pub mod node_wasi;
pub mod node_worker_threads;
pub mod node_zlib;
pub mod permission_bridge;
pub mod require;
pub mod resolver_bridge;
pub mod runtime;
pub mod s3_api;
pub mod stealth_http;
pub mod timers;
// @trace REQ-ENG-006 [api:process.on uncaughtException/unhandledRejection] —
// unified uncaught-exception / unhandled-rejection router (Node semantics).
pub mod uncaught;
pub mod web_api;
// @trace REQ-ENG-005 [api:Web Streams] — full WHATWG Streams implementation
// (web_streams.js, ported from Bun). Installed on the global by
// node_stream::install for realms that lack servo's native streams (CLI +
// browser privileged evaluate realm). Previously dead code: the module was
// never declared, so CLI fell back to a broken inline polyfill whose
// TransformStream hung the event loop (BCE-20260816-STREAM-WEB).
pub mod web_streams;
// @trace REQ-ENG-001 [entity:BaoRuntime] [api:fetch] — full WHATWG
// Headers/Request/Response classes (installed on the global by
// globals::install_web_apis; consumed by fetch_api::fetch_fn).
pub mod web_fetch_classes;
// @trace STUB-INVENTORY: product real ProcessExit owners (no link_noop) — residual=0 for PE
pub mod product_process_exit;
// @trace STUB-INVENTORY: product real BufferedReaderParentLink owners (no link_noop) — residual=0 for BR
pub mod product_buffered_reader;
// @trace STUB-INVENTORY: product residual RealImpl rehomed from bao_native_stubs
pub mod product_native_symbols;
// @trace STUB-INVENTORY: Bun__linux_trace_* RealImpl (cross-platform; residual=0)
pub mod linux_trace;

pub use runtime::BaoRuntime;

// ── Orderly exit infrastructure ──
// process.exit() / Bun.exit() set a flag instead of calling std::process::exit(),
// so the CLI main loop can return naturally → BaoRuntime drops → SmRuntimeGuard
// drops (Runtime then Engine) → JS_ShutDown. No segfault from bypassed drop chain.

thread_local! {
    static EXIT_REQUESTED: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
    static EXIT_CODE: std::cell::Cell<i32> = const { std::cell::Cell::new(0) };
}

/// Request process exit with the given code. Called by process.exit() and Bun.exit().
pub fn request_exit(code: i32) {
    EXIT_CODE.with(|c| c.set(code));
    EXIT_REQUESTED.with(|r| r.set(true));
}

/// Check whether process.exit() or Bun.exit() was called.
pub fn should_exit() -> bool {
    EXIT_REQUESTED.with(|r| r.get())
}

/// Return the exit code set by process.exit() / Bun.exit().
pub fn exit_code() -> i32 {
    EXIT_CODE.with(|c| c.get())
}

/// Set only the exit code without requesting an exit.
/// Backs the `process.exitCode` property setter: assigning the property alone
/// steers the final exit code (honoured on natural exit and by 'exit'
/// listeners), while the process keeps running until the event loop drains
/// or process.exit() is called — Node semantics.
pub fn set_exit_code(code: i32) {
    EXIT_CODE.with(|c| c.set(code));
}

/// Clear the exit flag. Used by test runner between test files
/// so one file's process.exit() doesn't affect subsequent files.
pub fn clear_exit() {
    EXIT_REQUESTED.with(|r| r.set(false));
    EXIT_CODE.with(|c| c.set(0));
}

/// Install orderly shutdown hooks for SpiderMonkey.
///
/// No-op at init time. Tests should call `shutdown_thread_sm()` at the end
/// of each test function (before returning) to properly clean up the
/// SpiderMonkey Runtime and Engine. Without this, C++ TLS destructors
/// (`MutexImpl::~MutexImpl`) will SIGSEGV on thread exit.
///
/// This function is kept for API compatibility with existing test files.
pub fn install_exit_handler() {
    // No-op at init time. Cleanup happens via shutdown_thread_sm() at test end.
}

/// Shut down SpiderMonkey Runtime on the current thread.
///
/// Drops Runtime (→ JS_DestroyContext) stored in TLS. Safe to call multiple
/// times per thread (e.g., between tests). The JSEngine remains alive so
/// subsequent `for_test()` calls can create a new Runtime.
///
/// For process exit cleanup (JS_ShutDown), use `shutdown_engine()` instead.
pub fn shutdown_thread_sm() {
    bao_engine::context::JsContext::shutdown_thread_sm();
}

/// Shut down the SpiderMonkey engine entirely (process exit only).
///
/// Calls `JS_ShutDown` to clean up SpiderMonkey's process-wide C++ state.
/// After this, no new Runtime/JSContext can be created on any thread.
/// Should only be called at process exit.
pub fn shutdown_engine() {
    bao_engine::context::JsContext::shutdown_engine();
}

/// Safe JS string conversion: returns "" if JS string allocation fails.
///
/// # Safety
/// Caller must ensure `cx` is a valid JSContext pointer and `val` is rooted or otherwise protected from GC.
pub unsafe fn js_to_rust_string(
    cx: *mut mozjs::jsapi::JSContext,
    val: mozjs::jsval::JSVal,
) -> String {
    let ptr = val.to_string();
    match ::std::ptr::NonNull::new(ptr) {
        Some(nn) => mozjs::conversions::unsafe_jsstr_to_string(cx, nn),
        None => String::new(),
    }
}

/// Safe JSString pointer conversion: returns "" if pointer is null.
///
/// # Safety
/// Caller must ensure `cx` is a valid JSContext pointer and `s` is either null or a valid JSString pointer.
pub unsafe fn jsstr_to_rust_string(
    cx: *mut mozjs::jsapi::JSContext,
    s: *mut mozjs::jsapi::JSString,
) -> String {
    match ::std::ptr::NonNull::new(s) {
        Some(nn) => mozjs::conversions::unsafe_jsstr_to_string(cx, nn),
        None => String::new(),
    }
}

/// Force-link `bun_install` compilation unit so the linker resolves
/// `__bun_resolver_init_package_manager` from `bun_install::auto_installer`.
#[inline(never)]
pub fn force_link_bun_install() {
    let _ = bun_install::Subcommand::Install;
}

// @trace STUB-INVENTORY: default product path must not force-link bao_native_stubs.
// Closed-set PE/BR → product_process_exit + product_buffered_reader (real link_impl).
// Real C libs → force_link_native_c_libs. Do not reintroduce BAO_NATIVE_STUBS_ANCHOR / link_noop residual.

// Real C-lib force-link (former bao_native_stubs::force_c_lib_stubs chain):
// quic.c (bun_uws_sys) needs liblsquic; TLS needs boringssl; loop from bao_uloop.
#[inline(never)]
fn force_link_native_c_libs() {
    let _ = bun_lsquic_sys::force_link as *const () as usize;
    let _ = bun_lsquic_sys::force_link_lshpack as *const () as usize;
    let _ = bun_boringssl_sys::force_link as *const () as usize;
    let _ = bao_uloop::force_link as *const () as usize;
    // Keep loop entry symbols live without calling with null (null-deref risk).
    let _ = bao_uloop::bao_loop_tick as *const () as usize;
    let _ = bao_uloop::us_wakeup_loop as *const () as usize;
    let _ = bao_uloop::uws_get_loop as *const () as usize;
    // Retain product real ProcessExit + BufferedReader + RealImpl compilation units.
    // product_dispatch_residual deleted (P3): PE/BR noops residual=0.
    product_process_exit::force_link_product_process_exit();
    product_buffered_reader::force_link_product_buffered_reader();
    product_native_symbols::force_link_product_native_symbols();
}

#[used]
static FORCE_NATIVE_C_LIBS: fn() = force_link_native_c_libs;