use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::heap_value::{
HashMapData, HashMapKindedRef, HashMapValueElem, HeapKind, HeapValue,
TraitObjectPtr, TypedObjectPtr, TypedObjectStorage,
};
use shape_value::{KindedSlot, NativeKind, ValueSlot, VMError};
use std::sync::Arc;
#[cold]
#[inline(never)]
fn ckpt5_hashmap_array_surface(op: &'static str) -> VMError {
VMError::NotImplemented(format!(
"HashMap.{op}: SURFACE — V3-S5 ckpt-5 consumer-cascade tier 3 \
surface. `Arc<TypedArrayData>` result carrier DELETED at V3-S5 \
ckpt-1..ckpt-4 per W12-typed-array-data-deletion audit §3.5 + \
§3.6 + §B + ADR-006 §2.7.24 Q25.A SUPERSEDED. Rebuild lands at \
ckpt-6 STRICT close per the per-T v2-raw `TypedArray<T>` carrier \
shape. REFUSED ON SIGHT: TypedArrayData resurrection under any \
rename (Refusal #1).",
op = op,
))
}
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
unsafe fn read_keys_owned(
keys: *const shape_value::v2::typed_array::TypedArray<
*const shape_value::v2::string_obj::StringObj,
>,
) -> Vec<Arc<String>> {
let n = unsafe { shape_value::v2::typed_array::TypedArray::len(keys) as usize };
let mut out = Vec::with_capacity(n);
for i in 0..n {
let ptr = unsafe {
shape_value::v2::typed_array::TypedArray::get_unchecked(keys, i as u32)
};
let s = unsafe { shape_value::v2::string_obj::StringObj::as_str(ptr).to_owned() };
out.push(Arc::new(s));
}
out
}
#[inline]
fn kref_len(kref: &HashMapKindedRef) -> usize {
kref.len()
}
fn ensure_entry_schema() -> u32 {
let fields = [String::from("key"), String::from("value")];
shape_runtime::type_schema::register_predeclared_any_schema(&fields)
}
fn build_entry_object(
key_arc: Arc<String>,
value_bits: u64,
value_kind: NativeKind,
) -> TypedObjectPtr {
let schema_id = ensure_entry_schema();
let key_bits = Arc::into_raw(key_arc) as u64;
let slots: Box<[ValueSlot]> = Box::new([
ValueSlot::from_raw(key_bits),
ValueSlot::from_raw(value_bits),
]);
let field_kinds: Arc<[NativeKind]> =
Arc::from(vec![NativeKind::String, value_kind].into_boxed_slice());
let heap_mask: u64 = 0b11;
let ptr = TypedObjectStorage::_new(
schema_id as u64,
slots,
heap_mask,
field_kinds,
);
TypedObjectPtr::new(ptr)
}
fn entry_object_at(kref: &HashMapKindedRef, i: usize, key_arc: Arc<String>) -> TypedObjectPtr {
match kref {
HashMapKindedRef::I64(arc) => {
let v: i64 = unsafe { *(*arc.values).data.add(i) };
build_entry_object(key_arc, v as u64, NativeKind::Int64)
}
HashMapKindedRef::F64(arc) => {
let v: f64 = unsafe { *(*arc.values).data.add(i) };
build_entry_object(key_arc, v.to_bits(), NativeKind::Float64)
}
HashMapKindedRef::Bool(arc) => {
let v: u8 = unsafe { *(*arc.values).data.add(i) };
build_entry_object(key_arc, v as u64, NativeKind::Bool)
}
HashMapKindedRef::Char(arc) => {
let v: char = unsafe { *(*arc.values).data.add(i) };
build_entry_object(key_arc, v as u64, NativeKind::Char)
}
HashMapKindedRef::String(arc) => {
let ptr: *const shape_value::v2::string_obj::StringObj =
unsafe { *(*arc.values).data.add(i) };
let s = unsafe { shape_value::v2::string_obj::StringObj::as_str(ptr).to_owned() };
let arc_s = Arc::new(s);
let value_bits = Arc::into_raw(arc_s) as u64;
build_entry_object(key_arc, value_bits, NativeKind::String)
}
HashMapKindedRef::Decimal(arc) => {
let ptr: *const shape_value::v2::decimal_obj::DecimalObj =
unsafe { *(*arc.values).data.add(i) };
unsafe {
shape_value::v2::refcount::v2_retain(&(*ptr).header);
}
build_entry_object(key_arc, ptr as u64, NativeKind::DecimalV2)
}
HashMapKindedRef::TypedObject(arc) => {
let elem: &TypedObjectPtr = unsafe { &*(*arc.values).data.add(i) };
let bumped: TypedObjectPtr = elem.clone();
let ptr = bumped.into_raw();
build_entry_object(key_arc, ptr as u64, NativeKind::Ptr(HeapKind::TypedObject))
}
HashMapKindedRef::TraitObject(arc) => {
let elem: &TraitObjectPtr = unsafe { &*(*arc.values).data.add(i) };
let bumped: TraitObjectPtr = elem.clone();
let ptr = bumped.into_raw();
build_entry_object(key_arc, ptr as u64, NativeKind::Ptr(HeapKind::TraitObject))
}
HashMapKindedRef::HashMap(arc) => {
let inner_ref: &HashMapKindedRef = unsafe { &*(*arc.values).data.add(i) };
let cloned = Arc::new(inner_ref.clone());
let slot = KindedSlot::from_hashmap(cloned);
let bits = slot.raw();
std::mem::forget(slot);
build_entry_object(key_arc, bits, NativeKind::Ptr(HeapKind::HashMap))
}
}
}
fn build_filtered_kref(
src: &HashMapKindedRef,
kept_indices: &[usize],
kept_keys: &[Arc<String>],
) -> Result<HashMapKindedRef, VMError> {
debug_assert_eq!(kept_indices.len(), kept_keys.len());
Ok(match src {
HashMapKindedRef::I64(arc) => {
let mut data: HashMapData<i64> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let v: i64 = unsafe { *(*arc.values).data.add(*slot) };
unsafe { data.insert(key.as_str(), v) };
}
HashMapKindedRef::I64(Arc::new(data))
}
HashMapKindedRef::F64(arc) => {
let mut data: HashMapData<f64> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let v: f64 = unsafe { *(*arc.values).data.add(*slot) };
unsafe { data.insert(key.as_str(), v) };
}
HashMapKindedRef::F64(Arc::new(data))
}
HashMapKindedRef::Bool(arc) => {
let mut data: HashMapData<u8> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let v: u8 = unsafe { *(*arc.values).data.add(*slot) };
unsafe { data.insert(key.as_str(), v) };
}
HashMapKindedRef::Bool(Arc::new(data))
}
HashMapKindedRef::Char(arc) => {
let mut data: HashMapData<char> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let v: char = unsafe { *(*arc.values).data.add(*slot) };
unsafe { data.insert(key.as_str(), v) };
}
HashMapKindedRef::Char(Arc::new(data))
}
HashMapKindedRef::String(arc) => {
let mut data: HashMapData<*const shape_value::v2::string_obj::StringObj> =
HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let elem_ref: &*const shape_value::v2::string_obj::StringObj =
unsafe { &*(*arc.values).data.add(*slot) };
let cloned = unsafe {
<*const shape_value::v2::string_obj::StringObj
as HashMapValueElem>::share_clone(elem_ref)
};
unsafe { data.insert(key.as_str(), cloned) };
}
HashMapKindedRef::String(Arc::new(data))
}
HashMapKindedRef::Decimal(arc) => {
let mut data: HashMapData<*const shape_value::v2::decimal_obj::DecimalObj> =
HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let elem_ref: &*const shape_value::v2::decimal_obj::DecimalObj =
unsafe { &*(*arc.values).data.add(*slot) };
let cloned = unsafe {
<*const shape_value::v2::decimal_obj::DecimalObj
as HashMapValueElem>::share_clone(elem_ref)
};
unsafe { data.insert(key.as_str(), cloned) };
}
HashMapKindedRef::Decimal(Arc::new(data))
}
HashMapKindedRef::TypedObject(arc) => {
let mut data: HashMapData<TypedObjectPtr> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let elem_ref: &TypedObjectPtr =
unsafe { &*(*arc.values).data.add(*slot) };
let cloned = unsafe {
<TypedObjectPtr as HashMapValueElem>::share_clone(elem_ref)
};
unsafe { data.insert(key.as_str(), cloned) };
}
HashMapKindedRef::TypedObject(Arc::new(data))
}
HashMapKindedRef::TraitObject(arc) => {
let mut data: HashMapData<TraitObjectPtr> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let elem_ref: &TraitObjectPtr =
unsafe { &*(*arc.values).data.add(*slot) };
let cloned = unsafe {
<TraitObjectPtr as HashMapValueElem>::share_clone(elem_ref)
};
unsafe { data.insert(key.as_str(), cloned) };
}
HashMapKindedRef::TraitObject(Arc::new(data))
}
HashMapKindedRef::HashMap(arc) => {
let mut data: HashMapData<HashMapKindedRef> = HashMapData::new();
for (slot, key) in kept_indices.iter().zip(kept_keys.iter()) {
let elem_ref: &HashMapKindedRef =
unsafe { &*(*arc.values).data.add(*slot) };
let cloned = unsafe {
<HashMapKindedRef as HashMapValueElem>::share_clone(elem_ref)
};
unsafe { data.insert(key.as_str(), cloned) };
}
HashMapKindedRef::HashMap(Arc::new(data))
}
})
}
fn read_entry_kinded(kref: &HashMapKindedRef, i: usize, key_arc: Arc<String>) -> (KindedSlot, KindedSlot) {
let key_slot = KindedSlot::from_string_arc(key_arc);
let value_slot = value_slot_at(kref, i);
(key_slot, value_slot)
}
fn value_slot_at(kref: &HashMapKindedRef, i: usize) -> KindedSlot {
match kref {
HashMapKindedRef::I64(arc) => {
let v: i64 = unsafe { *(*arc.values).data.add(i) };
KindedSlot::from_int(v)
}
HashMapKindedRef::F64(arc) => {
let v: f64 = unsafe { *(*arc.values).data.add(i) };
KindedSlot::from_number(v)
}
HashMapKindedRef::Bool(arc) => {
let v: u8 = unsafe { *(*arc.values).data.add(i) };
KindedSlot::from_bool(v != 0)
}
HashMapKindedRef::Char(arc) => {
let v: char = unsafe { *(*arc.values).data.add(i) };
KindedSlot::from_char(v)
}
HashMapKindedRef::String(arc) => {
let ptr: *const shape_value::v2::string_obj::StringObj =
unsafe { *(*arc.values).data.add(i) };
let s = unsafe { shape_value::v2::string_obj::StringObj::as_str(ptr).to_owned() };
KindedSlot::from_string_arc(Arc::new(s))
}
HashMapKindedRef::Decimal(arc) => {
let ptr: *const shape_value::v2::decimal_obj::DecimalObj =
unsafe { *(*arc.values).data.add(i) };
unsafe {
shape_value::v2::refcount::v2_retain(&(*ptr).header);
}
KindedSlot::from_decimal_v2_ptr(ptr)
}
HashMapKindedRef::TypedObject(arc) => {
let elem: &TypedObjectPtr = unsafe { &*(*arc.values).data.add(i) };
let bumped: TypedObjectPtr = elem.clone();
KindedSlot::from_typed_object_raw(bumped.into_raw())
}
HashMapKindedRef::TraitObject(arc) => {
let elem: &TraitObjectPtr = unsafe { &*(*arc.values).data.add(i) };
let bumped: TraitObjectPtr = elem.clone();
KindedSlot::from_trait_object_raw(bumped.into_raw())
}
HashMapKindedRef::HashMap(arc) => {
let inner_ref: &HashMapKindedRef = unsafe { &*(*arc.values).data.add(i) };
KindedSlot::from_hashmap(Arc::new(inner_ref.clone()))
}
}
}
#[inline]
fn as_hashmap(slot: &KindedSlot) -> Result<Arc<HashMapKindedRef>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::HashMap)) {
return Err(type_error(format!(
"HashMap method receiver must be a HashMap (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap method receiver slot bits null"));
}
let arc = unsafe {
Arc::<HashMapKindedRef>::from_raw(bits as *const HashMapKindedRef)
};
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
#[inline]
fn as_string_key(slot: &KindedSlot) -> Result<&str, VMError> {
match slot.kind {
NativeKind::String => slot
.as_str()
.ok_or_else(|| type_error("HashMap key kind=String but slot bits null")),
NativeKind::Ptr(HeapKind::String) => match slot.slot.as_heap_value() {
HeapValue::String(s) => Ok(s.as_str()),
_ => Err(type_error(
"HashMap key kind=Ptr(String) but heap arm mismatched",
)),
},
_ => Err(type_error(format!(
"HashMap key must be a string (got kind {:?})",
slot.kind
))),
}
}
fn heap_value_arc_to_slot(hv: &Arc<HeapValue>) -> KindedSlot {
match hv.as_ref() {
HeapValue::String(s) => KindedSlot::from_string_arc(Arc::clone(s)),
HeapValue::Decimal(d) => KindedSlot::from_decimal(Arc::clone(d)),
HeapValue::BigInt(b) => KindedSlot::from_bigint(Arc::clone(b)),
HeapValue::TypedObject(o) => KindedSlot::from_typed_object_raw(o.clone().into_raw()),
HeapValue::HashMap(m) => KindedSlot::from_hashmap(Arc::new(m.clone())),
HeapValue::Char(c) => KindedSlot::from_char(*c),
_ => KindedSlot::none(),
}
}
pub fn v2_get(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.get() requires exactly 1 argument (key)",
));
}
let map = as_hashmap(&args[0])?;
let key = as_string_key(&args[1])?;
Ok(get_kinded(&map, key).unwrap_or_else(KindedSlot::none))
}
fn get_kinded(map: &HashMapKindedRef, key: &str) -> Option<KindedSlot> {
match map {
HashMapKindedRef::I64(arc) => arc.get_share(key).map(KindedSlot::from_int),
HashMapKindedRef::F64(arc) => arc.get_share(key).map(KindedSlot::from_number),
HashMapKindedRef::Bool(arc) => arc.get_share(key).map(|v| KindedSlot::from_bool(v != 0)),
HashMapKindedRef::Char(arc) => arc.get_share(key).map(KindedSlot::from_char),
HashMapKindedRef::String(arc) => {
arc.get_share(key).map(|ptr| {
let s = unsafe {
shape_value::v2::string_obj::StringObj::as_str(ptr).to_owned()
};
unsafe {
use shape_value::v2::heap_element::HeapElement;
shape_value::v2::string_obj::StringObj::release_elem(ptr);
}
KindedSlot::from_string_arc(Arc::new(s))
})
}
HashMapKindedRef::Decimal(arc) => {
arc.get_share(key).map(KindedSlot::from_decimal_v2_ptr)
}
HashMapKindedRef::TypedObject(arc) => {
arc.get_share(key)
.map(|ptr| KindedSlot::from_typed_object_raw(ptr.into_raw()))
}
HashMapKindedRef::TraitObject(arc) => {
arc.get_share(key)
.map(|ptr| KindedSlot::from_trait_object_raw(ptr.into_raw()))
}
HashMapKindedRef::HashMap(arc) => {
arc.get_share(key)
.map(|kref| KindedSlot::from_hashmap(Arc::new(kref)))
}
}
}
pub fn v2_has(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.has() requires exactly 1 argument (key)",
));
}
let map = as_hashmap(&args[0])?;
let key = as_string_key(&args[1])?;
Ok(KindedSlot::from_bool(map.contains_key(key)))
}
pub fn v2_keys(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("HashMap.keys() takes no arguments"));
}
let _ = as_hashmap(&args[0])?;
let _ = read_keys_owned;
Err(ckpt5_hashmap_array_surface("keys"))
}
pub fn v2_values(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("HashMap.values() takes no arguments"));
}
let _ = as_hashmap(&args[0])?;
let _ = kref_len;
Err(ckpt5_hashmap_array_surface("values"))
}
pub fn v2_entries(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("HashMap.entries() takes no arguments"));
}
build_entries_array(&args[0])
}
pub fn v2_len(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("HashMap.len() takes no arguments"));
}
let map = as_hashmap(&args[0])?;
Ok(KindedSlot::from_int(map.len() as i64))
}
pub fn v2_is_empty(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("HashMap.isEmpty() takes no arguments"));
}
let map = as_hashmap(&args[0])?;
Ok(KindedSlot::from_bool(map.is_empty()))
}
pub fn v2_get_or_default(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 3 {
return Err(type_error(
"HashMap.getOrDefault() requires exactly 2 arguments (key, default)",
));
}
let map = as_hashmap(&args[0])?;
let key = as_string_key(&args[1])?;
Ok(get_kinded(&map, key).unwrap_or_else(|| args[2].clone()))
}
pub fn v2_to_array(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 1 {
return Err(type_error("HashMap.toArray() takes no arguments"));
}
build_entries_array(&args[0])
}
fn build_entries_array(receiver: &KindedSlot) -> Result<KindedSlot, VMError> {
let _ = as_hashmap(receiver)?;
let _ = entry_object_at;
Err(ckpt5_hashmap_array_surface("entries/toArray"))
}
pub fn v2_set(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 3 {
return Err(type_error(
"HashMap.set() requires exactly 2 arguments (key, value)",
));
}
let map_arc = as_hashmap(&args[0])?;
let key = as_string_key(&args[1])?.to_owned();
let value_slot = &args[2];
let new_kref = set_kinded(&map_arc, &key, value_slot)?;
Ok(KindedSlot::from_hashmap(std::sync::Arc::new(new_kref)))
}
fn set_kinded(
map: &HashMapKindedRef,
key: &str,
value_slot: &KindedSlot,
) -> Result<HashMapKindedRef, VMError> {
if map.is_empty() && map.values_kind() != value_kind_hint(value_slot) {
if let Some(promoted) = empty_set_with_promotion(key, value_slot)? {
return Ok(promoted);
}
}
match map {
HashMapKindedRef::I64(arc) => {
let v = match value_slot.kind {
NativeKind::Int64 => value_slot.as_i64().ok_or_else(|| {
type_error("HashMap.method set() -> Int64 slot bits not a valid integer")
})?,
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, int>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v) };
Ok(HashMapKindedRef::I64(new_arc))
}
HashMapKindedRef::F64(arc) => {
let v = match value_slot.kind {
NativeKind::Float64 => value_slot.as_f64().ok_or_else(|| {
type_error("HashMap.method set() -> Float64 slot bits not a valid f64")
})?,
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, number>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v) };
Ok(HashMapKindedRef::F64(new_arc))
}
HashMapKindedRef::Bool(arc) => {
let v: u8 = match value_slot.kind {
NativeKind::Bool => {
let b = value_slot.as_bool().ok_or_else(|| {
type_error("HashMap.method set() -> Bool slot bits not a valid bool")
})?;
if b {
1
} else {
0
}
}
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, bool>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v) };
Ok(HashMapKindedRef::Bool(new_arc))
}
HashMapKindedRef::Char(arc) => {
let v: char = match value_slot.kind {
NativeKind::Char => value_slot.as_char().ok_or_else(|| {
type_error("HashMap.method set() -> Char slot bits not a valid char")
})?,
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, char>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v) };
Ok(HashMapKindedRef::Char(new_arc))
}
HashMapKindedRef::String(arc) => {
let v_ptr = string_slot_to_v2_ptr(value_slot).ok_or_else(|| {
type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, string>",
value_slot.kind
))
})?;
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v_ptr) };
Ok(HashMapKindedRef::String(new_arc))
}
HashMapKindedRef::Decimal(arc) => {
let v_ptr: *const shape_value::v2::decimal_obj::DecimalObj =
match value_slot.kind {
NativeKind::DecimalV2 => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error(
"HashMap.method set() -> DecimalV2 slot bits null",
));
}
let ptr = bits as *const shape_value::v2::decimal_obj::DecimalObj;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
ptr
}
NativeKind::Ptr(HeapKind::Decimal) => {
match value_slot.slot.as_heap_value() {
HeapValue::Decimal(d) => {
shape_value::v2::decimal_obj::DecimalObj::new(**d)
as *const _
}
_ => {
return Err(type_error(
"HashMap.method set() -> Ptr(Decimal) slot heap arm mismatched",
))
}
}
}
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, decimal>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v_ptr) };
Ok(HashMapKindedRef::Decimal(new_arc))
}
HashMapKindedRef::TypedObject(arc) => {
let v_ptr: TypedObjectPtr = match value_slot.kind {
NativeKind::Ptr(HeapKind::TypedObject) => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error(
"HashMap.method set() -> TypedObject slot bits null",
));
}
let ptr = bits as *const TypedObjectStorage;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
TypedObjectPtr::new(ptr)
}
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, TypedObject>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v_ptr) };
Ok(HashMapKindedRef::TypedObject(new_arc))
}
HashMapKindedRef::TraitObject(arc) => {
let v_ptr: TraitObjectPtr = match value_slot.kind {
NativeKind::Ptr(HeapKind::TraitObject) => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error(
"HashMap.method set() -> TraitObject slot bits null",
));
}
let ptr = bits as *const shape_value::heap_value::TraitObjectStorage;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
TraitObjectPtr::new(ptr)
}
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, TraitObject>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v_ptr) };
Ok(HashMapKindedRef::TraitObject(new_arc))
}
HashMapKindedRef::HashMap(arc) => {
let v_kref: HashMapKindedRef = match value_slot.kind {
NativeKind::Ptr(HeapKind::HashMap) => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error(
"HashMap.method set() -> HashMap slot bits null",
));
}
unsafe {
Arc::increment_strong_count(
bits as *const HashMapKindedRef,
);
let arc_outer = Arc::<HashMapKindedRef>::from_raw(
bits as *const HashMapKindedRef,
);
let cloned: HashMapKindedRef = (*arc_outer).clone();
drop(arc_outer);
cloned
}
}
other => {
return Err(type_error(format!(
"HashMap.set(): value kind {:?} incompatible with HashMap<string, HashMap>",
other
)))
}
};
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).insert(key, v_kref) };
Ok(HashMapKindedRef::HashMap(new_arc))
}
}
}
fn value_kind_hint(slot: &KindedSlot) -> NativeKind {
slot.kind
}
fn empty_set_with_promotion(
key: &str,
value_slot: &KindedSlot,
) -> Result<Option<HashMapKindedRef>, VMError> {
use shape_value::heap_value::HashMapData;
match value_slot.kind {
NativeKind::Int64 => {
let v = value_slot
.as_i64()
.ok_or_else(|| type_error("HashMap.method set() -> Int64 slot bits not a valid integer"))?;
let mut data: HashMapData<i64> = HashMapData::new();
unsafe { data.insert(key, v) };
Ok(Some(HashMapKindedRef::I64(Arc::new(data))))
}
NativeKind::Float64 => {
let v = value_slot
.as_f64()
.ok_or_else(|| type_error("HashMap.method set() -> Float64 slot bits not a valid f64"))?;
let mut data: HashMapData<f64> = HashMapData::new();
unsafe { data.insert(key, v) };
Ok(Some(HashMapKindedRef::F64(Arc::new(data))))
}
NativeKind::Bool => {
let b = value_slot
.as_bool()
.ok_or_else(|| type_error("HashMap.method set() -> Bool slot bits not a valid bool"))?;
let mut data: HashMapData<u8> = HashMapData::new();
unsafe { data.insert(key, if b { 1 } else { 0 }) };
Ok(Some(HashMapKindedRef::Bool(Arc::new(data))))
}
NativeKind::Char => {
let c = value_slot
.as_char()
.ok_or_else(|| type_error("HashMap.method set() -> Char slot bits not a valid char"))?;
let mut data: HashMapData<char> = HashMapData::new();
unsafe { data.insert(key, c) };
Ok(Some(HashMapKindedRef::Char(Arc::new(data))))
}
NativeKind::String | NativeKind::Ptr(HeapKind::String) => {
let v_ptr = string_slot_to_v2_ptr(value_slot).ok_or_else(|| {
type_error("HashMap.method set() -> string slot bits could not be projected to *const StringObj")
})?;
let mut data: HashMapData<*const shape_value::v2::string_obj::StringObj> =
HashMapData::new();
unsafe { data.insert(key, v_ptr) };
Ok(Some(HashMapKindedRef::String(Arc::new(data))))
}
NativeKind::DecimalV2 => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method set() -> DecimalV2 slot bits null"));
}
let ptr = bits as *const shape_value::v2::decimal_obj::DecimalObj;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
let mut data: HashMapData<*const shape_value::v2::decimal_obj::DecimalObj> =
HashMapData::new();
unsafe { data.insert(key, ptr) };
Ok(Some(HashMapKindedRef::Decimal(Arc::new(data))))
}
NativeKind::Ptr(HeapKind::Decimal) => {
match value_slot.slot.as_heap_value() {
HeapValue::Decimal(d) => {
let new_obj = shape_value::v2::decimal_obj::DecimalObj::new(**d);
let mut data: HashMapData<
*const shape_value::v2::decimal_obj::DecimalObj,
> = HashMapData::new();
unsafe { data.insert(key, new_obj as *const _) };
Ok(Some(HashMapKindedRef::Decimal(Arc::new(data))))
}
_ => Ok(None),
}
}
NativeKind::Ptr(HeapKind::TypedObject) => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method set() -> TypedObject slot bits null"));
}
let ptr = bits as *const TypedObjectStorage;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
let to_ptr = TypedObjectPtr::new(ptr);
let mut data: HashMapData<TypedObjectPtr> = HashMapData::new();
unsafe { data.insert(key, to_ptr) };
Ok(Some(HashMapKindedRef::TypedObject(Arc::new(data))))
}
NativeKind::Ptr(HeapKind::TraitObject) => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method set() -> TraitObject slot bits null"));
}
let ptr = bits as *const shape_value::heap_value::TraitObjectStorage;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
let tr_ptr = TraitObjectPtr::new(ptr);
let mut data: HashMapData<TraitObjectPtr> = HashMapData::new();
unsafe { data.insert(key, tr_ptr) };
Ok(Some(HashMapKindedRef::TraitObject(Arc::new(data))))
}
NativeKind::Ptr(HeapKind::HashMap) => {
let bits = value_slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method set() -> HashMap slot bits null"));
}
let v_kref: HashMapKindedRef = unsafe {
Arc::increment_strong_count(bits as *const HashMapKindedRef);
let arc_outer = Arc::<HashMapKindedRef>::from_raw(
bits as *const HashMapKindedRef,
);
let cloned: HashMapKindedRef = (*arc_outer).clone();
drop(arc_outer);
cloned
};
let mut data: HashMapData<HashMapKindedRef> = HashMapData::new();
unsafe { data.insert(key, v_kref) };
Ok(Some(HashMapKindedRef::HashMap(Arc::new(data))))
}
_ => Ok(None),
}
}
fn string_slot_to_v2_ptr(slot: &KindedSlot) -> Option<*const shape_value::v2::string_obj::StringObj> {
use shape_value::v2::string_obj::StringObj;
let s_owned = match slot.kind {
NativeKind::String => slot.as_str().map(|s| s.to_owned())?,
NativeKind::Ptr(HeapKind::String) => match slot.slot.as_heap_value() {
HeapValue::String(arc) => (**arc).clone(),
_ => return None,
},
_ => return None,
};
Some(StringObj::new(&s_owned) as *const StringObj)
}
pub fn v2_delete(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.delete() requires exactly 1 argument (key)",
));
}
let map_arc = as_hashmap(&args[0])?;
let key = as_string_key(&args[1])?.to_owned();
let new_kref = delete_kinded(&map_arc, &key);
Ok(KindedSlot::from_hashmap(std::sync::Arc::new(new_kref)))
}
fn delete_kinded(map: &HashMapKindedRef, key: &str) -> HashMapKindedRef {
match map {
HashMapKindedRef::I64(arc) => {
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).remove(key) }; HashMapKindedRef::I64(new_arc)
}
HashMapKindedRef::F64(arc) => {
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).remove(key) };
HashMapKindedRef::F64(new_arc)
}
HashMapKindedRef::Bool(arc) => {
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).remove(key) };
HashMapKindedRef::Bool(new_arc)
}
HashMapKindedRef::Char(arc) => {
let mut new_arc = Arc::clone(arc);
unsafe { Arc::make_mut(&mut new_arc).remove(key) };
HashMapKindedRef::Char(new_arc)
}
HashMapKindedRef::String(arc) => {
let mut new_arc = Arc::clone(arc);
let removed = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
if let Some(ptr) = removed {
unsafe {
use shape_value::v2::heap_element::HeapElement;
shape_value::v2::string_obj::StringObj::release_elem(ptr);
}
}
HashMapKindedRef::String(new_arc)
}
HashMapKindedRef::Decimal(arc) => {
let mut new_arc = Arc::clone(arc);
let removed = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
if let Some(ptr) = removed {
unsafe {
use shape_value::v2::heap_element::HeapElement;
shape_value::v2::decimal_obj::DecimalObj::release_elem(ptr);
}
}
HashMapKindedRef::Decimal(new_arc)
}
HashMapKindedRef::TypedObject(arc) => {
let mut new_arc = Arc::clone(arc);
let _ = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
HashMapKindedRef::TypedObject(new_arc)
}
HashMapKindedRef::TraitObject(arc) => {
let mut new_arc = Arc::clone(arc);
let _ = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
HashMapKindedRef::TraitObject(new_arc)
}
HashMapKindedRef::HashMap(arc) => {
let mut new_arc = Arc::clone(arc);
let _ = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
HashMapKindedRef::HashMap(new_arc)
}
}
}
pub fn v2_remove(
vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.remove() requires exactly 1 argument (key)",
));
}
let map_arc = as_hashmap(&args[0])?;
let key = as_string_key(&args[1])?.to_owned();
let (new_kref, popped_slot) = remove_kinded(&map_arc, &key);
let new_self_slot = KindedSlot::from_hashmap(std::sync::Arc::new(new_kref));
vm.push_kinded(new_self_slot.raw(), new_self_slot.kind())?;
std::mem::forget(new_self_slot);
Ok(popped_slot)
}
fn remove_kinded(map: &HashMapKindedRef, key: &str) -> (HashMapKindedRef, KindedSlot) {
match map {
HashMapKindedRef::I64(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped.map(KindedSlot::from_int).unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::I64(new_arc), slot)
}
HashMapKindedRef::F64(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped.map(KindedSlot::from_number).unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::F64(new_arc), slot)
}
HashMapKindedRef::Bool(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped
.map(|v| KindedSlot::from_bool(v != 0))
.unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::Bool(new_arc), slot)
}
HashMapKindedRef::Char(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped.map(KindedSlot::from_char).unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::Char(new_arc), slot)
}
HashMapKindedRef::String(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped
.map(|ptr| {
let s = unsafe {
shape_value::v2::string_obj::StringObj::as_str(ptr).to_owned()
};
unsafe {
use shape_value::v2::heap_element::HeapElement;
shape_value::v2::string_obj::StringObj::release_elem(ptr);
}
KindedSlot::from_string_arc(Arc::new(s))
})
.unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::String(new_arc), slot)
}
HashMapKindedRef::Decimal(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped
.map(KindedSlot::from_decimal_v2_ptr)
.unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::Decimal(new_arc), slot)
}
HashMapKindedRef::TypedObject(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped
.map(|p| KindedSlot::from_typed_object_raw(p.into_raw()))
.unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::TypedObject(new_arc), slot)
}
HashMapKindedRef::TraitObject(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped
.map(|p| KindedSlot::from_trait_object_raw(p.into_raw()))
.unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::TraitObject(new_arc), slot)
}
HashMapKindedRef::HashMap(arc) => {
let mut new_arc = Arc::clone(arc);
let popped = unsafe { Arc::make_mut(&mut new_arc).remove(key) };
let slot = popped
.map(|kref| KindedSlot::from_hashmap(Arc::new(kref)))
.unwrap_or_else(KindedSlot::none);
(HashMapKindedRef::HashMap(new_arc), slot)
}
}
}
pub fn v2_merge(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.merge() requires exactly 1 argument (other)",
));
}
let a = as_hashmap(&args[0])?;
let b = as_hashmap(&args[1])?;
let new_kref = merge_kinded(&a, &b)?;
Ok(KindedSlot::from_hashmap(std::sync::Arc::new(new_kref)))
}
fn merge_kinded(a: &HashMapKindedRef, b: &HashMapKindedRef) -> Result<HashMapKindedRef, VMError> {
let mismatch = || {
type_error(format!(
"HashMap.merge(): value-kind mismatch ({:?} vs {:?}); merge requires \
same-V receivers at this layer",
a.values_kind(),
b.values_kind()
))
};
match (a, b) {
(HashMapKindedRef::I64(arc_a), HashMapKindedRef::I64(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::I64(new_arc))
}
(HashMapKindedRef::F64(arc_a), HashMapKindedRef::F64(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::F64(new_arc))
}
(HashMapKindedRef::Bool(arc_a), HashMapKindedRef::Bool(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::Bool(new_arc))
}
(HashMapKindedRef::Char(arc_a), HashMapKindedRef::Char(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::Char(new_arc))
}
(HashMapKindedRef::String(arc_a), HashMapKindedRef::String(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::String(new_arc))
}
(HashMapKindedRef::Decimal(arc_a), HashMapKindedRef::Decimal(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::Decimal(new_arc))
}
(HashMapKindedRef::TypedObject(arc_a), HashMapKindedRef::TypedObject(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::TypedObject(new_arc))
}
(HashMapKindedRef::TraitObject(arc_a), HashMapKindedRef::TraitObject(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::TraitObject(new_arc))
}
(HashMapKindedRef::HashMap(arc_a), HashMapKindedRef::HashMap(arc_b)) => {
let mut new_arc = Arc::clone(arc_a);
unsafe { Arc::make_mut(&mut new_arc).merge(arc_b) };
Ok(HashMapKindedRef::HashMap(new_arc))
}
_ => Err(mismatch()),
}
}
pub fn v2_for_each(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.forEach() requires exactly 1 argument (callback)",
));
}
let map = as_hashmap(&args[0])?;
let closure = &args[1];
let keys_ptr = match &*map {
HashMapKindedRef::I64(arc) => arc.keys,
HashMapKindedRef::F64(arc) => arc.keys,
HashMapKindedRef::Bool(arc) => arc.keys,
HashMapKindedRef::Char(arc) => arc.keys,
HashMapKindedRef::String(arc) => arc.keys,
HashMapKindedRef::Decimal(arc) => arc.keys,
HashMapKindedRef::TypedObject(arc) => arc.keys,
HashMapKindedRef::TraitObject(arc) => arc.keys,
HashMapKindedRef::HashMap(arc) => arc.keys,
};
let keys_vec: Vec<Arc<String>> = unsafe { read_keys_owned(keys_ptr) };
for (i, key_arc) in keys_vec.into_iter().enumerate() {
let (key_slot, value_slot) = read_entry_kinded(&map, i, key_arc);
let _result = vm.call_value_immediate_nb(
closure,
&[key_slot, value_slot],
ctx.as_deref_mut(),
)?;
}
Ok(KindedSlot::none())
}
pub fn v2_filter(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.filter() requires exactly 1 argument (predicate)",
));
}
let map = as_hashmap(&args[0])?;
let closure = &args[1];
let keys_ptr = match &*map {
HashMapKindedRef::I64(arc) => arc.keys,
HashMapKindedRef::F64(arc) => arc.keys,
HashMapKindedRef::Bool(arc) => arc.keys,
HashMapKindedRef::Char(arc) => arc.keys,
HashMapKindedRef::String(arc) => arc.keys,
HashMapKindedRef::Decimal(arc) => arc.keys,
HashMapKindedRef::TypedObject(arc) => arc.keys,
HashMapKindedRef::TraitObject(arc) => arc.keys,
HashMapKindedRef::HashMap(arc) => arc.keys,
};
let keys_vec: Vec<Arc<String>> = unsafe { read_keys_owned(keys_ptr) };
let mut kept_indices: Vec<usize> = Vec::with_capacity(keys_vec.len());
let mut kept_keys: Vec<Arc<String>> = Vec::new();
for (i, key_arc) in keys_vec.into_iter().enumerate() {
let (key_slot, value_slot) =
read_entry_kinded(&map, i, Arc::clone(&key_arc));
let result = vm.call_value_immediate_nb(
closure,
&[key_slot, value_slot],
ctx.as_deref_mut(),
)?;
match result.kind {
NativeKind::Bool => {
if result.slot.raw() != 0 {
kept_indices.push(i);
kept_keys.push(key_arc);
}
}
other => {
return Err(type_error(format!(
"HashMap.filter(): predicate must return bool, got kind {:?}",
other
)))
}
}
}
let kept = build_filtered_kref(&map, &kept_indices, &kept_keys)?;
Ok(KindedSlot::from_hashmap(Arc::new(kept)))
}
pub fn v2_map(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.map() requires exactly 1 argument (mapper)",
));
}
let map = as_hashmap(&args[0])?;
let closure = &args[1];
let keys_ptr = match &*map {
HashMapKindedRef::I64(arc) => arc.keys,
HashMapKindedRef::F64(arc) => arc.keys,
HashMapKindedRef::Bool(arc) => arc.keys,
HashMapKindedRef::Char(arc) => arc.keys,
HashMapKindedRef::String(arc) => arc.keys,
HashMapKindedRef::Decimal(arc) => arc.keys,
HashMapKindedRef::TypedObject(arc) => arc.keys,
HashMapKindedRef::TraitObject(arc) => arc.keys,
HashMapKindedRef::HashMap(arc) => arc.keys,
};
let keys_vec: Vec<Arc<String>> = unsafe { read_keys_owned(keys_ptr) };
let mut results: Vec<(Arc<String>, KindedSlot)> = Vec::with_capacity(keys_vec.len());
for (i, key_arc) in keys_vec.into_iter().enumerate() {
let (key_slot, value_slot) =
read_entry_kinded(&map, i, Arc::clone(&key_arc));
let result = vm.call_value_immediate_nb(
closure,
&[key_slot, value_slot],
ctx.as_deref_mut(),
)?;
results.push((key_arc, result));
}
let kref = build_kref_from_kinded_results(results)?;
Ok(KindedSlot::from_hashmap(Arc::new(kref)))
}
fn build_kref_from_kinded_results(
results: Vec<(Arc<String>, KindedSlot)>,
) -> Result<HashMapKindedRef, VMError> {
if results.is_empty() {
return Ok(HashMapKindedRef::String(Arc::new(HashMapData::new())));
}
let first_kind = results[0].1.kind;
for (i, (_, slot)) in results.iter().enumerate().skip(1) {
if slot.kind != first_kind {
return Err(type_error(format!(
"HashMap.map(): heterogeneous-kind results not supported \
(element 0 kind={:?}, element {} kind={:?})",
first_kind, i, slot.kind
)));
}
}
match first_kind {
NativeKind::Int64 => {
let mut data: HashMapData<i64> = HashMapData::new();
for (key, slot) in results.iter() {
let v = slot.as_i64().ok_or_else(|| {
type_error("HashMap.method map() -> Int64 slot bits invalid")
})?;
unsafe { data.insert(key.as_str(), v) };
}
drop(results);
Ok(HashMapKindedRef::I64(Arc::new(data)))
}
NativeKind::Float64 => {
let mut data: HashMapData<f64> = HashMapData::new();
for (key, slot) in results.iter() {
let v = slot.as_f64().ok_or_else(|| {
type_error("HashMap.method map() -> Float64 slot bits invalid")
})?;
unsafe { data.insert(key.as_str(), v) };
}
drop(results);
Ok(HashMapKindedRef::F64(Arc::new(data)))
}
NativeKind::Bool => {
let mut data: HashMapData<u8> = HashMapData::new();
for (key, slot) in results.iter() {
let b = slot.as_bool().ok_or_else(|| {
type_error("HashMap.method map() -> Bool slot bits invalid")
})?;
unsafe { data.insert(key.as_str(), if b { 1 } else { 0 }) };
}
drop(results);
Ok(HashMapKindedRef::Bool(Arc::new(data)))
}
NativeKind::Char => {
let mut data: HashMapData<char> = HashMapData::new();
for (key, slot) in results.iter() {
let c = slot.as_char().ok_or_else(|| {
type_error("HashMap.method map() -> Char slot bits invalid")
})?;
unsafe { data.insert(key.as_str(), c) };
}
drop(results);
Ok(HashMapKindedRef::Char(Arc::new(data)))
}
NativeKind::String | NativeKind::Ptr(HeapKind::String) => {
let mut data: HashMapData<*const shape_value::v2::string_obj::StringObj> =
HashMapData::new();
for (key, slot) in results.iter() {
let s_arc: Arc<String> = {
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method map() -> String slot bits null"));
}
unsafe {
Arc::increment_strong_count(bits as *const String);
Arc::from_raw(bits as *const String)
}
};
let new_obj = shape_value::v2::string_obj::StringObj::new(s_arc.as_str());
unsafe { data.insert(key.as_str(), new_obj as *const _) };
drop(s_arc); }
Ok(HashMapKindedRef::String(Arc::new(data)))
}
NativeKind::Ptr(HeapKind::TypedObject) => {
let mut data: HashMapData<TypedObjectPtr> = HashMapData::new();
for (key, slot) in results.iter() {
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method map() -> TypedObject slot bits null"));
}
let ptr = bits as *const TypedObjectStorage;
unsafe { shape_value::v2::refcount::v2_retain(&(*ptr).header); }
unsafe { data.insert(key.as_str(), TypedObjectPtr::new(ptr)) };
}
Ok(HashMapKindedRef::TypedObject(Arc::new(data)))
}
NativeKind::Ptr(HeapKind::HashMap) => {
let mut data: HashMapData<HashMapKindedRef> = HashMapData::new();
for (key, slot) in results.iter() {
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method map() -> HashMap slot bits null"));
}
let cloned_kref: HashMapKindedRef = unsafe {
Arc::increment_strong_count(bits as *const HashMapKindedRef);
let arc_outer = Arc::<HashMapKindedRef>::from_raw(
bits as *const HashMapKindedRef,
);
let cloned: HashMapKindedRef = (*arc_outer).clone();
drop(arc_outer);
cloned
};
unsafe { data.insert(key.as_str(), cloned_kref) };
}
Ok(HashMapKindedRef::HashMap(Arc::new(data)))
}
other => Err(type_error(format!(
"HashMap.map(): result kind {:?} not supported — only \
int, number, bool, char, string, TypedObject, HashMap result \
Vs land at this layer (Decimal / TraitObject value V requires \
a separate pull-from-slot helper, tracked alongside HashMap \
value-side V cluster).",
other
))),
}
}
pub fn v2_reduce(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 3 {
return Err(type_error(
"HashMap.reduce() requires exactly 2 arguments (reducer, initial)",
));
}
let map = as_hashmap(&args[0])?;
let closure = &args[1];
let mut acc = args[2].clone();
let keys_ptr = match &*map {
HashMapKindedRef::I64(arc) => arc.keys,
HashMapKindedRef::F64(arc) => arc.keys,
HashMapKindedRef::Bool(arc) => arc.keys,
HashMapKindedRef::Char(arc) => arc.keys,
HashMapKindedRef::String(arc) => arc.keys,
HashMapKindedRef::Decimal(arc) => arc.keys,
HashMapKindedRef::TypedObject(arc) => arc.keys,
HashMapKindedRef::TraitObject(arc) => arc.keys,
HashMapKindedRef::HashMap(arc) => arc.keys,
};
let keys_vec: Vec<Arc<String>> = unsafe { read_keys_owned(keys_ptr) };
for (i, key_arc) in keys_vec.into_iter().enumerate() {
let (key_slot, value_slot) = read_entry_kinded(&map, i, key_arc);
let result = vm.call_value_immediate_nb(
closure,
&[acc, key_slot, value_slot],
ctx.as_deref_mut(),
)?;
acc = result;
}
Ok(acc)
}
pub fn v2_group_by(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() != 2 {
return Err(type_error(
"HashMap.groupBy() requires exactly 1 argument (key-extractor)",
));
}
let map = as_hashmap(&args[0])?;
let closure = &args[1];
let keys_ptr = match &*map {
HashMapKindedRef::I64(arc) => arc.keys,
HashMapKindedRef::F64(arc) => arc.keys,
HashMapKindedRef::Bool(arc) => arc.keys,
HashMapKindedRef::Char(arc) => arc.keys,
HashMapKindedRef::String(arc) => arc.keys,
HashMapKindedRef::Decimal(arc) => arc.keys,
HashMapKindedRef::TypedObject(arc) => arc.keys,
HashMapKindedRef::TraitObject(arc) => arc.keys,
HashMapKindedRef::HashMap(arc) => arc.keys,
};
let keys_vec: Vec<Arc<String>> = unsafe { read_keys_owned(keys_ptr) };
let mut group_order: Vec<Arc<String>> = Vec::new();
let mut group_lookup: std::collections::HashMap<String, usize> =
std::collections::HashMap::new();
let mut bucket_indices: Vec<Vec<usize>> = Vec::new();
let mut bucket_keys: Vec<Vec<Arc<String>>> = Vec::new();
for (i, key_arc) in keys_vec.into_iter().enumerate() {
let (key_slot, value_slot) =
read_entry_kinded(&map, i, Arc::clone(&key_arc));
let result = vm.call_value_immediate_nb(
closure,
&[key_slot, value_slot],
ctx.as_deref_mut(),
)?;
let group_key: String = match result.kind {
NativeKind::String => result
.as_str()
.ok_or_else(|| {
type_error("HashMap.method groupBy() -> String slot bits null")
})?
.to_owned(),
NativeKind::Ptr(HeapKind::String) => match result.slot.as_heap_value() {
HeapValue::String(s) => (**s).clone(),
_ => {
return Err(type_error(
"HashMap.method groupBy() -> Ptr(String) slot heap arm mismatched",
))
}
},
other => {
return Err(type_error(format!(
"HashMap.groupBy(): key-extractor must return string, \
got kind {:?}",
other
)))
}
};
let group_idx = if let Some(&g) = group_lookup.get(&group_key) {
g
} else {
let g = group_order.len();
group_order.push(Arc::new(group_key.clone()));
group_lookup.insert(group_key, g);
bucket_indices.push(Vec::new());
bucket_keys.push(Vec::new());
g
};
bucket_indices[group_idx].push(i);
bucket_keys[group_idx].push(key_arc);
}
let mut outer: HashMapData<HashMapKindedRef> = HashMapData::new();
for (g, group_key) in group_order.iter().enumerate() {
let bucket =
build_filtered_kref(&map, &bucket_indices[g], &bucket_keys[g])?;
unsafe { outer.insert(group_key.as_str(), bucket) };
}
Ok(KindedSlot::from_hashmap(Arc::new(HashMapKindedRef::HashMap(
Arc::new(outer),
))))
}
fn result_slot_to_heap_value_arc(result: &KindedSlot) -> Result<Arc<HeapValue>, VMError> {
match result.kind {
NativeKind::Int64 => {
let i = result.as_i64().ok_or_else(|| {
type_error("HashMap.method map() -> Int64 slot bits not a valid integer")
})?;
Ok(Arc::new(HeapValue::BigInt(Arc::new(i))))
}
NativeKind::Float64 => Err(type_error(
"HashMap.method map() -> Float64 result cannot be heap-wrapped (no HeapValue::Number arm)",
)),
NativeKind::Bool => Err(type_error(
"HashMap.method map() -> Bool result cannot be heap-wrapped (no HeapValue::Bool arm)",
)),
NativeKind::String | NativeKind::Ptr(HeapKind::String) => {
let bits = result.slot.raw();
if bits == 0 {
return Err(type_error("HashMap.method map() -> String slot bits null"));
}
let arc = unsafe {
Arc::increment_strong_count(bits as *const String);
Arc::from_raw(bits as *const String)
};
Ok(Arc::new(HeapValue::String(arc)))
}
NativeKind::Ptr(_) => {
let hv: &HeapValue = result.slot.as_heap_value();
Ok(Arc::new(hv.clone()))
}
other => Err(type_error(format!(
"HashMap.map(): result kind {:?} cannot be stored in a HashMap value",
other
))),
}
}
fn result_slot_to_string_arc(result: &KindedSlot) -> Option<Arc<String>> {
match result.kind {
NativeKind::String | NativeKind::Ptr(HeapKind::String) => {
let bits = result.slot.raw();
if bits == 0 {
return None;
}
unsafe {
Arc::increment_strong_count(bits as *const String);
Some(Arc::from_raw(bits as *const String))
}
}
_ => None,
}
}
pub fn v2_iter(
vm: &mut VirtualMachine,
args: &[KindedSlot],
ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
crate::executor::objects::iterator_methods::handle_hashmap_iter(vm, args, ctx)
}