use crate::executor::v2_handlers::v2_array_detect::{
as_v2_typed_array, read_element, V2TypedArrayView,
};
use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::{HeapKind, KindedSlot, NativeKind, ValueSlot, VMError};
#[inline]
fn extract_view(op: &'static str, slot: &KindedSlot) -> Result<V2TypedArrayView, VMError> {
if slot.kind != NativeKind::Ptr(HeapKind::TypedArray) {
return Err(VMError::RuntimeError(format!(
"Array.{op}: expected v2 TypedArray receiver, got kind {:?}",
slot.kind
)));
}
as_v2_typed_array(slot.slot.raw(), slot.kind).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.{op}: receiver bits failed v2 TypedArray detection (kind {:?})",
slot.kind
))
})
}
#[inline]
fn pair_to_slot((bits, kind): (u64, NativeKind)) -> KindedSlot {
KindedSlot::new(ValueSlot::from_raw(bits), kind)
}
#[inline]
fn slot_truthy(slot: &KindedSlot) -> bool {
let bits = slot.slot.raw();
match slot.kind {
NativeKind::Bool => bits != 0,
NativeKind::Float64 => f64::from_bits(bits) != 0.0,
NativeKind::Int8
| NativeKind::Int16
| NativeKind::Int32
| NativeKind::Int64
| NativeKind::IntSize
| NativeKind::UInt8
| NativeKind::UInt16
| NativeKind::UInt32
| NativeKind::UInt64
| NativeKind::UIntSize => bits != 0,
NativeKind::NullableFloat64
| NativeKind::NullableInt8
| NativeKind::NullableInt16
| NativeKind::NullableInt32
| NativeKind::NullableInt64
| NativeKind::NullableIntSize
| NativeKind::NullableUInt8
| NativeKind::NullableUInt16
| NativeKind::NullableUInt32
| NativeKind::NullableUInt64
| NativeKind::NullableUIntSize => bits != 0,
NativeKind::Float32 => f32::from_bits(bits as u32) != 0.0,
NativeKind::Char => bits != 0,
NativeKind::StringV2 | NativeKind::DecimalV2 => bits != 0,
NativeKind::String | NativeKind::Ptr(_) => bits != 0,
NativeKind::Null => false,
}
}
pub(crate) fn handle_sum_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_view("sum", &args[0])?;
match crate::executor::v2_handlers::v2_array_detect::sum_elements(&view) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Err(VMError::RuntimeError(format!(
"Array.sum: not defined for element kind {:?}",
view.elem_type
))),
}
}
pub(crate) fn handle_avg_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_view("avg", &args[0])?;
match crate::executor::v2_handlers::v2_array_detect::avg_elements(&view) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Err(VMError::RuntimeError(format!(
"Array.avg: not defined for element kind {:?}",
view.elem_type
))),
}
}
pub(crate) fn handle_min_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_view("min", &args[0])?;
match crate::executor::v2_handlers::v2_array_detect::min_elements(&view) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Err(VMError::RuntimeError(format!(
"Array.min: not defined for element kind {:?}",
view.elem_type
))),
}
}
pub(crate) fn handle_max_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_view("max", &args[0])?;
match crate::executor::v2_handlers::v2_array_detect::max_elements(&view) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Err(VMError::RuntimeError(format!(
"Array.max: not defined for element kind {:?}",
view.elem_type
))),
}
}
pub(crate) fn handle_count_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_view("count", &args[0])?;
if args.len() < 2 {
return Ok(KindedSlot::from_int(view.len as i64));
}
let closure = &args[1];
if closure.kind != NativeKind::Ptr(HeapKind::Closure) {
return Err(VMError::RuntimeError(format!(
"Array.count: predicate must be a closure, got kind {:?}",
closure.kind
)));
}
let mut count: i64 = 0;
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.count: read_element({i}) returned None for element kind {:?}",
view.elem_type
))
})?;
let elem_slot = KindedSlot::new(ValueSlot::from_raw(bits), kind);
let result = vm.call_value_immediate_nb(closure, &[elem_slot], ctx.as_deref_mut())?;
if slot_truthy(&result) {
count += 1;
}
}
Ok(KindedSlot::from_int(count))
}
pub(crate) fn handle_reduce_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 3 {
return Err(VMError::RuntimeError(
"Array.reduce expects 2 arguments: (fn, init)".into(),
));
}
let view = extract_view("reduce", &args[0])?;
let closure = &args[1];
if closure.kind != NativeKind::Ptr(HeapKind::Closure) {
return Err(VMError::RuntimeError(format!(
"Array.reduce: first argument must be a closure, got kind {:?}",
closure.kind
)));
}
let mut acc = args[2].clone();
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.reduce: read_element({i}) returned None for element kind {:?}",
view.elem_type
))
})?;
let elem_slot = KindedSlot::new(ValueSlot::from_raw(bits), kind);
acc = vm.call_value_immediate_nb(closure, &[acc, elem_slot], ctx.as_deref_mut())?;
}
Ok(acc)
}