use std::{
borrow::Cow,
cmp::Ordering,
fmt,
fmt::{Debug, Display},
};
use either::Either;
use gazebo::{
any::AnyLifetime,
cast,
coerce::{Coerce, CoerceKey},
prelude::*,
};
use indexmap::Equivalent;
use num_bigint::BigInt;
use serde::{Serialize, Serializer};
use crate::{
collections::{Hashed, StarlarkHashValue, StarlarkHasher},
eval::{compiler::def::FrozenDef, runtime::call_stack::FrozenFileSpan, Arguments, Evaluator},
values::{
dict::FrozenDict,
docs::DocItem,
enumeration::{EnumType, FrozenEnumValue},
float::StarlarkFloat,
function::{FrozenBoundMethod, NativeFunction, FUNCTION_TYPE},
int::PointerI32,
layout::{
arena::{AValueHeader, AValueRepr},
avalue::{
basic_ref, AValue, AValueDyn, StarlarkStrAValue, VALUE_FALSE, VALUE_NONE,
VALUE_TRUE,
},
pointer::{FrozenPointer, Pointer},
static_string::VALUE_EMPTY_STRING,
typed::string::StringValueLike,
},
list::FrozenList,
num::Num,
range::Range,
record::{FrozenRecord, RecordType},
recursive_repr_or_json_guard::{json_stack_push, repr_stack_push},
stack_guard,
string::StarlarkStr,
structs::FrozenStruct,
tuple::FrozenTuple,
types::unbound::MaybeUnboundValue,
Freeze, Freezer, FrozenRef, FrozenStringValue, FrozenValueTyped, Heap, StarlarkValue,
StringValue, UnpackValue, ValueError, ValueIdentity,
},
};
#[derive(Clone_, Copy_, Dupe_, AnyLifetime)]
pub struct Value<'v>(pub(crate) Pointer<'v, AValueHeader>);
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) => {
self.get_ref().as_display().fmt(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, AnyLifetime)]
pub struct FrozenValue(pub(crate) FrozenPointer<'static, AValueHeader>);
unsafe impl Send for FrozenValue {}
unsafe impl Sync for FrozenValue {}
impl<'v> Value<'v> {
pub(crate) fn new_ptr(x: &'v AValueHeader, is_str: bool) -> Self {
Self(Pointer::new_unfrozen(x, is_str))
}
pub(crate) fn new_ptr_query_is_str(x: &'v AValueHeader) -> Self {
let is_string = x.unpack().is_str();
Self::new_ptr(x, is_string)
}
pub(crate) fn new_repr<T: AValue<'v>>(x: &'v AValueRepr<T>) -> Self {
Self::new_ptr(&x.header, T::IS_STR)
}
pub(crate) fn new_ptr_usize_with_str_tag(x: usize) -> Self {
Self(Pointer::new_unfrozen_usize_with_str_tag(x))
}
pub fn new_none() -> Self {
FrozenValue::new_none().to_value()
}
pub fn new_bool(x: bool) -> Self {
FrozenValue::new_bool(x).to_value()
}
pub fn new_int(x: i32) -> Self {
FrozenValue::new_int(x).to_value()
}
pub(crate) fn new_empty_string() -> Self {
FrozenValue::new_empty_string().to_value()
}
pub fn new_frozen(x: FrozenValue) -> Self {
Self(x.0.to_pointer())
}
pub fn unpack_frozen(self) -> Option<FrozenValue> {
if self.0.is_unfrozen() {
None
} else {
Some(FrozenValue(unsafe {
self.0.cast_lifetime().to_frozen_pointer()
}))
}
}
pub fn is_none(self) -> bool {
self.0.ptr_value() == cast::ptr_to_usize(&VALUE_NONE)
}
pub fn unpack_num(self) -> Option<Num<'v>> {
Num::unpack_value(self)
}
pub(crate) fn unpack_int_or_big(self) -> Option<Cow<'v, BigInt>> {
match self.unpack_num()? {
Num::Float(_) => None,
Num::Int(x) => Some(Cow::Owned(BigInt::from(x))),
Num::BigInt(x) => Some(Cow::Borrowed(x.get())),
}
}
pub fn unpack_bool(self) -> Option<bool> {
let p = self.0.ptr_value();
if p == cast::ptr_to_usize(&VALUE_TRUE) {
Some(true)
} else if p == cast::ptr_to_usize(&VALUE_FALSE) {
Some(false)
} else {
None
}
}
pub fn unpack_int(self) -> Option<i32> {
self.0.unpack_int()
}
pub(crate) fn is_str(self) -> bool {
self.0.is_str()
}
pub fn unpack_starlark_str(self) -> Option<&'v StarlarkStr> {
if self.is_str() {
unsafe {
Some(
&self
.0
.unpack_ptr_no_int_unchecked()
.as_repr::<StarlarkStrAValue>()
.payload
.1,
)
}
} else {
None
}
}
pub fn unpack_str(self) -> Option<&'v str> {
self.unpack_starlark_str().map(|s| s.as_str())
}
pub(crate) fn get_ref(self) -> &'v dyn AValueDyn<'v> {
match self.0.unpack() {
Either::Left(x) => x.unpack(),
Either::Right(x) => basic_ref(x),
}
}
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,
PointerI32::new(self.0.unpack_int_unchecked())
)
} else {
self.0.unpack_ptr_no_int_unchecked().payload()
}
}
pub(crate) fn get_hash(self) -> anyhow::Result<StarlarkHashValue> {
self.get_ref().get_hash()
}
pub fn ptr_eq(self, other: Value) -> bool {
self.0.ptr_eq(other.0)
}
pub fn identity(self) -> ValueIdentity<'v> {
ValueIdentity::new(self)
}
pub(crate) fn ptr_value(self) -> usize {
self.0.ptr_value()
}
pub fn get_type(self) -> &'static str {
self.get_ref().get_type()
}
pub fn to_bool(self) -> bool {
if let Some(x) = self.unpack_bool() {
x
} else {
self.get_ref().to_bool()
}
}
pub fn to_int(self) -> anyhow::Result<i32> {
if let Some(x) = self.unpack_int() {
Ok(x)
} else {
self.get_ref().to_int()
}
}
pub fn at(self, index: Value<'v>, heap: &'v Heap) -> anyhow::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,
) -> anyhow::Result<Value<'v>> {
self.get_ref().slice(start, stop, stride, heap)
}
pub fn length(self) -> anyhow::Result<i32> {
self.get_ref().length()
}
pub fn is_in(self, other: Value<'v>) -> anyhow::Result<bool> {
self.get_ref().is_in(other)
}
pub fn plus(self, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().plus(heap)
}
pub fn minus(self, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().minus(heap)
}
pub fn sub(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().sub(other, heap)
}
pub fn mul(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().mul(other, heap)
}
pub fn percent(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().percent(other, heap)
}
pub fn div(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().div(other, heap)
}
pub fn floor_div(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().floor_div(other, heap)
}
pub fn bit_and(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().bit_and(other, heap)
}
pub fn bit_or(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().bit_or(other, heap)
}
pub fn bit_xor(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().bit_xor(other, heap)
}
pub fn bit_not(self, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().bit_not(heap)
}
pub fn left_shift(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().left_shift(other, heap)
}
pub fn right_shift(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
self.get_ref().right_shift(other, heap)
}
pub(crate) fn invoke_with_loc(
self,
location: Option<FrozenRef<'static, FrozenFileSpan>>,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> anyhow::Result<Value<'v>> {
eval.with_call_stack(self, location, |eval| {
self.get_ref().invoke(self, args, eval)
})
}
pub fn invoke(
self,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> anyhow::Result<Value<'v>> {
self.invoke_with_loc(None, args, eval)
}
pub(crate) fn invoke_method(
self,
this: Value<'v>,
location: FrozenRef<'static, FrozenFileSpan>,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> anyhow::Result<Value<'v>> {
eval.with_call_stack(self, Some(location), |eval| {
self.get_ref().invoke_method(self, this, args, eval)
})
}
pub fn invoke_pos(
self,
pos: &[Value<'v>],
eval: &mut Evaluator<'v, '_>,
) -> anyhow::Result<Value<'v>> {
let params = Arguments {
pos,
..Arguments::default()
};
self.invoke(¶ms, eval)
}
pub fn get_type_value(self) -> FrozenStringValue {
self.get_ref().get_type_value()
}
pub fn add(self, other: Value<'v>, heap: &'v Heap) -> anyhow::Result<Value<'v>> {
if let Some(ls) = self.unpack_int() {
if let Some(rs) = other.unpack_int() {
if let Some(sum) = ls.checked_add(rs) {
return Ok(Value::new_int(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 set_attr(self, attribute: &str, alloc_value: Value<'v>) -> anyhow::Result<()> {
self.get_ref().set_attr(attribute, alloc_value)
}
pub fn set_at(self, index: Value<'v>, alloc_value: Value<'v>) -> anyhow::Result<()> {
self.get_ref().set_at(index, alloc_value)
}
pub fn documentation(self) -> Option<DocItem> {
self.get_ref().documentation()
}
pub fn iterate_collect(self, heap: &'v Heap) -> anyhow::Result<Vec<Value<'v>>> {
self.with_iterator(heap, |it| it.collect())
}
pub fn with_iterator<T>(
self,
heap: &'v Heap,
mut f: impl FnMut(&mut dyn Iterator<Item = Value<'v>>) -> T,
) -> anyhow::Result<T> {
let mut res = None;
self.get_ref().with_iterator(heap, &mut |it| {
res = Some(f(it));
Ok(())
})?;
Ok(res.take().expect("with_iterator to call the callback"))
}
pub fn iterate(
self,
heap: &'v Heap,
) -> anyhow::Result<Box<dyn Iterator<Item = Value<'v>> + 'v>> {
self.get_ref().iterate(heap)
}
pub fn get_hashed(self) -> anyhow::Result<Hashed<Self>> {
ValueLike::get_hashed(self)
}
pub fn equals(self, other: Value<'v>) -> anyhow::Result<bool> {
ValueLike::equals(self, other)
}
pub fn compare(self, other: Value<'v>) -> anyhow::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, '_>) {
self.get_ref().export_as(variable_name, eval)
}
pub fn get_attr(self, attribute: &str, heap: &'v Heap) -> anyhow::Result<Option<Value<'v>>> {
let aref = self.get_ref();
if let Some(methods) = aref.get_methods() {
if let Some(v) = methods.get_frozen(attribute) {
return Ok(Some(MaybeUnboundValue::new(v).bind(self, heap)?));
}
}
Ok(aref.get_attr(attribute, heap))
}
pub fn get_attr_error(self, attribute: &str, heap: &'v Heap) -> anyhow::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) -> bool {
let aref = self.get_ref();
if let Some(methods) = aref.get_methods() {
if methods.get(attribute).is_some() {
return true;
}
}
aref.has_attr(attribute)
}
pub fn dir_attr(self) -> Vec<String> {
let aref = self.get_ref();
let mut result = if let Some(methods) = aref.get_methods() {
let mut res = methods.names();
res.extend(aref.dir_attr());
res
} else {
aref.dir_attr()
};
result.sort();
result
}
}
impl FrozenValue {
pub(crate) fn new_ptr(x: &'static AValueHeader, is_str: bool) -> Self {
Self(FrozenPointer::new_frozen(x, is_str))
}
pub(crate) fn new_repr<'a, T: AValue<'a>>(x: &'static AValueRepr<T>) -> Self {
Self::new_ptr(&x.header, T::IS_STR)
}
pub(crate) fn new_ptr_usize_with_str_tag(x: usize) -> Self {
Self(FrozenPointer::new_frozen_usize_with_str_tag(x))
}
pub(crate) fn new_ptr_value(x: usize) -> Self {
unsafe { Self(FrozenPointer::new(x)) }
}
pub fn new_none() -> Self {
Self::new_repr(&VALUE_NONE)
}
pub fn new_bool(x: bool) -> Self {
if x {
Self::new_repr(&VALUE_TRUE)
} else {
Self::new_repr(&VALUE_FALSE)
}
}
pub fn new_int(x: i32) -> Self {
Self(FrozenPointer::new_int(x))
}
pub(crate) fn new_empty_string() -> Self {
VALUE_EMPTY_STRING.unpack()
}
pub(crate) fn ptr_value(self) -> usize {
self.0.ptr_value()
}
pub fn is_none(self) -> bool {
self.0.ptr_value() == cast::ptr_to_usize(&VALUE_NONE)
}
pub fn unpack_bool(self) -> Option<bool> {
let p = self.0.ptr_value();
if p == cast::ptr_to_usize(&VALUE_TRUE) {
Some(true)
} else if p == cast::ptr_to_usize(&VALUE_FALSE) {
Some(false)
} else {
None
}
}
pub fn unpack_int(self) -> Option<i32> {
self.0.unpack_int()
}
pub(crate) unsafe fn unpack_int_unchecked(self) -> i32 {
self.0.unpack_int_unchecked()
}
pub(crate) fn is_str(self) -> bool {
self.to_value().is_str()
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub(crate) fn unpack_str<'v>(&'v self) -> Option<&'v str> {
self.to_value().unpack_str()
}
pub(crate) fn get_ref<'v>(self) -> &'v dyn AValueDyn<'v> {
match self.0.unpack() {
Either::Left(x) => x.unpack(),
Either::Right(x) => basic_ref(x),
}
}
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()
|| self.unpack_int().is_some()
|| FrozenValueTyped::<StarlarkFloat>::new(self).is_some()
|| FrozenList::from_frozen_value(&self).is_some()
|| FrozenDict::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 fn downcast_frozen_ref<T: StarlarkValue<'static>>(self) -> Option<FrozenRef<'static, T>> {
self.downcast_ref::<T>().map(|value| FrozenRef { value })
}
pub fn downcast_frozen_str(self) -> Option<FrozenRef<'static, str>> {
self.to_value()
.unpack_str()
.map(|value| FrozenRef { value })
}
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(), 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)
}
}
pub trait ValueLike<'v>:
Eq + Copy + Debug + Default + Display + CoerceKey<Value<'v>> + Freeze<Frozen = FrozenValue>
{
type String: StringValueLike<'v>;
fn to_value(self) -> Value<'v>;
fn as_dyn_any(self) -> &'v dyn AnyLifetime<'v>;
fn invoke(
self,
args: &Arguments<'v, '_>,
eval: &mut Evaluator<'v, '_>,
) -> anyhow::Result<Value<'v>> {
self.to_value().invoke(args, eval)
}
fn write_hash(self, hasher: &mut StarlarkHasher) -> anyhow::Result<()>;
fn get_hashed(self) -> anyhow::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>) -> anyhow::Result<bool>;
fn compare(self, other: Value<'v>) -> anyhow::Result<Ordering>;
fn downcast_ref<T: StarlarkValue<'v>>(self) -> Option<&'v T>;
}
#[derive(Debug, thiserror::Error)]
#[error("Cycle detected when serializing value of type `{0}` to JSON")]
struct ToJsonCycleError(&'static str);
impl<'v> ValueLike<'v> for Value<'v> {
type String = StringValue<'v>;
fn to_value(self) -> Value<'v> {
self
}
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_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) -> anyhow::Result<()> {
self.get_ref().write_hash(hasher)
}
fn equals(self, other: Value<'v>) -> anyhow::Result<bool> {
if self.ptr_eq(other) {
Ok(true)
} else {
let _guard = stack_guard::stack_guard()?;
self.get_ref().equals(other)
}
}
fn compare(self, other: Value<'v>) -> anyhow::Result<Ordering> {
let _guard = stack_guard::stack_guard()?;
self.get_ref().compare(other)
}
fn as_dyn_any(self) -> &'v dyn AnyLifetime<'v> {
self.get_ref().value_as_dyn_any()
}
}
impl<'v> ValueLike<'v> for FrozenValue {
type String = FrozenStringValue;
fn to_value(self) -> Value<'v> {
Value::new_frozen(self)
}
fn downcast_ref<T: StarlarkValue<'v>>(self) -> Option<&'v T> {
self.to_value().downcast_ref()
}
fn collect_repr(self, collector: &mut String) {
self.to_value().collect_repr(collector)
}
fn write_hash(self, hasher: &mut StarlarkHasher) -> anyhow::Result<()> {
self.to_value().write_hash(hasher)
}
fn equals(self, other: Value<'v>) -> anyhow::Result<bool> {
self.to_value().equals(other)
}
fn compare(self, other: Value<'v>) -> anyhow::Result<Ordering> {
self.to_value().compare(other)
}
fn as_dyn_any(self) -> &'v dyn AnyLifetime<'v> {
self.get_ref().value_as_dyn_any()
}
}
fn _test_send_sync()
where
FrozenValue: Send + Sync,
{
}
#[cfg(test)]
mod tests {
use crate::values::{
none::NoneType, string::StarlarkStr, types::int::PointerI32, Heap, Value, 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::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());
}
}