use crate::{
bytecode::{Instruction, Operand},
executor::vm_impl::stack::drop_with_kind,
executor::VirtualMachine,
};
use rust_decimal::prelude::ToPrimitive;
use shape_runtime::type_schema::FieldType;
use shape_value::{
HeapKind, KindedSlot, NativeKind, TypedObjectStorage, ValueSlot, VMError,
};
use std::sync::Arc;
fn field_type_to_int_width(ft: &FieldType) -> Option<shape_ast::IntWidth> {
match ft {
FieldType::I8 => Some(shape_ast::IntWidth::I8),
FieldType::U8 => Some(shape_ast::IntWidth::U8),
FieldType::I16 => Some(shape_ast::IntWidth::I16),
FieldType::U16 => Some(shape_ast::IntWidth::U16),
FieldType::I32 => Some(shape_ast::IntWidth::I32),
FieldType::U32 => Some(shape_ast::IntWidth::U32),
FieldType::U64 => Some(shape_ast::IntWidth::U64),
_ => None,
}
}
#[cold]
#[inline(never)]
fn ckpt5_surface(op: &'static str, count: usize) -> VMError {
VMError::NotImplemented(format!(
"{op}({count}): SURFACE — V3-S5 ckpt-5 consumer-cascade tier 3 \
surface. The deleted typed-array-data enum + `Buf<T>` / \
aligned-typed-buf wrapper layer + outer `HeapValue::TypedArray(\
Arc<_>)` arm + `HeapKind::TypedArray=8` ordinal \
DELETED across V3-S5 ckpt-1..ckpt-4 per W12-typed-array-data-\
deletion audit §3.5 + §3.6 + ADR-006 §2.7.24 Q25.A SUPERSEDED. \
Post-deletion target is per-T v2-raw `TypedArray<T>` flat-struct \
monomorphization per audit §A.3 + §3.1 scalar recipe + §2.2 \
heap-element variants. Construction-site rebuild lands at ckpt-6 \
STRICT close after ckpt-5-prime (wire/marshal/json + 4-table \
lockstep) + ckpt-5-prime² (storage migration + 10 intrinsics \
marshal-parameter migration). REFUSED ON SIGHT: TypedArrayData \
resurrection under any rename (Refusal #1).",
op = op,
count = count,
))
}
impl VirtualMachine {
pub(in crate::executor) fn op_new_typed_object(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
let (schema_id, field_count) = match instruction.operand {
Some(Operand::TypedObjectAlloc {
schema_id,
field_count,
}) => (schema_id, field_count),
_ => return Err(VMError::InvalidOperand),
};
let field_types: Option<Vec<FieldType>> = self
.lookup_schema(schema_id as u32)
.map(|schema| schema.fields.iter().map(|f| f.field_type.clone()).collect());
let mut popped: Vec<(u64, NativeKind)> = Vec::with_capacity(field_count as usize);
for _ in 0..field_count {
match self.pop_kinded() {
Ok(pair) => popped.push(pair),
Err(e) => {
for (b, k) in popped.drain(..) {
drop_with_kind(b, k);
}
return Err(e);
}
}
}
popped.reverse();
let mut slots: Vec<ValueSlot> = Vec::with_capacity(field_count as usize);
let mut field_kinds: Vec<NativeKind> = Vec::with_capacity(field_count as usize);
let mut heap_mask: u64 = 0;
for (i, (bits, kind)) in popped.iter().enumerate() {
let field_type = field_types.as_ref().and_then(|types| types.get(i));
let (slot, is_heap, resolved_kind) =
kinded_to_slot(*bits, *kind, field_type);
if is_heap {
heap_mask |= 1u64 << i;
}
slots.push(slot);
field_kinds.push(resolved_kind);
}
let ptr = TypedObjectStorage::_new(
schema_id as u64,
slots.into_boxed_slice(),
heap_mask,
Arc::from(field_kinds.into_boxed_slice()),
);
let bits = ptr as u64;
self.push_kinded(bits, NativeKind::Ptr(HeapKind::TypedObject))
}
pub(in crate::executor) fn op_new_object(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
if let Some(Operand::Count(count)) = instruction.operand {
for _ in 0..count {
if let Ok((vb, vk)) = self.pop_kinded() {
drop_with_kind(vb, vk);
} else {
return Err(VMError::StackUnderflow);
}
if let Ok((kb, kk)) = self.pop_kinded() {
drop_with_kind(kb, kk);
} else {
return Err(VMError::StackUnderflow);
}
}
Err(VMError::NotImplemented(
"op_new_object: ad-hoc TypedObject construction depends on \
`create_typed_object_from_pairs` (E-vm-impl-tail territory) \
being migrated off ValueWord — phase-2c, see ADR-006 §2.7.4"
.to_string(),
))
} else {
Err(VMError::InvalidOperand)
}
}
pub(in crate::executor) fn op_new_matrix(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
let (rows, cols) = match instruction.operand {
Some(Operand::MatrixDims { rows, cols }) => (rows as u32, cols as u32),
_ => return Err(VMError::InvalidOperand),
};
let total = (rows as usize) * (cols as usize);
let mut popped: Vec<(u64, NativeKind)> = Vec::with_capacity(total);
for _ in 0..total {
match self.pop_kinded() {
Ok(pair) => popped.push(pair),
Err(_) => {
for (b, k) in popped.drain(..) {
drop_with_kind(b, k);
}
return Err(VMError::StackUnderflow);
}
}
}
popped.reverse();
let mut data =
shape_value::aligned_vec::AlignedVec::<f64>::with_capacity(total);
for (bits, kind) in popped.iter() {
let v = match kind {
NativeKind::Float64 => f64::from_bits(*bits),
NativeKind::Int64 => (*bits as i64) as f64,
NativeKind::Bool => {
if *bits != 0 {
1.0
} else {
0.0
}
}
_ => {
for (b, k) in popped.iter() {
drop_with_kind(*b, *k);
}
return Err(VMError::TypeError {
expected: "numeric matrix element",
got: "non-numeric kind",
});
}
};
data.push(v);
drop_with_kind(*bits, *kind);
}
let matrix = shape_value::heap_value::MatrixData::from_flat(data, rows, cols);
let arc = Arc::new(matrix);
let bits = Arc::into_raw(arc) as u64;
self.push_kinded(bits, NativeKind::Ptr(HeapKind::Matrix))
}
pub(in crate::executor) fn op_new_array(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
let count = match instruction.operand {
Some(Operand::Count(c)) => c as usize,
_ => return Err(VMError::InvalidOperand),
};
for _ in 0..count {
if let Ok((b, k)) = self.pop_kinded() {
drop_with_kind(b, k);
} else {
return Err(VMError::StackUnderflow);
}
}
Err(ckpt5_surface("op_new_array", count))
}
pub(in crate::executor) fn op_new_typed_array(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
let count = match instruction.operand {
Some(Operand::Count(c)) => c as usize,
_ => return Err(VMError::InvalidOperand),
};
for _ in 0..count {
if let Ok((b, k)) = self.pop_kinded() {
drop_with_kind(b, k);
} else {
return Err(VMError::StackUnderflow);
}
}
Err(ckpt5_surface("op_new_typed_array", count))
}
}
fn kinded_to_slot(
bits: u64,
kind: NativeKind,
field_type: Option<&FieldType>,
) -> (ValueSlot, bool, NativeKind) {
if matches!(field_type, Some(FieldType::F64) | Some(FieldType::Decimal)) {
let n = match kind {
NativeKind::Float64 => f64::from_bits(bits),
NativeKind::Int64 => (bits as i64) as f64,
NativeKind::Bool => {
if bits != 0 {
1.0
} else {
0.0
}
}
NativeKind::Ptr(HeapKind::Decimal) if bits != 0 => {
let arc: Arc<rust_decimal::Decimal> =
unsafe { Arc::from_raw(bits as *const rust_decimal::Decimal) };
let f = arc.to_f64().unwrap_or(0.0);
drop(arc);
f
}
_ => 0.0,
};
return (ValueSlot::from_number(n), false, NativeKind::Float64);
}
let is_heap = matches!(kind, NativeKind::String | NativeKind::Ptr(_));
if is_heap {
return (ValueSlot::from_raw(bits), true, kind);
}
match field_type {
Some(FieldType::I64) | Some(FieldType::Timestamp) => {
let i = match kind {
NativeKind::Int64 => bits as i64,
NativeKind::Float64 => f64::from_bits(bits) as i64,
NativeKind::Bool => (bits != 0) as i64,
_ => 0,
};
(ValueSlot::from_int(i), false, NativeKind::Int64)
}
Some(ft) if ft.is_width_integer() => {
let raw = match kind {
NativeKind::Int64 => bits as i64,
NativeKind::Float64 => f64::from_bits(bits) as i64,
NativeKind::Bool => (bits != 0) as i64,
k if matches!(
k,
NativeKind::Int8
| NativeKind::Int16
| NativeKind::Int32
| NativeKind::UInt8
| NativeKind::UInt16
| NativeKind::UInt32
| NativeKind::UInt64
) =>
{
bits as i64
}
_ => 0,
};
let resolved = ft.to_native_kind().unwrap_or(NativeKind::Int64);
if matches!(ft, FieldType::U64) {
(ValueSlot::from_int(raw), false, resolved)
} else {
let truncated = if let Some(w) = field_type_to_int_width(ft) {
w.truncate(raw)
} else {
raw
};
(ValueSlot::from_int(truncated), false, resolved)
}
}
Some(FieldType::Bool) => {
let b = match kind {
NativeKind::Bool => bits != 0,
NativeKind::Int64 => (bits as i64) != 0,
NativeKind::Float64 => f64::from_bits(bits) != 0.0,
_ => false,
};
(ValueSlot::from_bool(b), false, NativeKind::Bool)
}
Some(FieldType::Any) | None | Some(_) => {
(ValueSlot::from_raw(bits), false, kind)
}
}
}
#[allow(dead_code)]
fn _ckpt5_reserved_kinded_slot(_: KindedSlot) {}