ps-boa-gc 1.0.2-dev

Garbage collector for the Boa JavaScript engine.
Documentation
mod miri {
    use std::any::TypeId;

    use boa_macros::{Finalize, Trace};

    use super::super::run_test;
    use crate::{Gc, GcBox, GcErased, force_collect, test::Harness};

    #[test]
    fn erased_gc() {
        run_test(|| {
            let value = vec![1, 2, 3];
            let gc = Gc::new(value.clone());

            assert_eq!(Gc::type_id(&gc), TypeId::of::<Vec<i32>>());

            let erased = GcErased::new(gc.clone());

            assert_eq!(erased.type_id(), TypeId::of::<Vec<i32>>());
            assert!(erased.is::<Vec<i32>>());

            assert_eq!(erased.clone().downcast::<i32>(), None);

            let gc_from_erased = erased.downcast::<Vec<i32>>().unwrap();
            assert_eq!(**gc_from_erased, value);

            assert!(Gc::ptr_eq(&gc, &gc_from_erased));
        });
    }

    #[test]
    fn nested_erased_gc() {
        #[derive(Debug, Trace, Finalize)]
        struct List {
            value: i32,
            next: Option<GcErased>,
        }

        run_test(|| {
            let mut root = GcErased::new(Gc::new(List {
                value: 0,
                next: None,
            }));

            for value in 1..100 {
                root = GcErased::new(Gc::new(List {
                    value,
                    next: Some(root),
                }));
            }

            Harness::assert_exact_bytes_allocated(100 * size_of::<GcBox<List>>());
            force_collect();
            Harness::assert_exact_bytes_allocated(100 * size_of::<GcBox<List>>());

            let mut head = root.downcast::<List>();
            for value in (0..100).rev() {
                let head_unwrap = head.as_ref().unwrap();

                assert_eq!(head_unwrap.value, value);

                head = head_unwrap
                    .next
                    .clone()
                    .and_then(GcErased::downcast::<List>);
            }
        });
    }

    #[test]
    fn c_style_inheritance() {
        #[repr(C)]
        #[derive(Debug, Trace, Finalize, PartialEq, Eq)]
        struct Base {
            base_field: Vec<i32>,
        }

        #[repr(C)]
        #[derive(Debug, Trace, Finalize, PartialEq, Eq)]
        struct Derived {
            base: Base,
            derived_field: Vec<i64>,
        }

        run_test(|| {
            let value = vec![1, 2, 3];
            let derived = Gc::new(Derived {
                base: Base {
                    base_field: value.clone(),
                },
                derived_field: vec![4, 5, 6],
            });

            assert_eq!(Gc::type_id(&derived), TypeId::of::<Derived>());
            assert!(Gc::is::<Derived>(&derived));

            // SAFETY: The structs have #[repr(C)] so this is safe.
            let base = unsafe { Gc::cast_unchecked::<Base>(derived.clone()) };

            assert_eq!(Gc::type_id(&base), TypeId::of::<Derived>());
            assert!(Gc::is::<Derived>(&base));

            assert_eq!(base.base_field, value);
            assert_eq!(base.base_field, derived.base.base_field);

            assert!(Gc::ptr_eq(&base, &derived));

            assert_eq!(Gc::downcast::<i32>(base.clone()), None);
            assert_eq!(*Gc::downcast::<Derived>(base).unwrap(), *derived);
        });
    }
}