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