use crate::function::FunctionCallbackInfo;
use crate::isolate_create_params::raw;
use crate::isolate_create_params::CreateParams;
use crate::promise::PromiseRejectMessage;
use crate::scope::data::ScopeData;
use crate::support::BuildTypeIdHasher;
use crate::support::MapFnFrom;
use crate::support::MapFnTo;
use crate::support::Opaque;
use crate::support::ToCFn;
use crate::support::UnitType;
use crate::wasm::trampoline;
use crate::wasm::WasmStreaming;
use crate::Array;
use crate::CallbackScope;
use crate::Context;
use crate::FixedArray;
use crate::Function;
use crate::HandleScope;
use crate::Local;
use crate::Message;
use crate::Module;
use crate::Object;
use crate::Promise;
use crate::ScriptOrModule;
use crate::String;
use crate::Value;
use std::any::Any;
use std::any::TypeId;
use std::collections::HashMap;
use std::ffi::c_void;
use std::fmt::{self, Debug, Formatter};
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::ops::DerefMut;
use std::os::raw::c_char;
use std::ptr::null_mut;
use std::ptr::NonNull;
use std::sync::Arc;
use std::sync::Mutex;
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum MicrotasksPolicy {
Explicit = 0,
Auto = 2,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum PromiseHookType {
Init,
Resolve,
Before,
After,
}
pub type MessageCallback = extern "C" fn(Local<Message>, Local<Value>);
pub type PromiseHook =
extern "C" fn(PromiseHookType, Local<Promise>, Local<Value>);
pub type PromiseRejectCallback = extern "C" fn(PromiseRejectMessage);
pub type HostInitializeImportMetaObjectCallback =
extern "C" fn(Local<Context>, Local<Module>, Local<Object>);
pub type HostImportModuleDynamicallyWithImportAssertionsCallback =
extern "C" fn(
Local<Context>,
Local<ScriptOrModule>,
Local<String>,
Local<FixedArray>,
) -> *mut Promise;
pub type InterruptCallback =
extern "C" fn(isolate: &mut Isolate, data: *mut c_void);
pub type NearHeapLimitCallback = extern "C" fn(
data: *mut c_void,
current_heap_limit: usize,
initial_heap_limit: usize,
) -> usize;
pub type OomErrorCallback =
extern "C" fn(location: *const c_char, is_heap_oom: bool);
#[repr(C)]
#[derive(Debug)]
pub struct HeapStatistics([usize; 16]);
#[cfg(target_os = "windows")]
pub type PrepareStackTraceCallback<'s> = extern "C" fn(
*mut *const Value,
Local<'s, Context>,
Local<'s, Value>,
Local<'s, Array>,
) -> *mut *const Value;
#[cfg(not(target_os = "windows"))]
pub type PrepareStackTraceCallback<'s> = extern "C" fn(
Local<'s, Context>,
Local<'s, Value>,
Local<'s, Array>,
) -> *const Value;
extern "C" {
fn v8__Isolate__New(params: *const raw::CreateParams) -> *mut Isolate;
fn v8__Isolate__Dispose(this: *mut Isolate);
fn v8__Isolate__SetData(this: *mut Isolate, slot: u32, data: *mut c_void);
fn v8__Isolate__GetData(this: *const Isolate, slot: u32) -> *mut c_void;
fn v8__Isolate__GetNumberOfDataSlots(this: *const Isolate) -> u32;
fn v8__Isolate__Enter(this: *mut Isolate);
fn v8__Isolate__Exit(this: *mut Isolate);
fn v8__Isolate__ClearKeptObjects(isolate: *mut Isolate);
fn v8__Isolate__LowMemoryNotification(isolate: *mut Isolate);
fn v8__Isolate__GetHeapStatistics(this: *mut Isolate, s: *mut HeapStatistics);
fn v8__Isolate__SetCaptureStackTraceForUncaughtExceptions(
this: *mut Isolate,
caputre: bool,
frame_limit: i32,
);
fn v8__Isolate__AddMessageListener(
isolate: *mut Isolate,
callback: MessageCallback,
) -> bool;
fn v8__Isolate__AddNearHeapLimitCallback(
isolate: *mut Isolate,
callback: NearHeapLimitCallback,
data: *mut c_void,
);
fn v8__Isolate__RemoveNearHeapLimitCallback(
isolate: *mut Isolate,
callback: NearHeapLimitCallback,
heap_limit: usize,
);
fn v8__Isolate__SetOOMErrorHandler(
isolate: *mut Isolate,
callback: OomErrorCallback,
);
fn v8__Isolate__SetPrepareStackTraceCallback(
isolate: *mut Isolate,
callback: PrepareStackTraceCallback,
);
fn v8__Isolate__SetPromiseHook(isolate: *mut Isolate, hook: PromiseHook);
fn v8__Isolate__SetPromiseRejectCallback(
isolate: *mut Isolate,
callback: PromiseRejectCallback,
);
fn v8__Isolate__SetHostInitializeImportMetaObjectCallback(
isolate: *mut Isolate,
callback: HostInitializeImportMetaObjectCallback,
);
fn v8__Isolate__SetHostImportModuleDynamicallyCallback(
isolate: *mut Isolate,
callback: HostImportModuleDynamicallyWithImportAssertionsCallback,
);
fn v8__Isolate__RequestInterrupt(
isolate: *const Isolate,
callback: InterruptCallback,
data: *mut c_void,
);
fn v8__Isolate__TerminateExecution(isolate: *const Isolate);
fn v8__Isolate__IsExecutionTerminating(isolate: *const Isolate) -> bool;
fn v8__Isolate__CancelTerminateExecution(isolate: *const Isolate);
fn v8__Isolate__GetMicrotasksPolicy(
isolate: *const Isolate,
) -> MicrotasksPolicy;
fn v8__Isolate__SetMicrotasksPolicy(
isolate: *mut Isolate,
policy: MicrotasksPolicy,
);
fn v8__Isolate__PerformMicrotaskCheckpoint(isolate: *mut Isolate);
fn v8__Isolate__EnqueueMicrotask(
isolate: *mut Isolate,
function: *const Function,
);
fn v8__Isolate__SetAllowAtomicsWait(isolate: *mut Isolate, allow: bool);
fn v8__Isolate__SetWasmStreamingCallback(
isolate: *mut Isolate,
callback: extern "C" fn(*const FunctionCallbackInfo),
);
fn v8__Isolate__HasPendingBackgroundTasks(isolate: *const Isolate) -> bool;
fn v8__HeapProfiler__TakeHeapSnapshot(
isolate: *mut Isolate,
callback: extern "C" fn(*mut c_void, *const u8, usize) -> bool,
arg: *mut c_void,
);
fn v8__HeapStatistics__CONSTRUCT(s: *mut MaybeUninit<HeapStatistics>);
fn v8__HeapStatistics__total_heap_size(s: *const HeapStatistics) -> usize;
fn v8__HeapStatistics__total_heap_size_executable(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__total_physical_size(s: *const HeapStatistics)
-> usize;
fn v8__HeapStatistics__total_available_size(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__total_global_handles_size(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__used_global_handles_size(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__used_heap_size(s: *const HeapStatistics) -> usize;
fn v8__HeapStatistics__heap_size_limit(s: *const HeapStatistics) -> usize;
fn v8__HeapStatistics__malloced_memory(s: *const HeapStatistics) -> usize;
fn v8__HeapStatistics__external_memory(s: *const HeapStatistics) -> usize;
fn v8__HeapStatistics__peak_malloced_memory(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__number_of_native_contexts(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__number_of_detached_contexts(
s: *const HeapStatistics,
) -> usize;
fn v8__HeapStatistics__does_zap_garbage(s: *const HeapStatistics) -> usize;
}
#[repr(C)]
#[derive(Debug)]
pub struct Isolate(Opaque);
impl Isolate {
const ANNEX_SLOT: u32 = 0;
const CURRENT_SCOPE_DATA_SLOT: u32 = 1;
const INTERNAL_SLOT_COUNT: u32 = 2;
#[allow(clippy::new_ret_no_self)]
pub fn new(params: CreateParams) -> OwnedIsolate {
crate::V8::assert_initialized();
let (raw_create_params, create_param_allocations) = params.finalize();
let cxx_isolate = unsafe { v8__Isolate__New(&raw_create_params) };
let mut owned_isolate = OwnedIsolate::new(cxx_isolate);
ScopeData::new_root(&mut owned_isolate);
owned_isolate.create_annex(create_param_allocations);
unsafe {
owned_isolate.enter();
}
owned_isolate
}
pub fn create_params() -> CreateParams {
CreateParams::default()
}
pub fn thread_safe_handle(&self) -> IsolateHandle {
IsolateHandle::new(self)
}
pub fn terminate_execution(&self) -> bool {
self.thread_safe_handle().terminate_execution()
}
pub fn cancel_terminate_execution(&self) -> bool {
self.thread_safe_handle().cancel_terminate_execution()
}
pub fn is_execution_terminating(&self) -> bool {
self.thread_safe_handle().is_execution_terminating()
}
pub(crate) fn create_annex(
&mut self,
create_param_allocations: Box<dyn Any>,
) {
let annex_arc = Arc::new(IsolateAnnex::new(self, create_param_allocations));
let annex_ptr = Arc::into_raw(annex_arc);
unsafe {
assert!(v8__Isolate__GetData(self, Self::ANNEX_SLOT).is_null());
v8__Isolate__SetData(self, Self::ANNEX_SLOT, annex_ptr as *mut c_void);
};
}
fn get_annex(&self) -> &IsolateAnnex {
unsafe {
&*(v8__Isolate__GetData(self, Self::ANNEX_SLOT) as *const _
as *const IsolateAnnex)
}
}
fn get_annex_mut(&mut self) -> &mut IsolateAnnex {
unsafe {
&mut *(v8__Isolate__GetData(self, Self::ANNEX_SLOT) as *mut IsolateAnnex)
}
}
fn get_annex_arc(&self) -> Arc<IsolateAnnex> {
let annex_ptr = self.get_annex();
let annex_arc = unsafe { Arc::from_raw(annex_ptr) };
Arc::into_raw(annex_arc.clone());
annex_arc
}
unsafe fn set_data(&mut self, slot: u32, ptr: *mut c_void) {
v8__Isolate__SetData(self, slot + Self::INTERNAL_SLOT_COUNT, ptr)
}
fn get_data(&self, slot: u32) -> *mut c_void {
unsafe { v8__Isolate__GetData(self, slot + Self::INTERNAL_SLOT_COUNT) }
}
fn get_number_of_data_slots(&self) -> u32 {
unsafe {
v8__Isolate__GetNumberOfDataSlots(self) - Self::INTERNAL_SLOT_COUNT
}
}
pub(crate) fn get_current_scope_data(&self) -> Option<NonNull<ScopeData>> {
let scope_data_ptr =
unsafe { v8__Isolate__GetData(self, Self::CURRENT_SCOPE_DATA_SLOT) };
NonNull::new(scope_data_ptr).map(NonNull::cast)
}
pub(crate) fn set_current_scope_data(
&mut self,
scope_data: Option<NonNull<ScopeData>>,
) {
let scope_data_ptr = scope_data
.map(NonNull::cast)
.map(NonNull::as_ptr)
.unwrap_or_else(null_mut);
unsafe {
v8__Isolate__SetData(self, Self::CURRENT_SCOPE_DATA_SLOT, scope_data_ptr)
};
}
pub fn get_slot<T: 'static>(&self) -> Option<&T> {
let b = self.get_annex().slots.get(&TypeId::of::<T>())?;
let r = <dyn Any>::downcast_ref::<T>(&**b).unwrap();
Some(r)
}
pub fn get_slot_mut<T: 'static>(&mut self) -> Option<&mut T> {
let b = self.get_annex_mut().slots.get_mut(&TypeId::of::<T>())?;
let r = <dyn Any>::downcast_mut::<T>(&mut **b).unwrap();
Some(r)
}
pub fn set_slot<T: 'static>(&mut self, value: T) -> bool {
self
.get_annex_mut()
.slots
.insert(Any::type_id(&value), Box::new(value))
.is_none()
}
pub fn remove_slot<T: 'static>(&mut self) -> Option<T> {
let b = self.get_annex_mut().slots.remove(&TypeId::of::<T>())?;
let v: T = *b.downcast::<T>().unwrap();
Some(v)
}
pub unsafe fn enter(&mut self) {
v8__Isolate__Enter(self)
}
pub unsafe fn exit(&mut self) {
v8__Isolate__Exit(self)
}
pub fn clear_kept_objects(&mut self) {
unsafe { v8__Isolate__ClearKeptObjects(self) }
}
pub fn low_memory_notification(&mut self) {
unsafe { v8__Isolate__LowMemoryNotification(self) }
}
pub fn get_heap_statistics(&mut self, s: &mut HeapStatistics) {
unsafe { v8__Isolate__GetHeapStatistics(self, s) }
}
pub fn set_capture_stack_trace_for_uncaught_exceptions(
&mut self,
capture: bool,
frame_limit: i32,
) {
unsafe {
v8__Isolate__SetCaptureStackTraceForUncaughtExceptions(
self,
capture,
frame_limit,
)
}
}
pub fn add_message_listener(&mut self, callback: MessageCallback) -> bool {
unsafe { v8__Isolate__AddMessageListener(self, callback) }
}
pub fn set_prepare_stack_trace_callback<'s>(
&mut self,
callback: impl MapFnTo<PrepareStackTraceCallback<'s>>,
) {
unsafe {
v8__Isolate__SetPrepareStackTraceCallback(self, callback.map_fn_to())
};
}
pub fn set_promise_hook(&mut self, hook: PromiseHook) {
unsafe { v8__Isolate__SetPromiseHook(self, hook) }
}
pub fn set_promise_reject_callback(
&mut self,
callback: PromiseRejectCallback,
) {
unsafe { v8__Isolate__SetPromiseRejectCallback(self, callback) }
}
pub fn set_host_initialize_import_meta_object_callback(
&mut self,
callback: HostInitializeImportMetaObjectCallback,
) {
unsafe {
v8__Isolate__SetHostInitializeImportMetaObjectCallback(self, callback)
}
}
pub fn set_host_import_module_dynamically_callback(
&mut self,
callback: HostImportModuleDynamicallyWithImportAssertionsCallback,
) {
unsafe {
v8__Isolate__SetHostImportModuleDynamicallyCallback(self, callback)
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn add_near_heap_limit_callback(
&mut self,
callback: NearHeapLimitCallback,
data: *mut c_void,
) {
unsafe { v8__Isolate__AddNearHeapLimitCallback(self, callback, data) };
}
pub fn remove_near_heap_limit_callback(
&mut self,
callback: NearHeapLimitCallback,
heap_limit: usize,
) {
unsafe {
v8__Isolate__RemoveNearHeapLimitCallback(self, callback, heap_limit)
};
}
pub fn set_oom_error_handler(&mut self, callback: OomErrorCallback) {
unsafe { v8__Isolate__SetOOMErrorHandler(self, callback) };
}
pub fn get_microtasks_policy(&self) -> MicrotasksPolicy {
unsafe { v8__Isolate__GetMicrotasksPolicy(self) }
}
pub fn set_microtasks_policy(&mut self, policy: MicrotasksPolicy) {
unsafe { v8__Isolate__SetMicrotasksPolicy(self, policy) }
}
pub fn perform_microtask_checkpoint(&mut self) {
unsafe { v8__Isolate__PerformMicrotaskCheckpoint(self) }
}
#[deprecated(note = "Use Isolate::perform_microtask_checkpoint() instead")]
pub fn run_microtasks(&mut self) {
self.perform_microtask_checkpoint()
}
pub fn enqueue_microtask(&mut self, microtask: Local<Function>) {
unsafe { v8__Isolate__EnqueueMicrotask(self, &*microtask) }
}
pub fn set_allow_atomics_wait(&mut self, allow: bool) {
unsafe { v8__Isolate__SetAllowAtomicsWait(self, allow) }
}
pub fn set_wasm_streaming_callback<F>(&mut self, _: F)
where
F: UnitType + Fn(&mut HandleScope, Local<Value>, WasmStreaming),
{
unsafe { v8__Isolate__SetWasmStreamingCallback(self, trampoline::<F>()) }
}
pub fn has_pending_background_tasks(&self) -> bool {
unsafe { v8__Isolate__HasPendingBackgroundTasks(self) }
}
unsafe fn dispose(&mut self) {
ScopeData::drop_root(self);
let annex = self.get_annex_mut();
{
let _lock = annex.isolate_mutex.lock().unwrap();
annex.isolate = null_mut();
}
annex.create_param_allocations = Box::new(());
annex.slots.clear();
Arc::from_raw(annex);
self.set_data(0, null_mut());
v8__Isolate__Dispose(self)
}
pub fn take_heap_snapshot<F>(&mut self, mut callback: F)
where
F: FnMut(&[u8]) -> bool,
{
extern "C" fn trampoline<F>(
arg: *mut c_void,
data: *const u8,
size: usize,
) -> bool
where
F: FnMut(&[u8]) -> bool,
{
let p = arg as *mut F;
let callback = unsafe { &mut *p };
let slice = unsafe { std::slice::from_raw_parts(data, size) };
callback(slice)
}
let arg = &mut callback as *mut F as *mut c_void;
unsafe { v8__HeapProfiler__TakeHeapSnapshot(self, trampoline::<F>, arg) }
}
}
pub(crate) struct IsolateAnnex {
create_param_allocations: Box<dyn Any>,
slots: HashMap<TypeId, Box<dyn Any>, BuildTypeIdHasher>,
isolate: *mut Isolate,
isolate_mutex: Mutex<()>,
}
impl IsolateAnnex {
fn new(
isolate: &mut Isolate,
create_param_allocations: Box<dyn Any>,
) -> Self {
Self {
create_param_allocations,
slots: HashMap::default(),
isolate,
isolate_mutex: Mutex::new(()),
}
}
}
impl Debug for IsolateAnnex {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("IsolateAnnex")
.field("isolate", &self.isolate)
.field("isolate_mutex", &self.isolate_mutex)
.finish()
}
}
#[derive(Clone, Debug)]
pub struct IsolateHandle(Arc<IsolateAnnex>);
unsafe impl Send for IsolateHandle {}
unsafe impl Sync for IsolateHandle {}
impl IsolateHandle {
pub(crate) unsafe fn get_isolate_ptr(&self) -> *mut Isolate {
self.0.isolate
}
fn new(isolate: &Isolate) -> Self {
Self(isolate.get_annex_arc())
}
pub fn terminate_execution(&self) -> bool {
let _lock = self.0.isolate_mutex.lock().unwrap();
if self.0.isolate.is_null() {
false
} else {
unsafe { v8__Isolate__TerminateExecution(self.0.isolate) };
true
}
}
pub fn cancel_terminate_execution(&self) -> bool {
let _lock = self.0.isolate_mutex.lock().unwrap();
if self.0.isolate.is_null() {
false
} else {
unsafe { v8__Isolate__CancelTerminateExecution(self.0.isolate) };
true
}
}
pub fn is_execution_terminating(&self) -> bool {
let _lock = self.0.isolate_mutex.lock().unwrap();
if self.0.isolate.is_null() {
false
} else {
unsafe { v8__Isolate__IsExecutionTerminating(self.0.isolate) }
}
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn request_interrupt(
&self,
callback: InterruptCallback,
data: *mut c_void,
) -> bool {
let _lock = self.0.isolate_mutex.lock().unwrap();
if self.0.isolate.is_null() {
false
} else {
unsafe { v8__Isolate__RequestInterrupt(self.0.isolate, callback, data) };
true
}
}
}
#[derive(Debug)]
pub struct OwnedIsolate {
cxx_isolate: NonNull<Isolate>,
}
impl OwnedIsolate {
pub(crate) fn new(cxx_isolate: *mut Isolate) -> Self {
let cxx_isolate = NonNull::new(cxx_isolate).unwrap();
Self { cxx_isolate }
}
}
impl Drop for OwnedIsolate {
fn drop(&mut self) {
unsafe {
self.exit();
self.cxx_isolate.as_mut().dispose()
}
}
}
impl Deref for OwnedIsolate {
type Target = Isolate;
fn deref(&self) -> &Self::Target {
unsafe { self.cxx_isolate.as_ref() }
}
}
impl DerefMut for OwnedIsolate {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.cxx_isolate.as_mut() }
}
}
impl HeapStatistics {
pub fn total_heap_size(&self) -> usize {
unsafe { v8__HeapStatistics__total_heap_size(self) }
}
pub fn total_heap_size_executable(&self) -> usize {
unsafe { v8__HeapStatistics__total_heap_size_executable(self) }
}
pub fn total_physical_size(&self) -> usize {
unsafe { v8__HeapStatistics__total_physical_size(self) }
}
pub fn total_available_size(&self) -> usize {
unsafe { v8__HeapStatistics__total_available_size(self) }
}
pub fn total_global_handles_size(&self) -> usize {
unsafe { v8__HeapStatistics__total_global_handles_size(self) }
}
pub fn used_global_handles_size(&self) -> usize {
unsafe { v8__HeapStatistics__used_global_handles_size(self) }
}
pub fn used_heap_size(&self) -> usize {
unsafe { v8__HeapStatistics__used_heap_size(self) }
}
pub fn heap_size_limit(&self) -> usize {
unsafe { v8__HeapStatistics__heap_size_limit(self) }
}
pub fn malloced_memory(&self) -> usize {
unsafe { v8__HeapStatistics__malloced_memory(self) }
}
pub fn external_memory(&self) -> usize {
unsafe { v8__HeapStatistics__external_memory(self) }
}
pub fn peak_malloced_memory(&self) -> usize {
unsafe { v8__HeapStatistics__peak_malloced_memory(self) }
}
pub fn number_of_native_contexts(&self) -> usize {
unsafe { v8__HeapStatistics__number_of_native_contexts(self) }
}
pub fn number_of_detached_contexts(&self) -> usize {
unsafe { v8__HeapStatistics__number_of_detached_contexts(self) }
}
pub fn does_zap_garbage(&self) -> usize {
unsafe { v8__HeapStatistics__does_zap_garbage(self) }
}
}
impl Default for HeapStatistics {
fn default() -> Self {
let mut s = MaybeUninit::<Self>::uninit();
unsafe {
v8__HeapStatistics__CONSTRUCT(&mut s);
s.assume_init()
}
}
}
impl<'s, F> MapFnFrom<F> for PrepareStackTraceCallback<'s>
where
F: UnitType
+ Fn(
&mut HandleScope<'s>,
Local<'s, Value>,
Local<'s, Array>,
) -> Local<'s, Value>,
{
#[cfg(target_os = "windows")]
fn mapping() -> Self {
let f = |ret_ptr, context, error, sites| {
let mut scope: CallbackScope = unsafe { CallbackScope::new(context) };
let r = (F::get())(&mut scope, error, sites);
unsafe { std::ptr::write(ret_ptr, &*r as *const _) };
ret_ptr
};
f.to_c_fn()
}
#[cfg(not(target_os = "windows"))]
fn mapping() -> Self {
let f = |context, error, sites| {
let mut scope: CallbackScope = unsafe { CallbackScope::new(context) };
let r = (F::get())(&mut scope, error, sites);
&*r as *const _
};
f.to_c_fn()
}
}