use std::cmp;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
use allocative::Allocative;
use allocative::Visitor;
use derive_more::Display;
use crate as starlark;
use crate::any::ProvidesStaticType;
use crate::pagable::starlark_deserialize::StarlarkDeserializeContext;
use crate::pagable::starlark_serialize::StarlarkSerializeContext;
use crate::values::FreezeResult;
use crate::values::Freezer;
use crate::values::FrozenValue;
use crate::values::HeapSendable;
use crate::values::StarlarkValue;
use crate::values::Tracer;
use crate::values::Value;
use crate::values::layout::aligned_size::AlignedSize;
use crate::values::layout::heap::arena::MIN_ALLOC;
use crate::values::layout::heap::repr::AValueHeader;
use crate::values::layout::heap::repr::AValueRepr;
use crate::values::layout::heap::repr::ForwardPtr;
use crate::values::layout::heap::send::HeapSyncable;
use crate::values::layout::value_alloc_size::ValueAllocSize;
#[cfg(feature = "pagable")]
pub trait AValueSimpleBound<'v>:
StarlarkValue<'v>
+ HeapSendable<'v>
+ HeapSyncable<'v>
+ crate::pagable::StarlarkPagable
+ crate::pagable::vtable_register::VtableRegistered
{
}
#[cfg(feature = "pagable")]
impl<'v, T> AValueSimpleBound<'v> for T where
T: StarlarkValue<'v>
+ HeapSendable<'v>
+ HeapSyncable<'v>
+ crate::pagable::StarlarkPagable
+ crate::pagable::vtable_register::VtableRegistered
{
}
#[cfg(not(feature = "pagable"))]
pub trait AValueSimpleBound<'v>: StarlarkValue<'v> + HeapSendable<'v> + HeapSyncable<'v> {}
#[cfg(not(feature = "pagable"))]
impl<'v, T> AValueSimpleBound<'v> for T where
T: StarlarkValue<'v> + HeapSendable<'v> + HeapSyncable<'v>
{
}
pub(crate) trait AValue<'v>: Sized + 'v {
type StarlarkValue: StarlarkValue<'v>;
type ExtraElem: 'v;
fn extra_len(value: &Self::StarlarkValue) -> usize;
fn offset_of_extra() -> usize;
const IS_STR: bool = false;
fn alloc_size_for_extra_len(extra_len: usize) -> ValueAllocSize {
assert!(
Self::offset_of_extra() % mem::align_of::<Self::ExtraElem>() == 0,
"extra must be aligned"
);
ValueAllocSize::new(cmp::max(
cmp::max(
AlignedSize::of::<AValueRepr<Self::StarlarkValue>>(),
MIN_ALLOC,
),
AlignedSize::align_up(
AValueRepr::<Self>::offset_of_extra()
+ (mem::size_of::<Self::ExtraElem>() * extra_len),
),
))
}
fn total_memory_for_profile(value: &Self::StarlarkValue) -> usize {
Self::alloc_size_for_extra_len(Self::extra_len(value)).bytes() as usize
+ allocative::size_of_unique_allocated_data(value)
}
fn visit_extra_allocative<'a, 'b: 'a>(
_value: &Self::StarlarkValue,
_visitor: &'a mut Visitor<'b>,
) {
}
unsafe fn heap_freeze<'fv>(
me: *mut AValueRepr<Self::StarlarkValue>,
freezer: &Freezer<'fv>,
) -> FreezeResult<FrozenValue>;
unsafe fn heap_copy(me: *mut AValueRepr<Self::StarlarkValue>, tracer: &Tracer<'v>)
-> Value<'v>;
fn starlark_serialize(
_me: *const AValueRepr<Self::StarlarkValue>,
_ctx: &mut dyn StarlarkSerializeContext,
) -> crate::Result<()> {
Err(crate::Error::new_kind(crate::ErrorKind::Other(
anyhow::anyhow!(
"Type `{}` does not support starlark serialization",
Self::StarlarkValue::TYPE
),
)))
}
fn starlark_deserialize(
_me: *mut AValueRepr<Self::StarlarkValue>,
_ctx: &mut dyn StarlarkDeserializeContext<'_>,
) -> crate::Result<()> {
Err(crate::Error::new_kind(crate::ErrorKind::Other(
anyhow::anyhow!(
"Type `{}` does not support starlark deserialization",
Self::StarlarkValue::TYPE
),
)))
}
}
#[repr(C)]
pub(crate) struct AValueImpl<'v, T: AValue<'v>>(PhantomData<T>, pub(crate) T::StarlarkValue);
impl<'v, T: AValue<'v>> AValueImpl<'v, T> {
pub(crate) const fn new(value: T::StarlarkValue) -> Self {
AValueImpl(PhantomData, value)
}
}
pub(super) unsafe fn try_freeze_directly<'v, A>(
me: *mut AValueRepr<A::StarlarkValue>,
freezer: &Freezer<'_>,
) -> Option<FreezeResult<FrozenValue>>
where
A: AValue<'v>,
{
unsafe {
let f = match (*me).payload.try_freeze_directly(freezer)? {
Ok(x) => x,
Err(e) => return Some(Err(e)),
};
drop(AValueHeader::overwrite_with_forward::<A::StarlarkValue>(
me,
ForwardPtr::new_frozen(f),
));
Some(Ok(f))
}
}
pub(super) unsafe fn heap_freeze_simple_impl<'v, A>(
me: *mut AValueRepr<A::StarlarkValue>,
freezer: &Freezer,
) -> FreezeResult<FrozenValue>
where
A: AValue<'v, ExtraElem = ()>,
A::StarlarkValue: HeapSendable<'v> + HeapSyncable<'v>,
{
unsafe {
let (fv, r) = freezer.reserve::<A>();
let x = AValueHeader::overwrite_with_forward::<A::StarlarkValue>(
me,
ForwardPtr::new_frozen(fv),
);
r.fill(x);
Ok(fv)
}
}
pub(super) unsafe fn heap_copy_impl<'v, A>(
me: *mut AValueRepr<A::StarlarkValue>,
tracer: &Tracer<'v>,
trace: impl FnOnce(&mut A::StarlarkValue, &Tracer<'v>),
) -> Value<'v>
where
A: AValue<'v, ExtraElem = ()>,
{
unsafe {
let (v, r) = tracer.reserve::<A>();
let mut x = AValueHeader::overwrite_with_forward::<A::StarlarkValue>(
me,
ForwardPtr::new_unfrozen(v),
);
trace(&mut x, tracer);
r.fill(x);
v
}
}
#[derive(Debug, Display, ProvidesStaticType, Allocative)]
#[display("BlackHole")]
pub(crate) struct BlackHole(pub(crate) ValueAllocSize);
#[cfg(test)]
mod tests {
use crate::environment::Module;
use crate::values::UnpackValue;
use crate::values::Value;
use crate::values::dict::AllocDict;
use crate::values::layout::heap::heap_type::StarlarkTestHeapName;
use crate::values::types::list::value::ListData;
#[test]
fn tuple_cycle_freeze() {
Module::with_temp_heap(|module| {
let list = module.heap().alloc_list(&[]);
let tuple = module.heap().alloc_tuple(&[list]);
ListData::from_value_mut(list)
.unwrap()
.push(tuple, module.heap());
module.set("t", tuple);
module.freeze_named(StarlarkTestHeapName::frozen_heap_name())?;
crate::Result::Ok(())
})
.unwrap();
}
#[test]
fn test_try_freeze_directly() {
Module::with_temp_heap(|module| {
let d0 = module.heap().alloc(AllocDict::EMPTY);
let d1 = module.heap().alloc(AllocDict::EMPTY);
assert_ne!(d0.0.raw(), d1.0.raw());
module.set_extra_value(module.heap().alloc((d0, d1)));
let module = module.freeze_named(StarlarkTestHeapName::frozen_heap_name())?;
let (d0, d1) =
<(Value, Value)>::unpack_value_err(module.extra_value().unwrap().to_value())
.unwrap();
assert_eq!(d0.0.raw(), d1.0.raw());
crate::Result::Ok(())
})
.unwrap();
}
}