rune/runtime/
vm.rs

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