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