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