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