Skip to main content

datex_core/shared_values/
shared_container.rs

1use crate::{
2    shared_values::shared_type_container::{
3        NominalTypeDeclaration, SharedTypeContainer,
4    },
5    values::core_value::CoreValue,
6};
7use core::result::Result;
8
9use crate::{
10    prelude::*,
11    runtime::execution::ExecutionError,
12    shared_values::{
13        pointer::{Pointer, PointerReferenceMutability},
14        pointer_address::PointerAddress,
15        shared_value_container::SharedValueContainer,
16    },
17    traits::{
18        apply::Apply, identity::Identity, structural_eq::StructuralEq,
19        value_eq::ValueEq,
20    },
21    types::definition::TypeDefinition,
22    values::{
23        core_values::{map::MapAccessError, r#type::Type},
24        value::Value,
25        value_container::{ValueContainer, ValueKey},
26    },
27};
28use core::{
29    cell::{Ref, RefCell},
30    fmt::Display,
31    hash::{Hash, Hasher},
32    ops::FnOnce,
33    option::Option,
34    unreachable, write,
35};
36use num_enum::TryFromPrimitive;
37use serde::{Deserialize, Serialize};
38
39#[derive(Debug)]
40pub struct IndexOutOfBoundsError {
41    pub index: u32,
42}
43
44impl Display for IndexOutOfBoundsError {
45    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
46        write!(f, "Index out of bounds: {}", self.index)
47    }
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub struct KeyNotFoundError {
52    pub key: ValueContainer,
53}
54
55impl Display for KeyNotFoundError {
56    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
57        write!(f, "Property not found: {}", self.key)
58    }
59}
60
61#[derive(Debug)]
62pub enum AccessError {
63    ImmutableReference,
64    InvalidOperation(String),
65    KeyNotFound(KeyNotFoundError),
66    IndexOutOfBounds(IndexOutOfBoundsError),
67    MapAccessError(MapAccessError),
68    InvalidIndexKey,
69}
70
71impl From<IndexOutOfBoundsError> for AccessError {
72    fn from(err: IndexOutOfBoundsError) -> Self {
73        AccessError::IndexOutOfBounds(err)
74    }
75}
76
77impl From<MapAccessError> for AccessError {
78    fn from(err: MapAccessError) -> Self {
79        AccessError::MapAccessError(err)
80    }
81}
82
83impl From<KeyNotFoundError> for AccessError {
84    fn from(err: KeyNotFoundError) -> Self {
85        AccessError::KeyNotFound(err)
86    }
87}
88
89impl Display for AccessError {
90    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91        match self {
92            AccessError::MapAccessError(err) => {
93                write!(f, "Map access error: {}", err)
94            }
95            AccessError::ImmutableReference => {
96                write!(f, "Cannot modify an immutable reference")
97            }
98            AccessError::InvalidOperation(op) => {
99                write!(f, "Invalid operation: {}", op)
100            }
101            AccessError::KeyNotFound(key) => {
102                write!(f, "{}", key)
103            }
104            AccessError::IndexOutOfBounds(error) => {
105                write!(f, "{}", error)
106            }
107            AccessError::InvalidIndexKey => {
108                write!(f, "Invalid index key")
109            }
110        }
111    }
112}
113
114#[derive(Debug)]
115pub enum TypeError {
116    TypeMismatch { expected: Type, found: Type },
117}
118impl Display for TypeError {
119    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
120        match self {
121            TypeError::TypeMismatch { expected, found } => write!(
122                f,
123                "Type mismatch: expected {}, found {}",
124                expected, found
125            ),
126        }
127    }
128}
129
130#[derive(Debug)]
131pub enum AssignmentError {
132    ImmutableReference,
133    TypeError(Box<TypeError>),
134}
135
136impl Display for AssignmentError {
137    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138        match self {
139            AssignmentError::ImmutableReference => {
140                write!(f, "Cannot assign to an immutable reference")
141            }
142            AssignmentError::TypeError(e) => {
143                write!(f, "Type error: {}", e)
144            }
145        }
146    }
147}
148
149#[derive(
150    Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TryFromPrimitive,
151)]
152#[repr(u8)]
153pub enum SharedContainerMutability {
154    Mutable = 0,
155    Immutable = 1,
156}
157
158pub mod mutability_as_int {
159    use super::SharedContainerMutability;
160    use crate::prelude::*;
161    use serde::{Deserialize, Deserializer, Serializer, de::Error};
162
163    pub fn serialize<S>(
164        value: &SharedContainerMutability,
165        serializer: S,
166    ) -> Result<S::Ok, S::Error>
167    where
168        S: Serializer,
169    {
170        match value {
171            SharedContainerMutability::Mutable => serializer.serialize_u8(0),
172            SharedContainerMutability::Immutable => serializer.serialize_u8(1),
173        }
174    }
175
176    pub fn deserialize<'de, D>(
177        deserializer: D,
178    ) -> Result<SharedContainerMutability, D::Error>
179    where
180        D: Deserializer<'de>,
181    {
182        let opt = u8::deserialize(deserializer)?;
183        Ok(match opt {
184            0 => SharedContainerMutability::Mutable,
185            1 => SharedContainerMutability::Immutable,
186            x => {
187                return Err(D::Error::custom(format!(
188                    "invalid mutability code: {}",
189                    x
190                )));
191            }
192        })
193    }
194}
195pub mod mutability_option_as_int {
196    use super::SharedContainerMutability;
197
198    use crate::prelude::*;
199    use serde::{Deserialize, Deserializer, Serializer, de::Error};
200
201    pub fn serialize<S>(
202        value: &Option<SharedContainerMutability>,
203        serializer: S,
204    ) -> Result<S::Ok, S::Error>
205    where
206        S: Serializer,
207    {
208        match value {
209            Some(SharedContainerMutability::Mutable) => {
210                serializer.serialize_u8(0)
211            }
212            Some(SharedContainerMutability::Immutable) => {
213                serializer.serialize_u8(1)
214            }
215            None => serializer.serialize_none(),
216        }
217    }
218
219    pub fn deserialize<'de, D>(
220        deserializer: D,
221    ) -> Result<Option<SharedContainerMutability>, D::Error>
222    where
223        D: Deserializer<'de>,
224    {
225        let opt = Option::<u8>::deserialize(deserializer)?;
226        Ok(match opt {
227            Some(0) => Some(SharedContainerMutability::Mutable),
228            Some(1) => Some(SharedContainerMutability::Immutable),
229            Some(x) => {
230                return Err(D::Error::custom(format!(
231                    "invalid mutability code: {}",
232                    x
233                )));
234            }
235            None => None,
236        })
237    }
238}
239
240impl Display for SharedContainerMutability {
241    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
242        match self {
243            SharedContainerMutability::Mutable => write!(f, "mut"),
244            SharedContainerMutability::Immutable => write!(f, ""),
245        }
246    }
247}
248
249#[derive(Debug, Clone)]
250pub enum SharedContainer {
251    Value(Rc<RefCell<SharedValueContainer>>),
252    Type(Rc<RefCell<SharedTypeContainer>>),
253}
254
255impl Display for SharedContainer {
256    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257        match self {
258            SharedContainer::Value(vr) => {
259                let vr = vr.borrow();
260                write!(f, "{} {}", vr.mutability, vr.value_container)
261            }
262            SharedContainer::Type(tr) => {
263                let tr = tr.borrow();
264                write!(f, "{}", tr)
265            }
266        }
267    }
268}
269
270impl From<SharedValueContainer> for SharedContainer {
271    fn from(reference: SharedValueContainer) -> Self {
272        SharedContainer::Value(Rc::new(RefCell::new(reference)))
273    }
274}
275impl From<SharedTypeContainer> for SharedContainer {
276    fn from(reference: SharedTypeContainer) -> Self {
277        SharedContainer::Type(Rc::new(RefCell::new(reference)))
278    }
279}
280
281/// Two references are identical if they point to the same data
282impl Identity for SharedContainer {
283    fn identical(&self, other: &Self) -> bool {
284        match (self, other) {
285            (SharedContainer::Value(a), SharedContainer::Value(b)) => {
286                Rc::ptr_eq(a, b)
287            }
288            (SharedContainer::Type(a), SharedContainer::Type(b)) => {
289                Rc::ptr_eq(a, b)
290            }
291            _ => false,
292        }
293    }
294}
295
296impl Eq for SharedContainer {}
297
298/// PartialEq corresponds to pointer equality / identity for `Reference`.
299impl PartialEq for SharedContainer {
300    fn eq(&self, other: &Self) -> bool {
301        self.identical(other)
302    }
303}
304
305impl StructuralEq for SharedContainer {
306    fn structural_eq(&self, other: &Self) -> bool {
307        match (self, other) {
308            (SharedContainer::Type(a), SharedContainer::Type(b)) => {
309                a.borrow().type_value.structural_eq(&b.borrow().type_value)
310            }
311            (SharedContainer::Value(a), SharedContainer::Value(b)) => a
312                .borrow()
313                .value_container
314                .structural_eq(&b.borrow().value_container),
315            _ => false,
316        }
317    }
318}
319
320impl ValueEq for SharedContainer {
321    fn value_eq(&self, other: &Self) -> bool {
322        match (self, other) {
323            (SharedContainer::Type(a), SharedContainer::Type(b)) => {
324                // FIXME #281: Implement value_eq for type and use here instead (recursive)
325                a.borrow().type_value.structural_eq(&b.borrow().type_value)
326            }
327            (SharedContainer::Value(a), SharedContainer::Value(b)) => a
328                .borrow()
329                .value_container
330                .value_eq(&b.borrow().value_container),
331            _ => false,
332        }
333    }
334}
335
336// x = mut shared {x: 1};
337// y = mut {a: 2};
338// mut shared {x: 1};
339// &mut shared {a: 3};
340
341impl Hash for SharedContainer {
342    fn hash<H: Hasher>(&self, state: &mut H) {
343        match self {
344            SharedContainer::Type(tr) => {
345                let ptr = Rc::as_ptr(tr);
346                ptr.hash(state); // hash the address
347            }
348            SharedContainer::Value(vr) => {
349                let ptr = Rc::as_ptr(vr);
350                ptr.hash(state); // hash the address
351            }
352        }
353    }
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, Hash)]
357pub enum SharedValueCreationError {
358    InvalidType,
359    MutabilityMismatch,
360}
361
362impl Display for SharedValueCreationError {
363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        match self {
365            SharedValueCreationError::InvalidType => {
366                write!(
367                    f,
368                    "Cannot create shared value from value container: invalid type"
369                )
370            }
371            SharedValueCreationError::MutabilityMismatch => {
372                write!(
373                    f,
374                    "Cannot create mutable shared value for immutable value"
375                )
376            }
377        }
378    }
379}
380
381impl SharedContainer {
382    /// Runs a closure with the current value of this reference.
383    pub(crate) fn with_value<R, F: FnOnce(&mut Value) -> R>(
384        &self,
385        f: F,
386    ) -> Option<R> {
387        let reference = self.collapse_reference_chain();
388        match reference {
389            SharedContainer::Value(vr) => {
390                match &mut vr.borrow_mut().value_container {
391                    ValueContainer::Local(value) => Some(f(value)),
392                    ValueContainer::Shared(_) => {
393                        unreachable!(
394                            "Expected a ValueContainer::Value, but found a Reference"
395                        )
396                    }
397                }
398            }
399            SharedContainer::Type(_) => None,
400        }
401    }
402
403    // TODO #282: Mark as unsafe function
404    /// Note: borrows the contained value. While in callback, no other borrows to the value are allowed.
405    pub(crate) fn with_value_unchecked<R, F: FnOnce(&mut Value) -> R>(
406        &self,
407        f: F,
408    ) -> R {
409        unsafe { self.with_value(f).unwrap_unchecked() }
410    }
411}
412
413impl SharedContainer {
414    pub fn pointer_address(&self) -> PointerAddress {
415        match self {
416            SharedContainer::Value(vr) => vr.borrow().pointer.address(),
417            SharedContainer::Type(tr) => tr.borrow().pointer.address(),
418        }
419    }
420
421    pub fn pointer(&self) -> Ref<'_, Pointer> {
422        match self {
423            SharedContainer::Value(vr) => {
424                Ref::map(vr.borrow(), |vr| &vr.pointer)
425            }
426            SharedContainer::Type(tr) => {
427                Ref::map(tr.borrow(), |tr| &tr.pointer)
428            }
429        }
430    }
431
432    /// Gets the mutability of the shared value.
433    /// TypeReferences are always immutable.
434    pub(crate) fn mutability(&self) -> SharedContainerMutability {
435        match self {
436            SharedContainer::Value(vr) => vr.borrow().mutability.clone(),
437            SharedContainer::Type(_) => SharedContainerMutability::Immutable,
438        }
439    }
440
441    /// Checks if the reference can be mutated by the current endpoint.
442    pub(crate) fn can_mutate(&self) -> bool {
443        match self.pointer().reference_mutability() {
444            // is &mut reference to a pointer
445            Some(PointerReferenceMutability::Mutable) => true,
446            // is owned pointer, check if the shared container is mutable
447            None => self.mutability() == SharedContainerMutability::Mutable,
448            _ => false,
449        }
450    }
451
452    /// Checks if the reference is mutable.
453    /// A reference is mutable if it is a mutable ValueReference and all references in the chain are mutable.
454    /// TypeReferences are always immutable.
455    /// FIXME #284: Do we really need this? Probably we already collapse the ref and then change it's value and perform
456    /// the mutability check on the most inner ref.
457    pub fn is_mutable(&self) -> bool {
458        match self {
459            SharedContainer::Type(_) => false, // type references are always immutable
460            SharedContainer::Value(vr) => {
461                let vr_borrow = vr.borrow();
462                // if the current reference is immutable, whole chain is immutable
463                if vr_borrow.mutability != SharedContainerMutability::Mutable {
464                    return false;
465                }
466
467                // otherwise, check if ref is pointing to another reference
468                match &vr_borrow.value_container {
469                    ValueContainer::Shared(inner) => inner.is_mutable(),
470                    ValueContainer::Local(_) => true,
471                }
472            }
473        }
474    }
475
476    /// Creates a new shared value containing the given value container
477    pub fn try_boxed(
478        value_container: ValueContainer,
479        allowed_type: Option<TypeDefinition>,
480        pointer: Pointer,
481        mutability: SharedContainerMutability,
482    ) -> Result<Self, SharedValueCreationError> {
483        // immutable shared value cannot be contained in a mutable shared container, since we want interior mutability
484        if let ValueContainer::Shared(ref shared) = value_container
485            && mutability == SharedContainerMutability::Mutable
486            && shared.mutability() != SharedContainerMutability::Mutable
487        {
488            return Err(SharedValueCreationError::MutabilityMismatch);
489        }
490        let allowed_type =
491            allowed_type.unwrap_or_else(|| value_container.allowed_type());
492
493        // TODO #286: make sure allowed type is superset of reference's allowed type
494        Ok(SharedContainer::Value(Rc::new(RefCell::new(
495            SharedValueContainer::new(
496                value_container,
497                pointer,
498                allowed_type,
499                mutability,
500            ),
501        ))))
502    }
503
504    pub fn new_from_type(
505        type_value: Type,
506        pointer: Pointer,
507        maybe_nominal_type_declaration: Option<NominalTypeDeclaration>,
508    ) -> Self {
509        let type_reference = SharedTypeContainer {
510            pointer,
511            nominal_type_declaration: maybe_nominal_type_declaration,
512            type_value,
513        };
514        SharedContainer::Type(Rc::new(RefCell::new(type_reference)))
515    }
516
517    pub fn boxed_mut(
518        value_container: ValueContainer,
519        pointer: Pointer,
520    ) -> Result<Self, SharedValueCreationError> {
521        SharedContainer::try_boxed(
522            value_container,
523            None,
524            pointer,
525            SharedContainerMutability::Mutable,
526        )
527    }
528
529    pub fn boxed(
530        value_container: impl Into<ValueContainer>,
531        pointer: Pointer,
532    ) -> Self {
533        SharedContainer::try_boxed(
534            value_container.into(),
535            None,
536            pointer,
537            SharedContainerMutability::Immutable,
538        )
539        .unwrap()
540    }
541
542    /// Collapses the reference chain to most inner reference to which this reference points.
543    pub fn collapse_reference_chain(&self) -> SharedContainer {
544        match self {
545            // FIXME #288: Can we optimize this to avoid creating rc ref cells?
546            SharedContainer::Type(tr) => SharedContainer::Type(Rc::new(
547                RefCell::new(tr.borrow().collapse_reference_chain()),
548            )),
549            SharedContainer::Value(vr) => {
550                match &vr.borrow().value_container {
551                    ValueContainer::Shared(reference) => {
552                        // If this is a reference, resolve it to its current value
553                        reference.collapse_reference_chain()
554                    }
555                    ValueContainer::Local(_) => {
556                        // If this is a value, return it directly
557                        self.clone()
558                    }
559                }
560            }
561        }
562    }
563
564    /// Converts a reference to its current value, collapsing any reference chains and converting type references to type values.
565    pub fn collapse_to_value(&self) -> Rc<RefCell<Value>> {
566        let reference = self.collapse_reference_chain();
567        match reference {
568            SharedContainer::Value(vr) => match &vr.borrow().value_container {
569                ValueContainer::Local(_) => {
570                    vr.borrow().value_container.to_value()
571                }
572                ValueContainer::Shared(_) => unreachable!(
573                    "Expected a ValueContainer::Value, but found a Reference"
574                ),
575            },
576            // TODO #289: can we optimize this to avoid cloning the type value?
577            SharedContainer::Type(tr) => Rc::new(RefCell::new(Value::from(
578                CoreValue::Type(tr.borrow().type_value.clone()),
579            ))),
580        }
581    }
582
583    // TODO #290: no clone?
584    pub fn value_container(&self) -> ValueContainer {
585        match self {
586            SharedContainer::Value(vr) => vr.borrow().value_container.clone(),
587            SharedContainer::Type(tr) => ValueContainer::Local(Value::from(
588                CoreValue::Type(tr.borrow().type_value.clone()),
589            )),
590        }
591    }
592
593    pub fn allowed_type(&self) -> TypeDefinition {
594        match self {
595            SharedContainer::Value(vr) => vr.borrow().allowed_type.clone(),
596            SharedContainer::Type(_) => core::todo!("#293 type Type"),
597        }
598    }
599
600    pub fn actual_type(&self) -> TypeDefinition {
601        match self {
602            SharedContainer::Value(vr) => vr
603                .borrow()
604                .value_container
605                .to_value()
606                .borrow()
607                .actual_type()
608                .clone(),
609            SharedContainer::Type(_tr) => core::todo!("#294 type Type"),
610        }
611    }
612
613    pub fn is_type(&self) -> bool {
614        match self {
615            SharedContainer::Type(_) => true,
616            SharedContainer::Value(vr) => {
617                vr.borrow().resolve_current_value().borrow().is_type()
618            }
619        }
620    }
621
622    /// Returns a mutable reference to the ValueReference if this is a mutable ValueReference.
623    pub fn mutable_reference(
624        &self,
625    ) -> Option<Rc<RefCell<SharedValueContainer>>> {
626        match self {
627            SharedContainer::Type(_) => None,
628            SharedContainer::Value(vr) => {
629                if vr.borrow().is_mutable() {
630                    Some(vr.clone())
631                } else {
632                    None
633                }
634            }
635        }
636    }
637
638    /// Sets the value container of the reference if it is mutable.
639    /// If the reference is immutable, an error is returned.
640    pub fn set_value_container(
641        &self,
642        new_value_container: ValueContainer,
643    ) -> Result<(), AssignmentError> {
644        match &self {
645            SharedContainer::Type(_) => {
646                Err(AssignmentError::ImmutableReference)
647            }
648            SharedContainer::Value(vr) => {
649                if self.is_mutable() {
650                    // TODO #295: check type compatibility, handle observers
651                    vr.borrow_mut().value_container = new_value_container;
652                    Ok(())
653                } else {
654                    Err(AssignmentError::ImmutableReference)
655                }
656            }
657        }
658    }
659}
660/// Getter for references
661impl SharedContainer {
662    /// Gets a property on the value if applicable (e.g. for map and structs)
663    // FIXME #296 make this return a reference to a value container
664    // Just for later as myRef.x += 1
665    // key_ref = myRef.x // myRef.try_get_property("x".into())
666    // key_val = &key_ref.value()
667    // &key_ref.set_value(key_val + 1)
668    // -> we could avoid some clones if so (as get, addition, set would all be a clone)
669    pub fn try_get_property<'a>(
670        &self,
671        key: impl Into<ValueKey<'a>>,
672    ) -> Result<ValueContainer, AccessError> {
673        self.with_value(|value| value.try_get_property(key))
674            .unwrap_or(Err(AccessError::InvalidOperation(
675                "Cannot get property on invalid reference".to_string(),
676            )))
677    }
678}
679
680impl Apply for SharedContainer {
681    fn apply(
682        &self,
683        args: &[ValueContainer],
684    ) -> Result<Option<ValueContainer>, ExecutionError> {
685        match self {
686            SharedContainer::Type(tr) => tr.borrow().apply(args),
687            SharedContainer::Value(vr) => {
688                vr.borrow().resolve_current_value().borrow().apply(args)
689            }
690        }
691    }
692
693    fn apply_single(
694        &self,
695        arg: &ValueContainer,
696    ) -> Result<Option<ValueContainer>, ExecutionError> {
697        match self {
698            SharedContainer::Type(tr) => tr.borrow().apply_single(arg),
699            SharedContainer::Value(vr) => vr
700                .borrow()
701                .resolve_current_value()
702                .borrow()
703                .apply_single(arg),
704        }
705    }
706}
707
708#[cfg(test)]
709mod tests {
710    use super::*;
711    use crate::{
712        assert_identical, assert_structural_eq, assert_value_eq, prelude::*,
713        runtime::memory::Memory, traits::value_eq::ValueEq,
714        values::core_values::map::Map,
715    };
716    use core::assert_matches;
717
718    #[test]
719    fn try_mut_from() {
720        // creating a mutable shared container from a value should work
721        let value = ValueContainer::from(42);
722        let reference =
723            SharedContainer::boxed_mut(value, Pointer::NULL).unwrap();
724        assert_eq!(reference.mutability(), SharedContainerMutability::Mutable);
725
726        // creating a mutable reference from a type should fail
727        let type_value = ValueContainer::Shared(SharedContainer::Type(
728            SharedTypeContainer::anonymous(Type::UNIT, Pointer::NULL)
729                .as_ref_cell(),
730        ));
731        assert_matches!(
732            SharedContainer::boxed_mut(type_value, Pointer::NULL),
733            Err(SharedValueCreationError::MutabilityMismatch)
734        );
735    }
736
737    #[test]
738    fn property() {
739        let mut map = Map::default();
740        map.set("name", ValueContainer::from("Jonas"));
741        map.set("age", ValueContainer::from(30));
742        let reference =
743            SharedContainer::boxed(ValueContainer::from(map), Pointer::NULL);
744        assert_eq!(
745            reference.try_get_property("name").unwrap(),
746            ValueContainer::from("Jonas")
747        );
748        assert_eq!(
749            reference.try_get_property("age").unwrap(),
750            ValueContainer::from(30)
751        );
752        assert!(reference.try_get_property("nonexistent").is_err());
753        assert_matches!(
754            reference.try_get_property("nonexistent"),
755            Err(AccessError::KeyNotFound(_))
756        );
757    }
758
759    #[test]
760    fn text_property() {
761        let struct_val = Map::from(vec![
762            ("name".to_string(), ValueContainer::from("Jonas")),
763            ("age".to_string(), ValueContainer::from(30)),
764        ]);
765        let reference = SharedContainer::boxed(
766            ValueContainer::from(struct_val),
767            Pointer::NULL,
768        );
769        assert_eq!(
770            reference.try_get_property("name").unwrap(),
771            ValueContainer::from("Jonas")
772        );
773        assert_eq!(
774            reference.try_get_property("age").unwrap(),
775            ValueContainer::from(30)
776        );
777        assert!(reference.try_get_property("nonexistent").is_err());
778        assert_matches!(
779            reference.try_get_property("nonexistent"),
780            Err(AccessError::KeyNotFound(_))
781        );
782    }
783
784    #[test]
785    fn numeric_property() {
786        let list = vec![
787            ValueContainer::from(1),
788            ValueContainer::from(2),
789            ValueContainer::from(3),
790        ];
791        let reference =
792            SharedContainer::boxed(ValueContainer::from(list), Pointer::NULL);
793
794        assert_eq!(
795            reference.try_get_property(0).unwrap(),
796            ValueContainer::from(1)
797        );
798        assert_eq!(
799            reference.try_get_property(1).unwrap(),
800            ValueContainer::from(2)
801        );
802        assert_eq!(
803            reference.try_get_property(2).unwrap(),
804            ValueContainer::from(3)
805        );
806        assert!(reference.try_get_property(3).is_err());
807
808        assert_matches!(
809            reference.try_get_property(100),
810            Err(AccessError::IndexOutOfBounds(IndexOutOfBoundsError {
811                index: 100
812            }))
813        );
814
815        let text_ref = SharedContainer::boxed(
816            ValueContainer::from("hello"),
817            Pointer::NULL,
818        );
819        assert_eq!(
820            text_ref.try_get_property(1).unwrap(),
821            ValueContainer::from("e".to_string())
822        );
823        assert!(text_ref.try_get_property(5).is_err());
824        assert_matches!(
825            text_ref.try_get_property(100),
826            Err(AccessError::IndexOutOfBounds(IndexOutOfBoundsError {
827                index: 100
828            }))
829        );
830    }
831
832    #[test]
833    fn reference_identity() {
834        let value = 42;
835        let reference1 = SharedContainer::boxed(value, Pointer::NULL);
836        let reference2 = reference1.clone();
837
838        // cloned reference should be equal (identical)
839        assert_eq!(reference1, reference2);
840        // value containers containing the references should also be equal
841        assert_eq!(
842            ValueContainer::Shared(reference1.clone()),
843            ValueContainer::Shared(reference2.clone())
844        );
845        // assert_identical! should also confirm identity
846        assert_identical!(reference1.clone(), reference2);
847        // separate reference containing the same value should not be equal
848        assert_ne!(reference1, SharedContainer::boxed(value, Pointer::NULL));
849    }
850
851    #[test]
852    fn reference_value_equality() {
853        let value = 42;
854        let reference1 = ValueContainer::Shared(SharedContainer::boxed(
855            value,
856            Pointer::NULL,
857        ));
858        let reference2 = ValueContainer::Shared(SharedContainer::boxed(
859            value,
860            Pointer::NULL,
861        ));
862
863        // different references should not be equal a.k.a. identical
864        assert_ne!(reference1, reference2);
865        // but their current resolved values should be equal
866        assert_value_eq!(reference1, ValueContainer::from(value));
867    }
868
869    #[test]
870    fn reference_structural_equality() {
871        let reference1 = SharedContainer::boxed(42.0, Pointer::NULL);
872        let reference2 = SharedContainer::boxed(42, Pointer::NULL);
873
874        // different references should not be equal a.k.a. identical
875        assert_ne!(reference1, reference2);
876        // but their current resolved values should be structurally equal
877        assert!(!reference1.structural_eq(&reference2));
878    }
879
880    #[test]
881    fn nested_references() {
882        let memory = &RefCell::new(Memory::default());
883
884        let mut map_a = Map::default();
885        map_a.set("number", ValueContainer::from(42));
886        map_a.set(
887            "obj",
888            ValueContainer::Shared(SharedContainer::boxed(
889                Map::default(),
890                Pointer::NULL,
891            )),
892        );
893
894        // construct map_a as a value first
895        let map_a_original_ref = ValueContainer::Shared(
896            SharedContainer::boxed(map_a, Pointer::NULL),
897        );
898
899        // create map_b as a reference
900        let map_b_ref = SharedContainer::try_boxed(
901            Map::default().into(),
902            None,
903            Pointer::NULL,
904            SharedContainerMutability::Mutable,
905        )
906        .unwrap();
907
908        // set map_a as property of b. This should create a reference to a clone of map_a that
909        // is upgraded to a reference
910        map_b_ref
911            .try_set_property(0, None, "a", map_a_original_ref.clone())
912            .unwrap();
913
914        // assert that the reference to map_a is set correctly
915        let map_a_ref = map_b_ref.try_get_property("a").unwrap();
916        assert_structural_eq!(map_a_ref, map_a_original_ref);
917        assert_eq!(map_a_ref, map_a_original_ref);
918        assert_identical!(map_a_ref, map_a_original_ref);
919        // map_a_ref should be a reference
920        assert_matches!(map_a_ref, ValueContainer::Shared(_));
921        map_a_ref.with_maybe_shared(|a_ref| {
922            // map_a_ref.number should be a value
923            assert_matches!(
924                a_ref.try_get_property("number"),
925                Ok(ValueContainer::Local(_))
926            );
927            // map_a_ref.obj should be a reference
928            assert_matches!(
929                a_ref.try_get_property("obj"),
930                Ok(ValueContainer::Shared(_))
931            );
932        });
933    }
934}