use crate::executor::v2_handlers::v2_array_detect::{
allocate_empty_typed_array, as_v2_typed_array, contains_element,
native_kind_to_v2_elem_type, position_of, push_element, read_element, V2ElemType,
V2TypedArrayView,
};
use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::HeapKind;
use shape_value::v2::typed_array::release_v2_typed_array;
use shape_value::{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 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,
}
}
#[inline]
fn require_closure(op: &str, arg: &KindedSlot) -> Result<(), VMError> {
if arg.kind != NativeKind::Ptr(HeapKind::Closure) {
Err(VMError::RuntimeError(format!(
"Array.{}: predicate must be a closure, got kind {:?}",
op, arg.kind
)))
} else {
Ok(())
}
}
#[cold]
#[inline(never)]
fn j5_builder_surface(op: &'static str, args: &[KindedSlot]) -> VMError {
let receiver_kind = if args.is_empty() {
"<no args>".to_string()
} else {
format!("{:?}", args[0].kind)
};
VMError::NotImplemented(format!(
"Array.{op}: SURFACE — J.5c territory. \
Per-kind value-equality comparison (especially for heap-element \
kinds `StringV2` / `DecimalV2` / `TypedObject`) requires a \
`v2_array_detect::position_of` / `contains_element` primitive \
that doesn't exist at HEAD. NO Bool-default fallback (ADR-006 \
§2.7.14). Receiver kind: {kind}.",
op = op,
kind = receiver_kind,
))
}
#[inline]
fn wrap_typed_array_result(ptr: *mut u8) -> KindedSlot {
KindedSlot::new(
ValueSlot::from_raw(ptr as usize as u64),
NativeKind::Ptr(HeapKind::TypedArray),
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum FilterMode {
All,
TakePrefix,
SkipPrefix,
}
fn run_filter_builder(
op: &'static str,
mode: FilterMode,
vm: &mut VirtualMachine,
view: &V2TypedArrayView,
closure: &KindedSlot,
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let out_ptr = allocate_empty_typed_array(view.elem_type, view.len);
let out_view = match as_v2_typed_array(
out_ptr as usize as u64,
NativeKind::Ptr(HeapKind::TypedArray),
) {
Some(v) => v,
None => {
unsafe { release_v2_typed_array(out_ptr) };
return Err(VMError::RuntimeError(format!(
"Array.{op}: failed to re-detect freshly-allocated TypedArray<{:?}>",
view.elem_type
)));
}
};
let mut skipping = matches!(mode, FilterMode::SkipPrefix);
for i in 0..view.len {
let (bits, kind) = match read_element(view, i) {
Some(pair) => pair,
None => {
unsafe { release_v2_typed_array(out_ptr) };
return Err(VMError::RuntimeError(format!(
"Array.{op}: read_element({i}) returned None for element kind {:?}",
view.elem_type
)));
}
};
let elem_slot = KindedSlot::new(ValueSlot::from_raw(bits), kind);
let elem_for_pred = elem_slot.clone();
let pred = match vm.call_value_immediate_nb(closure, &[elem_for_pred], ctx.as_deref_mut()) {
Ok(p) => p,
Err(e) => {
unsafe { release_v2_typed_array(out_ptr) };
return Err(e);
}
};
let truthy = slot_truthy(&pred);
let include = match mode {
FilterMode::All => truthy,
FilterMode::TakePrefix => {
if !truthy {
break;
}
true
}
FilterMode::SkipPrefix => {
if skipping {
if truthy {
continue;
}
skipping = false;
}
true
}
};
if include {
let push_bits = elem_slot.slot.raw();
let push_kind = elem_slot.kind;
if let Err(msg) = push_element(&out_view, push_bits, push_kind) {
unsafe { release_v2_typed_array(out_ptr) };
return Err(VMError::RuntimeError(format!(
"Array.{op}: push_element failed at index {i}: {msg}"
)));
}
std::mem::forget(elem_slot);
}
}
Ok(wrap_typed_array_result(out_ptr))
}
fn run_select_builder(
vm: &mut VirtualMachine,
view: &V2TypedArrayView,
closure: &KindedSlot,
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if view.len == 0 {
let out_ptr = allocate_empty_typed_array(view.elem_type, 0);
return Ok(wrap_typed_array_result(out_ptr));
}
let mut results: Vec<KindedSlot> = Vec::with_capacity(view.len as usize);
let mut established_kind: Option<NativeKind> = None;
for i in 0..view.len {
let (bits, kind) = read_element(view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.select: 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())?;
match established_kind {
None => {
established_kind = Some(result.kind);
}
Some(expected) if expected != result.kind => {
return Err(VMError::RuntimeError(format!(
"Array.select: closure-return kind mismatch at index {i}: \
expected {expected:?} (established by index 0), got {got:?}. \
HOF builders require a single output element kind per \
CLAUDE.md \"No `any` type\" rule + D3 binding (no coercion).",
expected = expected,
got = result.kind,
i = i,
)));
}
_ => {}
}
results.push(result);
}
let result_kind = established_kind.expect("non-empty input → established_kind is Some");
let elem_type = native_kind_to_v2_elem_type(result_kind).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.select: closure-return kind {result_kind:?} has no `TypedArray<T>` \
carrier monomorphization (no element-type stamp). Supported result \
kinds: Float64/Int64/Int32/Int16/Int8/UInt32/UInt16/UInt8/Float32/Char/\
Bool/StringV2/DecimalV2/Ptr(TypedObject). J.5d / future tuple-carrier \
territory for other kinds."
))
})?;
let out_ptr = allocate_empty_typed_array(elem_type, view.len);
let out_view = match as_v2_typed_array(
out_ptr as usize as u64,
NativeKind::Ptr(HeapKind::TypedArray),
) {
Some(v) => v,
None => {
unsafe { release_v2_typed_array(out_ptr) };
return Err(VMError::RuntimeError(format!(
"Array.select: failed to re-detect freshly-allocated TypedArray<{elem_type:?}>"
)));
}
};
for (i, slot) in results.into_iter().enumerate() {
let push_bits = slot.slot.raw();
let push_kind = slot.kind;
if let Err(msg) = push_element(&out_view, push_bits, push_kind) {
unsafe { release_v2_typed_array(out_ptr) };
return Err(VMError::RuntimeError(format!(
"Array.select: push_element failed at index {i}: {msg}"
)));
}
std::mem::forget(slot);
}
Ok(wrap_typed_array_result(out_ptr))
}
pub(crate) fn handle_where_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.where expects 1 argument: (predicate)".into(),
));
}
require_closure("where", &args[1])?;
let view = extract_view("where", &args[0])?;
let closure = &args[1];
run_filter_builder("where", FilterMode::All, vm, &view, closure, ctx)
}
pub(crate) fn handle_select_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.select expects 1 argument: (transform)".into(),
));
}
require_closure("select", &args[1])?;
let view = extract_view("select", &args[0])?;
let closure = &args[1];
run_select_builder(vm, &view, closure, ctx)
}
pub(crate) fn handle_find_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.find expects 1 argument: (predicate)".into(),
));
}
require_closure("find", &args[1])?;
let view = extract_view("find", &args[0])?;
let closure = &args[1];
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.find: read_element({i}) returned None for element kind {:?}",
view.elem_type
))
})?;
let elem_slot = KindedSlot::new(ValueSlot::from_raw(bits), kind);
let elem_for_pred = elem_slot.clone();
let result = vm.call_value_immediate_nb(closure, &[elem_for_pred], ctx.as_deref_mut())?;
if slot_truthy(&result) {
return Ok(elem_slot);
}
}
Ok(KindedSlot::none())
}
pub(crate) fn handle_find_index_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.findIndex expects 1 argument: (predicate)".into(),
));
}
require_closure("findIndex", &args[1])?;
let view = extract_view("findIndex", &args[0])?;
let closure = &args[1];
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.findIndex: 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) {
return Ok(KindedSlot::from_int(i as i64));
}
}
Ok(KindedSlot::from_int(-1))
}
pub(crate) fn handle_index_of_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.indexOf expects 1 argument: (value)".into(),
));
}
let view = extract_view("indexOf", &args[0])?;
let needle = &args[1];
if !needle_kind_matches(view.elem_type, needle.kind) {
return Ok(KindedSlot::from_int(-1));
}
let needle_bits = needle.slot.raw();
match position_of(&view, needle_bits) {
Some(i) => Ok(KindedSlot::from_int(i as i64)),
None => Ok(KindedSlot::from_int(-1)),
}
}
pub(crate) fn handle_includes_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.includes expects 1 argument: (value)".into(),
));
}
let view = extract_view("includes", &args[0])?;
let needle = &args[1];
if !needle_kind_matches(view.elem_type, needle.kind) {
return Ok(KindedSlot::from_bool(false));
}
let needle_bits = needle.slot.raw();
Ok(KindedSlot::from_bool(contains_element(&view, needle_bits)))
}
#[inline]
fn needle_kind_matches(elem_type: V2ElemType, needle_kind: NativeKind) -> bool {
match (elem_type, needle_kind) {
(V2ElemType::F64, NativeKind::Float64) => true,
(V2ElemType::I64, NativeKind::Int64) => true,
(V2ElemType::I32, NativeKind::Int32) => true,
(V2ElemType::Bool, NativeKind::Bool) => true,
(V2ElemType::I8, NativeKind::Int8) => true,
(V2ElemType::U8, NativeKind::UInt8) => true,
(V2ElemType::I16, NativeKind::Int16) => true,
(V2ElemType::U16, NativeKind::UInt16) => true,
(V2ElemType::U32, NativeKind::UInt32) => true,
(V2ElemType::F32, NativeKind::Float32) => true,
(V2ElemType::Char, NativeKind::Char) => true,
(V2ElemType::String, NativeKind::StringV2) => true,
(V2ElemType::Decimal, NativeKind::DecimalV2) => true,
(V2ElemType::TypedObject, NativeKind::Ptr(HeapKind::TypedObject)) => true,
_ => false,
}
}
pub(crate) fn handle_some_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.some expects 1 argument: (predicate)".into(),
));
}
require_closure("some", &args[1])?;
let view = extract_view("some", &args[0])?;
let closure = &args[1];
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.some: 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) {
return Ok(KindedSlot::from_bool(true));
}
}
Ok(KindedSlot::from_bool(false))
}
pub(crate) fn handle_every_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.every expects 1 argument: (predicate)".into(),
));
}
require_closure("every", &args[1])?;
let view = extract_view("every", &args[0])?;
let closure = &args[1];
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.every: 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) {
return Ok(KindedSlot::from_bool(false));
}
}
Ok(KindedSlot::from_bool(true))
}
pub(crate) fn handle_any_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
handle_some_v2(vm, args, ctx)
}
pub(crate) fn handle_all_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
handle_every_v2(vm, args, ctx)
}
pub(crate) fn handle_single_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.single expects 1 argument: (predicate)".into(),
));
}
require_closure("single", &args[1])?;
let view = extract_view("single", &args[0])?;
let closure = &args[1];
let mut found: Option<KindedSlot> = None;
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.single: read_element({i}) returned None for element kind {:?}",
view.elem_type
))
})?;
let elem_slot = KindedSlot::new(ValueSlot::from_raw(bits), kind);
let elem_for_pred = elem_slot.clone();
let result = vm.call_value_immediate_nb(closure, &[elem_for_pred], ctx.as_deref_mut())?;
if slot_truthy(&result) {
if found.is_some() {
return Err(VMError::RuntimeError(
"Array.single: more than one element matched the predicate".into(),
));
}
found = Some(elem_slot);
}
}
found.ok_or_else(|| {
VMError::RuntimeError(
"Array.single: no element matched the predicate".into(),
)
})
}
pub(crate) fn handle_take_while_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.takeWhile expects 1 argument: (predicate)".into(),
));
}
require_closure("takeWhile", &args[1])?;
let view = extract_view("takeWhile", &args[0])?;
let closure = &args[1];
run_filter_builder("takeWhile", FilterMode::TakePrefix, vm, &view, closure, ctx)
}
pub(crate) fn handle_skip_while_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.skipWhile expects 1 argument: (predicate)".into(),
));
}
require_closure("skipWhile", &args[1])?;
let view = extract_view("skipWhile", &args[0])?;
let closure = &args[1];
run_filter_builder("skipWhile", FilterMode::SkipPrefix, vm, &view, closure, ctx)
}
pub(crate) fn handle_for_each_v2(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(VMError::RuntimeError(
"Array.forEach expects 1 argument: (closure)".into(),
));
}
require_closure("forEach", &args[1])?;
let view = extract_view("forEach", &args[0])?;
let closure = &args[1];
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Array.forEach: read_element({i}) returned None for element kind {:?}",
view.elem_type
))
})?;
let elem_slot = KindedSlot::new(ValueSlot::from_raw(bits), kind);
let _ = vm.call_value_immediate_nb(closure, &[elem_slot], ctx.as_deref_mut())?;
}
Ok(KindedSlot::none())
}