use crate::executor::v2_handlers::v2_array_detect::{
as_v2_typed_array, clone_array, pop_element, push_element, read_element, reverse_array,
write_element, V2TypedArrayView,
};
use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::v2::heap_header::HEAP_KIND_V2_TYPED_ARRAY;
use shape_value::v2::typed_array::{TypedArray, ELEM_TYPE_TYPED_OBJECT};
use shape_value::{HeapKind, KindedSlot, NativeKind, TypedObjectStorage, ValueSlot, VMError};
#[inline]
fn extract_typed_array_view(slot: &KindedSlot) -> Option<V2TypedArrayView> {
if slot.kind != NativeKind::Ptr(HeapKind::TypedArray) {
return None;
}
as_v2_typed_array(slot.slot.raw(), slot.kind)
}
#[inline]
fn pair_to_slot((bits, kind): (u64, NativeKind)) -> KindedSlot {
KindedSlot::new(ValueSlot::from_raw(bits), kind)
}
pub(crate) fn handle_len_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.len: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
Ok(KindedSlot::from_int(view.len as i64))
}
pub(crate) fn handle_is_empty_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.isEmpty: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
Ok(KindedSlot::from_bool(view.len == 0))
}
pub(crate) fn handle_first_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.first: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
if view.len == 0 {
return Ok(KindedSlot::none());
}
match read_element(&view, 0) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Ok(KindedSlot::none()),
}
}
pub(crate) fn handle_last_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.last: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
if view.len == 0 {
return Ok(KindedSlot::none());
}
match read_element(&view, view.len - 1) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Ok(KindedSlot::none()),
}
}
pub(crate) fn handle_reverse_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.reverse: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
let new_ptr = reverse_array(&view);
Ok(KindedSlot::new(
ValueSlot::from_u64(new_ptr as usize as u64),
NativeKind::Ptr(HeapKind::TypedArray),
))
}
pub(crate) fn handle_push_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.push expects 1 argument".into(),
));
}
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.push: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
let bits = args[1].slot.raw();
let kind = args[1].kind;
push_element(&view, bits, kind)
.map_err(|e| VMError::RuntimeError(format!("Array.push: {}", e)))?;
let post = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(
"Array.push: receiver re-detection failed after push".into(),
)
})?;
Ok(KindedSlot::from_int(post.len as i64))
}
pub(crate) fn handle_pop_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.pop: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
match pop_element(&view) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Ok(KindedSlot::none()),
}
}
pub(crate) fn handle_zip_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.zip expects 1 argument".into(),
));
}
let a = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.zip: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
let b = extract_typed_array_view(&args[1]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.zip: expected v2 TypedArray argument, got kind {:?}",
args[1].kind
))
})?;
let out_len = a.len.min(b.len);
let new_arr = TypedArray::<*const TypedObjectStorage>::with_capacity(out_len);
let dst_data = unsafe { (*new_arr).data };
for i in 0..out_len {
let (a_bits, a_kind) = read_element(&a, i).ok_or_else(|| {
VMError::RuntimeError(format!("Array.zip: failed to read receiver element {}", i))
})?;
let (b_bits, b_kind) = read_element(&b, i).ok_or_else(|| {
VMError::RuntimeError(format!("Array.zip: failed to read argument element {}", i))
})?;
let a_slot = KindedSlot::new(ValueSlot::from_raw(a_bits), a_kind);
let b_slot = KindedSlot::new(ValueSlot::from_raw(b_bits), b_kind);
let pair = shape_runtime::type_schema::typed_object_from_pairs(&[
("_0", a_slot),
("_1", b_slot),
]);
if pair.kind != NativeKind::Ptr(HeapKind::TypedObject) {
return Err(VMError::RuntimeError(format!(
"Array.zip: typed_object_from_pairs returned unexpected kind {:?}",
pair.kind
)));
}
let pair_ptr = pair.slot.raw() as usize as *const TypedObjectStorage;
std::mem::forget(pair);
unsafe {
*dst_data.add(i as usize) = pair_ptr;
}
}
unsafe {
(*new_arr).len = out_len;
}
let p = new_arr as *mut u8;
unsafe {
crate::executor::v2_handlers::v2_array_detect::stamp_elem_type(
p,
ELEM_TYPE_TYPED_OBJECT,
);
}
debug_assert_eq!(
unsafe { (*(p as *const shape_value::HeapHeader)).kind },
HEAP_KIND_V2_TYPED_ARRAY,
"Array.zip: freshly-allocated TypedArray header kind mismatch"
);
Ok(KindedSlot::new(
ValueSlot::from_u64(p as usize as u64),
NativeKind::Ptr(HeapKind::TypedArray),
))
}
pub(crate) fn handle_clone_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.clone: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
let new_ptr = clone_array(&view);
Ok(KindedSlot::new(
ValueSlot::from_u64(new_ptr as usize as u64),
NativeKind::Ptr(HeapKind::TypedArray),
))
}
pub(crate) fn handle_get_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.get expects 1 argument".into(),
));
}
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.get: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
let idx = args[1].as_i64().ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.get: index must be an integer, got kind {:?}",
args[1].kind
))
})?;
if idx < 0 || (idx as u32) >= view.len {
return Err(VMError::RuntimeError(format!(
"Array.get: index {} out of bounds (len={})",
idx, view.len
)));
}
match read_element(&view, idx as u32) {
Some(pair) => Ok(pair_to_slot(pair)),
None => Err(VMError::RuntimeError(
"Array.get: read_element returned None".into(),
)),
}
}
pub(crate) fn handle_set_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 3 {
return Err(VMError::RuntimeError(
"Array.set expects 2 arguments".into(),
));
}
let view = extract_typed_array_view(&args[0]).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.set: expected v2 TypedArray receiver, got kind {:?}",
args[0].kind
))
})?;
let idx = args[1].as_i64().ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.set: index must be an integer, got kind {:?}",
args[1].kind
))
})?;
if idx < 0 || (idx as u32) >= view.len {
return Err(VMError::RuntimeError(format!(
"Array.set: index {} out of bounds (len={})",
idx, view.len
)));
}
let bits = args[2].slot.raw();
let kind = args[2].kind;
write_element(&view, idx as u32, bits, kind)
.map_err(|e| VMError::RuntimeError(format!("Array.set: {}", e)))?;
Ok(KindedSlot::new(
ValueSlot::from_u64(view.ptr as usize as u64),
NativeKind::Ptr(HeapKind::TypedArray),
))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::executor::v2_handlers::v2_array_detect::{
as_v2_typed_array, V2ElemType,
};
#[test]
fn kind_generic_len_reads_header() {
use crate::executor::v2_handlers::v2_array_detect::ELEM_TYPE_I64;
use shape_value::v2::typed_array::TypedArray;
let arr_ptr = TypedArray::<i64>::with_capacity(3) as *mut u8;
unsafe {
crate::executor::v2_handlers::v2_array_detect::stamp_elem_type(
arr_ptr,
ELEM_TYPE_I64,
);
let arr = arr_ptr as *mut TypedArray<i64>;
TypedArray::<i64>::push(arr, 10);
TypedArray::<i64>::push(arr, 20);
TypedArray::<i64>::push(arr, 30);
}
let view = as_v2_typed_array(
arr_ptr as u64,
NativeKind::Ptr(HeapKind::TypedArray),
)
.expect("view");
assert_eq!(view.elem_type, V2ElemType::I64);
assert_eq!(view.len, 3);
let len_result = view.len as i64;
assert_eq!(len_result, 3);
unsafe {
TypedArray::<i64>::drop_array(arr_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn kind_generic_is_empty_false_when_nonempty() {
use crate::executor::v2_handlers::v2_array_detect::ELEM_TYPE_BOOL;
use shape_value::v2::typed_array::TypedArray;
let arr_ptr = TypedArray::<u8>::with_capacity(2) as *mut u8;
unsafe {
crate::executor::v2_handlers::v2_array_detect::stamp_elem_type(
arr_ptr,
ELEM_TYPE_BOOL,
);
let arr = arr_ptr as *mut TypedArray<u8>;
TypedArray::<u8>::push(arr, 1);
TypedArray::<u8>::push(arr, 0);
}
let view = as_v2_typed_array(
arr_ptr as u64,
NativeKind::Ptr(HeapKind::TypedArray),
)
.expect("view");
assert_eq!(view.elem_type, V2ElemType::Bool);
assert_eq!(view.len == 0, false);
unsafe {
TypedArray::<u8>::drop_array(arr_ptr as *mut TypedArray<u8>);
}
}
#[test]
fn kind_generic_last_reads_element_at_len_minus_1() {
use crate::executor::v2_handlers::v2_array_detect::ELEM_TYPE_BOOL;
use shape_value::v2::typed_array::TypedArray;
let arr_ptr = TypedArray::<u8>::with_capacity(3) as *mut u8;
unsafe {
crate::executor::v2_handlers::v2_array_detect::stamp_elem_type(
arr_ptr,
ELEM_TYPE_BOOL,
);
let arr = arr_ptr as *mut TypedArray<u8>;
TypedArray::<u8>::push(arr, 1); TypedArray::<u8>::push(arr, 1); TypedArray::<u8>::push(arr, 0); }
let view = as_v2_typed_array(
arr_ptr as u64,
NativeKind::Ptr(HeapKind::TypedArray),
)
.expect("view");
let last = read_element(&view, view.len - 1).expect("read");
assert_eq!(last.0, 0); assert_eq!(last.1, NativeKind::Bool);
unsafe {
TypedArray::<u8>::drop_array(arr_ptr as *mut TypedArray<u8>);
}
}
}