piu_core 0.412.1

A modern JavaScript/TypeScript runtime built with V8, Rust, and Tokio
// Copyright 2018-2026 the Deno authors. MIT license.

use std::borrow::Cow;
use std::collections::HashMap;
use std::ffi::c_void;
use std::sync::Mutex;
use std::sync::Once;
use std::time::Duration;

use futures::task::AtomicWaker;

use super::bindings;

/// Extract the raw isolate address from an `UnsafeRawIsolatePtr`.
///
/// `UnsafeRawIsolatePtr` is `#[repr(transparent)]` over `*mut RealIsolate`,
/// so its bit-pattern is a single pointer-sized value. We use transmute
/// because the inner field is private.
///
/// The compile-time assert below guarantees the layout assumption holds.
const _: () = assert!(
  std::mem::size_of::<v8::UnsafeRawIsolatePtr>()
    == std::mem::size_of::<usize>()
);

pub(crate) fn isolate_ptr_to_key(ptr: v8::UnsafeRawIsolatePtr) -> usize {
  // SAFETY: UnsafeRawIsolatePtr is #[repr(transparent)] over *mut RealIsolate,
  // which is pointer-sized. The compile-time assert above guarantees this.
  unsafe { std::mem::transmute::<v8::UnsafeRawIsolatePtr, usize>(ptr) }
}

/// Thread-safe queue of V8 foreground tasks, shared between the global
/// isolate registry (written by V8 background threads) and the event
/// loop (drained on the main thread). Cloning is cheap (Arc).
pub type ForegroundTaskQueue = std::sync::Arc<Mutex<Vec<v8::Task>>>;

/// Per-isolate state stored in the global registry. Kept minimal: just
/// enough for platform callbacks (which only have an isolate pointer) to
/// push tasks and wake the event loop.
struct IsolateEntry {
  waker: std::sync::Arc<AtomicWaker>,
  handle: Option<tokio::runtime::Handle>,
  tasks: ForegroundTaskQueue,
}

static ISOLATE_ENTRIES: std::sync::LazyLock<
  Mutex<HashMap<usize, IsolateEntry>>,
> = std::sync::LazyLock::new(|| Mutex::new(HashMap::new()));

/// Register an isolate in the global platform registry. The `tasks`
/// queue is shared with `JsRuntimeState` so the event loop drains it
/// directly without touching the global map.
pub fn register_isolate(
  isolate_ptr: usize,
  waker: std::sync::Arc<AtomicWaker>,
  handle: Option<tokio::runtime::Handle>,
  tasks: ForegroundTaskQueue,
) {
  let mut map = ISOLATE_ENTRIES.lock().unwrap();
  map.insert(
    isolate_ptr,
    IsolateEntry {
      waker,
      handle,
      tasks,
    },
  );
}

pub fn unregister_isolate(isolate_ptr: usize) {
  let mut map = ISOLATE_ENTRIES.lock().unwrap();
  map.remove(&isolate_ptr);
}

/// Queue an immediate foreground task and wake the event loop.
fn queue_task(key: usize, task: v8::Task) {
  let map = ISOLATE_ENTRIES.lock().unwrap();
  if let Some(entry) = map.get(&key) {
    entry.tasks.lock().unwrap().push(task);
    entry.waker.wake();
  }
}

/// Spawn a delayed V8 foreground task on the isolate's tokio runtime.
/// After the delay, the task is queued for synchronous draining (not
/// run directly on the tokio worker thread).
///
/// Aborts the process when the isolate was registered without a tokio
/// handle: the delay cannot be honored, queuing the task immediately
/// would busy-loop the event loop on any self-rescheduling V8 task
/// (e.g. GC memory reducer tasks), and dropping it would lose foreground
/// work. Note that a regular `panic!` cannot be used here — this is
/// called by V8 from C++ frames that Rust cannot unwind through, which
/// would abort anyway but with the panic message swallowed.
fn spawn_delayed_task(key: usize, task: v8::Task, delay_in_seconds: f64) {
  let map = ISOLATE_ENTRIES.lock().unwrap();
  let Some(entry) = map.get(&key) else { return };
  let Some(handle) = entry.handle.as_ref() else {
    #[allow(clippy::print_stderr, reason = "printed before aborting")]
    {
      eprintln!(
        "V8 posted a delayed task, but this isolate was created outside of a tokio runtime context and the delay cannot be honored. Enter a tokio runtime (e.g. via `tokio::runtime::Runtime::enter()`) before creating the `JsRuntime`."
      );
    }
    std::process::abort();
  };
  let tasks = entry.tasks.clone();
  let waker = entry.waker.clone();
  handle.spawn(async move {
    tokio::time::sleep(Duration::from_secs_f64(delay_in_seconds)).await;
    tasks.lock().unwrap().push(task);
    waker.wake();
  });
}

/// Custom V8 platform implementation that queues immediate foreground
/// tasks for synchronous draining, and spawns delayed tasks on tokio.
struct DenoPlatformImpl;

impl v8::PlatformImpl for DenoPlatformImpl {
  fn post_task(&self, isolate_ptr: *mut c_void, task: v8::Task) {
    queue_task(isolate_ptr as usize, task);
  }

  fn post_non_nestable_task(&self, isolate_ptr: *mut c_void, task: v8::Task) {
    queue_task(isolate_ptr as usize, task);
  }

  fn post_delayed_task(
    &self,
    isolate_ptr: *mut c_void,
    task: v8::Task,
    delay_in_seconds: f64,
  ) {
    spawn_delayed_task(isolate_ptr as usize, task, delay_in_seconds);
  }

  fn post_non_nestable_delayed_task(
    &self,
    isolate_ptr: *mut c_void,
    task: v8::Task,
    delay_in_seconds: f64,
  ) {
    spawn_delayed_task(isolate_ptr as usize, task, delay_in_seconds);
  }

  fn post_idle_task(&self, _isolate_ptr: *mut c_void, _task: v8::IdleTask) {
    unreachable!();
  }
}

fn v8_init(
  v8_platform: Option<v8::SharedRef<v8::Platform>>,
  expose_natives: bool,
) {
  #[cfg(feature = "include_icu_data")]
  {
    v8::icu::set_common_data_77(deno_core_icudata::ICU_DATA).unwrap();
  }

  let base_flags = concat!(
    " --no-validate-asm",
    " --turbo_fast_api_calls",
    " --harmony-temporal",
    " --js-float16array",
    " --js-explicit-resource-management",
    " --js-source-phase-imports",
    " --js-defer-import-eval",
    // Installs a native `queueMicrotask` on the global object, letting us
    // enqueue microtasks from JS without crossing the op boundary. See
    // `queueMicrotask` in `01_core.js`.
    " --enable-queue-microtask"
  );
  let expose_natives_flags = "--expose_gc --allow_natives_syntax";
  let flags = if expose_natives {
    format!("{base_flags} {expose_natives_flags}")
  } else {
    base_flags.to_string()
  };
  v8::V8::set_flags_from_string(&flags);

  let v8_platform = v8_platform.unwrap_or_else(|| {
    let unprotected =
      cfg!(any(test, feature = "unsafe_use_unprotected_platform"));
    // Cap V8 platform thread pool to 4 threads (like Node.js).
    // Using all available cores (the default when 0 is passed) wastes
    // memory for workloads that rarely use background V8 tasks.
    let thread_pool_size = std::cmp::min(
      std::thread::available_parallelism()
        .map(|n| n.get() as u32)
        .unwrap_or(4),
      4,
    );
    v8::new_custom_platform(
      thread_pool_size,
      false,
      unprotected,
      DenoPlatformImpl,
    )
    .make_shared()
  });
  v8::V8::initialize_platform(v8_platform.clone());
  v8::V8::initialize();
}

pub fn init_v8(
  v8_platform: Option<v8::SharedRef<v8::Platform>>,
  expose_natives: bool,
) {
  static DENO_INIT: Once = Once::new();
  DENO_INIT.call_once(move || v8_init(v8_platform, expose_natives));
}

pub fn create_isolate(
  maybe_create_params: Option<v8::CreateParams>,
  external_refs: Cow<'static, [v8::ExternalReference]>,
) -> v8::OwnedIsolate {
  let mut params = maybe_create_params.unwrap_or_default();
  params = params.external_references(external_refs);
  let mut isolate = v8::Isolate::new(params);

  isolate.set_microtasks_policy(v8::MicrotasksPolicy::Explicit);
  isolate.set_capture_stack_trace_for_uncaught_exceptions(true, 10);
  isolate.set_promise_reject_callback(bindings::promise_reject_callback);
  isolate.set_prepare_stack_trace_callback(
    crate::error::prepare_stack_trace_callback,
  );
  isolate.set_host_initialize_import_meta_object_callback(
    bindings::host_initialize_import_meta_object_callback,
  );
  isolate.set_host_import_module_dynamically_callback(
    bindings::host_import_module_dynamically_callback,
  );
  isolate.set_host_import_module_with_phase_dynamically_callback(
    bindings::host_import_module_with_phase_dynamically_callback,
  );
  isolate.set_wasm_async_resolve_promise_callback(
    bindings::wasm_async_resolve_promise_callback,
  );
  isolate.set_wasm_streaming_callback(
    crate::ops_builtin_v8::wasm_streaming_callback,
  );

  isolate
}