Skip to main content

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