1use std::collections::{HashMap, hash_map};
2use std::fmt;
3use std::hash::{BuildHasherDefault, Hash, Hasher};
4use std::sync::{Arc, OnceLock};
5
6use crate::compiler::TypeSchema;
7
8pub type SharedString = Arc<String>;
9pub type SharedBytes = Arc<Vec<u8>>;
10pub type SharedArray = Arc<Vec<Value>>;
11pub type SharedMap = Arc<VmMap>;
12
13type VmMapStorage = HashMap<MapKey, Value, BuildHasherDefault<StableHasher>>;
14
15#[derive(Clone, Default)]
29pub struct VmMap {
30 entries: VmMapStorage,
31 cached_len: usize,
32}
33
34#[derive(Clone, Debug)]
35struct MapKey(Value);
36
37pub struct VmMapIter<'a> {
38 inner: hash_map::Iter<'a, MapKey, Value>,
39}
40
41pub struct VmMapIntoIter {
42 inner: hash_map::IntoIter<MapKey, Value>,
43}
44
45#[derive(Default)]
46pub(crate) struct StableHasher(u64);
47
48impl Hasher for StableHasher {
49 fn finish(&self) -> u64 {
50 self.0
51 }
52
53 fn write(&mut self, bytes: &[u8]) {
54 const OFFSET_BASIS: u64 = 0xcbf29ce484222325;
55 const PRIME: u64 = 0x100000001b3;
56
57 if self.0 == 0 {
58 self.0 = OFFSET_BASIS;
59 }
60 for byte in bytes {
61 self.0 ^= u64::from(*byte);
62 self.0 = self.0.wrapping_mul(PRIME);
63 }
64 }
65}
66
67impl VmMap {
68 pub fn new() -> Self {
69 Self::default()
70 }
71
72 pub fn from_entries(entries: Vec<(Value, Value)>) -> Self {
73 let mut out = Self::new();
74 for (key, value) in entries {
75 out.insert(key, value);
76 }
77 out
78 }
79
80 pub fn len(&self) -> usize {
81 debug_assert_eq!(self.cached_len, self.entries.len());
82 self.cached_len
83 }
84
85 pub fn is_empty(&self) -> bool {
86 self.len() == 0
87 }
88
89 pub fn iter(&self) -> VmMapIter<'_> {
90 VmMapIter {
91 inner: self.entries.iter(),
92 }
93 }
94
95 pub fn get(&self, key: &Value) -> Option<&Value> {
96 self.entries.get(&MapKey::new(key.clone()))
97 }
98
99 pub fn insert(&mut self, key: Value, value: Value) -> Option<Value> {
100 let replaced = self.entries.insert(MapKey::new(key), value);
101 self.cached_len = self.entries.len();
102 replaced
103 }
104
105 pub fn remove(&mut self, key: &Value) -> Option<Value> {
106 let removed = self.entries.remove(&MapKey::new(key.clone()));
107 self.cached_len = self.entries.len();
108 removed
109 }
110}
111
112#[allow(dead_code)]
113pub(crate) fn vm_map_len_field_offset() -> usize {
114 std::mem::offset_of!(VmMap, cached_len)
115}
116
117impl From<Vec<(Value, Value)>> for VmMap {
118 fn from(value: Vec<(Value, Value)>) -> Self {
119 Self::from_entries(value)
120 }
121}
122
123impl IntoIterator for VmMap {
124 type Item = (Value, Value);
125 type IntoIter = VmMapIntoIter;
126
127 fn into_iter(self) -> Self::IntoIter {
128 VmMapIntoIter {
129 inner: self.entries.into_iter(),
130 }
131 }
132}
133
134impl<'a> IntoIterator for &'a VmMap {
135 type Item = (&'a Value, &'a Value);
136 type IntoIter = VmMapIter<'a>;
137
138 fn into_iter(self) -> Self::IntoIter {
139 self.iter()
140 }
141}
142
143impl fmt::Debug for VmMap {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 f.debug_map().entries(self.iter()).finish()
146 }
147}
148
149impl PartialEq for VmMap {
150 fn eq(&self, other: &Self) -> bool {
151 self.entries == other.entries
152 }
153}
154
155impl Eq for VmMap {}
156
157impl MapKey {
158 fn new(value: Value) -> Self {
159 Self(value)
160 }
161
162 fn value(&self) -> &Value {
163 &self.0
164 }
165
166 fn into_value(self) -> Value {
167 self.0
168 }
169}
170
171impl PartialEq for MapKey {
172 fn eq(&self, other: &Self) -> bool {
173 map_key_eq(&self.0, &other.0)
174 }
175}
176
177impl Eq for MapKey {}
178
179impl Hash for MapKey {
180 fn hash<H: Hasher>(&self, state: &mut H) {
181 hash_map_key(&self.0, state);
182 }
183}
184
185impl<'a> Iterator for VmMapIter<'a> {
186 type Item = (&'a Value, &'a Value);
187
188 fn next(&mut self) -> Option<Self::Item> {
189 self.inner.next().map(|(key, value)| (key.value(), value))
190 }
191}
192
193impl Iterator for VmMapIntoIter {
194 type Item = (Value, Value);
195
196 fn next(&mut self) -> Option<Self::Item> {
197 self.inner
198 .next()
199 .map(|(key, value)| (key.into_value(), value))
200 }
201}
202
203fn hash_map_key(value: &Value, state: &mut impl Hasher) {
204 match value {
205 Value::Null => {
206 6u8.hash(state);
207 }
208 Value::Int(value) => {
209 0u8.hash(state);
210 value.hash(state);
211 }
212 Value::Float(value) => {
213 1u8.hash(state);
214 canonical_float_key_bits(*value).hash(state);
215 }
216 Value::Bool(value) => {
217 2u8.hash(state);
218 value.hash(state);
219 }
220 Value::String(value) => {
221 3u8.hash(state);
222 value.hash(state);
223 }
224 Value::Bytes(value) => {
225 4u8.hash(state);
226 value.hash(state);
227 }
228 Value::Array(values) => {
229 5u8.hash(state);
230 Arc::as_ptr(values).hash(state);
231 }
232 Value::Map(entries) => {
233 6u8.hash(state);
234 Arc::as_ptr(entries).hash(state);
235 }
236 }
237}
238
239fn map_key_eq(lhs: &Value, rhs: &Value) -> bool {
240 match (lhs, rhs) {
241 (Value::Null, Value::Null) => true,
242 (Value::Int(lhs), Value::Int(rhs)) => lhs == rhs,
243 (Value::Float(lhs), Value::Float(rhs)) => {
244 canonical_float_key_bits(*lhs) == canonical_float_key_bits(*rhs)
245 }
246 (Value::Bool(lhs), Value::Bool(rhs)) => lhs == rhs,
247 (Value::String(lhs), Value::String(rhs)) => lhs == rhs,
248 (Value::Bytes(lhs), Value::Bytes(rhs)) => lhs == rhs,
249 (Value::Array(lhs), Value::Array(rhs)) => Arc::ptr_eq(lhs, rhs),
250 (Value::Map(lhs), Value::Map(rhs)) => Arc::ptr_eq(lhs, rhs),
251 _ => false,
252 }
253}
254
255#[allow(dead_code)]
261pub(crate) fn hash_value(value: &Value, state: &mut impl Hasher) {
262 match value {
263 Value::Null => {
264 6u8.hash(state);
265 }
266 Value::Int(value) => {
267 0u8.hash(state);
268 value.hash(state);
269 }
270 Value::Float(value) => {
271 1u8.hash(state);
272 canonical_float_key_bits(*value).hash(state);
273 }
274 Value::Bool(value) => {
275 2u8.hash(state);
276 value.hash(state);
277 }
278 Value::String(value) => {
279 3u8.hash(state);
280 value.hash(state);
281 }
282 Value::Bytes(value) => {
283 4u8.hash(state);
284 value.hash(state);
285 }
286 Value::Array(values) => {
287 5u8.hash(state);
288 values.len().hash(state);
289 for value in values.iter() {
290 hash_value(value, state);
291 }
292 }
293 Value::Map(entries) => {
294 6u8.hash(state);
295 entries.len().hash(state);
296 let mut entry_hashes = entries
297 .iter()
298 .map(|(key, value)| {
299 let mut entry_hasher = StableHasher::default();
300 hash_value(key, &mut entry_hasher);
301 hash_value(value, &mut entry_hasher);
302 entry_hasher.finish()
303 })
304 .collect::<Vec<_>>();
305 entry_hashes.sort_unstable();
306 for entry_hash in entry_hashes {
307 entry_hash.hash(state);
308 }
309 }
310 }
311}
312
313fn canonical_float_key_bits(value: f64) -> u64 {
314 if value == 0.0 {
315 0.0f64.to_bits()
316 } else {
317 value.to_bits()
318 }
319}
320
321#[derive(Clone, Debug)]
322pub enum Value {
323 Null,
324 Int(i64),
325 Float(f64),
326 Bool(bool),
327 String(SharedString),
328 Bytes(SharedBytes),
329 Array(SharedArray),
330 Map(SharedMap),
331}
332
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
334#[repr(u8)]
335pub enum ValueType {
336 Unknown = 0,
337 Null = 1,
338 Int = 2,
339 Float = 3,
340 Bool = 4,
341 String = 5,
342 Bytes = 6,
343 Array = 7,
344 Map = 8,
345}
346
347#[derive(Clone, Debug, Default, PartialEq, Eq)]
348pub struct TypeMap {
349 pub strict_types: bool,
350 pub local_types: Vec<ValueType>,
351 pub local_schemas: Vec<Option<TypeSchema>>,
352 pub callable_slots: Vec<bool>,
353 pub optional_slots: Vec<bool>,
354 pub operand_types: HashMap<usize, (ValueType, ValueType)>,
355}
356
357impl Value {
358 pub fn string(value: impl Into<String>) -> Self {
359 Self::String(Arc::new(value.into()))
360 }
361
362 pub fn array(values: Vec<Value>) -> Self {
363 Self::Array(Arc::new(values))
364 }
365
366 pub fn bytes(value: impl Into<Vec<u8>>) -> Self {
367 Self::Bytes(Arc::new(value.into()))
368 }
369
370 pub fn map(entries: Vec<(Value, Value)>) -> Self {
371 Self::Map(Arc::new(VmMap::from(entries)))
372 }
373
374 pub fn into_owned_string(self) -> Result<String, Self> {
375 match self {
376 Self::String(value) => Ok(unwrap_or_clone_shared(value)),
377 other => Err(other),
378 }
379 }
380
381 pub fn into_owned_array(self) -> Result<Vec<Value>, Self> {
382 match self {
383 Self::Array(values) => Ok(unwrap_or_clone_shared(values)),
384 other => Err(other),
385 }
386 }
387
388 pub fn into_owned_bytes(self) -> Result<Vec<u8>, Self> {
389 match self {
390 Self::Bytes(value) => Ok(unwrap_or_clone_shared(value)),
391 other => Err(other),
392 }
393 }
394
395 pub fn into_owned_map(self) -> Result<VmMap, Self> {
396 match self {
397 Self::Map(entries) => Ok(unwrap_or_clone_shared(entries)),
398 other => Err(other),
399 }
400 }
401}
402
403pub(crate) fn unwrap_or_clone_shared<T: Clone>(value: Arc<T>) -> T {
404 match Arc::try_unwrap(value) {
405 Ok(inner) => inner,
406 Err(shared) => (*shared).clone(),
407 }
408}
409
410impl PartialEq for Value {
411 fn eq(&self, other: &Self) -> bool {
412 match (self, other) {
413 (Self::Null, Self::Null) => true,
414 (Self::Int(lhs), Self::Int(rhs)) => lhs == rhs,
415 (Self::Float(lhs), Self::Float(rhs)) => lhs == rhs,
416 (Self::Bool(lhs), Self::Bool(rhs)) => lhs == rhs,
417 (Self::String(lhs), Self::String(rhs)) => lhs == rhs,
418 (Self::Bytes(lhs), Self::Bytes(rhs)) => lhs == rhs,
419 (Self::Array(lhs), Self::Array(rhs)) => lhs == rhs,
420 (Self::Map(lhs), Self::Map(rhs)) => lhs == rhs,
421 _ => false,
422 }
423 }
424}
425
426#[derive(Clone, Debug, PartialEq, Eq, Hash)]
427pub struct HostImport {
428 pub name: String,
429 pub arity: u8,
430 pub return_type: ValueType,
431}
432
433#[allow(dead_code)]
434#[derive(Debug)]
435pub(crate) struct DecodedInstructionData {
436 pub(crate) ldc_values: Box<[Option<Value>]>,
437 pub(crate) jump_targets: Box<[Option<usize>]>,
438 pub(crate) local_indices: Box<[Option<u8>]>,
439}
440
441impl DecodedInstructionData {
442 fn build(program: &Program) -> Self {
443 let mut ldc_values = vec![None; program.code.len()];
444 let mut jump_targets = vec![None; program.code.len()];
445 let mut local_indices = vec![None; program.code.len()];
446 let mut ip = 0usize;
447 while ip < program.code.len() {
448 let opcode = match OpCode::try_from(program.code[ip]) {
449 Ok(opcode) => opcode,
450 Err(_) => break,
451 };
452 match opcode {
453 OpCode::Ldc => {
454 if let Some(raw_index) = read_u32_at(&program.code, ip + 1)
455 && let Some(value) = program.constants.get(raw_index as usize)
456 {
457 ldc_values[ip] = Some(value.clone());
458 }
459 }
460 OpCode::Br | OpCode::Brfalse => {
461 if let Some(target) = read_u32_at(&program.code, ip + 1) {
462 jump_targets[ip] = Some(target as usize);
463 }
464 }
465 OpCode::Ldloc | OpCode::Stloc => {
466 if let Some(index) = program.code.get(ip + 1).copied() {
467 local_indices[ip] = Some(index);
468 }
469 }
470 _ => {}
471 }
472 ip = ip.saturating_add(1 + opcode.operand_len());
473 }
474 Self {
475 ldc_values: ldc_values.into_boxed_slice(),
476 jump_targets: jump_targets.into_boxed_slice(),
477 local_indices: local_indices.into_boxed_slice(),
478 }
479 }
480}
481
482#[derive(Clone, Debug)]
483pub struct Program {
484 pub constants: Vec<Value>,
485 pub code: Vec<u8>,
486 pub local_count: usize,
487 pub imports: Vec<HostImport>,
488 pub debug: Option<crate::debug_info::DebugInfo>,
489 pub type_map: Option<TypeMap>,
490 #[allow(dead_code)]
491 decoded_instruction_data_cache: Arc<OnceLock<Arc<DecodedInstructionData>>>,
492 operand_type_hints_cache: Arc<OnceLock<Option<Arc<[u8]>>>>,
493}
494
495impl Program {
496 pub fn new(constants: Vec<Value>, code: Vec<u8>) -> Self {
497 let local_count = infer_local_count_from_code(&code);
498 Self {
499 constants,
500 code,
501 local_count,
502 imports: Vec::new(),
503 debug: None,
504 type_map: None,
505 decoded_instruction_data_cache: Arc::new(OnceLock::new()),
506 operand_type_hints_cache: Arc::new(OnceLock::new()),
507 }
508 }
509
510 pub fn with_debug(
511 constants: Vec<Value>,
512 code: Vec<u8>,
513 debug: Option<crate::debug_info::DebugInfo>,
514 ) -> Self {
515 let local_count = infer_local_count_from_code(&code);
516 Self {
517 constants,
518 code,
519 local_count,
520 imports: Vec::new(),
521 debug,
522 type_map: None,
523 decoded_instruction_data_cache: Arc::new(OnceLock::new()),
524 operand_type_hints_cache: Arc::new(OnceLock::new()),
525 }
526 }
527
528 pub fn with_imports_and_debug(
529 constants: Vec<Value>,
530 code: Vec<u8>,
531 imports: Vec<HostImport>,
532 debug: Option<crate::debug_info::DebugInfo>,
533 ) -> Self {
534 let local_count = infer_local_count_from_code(&code);
535 Self {
536 constants,
537 code,
538 local_count,
539 imports,
540 debug,
541 type_map: None,
542 decoded_instruction_data_cache: Arc::new(OnceLock::new()),
543 operand_type_hints_cache: Arc::new(OnceLock::new()),
544 }
545 }
546
547 pub fn with_local_count(mut self, local_count: usize) -> Self {
548 self.local_count = local_count;
549 self
550 }
551
552 pub fn with_type_map(mut self, type_map: TypeMap) -> Self {
553 self.type_map = Some(type_map);
554 self.operand_type_hints_cache = Arc::new(OnceLock::new());
555 self
556 }
557
558 #[allow(dead_code)]
559 pub(crate) fn shared_decoded_instruction_data(&self) -> Arc<DecodedInstructionData> {
560 Arc::clone(
561 self.decoded_instruction_data_cache
562 .get_or_init(|| Arc::new(DecodedInstructionData::build(self))),
563 )
564 }
565
566 #[allow(dead_code)]
567 pub(crate) fn shared_operand_type_hints(&self) -> Option<Arc<[u8]>> {
568 self.operand_type_hints_cache
569 .get_or_init(|| build_operand_type_hints(self.code.len(), self.type_map.as_ref()))
570 .clone()
571 }
572}
573
574#[allow(dead_code)]
575fn build_operand_type_hints(code_len: usize, type_map: Option<&TypeMap>) -> Option<Arc<[u8]>> {
576 let type_map = type_map?;
577 if type_map.operand_types.is_empty() {
578 return None;
579 }
580
581 let mut hints = vec![0u8; code_len];
582 for (offset, (lhs, rhs)) in &type_map.operand_types {
583 let Some(entry) = hints.get_mut(*offset) else {
584 continue;
585 };
586 *entry = (*lhs as u8) | ((*rhs as u8) << 4);
587 }
588 Some(Arc::from(hints.into_boxed_slice()))
589}
590
591#[allow(dead_code)]
592fn read_u32_at(code: &[u8], offset: usize) -> Option<u32> {
593 let bytes = code.get(offset..offset + 4)?;
594 Some(u32::from_le_bytes(bytes.try_into().ok()?))
595}
596
597fn infer_local_count_from_code(code: &[u8]) -> usize {
598 let mut ip = 0usize;
599 let mut max_local_index: Option<u8> = None;
600
601 while let Some(&opcode) = code.get(ip) {
602 ip += 1;
603 let Ok(opcode) = OpCode::try_from(opcode) else {
604 break;
605 };
606 let operand_len = opcode.operand_len();
607 if ip + operand_len > code.len() {
608 break;
609 }
610 match opcode {
611 OpCode::Ldloc | OpCode::Stloc => {
612 let index = code[ip];
613 max_local_index = Some(max_local_index.map_or(index, |prev| prev.max(index)));
614 }
615 _ => {}
616 }
617 ip += operand_len;
618 }
619
620 max_local_index.map_or(0, |index| index as usize + 1)
621}
622
623#[derive(Clone, Copy, Debug, PartialEq, Eq)]
624#[repr(u8)]
625pub enum OpCode {
626 Nop = 0x00,
627 Ret = 0x01,
628 Ldc = 0x02,
629 Add = 0x03,
630 Sub = 0x04,
631 Mul = 0x05,
632 Div = 0x06,
633 Neg = 0x07,
634 Ceq = 0x08,
635 Clt = 0x09,
636 Cgt = 0x0A,
637 Br = 0x0B,
638 Brfalse = 0x0C,
639 Pop = 0x0D,
640 Dup = 0x0E,
641 Ldloc = 0x0F,
642 Stloc = 0x10,
643 Call = 0x11,
644 Shl = 0x12,
645 Shr = 0x13,
646 Mod = 0x14,
647 And = 0x15,
648 Or = 0x16,
649 Not = 0x17,
650 Lshr = 0x18,
651}
652
653impl TryFrom<u8> for OpCode {
654 type Error = ();
655
656 fn try_from(value: u8) -> Result<Self, Self::Error> {
657 match value {
658 x if x == Self::Nop as u8 => Ok(Self::Nop),
659 x if x == Self::Ret as u8 => Ok(Self::Ret),
660 x if x == Self::Ldc as u8 => Ok(Self::Ldc),
661 x if x == Self::Add as u8 => Ok(Self::Add),
662 x if x == Self::Sub as u8 => Ok(Self::Sub),
663 x if x == Self::Mul as u8 => Ok(Self::Mul),
664 x if x == Self::Div as u8 => Ok(Self::Div),
665 x if x == Self::Neg as u8 => Ok(Self::Neg),
666 x if x == Self::Ceq as u8 => Ok(Self::Ceq),
667 x if x == Self::Clt as u8 => Ok(Self::Clt),
668 x if x == Self::Cgt as u8 => Ok(Self::Cgt),
669 x if x == Self::Br as u8 => Ok(Self::Br),
670 x if x == Self::Brfalse as u8 => Ok(Self::Brfalse),
671 x if x == Self::Pop as u8 => Ok(Self::Pop),
672 x if x == Self::Dup as u8 => Ok(Self::Dup),
673 x if x == Self::Ldloc as u8 => Ok(Self::Ldloc),
674 x if x == Self::Stloc as u8 => Ok(Self::Stloc),
675 x if x == Self::Call as u8 => Ok(Self::Call),
676 x if x == Self::Shl as u8 => Ok(Self::Shl),
677 x if x == Self::Shr as u8 => Ok(Self::Shr),
678 x if x == Self::Mod as u8 => Ok(Self::Mod),
679 x if x == Self::And as u8 => Ok(Self::And),
680 x if x == Self::Or as u8 => Ok(Self::Or),
681 x if x == Self::Not as u8 => Ok(Self::Not),
682 x if x == Self::Lshr as u8 => Ok(Self::Lshr),
683 _ => Err(()),
684 }
685 }
686}
687
688impl OpCode {
689 pub const fn operand_len(self) -> usize {
690 match self {
691 Self::Nop
692 | Self::Ret
693 | Self::Add
694 | Self::Sub
695 | Self::Mul
696 | Self::Div
697 | Self::Neg
698 | Self::Ceq
699 | Self::Clt
700 | Self::Cgt
701 | Self::Pop
702 | Self::Dup
703 | Self::Shl
704 | Self::Shr
705 | Self::Mod
706 | Self::And
707 | Self::Or
708 | Self::Not
709 | Self::Lshr => 0,
710 Self::Ldc | Self::Br | Self::Brfalse => 4,
711 Self::Ldloc | Self::Stloc => 1,
712 Self::Call => 3,
713 }
714 }
715
716 pub fn mnemonic(self) -> &'static str {
717 match self {
718 OpCode::Nop => "nop",
719 OpCode::Ret => "ret",
720 OpCode::Ldc => "ldc",
721 OpCode::Add => "add",
722 OpCode::Sub => "sub",
723 OpCode::Mul => "mul",
724 OpCode::Div => "div",
725 OpCode::Neg => "neg",
726 OpCode::Ceq => "ceq",
727 OpCode::Clt => "clt",
728 OpCode::Cgt => "cgt",
729 OpCode::Br => "br",
730 OpCode::Brfalse => "brfalse",
731 OpCode::Pop => "pop",
732 OpCode::Dup => "dup",
733 OpCode::Ldloc => "ldloc",
734 OpCode::Stloc => "stloc",
735 OpCode::Call => "call",
736 OpCode::Shl => "shl",
737 OpCode::Shr => "shr",
738 OpCode::Mod => "mod",
739 OpCode::And => "and",
740 OpCode::Or => "or",
741 OpCode::Not => "not",
742 OpCode::Lshr => "lshr",
743 }
744 }
745
746 pub fn parse_mnemonic(op: &str) -> Option<Self> {
747 match op {
748 "nop" => Some(OpCode::Nop),
749 "ret" => Some(OpCode::Ret),
750 "ldc" => Some(OpCode::Ldc),
751 "add" => Some(OpCode::Add),
752 "sub" => Some(OpCode::Sub),
753 "mul" => Some(OpCode::Mul),
754 "div" => Some(OpCode::Div),
755 "neg" => Some(OpCode::Neg),
756 "ceq" => Some(OpCode::Ceq),
757 "clt" => Some(OpCode::Clt),
758 "cgt" => Some(OpCode::Cgt),
759 "br" => Some(OpCode::Br),
760 "brfalse" => Some(OpCode::Brfalse),
761 "pop" => Some(OpCode::Pop),
762 "dup" => Some(OpCode::Dup),
763 "ldloc" => Some(OpCode::Ldloc),
764 "stloc" => Some(OpCode::Stloc),
765 "call" => Some(OpCode::Call),
766 "shl" => Some(OpCode::Shl),
767 "shr" => Some(OpCode::Shr),
768 "mod" => Some(OpCode::Mod),
769 "and" => Some(OpCode::And),
770 "or" => Some(OpCode::Or),
771 "not" => Some(OpCode::Not),
772 "lshr" => Some(OpCode::Lshr),
773 _ => None,
774 }
775 }
776}
777
778#[cfg(test)]
779mod tests {
780 use super::*;
781
782 #[test]
783 fn heap_value_clone_shares_backing() {
784 let string = Value::string("hello");
785 let string_clone = string.clone();
786 let (Value::String(lhs), Value::String(rhs)) = (&string, &string_clone) else {
787 panic!("expected string values");
788 };
789 assert!(Arc::ptr_eq(lhs, rhs));
790
791 let array = Value::array(vec![Value::Int(1), Value::Int(2)]);
792 let array_clone = array.clone();
793 let (Value::Array(lhs), Value::Array(rhs)) = (&array, &array_clone) else {
794 panic!("expected array values");
795 };
796 assert!(Arc::ptr_eq(lhs, rhs));
797
798 let bytes = Value::bytes([1u8, 2, 3]);
799 let bytes_clone = bytes.clone();
800 let (Value::Bytes(lhs), Value::Bytes(rhs)) = (&bytes, &bytes_clone) else {
801 panic!("expected bytes values");
802 };
803 assert!(Arc::ptr_eq(lhs, rhs));
804
805 let map = Value::map(vec![(Value::string("k"), Value::Int(9))]);
806 let map_clone = map.clone();
807 let (Value::Map(lhs), Value::Map(rhs)) = (&map, &map_clone) else {
808 panic!("expected map values");
809 };
810 assert!(Arc::ptr_eq(lhs, rhs));
811 }
812
813 #[test]
814 fn bytes_map_key_uses_value_lookup() {
815 let key = Value::bytes([0x01u8, 0x02, 0x03]);
816 let expected = Value::Bool(true);
817
818 let mut map = VmMap::new();
819 map.insert(key, expected.clone());
820
821 assert_eq!(
822 map.get(&Value::bytes([0x01u8, 0x02, 0x03])),
823 Some(&expected)
824 );
825 assert_eq!(map.get(&Value::bytes([0x01u8, 0x02, 0x04])), None);
826 }
827
828 #[test]
829 fn composite_map_key_remains_stable_after_alias_detach() {
830 let source_key = Value::array(vec![Value::Int(1), Value::Int(2)]);
831 let alias = source_key.clone();
832 let lookup_key = source_key.clone();
833 let expected = Value::string("kept");
834
835 let mut map = VmMap::new();
836 map.insert(source_key, expected.clone());
837
838 let mutated_alias = match alias {
839 Value::Array(values) => {
840 let mut owned = unwrap_or_clone_shared(values);
841 owned[0] = Value::Int(9);
842 Value::array(owned)
843 }
844 other => panic!("expected array alias, got {other:?}"),
845 };
846
847 assert_eq!(map.get(&lookup_key), Some(&expected));
848 assert_eq!(
849 map.get(&Value::array(vec![Value::Int(1), Value::Int(2)])),
850 None
851 );
852 assert_eq!(map.get(&mutated_alias), None);
853 }
854
855 #[test]
856 fn nested_map_keys_use_identity_lookup() {
857 let nested_key = Value::map(vec![
858 (Value::string("a"), Value::Int(1)),
859 (Value::string("b"), Value::Int(2)),
860 ]);
861 let lookup_key = nested_key.clone();
862 let structural_peer = Value::map(vec![
863 (Value::string("b"), Value::Int(2)),
864 (Value::string("a"), Value::Int(1)),
865 ]);
866 let expected = Value::Bool(true);
867
868 let mut map = VmMap::new();
869 map.insert(nested_key, expected.clone());
870
871 assert_eq!(map.get(&lookup_key), Some(&expected));
872 assert_eq!(map.get(&structural_peer), None);
873 }
874
875 #[test]
876 fn vm_map_cached_len_stays_in_sync() {
877 let mut map = VmMap::new();
878 assert_eq!(map.len(), 0);
879
880 assert_eq!(map.insert(Value::string("a"), Value::Int(1)), None);
881 assert_eq!(map.len(), 1);
882
883 assert_eq!(
884 map.insert(Value::string("a"), Value::Int(2)),
885 Some(Value::Int(1))
886 );
887 assert_eq!(map.len(), 1);
888
889 assert_eq!(map.insert(Value::string("b"), Value::Int(3)), None);
890 assert_eq!(map.len(), 2);
891
892 assert_eq!(map.remove(&Value::string("missing")), None);
893 assert_eq!(map.len(), 2);
894
895 assert_eq!(map.remove(&Value::string("a")), Some(Value::Int(2)));
896 assert_eq!(map.len(), 1);
897 }
898}