rune/runtime/
vm.rs

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