use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use std::ops::Deref;
use std::sync::atomic;
use allocative::Allocative;
use dupe::Clone_;
use dupe::Copy_;
use dupe::Dupe_;
use starlark_derive::NoSerialize;
use starlark_derive::StarlarkPagable;
use starlark_derive::starlark_value;
use crate as starlark;
use crate::any::ProvidesStaticType;
use crate::pagable::vtable_register::VtableRegistered;
use crate::typing::starlark_value::TyStarlarkValueVTable;
use crate::values::AllocValue;
use crate::values::Freeze;
use crate::values::FreezeResult;
use crate::values::Freezer;
use crate::values::FrozenHeap;
use crate::values::FrozenValue;
use crate::values::FrozenValueTyped;
use crate::values::Heap;
use crate::values::StarlarkValue;
use crate::values::Trace;
use crate::values::Tracer;
use crate::values::Value;
use crate::values::ValueLike;
#[derive(ProvidesStaticType, NoSerialize, Allocative, derive_more::Display)]
#[allocative(bound = "")]
#[display("{:?}", self)]
#[repr(transparent)]
pub struct StarlarkAny<T: Debug + Send + Sync + 'static>(
#[allocative(skip)] pub T,
);
impl<T: StarlarkAnyRegistered> crate::pagable::StarlarkSerialize for StarlarkAny<T> {
fn starlark_serialize(
&self,
ctx: &mut dyn crate::pagable::StarlarkSerializeContext,
) -> crate::Result<()> {
<T as crate::pagable::StarlarkSerialize>::starlark_serialize(&self.0, ctx)
}
}
impl<T: StarlarkAnyRegistered> crate::pagable::StarlarkDeserialize for StarlarkAny<T> {
fn starlark_deserialize(
ctx: &mut dyn crate::pagable::StarlarkDeserializeContext<'_>,
) -> crate::Result<Self> {
Ok(StarlarkAny(
<T as crate::pagable::StarlarkDeserialize>::starlark_deserialize(ctx)?,
))
}
}
pub unsafe trait StarlarkAnyRegistered:
Debug + Send + Sync + 'static + crate::pagable::StarlarkPagable
{
const TY_VTABLE_STATIC: pagable::StaticValue<TyStarlarkValueVTable>;
}
#[cfg(feature = "pagable")]
impl<T> crate::typing::HasTyVTable for StarlarkAny<T>
where
T: StarlarkAnyRegistered,
{
const TY_VTABLE_STATIC: pagable::StaticValue<TyStarlarkValueVTable> =
<T as StarlarkAnyRegistered>::TY_VTABLE_STATIC;
}
#[starlark_value(type = "any", skip_pagable)]
impl<'v, T: StarlarkAnyRegistered> StarlarkValue<'v> for StarlarkAny<T> {
type Canonical = Self;
}
#[macro_export]
macro_rules! register_starlark_any {
($t:ty) => {
const _: () = {
$crate::__declare_ty_vtable_static!($crate::values::any::StarlarkAny<$t>);
unsafe impl $crate::values::any::StarlarkAnyRegistered for $t {
const TY_VTABLE_STATIC: pagable::StaticValue<
$crate::__derive_refs::TyStarlarkValueVTable,
> = VTABLE_STATIC;
}
};
$crate::register_simple_vtable_entry!($crate::values::any::StarlarkAny<$t>);
};
}
impl<'v, T: StarlarkAnyRegistered> AllocValue<'v> for StarlarkAny<T> {
fn alloc_value(self, heap: Heap<'v>) -> Value<'v> {
heap.alloc_simple(self)
}
}
impl<T: Debug + Send + Sync + 'static> Debug for StarlarkAny<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}
impl<T: Debug + Send + Sync + 'static> Deref for StarlarkAny<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.0
}
}
impl<T: Debug + Send + Sync + 'static> StarlarkAny<T> {
pub const fn new(x: T) -> Self {
StarlarkAny(x)
}
}
impl<T: StarlarkAnyRegistered> StarlarkAny<T> {
pub fn get<'v>(x: Value<'v>) -> Option<&'v T> {
let x: &StarlarkAny<T> = x.downcast_ref()?;
Some(&x.0)
}
}
#[cfg(feature = "pagable")]
pub trait StarlarkAnyBound: StarlarkAnyRegistered {}
#[cfg(feature = "pagable")]
impl<T: StarlarkAnyRegistered> StarlarkAnyBound for T {}
#[cfg(not(feature = "pagable"))]
pub trait StarlarkAnyBound: Debug + Send + Sync + 'static {}
#[cfg(not(feature = "pagable"))]
impl<T: Debug + Send + Sync + 'static> StarlarkAnyBound for T {}
#[cfg(feature = "pagable")]
unsafe impl<T: StarlarkAnyBound> VtableRegistered for StarlarkAny<T> {}
#[cfg(not(feature = "pagable"))]
unsafe impl<T: StarlarkAnyBound> VtableRegistered for StarlarkAny<T> {}
#[macro_export]
macro_rules! static_starlark_any {
($vis:vis $name:ident : $T:ty = $value:expr) => {
unsafe impl $crate::pagable::static_value::StaticValueRegistered
for $crate::values::types::any::StarlarkAny<$T> {}
$crate::static_starlark_any!(@no_impl $vis $name : $T = $value);
};
(@no_impl $vis:vis $name:ident : $T:ty = $value:expr) => {
$vis static $name: $crate::values::AllocStaticSimple<
$crate::values::types::any::StarlarkAny<$T>,
> = $crate::values::AllocStaticSimple::alloc(
$crate::values::types::any::StarlarkAny::new($value),
);
$crate::__derive_refs::inventory::submit! {
$crate::__derive_refs::StaticValueEntry::new(
file!(),
line!(),
|| $name.to_frozen_value()
)
}
};
}
#[derive(Allocative, StarlarkPagable, Copy_, Clone_, Dupe_)]
#[allocative(skip)]
pub struct FrozenAnyValue<T: StarlarkAnyRegistered>(FrozenValueTyped<'static, StarlarkAny<T>>);
impl<T: StarlarkAnyRegistered> Debug for FrozenAnyValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self.as_ref(), f)
}
}
impl<T: Display + StarlarkAnyRegistered> Display for FrozenAnyValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(self.as_ref(), f)
}
}
impl<T: StarlarkAnyRegistered> Deref for FrozenAnyValue<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.as_ref()
}
}
impl<T: StarlarkAnyRegistered + PartialEq> PartialEq for FrozenAnyValue<T> {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl<T: StarlarkAnyRegistered + Eq> Eq for FrozenAnyValue<T> {}
unsafe impl<'v, T: StarlarkAnyRegistered> Trace<'v> for FrozenAnyValue<T> {
fn trace(&mut self, _: &Tracer<'v>) {}
}
impl<T: StarlarkAnyRegistered> Freeze for FrozenAnyValue<T> {
type Frozen = Self;
fn freeze(self, _freezer: &Freezer) -> FreezeResult<Self::Frozen> {
Ok(self)
}
}
impl<T: StarlarkAnyRegistered> FrozenAnyValue<T> {
#[inline]
pub fn as_ref(&self) -> &'static T {
&self.0.as_ref().0
}
#[inline]
pub fn to_frozen_value(self) -> FrozenValue {
self.0.to_frozen_value()
}
#[inline]
pub(crate) fn from_typed(typed: FrozenValueTyped<'static, StarlarkAny<T>>) -> Self {
FrozenAnyValue(typed)
}
#[inline]
pub(crate) unsafe fn new_unchecked(value: FrozenValue) -> Self {
FrozenAnyValue(unsafe { FrozenValueTyped::new_unchecked(value) })
}
}
pub(crate) struct AtomicFrozenAnyValueOption<T: StarlarkAnyRegistered>(
atomic::AtomicPtr<()>,
std::marker::PhantomData<T>,
);
const _: () = assert!(std::mem::size_of::<Option<FrozenValue>>() == std::mem::size_of::<*mut ()>());
unsafe impl<'v, T: StarlarkAnyRegistered> Trace<'v> for AtomicFrozenAnyValueOption<T> {
fn trace(&mut self, _: &Tracer<'v>) {
}
}
impl<T: StarlarkAnyRegistered> AtomicFrozenAnyValueOption<T> {
fn encode(value: Option<FrozenAnyValue<T>>) -> *mut () {
let opt: Option<FrozenValue> = value.map(FrozenAnyValue::to_frozen_value);
unsafe { std::mem::transmute(opt) }
}
unsafe fn decode(raw: *mut ()) -> Option<FrozenAnyValue<T>> {
let opt: Option<FrozenValue> = unsafe { std::mem::transmute(raw) };
opt.map(|fv| unsafe { FrozenAnyValue::new_unchecked(fv) })
}
pub(crate) fn new(value: Option<FrozenAnyValue<T>>) -> Self {
AtomicFrozenAnyValueOption(
atomic::AtomicPtr::new(Self::encode(value)),
std::marker::PhantomData,
)
}
pub(crate) fn load_relaxed(&self) -> Option<FrozenAnyValue<T>> {
let raw = self.0.load(atomic::Ordering::Relaxed);
unsafe { Self::decode(raw) }
}
pub(crate) fn store_relaxed(&self, value: FrozenAnyValue<T>) {
self.0
.store(Self::encode(Some(value)), atomic::Ordering::Relaxed);
}
}
impl<T: StarlarkAnyRegistered> crate::pagable::StarlarkSerialize for AtomicFrozenAnyValueOption<T> {
fn starlark_serialize(
&self,
ctx: &mut dyn crate::pagable::StarlarkSerializeContext,
) -> crate::Result<()> {
let value = self.load_relaxed();
value.starlark_serialize(ctx)
}
}
impl<T: StarlarkAnyRegistered> crate::pagable::StarlarkDeserialize
for AtomicFrozenAnyValueOption<T>
{
fn starlark_deserialize(
ctx: &mut dyn crate::pagable::StarlarkDeserializeContext<'_>,
) -> crate::Result<Self> {
let value: Option<FrozenAnyValue<T>> =
<Option<FrozenAnyValue<T>> as crate::pagable::StarlarkDeserialize>::starlark_deserialize(ctx)?;
Ok(AtomicFrozenAnyValueOption::new(value))
}
}
impl FrozenHeap {
pub fn alloc_any_value<T: StarlarkAnyRegistered>(&self, value: T) -> FrozenAnyValue<T> {
FrozenAnyValue::from_typed(self.alloc_simple_typed_static(StarlarkAny::new(value)))
}
}
#[cfg(test)]
crate::register_starlark_any!(String);