use std::mem::transmute;
use std::ptr::NonNull;
use crate::Data;
use crate::InIsolate;
use crate::Isolate;
use crate::IsolateHandle;
use crate::Local;
use crate::ToLocal;
extern "C" {
fn v8__Local__New(isolate: *mut Isolate, other: *const Data) -> *const Data;
fn v8__Global__New(isolate: *mut Isolate, other: *const Data) -> *const Data;
fn v8__Global__Reset__0(this: *mut *const Data);
fn v8__Global__Reset__2(
this: *mut *const Data,
isolate: *mut Isolate,
other: *const *const Data,
);
}
#[repr(C)]
pub struct Global<T> {
value: Option<NonNull<T>>,
isolate_handle: Option<IsolateHandle>,
}
impl<T> Global<T> {
pub fn new() -> Self {
Self {
value: None,
isolate_handle: None,
}
}
pub fn new_from(
scope: &mut impl InIsolate,
other: impl AnyHandle<T>,
) -> Self {
let isolate = scope.isolate();
let other_value = other.read(isolate);
Self {
value: other_value
.map(|v| unsafe { transmute(v8__Global__New(isolate, transmute(v))) }),
isolate_handle: other_value.map(|_| isolate.thread_safe_handle()),
}
}
pub fn is_empty(&self) -> bool {
self.value.is_none()
}
pub fn get<'sc>(
&self,
scope: &mut impl ToLocal<'sc>,
) -> Option<Local<'sc, T>> {
let isolate = scope.isolate();
self.check_isolate(isolate);
self
.value
.map(|g| g.as_ptr() as *const Data)
.map(|g| unsafe { v8__Local__New(isolate, g) })
.and_then(|l| unsafe { scope.to_local(l as *const T) })
}
pub fn set(&mut self, scope: &mut impl InIsolate, other: impl AnyHandle<T>) {
let isolate = scope.isolate();
self.check_isolate(isolate);
let other_value = other.read(isolate);
match (&mut self.value, &other_value) {
(None, None) => {}
(target, None) => unsafe {
v8__Global__Reset__0(
&mut *(target as *mut Option<NonNull<T>> as *mut *const Data),
)
},
(target, source) => unsafe {
v8__Global__Reset__2(
&mut *(target as *mut Option<NonNull<T>> as *mut *const Data),
isolate,
&*(source as *const Option<NonNull<T>> as *const *const Data),
)
},
}
self.isolate_handle = other_value.map(|_| isolate.thread_safe_handle());
}
pub fn reset(&mut self, scope: &mut impl InIsolate) {
self.set(scope, None);
}
fn check_isolate(&self, isolate: &mut Isolate) {
match self.value {
None => assert!(self.isolate_handle.is_none()),
Some(_) => assert_eq!(
unsafe { self.isolate_handle.as_ref().unwrap().get_isolate_ptr() },
isolate as *mut _
),
}
}
}
impl<T> Default for Global<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Drop for Global<T> {
fn drop(&mut self) {
match &mut self.value {
None => {
assert!(self.isolate_handle.is_none())
}
Some(_)
if unsafe {
self
.isolate_handle
.as_ref()
.unwrap()
.get_isolate_ptr()
.is_null()
} =>
{
}
addr @ Some(_) => unsafe {
v8__Global__Reset__0(
&mut *(addr as *mut Option<NonNull<T>> as *mut *const Data),
)
},
}
}
}
pub trait AnyHandle<T> {
fn read(self, isolate: &mut Isolate) -> Option<NonNull<T>>;
}
impl<'sc, T> AnyHandle<T> for Local<'sc, T> {
fn read(self, _isolate: &mut Isolate) -> Option<NonNull<T>> {
Some(self.as_non_null())
}
}
impl<'sc, T> AnyHandle<T> for Option<Local<'sc, T>> {
fn read(self, _isolate: &mut Isolate) -> Option<NonNull<T>> {
self.map(|local| local.as_non_null())
}
}
impl<'sc, T> AnyHandle<T> for &Global<T> {
fn read(self, isolate: &mut Isolate) -> Option<NonNull<T>> {
self.check_isolate(isolate);
self.value
}
}