use std::cmp::Ordering;
use std::fmt;
use std::fmt::Debug;
use std::fmt::Display;
use allocative::Allocative;
use dupe::Clone_;
use dupe::Copy_;
use dupe::Dupe;
use dupe::Dupe_;
use either::Either;
use num_bigint::BigInt;
use serde::Serialize;
use serde::Serializer;
use starlark_map::Equivalent;
use crate as starlark;
use crate::any::AnyLifetime;
use crate::any::ProvidesStaticType;
use crate::cast::transmute;
use crate::coerce::coerce;
use crate::coerce::Coerce;
use crate::coerce::CoerceKey;
use crate::collections::Hashed;
use crate::collections::StarlarkHashValue;
use crate::collections::StarlarkHasher;
use crate::docs::DocItem;
use crate::eval::compiler::def::Def;
use crate::eval::compiler::def::FrozenDef;
use crate::eval::runtime::arguments::ArgumentsFull;
use crate::eval::runtime::frame_span::FrameSpan;
use crate::eval::Arguments;
use crate::eval::Evaluator;
use crate::eval::ParametersSpec;
use crate::sealed::Sealed;
use crate::typing::Ty;
use crate::values::bool::VALUE_FALSE_TRUE;
use crate::values::demand::request_value_impl;
use crate::values::dict::FrozenDictRef;
use crate::values::enumeration::EnumType;
use crate::values::enumeration::FrozenEnumValue;
use crate::values::function::FrozenBoundMethod;
use crate::values::function::NativeFunction;
use crate::values::function::FUNCTION_TYPE;
use crate::values::int::PointerI32;
use crate::values::iter::StarlarkIterator;
use crate::values::layout::avalue::AValue;
use crate::values::layout::avalue::StarlarkStrAValue;
use crate::values::layout::heap::repr::AValueHeader;
use crate::values::layout::heap::repr::AValueRepr;
use crate::values::layout::pointer::FrozenPointer;
use crate::values::layout::pointer::Pointer;
use crate::values::layout::pointer::RawPointer;
use crate::values::layout::static_string::VALUE_EMPTY_STRING;
use crate::values::layout::typed::string::StringValueLike;
use crate::values::layout::vtable::AValueDyn;
use crate::values::layout::vtable::AValueDynFull;
use crate::values::layout::vtable::AValueVTable;
use crate::values::none::none_type::VALUE_NONE;
use crate::values::num::value::NumRef;
use crate::values::range::Range;
use crate::values::record::instance::FrozenRecord;
use crate::values::record::record_type::RecordType;
use crate::values::recursive_repr_or_json_guard::json_stack_push;
use crate::values::recursive_repr_or_json_guard::repr_stack_push;
use crate::values::stack_guard;
use crate::values::starlark_type_id::StarlarkTypeId;
use crate::values::string::StarlarkStr;
use crate::values::structs::value::FrozenStruct;
use crate::values::tuple::value::VALUE_EMPTY_TUPLE;
use crate::values::type_repr::StarlarkTypeRepr;
use crate::values::types::inline_int::InlineInt;
use crate::values::types::int_or_big::StarlarkIntRef;
use crate::values::types::list::value::FrozenListData;
use crate::values::types::tuple::value::FrozenTuple;
use crate::values::types::tuple::value::Tuple;
use crate::values::types::unbound::MaybeUnboundValue;
use crate::values::Freeze;
use crate::values::Freezer;
use crate::values::FrozenRef;
use crate::values::FrozenStringValue;
use crate::values::FrozenValueTyped;
use crate::values::Heap;
use crate::values::StarlarkValue;
use crate::values::StringValue;
use crate::values::UnpackValue;
use crate::values::ValueError;
use crate::values::ValueIdentity;
#[derive(Debug, thiserror::Error)]
enum ValueValueError {
#[error("Value is of type `{0}` but `{1}` was expected")]
WrongType(&'static str, &'static str),
}
#[derive(Clone_, Copy_, Dupe_, ProvidesStaticType, Allocative)]
#[allocative(skip)] pub struct Value<'v>(pub(crate) Pointer<'v>);
unsafe impl<'v> Coerce<Value<'v>> for Value<'v> {}
unsafe impl<'v> CoerceKey<Value<'v>> for Value<'v> {}
unsafe impl<'v> Coerce<Value<'v>> for FrozenValue {}
unsafe impl<'v> CoerceKey<Value<'v>> for FrozenValue {}
impl Default for Value<'_> {
fn default() -> Self {
Self::new_none()
}
}
impl Default for FrozenValue {
fn default() -> Self {
Self::new_none()
}
}
impl Display for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match repr_stack_push(*self) {
Ok(_guard) => {
Display::fmt(self.get_ref().as_display(), f)
}
Err(..) => {
let mut recursive = String::new();
self.get_ref().collect_repr_cycle(&mut recursive);
write!(f, "{}", recursive)
}
}
}
}
impl Display for FrozenValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.to_value(), f)
}
}
fn debug_value(typ: &str, v: Value, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple(typ).field(v.get_ref().as_debug()).finish()
}
impl Debug for Value<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
debug_value("Value", *self, f)
}
}
impl Debug for FrozenValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
debug_value("FrozenValue", Value::new_frozen(*self), f)
}
}
impl<'v> PartialEq for Value<'v> {
fn eq(&self, other: &Value<'v>) -> bool {
self.equals(*other).ok() == Some(true)
}
}
impl PartialEq for FrozenValue {
fn eq(&self, other: &FrozenValue) -> bool {
self.to_value().eq(&other.to_value())
}
}
impl Eq for Value<'_> {}
impl Eq for FrozenValue {}
impl Equivalent<FrozenValue> for Value<'_> {
fn equivalent(&self, key: &FrozenValue) -> bool {
key.equals(*self).unwrap()
}
}
impl Equivalent<Value<'_>> for FrozenValue {
fn equivalent(&self, key: &Value) -> bool {
self.equals(*key).unwrap()
}
}
#[derive(Clone, Copy, Dupe, ProvidesStaticType, Allocative)]
pub struct FrozenValue(
#[allocative(skip)] pub(crate) FrozenPointer<'static>,
);
unsafe impl Send for FrozenValue {}
unsafe impl Sync for FrozenValue {}
impl<'v> Value<'v> {
#[inline]
pub(crate) fn new_ptr(x: &'v AValueHeader, is_str: bool) -> Self {
Self(Pointer::new_unfrozen(x, is_str))
}
#[inline]
pub(crate) fn new_ptr_query_is_str(x: &'v AValueHeader) -> Self {
let is_string = x.0.is_str;
Self::new_ptr(x, is_string)
}
#[inline]
pub(crate) fn new_repr<T: AValue<'v>>(x: &'v AValueRepr<T>) -> Self {
Self::new_ptr(&x.header, T::IS_STR)
}
#[inline]
pub(crate) unsafe fn new_ptr_usize_with_str_tag(x: usize) -> Self {
Self(Pointer::new_unfrozen_usize_with_str_tag(x))
}
#[inline]
pub(crate) unsafe fn cast_lifetime<'w>(self) -> Value<'w> {
Value(self.0.cast_lifetime())
}
#[inline]
pub fn new_none() -> Self {
FrozenValue::new_none().to_value()
}
#[inline]
pub fn new_bool(x: bool) -> Self {
FrozenValue::new_bool(x).to_value()
}
#[inline]
pub(crate) fn new_int(x: InlineInt) -> Self {
FrozenValue::new_int(x).to_value()
}
#[cfg(test)]
pub(crate) fn testing_new_int(x: i32) -> Self {
FrozenValue::testing_new_int(x).to_value()
}
#[inline]
pub(crate) fn new_empty_string() -> Self {
FrozenValue::new_empty_string().to_value()
}
#[inline]
pub(crate) fn new_empty_tuple() -> Self {
FrozenValue::new_empty_tuple().to_value()
}
#[inline]
pub fn new_frozen(x: FrozenValue) -> Self {
Self(x.0.to_pointer())
}
#[inline]
pub fn unpack_frozen(self) -> Option<FrozenValue> {
if self.0.is_unfrozen() {
None
} else {
unsafe { Some(self.unpack_frozen_unchecked()) }
}
}
#[inline]
unsafe fn unpack_frozen_unchecked(self) -> FrozenValue {
debug_assert!(!self.0.is_unfrozen());
FrozenValue(self.0.cast_lifetime().to_frozen_pointer_unchecked())
}
#[inline]
pub fn is_none(self) -> bool {
self.ptr_eq(Value::new_none())
}
pub(crate) fn unpack_num(self) -> Option<NumRef<'v>> {
NumRef::unpack_value(self)
}
pub(crate) fn unpack_integer<I>(self) -> Option<I>
where
I: TryFrom<i32>,
I: TryFrom<&'v BigInt>,
{
match self.unpack_num()? {
NumRef::Float(_) => None,
NumRef::Int(StarlarkIntRef::Small(x)) => I::try_from(x.to_i32()).ok(),
NumRef::Int(StarlarkIntRef::Big(x)) => x.unpack_integer(),
}
}
#[inline]
pub fn unpack_bool(self) -> Option<bool> {
if self.ptr_eq(Value::new_bool(true)) {
Some(true)
} else if self.ptr_eq(Value::new_bool(false)) {
Some(false)
} else {
None
}
}
#[inline]
pub fn unpack_i32(self) -> Option<i32> {
i32::unpack_value(self)
}
#[inline]
pub(crate) fn unpack_inline_int(self) -> Option<InlineInt> {
self.0.unpack_int()
}
#[inline]
pub(crate) fn unpack_int_value(self) -> Option<FrozenValueTyped<'static, PointerI32>> {
if self.unpack_inline_int().is_some() {
unsafe {
Some(FrozenValueTyped::new_unchecked(
self.unpack_frozen_unchecked(),
))
}
} else {
None
}
}
#[inline]
pub(crate) fn is_str(self) -> bool {
self.0.is_str()
}
#[inline]
pub fn unpack_starlark_str(self) -> Option<&'v StarlarkStr> {
if self.is_str() {
unsafe {
Some(
&self
.0
.unpack_ptr_no_int_unchecked()
.unpack_header_unchecked()
.as_repr::<StarlarkStrAValue>()
.payload
.1,
)
}
} else {
None
}
}
#[inline]
pub fn unpack_str(self) -> Option<&'v str> {
self.unpack_starlark_str().map(|s| s.as_str())
}
#[inline]
pub(crate) fn get_ref(self) -> AValueDyn<'v> {
unsafe {
match self.0.unpack() {
Either::Left(x) => x.unpack_header_unchecked().unpack(),
Either::Right(x) => x.as_avalue_dyn(),
}
}
}
#[inline]
fn get_ref_full(self) -> AValueDynFull<'v> {
unsafe { AValueDynFull::new(self.get_ref(), self) }
}
#[inline]
pub(crate) fn vtable(self) -> &'static AValueVTable {
unsafe {
match self.0.unpack() {
Either::Left(x) => x.unpack_header_unchecked().0,
Either::Right(_) => PointerI32::vtable(),
}
}
}
#[inline]
pub(crate) unsafe fn downcast_ref_unchecked<T: StarlarkValue<'v>>(self) -> &'v T {
debug_assert!(self.get_ref().downcast_ref::<T>().is_some());
if PointerI32::type_is_pointer_i32::<T>() {
transmute!(&PointerI32, &T, self.0.unpack_pointer_i32_unchecked())
} else {
self.0
.unpack_ptr_no_int_unchecked()
.unpack_header_unchecked()
.payload()
}
}
pub(crate) fn get_hash(self) -> crate::Result<StarlarkHashValue> {
self.get_ref().get_hash()
}
#[inline]
pub fn ptr_eq(self, other: Value) -> bool {
self.0.ptr_eq(other.0)
}
#[inline]
pub fn identity(self) -> ValueIdentity<'v> {
ValueIdentity::new(self)
}
#[inline]
pub(crate) fn ptr_value(self) -> RawPointer {
self.0.raw()
}
pub fn get_type(self) -> &'static str {
self.vtable().type_name
}
pub fn to_bool(self) -> bool {
if let Some(x) = self.unpack_bool() {
x
} else {
self.get_ref().to_bool()
}
}
pub(crate) fn to_int(self) -> crate::Result<i32> {
if let Some(x) = self.unpack_i32() {
Ok(x)
} else if let Some(x) = self.unpack_bool() {
Ok(x as i32)
} else if let Some(NumRef::Int(_)) = self.unpack_num() {
Err(ValueError::IntegerOverflow.into())
} else {
ValueError::unsupported_owned(self.get_type(), "int()", None)
}
}
pub fn at(self, index: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().at(index, heap)
}
pub fn slice(
self,
start: Option<Value<'v>>,
stop: Option<Value<'v>>,
stride: Option<Value<'v>>,
heap: &'v Heap,
) -> crate::Result<Value<'v>> {
self.get_ref().slice(start, stop, stride, heap)
}
pub fn length(self) -> crate::Result<i32> {
self.get_ref().length()
}
pub fn is_in(self, other: Value<'v>) -> crate::Result<bool> {
self.get_ref().is_in(other)
}
pub fn plus(self, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().plus(heap)
}
pub fn minus(self, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().minus(heap)
}
pub fn sub(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().sub(other, heap)
}
pub fn mul(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
if let Some(r) = self.get_ref().mul(other, heap) {
r
} else if let Some(r) = other.get_ref().rmul(self, heap) {
r
} else {
ValueError::unsupported_owned(self.get_type(), "*", Some(other.get_type()))
}
}
pub fn percent(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().percent(other, heap)
}
pub fn div(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().div(other, heap)
}
pub fn floor_div(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().floor_div(other, heap)
}
pub fn bit_and(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().bit_and(other, heap)
}
pub fn bit_or(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().bit_or(other, heap)
}
pub fn bit_xor(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().bit_xor(other, heap)
}
pub fn bit_not(self, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().bit_not(heap)
}
pub fn left_shift(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().left_shift(other, heap)
}
pub fn right_shift(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
self.get_ref().right_shift(other, heap)
}
pub(crate) fn invoke_with_loc(
self,
location: Option<FrozenRef<'static, FrameSpan>>,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> crate::Result<Value<'v>> {
eval.with_call_stack(self, location, |eval| {
self.get_ref_full().invoke(args, eval)
})
}
pub fn parameters_spec(self) -> Option<&'v ParametersSpec<Value<'v>>> {
if let Some(def) = self.downcast_ref::<Def>() {
Some(&def.parameters)
} else if let Some(def) = self.downcast_ref::<FrozenDef>() {
Some(coerce(&def.parameters))
} else {
None
}
}
pub(crate) fn invoke(
self,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> crate::Result<Value<'v>> {
self.invoke_with_loc(None, args, eval)
}
pub(crate) fn invoke_pos(
self,
pos: &[Value<'v>],
eval: &mut Evaluator<'v, '_>,
) -> crate::Result<Value<'v>> {
let params = Arguments(ArgumentsFull {
pos,
..ArgumentsFull::default()
});
self.invoke(¶ms, eval)
}
pub fn get_type_value(self) -> FrozenStringValue {
self.vtable().type_value()
}
#[inline]
pub(crate) fn starlark_type_id(self) -> StarlarkTypeId {
self.vtable().starlark_type_id
}
pub(crate) fn get_type_starlark_repr(self) -> Ty {
self.vtable().type_starlark_repr()
}
pub fn add(self, other: Value<'v>, heap: &'v Heap) -> crate::Result<Value<'v>> {
if let Some(ls) = self.unpack_inline_int() {
if let Some(rs) = other.unpack_inline_int() {
if let Some(sum) = ls.checked_add(rs) {
return Ok(heap.alloc(sum));
}
}
}
if let Some(ls) = self.unpack_str() {
if let Some(rs) = other.unpack_str() {
if ls.is_empty() {
return Ok(other);
} else if rs.is_empty() {
return Ok(self);
} else {
return Ok(heap.alloc_str_concat(ls, rs).to_value());
}
}
}
if let Some(v) = self.get_ref().add(other, heap) {
v
} else if let Some(v) = other.get_ref().radd(self, heap) {
v
} else {
ValueError::unsupported_owned(self.get_type(), "+", Some(other.get_type()))
}
}
pub fn freeze(self, freezer: &Freezer) -> anyhow::Result<FrozenValue> {
freezer.freeze(self)
}
pub fn to_str(self) -> String {
match self.unpack_str() {
None => self.to_repr(),
Some(s) => s.to_owned(),
}
}
pub fn to_repr(self) -> String {
let mut s = String::new();
self.collect_repr(&mut s);
s
}
pub(crate) fn name_for_call_stack(self) -> String {
self.get_ref().name_for_call_stack(self)
}
pub fn to_json(self) -> anyhow::Result<String> {
serde_json::to_string(&self).map_err(|e| anyhow::anyhow!(e))
}
pub fn to_json_value(self) -> anyhow::Result<serde_json::Value> {
serde_json::to_value(self).map_err(|e| anyhow::anyhow!(e))
}
pub fn set_attr(self, attribute: &str, alloc_value: Value<'v>) -> crate::Result<()> {
self.get_ref().set_attr(attribute, alloc_value)
}
pub fn set_at(self, index: Value<'v>, alloc_value: Value<'v>) -> crate::Result<()> {
self.get_ref().set_at(index, alloc_value)
}
pub fn documentation(self) -> Option<DocItem> {
self.get_ref().documentation()
}
#[inline]
pub fn iterate(self, heap: &'v Heap) -> crate::Result<StarlarkIterator<'v>> {
let iter = self.get_ref().iterate(self, heap)?;
Ok(StarlarkIterator::new(iter, heap))
}
#[inline]
pub fn get_hashed(self) -> crate::Result<Hashed<Self>> {
ValueLike::get_hashed(self)
}
#[inline]
pub fn equals(self, other: Value<'v>) -> crate::Result<bool> {
if self.ptr_eq(other) {
Ok(true)
} else {
self.equals_not_ptr_eq(other)
}
}
#[inline]
fn equals_not_ptr_eq(self, other: Value<'v>) -> crate::Result<bool> {
let _guard = stack_guard::stack_guard()?;
self.get_ref().equals(other)
}
#[inline]
pub fn compare(self, other: Value<'v>) -> crate::Result<Ordering> {
ValueLike::compare(self, other)
}
pub fn describe(self, name: &str) -> String {
if self.get_type() == FUNCTION_TYPE {
format!("def {}: pass", self.to_repr().replace(" = ...", " = None"))
} else {
format!("# {} = {}", name, self.to_repr())
}
}
pub fn export_as(self, variable_name: &str, eval: &mut Evaluator<'v, '_>) -> crate::Result<()> {
self.get_ref().export_as(variable_name, eval)
}
pub fn get_attr(self, attribute: &str, heap: &'v Heap) -> crate::Result<Option<Value<'v>>> {
let aref = self.get_ref();
if let Some(methods) = aref.vtable().methods() {
let attribute = Hashed::new(attribute);
if let Some(v) = methods.get_hashed(attribute) {
return Ok(Some(MaybeUnboundValue::new(v).bind(self, heap)?));
}
Ok(aref.get_attr_hashed(attribute, heap))
} else {
Ok(aref.get_attr(attribute, heap))
}
}
pub fn get_attr_error(self, attribute: &str, heap: &'v Heap) -> crate::Result<Value<'v>> {
match self.get_attr(attribute, heap)? {
None => {
ValueError::unsupported_owned(self.get_type(), &format!(".{}", attribute), None)
}
Some(x) => Ok(x),
}
}
pub fn has_attr(self, attribute: &str, heap: &'v Heap) -> bool {
let aref = self.get_ref();
if let Some(methods) = aref.vtable().methods() {
if methods.get(attribute).is_some() {
return true;
}
}
aref.has_attr(attribute, heap)
}
pub fn dir_attr(self) -> Vec<String> {
let aref = self.get_ref();
let mut result = if let Some(methods) = aref.vtable().methods() {
let mut res = methods.names();
res.extend(aref.dir_attr());
res
} else {
aref.dir_attr()
};
result.sort();
result
}
pub fn request_value<T: AnyLifetime<'v>>(self) -> Option<T> {
request_value_impl(self)
}
}
impl FrozenValue {
#[inline]
pub(crate) fn new_ptr(x: &'static AValueHeader, is_str: bool) -> Self {
Self(FrozenPointer::new_frozen(x, is_str))
}
#[inline]
pub(crate) fn new_ptr_query_is_str(x: &'static AValueHeader) -> Self {
let is_string = x.0.is_str;
Self::new_ptr(x, is_string)
}
#[inline]
pub(crate) fn new_repr<'a, T: AValue<'a>>(x: &'static AValueRepr<T>) -> Self {
Self::new_ptr(&x.header, T::IS_STR)
}
#[inline]
pub(crate) fn new_ptr_usize_with_str_tag(x: usize) -> Self {
Self(FrozenPointer::new_frozen_usize_with_str_tag(x))
}
#[inline]
pub fn new_none() -> Self {
Self::new_repr(&VALUE_NONE)
}
#[inline]
pub fn new_bool(x: bool) -> Self {
Self::new_repr(&VALUE_FALSE_TRUE[x as usize])
}
#[inline]
pub(crate) fn new_int(x: InlineInt) -> Self {
Self(FrozenPointer::new_int(x))
}
#[cfg(test)]
pub(crate) fn testing_new_int(x: i32) -> Self {
Self::new_int(InlineInt::try_from(x).ok().unwrap())
}
#[inline]
pub(crate) fn new_empty_string() -> Self {
VALUE_EMPTY_STRING.unpack()
}
#[inline]
pub(crate) fn new_empty_tuple() -> Self {
FrozenValue::new_repr(&VALUE_EMPTY_TUPLE)
}
#[inline]
pub(crate) fn ptr_value(self) -> RawPointer {
self.0.raw()
}
#[inline]
pub fn is_none(self) -> bool {
self.to_value().is_none()
}
#[inline]
pub fn unpack_bool(self) -> Option<bool> {
self.to_value().unpack_bool()
}
#[inline]
pub fn unpack_i32(self) -> Option<i32> {
self.to_value().unpack_i32()
}
#[inline]
pub(crate) fn unpack_inline_int(self) -> Option<InlineInt> {
self.to_value().unpack_inline_int()
}
#[inline]
pub(crate) fn is_str(self) -> bool {
self.to_value().is_str()
}
#[allow(clippy::trivially_copy_pass_by_ref)]
#[inline]
pub(crate) fn unpack_str<'v>(&'v self) -> Option<&'v str> {
self.to_value().unpack_str()
}
#[inline]
pub fn to_value<'v>(self) -> Value<'v> {
Value::new_frozen(self)
}
pub(crate) fn is_builtin(self) -> bool {
self.is_none()
|| self.is_str()
|| self.unpack_bool().is_some()
|| NumRef::unpack_value(self.to_value()).is_some()
|| FrozenListData::from_frozen_value(&self).is_some()
|| FrozenDictRef::from_frozen_value(self).is_some()
|| FrozenValueTyped::<FrozenTuple>::new(self).is_some()
|| FrozenValueTyped::<Range>::new(self).is_some()
|| FrozenValueTyped::<FrozenDef>::new(self).is_some()
|| FrozenValueTyped::<NativeFunction>::new(self).is_some()
|| FrozenValueTyped::<FrozenStruct>::new(self).is_some()
|| FrozenValueTyped::<RecordType>::new(self).is_some()
|| FrozenValueTyped::<FrozenRecord>::new(self).is_some()
|| FrozenValueTyped::<EnumType>::new(self).is_some()
|| FrozenValueTyped::<FrozenEnumValue>::new(self).is_some()
}
pub(crate) fn speculative_exec_safe(self) -> bool {
if let Some(v) = FrozenValueTyped::<NativeFunction>::new(self) {
v.speculative_exec_safe
} else if let Some(v) = FrozenValueTyped::<FrozenBoundMethod>::new(self) {
v.method.speculative_exec_safe
} else {
false
}
}
pub(crate) fn eq_is_ptr_eq(self) -> bool {
self.is_none()
|| self.unpack_bool().is_some()
|| matches!(self.unpack_str(), Some(s) if s.len() <= 1)
|| matches!(Tuple::from_value(self.to_value()), Some(t) if t.len() == 0)
}
#[inline]
pub fn downcast_frozen_ref<T: StarlarkValue<'static>>(self) -> Option<FrozenRef<'static, T>> {
self.downcast_ref::<T>().map(|value| FrozenRef { value })
}
#[inline]
pub fn downcast_frozen_str(self) -> Option<FrozenRef<'static, str>> {
self.to_value()
.unpack_str()
.map(|value| FrozenRef { value })
}
#[inline]
pub fn downcast_frozen_starlark_str(self) -> Option<FrozenRef<'static, StarlarkStr>> {
self.to_value()
.unpack_starlark_str()
.map(|value| FrozenRef { value })
}
}
impl<'v> Serialize for Value<'v> {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match json_stack_push(*self) {
Ok(_guard) => erased_serde::serialize(self.get_ref().as_serialize(), s),
Err(..) => Err(serde::ser::Error::custom(ToJsonCycleError(self.get_type()))),
}
}
}
impl Serialize for FrozenValue {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_value().serialize(s)
}
}
impl<'v> StarlarkTypeRepr for Value<'v> {
fn starlark_type_repr() -> Ty {
FrozenValue::starlark_type_repr()
}
}
impl StarlarkTypeRepr for FrozenValue {
fn starlark_type_repr() -> Ty {
Ty::any()
}
}
pub trait ValueLike<'v>:
Eq
+ Copy
+ Debug
+ Default
+ Display
+ Serialize
+ CoerceKey<Value<'v>>
+ Freeze<Frozen = FrozenValue>
+ Allocative
+ ProvidesStaticType<'v>
+ Sealed
+ 'v
{
type String: StringValueLike<'v>;
fn to_value(self) -> Value<'v>;
fn from_frozen_value(v: FrozenValue) -> Self;
fn invoke(
self,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> crate::Result<Value<'v>> {
self.to_value().invoke(args, eval)
}
fn write_hash(self, hasher: &mut StarlarkHasher) -> crate::Result<()>;
fn get_hashed(self) -> crate::Result<Hashed<Self>> {
let hash = if let Some(s) = self.to_value().unpack_starlark_str() {
s.get_hash()
} else {
self.to_value().get_hash()?
};
Ok(Hashed::new_unchecked(hash, self))
}
fn collect_repr(self, collector: &mut String);
fn collect_str(self, collector: &mut String) {
if let Some(s) = self.to_value().unpack_str() {
collector.push_str(s);
} else {
self.collect_repr(collector);
}
}
fn equals(self, other: Value<'v>) -> crate::Result<bool>;
fn compare(self, other: Value<'v>) -> crate::Result<Ordering>;
fn downcast_ref<T: StarlarkValue<'v>>(self) -> Option<&'v T>;
fn downcast_ref_err<T: StarlarkValue<'v>>(self) -> anyhow::Result<&'v T> {
match self.downcast_ref() {
Some(v) => Ok(v),
None => Err(ValueValueError::WrongType(self.to_value().get_type(), T::TYPE).into()),
}
}
}
#[derive(Debug, thiserror::Error)]
#[error("Cycle detected when serializing value of type `{0}` to JSON")]
struct ToJsonCycleError(&'static str);
impl<'v> Sealed for Value<'v> {}
impl<'v> ValueLike<'v> for Value<'v> {
type String = StringValue<'v>;
#[inline]
fn to_value(self) -> Value<'v> {
self
}
#[inline]
fn from_frozen_value(v: FrozenValue) -> Self {
v.to_value()
}
fn downcast_ref<T: StarlarkValue<'v>>(self) -> Option<&'v T> {
if T::static_type_id() == StarlarkStr::static_type_id() {
if self.is_str() {
Some(unsafe { self.downcast_ref_unchecked() })
} else {
None
}
} else if PointerI32::type_is_pointer_i32::<T>() {
if self.unpack_inline_int().is_some() {
Some(unsafe { self.downcast_ref_unchecked() })
} else {
None
}
} else {
self.get_ref().downcast_ref::<T>()
}
}
fn collect_repr(self, collector: &mut String) {
match repr_stack_push(self) {
Ok(_guard) => {
self.get_ref().collect_repr(collector);
}
Err(..) => {
self.get_ref().collect_repr_cycle(collector);
}
}
}
fn write_hash(self, hasher: &mut StarlarkHasher) -> crate::Result<()> {
self.get_ref().write_hash(hasher)
}
#[inline]
fn equals(self, other: Value<'v>) -> crate::Result<bool> {
self.equals(other)
}
fn compare(self, other: Value<'v>) -> crate::Result<Ordering> {
let _guard = stack_guard::stack_guard()?;
self.get_ref().compare(other)
}
}
impl Sealed for FrozenValue {}
impl<'v> ValueLike<'v> for FrozenValue {
type String = FrozenStringValue;
#[inline]
fn to_value(self) -> Value<'v> {
Value::new_frozen(self)
}
#[inline]
fn from_frozen_value(v: FrozenValue) -> Self {
v
}
#[inline]
fn downcast_ref<T: StarlarkValue<'v>>(self) -> Option<&'v T> {
self.to_value().downcast_ref()
}
#[inline]
fn collect_repr(self, collector: &mut String) {
self.to_value().collect_repr(collector)
}
#[inline]
fn write_hash(self, hasher: &mut StarlarkHasher) -> crate::Result<()> {
self.to_value().write_hash(hasher)
}
#[inline]
fn equals(self, other: Value<'v>) -> crate::Result<bool> {
self.to_value().equals(other)
}
#[inline]
fn compare(self, other: Value<'v>) -> crate::Result<Ordering> {
self.to_value().compare(other)
}
}
fn _test_send_sync()
where
FrozenValue: Send + Sync,
{
}
#[cfg(test)]
mod tests {
use num_bigint::BigInt;
use crate::assert;
use crate::values::none::NoneType;
use crate::values::string::StarlarkStr;
use crate::values::types::int::PointerI32;
use crate::values::unpack::UnpackValue;
use crate::values::Heap;
use crate::values::Value;
use crate::values::ValueLike;
#[test]
fn test_downcast_ref() {
let heap = Heap::new();
let string = heap.alloc_str("asd").to_value();
let none = Value::new_none();
let integer = Value::testing_new_int(17);
assert!(string.downcast_ref::<NoneType>().is_none());
assert!(integer.downcast_ref::<NoneType>().is_none());
assert!(none.downcast_ref::<NoneType>().is_some());
assert_eq!(
"asd",
string.downcast_ref::<StarlarkStr>().unwrap().as_str()
);
assert!(integer.downcast_ref::<StarlarkStr>().is_none());
assert!(none.downcast_ref::<StarlarkStr>().is_none());
assert!(string.downcast_ref::<PointerI32>().is_none());
assert_eq!(17, integer.downcast_ref::<PointerI32>().unwrap().get());
assert!(none.downcast_ref::<PointerI32>().is_none());
}
#[test]
fn test_unpack_i32() {
let heap = Heap::new();
let value = heap.alloc(i32::MAX);
assert_eq!(Some(i32::MAX), value.unpack_i32());
}
#[test]
fn test_unpack_bigint() {
let heap = Heap::new();
let value = heap.alloc(BigInt::from(i64::MAX));
assert_eq!(None, value.unpack_i32());
assert_eq!(Some(BigInt::from(i64::MAX)), BigInt::unpack_value(value));
}
#[test]
fn test_to_json_value() {
let value = assert::pass("{'a': 10}");
assert_eq!(
serde_json::json!({"a": 10}),
value.value().to_json_value().unwrap()
);
}
}