use crate::jsapi::JS;
use crate::jsapi::{jsid, JSFunction, JSObject, JSScript, JSString, JSTracer};
use crate::jsid::VoidId;
use std::cell::UnsafeCell;
use std::ffi::{c_char, c_void};
use std::mem;
use std::ptr;
pub trait RootKind {
type Vtable;
const VTABLE: Self::Vtable;
const KIND: JS::RootKind;
}
impl RootKind for *mut JSObject {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::Object;
}
impl RootKind for *mut JSFunction {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::Object;
}
impl RootKind for *mut JSString {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::String;
}
impl RootKind for *mut JS::Symbol {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::Symbol;
}
impl RootKind for *mut JS::BigInt {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::BigInt;
}
impl RootKind for *mut JSScript {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::Script;
}
impl RootKind for jsid {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::Id;
}
impl RootKind for JS::Value {
type Vtable = ();
const VTABLE: Self::Vtable = ();
const KIND: JS::RootKind = JS::RootKind::Value;
}
impl<T: Rootable> RootKind for T {
type Vtable = *const RootedVFTable;
const VTABLE: Self::Vtable = &<Self as Rootable>::VTABLE;
const KIND: JS::RootKind = JS::RootKind::Traceable;
}
#[repr(C)]
pub struct RootedVFTable {
#[cfg(windows)]
pub padding: [usize; 1],
#[cfg(not(windows))]
pub padding: [usize; 2],
pub trace: unsafe extern "C" fn(this: *mut c_void, trc: *mut JSTracer, name: *const c_char),
}
impl RootedVFTable {
#[cfg(windows)]
pub const PADDING: [usize; 1] = [0];
#[cfg(not(windows))]
pub const PADDING: [usize; 2] = [0, 0];
}
pub trait Rootable: crate::trace::Traceable + Sized {
const VTABLE: RootedVFTable = RootedVFTable {
padding: RootedVFTable::PADDING,
trace: <Self as Rootable>::trace,
};
unsafe extern "C" fn trace(this: *mut c_void, trc: *mut JSTracer, _name: *const c_char) {
let rooted = this as *mut Rooted<Self>;
let rooted = rooted.as_mut().unwrap();
<Self as crate::trace::Traceable>::trace(&mut rooted.ptr, trc);
}
}
impl<T: Rootable> Rootable for Option<T> {}
#[repr(C)]
#[derive(Debug)]
pub struct RootedBase {
pub stack: *mut *mut RootedBase,
pub prev: *mut RootedBase,
}
#[repr(C)]
#[cfg_attr(
feature = "crown",
crown::unrooted_must_root_lint::allow_unrooted_interior
)]
pub struct Rooted<T: RootKind> {
pub vtable: T::Vtable,
pub base: RootedBase,
pub ptr: T,
}
pub trait Initialize: Sized {
unsafe fn initial() -> Option<Self>;
}
impl<T> Initialize for Option<T> {
unsafe fn initial() -> Option<Self> {
Some(None)
}
}
pub trait GCMethods: Initialize {
unsafe fn initial() -> Self {
<Self as Initialize>::initial()
.expect("Types used with heap GC methods must have a valid default")
}
unsafe fn post_barrier(v: *mut Self, prev: Self, next: Self);
}
impl Initialize for *mut JSObject {
unsafe fn initial() -> Option<*mut JSObject> {
Some(ptr::null_mut())
}
}
impl GCMethods for *mut JSObject {
unsafe fn post_barrier(v: *mut *mut JSObject, prev: *mut JSObject, next: *mut JSObject) {
JS::HeapObjectWriteBarriers(v, prev, next);
}
}
impl Initialize for *mut JSFunction {
unsafe fn initial() -> Option<*mut JSFunction> {
Some(ptr::null_mut())
}
}
impl GCMethods for *mut JSFunction {
unsafe fn post_barrier(v: *mut *mut JSFunction, prev: *mut JSFunction, next: *mut JSFunction) {
JS::HeapObjectWriteBarriers(
mem::transmute(v),
mem::transmute(prev),
mem::transmute(next),
);
}
}
impl Initialize for *mut JSString {
unsafe fn initial() -> Option<*mut JSString> {
Some(ptr::null_mut())
}
}
impl GCMethods for *mut JSString {
unsafe fn post_barrier(v: *mut *mut JSString, prev: *mut JSString, next: *mut JSString) {
JS::HeapStringWriteBarriers(v, prev, next);
}
}
impl Initialize for *mut JS::Symbol {
unsafe fn initial() -> Option<*mut JS::Symbol> {
Some(ptr::null_mut())
}
}
impl GCMethods for *mut JS::Symbol {
unsafe fn post_barrier(_: *mut *mut JS::Symbol, _: *mut JS::Symbol, _: *mut JS::Symbol) {}
}
impl Initialize for *mut JS::BigInt {
unsafe fn initial() -> Option<*mut JS::BigInt> {
Some(ptr::null_mut())
}
}
impl GCMethods for *mut JS::BigInt {
unsafe fn post_barrier(v: *mut *mut JS::BigInt, prev: *mut JS::BigInt, next: *mut JS::BigInt) {
JS::HeapBigIntWriteBarriers(v, prev, next);
}
}
impl Initialize for *mut JSScript {
unsafe fn initial() -> Option<*mut JSScript> {
Some(ptr::null_mut())
}
}
impl GCMethods for *mut JSScript {
unsafe fn post_barrier(v: *mut *mut JSScript, prev: *mut JSScript, next: *mut JSScript) {
JS::HeapScriptWriteBarriers(v, prev, next);
}
}
impl Initialize for jsid {
unsafe fn initial() -> Option<jsid> {
Some(VoidId())
}
}
impl GCMethods for jsid {
unsafe fn post_barrier(_: *mut jsid, _: jsid, _: jsid) {}
}
impl Initialize for JS::Value {
unsafe fn initial() -> Option<JS::Value> {
Some(JS::Value::default())
}
}
impl GCMethods for JS::Value {
unsafe fn post_barrier(v: *mut JS::Value, prev: JS::Value, next: JS::Value) {
JS::HeapValueWriteBarriers(v, &prev, &next);
}
}
impl Rootable for JS::PropertyDescriptor {}
impl Initialize for JS::PropertyDescriptor {
unsafe fn initial() -> Option<JS::PropertyDescriptor> {
Some(JS::PropertyDescriptor::default())
}
}
impl GCMethods for JS::PropertyDescriptor {
unsafe fn post_barrier(
_: *mut JS::PropertyDescriptor,
_: JS::PropertyDescriptor,
_: JS::PropertyDescriptor,
) {
}
}
pub struct ValueArray<const N: usize> {
elements: [JS::Value; N],
}
impl<const N: usize> ValueArray<N> {
pub fn new(elements: [JS::Value; N]) -> Self {
Self { elements }
}
pub unsafe fn get_ptr(&self) -> *const JS::Value {
self.elements.as_ptr()
}
pub unsafe fn get_mut_ptr(&self) -> *mut JS::Value {
self.elements.as_ptr() as *mut _
}
}
impl<const N: usize> Rootable for ValueArray<N> {}
impl<const N: usize> Initialize for ValueArray<N> {
unsafe fn initial() -> Option<Self> {
Some(Self {
elements: [<JS::Value as GCMethods>::initial(); N],
})
}
}
pub type RootedValueArray<const N: usize> = Rooted<ValueArray<N>>;
#[repr(C)]
#[derive(Debug)]
pub struct Heap<T: GCMethods + Copy> {
pub ptr: UnsafeCell<T>,
}
impl<T: GCMethods + Copy> Heap<T> {
pub fn boxed(v: T) -> Box<Heap<T>>
where
Heap<T>: Default,
{
let boxed = Box::new(Heap::default());
boxed.set(v);
boxed
}
pub fn set(&self, v: T) {
unsafe {
let ptr = self.ptr.get();
let prev = *ptr;
*ptr = v;
T::post_barrier(ptr, prev, v);
}
}
pub fn get(&self) -> T {
unsafe { *self.ptr.get() }
}
pub fn get_unsafe(&self) -> *mut T {
self.ptr.get()
}
pub unsafe fn handle(&self) -> JS::Handle<T> {
JS::Handle::from_marked_location(self.ptr.get() as *const _)
}
}
impl<T> Default for Heap<*mut T>
where
*mut T: GCMethods + Copy,
{
fn default() -> Heap<*mut T> {
Heap {
ptr: UnsafeCell::new(ptr::null_mut()),
}
}
}
impl Default for Heap<JS::Value> {
fn default() -> Heap<JS::Value> {
Heap {
ptr: UnsafeCell::new(JS::Value::default()),
}
}
}
impl<T: GCMethods + Copy> Drop for Heap<T> {
fn drop(&mut self) {
unsafe {
let ptr = self.ptr.get();
T::post_barrier(ptr, *ptr, <T as GCMethods>::initial());
}
}
}
impl<T: GCMethods + Copy + PartialEq> PartialEq for Heap<T> {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
pub trait IntoHandle {
type Target;
fn into_handle(self) -> JS::Handle<Self::Target>;
}
pub trait IntoMutableHandle: IntoHandle {
fn into_handle_mut(self) -> JS::MutableHandle<Self::Target>;
}
impl<T: IntoHandle> From<T> for JS::Handle<T::Target> {
fn from(value: T) -> Self {
value.into_handle()
}
}
impl<T: IntoMutableHandle> From<T> for JS::MutableHandle<T::Target> {
fn from(value: T) -> Self {
value.into_handle_mut()
}
}
#[repr(C)]
pub struct CustomAutoRooterVFTable {
#[cfg(windows)]
pub padding: [usize; 1],
#[cfg(not(windows))]
pub padding: [usize; 2],
pub trace: unsafe extern "C" fn(this: *mut c_void, trc: *mut JSTracer),
}
impl CustomAutoRooterVFTable {
#[cfg(windows)]
pub const PADDING: [usize; 1] = [0];
#[cfg(not(windows))]
pub const PADDING: [usize; 2] = [0, 0];
}