use std::sync::atomic::AtomicU64;
use std::sync::Arc;
unsafe impl Send for Mutator {}
use catallaxy_gc::{Header, LocalHeap, ObjState, ThreadRecord};
use num_bigint::BigInt;
use num_traits::ToPrimitive;
use crate::class::{Class, BUILTINS};
use crate::dict::DictTable;
use crate::object::*;
use crate::value::{fits_small, Value};
#[derive(Debug)]
pub enum Trap {
Panic(String),
Unwind { frame: u64, kind: u8, value: Value },
Exit(i32),
}
pub type RtResult<T> = Result<T, Trap>;
pub fn panic<T>(msg: impl Into<String>) -> RtResult<T> {
Err(Trap::Panic(msg.into()))
}
pub struct Mutator {
pub tid: u16,
pub rec: Arc<ThreadRecord>,
heap: *mut LocalHeap,
handles: *mut Vec<Value>,
}
#[derive(Copy, Clone)]
pub struct Handle(usize);
impl Mutator {
pub fn new(rec: Arc<ThreadRecord>) -> Self {
let heap = Box::into_raw(Box::new(LocalHeap::new(rec.tid)));
let handles = Box::into_raw(Box::new(Vec::new()));
Mutator { tid: rec.tid, rec, heap, handles }
}
pub fn heap_ptr(&self) -> *mut LocalHeap {
self.heap
}
pub fn release_pages(&mut self) {
unsafe { (*self.heap).release_pages() }
}
pub fn scope<R>(&mut self, f: impl FnOnce(&mut Mutator) -> R) -> R {
let base = self.handles_len();
let r = f(self);
unsafe { (*self.handles).truncate(base) };
r
}
pub fn root(&mut self, v: Value) -> Handle {
unsafe {
(*self.handles).push(v);
Handle((*self.handles).len() - 1)
}
}
pub fn get(&self, h: Handle) -> Value {
unsafe { (&(*self.handles))[h.0] }
}
pub fn set(&mut self, h: Handle, v: Value) {
unsafe { (&mut (*self.handles))[h.0] = v };
}
fn handles_len(&self) -> usize {
unsafe { (*self.handles).len() }
}
pub fn handle_values(&self) -> &[Value] {
unsafe { &*self.handles }
}
pub fn handles_ptr(&self) -> *const Vec<Value> {
self.handles
}
#[inline]
pub fn alloc_raw(&mut self, size: usize, cls: &'static Class) -> *mut Header {
unsafe { (*self.heap).alloc(size, &cls.gc as *const _) }
}
pub fn alloc_instance(&mut self, cls: &'static Class) -> Value {
let n = cls.nfields();
let h = self.alloc_raw(InstanceObj::size_for(n), cls);
let inst = unsafe { &*(h as *const InstanceObj) };
for i in 0..n {
inst.set(i, Value::NIL);
}
Value::from_ptr(h)
}
pub fn alloc_str(&mut self, s: &str) -> Value {
let h = self.alloc_raw(StrObj::BASE + s.len(), BUILTINS.string);
unsafe {
let o = &mut *(h as *mut StrObj);
o.len = s.len();
std::ptr::copy_nonoverlapping(s.as_ptr(), o.data_ptr(), s.len());
(*h).set_state(ObjState::Frozen);
}
Value::from_ptr(h)
}
pub fn int_from_big(&mut self, b: BigInt) -> Value {
if let Some(i) = b.to_i64() {
if fits_small(i) {
return Value::small_int(i);
}
}
let h = self.alloc_raw(std::mem::size_of::<BigIntObj>(), BUILTINS.bigint);
unsafe {
std::ptr::write(&mut (*(h as *mut BigIntObj)).value, b);
(*h).set_state(ObjState::Frozen);
}
Value::from_ptr(h)
}
#[inline]
pub fn int(&mut self, i: i64) -> Value {
if fits_small(i) {
Value::small_int(i)
} else {
self.int_from_big(BigInt::from(i))
}
}
pub fn alloc_list(&mut self, items: Vec<Value>) -> Value {
crate::note_external(items.capacity() * 8);
let h = self.alloc_raw(std::mem::size_of::<ListObj>(), BUILTINS.list);
unsafe { ListObj::init(h as *mut ListObj, items) };
Value::from_ptr(h)
}
pub fn alloc_dict(&mut self, table: DictTable) -> Value {
self.alloc_table(table, BUILTINS.dict)
}
pub fn alloc_set(&mut self, table: DictTable) -> Value {
self.alloc_table(table, BUILTINS.set)
}
fn alloc_table(&mut self, table: DictTable, cls: &'static Class) -> Value {
crate::note_external(table.heap_bytes());
let h = self.alloc_raw(std::mem::size_of::<DictObj>(), cls);
unsafe { std::ptr::write((*(h as *mut DictObj)).table.get(), table) };
Value::from_ptr(h)
}
pub fn alloc_tuple(&mut self, items: &[Value]) -> Value {
let h = self.alloc_raw(TupleObj::size_for(items.len()), BUILTINS.tuple);
unsafe {
let t = &mut *(h as *mut TupleObj);
t.len = items.len();
std::ptr::copy_nonoverlapping(items.as_ptr(), t.elems_mut(), items.len());
}
Value::from_ptr(h)
}
pub fn alloc_closure(&mut self, func: u32, caps: &[Value]) -> Value {
let h = self.alloc_raw(ClosureObj::size_for(caps.len()), BUILTINS.closure);
unsafe {
let c = &mut *(h as *mut ClosureObj);
c.func = func;
c.ncaps = caps.len() as u32;
std::ptr::copy_nonoverlapping(caps.as_ptr(), c.caps_mut(), caps.len());
}
Value::from_ptr(h)
}
pub fn alloc_cell(&mut self, v: Value) -> Value {
let h = self.alloc_raw(std::mem::size_of::<CellObj>(), BUILTINS.cell);
unsafe { (*(h as *mut CellObj)).value = AtomicU64::new(v.0) };
Value::from_ptr(h)
}
pub fn alloc_string_builder(&mut self, s: String) -> Value {
let h = self.alloc_raw(std::mem::size_of::<StringBuilderObj>(), BUILTINS.string_builder);
unsafe { std::ptr::write((*(h as *mut StringBuilderObj)).buf.get(), s) };
Value::from_ptr(h)
}
pub fn alloc_mutex(&mut self, v: Value) -> Value {
let h = self.alloc_raw(std::mem::size_of::<MutexObj>(), BUILTINS.mutex);
unsafe {
std::ptr::write(&mut (*(h as *mut MutexObj)).raw, parking_lot::ReentrantMutex::new(()));
(*(h as *mut MutexObj)).value = AtomicU64::new(v.0);
(*h).set_state(ObjState::Shared);
}
Value::from_ptr(h)
}
pub fn alloc_atomic(&mut self, v: Value) -> Value {
let h = self.alloc_raw(std::mem::size_of::<AtomicObj>(), BUILTINS.atomic);
unsafe {
(*(h as *mut AtomicObj)).value = AtomicU64::new(v.0);
(*h).set_state(ObjState::Shared);
}
Value::from_ptr(h)
}
pub fn alloc_channel(&mut self, capacity: usize) -> Value {
let h = self.alloc_raw(std::mem::size_of::<ChannelObj>(), BUILTINS.channel);
let inner = Box::new(ChannelInner {
queue: parking_lot::Mutex::new(ChannelState { items: Default::default(), closed: false }),
not_empty: parking_lot::Condvar::new(),
not_full: parking_lot::Condvar::new(),
capacity,
});
unsafe {
std::ptr::write(&mut (*(h as *mut ChannelObj)).inner, inner);
(*h).set_state(ObjState::Shared);
}
Value::from_ptr(h)
}
pub fn alloc_thread(&mut self) -> Value {
let h = self.alloc_raw(std::mem::size_of::<ThreadObj>(), BUILTINS.thread);
let inner = std::sync::Arc::new(ThreadInner {
outcome: parking_lot::Mutex::new(ThreadOutcome::Running),
done: parking_lot::Condvar::new(),
handle: parking_lot::Mutex::new(None),
tid: AtomicU64::new(0),
});
unsafe {
std::ptr::write(&mut (*(h as *mut ThreadObj)).inner, inner);
(*h).set_state(ObjState::Shared);
}
Value::from_ptr(h)
}
pub fn alloc_native(&mut self, data: Box<dyn NativeData>) -> Value {
let h = self.alloc_raw(std::mem::size_of::<NativeObj>(), BUILTINS.native);
unsafe { std::ptr::write(&mut (*(h as *mut NativeObj)).data, data) };
Value::from_ptr(h)
}
pub fn alloc_range(&mut self, first: Value, last: Value, exclusive: bool) -> Value {
let v = self.alloc_instance(BUILTINS.range);
let r = unsafe { as_instance(v) };
r.set(0, first);
r.set(1, last);
r.set(2, Value::bool(exclusive));
unsafe { (*v.as_ptr()).set_state(ObjState::Frozen) };
v
}
}
pub fn note_external(bytes: usize) {
if bytes > 0 {
catallaxy_gc::stats::note_allocated(bytes);
}
}
pub struct HandleScope<'m> {
m: *mut Mutator,
base: usize,
_borrow: std::marker::PhantomData<&'m mut Mutator>,
}
#[derive(Copy, Clone)]
pub struct Local<'s> {
index: usize,
_scope: std::marker::PhantomData<&'s ()>,
}
impl<'m> HandleScope<'m> {
pub fn new(m: &'m mut Mutator) -> Self {
let base = m.handles_len();
HandleScope { m, base, _borrow: std::marker::PhantomData }
}
pub unsafe fn detached(m: *mut Mutator) -> HandleScope<'static> {
let base = (*m).handles_len();
HandleScope { m, base, _borrow: std::marker::PhantomData }
}
pub fn root<'s>(&'s self, v: Value) -> Local<'s> {
let hs = unsafe { &mut *(*self.m).handles };
hs.push(v);
Local { index: hs.len() - 1, _scope: std::marker::PhantomData }
}
pub fn get(&self, h: Local<'_>) -> Value {
unsafe { (&(*(*self.m).handles))[h.index] }
}
pub fn set(&self, h: Local<'_>, v: Value) {
unsafe { (&mut (*(*self.m).handles))[h.index] = v }
}
#[allow(clippy::mut_from_ref)]
pub fn mutator(&self) -> &mut Mutator {
unsafe { &mut *self.m }
}
}
impl Drop for HandleScope<'_> {
fn drop(&mut self) {
unsafe { (*(*self.m).handles).truncate(self.base) };
}
}