cao_lang/vm/runtime/
cao_lang_object.rs

1use std::ptr::NonNull;
2
3use crate::value::Value;
4
5use super::{
6    cao_lang_function::{CaoLangClosure, CaoLangFunction, CaoLangNativeFunction, CaoLangUpvalue},
7    cao_lang_string::CaoLangString,
8    cao_lang_table::CaoLangTable,
9};
10
11// note Gray is not actually useful for now, but it might come in handy if we want to do finalizers
12#[derive(Debug, Clone, Copy)]
13pub enum GcMarker {
14    /// Unprocessed
15    White,
16    /// Visited
17    Gray,
18    /// Done
19    Black,
20    /// This object can not be collected
21    Protected,
22}
23
24#[derive(Debug)]
25pub struct CaoLangObject {
26    pub marker: GcMarker,
27    pub body: CaoLangObjectBody,
28}
29
30#[derive(Debug)]
31pub enum CaoLangObjectBody {
32    Table(CaoLangTable),
33    String(CaoLangString),
34    Function(CaoLangFunction),
35    NativeFunction(CaoLangNativeFunction),
36    Closure(CaoLangClosure),
37    Upvalue(CaoLangUpvalue),
38}
39
40/// RAII style guard that ensures that an object survives the GC
41/// Useful for native function that allocate multiple objects, potentially triggering GC
42pub struct ObjectGcGuard(pub(crate) NonNull<CaoLangObject>);
43
44impl std::ops::Deref for ObjectGcGuard {
45    type Target = CaoLangObject;
46
47    fn deref(&self) -> &Self::Target {
48        unsafe { self.0.as_ref() }
49    }
50}
51
52impl std::ops::DerefMut for ObjectGcGuard {
53    fn deref_mut(&mut self) -> &mut Self::Target {
54        unsafe { self.0.as_mut() }
55    }
56}
57
58impl Drop for ObjectGcGuard {
59    fn drop(&mut self) {
60        unsafe {
61            self.0.as_mut().marker = GcMarker::White;
62        }
63    }
64}
65
66impl ObjectGcGuard {
67    pub fn new(mut obj: NonNull<CaoLangObject>) -> Self {
68        unsafe {
69            obj.as_mut().marker = GcMarker::Protected;
70        }
71        Self(obj)
72    }
73
74    pub fn into_inner(self) -> NonNull<CaoLangObject> {
75        self.0
76    }
77}
78
79impl From<ObjectGcGuard> for Value {
80    fn from(value: ObjectGcGuard) -> Self {
81        Value::Object(value.0)
82    }
83}
84
85impl CaoLangObject {
86    pub fn type_name(&self) -> &'static str {
87        match &self.body {
88            CaoLangObjectBody::Table(_) => "Table",
89            CaoLangObjectBody::String(_) => "String",
90            CaoLangObjectBody::Function(_) => "Function",
91            CaoLangObjectBody::NativeFunction(_) => "NativeFunction",
92            CaoLangObjectBody::Closure(_) => "Closure",
93            CaoLangObjectBody::Upvalue(_) => "Upvalue",
94        }
95    }
96
97    pub fn as_table(&self) -> Option<&CaoLangTable> {
98        match &self.body {
99            CaoLangObjectBody::Table(v) => Some(v),
100            _ => None,
101        }
102    }
103
104    pub fn as_table_mut(&mut self) -> Option<&mut CaoLangTable> {
105        match &mut self.body {
106            CaoLangObjectBody::Table(v) => Some(v),
107            _ => None,
108        }
109    }
110
111    pub fn as_str(&self) -> Option<&str> {
112        match &self.body {
113            CaoLangObjectBody::String(s) => Some(s.as_str()),
114            _ => None,
115        }
116    }
117
118    pub fn as_function(&self) -> Option<&CaoLangFunction> {
119        match &self.body {
120            CaoLangObjectBody::Function(f) => Some(f),
121            _ => None,
122        }
123    }
124
125    pub fn as_closure(&self) -> Option<&CaoLangClosure> {
126        match &self.body {
127            CaoLangObjectBody::Closure(f) => Some(f),
128            _ => None,
129        }
130    }
131
132    pub fn as_upvalue(&self) -> Option<&CaoLangUpvalue> {
133        match &self.body {
134            CaoLangObjectBody::Upvalue(v) => Some(v),
135            _ => None,
136        }
137    }
138
139    pub fn as_upvalue_mut(&mut self) -> Option<&mut CaoLangUpvalue> {
140        match &mut self.body {
141            CaoLangObjectBody::Upvalue(v) => Some(v),
142            _ => None,
143        }
144    }
145
146    pub fn len(&self) -> usize {
147        match &self.body {
148            CaoLangObjectBody::Table(t) => t.len(),
149            CaoLangObjectBody::String(s) => s.len(),
150            CaoLangObjectBody::Function(_) => 0,
151            CaoLangObjectBody::NativeFunction(_) => 0,
152            CaoLangObjectBody::Closure(_) => 0,
153            CaoLangObjectBody::Upvalue(_) => 0,
154        }
155    }
156
157    pub fn is_empty(&self) -> bool {
158        match &self.body {
159            CaoLangObjectBody::Table(_) | CaoLangObjectBody::String(_) => self.len() == 0,
160            CaoLangObjectBody::Function(_)
161            | CaoLangObjectBody::Closure(_)
162            | CaoLangObjectBody::Upvalue(_)
163            | CaoLangObjectBody::NativeFunction(_) => false,
164        }
165    }
166}
167
168impl std::hash::Hash for CaoLangObject {
169    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
170        match &self.body {
171            CaoLangObjectBody::Table(o) => {
172                for (k, v) in o.iter() {
173                    k.hash(state);
174                    v.hash(state);
175                }
176            }
177            CaoLangObjectBody::String(s) => {
178                s.as_str().hash(state);
179            }
180            CaoLangObjectBody::Function(f) => {
181                f.handle.value().hash(state);
182                f.arity.hash(state);
183            }
184            CaoLangObjectBody::NativeFunction(f) => f.handle.value().hash(state),
185            CaoLangObjectBody::Closure(c) => {
186                c.function.handle.value().hash(state);
187                c.function.arity.hash(state);
188            }
189            CaoLangObjectBody::Upvalue(u) => {
190                u.location.hash(state);
191            }
192        }
193    }
194}
195
196impl PartialEq for CaoLangObject {
197    fn eq(&self, other: &Self) -> bool {
198        match (&self.body, &other.body) {
199            (CaoLangObjectBody::Table(lhs), CaoLangObjectBody::Table(rhs)) => {
200                if lhs.len() != rhs.len() {
201                    return false;
202                }
203                for ((kl, vl), (kr, vr)) in lhs.iter().zip(rhs.iter()) {
204                    if kl != kr || vl != vr {
205                        return false;
206                    }
207                }
208                true
209            }
210            (CaoLangObjectBody::String(lhs), CaoLangObjectBody::String(rhs)) => {
211                lhs.as_str().eq(rhs.as_str())
212            }
213            _ => false,
214        }
215    }
216}
217
218impl PartialOrd for CaoLangObject {
219    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
220        self.eq(other)
221            .then_some(std::cmp::Ordering::Equal)
222            .or_else(|| {
223                // equal len but non-eq objects should not return Equal
224                let res = self.len().cmp(&other.len());
225                match res {
226                    std::cmp::Ordering::Equal => None,
227                    _ => Some(res),
228                }
229            })
230    }
231}