1use std::sync::atomic::AtomicU64;
5use std::sync::Arc;
6
7unsafe impl Send for Mutator {}
9
10use catallaxy_gc::{Header, LocalHeap, ObjState, ThreadRecord};
11use num_bigint::BigInt;
12use num_traits::ToPrimitive;
13
14use crate::class::{Class, BUILTINS};
15use crate::dict::DictTable;
16use crate::object::*;
17use crate::value::{fits_small, Value};
18
19#[derive(Debug)]
21pub enum Trap {
22 Panic(String),
24 Unwind { frame: u64, kind: u8, value: Value },
26 Exit(i32),
28}
29
30pub type RtResult<T> = Result<T, Trap>;
31
32pub fn panic<T>(msg: impl Into<String>) -> RtResult<T> {
33 Err(Trap::Panic(msg.into()))
34}
35
36pub struct Mutator {
37 pub tid: u16,
38 pub rec: Arc<ThreadRecord>,
39 heap: *mut LocalHeap,
42 handles: *mut Vec<Value>,
45}
46
47#[derive(Copy, Clone)]
49pub struct Handle(usize);
50
51impl Mutator {
52 pub fn new(rec: Arc<ThreadRecord>) -> Self {
54 let heap = Box::into_raw(Box::new(LocalHeap::new(rec.tid)));
55 let handles = Box::into_raw(Box::new(Vec::new()));
56 Mutator { tid: rec.tid, rec, heap, handles }
57 }
58
59 pub fn set_young_alloc(&mut self, on: bool) {
61 unsafe { (*self.heap).set_young_alloc(on) }
62 }
63
64 pub fn sharing(other: &Mutator) -> Self {
67 Mutator {
68 tid: other.tid,
69 rec: other.rec.clone(),
70 heap: other.heap,
71 handles: Box::into_raw(Box::new(Vec::new())),
72 }
73 }
74
75 pub unsafe fn free_handles(&mut self) {
80 drop(Box::from_raw(self.handles));
81 self.handles = std::ptr::null_mut();
82 }
83
84 pub fn heap_ptr(&self) -> *mut LocalHeap {
86 self.heap
87 }
88
89 pub fn release_pages(&mut self) {
91 unsafe { (*self.heap).release_pages() }
95 }
96
97 pub fn scope<R>(&mut self, f: impl FnOnce(&mut Mutator) -> R) -> R {
101 let base = self.handles_len();
102 let r = f(self);
103 unsafe { (*self.handles).truncate(base) };
105 r
106 }
107 pub fn root(&mut self, v: Value) -> Handle {
108 unsafe {
109 (*self.handles).push(v);
110 Handle((*self.handles).len() - 1)
111 }
112 }
113 pub fn get(&self, h: Handle) -> Value {
114 unsafe { (&(*self.handles))[h.0] }
115 }
116 pub fn set(&mut self, h: Handle, v: Value) {
117 unsafe { (&mut (*self.handles))[h.0] = v };
118 }
119 fn handles_len(&self) -> usize {
120 unsafe { (*self.handles).len() }
121 }
122 pub fn handle_mark(&self) -> usize {
125 self.handles_len()
126 }
127 pub fn release_handles(&mut self, mark: usize) {
129 unsafe { (*self.handles).truncate(mark) };
130 }
131 pub fn handle_values(&self) -> &[Value] {
133 unsafe { &*self.handles }
134 }
135 pub fn handles_ptr(&self) -> *const Vec<Value> {
137 self.handles
138 }
139
140 #[inline]
143 pub fn alloc_raw(&mut self, size: usize, cls: &'static Class) -> *mut Header {
144 unsafe { (*self.heap).alloc(size, &cls.gc as *const _) }
146 }
147
148 pub fn alloc_instance(&mut self, cls: &'static Class) -> Value {
149 let n = cls.nfields();
150 let h = self.alloc_raw(InstanceObj::size_for(n), cls);
151 let inst = unsafe { &*(h as *const InstanceObj) };
152 for i in 0..n {
153 inst.set(i, Value::NIL);
154 }
155 Value::from_ptr(h)
156 }
157
158 pub fn alloc_str(&mut self, s: &str) -> Value {
159 let h = self.alloc_raw(StrObj::BASE + s.len(), BUILTINS.string);
160 unsafe {
161 let o = &mut *(h as *mut StrObj);
162 o.len = s.len();
163 std::ptr::copy_nonoverlapping(s.as_ptr(), o.data_ptr(), s.len());
164 (*h).set_state(ObjState::Frozen);
165 }
166 Value::from_ptr(h)
167 }
168
169 pub fn int_from_big(&mut self, b: BigInt) -> Value {
171 if let Some(i) = b.to_i64() {
172 if fits_small(i) {
173 return Value::small_int(i);
174 }
175 }
176 let h = self.alloc_raw(std::mem::size_of::<BigIntObj>(), BUILTINS.bigint);
177 unsafe {
178 std::ptr::write(&mut (*(h as *mut BigIntObj)).value, b);
179 (*h).set_state(ObjState::Frozen);
180 }
181 Value::from_ptr(h)
182 }
183
184 #[inline]
185 pub fn int(&mut self, i: i64) -> Value {
186 if fits_small(i) {
187 Value::small_int(i)
188 } else {
189 self.int_from_big(BigInt::from(i))
190 }
191 }
192
193 pub fn alloc_list(&mut self, items: Vec<Value>) -> Value {
194 let h = self.alloc_raw(std::mem::size_of::<ListObj>(), BUILTINS.list);
195 unsafe { ListObj::init(h as *mut ListObj, items) };
196 Value::from_ptr(h)
197 }
198
199 pub fn alloc_dict(&mut self, table: DictTable) -> Value {
200 self.alloc_table(table, BUILTINS.dict)
201 }
202
203 pub fn alloc_set(&mut self, table: DictTable) -> Value {
204 self.alloc_table(table, BUILTINS.set)
205 }
206
207 fn alloc_table(&mut self, table: DictTable, cls: &'static Class) -> Value {
208 let h = self.alloc_raw(std::mem::size_of::<DictObj>(), cls);
209 unsafe { std::ptr::write(std::ptr::addr_of_mut!((*(h as *mut DictObj)).table), table) };
210 Value::from_ptr(h)
211 }
212
213 pub fn alloc_tuple(&mut self, items: &[Value]) -> Value {
214 let h = self.alloc_raw(TupleObj::size_for(items.len()), BUILTINS.tuple);
215 unsafe {
216 let t = &mut *(h as *mut TupleObj);
217 t.len = items.len();
218 std::ptr::copy_nonoverlapping(items.as_ptr(), t.elems_mut(), items.len());
219 }
220 Value::from_ptr(h)
221 }
222
223 pub fn alloc_closure(&mut self, func: u32, caps: &[Value]) -> Value {
224 let h = self.alloc_raw(ClosureObj::size_for(caps.len()), BUILTINS.closure);
225 unsafe {
226 let c = &mut *(h as *mut ClosureObj);
227 c.func = func;
228 c.ncaps = caps.len() as u32;
229 std::ptr::copy_nonoverlapping(caps.as_ptr(), c.caps_mut(), caps.len());
230 }
231 Value::from_ptr(h)
232 }
233
234 pub fn alloc_cell(&mut self, v: Value) -> Value {
235 let h = self.alloc_raw(std::mem::size_of::<CellObj>(), BUILTINS.cell);
236 unsafe { (*(h as *mut CellObj)).value = AtomicU64::new(v.0) };
237 Value::from_ptr(h)
238 }
239
240 pub fn alloc_string_builder(&mut self, s: String) -> Value {
241 let h = self.alloc_raw(std::mem::size_of::<StringBuilderObj>(), BUILTINS.string_builder);
242 unsafe { std::ptr::write((*(h as *mut StringBuilderObj)).buf.get(), s) };
243 Value::from_ptr(h)
244 }
245
246 pub fn alloc_mutex(&mut self, v: Value) -> Value {
247 let h = self.alloc_raw(std::mem::size_of::<MutexObj>(), BUILTINS.mutex);
248 unsafe {
249 std::ptr::write(&mut (*(h as *mut MutexObj)).raw, parking_lot::ReentrantMutex::new(()));
250 (*(h as *mut MutexObj)).value = AtomicU64::new(v.0);
251 (*h).set_state(ObjState::Shared);
252 }
253 Value::from_ptr(h)
254 }
255
256 pub fn alloc_atomic(&mut self, v: Value) -> Value {
257 let h = self.alloc_raw(std::mem::size_of::<AtomicObj>(), BUILTINS.atomic);
258 unsafe {
259 (*(h as *mut AtomicObj)).value = AtomicU64::new(v.0);
260 (*h).set_state(ObjState::Shared);
261 }
262 Value::from_ptr(h)
263 }
264
265 pub fn alloc_channel(&mut self, capacity: usize) -> Value {
266 let h = self.alloc_raw(std::mem::size_of::<ChannelObj>(), BUILTINS.channel);
267 let inner = Box::new(ChannelInner {
268 queue: parking_lot::Mutex::new(ChannelState { items: Default::default(), closed: false }),
269 not_empty: parking_lot::Condvar::new(),
270 not_full: parking_lot::Condvar::new(),
271 capacity,
272 waiters: parking_lot::Mutex::new(Vec::new()),
273 });
274 unsafe {
275 std::ptr::write(&mut (*(h as *mut ChannelObj)).inner, inner);
276 (*h).set_state(ObjState::Shared);
277 }
278 Value::from_ptr(h)
279 }
280
281 pub fn alloc_thread(&mut self) -> Value {
282 let h = self.alloc_raw(std::mem::size_of::<ThreadObj>(), BUILTINS.thread);
283 let inner = std::sync::Arc::new(ThreadInner {
284 outcome: parking_lot::Mutex::new(ThreadOutcome::Running),
285 done: parking_lot::Condvar::new(),
286 handle: parking_lot::Mutex::new(None),
287 tid: AtomicU64::new(0),
288 });
289 unsafe {
290 std::ptr::write(&mut (*(h as *mut ThreadObj)).inner, inner);
291 (*h).set_state(ObjState::Shared);
292 }
293 Value::from_ptr(h)
294 }
295
296 pub fn alloc_native(&mut self, data: Box<dyn NativeData>) -> Value {
297 let h = self.alloc_raw(std::mem::size_of::<NativeObj>(), BUILTINS.native);
298 unsafe { std::ptr::write(&mut (*(h as *mut NativeObj)).data, data) };
299 Value::from_ptr(h)
300 }
301
302 pub fn alloc_native_frozen(&mut self, data: Box<dyn NativeData>) -> Value {
305 let v = self.alloc_native(data);
306 unsafe { (*v.as_ptr()).set_state(ObjState::Frozen) };
308 v
309 }
310
311 pub fn alloc_range(&mut self, first: Value, last: Value, exclusive: bool) -> Value {
312 let v = self.alloc_instance(BUILTINS.range);
313 let r = unsafe { as_instance(v) };
314 r.set(0, first);
315 r.set(1, last);
316 r.set(2, Value::bool(exclusive));
317 unsafe { (*v.as_ptr()).set_state(ObjState::Frozen) };
318 v
319 }
320}
321
322pub fn note_external(bytes: usize) {
324 if bytes > 0 {
325 catallaxy_gc::stats::note_allocated(bytes);
326 }
327}
328
329pub struct HandleScope<'m> {
340 m: *mut Mutator,
341 base: usize,
342 _borrow: std::marker::PhantomData<&'m mut Mutator>,
343}
344
345#[derive(Copy, Clone)]
347pub struct Local<'s> {
348 index: usize,
349 _scope: std::marker::PhantomData<&'s ()>,
350}
351
352impl<'m> HandleScope<'m> {
353 pub fn new(m: &'m mut Mutator) -> Self {
354 let base = m.handles_len();
355 HandleScope { m, base, _borrow: std::marker::PhantomData }
356 }
357
358 pub unsafe fn detached(m: *mut Mutator) -> HandleScope<'static> {
364 let base = (*m).handles_len();
365 HandleScope { m, base, _borrow: std::marker::PhantomData }
366 }
367
368 pub fn root<'s>(&'s self, v: Value) -> Local<'s> {
370 let hs = unsafe { &mut *(*self.m).handles };
373 hs.push(v);
374 Local { index: hs.len() - 1, _scope: std::marker::PhantomData }
375 }
376
377 pub fn get(&self, h: Local<'_>) -> Value {
378 unsafe { (&(*(*self.m).handles))[h.index] }
379 }
380
381 pub fn set(&self, h: Local<'_>, v: Value) {
382 unsafe { (&mut (*(*self.m).handles))[h.index] = v }
383 }
384
385 #[allow(clippy::mut_from_ref)]
387 pub fn mutator(&self) -> &mut Mutator {
388 unsafe { &mut *self.m }
389 }
390}
391
392impl Drop for HandleScope<'_> {
393 fn drop(&mut self) {
394 unsafe { (*(*self.m).handles).truncate(self.base) };
395 }
396}