use crate::executor::objects::raw_helpers;
use crate::{
bytecode::{Instruction, OpCode},
executor::vm_impl::stack::drop_with_kind,
executor::VirtualMachine,
};
use shape_value::{FilterNode, NativeKind, VMError, heap_value::HeapKind};
use std::sync::Arc;
#[inline]
fn kind_is_heap(k: NativeKind) -> bool {
matches!(k, NativeKind::String | NativeKind::Ptr(_))
}
#[inline]
fn kinded_truthy(bits: u64, kind: NativeKind) -> bool {
match 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,
}
}
impl VirtualMachine {
#[inline(always)]
pub(in crate::executor) fn exec_logical(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
use OpCode::*;
match instruction.opcode {
And => {
let (b_bits, b_kind) = self.pop_kinded()?;
let (a_bits, a_kind) = self.pop_kinded()?;
if kind_is_heap(a_kind) || kind_is_heap(b_kind) {
if let (Some(left), Some(right)) = (
raw_helpers::extract_filter_expr(a_bits, a_kind),
raw_helpers::extract_filter_expr(b_bits, b_kind),
) {
let combined = Arc::new(FilterNode::And(
Box::new(left.clone()),
Box::new(right.clone()),
));
drop_with_kind(a_bits, a_kind);
drop_with_kind(b_bits, b_kind);
let raw = Arc::into_raw(combined) as u64;
self.push_kinded(raw, NativeKind::Ptr(HeapKind::FilterExpr))?;
} else {
let r = kinded_truthy(a_bits, a_kind) && kinded_truthy(b_bits, b_kind);
drop_with_kind(a_bits, a_kind);
drop_with_kind(b_bits, b_kind);
self.push_kinded(r as u64, NativeKind::Bool)?;
}
} else {
let r = kinded_truthy(a_bits, a_kind) && kinded_truthy(b_bits, b_kind);
self.push_kinded(r as u64, NativeKind::Bool)?;
}
}
Or => {
let (b_bits, b_kind) = self.pop_kinded()?;
let (a_bits, a_kind) = self.pop_kinded()?;
if kind_is_heap(a_kind) || kind_is_heap(b_kind) {
if let (Some(left), Some(right)) = (
raw_helpers::extract_filter_expr(a_bits, a_kind),
raw_helpers::extract_filter_expr(b_bits, b_kind),
) {
let combined = Arc::new(FilterNode::Or(
Box::new(left.clone()),
Box::new(right.clone()),
));
drop_with_kind(a_bits, a_kind);
drop_with_kind(b_bits, b_kind);
let raw = Arc::into_raw(combined) as u64;
self.push_kinded(raw, NativeKind::Ptr(HeapKind::FilterExpr))?;
} else {
let r = kinded_truthy(a_bits, a_kind) || kinded_truthy(b_bits, b_kind);
drop_with_kind(a_bits, a_kind);
drop_with_kind(b_bits, b_kind);
self.push_kinded(r as u64, NativeKind::Bool)?;
}
} else {
let r = kinded_truthy(a_bits, a_kind) || kinded_truthy(b_bits, b_kind);
self.push_kinded(r as u64, NativeKind::Bool)?;
}
}
Not => {
let (bits, kind) = self.pop_kinded()?;
if kind_is_heap(kind) {
if let Some(node) = raw_helpers::extract_filter_expr(bits, kind) {
let combined = Arc::new(FilterNode::Not(Box::new(node.clone())));
drop_with_kind(bits, kind);
let raw = Arc::into_raw(combined) as u64;
self.push_kinded(raw, NativeKind::Ptr(HeapKind::FilterExpr))?;
} else {
let r = !kinded_truthy(bits, kind);
drop_with_kind(bits, kind);
self.push_kinded(r as u64, NativeKind::Bool)?;
}
} else {
self.push_kinded(!kinded_truthy(bits, kind) as u64, NativeKind::Bool)?;
}
}
_ => unreachable!(
"exec_logical called with non-logical opcode: {:?}",
instruction.opcode
),
}
Ok(())
}
pub(in crate::executor) fn op_null_coalesce(&mut self) -> Result<(), VMError> {
let (right_bits, right_kind) = self.pop_kinded()?;
let (left_bits, left_kind) = self.pop_kinded()?;
let left_is_null = is_null_kinded(left_bits, left_kind);
if left_is_null {
drop_with_kind(left_bits, left_kind);
self.push_kinded(right_bits, right_kind)
} else {
drop_with_kind(right_bits, right_kind);
self.push_kinded(left_bits, left_kind)
}
}
}
#[inline]
fn is_null_kinded(bits: u64, kind: NativeKind) -> bool {
match kind {
NativeKind::Null => true,
NativeKind::String | NativeKind::Ptr(_) => bits == 0,
NativeKind::NullableFloat64 => f64::from_bits(bits).is_nan(),
NativeKind::NullableInt8
| NativeKind::NullableInt16
| NativeKind::NullableInt32
| NativeKind::NullableInt64
| NativeKind::NullableIntSize
| NativeKind::NullableUInt8
| NativeKind::NullableUInt16
| NativeKind::NullableUInt32
| NativeKind::NullableUInt64
| NativeKind::NullableUIntSize => bits == 0,
_ => false,
}
}