use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::{AtomicData, HeapKind, LazyData, MutexData};
use shape_value::{KindedSlot, NativeKind, VMError};
use std::sync::Arc;
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
#[inline]
fn as_mutex(slot: &KindedSlot) -> Result<Arc<MutexData>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::Mutex)) {
return Err(type_error(format!(
"Mutex method receiver must be a Mutex (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("Mutex method receiver slot bits null"));
}
let arc = unsafe { Arc::<MutexData>::from_raw(bits as *const MutexData) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
#[inline]
fn as_atomic(slot: &KindedSlot) -> Result<Arc<AtomicData>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::Atomic)) {
return Err(type_error(format!(
"Atomic method receiver must be an Atomic (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("Atomic method receiver slot bits null"));
}
let arc = unsafe { Arc::<AtomicData>::from_raw(bits as *const AtomicData) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
#[inline]
fn as_lazy(slot: &KindedSlot) -> Result<Arc<LazyData>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::Lazy)) {
return Err(type_error(format!(
"Lazy method receiver must be a Lazy (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("Lazy method receiver slot bits null"));
}
let arc = unsafe { Arc::<LazyData>::from_raw(bits as *const LazyData) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
#[inline]
fn arg_as_i64(slot: &KindedSlot, method: &str, idx: usize) -> Result<i64, VMError> {
slot.as_i64().ok_or_else(|| {
type_error(format!(
"{}: argument {} must be an int (got kind {:?})",
method, idx, slot.kind
))
})
}
pub fn v2_mutex_lock(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Mutex.lock() takes no arguments"));
}
let m: Arc<MutexData> = as_mutex(&args[0])?;
m.lock();
Ok(KindedSlot::from_mutex(m))
}
pub fn v2_mutex_try_lock(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Mutex.try_lock() takes no arguments"));
}
let m: Arc<MutexData> = as_mutex(&args[0])?;
let ok = m.try_lock();
Ok(KindedSlot::from_bool(ok))
}
pub fn v2_mutex_get(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Mutex.get() takes no arguments"));
}
let m: Arc<MutexData> = as_mutex(&args[0])?;
Ok(m.get())
}
pub fn v2_mutex_set(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Mutex.set() requires exactly 1 argument (value)",
));
}
let m: Arc<MutexData> = as_mutex(&args[0])?;
let new_value = args[1].clone();
m.set(new_value);
Ok(KindedSlot::from_mutex(m))
}
pub fn v2_atomic_load(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Atomic.load() takes no arguments"));
}
let a: Arc<AtomicData> = as_atomic(&args[0])?;
let v = a.load();
Ok(KindedSlot::from_int(v))
}
pub fn v2_atomic_store(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Atomic.store() requires exactly 1 argument (value)",
));
}
let a: Arc<AtomicData> = as_atomic(&args[0])?;
let v = arg_as_i64(&args[1], "Atomic.store", 1)?;
a.store(v);
Ok(KindedSlot::from_atomic(a))
}
pub fn v2_atomic_fetch_add(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Atomic.fetch_add() requires exactly 1 argument (delta)",
));
}
let a: Arc<AtomicData> = as_atomic(&args[0])?;
let delta = arg_as_i64(&args[1], "Atomic.fetch_add", 1)?;
let prior = a.fetch_add(delta);
Ok(KindedSlot::from_int(prior))
}
pub fn v2_atomic_fetch_sub(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"Atomic.fetch_sub() requires exactly 1 argument (delta)",
));
}
let a: Arc<AtomicData> = as_atomic(&args[0])?;
let delta = arg_as_i64(&args[1], "Atomic.fetch_sub", 1)?;
let prior = a.fetch_sub(delta);
Ok(KindedSlot::from_int(prior))
}
pub fn v2_atomic_compare_exchange(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 3 {
return Err(type_error(
"Atomic.compare_exchange() requires exactly 2 arguments \
(expected, new)",
));
}
let a: Arc<AtomicData> = as_atomic(&args[0])?;
let expected = arg_as_i64(&args[1], "Atomic.compare_exchange", 1)?;
let new_v = arg_as_i64(&args[2], "Atomic.compare_exchange", 2)?;
let prior = a.compare_exchange(expected, new_v);
Ok(KindedSlot::from_int(prior))
}
pub fn v2_lazy_get(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("Lazy.get() takes no arguments"));
}
let l: Arc<LazyData> = as_lazy(&args[0])?;
if let Some(cached) = l.cached() {
return Ok(cached);
}
let initializer = l.take_initializer().ok_or_else(|| {
type_error(
"Lazy.get(): initializer already taken but value not \
cached — concurrent get() race (single-threaded landing \
treats as defensive bug)",
)
})?;
let result = vm.call_value_immediate_nb(&initializer, &[], ctx)?;
let cached_copy = result.clone();
l.store_result(result);
Ok(cached_copy)
}
pub fn v2_lazy_is_initialized(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error(
"Lazy.is_initialized() takes no arguments",
));
}
let l: Arc<LazyData> = as_lazy(&args[0])?;
Ok(KindedSlot::from_bool(l.is_initialized()))
}