1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
use std::{
    mem,
    os::raw::c_char,
};
use crate::prelude::*;

/// A Rust type that can be used as a object.
///
/// # Examples
///
/// Implementing this trait allows for wrapping Rust data in a
/// [`RosyObject`](object/struct.RosyObject.html).
///
/// ```
/// # rosy::vm::init().unwrap();
/// use std::os::raw::c_char;
/// use rosy::{Rosy, RosyObject, Class};
///
/// struct Flag<'a> {
///     did_free: &'a mut bool
/// }
///
/// unsafe impl Rosy for Flag<'_> {
///     const ID: *const c_char = "rosy_flag\0".as_ptr() as _;
///
///     fn class() -> Class {
///         Class::get_or_def("RosyFlag").unwrap()
///     }
///
///     fn mark(&self) {}
///
///     fn free(self: Box<Self>) {
///         *self.did_free = true;
///     }
/// }
///
/// let mut did_free = false;
/// let obj = RosyObject::from(Flag { did_free: &mut did_free });
///
/// unsafe { rosy::vm::destroy() };
///
/// assert!(did_free);
/// ```
pub unsafe trait Rosy: Sized {
    /// A C string of the unique identifier of the type.
    ///
    /// Note that the type is not
    /// [`CStr`](https://doc.rust-lang.org/std/ffi/struct.CStr.html). This is
    /// because creating a constant instance can only be done on nightly.
    const ID: *const c_char;

    /// A unique identifier for `RosyObject<Self>` to facilitate casting.
    ///
    /// # Safety
    ///
    /// This value _must_ be unique. Rosy's built-in objects use identifiers
    /// that are very close to `u128::max_value()`, so those are easy to avoid.
    #[inline]
    fn unique_object_id() -> Option<u128> {
        None
    }

    /// Returns the class defined for this type.
    ///
    /// The default is `RustObject`, however other implementors of this trait
    /// should consider using a different class to define methods or properties
    /// on.
    #[inline]
    fn class() -> Class {
        Class::rust_object()
    }

    /// Attempts to create a `RosyObject` instance by casting `obj`.
    ///
    /// This could be implemented by checking against [`class`](#method.class)
    /// but care must be taken to ensure that all instances of this type's class
    /// refer to Rust data of type `Self`.
    ///
    /// The default implementation checks the
    /// [`unique_object_id`](#method.unique_object_id) of `Self` against the
    /// `unique_id` of `A`.
    #[inline]
    #[allow(unused_variables)]
    fn cast<A: Object>(obj: A) -> Option<RosyObject<Self>> {
        if A::unique_id() == Self::unique_object_id() {
            unsafe { Some(RosyObject::cast_unchecked(obj)) }
        } else {
            None
        }
    }

    /// Called during Ruby's mark phase of garbage collection to determine which
    /// Ruby references in `self` are live and should not be swept.
    ///
    /// # Safety
    ///
    /// This method is called during garbage collection and it is required that:
    /// - _All_ live Ruby objects are properly marked
    /// - No new Ruby objects are allocated
    fn mark(&self);

    /// Runs destructors and frees `self`.
    ///
    /// # Safety
    ///
    /// The implementor must ensure that no new Ruby objects are allocated.
    #[inline]
    fn free(self: Box<Self>) {
        drop(self);
    }

    /// Returns the estimated memory consumption of `self` in bytes.
    #[inline]
    fn size(&self) -> usize {
        mem::size_of_val(self)
    }
}

unsafe impl<R: Rosy> Rosy for &[R] {
    const ID: *const c_char = b"rust_slice\0".as_ptr() as _;

    #[inline]
    fn mark(&self) {
        self.iter().for_each(Rosy::mark);
    }

    #[inline]
    fn size(&self) -> usize {
        self.iter().fold(0, |cur, r| cur + r.size())
    }
}

unsafe impl<R: Rosy> Rosy for &mut [R] {
    const ID: *const c_char = b"rust_mut_slice\0".as_ptr() as _;

    #[inline]
    fn mark(&self) {
        self.iter().for_each(Rosy::mark);
    }

    #[inline]
    fn size(&self) -> usize {
        self.iter().fold(0, |cur, r| cur + r.size())
    }
}

unsafe impl<R: Rosy> Rosy for Vec<R> {
    const ID: *const c_char = b"rust_vec\0".as_ptr() as _;

    #[inline]
    fn unique_object_id() -> Option<u128> {
        let inner = R::unique_object_id()?;
        let base = u128::from_le_bytes(*b"built-in Vec<T> ");
        Some(inner.rotate_right(1) ^ base)
    }

    #[inline]
    fn mark(&self) {
        self.iter().for_each(Rosy::mark);
    }

    #[inline]
    fn size(&self) -> usize {
        self.iter().fold(0, |cur, r| cur + r.size())
    }
}

unsafe impl Rosy for &str {
    const ID: *const c_char = b"rust_str\0".as_ptr() as _;

    #[inline]
    fn mark(&self) {}

    #[inline]
    fn size(&self) -> usize {
        mem::size_of_val(*self)
    }
}

unsafe impl Rosy for std::string::String {
    const ID: *const c_char = b"rust_string\0".as_ptr() as _;

    #[inline]
    fn unique_object_id() -> Option<u128> {
        Some((!0) - 0xff)
    }

    #[inline]
    fn mark(&self) {}

    #[inline]
    fn size(&self) -> usize {
        self.as_str().size()
    }
}