use std::borrow::Borrow;
use std::cell::Cell;
use std::ffi::c_void;
use std::hash::Hash;
use std::hash::Hasher;
use std::marker::PhantomData;
use std::mem::forget;
use std::mem::transmute;
use std::ops::Deref;
use std::ptr::NonNull;
use crate::support::Opaque;
use crate::Data;
use crate::HandleScope;
use crate::Isolate;
use crate::IsolateHandle;
extern "C" {
fn v8__Local__New(isolate: *mut Isolate, other: *const Data) -> *const Data;
fn v8__Global__New(isolate: *mut Isolate, data: *const Data) -> *const Data;
fn v8__Global__NewWeak(
isolate: *mut Isolate,
data: *const Data,
parameter: *const c_void,
callback: extern "C" fn(*const WeakCallbackInfo),
) -> *const Data;
fn v8__Global__Reset(data: *const Data);
fn v8__WeakCallbackInfo__GetIsolate(
this: *const WeakCallbackInfo,
) -> *mut Isolate;
fn v8__WeakCallbackInfo__GetParameter(
this: *const WeakCallbackInfo,
) -> *mut c_void;
fn v8__WeakCallbackInfo__SetSecondPassCallback(
this: *const WeakCallbackInfo,
callback: extern "C" fn(*const WeakCallbackInfo),
);
}
#[repr(C)]
#[derive(Debug)]
pub struct Local<'s, T>(NonNull<T>, PhantomData<&'s ()>);
impl<'s, T> Local<'s, T> {
#[inline(always)]
pub fn new(
scope: &mut HandleScope<'s, ()>,
handle: impl Handle<Data = T>,
) -> Self {
let HandleInfo { data, host } = handle.get_handle_info();
host.assert_match_isolate(scope);
unsafe {
scope.cast_local(|sd| {
v8__Local__New(sd.get_isolate_ptr(), data.cast().as_ptr()) as *const T
})
}
.unwrap()
}
#[inline(always)]
pub unsafe fn cast<A>(other: Local<'s, A>) -> Self
where
Local<'s, A>: From<Self>,
{
transmute(other)
}
#[inline(always)]
pub(crate) unsafe fn from_raw(ptr: *const T) -> Option<Self> {
NonNull::new(ptr as *mut _).map(|nn| Self::from_non_null(nn))
}
#[inline(always)]
pub(crate) unsafe fn from_raw_unchecked(ptr: *const T) -> Self {
Self(NonNull::new_unchecked(ptr as *mut _), PhantomData)
}
#[inline(always)]
pub(crate) unsafe fn from_non_null(nn: NonNull<T>) -> Self {
Self(nn, PhantomData)
}
#[inline(always)]
pub(crate) fn as_non_null(self) -> NonNull<T> {
self.0
}
#[inline(always)]
pub(crate) fn slice_into_raw(slice: &[Self]) -> &[*const T] {
unsafe { &*(slice as *const [Self] as *const [*const T]) }
}
}
impl<'s, T> Copy for Local<'s, T> {}
impl<'s, T> Clone for Local<'s, T> {
fn clone(&self) -> Self {
*self
}
}
impl<'s, T> Deref for Local<'s, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.0.as_ref() }
}
}
#[derive(Debug)]
pub struct Global<T> {
data: NonNull<T>,
isolate_handle: IsolateHandle,
}
impl<T> Global<T> {
#[inline(always)]
pub fn new(isolate: &mut Isolate, handle: impl Handle<Data = T>) -> Self {
let HandleInfo { data, host } = handle.get_handle_info();
host.assert_match_isolate(isolate);
unsafe { Self::new_raw(isolate, data) }
}
#[inline(always)]
unsafe fn new_raw(isolate: *mut Isolate, data: NonNull<T>) -> Self {
let data = data.cast().as_ptr();
let data = v8__Global__New(isolate, data) as *const T;
let data = NonNull::new_unchecked(data as *mut _);
let isolate_handle = (*isolate).thread_safe_handle();
Self {
data,
isolate_handle,
}
}
#[inline(always)]
pub fn into_raw(self) -> NonNull<T> {
let data = self.data;
forget(self);
data
}
#[inline(always)]
pub unsafe fn from_raw(isolate: &mut Isolate, data: NonNull<T>) -> Self {
let isolate_handle = isolate.thread_safe_handle();
Self {
data,
isolate_handle,
}
}
#[inline(always)]
pub fn open<'a>(&'a self, scope: &mut Isolate) -> &'a T {
Handle::open(self, scope)
}
}
impl<T> Clone for Global<T> {
fn clone(&self) -> Self {
let HandleInfo { data, host } = self.get_handle_info();
unsafe { Self::new_raw(host.get_isolate().as_mut(), data) }
}
}
impl<T> Drop for Global<T> {
fn drop(&mut self) {
unsafe {
if self.isolate_handle.get_isolate_ptr().is_null() {
} else {
v8__Global__Reset(self.data.cast().as_ptr())
}
}
}
}
pub(crate) struct UnsafeRefHandle<'a, T> {
reference: &'a T,
isolate_handle: IsolateHandle,
}
impl<'a, T> UnsafeRefHandle<'a, T> {
#[inline(always)]
pub unsafe fn new(reference: &'a T, isolate: &mut Isolate) -> Self {
UnsafeRefHandle {
reference,
isolate_handle: isolate.thread_safe_handle(),
}
}
}
pub trait Handle: Sized {
type Data;
#[doc(hidden)]
fn get_handle_info(&self) -> HandleInfo<Self::Data>;
fn open<'a>(&'a self, isolate: &mut Isolate) -> &'a Self::Data {
let HandleInfo { data, host } = self.get_handle_info();
host.assert_match_isolate(isolate);
unsafe { &*data.as_ptr() }
}
unsafe fn get_unchecked(&self) -> &Self::Data {
let HandleInfo { data, host } = self.get_handle_info();
if let HandleHost::DisposedIsolate = host {
panic!("attempt to access Handle hosted by disposed Isolate");
}
&*data.as_ptr()
}
}
impl<'s, T> Handle for Local<'s, T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(self.as_non_null(), HandleHost::Scope)
}
}
impl<'a, 's: 'a, T> Handle for &'a Local<'s, T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(self.as_non_null(), HandleHost::Scope)
}
}
impl<T> Handle for Global<T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(self.data, (&self.isolate_handle).into())
}
}
impl<'a, T> Handle for &'a Global<T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(self.data, (&self.isolate_handle).into())
}
}
impl<'a, T> Handle for UnsafeRefHandle<'a, T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(
NonNull::from(self.reference),
(&self.isolate_handle).into(),
)
}
}
impl<'a, T> Handle for &'a UnsafeRefHandle<'_, T> {
type Data = T;
fn get_handle_info(&self) -> HandleInfo<T> {
HandleInfo::new(
NonNull::from(self.reference),
(&self.isolate_handle).into(),
)
}
}
impl<'s, T> Borrow<T> for Local<'s, T> {
fn borrow(&self) -> &T {
self
}
}
impl<T> Borrow<T> for Global<T> {
fn borrow(&self) -> &T {
let HandleInfo { data, host } = self.get_handle_info();
if let HandleHost::DisposedIsolate = host {
panic!("attempt to access Handle hosted by disposed Isolate");
}
unsafe { &*data.as_ptr() }
}
}
impl<'s, T> Eq for Local<'s, T> where T: Eq {}
impl<T> Eq for Global<T> where T: Eq {}
impl<'s, T: Hash> Hash for Local<'s, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<T: Hash> Hash for Global<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe {
if self.isolate_handle.get_isolate_ptr().is_null() {
panic!("can't hash Global after its host Isolate has been disposed");
}
self.data.as_ref().hash(state);
}
}
}
impl<'s, T, Rhs: Handle> PartialEq<Rhs> for Local<'s, T>
where
T: PartialEq<Rhs::Data>,
{
fn eq(&self, other: &Rhs) -> bool {
let i1 = self.get_handle_info();
let i2 = other.get_handle_info();
i1.host.match_host(i2.host, None)
&& unsafe { i1.data.as_ref() == i2.data.as_ref() }
}
}
impl<T, Rhs: Handle> PartialEq<Rhs> for Global<T>
where
T: PartialEq<Rhs::Data>,
{
fn eq(&self, other: &Rhs) -> bool {
let i1 = self.get_handle_info();
let i2 = other.get_handle_info();
i1.host.match_host(i2.host, None)
&& unsafe { i1.data.as_ref() == i2.data.as_ref() }
}
}
#[derive(Copy, Debug, Clone)]
pub struct HandleInfo<T> {
data: NonNull<T>,
host: HandleHost,
}
impl<T> HandleInfo<T> {
fn new(data: NonNull<T>, host: HandleHost) -> Self {
Self { data, host }
}
}
#[derive(Copy, Debug, Clone)]
enum HandleHost {
Scope,
Isolate(NonNull<Isolate>),
DisposedIsolate,
}
impl From<&'_ mut Isolate> for HandleHost {
fn from(isolate: &'_ mut Isolate) -> Self {
Self::Isolate(NonNull::from(isolate))
}
}
impl From<&'_ IsolateHandle> for HandleHost {
fn from(isolate_handle: &IsolateHandle) -> Self {
NonNull::new(unsafe { isolate_handle.get_isolate_ptr() })
.map(Self::Isolate)
.unwrap_or(Self::DisposedIsolate)
}
}
impl HandleHost {
fn match_host(
self,
other: Self,
scope_isolate_opt: Option<&mut Isolate>,
) -> bool {
let scope_isolate_opt_nn = scope_isolate_opt.map(NonNull::from);
match (self, other, scope_isolate_opt_nn) {
(Self::Scope, Self::Scope, _) => true,
(Self::Isolate(ile1), Self::Isolate(ile2), _) => ile1 == ile2,
(Self::Scope, Self::Isolate(ile1), Some(ile2)) => ile1 == ile2,
(Self::Isolate(ile1), Self::Scope, Some(ile2)) => ile1 == ile2,
(Self::Scope, Self::Isolate(_), _) => true,
(Self::Isolate(_), Self::Scope, _) => true,
(Self::DisposedIsolate, ..) | (_, Self::DisposedIsolate, _) => {
panic!("attempt to access Handle hosted by disposed Isolate")
}
}
}
fn assert_match_host(self, other: Self, scope_opt: Option<&mut Isolate>) {
assert!(
self.match_host(other, scope_opt),
"attempt to use Handle in an Isolate that is not its host"
)
}
#[allow(dead_code)]
fn match_isolate(self, isolate: &mut Isolate) -> bool {
self.match_host(isolate.into(), Some(isolate))
}
fn assert_match_isolate(self, isolate: &mut Isolate) {
self.assert_match_host(isolate.into(), Some(isolate))
}
fn get_isolate(self) -> NonNull<Isolate> {
match self {
Self::Scope => panic!("host Isolate for Handle not available"),
Self::Isolate(ile) => ile,
Self::DisposedIsolate => panic!("attempt to access disposed Isolate"),
}
}
#[allow(dead_code)]
fn get_isolate_handle(self) -> IsolateHandle {
unsafe { self.get_isolate().as_ref() }.thread_safe_handle()
}
}
#[derive(Debug)]
pub struct Weak<T> {
data: Option<Box<WeakData<T>>>,
isolate_handle: IsolateHandle,
}
impl<T> Weak<T> {
pub fn new(isolate: &mut Isolate, handle: impl Handle<Data = T>) -> Self {
let HandleInfo { data, host } = handle.get_handle_info();
host.assert_match_isolate(isolate);
Self::new_raw(isolate, data, None)
}
pub fn with_finalizer(
isolate: &mut Isolate,
handle: impl Handle<Data = T>,
finalizer: Box<dyn FnOnce(&mut Isolate)>,
) -> Self {
let HandleInfo { data, host } = handle.get_handle_info();
host.assert_match_isolate(isolate);
let finalizer_id = isolate
.get_finalizer_map_mut()
.add(FinalizerCallback::Regular(finalizer));
Self::new_raw(isolate, data, Some(finalizer_id))
}
pub fn with_guaranteed_finalizer(
isolate: &mut Isolate,
handle: impl Handle<Data = T>,
finalizer: Box<dyn FnOnce()>,
) -> Self {
let HandleInfo { data, host } = handle.get_handle_info();
host.assert_match_isolate(isolate);
let finalizer_id = isolate
.get_finalizer_map_mut()
.add(FinalizerCallback::Guaranteed(finalizer));
Self::new_raw(isolate, data, Some(finalizer_id))
}
fn new_raw(
isolate: *mut Isolate,
data: NonNull<T>,
finalizer_id: Option<FinalizerId>,
) -> Self {
let weak_data = Box::new(WeakData {
pointer: Default::default(),
finalizer_id,
weak_dropped: Cell::new(false),
});
let data = data.cast().as_ptr();
let data = unsafe {
v8__Global__NewWeak(
isolate,
data,
weak_data.deref() as *const _ as *const c_void,
Self::first_pass_callback,
)
};
weak_data
.pointer
.set(Some(unsafe { NonNull::new_unchecked(data as *mut _) }));
Self {
data: Some(weak_data),
isolate_handle: unsafe { (*isolate).thread_safe_handle() },
}
}
pub fn empty(isolate: &mut Isolate) -> Self {
Weak {
data: None,
isolate_handle: isolate.thread_safe_handle(),
}
}
pub fn clone_with_finalizer(
&self,
finalizer: Box<dyn FnOnce(&mut Isolate)>,
) -> Self {
self.clone_raw(Some(FinalizerCallback::Regular(finalizer)))
}
pub fn clone_with_guaranteed_finalizer(
&self,
finalizer: Box<dyn FnOnce()>,
) -> Self {
self.clone_raw(Some(FinalizerCallback::Guaranteed(finalizer)))
}
fn clone_raw(&self, finalizer: Option<FinalizerCallback>) -> Self {
if let Some(data) = self.get_pointer() {
let isolate_ptr = unsafe { self.isolate_handle.get_isolate_ptr() };
if isolate_ptr.is_null() {
unreachable!("Isolate was dropped but weak handle wasn't reset.");
}
let finalizer_id = if let Some(finalizer) = finalizer {
let isolate = unsafe { &mut *isolate_ptr };
Some(isolate.get_finalizer_map_mut().add(finalizer))
} else {
None
};
Self::new_raw(isolate_ptr, data, finalizer_id)
} else {
Weak {
data: None,
isolate_handle: self.isolate_handle.clone(),
}
}
}
pub unsafe fn from_raw(
isolate: &mut Isolate,
data: Option<NonNull<WeakData<T>>>,
) -> Self {
Weak {
data: data.map(|raw| Box::from_raw(raw.cast().as_ptr())),
isolate_handle: isolate.thread_safe_handle(),
}
}
pub fn into_raw(mut self) -> Option<NonNull<WeakData<T>>> {
if let Some(data) = self.data.take() {
let has_finalizer = if let Some(finalizer_id) = data.finalizer_id {
let isolate_ptr = unsafe { self.isolate_handle.get_isolate_ptr() };
if isolate_ptr.is_null() {
false
} else {
let isolate = unsafe { &mut *isolate_ptr };
isolate.get_finalizer_map().map.contains_key(&finalizer_id)
}
} else {
false
};
if data.pointer.get().is_none() && !has_finalizer {
None
} else {
assert!(!data.weak_dropped.get());
Some(unsafe { NonNull::new_unchecked(Box::into_raw(data)) })
}
} else {
None
}
}
fn get_pointer(&self) -> Option<NonNull<T>> {
if let Some(data) = &self.data {
if unsafe { self.isolate_handle.get_isolate_ptr() }.is_null() {
None
} else {
data.pointer.get()
}
} else {
None
}
}
pub fn is_empty(&self) -> bool {
self.get_pointer().is_none()
}
pub fn to_global(&self, isolate: &mut Isolate) -> Option<Global<T>> {
if let Some(data) = self.get_pointer() {
let handle_host: HandleHost = (&self.isolate_handle).into();
handle_host.assert_match_isolate(isolate);
Some(unsafe { Global::new_raw(isolate, data) })
} else {
None
}
}
pub fn to_local<'s>(
&self,
scope: &mut HandleScope<'s, ()>,
) -> Option<Local<'s, T>> {
if let Some(data) = self.get_pointer() {
let handle_host: HandleHost = (&self.isolate_handle).into();
handle_host.assert_match_isolate(scope);
let local = unsafe {
scope.cast_local(|sd| {
v8__Local__New(sd.get_isolate_ptr(), data.cast().as_ptr()) as *const T
})
};
Some(local.unwrap())
} else {
None
}
}
extern "C" fn first_pass_callback(wci: *const WeakCallbackInfo) {
let weak_data = unsafe {
let ptr = v8__WeakCallbackInfo__GetParameter(wci);
&*(ptr as *mut WeakData<T>)
};
let data = weak_data.pointer.take().unwrap();
unsafe {
v8__Global__Reset(data.cast().as_ptr());
}
if weak_data.finalizer_id.is_some() {
unsafe {
v8__WeakCallbackInfo__SetSecondPassCallback(
wci,
Self::second_pass_callback,
)
};
}
}
extern "C" fn second_pass_callback(wci: *const WeakCallbackInfo) {
let isolate = unsafe { &mut *v8__WeakCallbackInfo__GetIsolate(wci) };
let weak_data = unsafe {
let ptr = v8__WeakCallbackInfo__GetParameter(wci);
&*(ptr as *mut WeakData<T>)
};
let finalizer: Option<FinalizerCallback> = {
let finalizer_id = weak_data.finalizer_id.unwrap();
isolate.get_finalizer_map_mut().map.remove(&finalizer_id)
};
if weak_data.weak_dropped.get() {
let _ = unsafe {
Box::from_raw(weak_data as *const WeakData<T> as *mut WeakData<T>)
};
}
match finalizer {
Some(FinalizerCallback::Regular(finalizer)) => finalizer(isolate),
Some(FinalizerCallback::Guaranteed(finalizer)) => finalizer(),
None => {}
}
}
}
impl<T> Clone for Weak<T> {
fn clone(&self) -> Self {
self.clone_raw(None)
}
}
impl<T> Drop for Weak<T> {
fn drop(&mut self) {
let remove_finalizer = |finalizer_id: Option<FinalizerId>| -> bool {
if let Some(finalizer_id) = finalizer_id {
let isolate_ptr = unsafe { self.isolate_handle.get_isolate_ptr() };
if !isolate_ptr.is_null() {
let isolate = unsafe { &mut *isolate_ptr };
let finalizer =
isolate.get_finalizer_map_mut().map.remove(&finalizer_id);
return finalizer.is_some();
}
}
false
};
if let Some(data) = self.get_pointer() {
unsafe { v8__Global__Reset(data.cast().as_ptr()) };
remove_finalizer(self.data.as_ref().unwrap().finalizer_id);
} else if let Some(weak_data) = self.data.take() {
if remove_finalizer(weak_data.finalizer_id) {
weak_data.weak_dropped.set(true);
Box::leak(weak_data);
}
}
}
}
impl<T> Eq for Weak<T> where T: Eq {}
impl<T, Rhs: Handle> PartialEq<Rhs> for Weak<T>
where
T: PartialEq<Rhs::Data>,
{
fn eq(&self, other: &Rhs) -> bool {
let HandleInfo {
data: other_data,
host: other_host,
} = other.get_handle_info();
let self_host: HandleHost = (&self.isolate_handle).into();
if !self_host.match_host(other_host, None) {
false
} else if let Some(self_data) = self.get_pointer() {
unsafe { self_data.as_ref() == other_data.as_ref() }
} else {
false
}
}
}
impl<T, T2> PartialEq<Weak<T2>> for Weak<T>
where
T: PartialEq<T2>,
{
fn eq(&self, other: &Weak<T2>) -> bool {
let self_host: HandleHost = (&self.isolate_handle).into();
let other_host: HandleHost = (&other.isolate_handle).into();
if !self_host.match_host(other_host, None) {
return false;
}
match (self.get_pointer(), other.get_pointer()) {
(Some(self_data), Some(other_data)) => unsafe {
self_data.as_ref() == other_data.as_ref()
},
(None, None) => true,
_ => false,
}
}
}
pub struct WeakData<T> {
pointer: Cell<Option<NonNull<T>>>,
finalizer_id: Option<FinalizerId>,
weak_dropped: Cell<bool>,
}
impl<T> std::fmt::Debug for WeakData<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WeakData")
.field("pointer", &self.pointer)
.finish_non_exhaustive()
}
}
#[repr(C)]
struct WeakCallbackInfo(Opaque);
type FinalizerId = usize;
pub(crate) enum FinalizerCallback {
Regular(Box<dyn FnOnce(&mut Isolate)>),
Guaranteed(Box<dyn FnOnce()>),
}
#[derive(Default)]
pub(crate) struct FinalizerMap {
map: std::collections::HashMap<FinalizerId, FinalizerCallback>,
next_id: FinalizerId,
}
impl FinalizerMap {
fn add(&mut self, finalizer: FinalizerCallback) -> FinalizerId {
let id = self.next_id;
self.next_id += 1;
self.map.insert(id, finalizer);
id
}
pub(crate) fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub(crate) fn drain(
&mut self,
) -> impl Iterator<Item = FinalizerCallback> + '_ {
self.map.drain().map(|(_, finalizer)| finalizer)
}
}