1use core::cmp::Ordering;
2use core::fmt;
3use core::mem::replace;
4use core::ptr::NonNull;
5
6use crate::alloc::prelude::*;
7use crate::alloc::{self, String};
8use crate::hash::{Hash, IntoHash, ToTypeHash};
9use crate::modules::{cmp, option, result};
10use crate::runtime;
11use crate::sync::Arc;
12
13mod ops;
14use self::ops::*;
15
16use super::{
17 budget, inst, Address, AnySequence, Args, Awaited, BorrowMut, Bytes, Call, ControlFlow,
18 DynArgs, DynGuardedArgs, Format, FormatSpec, Formatter, FromValue, Function, Future, Generator,
19 GeneratorState, GuardedArgs, Inline, InstArithmeticOp, InstBitwiseOp, InstOp, InstRange,
20 InstShiftOp, InstTarget, InstValue, Object, Output, OwnedTuple, Pair, Panic, Protocol,
21 ProtocolCaller, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Repr,
22 RttiKind, RuntimeContext, Select, SelectFuture, Stack, Stream, Type, TypeHash, TypeInfo,
23 TypeOf, Unit, UnitFn, UnitStorage, Value, Vec, VmDiagnostics, VmDiagnosticsObj, VmError,
24 VmErrorKind, VmExecution, VmHalt, VmIntegerRepr, VmOutcome, VmSendExecution,
25};
26
27#[inline(always)]
29fn take(value: &mut Value) -> Value {
30 replace(value, Value::empty())
31}
32
33#[inline(always)]
34fn consume(value: &mut Value) {
35 *value = Value::empty();
36}
37
38#[derive(Debug, Clone, Copy)]
40pub enum Isolated {
41 Isolated,
43 None,
45}
46
47impl Isolated {
48 #[inline]
49 pub(crate) fn new(value: bool) -> Self {
50 if value {
51 Self::Isolated
52 } else {
53 Self::None
54 }
55 }
56
57 #[inline]
58 pub(crate) fn then_some<T>(self, value: T) -> Option<T> {
59 match self {
60 Self::Isolated => Some(value),
61 Self::None => None,
62 }
63 }
64}
65
66impl fmt::Display for Isolated {
67 #[inline]
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 match self {
70 Self::Isolated => write!(f, "isolated"),
71 Self::None => write!(f, "none"),
72 }
73 }
74}
75
76#[derive(Debug)]
79pub(crate) enum CallResultOnly<T> {
80 Ok(T),
82 Unsupported(Value),
85}
86
87#[derive(Debug)]
90pub(crate) enum CallResult<T> {
91 Ok(T),
93 Frame,
96 Unsupported(Value),
99}
100
101#[derive(Debug)]
103pub struct Vm {
104 context: Arc<RuntimeContext>,
106 unit: Arc<Unit>,
108 ip: usize,
110 last_ip_len: u8,
112 stack: Stack,
114 call_frames: alloc::Vec<CallFrame>,
116}
117
118impl Vm {
119 pub const fn new(context: Arc<RuntimeContext>, unit: Arc<Unit>) -> Self {
129 Self::with_stack(context, unit, Stack::new())
130 }
131
132 pub const fn with_stack(context: Arc<RuntimeContext>, unit: Arc<Unit>, stack: Stack) -> Self {
134 Self {
135 context,
136 unit,
137 ip: 0,
138 last_ip_len: 0,
139 stack,
140 call_frames: alloc::Vec::new(),
141 }
142 }
143
144 pub fn without_runtime(unit: Arc<Unit>) -> alloc::Result<Self> {
148 Ok(Self::new(Arc::try_new(RuntimeContext::default())?, unit))
149 }
150
151 pub fn is_same(&self, context: &Arc<RuntimeContext>, unit: &Arc<Unit>) -> bool {
153 Arc::ptr_eq(&self.context, context) && Arc::ptr_eq(&self.unit, unit)
154 }
155
156 pub fn is_same_context(&self, context: &Arc<RuntimeContext>) -> bool {
158 Arc::ptr_eq(&self.context, context)
159 }
160
161 pub fn is_same_unit(&self, unit: &Arc<Unit>) -> bool {
163 Arc::ptr_eq(&self.unit, unit)
164 }
165
166 #[inline]
168 pub fn set_ip(&mut self, ip: usize) {
169 self.ip = ip;
170 }
171
172 #[inline]
174 pub fn call_frames(&self) -> &[CallFrame] {
175 &self.call_frames
176 }
177
178 #[inline]
180 pub fn stack(&self) -> &Stack {
181 &self.stack
182 }
183
184 #[inline]
186 pub fn stack_mut(&mut self) -> &mut Stack {
187 &mut self.stack
188 }
189
190 #[inline]
205 pub fn context_mut(&mut self) -> &mut Arc<RuntimeContext> {
206 &mut self.context
207 }
208
209 #[inline]
211 pub fn context(&self) -> &Arc<RuntimeContext> {
212 &self.context
213 }
214
215 #[inline]
227 pub fn unit_mut(&mut self) -> &mut Arc<Unit> {
228 &mut self.unit
229 }
230
231 #[inline]
233 pub fn unit(&self) -> &Arc<Unit> {
234 &self.unit
235 }
236
237 #[inline]
239 pub fn ip(&self) -> usize {
240 self.ip
241 }
242
243 #[inline]
245 pub fn last_ip(&self) -> usize {
246 self.ip.wrapping_sub(self.last_ip_len as usize)
247 }
248
249 pub fn clear(&mut self) {
251 self.ip = 0;
252 self.stack.clear();
253 self.call_frames.clear();
254 }
255
256 pub fn lookup_function<N>(&self, name: N) -> Result<Function, VmError>
299 where
300 N: ToTypeHash,
301 {
302 Ok(self.lookup_function_by_hash(name.to_type_hash())?)
303 }
304
305 pub(crate) fn into_execution(self) -> VmExecution<Self> {
307 VmExecution::new(self)
308 }
309
310 pub fn complete(self) -> Result<Value, VmError> {
317 self.into_execution().complete()
318 }
319
320 pub async fn async_complete(self) -> Result<Value, VmError> {
322 self.into_execution().resume().await?.into_complete()
323 }
324
325 pub fn execute(
375 &mut self,
376 name: impl ToTypeHash,
377 args: impl Args,
378 ) -> Result<VmExecution<&mut Self>, VmError> {
379 self.set_entrypoint(name, args.count())?;
380 args.into_stack(&mut self.stack)?;
381 Ok(VmExecution::new(self))
382 }
383
384 pub fn send_execute(
391 mut self,
392 name: impl ToTypeHash,
393 args: impl Args + Send,
394 ) -> Result<VmSendExecution, VmError> {
395 self.stack.clear();
398
399 self.set_entrypoint(name, args.count())?;
400 args.into_stack(&mut self.stack)?;
401 Ok(VmSendExecution(VmExecution::new(self)))
402 }
403
404 pub fn call(
409 &mut self,
410 name: impl ToTypeHash,
411 args: impl GuardedArgs,
412 ) -> Result<Value, VmError> {
413 self.set_entrypoint(name, args.count())?;
414
415 let guard = unsafe { args.guarded_into_stack(&mut self.stack)? };
419
420 let value = {
421 let vm = ClearStack(self);
424 VmExecution::new(&mut *vm.0).complete()?
425 };
426
427 drop(guard);
431 Ok(value)
432 }
433
434 pub fn call_with_diagnostics(
439 &mut self,
440 name: impl ToTypeHash,
441 args: impl GuardedArgs,
442 diagnostics: &mut dyn VmDiagnostics,
443 ) -> Result<Value, VmError> {
444 self.set_entrypoint(name, args.count())?;
445
446 let guard = unsafe { args.guarded_into_stack(&mut self.stack)? };
450
451 let value = {
452 let vm = ClearStack(self);
455 VmExecution::new(&mut *vm.0)
456 .resume()
457 .with_diagnostics(diagnostics)
458 .complete()
459 .and_then(VmOutcome::into_complete)
460 };
461
462 drop(guard);
466 value
467 }
468
469 pub async fn async_call<A, N>(&mut self, name: N, args: A) -> Result<Value, VmError>
475 where
476 N: ToTypeHash,
477 A: GuardedArgs,
478 {
479 self.set_entrypoint(name, args.count())?;
480
481 let guard = unsafe { args.guarded_into_stack(&mut self.stack)? };
485
486 let value = {
487 let vm = ClearStack(self);
490 VmExecution::new(&mut *vm.0)
491 .resume()
492 .await
493 .and_then(VmOutcome::into_complete)
494 };
495
496 drop(guard);
500 value
501 }
502
503 fn set_entrypoint<N>(&mut self, name: N, count: usize) -> Result<(), VmErrorKind>
506 where
507 N: ToTypeHash,
508 {
509 let hash = name.to_type_hash();
510
511 let Some(info) = self.unit.function(&hash) else {
512 return Err(if let Some(item) = name.to_item()? {
513 VmErrorKind::MissingEntry { hash, item }
514 } else {
515 VmErrorKind::MissingEntryHash { hash }
516 });
517 };
518
519 let offset = match info {
520 UnitFn::Offset {
523 offset,
524 args: expected,
525 ..
526 } => {
527 check_args(count, *expected)?;
528 *offset
529 }
530 _ => {
531 return Err(VmErrorKind::MissingFunction { hash });
532 }
533 };
534
535 self.ip = offset;
536 self.stack.clear();
537 self.call_frames.clear();
538 Ok(())
539 }
540
541 #[inline]
543 pub(crate) fn call_instance_fn(
544 &mut self,
545 isolated: Isolated,
546 target: Value,
547 hash: impl ToTypeHash,
548 args: &mut dyn DynArgs,
549 out: Output,
550 ) -> Result<CallResult<()>, VmError> {
551 let count = args.count().wrapping_add(1);
552 let type_hash = target.type_hash();
553 let hash = Hash::associated_function(type_hash, hash.to_type_hash());
554 self.call_hash_with(isolated, hash, target, args, count, out)
555 }
556
557 #[inline]
559 fn call_field_fn(
560 &mut self,
561 protocol: impl IntoHash,
562 target: Value,
563 name: impl IntoHash,
564 args: &mut dyn DynArgs,
565 out: Output,
566 ) -> Result<CallResult<()>, VmError> {
567 let count = args.count().wrapping_add(1);
568 let hash = Hash::field_function(protocol, target.type_hash(), name);
569 self.call_hash_with(Isolated::None, hash, target, args, count, out)
570 }
571
572 #[inline]
574 fn call_index_fn(
575 &mut self,
576 protocol: impl IntoHash,
577 target: Value,
578 index: usize,
579 args: &mut dyn DynArgs,
580 out: Output,
581 ) -> Result<CallResult<()>, VmError> {
582 let count = args.count().wrapping_add(1);
583 let hash = Hash::index_function(protocol, target.type_hash(), Hash::index(index));
584 self.call_hash_with(Isolated::None, hash, target, args, count, out)
585 }
586
587 fn called_function_hook(&self, hash: Hash) -> Result<(), VmError> {
588 runtime::env::exclusive(|_, _, diagnostics| {
589 if let Some(diagnostics) = diagnostics {
590 diagnostics.function_used(hash, self.ip())?;
591 }
592
593 Ok(())
594 })
595 }
596
597 #[inline(never)]
598 fn call_hash_with(
599 &mut self,
600 isolated: Isolated,
601 hash: Hash,
602 target: Value,
603 args: &mut dyn DynArgs,
604 count: usize,
605 out: Output,
606 ) -> Result<CallResult<()>, VmError> {
607 if let Some(handler) = self.context.function(&hash) {
608 let addr = self.stack.addr();
609
610 self.called_function_hook(hash)?;
611 self.stack.push(target)?;
612 args.push_to_stack(&mut self.stack)?;
613
614 let result = handler.call(&mut self.stack, addr, count, out);
615 self.stack.truncate(addr);
616 result?;
617 return Ok(CallResult::Ok(()));
618 }
619
620 if let Some(UnitFn::Offset {
621 offset,
622 call,
623 args: expected,
624 ..
625 }) = self.unit.function(&hash)
626 {
627 check_args(count, *expected)?;
628
629 let addr = self.stack.addr();
630
631 self.called_function_hook(hash)?;
632 self.stack.push(target)?;
633 args.push_to_stack(&mut self.stack)?;
634
635 let result = self.call_offset_fn(*offset, *call, addr, count, isolated, out);
636
637 if result? {
638 self.stack.truncate(addr);
639 return Ok(CallResult::Frame);
640 } else {
641 return Ok(CallResult::Ok(()));
642 }
643 }
644
645 Ok(CallResult::Unsupported(target))
646 }
647
648 #[cfg_attr(feature = "bench", inline(never))]
649 fn internal_cmp(
650 &mut self,
651 match_ordering: fn(Ordering) -> bool,
652 lhs: Address,
653 rhs: Address,
654 out: Output,
655 ) -> Result<(), VmError> {
656 let rhs = self.stack.at(rhs);
657 let lhs = self.stack.at(lhs);
658
659 let ordering = match (lhs.as_inline_unchecked(), rhs.as_inline_unchecked()) {
660 (Some(lhs), Some(rhs)) => lhs.partial_cmp(rhs)?,
661 _ => {
662 let lhs = lhs.clone();
663 let rhs = rhs.clone();
664 Value::partial_cmp_with(&lhs, &rhs, self)?
665 }
666 };
667
668 self.stack.store(out, || match ordering {
669 Some(ordering) => match_ordering(ordering),
670 None => false,
671 })?;
672
673 Ok(())
674 }
675
676 #[tracing::instrument(skip(self), fields(call_frames = self.call_frames.len(), top = self.stack.top(), stack = self.stack.len(), self.ip))]
681 pub(crate) fn push_call_frame(
682 &mut self,
683 ip: usize,
684 addr: Address,
685 args: usize,
686 isolated: Isolated,
687 out: Output,
688 ) -> Result<(), VmErrorKind> {
689 tracing::trace!("pushing call frame");
690
691 let top = self.stack.swap_top(addr, args)?;
692 let ip = replace(&mut self.ip, ip);
693
694 let frame = CallFrame {
695 ip,
696 top,
697 isolated,
698 out,
699 };
700
701 self.call_frames.try_push(frame)?;
702 Ok(())
703 }
704
705 #[tracing::instrument(skip(self), fields(call_frames = self.call_frames.len(), top = self.stack.top(), stack = self.stack.len(), self.ip))]
709 pub(crate) fn pop_call_frame_from_call(&mut self) -> Option<usize> {
710 tracing::trace!("popping call frame from call");
711 let frame = self.call_frames.pop()?;
712 tracing::trace!(?frame);
713 self.stack.pop_stack_top(frame.top);
714 Some(replace(&mut self.ip, frame.ip))
715 }
716
717 #[tracing::instrument(skip(self), fields(call_frames = self.call_frames.len(), top = self.stack.top(), stack = self.stack.len(), self.ip))]
719 pub(crate) fn pop_call_frame(&mut self) -> (Isolated, Option<Output>) {
720 tracing::trace!("popping call frame");
721
722 let Some(frame) = self.call_frames.pop() else {
723 self.stack.pop_stack_top(0);
724 return (Isolated::Isolated, None);
725 };
726
727 tracing::trace!(?frame);
728 self.stack.pop_stack_top(frame.top);
729 self.ip = frame.ip;
730 (frame.isolated, Some(frame.out))
731 }
732
733 fn try_object_like_index_get(target: &Value, field: &str) -> Result<Option<Value>, VmError> {
735 match target.as_ref() {
736 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => {
737 let Some(value) = data.get_field_ref(field)? else {
738 return Err(VmError::new(VmErrorKind::MissingField {
739 target: data.type_info(),
740 field: field.try_to_owned()?,
741 }));
742 };
743
744 Ok(Some(value.clone()))
745 }
746 Repr::Any(value) => match value.type_hash() {
747 Object::HASH => {
748 let target = value.borrow_ref::<Object>()?;
749
750 let Some(value) = target.get(field) else {
751 return Err(VmError::new(VmErrorKind::MissingField {
752 target: TypeInfo::any::<Object>(),
753 field: field.try_to_owned()?,
754 }));
755 };
756
757 Ok(Some(value.clone()))
758 }
759 _ => Ok(None),
760 },
761 _ => Ok(None),
762 }
763 }
764
765 fn try_tuple_like_index_get(target: &Value, index: usize) -> Result<Option<Value>, VmError> {
767 let result = match target.as_ref() {
768 Repr::Inline(target) => match target {
769 Inline::Unit => Err(target.type_info()),
770 _ => return Ok(None),
771 },
772 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Tuple) => {
773 match data.get_ref(index)? {
774 Some(value) => Ok(value.clone()),
775 None => Err(data.type_info()),
776 }
777 }
778 Repr::Dynamic(data) => Err(data.type_info()),
779 Repr::Any(target) => match target.type_hash() {
780 Result::<Value, Value>::HASH => {
781 match (index, &*target.borrow_ref::<Result<Value, Value>>()?) {
782 (0, Ok(value)) => Ok(value.clone()),
783 (0, Err(value)) => Ok(value.clone()),
784 _ => Err(target.type_info()),
785 }
786 }
787 Option::<Value>::HASH => match (index, &*target.borrow_ref::<Option<Value>>()?) {
788 (0, Some(value)) => Ok(value.clone()),
789 _ => Err(target.type_info()),
790 },
791 GeneratorState::HASH => match (index, &*target.borrow_ref()?) {
792 (0, GeneratorState::Yielded(value)) => Ok(value.clone()),
793 (0, GeneratorState::Complete(value)) => Ok(value.clone()),
794 _ => Err(target.type_info()),
795 },
796 runtime::Vec::HASH => {
797 let vec = target.borrow_ref::<runtime::Vec>()?;
798
799 match vec.get(index) {
800 Some(value) => Ok(value.clone()),
801 None => Err(target.type_info()),
802 }
803 }
804 runtime::OwnedTuple::HASH => {
805 let tuple = target.borrow_ref::<runtime::OwnedTuple>()?;
806
807 match tuple.get(index) {
808 Some(value) => Ok(value.clone()),
809 None => Err(target.type_info()),
810 }
811 }
812 _ => {
813 return Ok(None);
814 }
815 },
816 };
817
818 match result {
819 Ok(value) => Ok(Some(value)),
820 Err(target) => Err(VmError::new(VmErrorKind::MissingIndexInteger {
821 target,
822 index: VmIntegerRepr::from(index),
823 })),
824 }
825 }
826
827 fn try_tuple_like_index_set(
829 target: &Value,
830 index: usize,
831 from: &Value,
832 ) -> Result<bool, VmError> {
833 match target.as_ref() {
834 Repr::Inline(target) => match target {
835 Inline::Unit => Ok(false),
836 _ => Ok(false),
837 },
838 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Tuple) => {
839 if let Some(target) = data.borrow_mut()?.get_mut(index) {
840 target.clone_from(from);
841 return Ok(true);
842 }
843
844 Ok(false)
845 }
846 Repr::Dynamic(..) => Ok(false),
847 Repr::Any(value) => match value.type_hash() {
848 Result::<Value, Value>::HASH => {
849 let mut result = value.borrow_mut::<Result<Value, Value>>()?;
850
851 let target = match &mut *result {
852 Ok(ok) if index == 0 => ok,
853 Err(err) if index == 1 => err,
854 _ => return Ok(false),
855 };
856
857 target.clone_from(from);
858 Ok(true)
859 }
860 Option::<Value>::HASH => {
861 let mut option = value.borrow_mut::<Option<Value>>()?;
862
863 let target = match &mut *option {
864 Some(some) if index == 0 => some,
865 _ => return Ok(false),
866 };
867
868 target.clone_from(from);
869 Ok(true)
870 }
871 runtime::Vec::HASH => {
872 let mut vec = value.borrow_mut::<runtime::Vec>()?;
873
874 if let Some(target) = vec.get_mut(index) {
875 target.clone_from(from);
876 return Ok(true);
877 }
878
879 Ok(false)
880 }
881 runtime::OwnedTuple::HASH => {
882 let mut tuple = value.borrow_mut::<runtime::OwnedTuple>()?;
883
884 if let Some(target) = tuple.get_mut(index) {
885 target.clone_from(from);
886 return Ok(true);
887 }
888
889 Ok(false)
890 }
891 _ => Ok(false),
892 },
893 }
894 }
895
896 fn try_object_slot_index_set(
897 target: &Value,
898 field: &str,
899 value: &Value,
900 ) -> Result<bool, VmErrorKind> {
901 match target.as_ref() {
902 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => {
903 if let Some(mut v) = data.get_field_mut(field)? {
904 v.clone_from(value);
905 return Ok(true);
906 }
907
908 Err(VmErrorKind::MissingField {
909 target: data.type_info(),
910 field: field.try_to_owned()?,
911 })
912 }
913 Repr::Any(target) => match target.type_hash() {
914 Object::HASH => {
915 let mut target = target.borrow_mut::<Object>()?;
916
917 if let Some(target) = target.get_mut(field) {
918 target.clone_from(value);
919 } else {
920 let key = field.try_to_owned()?;
921 target.insert(key, value.clone())?;
922 }
923
924 Ok(true)
925 }
926 _ => Ok(false),
927 },
928 target => Err(VmErrorKind::MissingField {
929 target: target.type_info(),
930 field: field.try_to_owned()?,
931 }),
932 }
933 }
934
935 fn as_op(&mut self, lhs: Address, rhs: Address) -> Result<Value, VmError> {
937 let b = self.stack.at(rhs);
938 let a = self.stack.at(lhs);
939
940 let Repr::Inline(Inline::Type(ty)) = b.as_ref() else {
941 return Err(VmError::new(VmErrorKind::UnsupportedIs {
942 value: a.type_info(),
943 test_type: b.type_info(),
944 }));
945 };
946
947 macro_rules! convert {
948 ($from:ty, $value:expr) => {
949 match ty.into_hash() {
950 f64::HASH => Value::from($value as f64),
951 u64::HASH => Value::from($value as u64),
952 i64::HASH => Value::from($value as i64),
953 ty => {
954 return Err(VmError::new(VmErrorKind::UnsupportedAs {
955 value: TypeInfo::from(<$from as TypeOf>::STATIC_TYPE_INFO),
956 type_hash: ty,
957 }));
958 }
959 }
960 };
961 }
962
963 let value = match a.as_ref() {
964 Repr::Inline(Inline::Unsigned(a)) => convert!(u64, *a),
965 Repr::Inline(Inline::Signed(a)) => convert!(i64, *a),
966 Repr::Inline(Inline::Float(a)) => convert!(f64, *a),
967 value => {
968 return Err(VmError::new(VmErrorKind::UnsupportedAs {
969 value: value.type_info(),
970 type_hash: ty.into_hash(),
971 }));
972 }
973 };
974
975 Ok(value)
976 }
977
978 fn test_is_instance(&mut self, lhs: Address, rhs: Address) -> Result<bool, VmError> {
980 let b = self.stack.at(rhs);
981 let a = self.stack.at(lhs);
982
983 let Some(Inline::Type(ty)) = b.as_inline() else {
984 return Err(VmError::new(VmErrorKind::UnsupportedIs {
985 value: a.type_info(),
986 test_type: b.type_info(),
987 }));
988 };
989
990 Ok(a.type_hash() == ty.into_hash())
991 }
992
993 fn internal_bool(
994 &mut self,
995 bool_op: impl FnOnce(bool, bool) -> bool,
996 op: &'static str,
997 lhs: Address,
998 rhs: Address,
999 out: Output,
1000 ) -> Result<(), VmError> {
1001 let rhs = self.stack.at(rhs);
1002 let lhs = self.stack.at(lhs);
1003
1004 let inline = match (lhs.as_ref(), rhs.as_ref()) {
1005 (Repr::Inline(Inline::Bool(lhs)), Repr::Inline(Inline::Bool(rhs))) => {
1006 let value = bool_op(*lhs, *rhs);
1007 Inline::Bool(value)
1008 }
1009 (lhs, rhs) => {
1010 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1011 op,
1012 lhs: lhs.type_info(),
1013 rhs: rhs.type_info(),
1014 }));
1015 }
1016 };
1017
1018 self.stack.store(out, inline)?;
1019 Ok(())
1020 }
1021
1022 fn call_generator_fn(
1024 &mut self,
1025 offset: usize,
1026 addr: Address,
1027 args: usize,
1028 out: Output,
1029 ) -> Result<(), VmErrorKind> {
1030 let values = self.stack.slice_at_mut(addr, args)?;
1031
1032 if let Some(at) = out.as_addr() {
1033 let stack = values.iter_mut().map(take).try_collect::<Stack>()?;
1034 let mut vm = Self::with_stack(self.context.clone(), self.unit.clone(), stack);
1035 vm.ip = offset;
1036 *self.stack.at_mut(at)? = Value::try_from(Generator::new(vm))?;
1037 } else {
1038 values.iter_mut().for_each(consume);
1039 }
1040
1041 Ok(())
1042 }
1043
1044 fn call_stream_fn(
1046 &mut self,
1047 offset: usize,
1048 addr: Address,
1049 args: usize,
1050 out: Output,
1051 ) -> Result<(), VmErrorKind> {
1052 let values = self.stack.slice_at_mut(addr, args)?;
1053
1054 if let Some(at) = out.as_addr() {
1055 let stack = values.iter_mut().map(take).try_collect::<Stack>()?;
1056 let mut vm = Self::with_stack(self.context.clone(), self.unit.clone(), stack);
1057 vm.ip = offset;
1058 *self.stack.at_mut(at)? = Value::try_from(Stream::new(vm))?;
1059 } else {
1060 values.iter_mut().for_each(consume);
1061 }
1062
1063 Ok(())
1064 }
1065
1066 fn call_async_fn(
1068 &mut self,
1069 offset: usize,
1070 addr: Address,
1071 args: usize,
1072 out: Output,
1073 ) -> Result<(), VmErrorKind> {
1074 let values = self.stack.slice_at_mut(addr, args)?;
1075
1076 if let Some(at) = out.as_addr() {
1077 let stack = values.iter_mut().map(take).try_collect::<Stack>()?;
1078 let mut vm = Self::with_stack(self.context.clone(), self.unit.clone(), stack);
1079 vm.ip = offset;
1080 let mut execution = vm.into_execution();
1081 let future = Future::new(async move { execution.resume().await?.into_complete() })?;
1082 *self.stack.at_mut(at)? = Value::try_from(future)?;
1083 } else {
1084 values.iter_mut().for_each(consume);
1085 }
1086
1087 Ok(())
1088 }
1089
1090 #[cfg_attr(feature = "bench", inline(never))]
1092 fn call_offset_fn(
1093 &mut self,
1094 offset: usize,
1095 call: Call,
1096 addr: Address,
1097 args: usize,
1098 isolated: Isolated,
1099 out: Output,
1100 ) -> Result<bool, VmErrorKind> {
1101 let moved = match call {
1102 Call::Async => {
1103 self.call_async_fn(offset, addr, args, out)?;
1104 false
1105 }
1106 Call::Immediate => {
1107 self.push_call_frame(offset, addr, args, isolated, out)?;
1108 true
1109 }
1110 Call::Stream => {
1111 self.call_stream_fn(offset, addr, args, out)?;
1112 false
1113 }
1114 Call::Generator => {
1115 self.call_generator_fn(offset, addr, args, out)?;
1116 false
1117 }
1118 };
1119
1120 Ok(moved)
1121 }
1122
1123 #[cfg_attr(feature = "bench", inline(never))]
1125 fn target_fallback_assign(
1126 &mut self,
1127 fallback: TargetFallback,
1128 protocol: &Protocol,
1129 ) -> Result<(), VmError> {
1130 match fallback {
1131 TargetFallback::Value(lhs, rhs) => {
1132 let mut args = DynGuardedArgs::new((rhs.clone(),));
1133
1134 if let CallResult::Unsupported(lhs) = self.call_instance_fn(
1135 Isolated::None,
1136 lhs,
1137 protocol.hash,
1138 &mut args,
1139 Output::discard(),
1140 )? {
1141 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1142 op: protocol.name,
1143 lhs: lhs.type_info(),
1144 rhs: rhs.type_info(),
1145 }));
1146 };
1147 }
1148 TargetFallback::Field(lhs, hash, slot, rhs) => {
1149 let mut args = DynGuardedArgs::new((rhs,));
1150
1151 if let CallResult::Unsupported(lhs) =
1152 self.call_field_fn(protocol, lhs.clone(), hash, &mut args, Output::discard())?
1153 {
1154 let Some(field) = self.unit.lookup_string(slot) else {
1155 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
1156 };
1157
1158 return Err(VmError::new(VmErrorKind::UnsupportedObjectSlotIndexGet {
1159 target: lhs.type_info(),
1160 field: field.clone(),
1161 }));
1162 }
1163 }
1164 TargetFallback::Index(lhs, index, rhs) => {
1165 let mut args = DynGuardedArgs::new((rhs,));
1166
1167 if let CallResult::Unsupported(lhs) = self.call_index_fn(
1168 protocol.hash,
1169 lhs.clone(),
1170 index,
1171 &mut args,
1172 Output::discard(),
1173 )? {
1174 return Err(VmError::new(VmErrorKind::UnsupportedTupleIndexGet {
1175 target: lhs.type_info(),
1176 index,
1177 }));
1178 }
1179 }
1180 }
1181
1182 Ok(())
1183 }
1184
1185 #[cfg_attr(feature = "bench", inline(never))]
1186 fn op_await(&mut self, addr: Address) -> Result<Future, VmError> {
1187 Ok(self.stack.at(addr).clone().into_future()?)
1188 }
1189
1190 #[cfg_attr(feature = "bench", inline(never))]
1191 fn op_select(
1192 &mut self,
1193 addr: Address,
1194 len: usize,
1195 value: Output,
1196 ) -> Result<Option<Select>, VmError> {
1197 let futures = futures_util::stream::FuturesUnordered::new();
1198
1199 for (branch, value) in self.stack.slice_at(addr, len)?.iter().enumerate() {
1200 let future = value.clone().into_mut::<Future>()?;
1201
1202 if !future.is_completed() {
1203 futures.push(SelectFuture::new(self.ip + branch, future));
1204 }
1205 }
1206
1207 if futures.is_empty() {
1208 self.stack.store(value, ())?;
1209 self.ip = self.ip.wrapping_add(len);
1210 return Ok(None);
1211 }
1212
1213 Ok(Some(Select::new(futures)))
1214 }
1215
1216 #[cfg_attr(feature = "bench", inline(never))]
1217 fn op_store(&mut self, value: InstValue, out: Output) -> Result<(), VmError> {
1218 self.stack.store(out, value.into_value())?;
1219 Ok(())
1220 }
1221
1222 #[cfg_attr(feature = "bench", inline(never))]
1225 fn op_copy(&mut self, addr: Address, out: Output) -> Result<(), VmError> {
1226 self.stack.copy(addr, out)?;
1227 Ok(())
1228 }
1229
1230 #[cfg_attr(feature = "bench", inline(never))]
1233 fn op_move(&mut self, addr: Address, out: Output) -> Result<(), VmError> {
1234 let value = self.stack.at(addr).clone();
1235 let value = value.move_()?;
1236 self.stack.store(out, value)?;
1237 Ok(())
1238 }
1239
1240 #[cfg_attr(feature = "bench", inline(never))]
1241 fn op_drop(&mut self, set: usize) -> Result<(), VmError> {
1242 let Some(addresses) = self.unit.lookup_drop_set(set) else {
1243 return Err(VmError::new(VmErrorKind::MissingDropSet { set }));
1244 };
1245
1246 for &addr in addresses {
1247 *self.stack.at_mut(addr)? = Value::empty();
1248 }
1249
1250 Ok(())
1251 }
1252
1253 #[cfg_attr(feature = "bench", inline(never))]
1255 fn op_swap(&mut self, a: Address, b: Address) -> Result<(), VmError> {
1256 self.stack.swap(a, b)?;
1257 Ok(())
1258 }
1259
1260 #[cfg_attr(feature = "bench", inline(never))]
1262 fn op_jump(&mut self, jump: usize) -> Result<(), VmError> {
1263 self.ip = self.unit.translate(jump)?;
1264 Ok(())
1265 }
1266
1267 #[cfg_attr(feature = "bench", inline(never))]
1269 #[cfg_attr(not(feature = "bench"), inline)]
1270 fn op_jump_if(&mut self, cond: Address, jump: usize) -> Result<(), VmErrorKind> {
1271 if matches!(
1272 self.stack.at(cond).as_ref(),
1273 Repr::Inline(Inline::Bool(true))
1274 ) {
1275 self.ip = self.unit.translate(jump)?;
1276 }
1277
1278 Ok(())
1279 }
1280
1281 #[cfg_attr(feature = "bench", inline(never))]
1283 fn op_jump_if_not(&mut self, cond: Address, jump: usize) -> Result<(), VmErrorKind> {
1284 if matches!(
1285 self.stack.at(cond).as_ref(),
1286 Repr::Inline(Inline::Bool(false))
1287 ) {
1288 self.ip = self.unit.translate(jump)?;
1289 }
1290
1291 Ok(())
1292 }
1293
1294 #[cfg_attr(feature = "bench", inline(never))]
1296 fn op_vec(&mut self, addr: Address, count: usize, out: Output) -> Result<(), VmError> {
1297 let vec = self.stack.slice_at_mut(addr, count)?;
1298 let vec = vec
1299 .iter_mut()
1300 .map(take)
1301 .try_collect::<alloc::Vec<Value>>()?;
1302 self.stack.store(out, Vec::from(vec))?;
1303 Ok(())
1304 }
1305
1306 #[cfg_attr(feature = "bench", inline(never))]
1308 fn op_tuple(&mut self, addr: Address, count: usize, out: Output) -> Result<(), VmError> {
1309 let tuple = self.stack.slice_at_mut(addr, count)?;
1310
1311 let tuple = tuple
1312 .iter_mut()
1313 .map(take)
1314 .try_collect::<alloc::Vec<Value>>()?;
1315
1316 self.stack.store(out, || OwnedTuple::try_from(tuple))?;
1317 Ok(())
1318 }
1319
1320 #[cfg_attr(feature = "bench", inline(never))]
1322 fn op_tuple_n(&mut self, addr: &[Address], out: Output) -> Result<(), VmError> {
1323 let mut tuple = alloc::Vec::<Value>::try_with_capacity(addr.len())?;
1324
1325 for &arg in addr {
1326 let value = self.stack.at(arg).clone();
1327 tuple.try_push(value)?;
1328 }
1329
1330 self.stack.store(out, || OwnedTuple::try_from(tuple))?;
1331 Ok(())
1332 }
1333
1334 #[cfg_attr(feature = "bench", inline(never))]
1336 fn op_environment(&mut self, addr: Address, count: usize, out: Output) -> Result<(), VmError> {
1337 let tuple = self.stack.at(addr).clone();
1338 let tuple = tuple.borrow_tuple_ref()?;
1339
1340 if tuple.len() != count {
1341 return Err(VmError::new(VmErrorKind::BadEnvironmentCount {
1342 expected: count,
1343 actual: tuple.len(),
1344 }));
1345 }
1346
1347 if let Some(addr) = out.as_addr() {
1348 let out = self.stack.slice_at_mut(addr, count)?;
1349
1350 for (value, out) in tuple.iter().zip(out.iter_mut()) {
1351 out.clone_from(value);
1352 }
1353 }
1354
1355 Ok(())
1356 }
1357
1358 #[cfg_attr(feature = "bench", inline(never))]
1359 fn op_allocate(&mut self, size: usize) -> Result<(), VmError> {
1360 self.stack.resize(size)?;
1361 Ok(())
1362 }
1363
1364 #[cfg_attr(feature = "bench", inline(never))]
1365 fn op_not(&mut self, addr: Address, out: Output) -> Result<(), VmError> {
1366 self.unary(addr, out, &Protocol::NOT, |inline| match *inline {
1367 Inline::Bool(value) => Some(Inline::Bool(!value)),
1368 Inline::Unsigned(value) => Some(Inline::Unsigned(!value)),
1369 Inline::Signed(value) => Some(Inline::Signed(!value)),
1370 _ => None,
1371 })
1372 }
1373
1374 #[cfg_attr(feature = "bench", inline(never))]
1375 fn op_neg(&mut self, addr: Address, out: Output) -> Result<(), VmError> {
1376 self.unary(addr, out, &Protocol::NEG, |inline| match *inline {
1377 Inline::Signed(value) => Some(Inline::Signed(-value)),
1378 Inline::Float(value) => Some(Inline::Float(-value)),
1379 _ => None,
1380 })
1381 }
1382
1383 fn unary(
1384 &mut self,
1385 operand: Address,
1386 out: Output,
1387 protocol: &'static Protocol,
1388 op: impl FnOnce(&Inline) -> Option<Inline>,
1389 ) -> Result<(), VmError> {
1390 let operand = self.stack.at(operand);
1391
1392 'fallback: {
1393 let store = match operand.as_ref() {
1394 Repr::Inline(inline) => op(inline),
1395 Repr::Any(..) => break 'fallback,
1396 _ => None,
1397 };
1398
1399 let Some(store) = store else {
1400 return Err(VmError::new(VmErrorKind::UnsupportedUnaryOperation {
1401 op: protocol.name,
1402 operand: operand.type_info(),
1403 }));
1404 };
1405
1406 self.stack.store(out, store)?;
1407 return Ok(());
1408 };
1409
1410 let operand = operand.clone();
1411
1412 if let CallResult::Unsupported(operand) =
1413 self.call_instance_fn(Isolated::None, operand, protocol, &mut (), out)?
1414 {
1415 return Err(VmError::new(VmErrorKind::UnsupportedUnaryOperation {
1416 op: protocol.name,
1417 operand: operand.type_info(),
1418 }));
1419 }
1420
1421 Ok(())
1422 }
1423
1424 #[cfg_attr(feature = "bench", inline(never))]
1425 fn op_op(
1426 &mut self,
1427 op: InstOp,
1428 lhs: Address,
1429 rhs: Address,
1430 out: Output,
1431 ) -> Result<(), VmError> {
1432 match op {
1433 InstOp::Lt => {
1434 self.internal_cmp(|o| matches!(o, Ordering::Less), lhs, rhs, out)?;
1435 }
1436 InstOp::Le => {
1437 self.internal_cmp(
1438 |o| matches!(o, Ordering::Less | Ordering::Equal),
1439 lhs,
1440 rhs,
1441 out,
1442 )?;
1443 }
1444 InstOp::Gt => {
1445 self.internal_cmp(|o| matches!(o, Ordering::Greater), lhs, rhs, out)?;
1446 }
1447 InstOp::Ge => {
1448 self.internal_cmp(
1449 |o| matches!(o, Ordering::Greater | Ordering::Equal),
1450 lhs,
1451 rhs,
1452 out,
1453 )?;
1454 }
1455 InstOp::Eq => {
1456 let rhs = self.stack.at(rhs);
1457 let lhs = self.stack.at(lhs);
1458
1459 let test = if let (Some(lhs), Some(rhs)) = (lhs.as_inline(), rhs.as_inline()) {
1460 lhs.partial_eq(rhs)?
1461 } else {
1462 let lhs = lhs.clone();
1463 let rhs = rhs.clone();
1464 Value::partial_eq_with(&lhs, &rhs, self)?
1465 };
1466
1467 self.stack.store(out, test)?;
1468 }
1469 InstOp::Neq => {
1470 let rhs = self.stack.at(rhs);
1471 let lhs = self.stack.at(lhs);
1472
1473 let test = if let (Some(lhs), Some(rhs)) = (lhs.as_inline(), rhs.as_inline()) {
1474 lhs.partial_eq(rhs)?
1475 } else {
1476 let lhs = lhs.clone();
1477 let rhs = rhs.clone();
1478 Value::partial_eq_with(&lhs, &rhs, self)?
1479 };
1480
1481 self.stack.store(out, !test)?;
1482 }
1483 InstOp::And => {
1484 self.internal_bool(|a, b| a && b, "&&", lhs, rhs, out)?;
1485 }
1486 InstOp::Or => {
1487 self.internal_bool(|a, b| a || b, "||", lhs, rhs, out)?;
1488 }
1489 InstOp::As => {
1490 let value = self.as_op(lhs, rhs)?;
1491 self.stack.store(out, value)?;
1492 }
1493 InstOp::Is => {
1494 let is_instance = self.test_is_instance(lhs, rhs)?;
1495 self.stack.store(out, is_instance)?;
1496 }
1497 InstOp::IsNot => {
1498 let is_instance = self.test_is_instance(lhs, rhs)?;
1499 self.stack.store(out, !is_instance)?;
1500 }
1501 }
1502
1503 Ok(())
1504 }
1505
1506 #[cfg_attr(feature = "bench", inline(never))]
1507 fn op_arithmetic(
1508 &mut self,
1509 op: InstArithmeticOp,
1510 lhs: Address,
1511 rhs: Address,
1512 out: Output,
1513 ) -> Result<(), VmError> {
1514 let ops = ArithmeticOps::from_op(op);
1515
1516 let lhs = self.stack.at(lhs);
1517 let rhs = self.stack.at(rhs);
1518
1519 'fallback: {
1520 let inline = match (lhs.as_ref(), rhs.as_ref()) {
1521 (Repr::Inline(lhs), Repr::Inline(rhs)) => match (lhs, rhs) {
1522 (Inline::Unsigned(lhs), rhs) => {
1523 let rhs = rhs.as_integer()?;
1524 let value = (ops.u64)(*lhs, rhs).ok_or_else(ops.error)?;
1525 Inline::Unsigned(value)
1526 }
1527 (Inline::Signed(lhs), rhs) => {
1528 let rhs = rhs.as_integer()?;
1529 let value = (ops.i64)(*lhs, rhs).ok_or_else(ops.error)?;
1530 Inline::Signed(value)
1531 }
1532 (Inline::Float(lhs), Inline::Float(rhs)) => {
1533 let value = (ops.f64)(*lhs, *rhs);
1534 Inline::Float(value)
1535 }
1536 (lhs, rhs) => {
1537 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1538 op: ops.protocol.name,
1539 lhs: lhs.type_info(),
1540 rhs: rhs.type_info(),
1541 }));
1542 }
1543 },
1544 (Repr::Any(..), ..) => {
1545 break 'fallback;
1546 }
1547 (lhs, rhs) => {
1548 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1549 op: ops.protocol.name,
1550 lhs: lhs.type_info(),
1551 rhs: rhs.type_info(),
1552 }));
1553 }
1554 };
1555
1556 self.stack.store(out, inline)?;
1557 return Ok(());
1558 }
1559
1560 let lhs = lhs.clone();
1561 let rhs = rhs.clone();
1562
1563 let mut args = DynGuardedArgs::new((rhs.clone(),));
1564
1565 if let CallResult::Unsupported(lhs) =
1566 self.call_instance_fn(Isolated::None, lhs, &ops.protocol, &mut args, out)?
1567 {
1568 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1569 op: ops.protocol.name,
1570 lhs: lhs.type_info(),
1571 rhs: rhs.type_info(),
1572 }));
1573 }
1574
1575 Ok(())
1576 }
1577
1578 #[cfg_attr(feature = "bench", inline(never))]
1579 fn op_bitwise(
1580 &mut self,
1581 op: InstBitwiseOp,
1582 lhs: Address,
1583 rhs: Address,
1584 out: Output,
1585 ) -> Result<(), VmError> {
1586 let ops = BitwiseOps::from_op(op);
1587
1588 let lhs = self.stack.at(lhs);
1589 let rhs = self.stack.at(rhs);
1590
1591 'fallback: {
1592 let inline = match (lhs.as_ref(), rhs.as_ref()) {
1593 (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => {
1594 let rhs = rhs.as_integer()?;
1595 let value = (ops.u64)(*lhs, rhs);
1596 Inline::Unsigned(value)
1597 }
1598 (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => {
1599 let rhs = rhs.as_integer()?;
1600 let value = (ops.i64)(*lhs, rhs);
1601 Inline::Signed(value)
1602 }
1603 (Repr::Inline(Inline::Bool(lhs)), Repr::Inline(Inline::Bool(rhs))) => {
1604 let value = (ops.bool)(*lhs, *rhs);
1605 Inline::Bool(value)
1606 }
1607 (Repr::Any(_), _) => {
1608 break 'fallback;
1609 }
1610 (lhs, rhs) => {
1611 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1612 op: ops.protocol.name,
1613 lhs: lhs.type_info(),
1614 rhs: rhs.type_info(),
1615 }));
1616 }
1617 };
1618
1619 self.stack.store(out, inline)?;
1620 return Ok(());
1621 };
1622
1623 let lhs = lhs.clone();
1624 let rhs = rhs.clone();
1625
1626 let mut args = DynGuardedArgs::new((&rhs,));
1627
1628 if let CallResult::Unsupported(lhs) =
1629 self.call_instance_fn(Isolated::None, lhs, &ops.protocol, &mut args, out)?
1630 {
1631 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1632 op: ops.protocol.name,
1633 lhs: lhs.type_info(),
1634 rhs: rhs.type_info(),
1635 }));
1636 }
1637
1638 Ok(())
1639 }
1640
1641 #[cfg_attr(feature = "bench", inline(never))]
1642 fn op_shift(
1643 &mut self,
1644 op: InstShiftOp,
1645 lhs: Address,
1646 rhs: Address,
1647 out: Output,
1648 ) -> Result<(), VmError> {
1649 let ops = ShiftOps::from_op(op);
1650
1651 let (lhs, rhs) = 'fallback: {
1652 let inline = {
1653 match self.stack.pair(lhs, rhs)? {
1654 Pair::Same(value) => match value.as_mut() {
1655 Repr::Inline(Inline::Unsigned(value)) => {
1656 let shift = u32::try_from(*value).ok().ok_or_else(ops.error)?;
1657 let value = (ops.u64)(*value, shift).ok_or_else(ops.error)?;
1658 Inline::Unsigned(value)
1659 }
1660 Repr::Inline(Inline::Signed(value)) => {
1661 let shift = u32::try_from(*value).ok().ok_or_else(ops.error)?;
1662 let value = (ops.i64)(*value, shift).ok_or_else(ops.error)?;
1663 Inline::Signed(value)
1664 }
1665 Repr::Any(..) => break 'fallback (value.clone(), value.clone()),
1666 value => {
1667 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1668 op: ops.protocol.name,
1669 lhs: value.type_info(),
1670 rhs: value.type_info(),
1671 }));
1672 }
1673 },
1674 Pair::Pair(lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) {
1675 (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => {
1676 let rhs = rhs.as_integer()?;
1677 let value = (ops.u64)(*lhs, rhs).ok_or_else(ops.error)?;
1678 Inline::Unsigned(value)
1679 }
1680 (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => {
1681 let rhs = rhs.as_integer()?;
1682 let value = (ops.i64)(*lhs, rhs).ok_or_else(ops.error)?;
1683 Inline::Signed(value)
1684 }
1685 (Repr::Any(..), _) => {
1686 break 'fallback (lhs.clone(), rhs.clone());
1687 }
1688 (lhs, rhs) => {
1689 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1690 op: ops.protocol.name,
1691 lhs: lhs.type_info(),
1692 rhs: rhs.type_info(),
1693 }));
1694 }
1695 },
1696 }
1697 };
1698
1699 self.stack.store(out, inline)?;
1700 return Ok(());
1701 };
1702
1703 let mut args = DynGuardedArgs::new((rhs.clone(),));
1704
1705 if let CallResult::Unsupported(lhs) =
1706 self.call_instance_fn(Isolated::None, lhs, &ops.protocol, &mut args, out)?
1707 {
1708 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1709 op: ops.protocol.name,
1710 lhs: lhs.type_info(),
1711 rhs: rhs.type_info(),
1712 }));
1713 }
1714
1715 Ok(())
1716 }
1717
1718 #[cfg_attr(feature = "bench", inline(never))]
1719 fn op_assign_arithmetic(
1720 &mut self,
1721 op: InstArithmeticOp,
1722 target: InstTarget,
1723 rhs: Address,
1724 ) -> Result<(), VmError> {
1725 let ops = AssignArithmeticOps::from_op(op);
1726
1727 let fallback = match target_value(&mut self.stack, &self.unit, target, rhs)? {
1728 TargetValue::Same(value) => match value.as_mut() {
1729 Repr::Inline(Inline::Signed(value)) => {
1730 let out = (ops.i64)(*value, *value).ok_or_else(ops.error)?;
1731 *value = out;
1732 return Ok(());
1733 }
1734 Repr::Inline(Inline::Unsigned(value)) => {
1735 let out = (ops.u64)(*value, *value).ok_or_else(ops.error)?;
1736 *value = out;
1737 return Ok(());
1738 }
1739 Repr::Inline(Inline::Float(value)) => {
1740 let out = (ops.f64)(*value, *value);
1741 *value = out;
1742 return Ok(());
1743 }
1744 Repr::Any(..) => TargetFallback::Value(value.clone(), value.clone()),
1745 value => {
1746 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1747 op: ops.protocol.name,
1748 lhs: value.type_info(),
1749 rhs: value.type_info(),
1750 }));
1751 }
1752 },
1753 TargetValue::Pair(mut lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) {
1754 (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => {
1755 let rhs = rhs.as_integer()?;
1756 let out = (ops.i64)(*lhs, rhs).ok_or_else(ops.error)?;
1757 *lhs = out;
1758 return Ok(());
1759 }
1760 (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => {
1761 let rhs = rhs.as_integer()?;
1762 let out = (ops.u64)(*lhs, rhs).ok_or_else(ops.error)?;
1763 *lhs = out;
1764 return Ok(());
1765 }
1766 (Repr::Inline(Inline::Float(lhs)), Repr::Inline(Inline::Float(rhs))) => {
1767 let out = (ops.f64)(*lhs, *rhs);
1768 *lhs = out;
1769 return Ok(());
1770 }
1771 (Repr::Any(..), _) => TargetFallback::Value(lhs.clone(), rhs.clone()),
1772 (lhs, rhs) => {
1773 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1774 op: ops.protocol.name,
1775 lhs: lhs.type_info(),
1776 rhs: rhs.type_info(),
1777 }));
1778 }
1779 },
1780 TargetValue::Fallback(fallback) => fallback,
1781 };
1782
1783 self.target_fallback_assign(fallback, &ops.protocol)
1784 }
1785
1786 #[cfg_attr(feature = "bench", inline(never))]
1787 fn op_assign_bitwise(
1788 &mut self,
1789 op: InstBitwiseOp,
1790 target: InstTarget,
1791 rhs: Address,
1792 ) -> Result<(), VmError> {
1793 let ops = AssignBitwiseOps::from_ops(op);
1794
1795 let fallback = match target_value(&mut self.stack, &self.unit, target, rhs)? {
1796 TargetValue::Same(value) => match value.as_mut() {
1797 Repr::Inline(Inline::Unsigned(value)) => {
1798 let rhs = *value;
1799 (ops.u64)(value, rhs);
1800 return Ok(());
1801 }
1802 Repr::Inline(Inline::Signed(value)) => {
1803 let rhs = *value;
1804 (ops.i64)(value, rhs);
1805 return Ok(());
1806 }
1807 Repr::Inline(Inline::Bool(value)) => {
1808 let rhs = *value;
1809 (ops.bool)(value, rhs);
1810 return Ok(());
1811 }
1812 Repr::Any(..) => TargetFallback::Value(value.clone(), value.clone()),
1813 value => {
1814 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1815 op: ops.protocol.name,
1816 lhs: value.type_info(),
1817 rhs: value.type_info(),
1818 }));
1819 }
1820 },
1821 TargetValue::Pair(mut lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) {
1822 (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => {
1823 let rhs = rhs.as_integer()?;
1824 (ops.u64)(lhs, rhs);
1825 return Ok(());
1826 }
1827 (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => {
1828 let rhs = rhs.as_integer()?;
1829 (ops.i64)(lhs, rhs);
1830 return Ok(());
1831 }
1832 (Repr::Inline(Inline::Bool(lhs)), Repr::Inline(Inline::Bool(rhs))) => {
1833 (ops.bool)(lhs, *rhs);
1834 return Ok(());
1835 }
1836 (Repr::Any(..), ..) => TargetFallback::Value(lhs.clone(), rhs.clone()),
1837 (lhs, rhs) => {
1838 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1839 op: ops.protocol.name,
1840 lhs: lhs.type_info(),
1841 rhs: rhs.type_info(),
1842 }));
1843 }
1844 },
1845 TargetValue::Fallback(fallback) => fallback,
1846 };
1847
1848 self.target_fallback_assign(fallback, &ops.protocol)
1849 }
1850
1851 #[cfg_attr(feature = "bench", inline(never))]
1852 fn op_assign_shift(
1853 &mut self,
1854 op: InstShiftOp,
1855 target: InstTarget,
1856 rhs: Address,
1857 ) -> Result<(), VmError> {
1858 let ops = AssignShiftOps::from_op(op);
1859
1860 let fallback = match target_value(&mut self.stack, &self.unit, target, rhs)? {
1861 TargetValue::Same(value) => match value.as_mut() {
1862 Repr::Inline(Inline::Unsigned(value)) => {
1863 let shift = u32::try_from(*value).ok().ok_or_else(ops.error)?;
1864 let out = (ops.u64)(*value, shift).ok_or_else(ops.error)?;
1865 *value = out;
1866 return Ok(());
1867 }
1868 Repr::Inline(Inline::Signed(value)) => {
1869 let shift = u32::try_from(*value).ok().ok_or_else(ops.error)?;
1870 let out = (ops.i64)(*value, shift).ok_or_else(ops.error)?;
1871 *value = out;
1872 return Ok(());
1873 }
1874 Repr::Any(..) => TargetFallback::Value(value.clone(), value.clone()),
1875 value => {
1876 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1877 op: ops.protocol.name,
1878 lhs: value.type_info(),
1879 rhs: value.type_info(),
1880 }));
1881 }
1882 },
1883 TargetValue::Pair(mut lhs, rhs) => match (lhs.as_mut(), rhs.as_ref()) {
1884 (Repr::Inline(Inline::Unsigned(lhs)), Repr::Inline(rhs)) => {
1885 let rhs = rhs.as_integer()?;
1886 let out = (ops.u64)(*lhs, rhs).ok_or_else(ops.error)?;
1887 *lhs = out;
1888 return Ok(());
1889 }
1890 (Repr::Inline(Inline::Signed(lhs)), Repr::Inline(rhs)) => {
1891 let rhs = rhs.as_integer()?;
1892 let out = (ops.i64)(*lhs, rhs).ok_or_else(ops.error)?;
1893 *lhs = out;
1894 return Ok(());
1895 }
1896 (Repr::Any(..), _) => TargetFallback::Value(lhs.clone(), rhs.clone()),
1897 (lhs, rhs) => {
1898 return Err(VmError::new(VmErrorKind::UnsupportedBinaryOperation {
1899 op: ops.protocol.name,
1900 lhs: lhs.type_info(),
1901 rhs: rhs.type_info(),
1902 }));
1903 }
1904 },
1905 TargetValue::Fallback(fallback) => fallback,
1906 };
1907
1908 self.target_fallback_assign(fallback, &ops.protocol)
1909 }
1910
1911 #[cfg_attr(feature = "bench", inline(never))]
1913 fn op_index_set(
1914 &mut self,
1915 target: Address,
1916 index: Address,
1917 value: Address,
1918 ) -> Result<(), VmError> {
1919 let target = self.stack.at(target);
1920 let index = self.stack.at(index);
1921 let value = self.stack.at(value);
1922
1923 if let Some(field) = index.try_borrow_ref::<String>()? {
1924 if Self::try_object_slot_index_set(target, &field, value)? {
1925 return Ok(());
1926 }
1927 }
1928
1929 let target = target.clone();
1930 let index = index.clone();
1931 let value = value.clone();
1932
1933 let mut args = DynGuardedArgs::new((&index, &value));
1934
1935 if let CallResult::Unsupported(target) = self.call_instance_fn(
1936 Isolated::None,
1937 target,
1938 &Protocol::INDEX_SET,
1939 &mut args,
1940 Output::discard(),
1941 )? {
1942 return Err(VmError::new(VmErrorKind::UnsupportedIndexSet {
1943 target: target.type_info(),
1944 index: index.type_info(),
1945 value: value.type_info(),
1946 }));
1947 }
1948
1949 Ok(())
1950 }
1951
1952 #[inline]
1953 #[tracing::instrument(skip(self, return_value))]
1954 fn op_return_internal(&mut self, return_value: Value) -> Result<Option<Output>, VmError> {
1955 let (exit, out) = self.pop_call_frame();
1956
1957 let out = if let Some(out) = out {
1958 self.stack.store(out, return_value)?;
1959 out
1960 } else {
1961 let addr = self.stack.addr();
1962 self.stack.push(return_value)?;
1963 addr.output()
1964 };
1965
1966 Ok(exit.then_some(out))
1967 }
1968
1969 fn lookup_function_by_hash(&self, hash: Hash) -> Result<Function, VmErrorKind> {
1970 let Some(info) = self.unit.function(&hash) else {
1971 let Some(handler) = self.context.function(&hash) else {
1972 return Err(VmErrorKind::MissingContextFunction { hash });
1973 };
1974
1975 return Ok(Function::from_handler(handler.clone(), hash));
1976 };
1977
1978 let f = match info {
1979 UnitFn::Offset {
1980 offset, call, args, ..
1981 } => Function::from_vm_offset(
1982 self.context.clone(),
1983 self.unit.clone(),
1984 *offset,
1985 *call,
1986 *args,
1987 hash,
1988 ),
1989 UnitFn::EmptyStruct { hash } => {
1990 let Some(rtti) = self.unit.lookup_rtti(hash) else {
1991 return Err(VmErrorKind::MissingRtti { hash: *hash });
1992 };
1993
1994 Function::from_unit_struct(rtti.clone())
1995 }
1996 UnitFn::TupleStruct { hash, args } => {
1997 let Some(rtti) = self.unit.lookup_rtti(hash) else {
1998 return Err(VmErrorKind::MissingRtti { hash: *hash });
1999 };
2000
2001 Function::from_tuple_struct(rtti.clone(), *args)
2002 }
2003 };
2004
2005 Ok(f)
2006 }
2007
2008 #[cfg_attr(feature = "bench", inline(never))]
2009 fn op_return(&mut self, addr: Address) -> Result<Option<Output>, VmError> {
2010 let return_value = self.stack.at(addr).clone();
2011 self.op_return_internal(return_value)
2012 }
2013
2014 #[cfg_attr(feature = "bench", inline(never))]
2015 #[tracing::instrument(skip(self))]
2016 fn op_return_unit(&mut self) -> Result<Option<Output>, VmError> {
2017 let (exit, out) = self.pop_call_frame();
2018
2019 let out = if let Some(out) = out {
2020 self.stack.store(out, ())?;
2021 out
2022 } else {
2023 let addr = self.stack.addr();
2024 self.stack.push(())?;
2025 addr.output()
2026 };
2027
2028 Ok(exit.then_some(out))
2029 }
2030
2031 #[cfg_attr(feature = "bench", inline(never))]
2032 fn op_load_instance_fn(
2033 &mut self,
2034 addr: Address,
2035 hash: Hash,
2036 out: Output,
2037 ) -> Result<(), VmError> {
2038 let instance = self.stack.at(addr);
2039 let ty = instance.type_hash();
2040 let hash = Hash::associated_function(ty, hash);
2041 self.stack.store(out, || Type::new(hash))?;
2042 Ok(())
2043 }
2044
2045 #[cfg_attr(feature = "bench", inline(never))]
2047 fn op_index_get(
2048 &mut self,
2049 target: Address,
2050 index: Address,
2051 out: Output,
2052 ) -> Result<(), VmError> {
2053 let value = 'store: {
2054 let index = self.stack.at(index);
2055 let target = self.stack.at(target);
2056
2057 match index.as_ref() {
2058 Repr::Inline(inline) => {
2059 let index = inline.as_integer::<usize>()?;
2060
2061 if let Some(value) = Self::try_tuple_like_index_get(target, index)? {
2062 break 'store value;
2063 }
2064 }
2065 Repr::Any(value) => {
2066 if let Some(index) = value.try_borrow_ref::<String>()? {
2067 if let Some(value) =
2068 Self::try_object_like_index_get(target, index.as_str())?
2069 {
2070 break 'store value;
2071 }
2072 }
2073 }
2074 _ => {}
2075 }
2076
2077 let target = target.clone();
2078 let index = index.clone();
2079
2080 let mut args = DynGuardedArgs::new((&index,));
2081
2082 if let CallResult::Unsupported(target) =
2083 self.call_instance_fn(Isolated::None, target, &Protocol::INDEX_GET, &mut args, out)?
2084 {
2085 return Err(VmError::new(VmErrorKind::UnsupportedIndexGet {
2086 target: target.type_info(),
2087 index: index.type_info(),
2088 }));
2089 }
2090
2091 return Ok(());
2092 };
2093
2094 self.stack.store(out, value)?;
2095 Ok(())
2096 }
2097
2098 #[cfg_attr(feature = "bench", inline(never))]
2100 fn op_tuple_index_set(
2101 &mut self,
2102 target: Address,
2103 index: usize,
2104 value: Address,
2105 ) -> Result<(), VmError> {
2106 let value = self.stack.at(value);
2107 let target = self.stack.at(target);
2108
2109 if Self::try_tuple_like_index_set(target, index, value)? {
2110 return Ok(());
2111 }
2112
2113 Err(VmError::new(VmErrorKind::UnsupportedTupleIndexSet {
2114 target: target.type_info(),
2115 }))
2116 }
2117
2118 #[cfg_attr(feature = "bench", inline(never))]
2120 fn op_tuple_index_get_at(
2121 &mut self,
2122 addr: Address,
2123 index: usize,
2124 out: Output,
2125 ) -> Result<(), VmError> {
2126 let value = self.stack.at(addr);
2127
2128 if let Some(value) = Self::try_tuple_like_index_get(value, index)? {
2129 self.stack.store(out, value)?;
2130 return Ok(());
2131 }
2132
2133 let value = value.clone();
2134
2135 if let CallResult::Unsupported(value) =
2136 self.call_index_fn(&Protocol::GET, value, index, &mut (), out)?
2137 {
2138 return Err(VmError::new(VmErrorKind::UnsupportedTupleIndexGet {
2139 target: value.type_info(),
2140 index,
2141 }));
2142 }
2143
2144 Ok(())
2145 }
2146
2147 #[cfg_attr(feature = "bench", inline(never))]
2149 fn op_object_index_set(
2150 &mut self,
2151 target: Address,
2152 slot: usize,
2153 value: Address,
2154 ) -> Result<(), VmError> {
2155 let target = self.stack.at(target);
2156 let value = self.stack.at(value);
2157
2158 let Some(field) = self.unit.lookup_string(slot) else {
2159 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
2160 };
2161
2162 if Self::try_object_slot_index_set(target, field, value)? {
2163 return Ok(());
2164 }
2165
2166 let target = target.clone();
2167 let value = value.clone();
2168
2169 let hash = field.hash();
2170
2171 let mut args = DynGuardedArgs::new((value,));
2172
2173 let result =
2174 self.call_field_fn(&Protocol::SET, target, hash, &mut args, Output::discard())?;
2175
2176 if let CallResult::Unsupported(target) = result {
2177 let Some(field) = self.unit.lookup_string(slot) else {
2178 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
2179 };
2180
2181 return Err(VmError::new(VmErrorKind::UnsupportedObjectSlotIndexSet {
2182 target: target.type_info(),
2183 field: field.clone(),
2184 }));
2185 };
2186
2187 Ok(())
2188 }
2189
2190 #[cfg_attr(feature = "bench", inline(never))]
2192 fn op_object_index_get_at(
2193 &mut self,
2194 addr: Address,
2195 slot: usize,
2196 out: Output,
2197 ) -> Result<(), VmError> {
2198 let target = self.stack.at(addr);
2199
2200 let Some(index) = self.unit.lookup_string(slot) else {
2201 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
2202 };
2203
2204 match target.as_ref() {
2205 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => {
2206 let Some(value) = data.get_field_ref(index.as_str())? else {
2207 return Err(VmError::new(VmErrorKind::ObjectIndexMissing { slot }));
2208 };
2209
2210 let value = value.clone();
2211 self.stack.store(out, value)?;
2212 return Ok(());
2213 }
2214 Repr::Any(value) if value.type_hash() == Object::HASH => {
2215 let object = value.borrow_ref::<Object>()?;
2216
2217 let Some(value) = object.get(index.as_str()) else {
2218 return Err(VmError::new(VmErrorKind::ObjectIndexMissing { slot }));
2219 };
2220
2221 let value = value.clone();
2222 self.stack.store(out, value)?;
2223 return Ok(());
2224 }
2225 Repr::Any(..) => {}
2226 target => {
2227 return Err(VmError::new(VmErrorKind::UnsupportedObjectSlotIndexGet {
2228 target: target.type_info(),
2229 field: index.clone(),
2230 }));
2231 }
2232 }
2233
2234 let target = target.clone();
2235
2236 if let CallResult::Unsupported(target) =
2237 self.call_field_fn(&Protocol::GET, target, index.hash(), &mut (), out)?
2238 {
2239 let Some(field) = self.unit.lookup_string(slot) else {
2240 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
2241 };
2242
2243 return Err(VmError::new(VmErrorKind::UnsupportedObjectSlotIndexGet {
2244 target: target.type_info(),
2245 field: field.clone(),
2246 }));
2247 }
2248
2249 Ok(())
2250 }
2251
2252 #[cfg_attr(feature = "bench", inline(never))]
2254 fn op_object(&mut self, addr: Address, slot: usize, out: Output) -> Result<(), VmError> {
2255 let Some(keys) = self.unit.lookup_object_keys(slot) else {
2256 return Err(VmError::new(VmErrorKind::MissingStaticObjectKeys { slot }));
2257 };
2258
2259 let mut object = Object::with_capacity(keys.len())?;
2260 let values = self.stack.slice_at_mut(addr, keys.len())?;
2261
2262 for (key, value) in keys.iter().zip(values) {
2263 let key = String::try_from(key.as_str())?;
2264 object.insert(key, take(value))?;
2265 }
2266
2267 self.stack.store(out, object)?;
2268 Ok(())
2269 }
2270
2271 #[cfg_attr(feature = "bench", inline(never))]
2273 fn op_range(&mut self, range: InstRange, out: Output) -> Result<(), VmError> {
2274 let value = match range {
2275 InstRange::RangeFrom { start } => {
2276 let s = self.stack.at(start).clone();
2277 Value::new(RangeFrom::new(s.clone()))?
2278 }
2279 InstRange::RangeFull => Value::new(RangeFull::new())?,
2280 InstRange::RangeInclusive { start, end } => {
2281 let s = self.stack.at(start).clone();
2282 let e = self.stack.at(end).clone();
2283 Value::new(RangeInclusive::new(s.clone(), e.clone()))?
2284 }
2285 InstRange::RangeToInclusive { end } => {
2286 let e = self.stack.at(end).clone();
2287 Value::new(RangeToInclusive::new(e.clone()))?
2288 }
2289 InstRange::RangeTo { end } => {
2290 let e = self.stack.at(end).clone();
2291 Value::new(RangeTo::new(e.clone()))?
2292 }
2293 InstRange::Range { start, end } => {
2294 let s = self.stack.at(start).clone();
2295 let e = self.stack.at(end).clone();
2296 Value::new(Range::new(s.clone(), e.clone()))?
2297 }
2298 };
2299
2300 self.stack.store(out, value)?;
2301 Ok(())
2302 }
2303
2304 #[cfg_attr(feature = "bench", inline(never))]
2306 fn op_struct(&mut self, addr: Address, hash: Hash, out: Output) -> Result<(), VmError> {
2307 let Some(rtti) = self.unit.lookup_rtti(&hash) else {
2308 return Err(VmError::new(VmErrorKind::MissingRtti { hash }));
2309 };
2310
2311 let values = self.stack.slice_at_mut(addr, rtti.fields.len())?;
2312 let value = AnySequence::new(rtti.clone(), values.iter_mut().map(take))?;
2313 self.stack.store(out, value)?;
2314 Ok(())
2315 }
2316
2317 #[cfg_attr(feature = "bench", inline(never))]
2319 fn op_const_construct(
2320 &mut self,
2321 addr: Address,
2322 hash: Hash,
2323 count: usize,
2324 out: Output,
2325 ) -> Result<(), VmError> {
2326 let values = self.stack.slice_at_mut(addr, count)?;
2327
2328 let Some(construct) = self.context.construct(&hash) else {
2329 return Err(VmError::new(VmErrorKind::MissingConstantConstructor {
2330 hash,
2331 }));
2332 };
2333
2334 let value = construct.runtime_construct(values)?;
2335 self.stack.store(out, value)?;
2336 Ok(())
2337 }
2338
2339 #[cfg_attr(feature = "bench", inline(never))]
2340 fn op_string(&mut self, slot: usize, out: Output) -> Result<(), VmError> {
2341 let Some(string) = self.unit.lookup_string(slot) else {
2342 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
2343 };
2344
2345 self.stack.store(out, string.as_str())?;
2346 Ok(())
2347 }
2348
2349 #[cfg_attr(feature = "bench", inline(never))]
2350 fn op_bytes(&mut self, slot: usize, out: Output) -> Result<(), VmError> {
2351 let Some(bytes) = self.unit.lookup_bytes(slot) else {
2352 return Err(VmError::new(VmErrorKind::MissingStaticBytes { slot }));
2353 };
2354
2355 self.stack.store(out, bytes)?;
2356 Ok(())
2357 }
2358
2359 #[cfg_attr(feature = "bench", inline(never))]
2361 fn op_string_concat(
2362 &mut self,
2363 addr: Address,
2364 len: usize,
2365 size_hint: usize,
2366 out: Output,
2367 ) -> Result<(), VmError> {
2368 let values = self.stack.slice_at(addr, len)?;
2369 let values = values.iter().cloned().try_collect::<alloc::Vec<_>>()?;
2370
2371 let mut s = String::try_with_capacity(size_hint)?;
2372
2373 Formatter::format_with(&mut s, |f| {
2374 for value in values {
2375 value.display_fmt_with(f, &mut *self)?;
2376 }
2377
2378 Ok::<_, VmError>(())
2379 })?;
2380
2381 self.stack.store(out, s)?;
2382 Ok(())
2383 }
2384
2385 #[cfg_attr(feature = "bench", inline(never))]
2387 fn op_format(&mut self, addr: Address, spec: FormatSpec, out: Output) -> Result<(), VmError> {
2388 let value = self.stack.at(addr).clone();
2389 self.stack.store(out, || Format { value, spec })?;
2390 Ok(())
2391 }
2392
2393 #[cfg_attr(feature = "bench", inline(never))]
2395 fn op_try(&mut self, addr: Address, out: Output) -> Result<Option<Output>, VmError> {
2396 let result = 'out: {
2397 let value = {
2398 let value = self.stack.at(addr);
2399
2400 if let Repr::Any(value) = value.as_ref() {
2401 match value.type_hash() {
2402 Result::<Value, Value>::HASH => {
2403 let result = value.borrow_ref::<Result<Value, Value>>()?;
2404 break 'out result::result_try(&result)?;
2405 }
2406 Option::<Value>::HASH => {
2407 let option = value.borrow_ref::<Option<Value>>()?;
2408 break 'out option::option_try(&option)?;
2409 }
2410 _ => {}
2411 }
2412 }
2413
2414 value.clone()
2415 };
2416
2417 match self.try_call_protocol_fn(&Protocol::TRY, value, &mut ())? {
2418 CallResultOnly::Ok(value) => ControlFlow::from_value(value)?,
2419 CallResultOnly::Unsupported(target) => {
2420 return Err(VmError::new(VmErrorKind::UnsupportedTryOperand {
2421 actual: target.type_info(),
2422 }));
2423 }
2424 }
2425 };
2426
2427 match result {
2428 ControlFlow::Continue(value) => {
2429 self.stack.store(out, value)?;
2430 Ok(None)
2431 }
2432 ControlFlow::Break(error) => Ok(self.op_return_internal(error)?),
2433 }
2434 }
2435
2436 #[cfg_attr(feature = "bench", inline(never))]
2437 fn op_eq_character(&mut self, addr: Address, value: char, out: Output) -> Result<(), VmError> {
2438 let v = self.stack.at(addr);
2439
2440 let is_match = match v.as_inline() {
2441 Some(Inline::Char(actual)) => *actual == value,
2442 _ => false,
2443 };
2444
2445 self.stack.store(out, is_match)?;
2446 Ok(())
2447 }
2448
2449 #[cfg_attr(feature = "bench", inline(never))]
2450 fn op_eq_unsigned(&mut self, addr: Address, value: u64, out: Output) -> Result<(), VmError> {
2451 let v = self.stack.at(addr);
2452
2453 let is_match = match v.as_inline() {
2454 Some(Inline::Unsigned(actual)) => *actual == value,
2455 _ => false,
2456 };
2457
2458 self.stack.store(out, is_match)?;
2459 Ok(())
2460 }
2461
2462 #[cfg_attr(feature = "bench", inline(never))]
2463 fn op_eq_signed(&mut self, addr: Address, value: i64, out: Output) -> Result<(), VmError> {
2464 let is_match = match self.stack.at(addr).as_inline() {
2465 Some(Inline::Signed(actual)) => *actual == value,
2466 _ => false,
2467 };
2468
2469 self.stack.store(out, is_match)?;
2470 Ok(())
2471 }
2472
2473 #[cfg_attr(feature = "bench", inline(never))]
2474 fn op_eq_bool(&mut self, addr: Address, value: bool, out: Output) -> Result<(), VmError> {
2475 let v = self.stack.at(addr);
2476
2477 let is_match = match v.as_inline() {
2478 Some(Inline::Bool(actual)) => *actual == value,
2479 _ => false,
2480 };
2481
2482 self.stack.store(out, is_match)?;
2483 Ok(())
2484 }
2485
2486 #[cfg_attr(feature = "bench", inline(never))]
2489 fn op_eq_string(&mut self, addr: Address, slot: usize, out: Output) -> Result<(), VmError> {
2490 let v = self.stack.at(addr);
2491
2492 let is_match = 'out: {
2493 let Some(actual) = v.try_borrow_ref::<String>()? else {
2494 break 'out false;
2495 };
2496
2497 let Some(string) = self.unit.lookup_string(slot) else {
2498 return Err(VmError::new(VmErrorKind::MissingStaticString { slot }));
2499 };
2500
2501 actual.as_str() == string.as_str()
2502 };
2503
2504 self.stack.store(out, is_match)?;
2505 Ok(())
2506 }
2507
2508 #[cfg_attr(feature = "bench", inline(never))]
2511 fn op_eq_bytes(&mut self, addr: Address, slot: usize, out: Output) -> Result<(), VmError> {
2512 let v = self.stack.at(addr);
2513
2514 let is_match = 'out: {
2515 let Some(value) = v.try_borrow_ref::<Bytes>()? else {
2516 break 'out false;
2517 };
2518
2519 let Some(bytes) = self.unit.lookup_bytes(slot) else {
2520 return Err(VmError::new(VmErrorKind::MissingStaticBytes { slot }));
2521 };
2522
2523 value.as_slice() == bytes
2524 };
2525
2526 self.stack.store(out, is_match)?;
2527 Ok(())
2528 }
2529
2530 #[cfg_attr(feature = "bench", inline(never))]
2531 fn op_match_type(
2532 &mut self,
2533 hash: Hash,
2534 variant_hash: Hash,
2535 addr: Address,
2536 out: Output,
2537 ) -> Result<(), VmError> {
2538 let value = self.stack.at(addr);
2539
2540 let type_hash = value.type_hash();
2541
2542 let is_match = 'out: {
2543 if type_hash != hash {
2544 break 'out false;
2545 }
2546
2547 if variant_hash == Hash::EMPTY {
2549 break 'out true;
2550 }
2551
2552 match value.as_ref() {
2553 Repr::Inline(Inline::Ordering(ordering)) => {
2554 break 'out cmp::ordering_is_variant(*ordering, variant_hash);
2555 }
2556 Repr::Dynamic(value) => {
2557 break 'out value.rtti().is(hash, variant_hash);
2558 }
2559 Repr::Any(any) => match hash {
2560 Result::<Value, Value>::HASH => {
2561 let result = any.borrow_ref::<Result<Value, Value>>()?;
2562 break 'out result::is_variant(&result, variant_hash);
2563 }
2564 Option::<Value>::HASH => {
2565 let option = any.borrow_ref::<Option<Value>>()?;
2566 break 'out option::is_variant(&option, variant_hash);
2567 }
2568 _ => {}
2569 },
2570 _ => break 'out true,
2571 }
2572
2573 let value = value.clone();
2574
2575 match self.try_call_protocol_fn(
2576 &Protocol::IS_VARIANT,
2577 value,
2578 &mut Some((variant_hash,)),
2579 )? {
2580 CallResultOnly::Ok(value) => bool::from_value(value)?,
2581 CallResultOnly::Unsupported(..) => false,
2582 }
2583 };
2584
2585 self.stack.store(out, is_match)?;
2586 Ok(())
2587 }
2588
2589 #[cfg_attr(feature = "bench", inline(never))]
2590 fn op_match_sequence(
2591 &mut self,
2592 hash: Hash,
2593 len: usize,
2594 exact: bool,
2595 addr: Address,
2596 out: Output,
2597 ) -> Result<(), VmError> {
2598 let value = self.stack.at(addr);
2599
2600 let type_hash = value.type_hash();
2601
2602 let is_match = 'out: {
2603 if type_hash != hash {
2604 break 'out false;
2605 }
2606
2607 let actual = match value.as_ref() {
2608 Repr::Inline(Inline::Unit) => 0,
2609 Repr::Any(any) => match type_hash {
2610 runtime::Vec::HASH => any.borrow_ref::<runtime::Vec>()?.len(),
2611 runtime::OwnedTuple::HASH => any.borrow_ref::<runtime::OwnedTuple>()?.len(),
2612 _ => break 'out false,
2613 },
2614 _ => break 'out false,
2615 };
2616
2617 actual >= len && (!exact || actual == len)
2618 };
2619
2620 self.stack.store(out, is_match)?;
2621 Ok(())
2622 }
2623
2624 #[cfg_attr(feature = "bench", inline(never))]
2625 fn op_match_object(
2626 &mut self,
2627 slot: usize,
2628 exact: bool,
2629 addr: Address,
2630 out: Output,
2631 ) -> Result<(), VmError> {
2632 let is_match = 'is_match: {
2633 let Some(object) = self.stack.at(addr).try_borrow_ref::<Object>()? else {
2634 break 'is_match false;
2635 };
2636
2637 let Some(keys) = self.unit.lookup_object_keys(slot) else {
2638 return Err(VmError::new(VmErrorKind::MissingStaticObjectKeys { slot }));
2639 };
2640
2641 if object.len() < keys.len() || (exact && object.len() != keys.len()) {
2642 break 'is_match false;
2643 }
2644
2645 for key in keys {
2646 if !object.contains_key(key.as_str()) {
2647 break 'is_match false;
2648 }
2649 }
2650
2651 true
2652 };
2653
2654 self.stack.store(out, is_match)?;
2655 Ok(())
2656 }
2657
2658 #[cfg_attr(feature = "bench", inline(never))]
2660 fn op_load_fn(&mut self, hash: Hash, out: Output) -> Result<(), VmError> {
2661 let function = self.lookup_function_by_hash(hash)?;
2662 self.stack.store(out, function)?;
2663 Ok(())
2664 }
2665
2666 #[cfg_attr(feature = "bench", inline(never))]
2668 fn op_closure(
2669 &mut self,
2670 hash: Hash,
2671 addr: Address,
2672 count: usize,
2673 out: Output,
2674 ) -> Result<(), VmError> {
2675 let Some(UnitFn::Offset {
2676 offset,
2677 call,
2678 args,
2679 captures: Some(captures),
2680 }) = self.unit.function(&hash)
2681 else {
2682 return Err(VmError::new(VmErrorKind::MissingFunction { hash }));
2683 };
2684
2685 if *captures != count {
2686 return Err(VmError::new(VmErrorKind::BadEnvironmentCount {
2687 expected: *captures,
2688 actual: count,
2689 }));
2690 }
2691
2692 let environment = self.stack.slice_at(addr, count)?;
2693 let environment = environment
2694 .iter()
2695 .cloned()
2696 .try_collect::<alloc::Vec<Value>>()?;
2697 let environment = environment.try_into_boxed_slice()?;
2698
2699 let function = Function::from_vm_closure(
2700 self.context.clone(),
2701 self.unit.clone(),
2702 *offset,
2703 *call,
2704 *args,
2705 environment,
2706 hash,
2707 );
2708
2709 self.stack.store(out, function)?;
2710 Ok(())
2711 }
2712
2713 #[cfg_attr(feature = "bench", inline(never))]
2715 fn op_call(
2716 &mut self,
2717 hash: Hash,
2718 addr: Address,
2719 args: usize,
2720 out: Output,
2721 ) -> Result<(), VmError> {
2722 let Some(info) = self.unit.function(&hash) else {
2723 let Some(handler) = self.context.function(&hash) else {
2724 return Err(VmError::new(VmErrorKind::MissingFunction { hash }));
2725 };
2726
2727 handler.call(&mut self.stack, addr, args, out)?;
2728 return Ok(());
2729 };
2730
2731 match info {
2732 UnitFn::Offset {
2733 offset,
2734 call,
2735 args: expected,
2736 ..
2737 } => {
2738 check_args(args, *expected)?;
2739 self.call_offset_fn(*offset, *call, addr, args, Isolated::None, out)?;
2740 }
2741 UnitFn::EmptyStruct { hash } => {
2742 check_args(args, 0)?;
2743
2744 let Some(rtti) = self.unit.lookup_rtti(hash) else {
2745 return Err(VmError::new(VmErrorKind::MissingRtti { hash: *hash }));
2746 };
2747
2748 self.stack
2749 .store(out, || Value::empty_struct(rtti.clone()))?;
2750 }
2751 UnitFn::TupleStruct {
2752 hash,
2753 args: expected,
2754 } => {
2755 check_args(args, *expected)?;
2756
2757 let Some(rtti) = self.unit.lookup_rtti(hash) else {
2758 return Err(VmError::new(VmErrorKind::MissingRtti { hash: *hash }));
2759 };
2760
2761 let tuple = self.stack.slice_at_mut(addr, args)?;
2762 let data = tuple.iter_mut().map(take);
2763 let value = AnySequence::new(rtti.clone(), data)?;
2764 self.stack.store(out, value)?;
2765 }
2766 }
2767
2768 Ok(())
2769 }
2770
2771 #[cfg_attr(feature = "bench", inline(never))]
2773 fn op_call_offset(
2774 &mut self,
2775 offset: usize,
2776 call: Call,
2777 addr: Address,
2778 args: usize,
2779 out: Output,
2780 ) -> Result<(), VmError> {
2781 self.call_offset_fn(offset, call, addr, args, Isolated::None, out)?;
2782 Ok(())
2783 }
2784
2785 #[cfg_attr(feature = "bench", inline(never))]
2786 fn op_call_associated(
2787 &mut self,
2788 hash: Hash,
2789 addr: Address,
2790 args: usize,
2791 out: Output,
2792 ) -> Result<(), VmError> {
2793 let instance = self.stack.at(addr);
2794 let type_hash = instance.type_hash();
2795 let hash = Hash::associated_function(type_hash, hash);
2796
2797 if let Some(handler) = self.context.function(&hash) {
2798 self.called_function_hook(hash)?;
2799 handler.call(&mut self.stack, addr, args, out)?;
2800 return Ok(());
2801 }
2802
2803 if let Some(UnitFn::Offset {
2804 offset,
2805 call,
2806 args: expected,
2807 ..
2808 }) = self.unit.function(&hash)
2809 {
2810 self.called_function_hook(hash)?;
2811 check_args(args, *expected)?;
2812 self.call_offset_fn(*offset, *call, addr, args, Isolated::None, out)?;
2813 return Ok(());
2814 }
2815
2816 Err(VmError::new(VmErrorKind::MissingInstanceFunction {
2817 instance: instance.type_info(),
2818 hash,
2819 }))
2820 }
2821
2822 #[cfg_attr(feature = "bench", inline(never))]
2823 #[tracing::instrument(skip(self))]
2824 fn op_call_fn(
2825 &mut self,
2826 function: Address,
2827 addr: Address,
2828 args: usize,
2829 out: Output,
2830 ) -> Result<Option<VmHalt>, VmError> {
2831 let function = self.stack.at(function);
2832
2833 match function.as_ref() {
2834 Repr::Inline(Inline::Type(ty)) => {
2835 self.op_call(ty.into_hash(), addr, args, out)?;
2836 Ok(None)
2837 }
2838 Repr::Any(value) if value.type_hash() == Function::HASH => {
2839 let value = value.clone();
2840 let f = value.borrow_ref::<Function>()?;
2841 f.call_with_vm(self, addr, args, out)
2842 }
2843 value => Err(VmError::new(VmErrorKind::UnsupportedCallFn {
2844 actual: value.type_info(),
2845 })),
2846 }
2847 }
2848
2849 #[cfg_attr(feature = "bench", inline(never))]
2850 fn op_iter_next(&mut self, addr: Address, jump: usize, out: Output) -> Result<(), VmError> {
2851 let value = self.stack.at(addr);
2852
2853 let some = match value.as_ref() {
2854 Repr::Any(value) => match value.type_hash() {
2855 Option::<Value>::HASH => {
2856 let option = value.borrow_ref::<Option<Value>>()?;
2857
2858 let Some(some) = &*option else {
2859 self.ip = self.unit.translate(jump)?;
2860 return Ok(());
2861 };
2862
2863 some.clone()
2864 }
2865 _ => {
2866 return Err(VmError::new(VmErrorKind::UnsupportedIterNextOperand {
2867 actual: value.type_info(),
2868 }));
2869 }
2870 },
2871 actual => {
2872 return Err(VmError::new(VmErrorKind::UnsupportedIterNextOperand {
2873 actual: actual.type_info(),
2874 }));
2875 }
2876 };
2877
2878 self.stack.store(out, some)?;
2879 Ok(())
2880 }
2881
2882 pub fn with<F, T>(&self, f: F) -> T
2897 where
2898 F: FnOnce() -> T,
2899 {
2900 let _guard = runtime::env::Guard::new(self.context.clone(), self.unit.clone(), None);
2901 f()
2902 }
2903
2904 pub(crate) fn run(
2906 &mut self,
2907 diagnostics: Option<&mut dyn VmDiagnostics>,
2908 ) -> Result<VmHalt, VmError> {
2909 let mut vm_diagnostics_obj;
2910
2911 let diagnostics = match diagnostics {
2912 Some(diagnostics) => {
2913 vm_diagnostics_obj = VmDiagnosticsObj::new(diagnostics);
2914 Some(NonNull::from(&mut vm_diagnostics_obj))
2915 }
2916 None => None,
2917 };
2918
2919 let _guard = runtime::env::Guard::new(self.context.clone(), self.unit.clone(), diagnostics);
2922
2923 let mut budget = budget::acquire();
2924
2925 loop {
2926 if !budget.take() {
2927 return Ok(VmHalt::Limited);
2928 }
2929
2930 let Some((inst, inst_len)) = self.unit.instruction_at(self.ip)? else {
2931 return Err(VmError::new(VmErrorKind::IpOutOfBounds {
2932 ip: self.ip,
2933 length: self.unit.instructions().end(),
2934 }));
2935 };
2936
2937 tracing::trace!(ip = ?self.ip, ?inst);
2938
2939 self.ip = self.ip.wrapping_add(inst_len);
2940 self.last_ip_len = inst_len as u8;
2941
2942 match inst.kind {
2943 inst::Kind::Allocate { size } => {
2944 self.op_allocate(size)?;
2945 }
2946 inst::Kind::Not { addr, out } => {
2947 self.op_not(addr, out)?;
2948 }
2949 inst::Kind::Neg { addr, out } => {
2950 self.op_neg(addr, out)?;
2951 }
2952 inst::Kind::Closure {
2953 hash,
2954 addr,
2955 count,
2956 out,
2957 } => {
2958 self.op_closure(hash, addr, count, out)?;
2959 }
2960 inst::Kind::Call {
2961 hash,
2962 addr,
2963 args,
2964 out,
2965 } => {
2966 self.op_call(hash, addr, args, out)?;
2967 }
2968 inst::Kind::CallOffset {
2969 offset,
2970 call,
2971 addr,
2972 args,
2973 out,
2974 } => {
2975 self.op_call_offset(offset, call, addr, args, out)?;
2976 }
2977 inst::Kind::CallAssociated {
2978 hash,
2979 addr,
2980 args,
2981 out,
2982 } => {
2983 self.op_call_associated(hash, addr, args, out)?;
2984 }
2985 inst::Kind::CallFn {
2986 function,
2987 addr,
2988 args,
2989 out,
2990 } => {
2991 if let Some(reason) = self.op_call_fn(function, addr, args, out)? {
2992 return Ok(reason);
2993 }
2994 }
2995 inst::Kind::LoadInstanceFn { addr, hash, out } => {
2996 self.op_load_instance_fn(addr, hash, out)?;
2997 }
2998 inst::Kind::IndexGet { target, index, out } => {
2999 self.op_index_get(target, index, out)?;
3000 }
3001 inst::Kind::TupleIndexSet {
3002 target,
3003 index,
3004 value,
3005 } => {
3006 self.op_tuple_index_set(target, index, value)?;
3007 }
3008 inst::Kind::TupleIndexGetAt { addr, index, out } => {
3009 self.op_tuple_index_get_at(addr, index, out)?;
3010 }
3011 inst::Kind::ObjectIndexSet {
3012 target,
3013 slot,
3014 value,
3015 } => {
3016 self.op_object_index_set(target, slot, value)?;
3017 }
3018 inst::Kind::ObjectIndexGetAt { addr, slot, out } => {
3019 self.op_object_index_get_at(addr, slot, out)?;
3020 }
3021 inst::Kind::IndexSet {
3022 target,
3023 index,
3024 value,
3025 } => {
3026 self.op_index_set(target, index, value)?;
3027 }
3028 inst::Kind::Return { addr } => {
3029 if let Some(out) = self.op_return(addr)? {
3030 return Ok(VmHalt::Exited(out.as_addr()));
3031 }
3032 }
3033 inst::Kind::ReturnUnit => {
3034 if let Some(out) = self.op_return_unit()? {
3035 return Ok(VmHalt::Exited(out.as_addr()));
3036 }
3037 }
3038 inst::Kind::Await { addr, out } => {
3039 let future = self.op_await(addr)?;
3040 return Ok(VmHalt::Awaited(Awaited::Future(future, out)));
3041 }
3042 inst::Kind::Select { addr, len, value } => {
3043 if let Some(select) = self.op_select(addr, len, value)? {
3044 return Ok(VmHalt::Awaited(Awaited::Select(select, value)));
3045 }
3046 }
3047 inst::Kind::LoadFn { hash, out } => {
3048 self.op_load_fn(hash, out)?;
3049 }
3050 inst::Kind::Store { value, out } => {
3051 self.op_store(value, out)?;
3052 }
3053 inst::Kind::Copy { addr, out } => {
3054 self.op_copy(addr, out)?;
3055 }
3056 inst::Kind::Move { addr, out } => {
3057 self.op_move(addr, out)?;
3058 }
3059 inst::Kind::Drop { set } => {
3060 self.op_drop(set)?;
3061 }
3062 inst::Kind::Swap { a, b } => {
3063 self.op_swap(a, b)?;
3064 }
3065 inst::Kind::Jump { jump } => {
3066 self.op_jump(jump)?;
3067 }
3068 inst::Kind::JumpIf { cond, jump } => {
3069 self.op_jump_if(cond, jump)?;
3070 }
3071 inst::Kind::JumpIfNot { cond, jump } => {
3072 self.op_jump_if_not(cond, jump)?;
3073 }
3074 inst::Kind::Vec { addr, count, out } => {
3075 self.op_vec(addr, count, out)?;
3076 }
3077 inst::Kind::Tuple { addr, count, out } => {
3078 self.op_tuple(addr, count, out)?;
3079 }
3080 inst::Kind::Tuple1 { addr, out } => {
3081 self.op_tuple_n(&addr[..], out)?;
3082 }
3083 inst::Kind::Tuple2 { addr, out } => {
3084 self.op_tuple_n(&addr[..], out)?;
3085 }
3086 inst::Kind::Tuple3 { addr, out } => {
3087 self.op_tuple_n(&addr[..], out)?;
3088 }
3089 inst::Kind::Tuple4 { addr, out } => {
3090 self.op_tuple_n(&addr[..], out)?;
3091 }
3092 inst::Kind::Environment { addr, count, out } => {
3093 self.op_environment(addr, count, out)?;
3094 }
3095 inst::Kind::Object { addr, slot, out } => {
3096 self.op_object(addr, slot, out)?;
3097 }
3098 inst::Kind::Range { range, out } => {
3099 self.op_range(range, out)?;
3100 }
3101 inst::Kind::Struct { addr, hash, out } => {
3102 self.op_struct(addr, hash, out)?;
3103 }
3104 inst::Kind::ConstConstruct {
3105 addr,
3106 hash,
3107 count,
3108 out,
3109 } => {
3110 self.op_const_construct(addr, hash, count, out)?;
3111 }
3112 inst::Kind::String { slot, out } => {
3113 self.op_string(slot, out)?;
3114 }
3115 inst::Kind::Bytes { slot, out } => {
3116 self.op_bytes(slot, out)?;
3117 }
3118 inst::Kind::StringConcat {
3119 addr,
3120 len,
3121 size_hint,
3122 out,
3123 } => {
3124 self.op_string_concat(addr, len, size_hint, out)?;
3125 }
3126 inst::Kind::Format { addr, spec, out } => {
3127 self.op_format(addr, spec, out)?;
3128 }
3129 inst::Kind::Try { addr, out } => {
3130 if let Some(out) = self.op_try(addr, out)? {
3131 return Ok(VmHalt::Exited(out.as_addr()));
3132 }
3133 }
3134 inst::Kind::EqChar { addr, value, out } => {
3135 self.op_eq_character(addr, value, out)?;
3136 }
3137 inst::Kind::EqUnsigned { addr, value, out } => {
3138 self.op_eq_unsigned(addr, value, out)?;
3139 }
3140 inst::Kind::EqSigned { addr, value, out } => {
3141 self.op_eq_signed(addr, value, out)?;
3142 }
3143 inst::Kind::EqBool {
3144 addr,
3145 value: boolean,
3146 out,
3147 } => {
3148 self.op_eq_bool(addr, boolean, out)?;
3149 }
3150 inst::Kind::EqString { addr, slot, out } => {
3151 self.op_eq_string(addr, slot, out)?;
3152 }
3153 inst::Kind::EqBytes { addr, slot, out } => {
3154 self.op_eq_bytes(addr, slot, out)?;
3155 }
3156 inst::Kind::MatchType {
3157 hash,
3158 variant_hash,
3159 addr,
3160 out,
3161 } => {
3162 self.op_match_type(hash, variant_hash, addr, out)?;
3163 }
3164 inst::Kind::MatchSequence {
3165 hash,
3166 len,
3167 exact,
3168 addr,
3169 out,
3170 } => {
3171 self.op_match_sequence(hash, len, exact, addr, out)?;
3172 }
3173 inst::Kind::MatchObject {
3174 slot,
3175 exact,
3176 addr,
3177 out,
3178 } => {
3179 self.op_match_object(slot, exact, addr, out)?;
3180 }
3181 inst::Kind::Yield { addr, out } => {
3182 return Ok(VmHalt::Yielded(Some(addr), out));
3183 }
3184 inst::Kind::YieldUnit { out } => {
3185 return Ok(VmHalt::Yielded(None, out));
3186 }
3187 inst::Kind::Op { op, a, b, out } => {
3188 self.op_op(op, a, b, out)?;
3189 }
3190 inst::Kind::Arithmetic { op, a, b, out } => {
3191 self.op_arithmetic(op, a, b, out)?;
3192 }
3193 inst::Kind::Bitwise { op, a, b, out } => {
3194 self.op_bitwise(op, a, b, out)?;
3195 }
3196 inst::Kind::Shift { op, a, b, out } => {
3197 self.op_shift(op, a, b, out)?;
3198 }
3199 inst::Kind::AssignArithmetic { op, target, rhs } => {
3200 self.op_assign_arithmetic(op, target, rhs)?;
3201 }
3202 inst::Kind::AssignBitwise { op, target, rhs } => {
3203 self.op_assign_bitwise(op, target, rhs)?;
3204 }
3205 inst::Kind::AssignShift { op, target, rhs } => {
3206 self.op_assign_shift(op, target, rhs)?;
3207 }
3208 inst::Kind::IterNext { addr, jump, out } => {
3209 self.op_iter_next(addr, jump, out)?;
3210 }
3211 inst::Kind::Panic { reason } => {
3212 return Err(VmError::new(VmErrorKind::Panic {
3213 reason: Panic::from(reason),
3214 }));
3215 }
3216 }
3217 }
3218 }
3219}
3220
3221impl TryClone for Vm {
3222 fn try_clone(&self) -> alloc::Result<Self> {
3223 Ok(Self {
3224 context: self.context.clone(),
3225 unit: self.unit.clone(),
3226 ip: self.ip,
3227 last_ip_len: self.last_ip_len,
3228 stack: self.stack.try_clone()?,
3229 call_frames: self.call_frames.try_clone()?,
3230 })
3231 }
3232}
3233
3234impl AsMut<Vm> for Vm {
3235 #[inline]
3236 fn as_mut(&mut self) -> &mut Vm {
3237 self
3238 }
3239}
3240
3241impl AsRef<Vm> for Vm {
3242 #[inline]
3243 fn as_ref(&self) -> &Vm {
3244 self
3245 }
3246}
3247
3248#[derive(Debug, Clone, Copy)]
3252#[non_exhaustive]
3253pub struct CallFrame {
3254 pub ip: usize,
3256 pub top: usize,
3262 pub isolated: Isolated,
3265 pub out: Output,
3267}
3268
3269impl TryClone for CallFrame {
3270 #[inline]
3271 fn try_clone(&self) -> alloc::Result<Self> {
3272 Ok(*self)
3273 }
3274}
3275
3276struct ClearStack<'a>(&'a mut Vm);
3278
3279impl Drop for ClearStack<'_> {
3280 fn drop(&mut self) {
3281 self.0.stack.clear();
3282 }
3283}
3284
3285#[inline(always)]
3287fn check_args(args: usize, expected: usize) -> Result<(), VmErrorKind> {
3288 if args != expected {
3289 return Err(VmErrorKind::BadArgumentCount {
3290 actual: args,
3291 expected,
3292 });
3293 }
3294
3295 Ok(())
3296}
3297
3298enum TargetFallback {
3299 Value(Value, Value),
3300 Field(Value, Hash, usize, Value),
3301 Index(Value, usize, Value),
3302}
3303
3304enum TargetValue<'a> {
3305 Same(&'a mut Value),
3307 Pair(BorrowMut<'a, Value>, &'a Value),
3309 Fallback(TargetFallback),
3311}
3312
3313#[inline]
3314fn target_value<'a>(
3315 stack: &'a mut Stack,
3316 unit: &Unit,
3317 target: InstTarget,
3318 rhs: Address,
3319) -> Result<TargetValue<'a>, VmErrorKind> {
3320 match target {
3321 InstTarget::Address(addr) => match stack.pair(addr, rhs)? {
3322 Pair::Same(value) => Ok(TargetValue::Same(value)),
3323 Pair::Pair(lhs, rhs) => Ok(TargetValue::Pair(BorrowMut::from_ref(lhs), rhs)),
3324 },
3325 InstTarget::TupleField(lhs, index) => {
3326 let lhs = stack.at(lhs);
3327 let rhs = stack.at(rhs);
3328
3329 if let Some(value) = try_tuple_like_index_get_mut(lhs, index)? {
3330 Ok(TargetValue::Pair(value, rhs))
3331 } else {
3332 Ok(TargetValue::Fallback(TargetFallback::Index(
3333 lhs.clone(),
3334 index,
3335 rhs.clone(),
3336 )))
3337 }
3338 }
3339 InstTarget::Field(lhs, slot) => {
3340 let rhs = stack.at(rhs);
3341
3342 let Some(field) = unit.lookup_string(slot) else {
3343 return Err(VmErrorKind::MissingStaticString { slot });
3344 };
3345
3346 let lhs = stack.at(lhs);
3347
3348 if let Some(value) = try_object_like_index_get_mut(lhs, field)? {
3349 Ok(TargetValue::Pair(value, rhs))
3350 } else {
3351 Ok(TargetValue::Fallback(TargetFallback::Field(
3352 lhs.clone(),
3353 field.hash(),
3354 slot,
3355 rhs.clone(),
3356 )))
3357 }
3358 }
3359 }
3360}
3361
3362fn try_tuple_like_index_get_mut(
3364 target: &Value,
3365 index: usize,
3366) -> Result<Option<BorrowMut<'_, Value>>, VmErrorKind> {
3367 match target.as_ref() {
3368 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Tuple) => {
3369 let Some(value) = data.get_mut(index)? else {
3370 return Err(VmErrorKind::MissingIndexInteger {
3371 target: data.type_info(),
3372 index: VmIntegerRepr::from(index),
3373 });
3374 };
3375
3376 Ok(Some(value))
3377 }
3378 Repr::Dynamic(data) => Err(VmErrorKind::MissingIndexInteger {
3379 target: data.type_info(),
3380 index: VmIntegerRepr::from(index),
3381 }),
3382 Repr::Any(value) => match value.type_hash() {
3383 Result::<Value, Value>::HASH => {
3384 let result = BorrowMut::try_map(
3385 value.borrow_mut::<Result<Value, Value>>()?,
3386 |value| match (index, value) {
3387 (0, Ok(value)) => Some(value),
3388 (0, Err(value)) => Some(value),
3389 _ => None,
3390 },
3391 );
3392
3393 if let Ok(value) = result {
3394 return Ok(Some(value));
3395 }
3396
3397 Err(VmErrorKind::MissingIndexInteger {
3398 target: TypeInfo::any::<Result<Value, Value>>(),
3399 index: VmIntegerRepr::from(index),
3400 })
3401 }
3402 Option::<Value>::HASH => {
3403 let result =
3404 BorrowMut::try_map(value.borrow_mut::<Option<Value>>()?, |value| {
3405 match (index, value) {
3406 (0, Some(value)) => Some(value),
3407 _ => None,
3408 }
3409 });
3410
3411 if let Ok(value) = result {
3412 return Ok(Some(value));
3413 }
3414
3415 Err(VmErrorKind::MissingIndexInteger {
3416 target: TypeInfo::any::<Option<Value>>(),
3417 index: VmIntegerRepr::from(index),
3418 })
3419 }
3420 GeneratorState::HASH => {
3421 let result = BorrowMut::try_map(value.borrow_mut::<GeneratorState>()?, |value| {
3422 match (index, value) {
3423 (0, GeneratorState::Yielded(value)) => Some(value),
3424 (0, GeneratorState::Complete(value)) => Some(value),
3425 _ => None,
3426 }
3427 });
3428
3429 if let Ok(value) = result {
3430 return Ok(Some(value));
3431 }
3432
3433 Err(VmErrorKind::MissingIndexInteger {
3434 target: TypeInfo::any::<GeneratorState>(),
3435 index: VmIntegerRepr::from(index),
3436 })
3437 }
3438 runtime::Vec::HASH => {
3439 let vec = value.borrow_mut::<runtime::Vec>()?;
3440 let result = BorrowMut::try_map(vec, |vec| vec.get_mut(index));
3441
3442 if let Ok(value) = result {
3443 return Ok(Some(value));
3444 }
3445
3446 Err(VmErrorKind::MissingIndexInteger {
3447 target: TypeInfo::any::<runtime::Vec>(),
3448 index: VmIntegerRepr::from(index),
3449 })
3450 }
3451 runtime::OwnedTuple::HASH => {
3452 let tuple = value.borrow_mut::<runtime::OwnedTuple>()?;
3453 let result = BorrowMut::try_map(tuple, |tuple| tuple.get_mut(index));
3454
3455 if let Ok(value) = result {
3456 return Ok(Some(value));
3457 }
3458
3459 Err(VmErrorKind::MissingIndexInteger {
3460 target: TypeInfo::any::<runtime::OwnedTuple>(),
3461 index: VmIntegerRepr::from(index),
3462 })
3463 }
3464 _ => Ok(None),
3465 },
3466 _ => Ok(None),
3467 }
3468}
3469
3470fn try_object_like_index_get_mut<'a>(
3472 target: &'a Value,
3473 field: &str,
3474) -> Result<Option<BorrowMut<'a, Value>>, VmErrorKind> {
3475 match target.as_ref() {
3476 Repr::Inline(value) => Err(VmErrorKind::MissingField {
3477 target: value.type_info(),
3478 field: field.try_to_owned()?,
3479 }),
3480 Repr::Dynamic(data) if matches!(data.rtti().kind, RttiKind::Struct) => {
3481 Ok(data.get_field_mut(field)?)
3482 }
3483 Repr::Dynamic(data) => Err(VmErrorKind::MissingField {
3484 target: data.type_info(),
3485 field: field.try_to_owned()?,
3486 }),
3487 Repr::Any(value) => match value.type_hash() {
3488 Object::HASH => {
3489 let object = value.borrow_mut::<Object>()?;
3490
3491 let Ok(value) = BorrowMut::try_map(object, |object| object.get_mut(field)) else {
3492 return Err(VmErrorKind::MissingField {
3493 target: value.type_info(),
3494 field: field.try_to_owned()?,
3495 });
3496 };
3497
3498 Ok(Some(value))
3499 }
3500 _ => Ok(None),
3501 },
3502 }
3503}