use crate::{
bytecode::{Instruction, Operand},
executor::vm_impl::stack::{clone_with_kind, drop_with_kind},
executor::VirtualMachine,
};
use shape_value::heap_value::{HeapKind, TypedObjectStorage};
use shape_value::{NativeKind, ValueSlot, VMError};
use std::sync::Arc;
impl VirtualMachine {
pub(in crate::executor) fn op_typed_merge_object(
&mut self,
instruction: &Instruction,
) -> Result<(), VMError> {
let (target_schema_id, left_size, right_size) = match instruction.operand {
Some(Operand::TypedMerge {
target_schema_id,
left_size,
right_size,
}) => (target_schema_id, left_size as usize, right_size as usize),
_ => return Err(VMError::InvalidOperand),
};
let (right_bits, right_kind) = self.pop_kinded()?;
let (left_bits, left_kind) = self.pop_kinded()?;
match (left_kind, right_kind) {
(
NativeKind::Ptr(HeapKind::TypedObject),
NativeKind::Ptr(HeapKind::TypedObject),
) => {}
_ => {
drop_with_kind(right_bits, right_kind);
drop_with_kind(left_bits, left_kind);
return Err(VMError::TypeError {
expected: "two TypedObject operands for TypedMergeObject",
got: "non-TypedObject kind",
});
}
}
use shape_value::v2::heap_element::HeapElement;
let left_ptr = left_bits as *const TypedObjectStorage;
let right_ptr = right_bits as *const TypedObjectStorage;
let left: &TypedObjectStorage = unsafe { &*left_ptr };
let right: &TypedObjectStorage = unsafe { &*right_ptr };
let left_count = left.slots.len().min(left_size / 8);
let right_count = right.slots.len().min(right_size / 8);
let merged_ptr = build_concat_merged_storage(
target_schema_id as u64,
left,
left_count,
right,
right_count,
);
unsafe {
TypedObjectStorage::release_elem(left_ptr);
TypedObjectStorage::release_elem(right_ptr);
}
let bits = merged_ptr as u64;
self.push_kinded(bits, NativeKind::Ptr(HeapKind::TypedObject))
}
pub(in crate::executor) fn op_merge_object(&mut self) -> Result<(), VMError> {
let (source_bits, source_kind) = self.pop_kinded()?;
let (target_bits, target_kind) = self.pop_kinded()?;
match (target_kind, source_kind) {
(
NativeKind::Ptr(HeapKind::TypedObject),
NativeKind::Ptr(HeapKind::TypedObject),
) => {}
_ => {
drop_with_kind(source_bits, source_kind);
drop_with_kind(target_bits, target_kind);
return Err(VMError::RuntimeError(
"MergeObject requires compile-time typed objects; \
dynamic object merge is disabled"
.to_string(),
));
}
}
use shape_value::v2::heap_element::HeapElement;
let target_ptr = target_bits as *const TypedObjectStorage;
let source_ptr = source_bits as *const TypedObjectStorage;
let target: &TypedObjectStorage = unsafe { &*target_ptr };
let source: &TypedObjectStorage = unsafe { &*source_ptr };
let target_id = target.schema_id as u32;
let source_id = source.schema_id as u32;
let (keep_left_indices, right_count) = {
let left_schema = self.lookup_schema(target_id).ok_or_else(|| {
VMError::RuntimeError(format!("Schema {} not found", target_id))
})?;
let right_schema = self.lookup_schema(source_id).ok_or_else(|| {
VMError::RuntimeError(format!("Schema {} not found", source_id))
})?;
let right_names: std::collections::HashSet<&str> = right_schema
.fields
.iter()
.map(|f| f.name.as_str())
.collect();
let keep: Vec<usize> = left_schema
.fields
.iter()
.enumerate()
.filter(|(_, f)| !right_names.contains(f.name.as_str()))
.map(|(i, _)| i)
.collect();
(keep, right_schema.fields.len())
};
let merged_schema_id = match self.derive_merged_schema(target_id, source_id) {
Ok(id) => id,
Err(e) => {
unsafe {
TypedObjectStorage::release_elem(target_ptr);
TypedObjectStorage::release_elem(source_ptr);
}
return Err(e);
}
};
let merged_ptr = build_named_merged_storage(
merged_schema_id as u64,
target,
&keep_left_indices,
source,
right_count.min(source.slots.len()),
);
unsafe {
TypedObjectStorage::release_elem(target_ptr);
TypedObjectStorage::release_elem(source_ptr);
}
let bits = merged_ptr as u64;
self.push_kinded(bits, NativeKind::Ptr(HeapKind::TypedObject))
}
}
fn build_concat_merged_storage(
merged_schema_id: u64,
left: &TypedObjectStorage,
left_count: usize,
right: &TypedObjectStorage,
right_count: usize,
) -> *mut TypedObjectStorage {
let total = left_count + right_count;
let mut merged_slots: Vec<ValueSlot> = Vec::with_capacity(total);
let mut merged_kinds: Vec<NativeKind> = Vec::with_capacity(total);
let mut merged_mask: u64 = 0;
append_kept_slots(
left,
(0..left_count).collect::<Vec<_>>().as_slice(),
&mut merged_slots,
&mut merged_kinds,
&mut merged_mask,
);
append_kept_slots(
right,
(0..right_count).collect::<Vec<_>>().as_slice(),
&mut merged_slots,
&mut merged_kinds,
&mut merged_mask,
);
TypedObjectStorage::_new(
merged_schema_id,
merged_slots.into_boxed_slice(),
merged_mask,
Arc::from(merged_kinds.into_boxed_slice()),
)
}
fn build_named_merged_storage(
merged_schema_id: u64,
left: &TypedObjectStorage,
keep_left_indices: &[usize],
right: &TypedObjectStorage,
right_count: usize,
) -> *mut TypedObjectStorage {
let total = keep_left_indices.len() + right_count;
let mut merged_slots: Vec<ValueSlot> = Vec::with_capacity(total);
let mut merged_kinds: Vec<NativeKind> = Vec::with_capacity(total);
let mut merged_mask: u64 = 0;
append_kept_slots(
left,
keep_left_indices,
&mut merged_slots,
&mut merged_kinds,
&mut merged_mask,
);
let right_indices: Vec<usize> = (0..right_count).collect();
append_kept_slots(
right,
&right_indices,
&mut merged_slots,
&mut merged_kinds,
&mut merged_mask,
);
TypedObjectStorage::_new(
merged_schema_id,
merged_slots.into_boxed_slice(),
merged_mask,
Arc::from(merged_kinds.into_boxed_slice()),
)
}
fn append_kept_slots(
src: &TypedObjectStorage,
indices: &[usize],
merged_slots: &mut Vec<ValueSlot>,
merged_kinds: &mut Vec<NativeKind>,
merged_mask: &mut u64,
) {
let src_slots = &src.slots;
let src_mask = src.heap_mask;
let src_kinds = &src.field_kinds;
for &orig_idx in indices {
if orig_idx >= src_slots.len() {
debug_assert!(false, "append_kept_slots: idx {} out of range", orig_idx);
continue;
}
let new_idx = merged_slots.len();
let bits = src_slots[orig_idx].raw();
let kind = src_kinds.get(orig_idx).copied().unwrap_or(NativeKind::Bool);
merged_slots.push(src_slots[orig_idx]);
merged_kinds.push(kind);
if src_mask & (1u64 << orig_idx) != 0 {
*merged_mask |= 1u64 << new_idx;
clone_with_kind(bits, kind);
}
}
}