use std::sync::Arc;
use crate::bytecode::{Instruction, OpCode, Operand};
use crate::executor::vm_impl::stack::drop_with_kind;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::HeapKind;
use shape_value::{
HeapValue, KindedSlot, NativeKind, TableViewData, VMError,
};
use super::VirtualMachine;
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
#[cold]
#[inline(never)]
fn ckpt5_window_surface(op: &'static str) -> VMError {
VMError::NotImplemented(format!(
"{op}: SURFACE — V3-S5 ckpt-5 consumer-cascade tier 3 surface. \
`Arc<TypedArrayData>` carrier + per-arm dispatch helpers \
(`as_typed_array` / `typed_array_to_f64_vec` / `typed_array_len`) \
DELETED across V3-S5 ckpt-1..ckpt-4 per W12-typed-array-data-\
deletion-audit §3.5 + §B + ADR-006 §2.7.24 Q25.A SUPERSEDED. \
Window-aggregate scalar arm preserved (Int64/Float64); array arm \
rebuild lands at ckpt-6 STRICT close per per-element-kind v2-raw \
`TypedArray<T>` direct-access target. REFUSED ON SIGHT: \
TypedArrayData resurrection under any rename (Refusal #1).",
op = op,
))
}
pub(crate) fn handle_window_row_number_v2(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Ok(KindedSlot::from_int(1))
}
pub(crate) fn handle_window_lag_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Ok(args.get(2).cloned().unwrap_or_else(KindedSlot::none))
}
pub(crate) fn handle_window_first_value_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Ok(args.first().cloned().unwrap_or_else(KindedSlot::none))
}
pub(crate) fn handle_window_sum_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let arg = args.first().ok_or_else(|| {
type_error("WindowSum requires at least 1 argument (value or array)")
})?;
match arg.kind {
NativeKind::Int64 | NativeKind::Float64 => Ok(arg.clone()),
_ => Err(ckpt5_window_surface("WindowSum")),
}
}
pub(crate) fn handle_window_avg_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let arg = args.first().ok_or_else(|| {
type_error("WindowAvg requires at least 1 argument (value or array)")
})?;
match arg.kind {
NativeKind::Int64 => {
let i = arg.as_i64().expect("kind=Int64");
Ok(KindedSlot::from_number(i as f64))
}
NativeKind::Float64 => Ok(arg.clone()),
_ => Err(ckpt5_window_surface("WindowAvg")),
}
}
pub(crate) fn handle_window_min_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
handle_window_min_max_inner(args, false)
}
pub(crate) fn handle_window_max_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
handle_window_min_max_inner(args, true)
}
fn handle_window_min_max_inner(
args: &[KindedSlot],
pick_max: bool,
) -> Result<KindedSlot, VMError> {
let _ = pick_max;
let arg = args.first().ok_or_else(|| {
type_error("WindowMin/Max requires at least 1 argument (value or array)")
})?;
match arg.kind {
NativeKind::Int64 | NativeKind::Float64 => Ok(arg.clone()),
_ => Err(ckpt5_window_surface("WindowMin/Max")),
}
}
pub(crate) fn handle_window_count_v2(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let arg = match args.first() {
Some(a) => a,
None => return Ok(KindedSlot::from_int(0)),
};
match arg.kind {
NativeKind::Bool if arg.slot.raw() == 0 => Ok(KindedSlot::from_int(0)),
NativeKind::Int64 | NativeKind::Float64 | NativeKind::Bool => Ok(KindedSlot::from_int(1)),
_ => Err(ckpt5_window_surface("WindowCount")),
}
}
impl VirtualMachine {
pub(crate) fn handle_eval_datetime_expr(
&mut self,
_ctx: Option<&mut ExecutionContext>,
) -> Result<(), VMError> {
Err(VMError::NotImplemented(
"W8-WJ — handle_eval_datetime_expr SURFACE: depends on \
HeapKind::Temporal carrier dispatch (§2.7.6 / Q8). \
Phase-2c §2.7.4 boundary; body re-fill lands when the \
Temporal heap arm dispatch table is wired in the §2.7.10 \
MethodFnV2 surface."
.to_string(),
))
}
pub(crate) fn eval_datetime_expr_recursive(
&self,
expr: &shape_ast::ast::DateTimeExpr,
) -> Result<chrono::DateTime<chrono::FixedOffset>, VMError> {
use shape_ast::ast::{DateTimeExpr, NamedTime};
match expr {
DateTimeExpr::Literal(s) | DateTimeExpr::Absolute(s) => {
crate::executor::builtins::datetime_builtins::parse_datetime_string(s)
.map_err(|e| VMError::RuntimeError(e))
}
DateTimeExpr::Named(named) => {
let now = chrono::Utc::now().fixed_offset();
match named {
NamedTime::Now => Ok(now),
NamedTime::Today => {
let date = now.date_naive();
let midnight = date
.and_hms_opt(0, 0, 0)
.expect("midnight should always be valid");
Ok(midnight.and_utc().fixed_offset())
}
NamedTime::Yesterday => {
let yesterday = now
.checked_sub_signed(chrono::Duration::days(1))
.ok_or_else(|| {
VMError::RuntimeError(
"DateTime overflow computing yesterday".to_string(),
)
})?;
let date = yesterday.date_naive();
let midnight = date
.and_hms_opt(0, 0, 0)
.expect("midnight should always be valid");
Ok(midnight.and_utc().fixed_offset())
}
}
}
DateTimeExpr::Relative { base, offset } => {
let base_dt = self.eval_datetime_expr_recursive(base)?;
let chrono_dur =
crate::executor::builtins::datetime_builtins::ast_duration_to_chrono(offset);
base_dt.checked_add_signed(chrono_dur).ok_or_else(|| {
VMError::RuntimeError("DateTime overflow in relative expression".to_string())
})
}
DateTimeExpr::Arithmetic {
base,
operator,
duration,
} => {
let base_dt = self.eval_datetime_expr_recursive(base)?;
let chrono_dur =
crate::executor::builtins::datetime_builtins::ast_duration_to_chrono(duration);
match operator.as_str() {
"+" => base_dt.checked_add_signed(chrono_dur).ok_or_else(|| {
VMError::RuntimeError("DateTime overflow in addition".to_string())
}),
"-" => base_dt.checked_sub_signed(chrono_dur).ok_or_else(|| {
VMError::RuntimeError("DateTime overflow in subtraction".to_string())
}),
_ => Err(VMError::RuntimeError(format!(
"Invalid datetime arithmetic operator: {}",
operator
))),
}
}
}
}
pub(crate) fn handle_join_execute(&mut self) -> Result<(), VMError> {
Err(VMError::NotImplemented(
"W8-WJ — handle_join_execute SURFACE: depends on \
datatable_methods::joins ABI flip to &[KindedSlot] (W9 \
method-body re-fill). Cross-cluster cascade per playbook \
§5; body re-fill lands when the join handler call \
boundary is kinded."
.to_string(),
))
}
pub(crate) fn exec_bind_schema(&mut self, instruction: &Instruction) -> Result<(), VMError> {
let schema_id = match &instruction.operand {
Some(Operand::Count(id)) => *id as u64,
_ => {
return Err(VMError::RuntimeError(
"BindSchema requires Count operand (schema_id)".to_string(),
));
}
};
let (bits, kind) = self.pop_kinded()?;
let table = match kind {
NativeKind::Ptr(HeapKind::TableView) => {
let tv = unsafe { &*(bits as *const TableViewData) };
let cloned = match tv {
TableViewData::TypedTable { table, .. }
| TableViewData::IndexedTable { table, .. }
| TableViewData::RowView { table, .. }
| TableViewData::ColumnRef { table, .. } => Arc::clone(table),
};
drop_with_kind(bits, kind);
cloned
}
NativeKind::Ptr(HeapKind::DataTable) => {
unsafe { Arc::from_raw(bits as *const shape_value::DataTable) }
}
_ => {
drop_with_kind(bits, kind);
return Err(VMError::RuntimeError(format!(
"BindSchema expected DataTable/TableView, got {:?}",
kind
)));
}
};
let schema = self
.program
.type_schema_registry
.get_by_id(schema_id as u32)
.ok_or_else(|| {
VMError::RuntimeError(format!("BindSchema: unknown schema ID {}", schema_id))
})?;
let arrow_schema = table.schema();
match schema.bind_to_arrow_schema(&arrow_schema) {
Ok(_binding) => {
let tv = Arc::new(TableViewData::TypedTable {
schema_id,
table,
});
let out_bits = Arc::into_raw(tv) as u64;
self.push_kinded(out_bits, NativeKind::Ptr(HeapKind::TableView))?;
Ok(())
}
Err(e) => Err(VMError::RuntimeError(format!(
"Schema binding failed: {}",
e
))),
}
}
pub(crate) fn exec_load_col(&mut self, instruction: &Instruction) -> Result<(), VMError> {
let col_id = match &instruction.operand {
Some(Operand::ColumnAccess { col_id }) => *col_id,
_ => return Err(VMError::InvalidOperand),
};
let (bits, kind) = self.pop_kinded()?;
let result = match kind {
NativeKind::Ptr(HeapKind::TableView) => unsafe {
let tv = &*(bits as *const TableViewData);
match tv {
TableViewData::RowView { table, row_idx, .. } => {
let row_idx = *row_idx;
let ptrs = match table.column_ptr(col_id as usize) {
Some(p) => p,
None => {
let cc = table.column_count();
drop_with_kind(bits, kind);
return Err(VMError::RuntimeError(format!(
"Column index {} out of bounds (table has {} columns)",
col_id, cc
)));
}
};
if row_idx >= table.row_count() {
let rc = table.row_count();
drop_with_kind(bits, kind);
return Err(VMError::RuntimeError(format!(
"Row index {} out of bounds (table has {} rows)",
row_idx, rc
)));
}
match instruction.opcode {
OpCode::LoadColF64 => {
let v = match &ptrs.data_type {
arrow_schema::DataType::Float64 => {
let ptr = ptrs.values_ptr as *const f64;
*ptr.add(row_idx)
}
arrow_schema::DataType::Float32 => {
let ptr = ptrs.values_ptr as *const f32;
(*ptr.add(row_idx)) as f64
}
arrow_schema::DataType::Int64 => {
let ptr = ptrs.values_ptr as *const i64;
(*ptr.add(row_idx)) as f64
}
_ => f64::NAN,
};
Ok((v.to_bits(), NativeKind::Float64))
}
OpCode::LoadColI64 => {
let v = match &ptrs.data_type {
arrow_schema::DataType::Int64
| arrow_schema::DataType::Timestamp(_, _) => {
let ptr = ptrs.values_ptr as *const i64;
*ptr.add(row_idx)
}
arrow_schema::DataType::Int32 => {
let ptr = ptrs.values_ptr as *const i32;
(*ptr.add(row_idx)) as i64
}
_ => 0,
};
Ok((v as u64, NativeKind::Int64))
}
OpCode::LoadColBool => {
let byte_idx = row_idx / 8;
let bit_idx = row_idx % 8;
let byte = *ptrs.values_ptr.add(byte_idx);
let v = (byte >> bit_idx) & 1 == 1;
Ok((v as u64, NativeKind::Bool))
}
OpCode::LoadColStr => {
let offsets = ptrs.offsets_ptr as *const i32;
let start = *offsets.add(row_idx) as usize;
let end = *offsets.add(row_idx + 1) as usize;
let bytes = std::slice::from_raw_parts(
ptrs.values_ptr.add(start),
end - start,
);
let s = std::str::from_utf8_unchecked(bytes);
let arc = Arc::new(s.to_string());
let str_bits = Arc::into_raw(arc) as u64;
Ok((str_bits, NativeKind::String))
}
_ => Err(VMError::RuntimeError(format!(
"exec_load_col called with non-LoadCol opcode: {:?}",
instruction.opcode
))),
}
}
_ => Err(VMError::RuntimeError(format!(
"LoadCol* expected RowView TableView, got {:?}",
kind
))),
}
},
_ => Err(VMError::RuntimeError(format!(
"LoadCol* expected RowView (TableView heap kind), got {:?}",
kind
))),
};
drop_with_kind(bits, kind);
let (out_bits, out_kind) = result?;
self.push_kinded(out_bits, out_kind)?;
Ok(())
}
}
#[cfg(any())]
mod tests {}