Skip to main content

rune/runtime/
memory.rs

1use core::array;
2use core::convert::Infallible;
3use core::fmt;
4use core::mem::replace;
5use core::slice;
6#[cfg(feature = "cli")]
7use core::slice::SliceIndex;
8
9use crate::alloc::alloc::Global;
10use crate::alloc::prelude::*;
11use crate::alloc::{self, Vec};
12
13use super::{Address, Dismantle, Handover, IntoOutput, Output, Value, VmErrorKind, Worklist};
14
15// This is a bit tricky. We know that `Value::empty()` is `Sync` but we can't
16// convince Rust that is the case.
17struct AssertSync<T>(T);
18unsafe impl<T> Sync for AssertSync<T> {}
19
20static EMPTY: AssertSync<Value> = AssertSync(Value::empty());
21
22/// An error raised when accessing an address on the stack.
23#[derive(Debug)]
24#[cfg_attr(test, derive(PartialEq))]
25#[non_exhaustive]
26pub struct StackError {
27    addr: Address,
28}
29
30impl From<Infallible> for StackError {
31    #[inline]
32    fn from(value: Infallible) -> Self {
33        match value {}
34    }
35}
36
37impl fmt::Display for StackError {
38    #[inline]
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "Tried to access out-of-bounds stack entry {}", self.addr)
41    }
42}
43
44impl core::error::Error for StackError {}
45
46/// An error raised when accessing a slice on the stack.
47#[derive(Debug)]
48#[cfg_attr(test, derive(PartialEq))]
49#[non_exhaustive]
50pub struct SliceError {
51    addr: Address,
52    len: usize,
53    stack: usize,
54}
55
56impl fmt::Display for SliceError {
57    #[inline]
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(
60            f,
61            "Tried to access out-of-bounds stack slice {}-{} in 0-{}",
62            self.addr,
63            self.addr.offset() + self.len,
64            self.stack
65        )
66    }
67}
68
69impl core::error::Error for SliceError {}
70
71pub(crate) enum Pair<'a> {
72    Same(&'a mut Value),
73    Pair(&'a mut Value, &'a Value),
74}
75
76/// An error produced by a call to `Memory::store`.
77pub struct StoreError<E> {
78    kind: StoreErrorKind<E>,
79}
80
81impl<E> StoreError<E> {
82    #[inline]
83    pub(crate) fn into_kind(self) -> StoreErrorKind<E> {
84        self.kind
85    }
86}
87
88pub(crate) enum StoreErrorKind<E> {
89    Stack(StackError),
90    Error(E),
91    /// Taking the value which was replaced apart ran out of memory.
92    Alloc(alloc::Error),
93}
94
95impl<E> From<StackError> for StoreError<E> {
96    #[inline]
97    fn from(error: StackError) -> Self {
98        Self {
99            kind: StoreErrorKind::Stack(error),
100        }
101    }
102}
103
104impl<E> From<alloc::Error> for StoreError<E> {
105    #[inline]
106    fn from(error: alloc::Error) -> Self {
107        Self {
108            kind: StoreErrorKind::Alloc(error),
109        }
110    }
111}
112
113impl<E> StoreError<E> {
114    #[inline]
115    fn error(error: E) -> Self {
116        Self {
117            kind: StoreErrorKind::Error(error),
118        }
119    }
120}
121
122/// Memory access.
123pub trait Memory {
124    /// Get the slice at the given address with the given length.
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// use rune::runtime::{Address, Memory, Output, VmError};
130    ///
131    /// fn sum(memory: &mut dyn Memory, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
132    ///     let mut number = 0;
133    ///
134    ///     for value in memory.slice_at(addr, args)? {
135    ///         number += value.as_integer::<i64>()?;
136    ///     }
137    ///
138    ///     memory.store(out, number)?;
139    ///     Ok(())
140    /// }
141    /// ```
142    fn slice_at(&self, addr: Address, len: usize) -> Result<&[Value], SliceError>;
143
144    /// Access the given slice mutably.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use rune::runtime::{Address, Memory, Output, Value, VmError};
150    ///
151    /// fn drop_values(memory: &mut dyn Memory, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
152    ///     for value in memory.slice_at_mut(addr, args)? {
153    ///         *value = Value::empty();
154    ///     }
155    ///
156    ///     memory.store(out, ())?;
157    ///     Ok(())
158    /// }
159    /// ```
160    fn slice_at_mut(&mut self, addr: Address, len: usize) -> Result<&mut [Value], SliceError>;
161
162    /// Get a value mutable at the given index from the stack bottom.
163    ///
164    /// # Examples
165    ///
166    /// ```
167    /// use rune::runtime::{Address, Memory, Output, VmError};
168    ///
169    /// fn add_one(memory: &mut dyn Memory, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
170    ///     let mut value = memory.at_mut(addr)?;
171    ///     let number = value.as_integer::<i64>()?;
172    ///     *value = rune::to_value(number + 1)?;
173    ///     memory.store(out, ())?;
174    ///     Ok(())
175    /// }
176    /// ```
177    fn at_mut(&mut self, addr: Address) -> Result<&mut Value, StackError>;
178
179    /// Get the slice at the given address with the given static length.
180    fn array_at<const N: usize>(&self, addr: Address) -> Result<[&Value; N], SliceError>
181    where
182        Self: Sized,
183    {
184        let slice = self.slice_at(addr, N)?;
185        Ok(array::from_fn(|i| &slice[i]))
186    }
187}
188
189impl dyn Memory + '_ {
190    /// Write output using the provided [`IntoOutput`] implementation onto the
191    /// stack.
192    ///
193    /// The [`IntoOutput`] trait primarily allows for deferring a computation
194    /// since it's implemented by [`FnOnce`]. However, you must take care that
195    /// any side effects calling a function may have are executed outside of the
196    /// call to `store`. Like if the function would error.
197    ///
198    /// # Examples
199    ///
200    /// ```
201    /// use rune::runtime::{Output, Memory, ToValue, VmError, Address};
202    /// use rune::vm_try;
203    ///
204    /// fn sum(memory: &mut dyn Memory, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
205    ///     let mut number = 0;
206    ///
207    ///     for value in memory.slice_at(addr, args)? {
208    ///         number += value.as_integer::<i64>()?;
209    ///     }
210    ///
211    ///     memory.store(out, number)?;
212    ///     Ok(())
213    /// }
214    /// ```
215    #[inline(always)]
216    pub fn store<O>(&mut self, out: Output, o: O) -> Result<(), StoreError<O::Error>>
217    where
218        O: IntoOutput,
219    {
220        if let Some(addr) = out.as_addr() {
221            let value = o.into_output().map_err(StoreError::error)?;
222
223            // A native function has no worklist of its own to take the value
224            // which is replaced apart over, so it gets one which is empty. It
225            // costs nothing until it is handed a value made of other values.
226            let mut worklist = Worklist::new();
227
228            // The value which is replaced is taken apart rather than left to
229            // its destructor, so that the walk does not descend into it.
230            worklist.replace(self.at_mut(addr)?, value);
231        }
232
233        Ok(())
234    }
235}
236
237impl<M> Memory for &mut M
238where
239    M: Memory + ?Sized,
240{
241    #[inline]
242    fn slice_at(&self, addr: Address, len: usize) -> Result<&[Value], SliceError> {
243        (**self).slice_at(addr, len)
244    }
245
246    #[inline]
247    fn slice_at_mut(&mut self, addr: Address, len: usize) -> Result<&mut [Value], SliceError> {
248        (**self).slice_at_mut(addr, len)
249    }
250
251    #[inline]
252    fn at_mut(&mut self, addr: Address) -> Result<&mut Value, StackError> {
253        (**self).at_mut(addr)
254    }
255}
256
257impl<const N: usize> Memory for [Value; N] {
258    fn slice_at(&self, addr: Address, len: usize) -> Result<&[Value], SliceError> {
259        if len == 0 {
260            return Ok(&[]);
261        }
262
263        let start = addr.offset();
264
265        let Some(values) = start.checked_add(len).and_then(|end| self.get(start..end)) else {
266            return Err(SliceError {
267                addr,
268                len,
269                stack: N,
270            });
271        };
272
273        Ok(values)
274    }
275
276    fn slice_at_mut(&mut self, addr: Address, len: usize) -> Result<&mut [Value], SliceError> {
277        if len == 0 {
278            return Ok(&mut []);
279        }
280
281        let start = addr.offset();
282
283        let Some(values) = start
284            .checked_add(len)
285            .and_then(|end| self.get_mut(start..end))
286        else {
287            return Err(SliceError {
288                addr,
289                len,
290                stack: N,
291            });
292        };
293
294        Ok(values)
295    }
296
297    #[inline]
298    fn at_mut(&mut self, addr: Address) -> Result<&mut Value, StackError> {
299        let Some(value) = self.get_mut(addr.offset()) else {
300            return Err(StackError { addr });
301        };
302
303        Ok(value)
304    }
305}
306
307/// The stack of the virtual machine, where all values are stored.
308#[derive(Default, Debug)]
309pub struct Stack {
310    /// The current stack of values.
311    stack: Vec<Value>,
312    /// The top of the current stack frame.
313    ///
314    /// It is not possible to interact with values below this stack frame.
315    top: usize,
316}
317
318impl Stack {
319    /// Construct a new stack.
320    #[inline]
321    pub(crate) const fn new() -> Self {
322        Self {
323            stack: Vec::new(),
324            top: 0,
325        }
326    }
327
328    /// Access the value at the given frame offset.
329    ///
330    /// # Examples
331    ///
332    /// ```
333    /// use rune::vm_try;
334    /// use rune::Module;
335    /// use rune::runtime::{Output, Stack, VmError, Address};
336    ///
337    /// fn add_one(memory: &mut Stack, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
338    ///     let value = memory.at(addr).as_integer::<i64>()?;
339    ///     memory.store(out, value + 1);
340    ///     Ok(())
341    /// }
342    /// ```
343    #[inline(always)]
344    pub fn at(&self, addr: Address) -> &Value {
345        let n = self.top.wrapping_add(addr.offset());
346        self.stack.get(n).unwrap_or(&EMPTY.0)
347    }
348
349    /// Get a value mutable at the given index from the stack bottom.
350    ///
351    /// # Examples
352    ///
353    /// ```
354    /// use rune::vm_try;
355    /// use rune::Module;
356    /// use rune::runtime::{Output, Stack, VmError, Address};
357    ///
358    /// fn add_one(memory: &mut Stack, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
359    ///     let mut value = memory.at_mut(addr)?;
360    ///     let number = value.as_integer::<i64>()?;
361    ///     *value = rune::to_value(number + 1)?;
362    ///     memory.store(out, ())?;
363    ///     Ok(())
364    /// }
365    /// ```
366    #[inline]
367    pub fn at_mut(&mut self, addr: Address) -> Result<&mut Value, StackError> {
368        let n = self.top.wrapping_add(addr.offset());
369        self.stack.get_mut(n).ok_or(StackError { addr })
370    }
371
372    /// Get the slice at the given address with the given length.
373    ///
374    /// # Examples
375    ///
376    /// ```
377    /// use rune::vm_try;
378    /// use rune::Module;
379    /// use rune::runtime::{Output, Stack, ToValue, VmError, Address};
380    ///
381    /// fn sum(memory: &mut Stack, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
382    ///     let mut number = 0;
383    ///
384    ///     for value in memory.slice_at(addr, args)? {
385    ///         number += value.as_integer::<i64>()?;
386    ///     }
387    ///
388    ///     memory.store(out, number)?;
389    ///     Ok(())
390    /// }
391    /// ```
392    #[inline]
393    pub fn slice_at(&self, addr: Address, len: usize) -> Result<&[Value], SliceError> {
394        let stack_len = self.stack.len();
395
396        if let Some(slice) = inner_slice_at(&self.stack, self.top, addr, len) {
397            return Ok(slice);
398        }
399
400        Err(slice_error(stack_len, self.top, addr, len))
401    }
402
403    /// Get the mutable slice at the given address with the given length.
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// use rune::vm_try;
409    /// use rune::Module;
410    /// use rune::runtime::{Output, Memory, VmError, Address};
411    ///
412    /// fn sum(memory: &mut dyn Memory, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
413    ///     for value in memory.slice_at_mut(addr, args)? {
414    ///         let number = value.as_integer::<i64>()?;
415    ///         *value = rune::to_value(number + 1)?;
416    ///     }
417    ///
418    ///     memory.store(out, ())?;
419    ///     Ok(())
420    /// }
421    /// ```
422    #[inline]
423    pub fn slice_at_mut(&mut self, addr: Address, len: usize) -> Result<&mut [Value], SliceError> {
424        let stack_len = self.stack.len();
425
426        if let Some(slice) = inner_slice_at_mut(&mut self.stack, self.top, addr, len) {
427            return Ok(slice);
428        }
429
430        Err(slice_error(stack_len, self.top, addr, len))
431    }
432
433    /// Write output using the provided [`IntoOutput`] implementation onto the
434    /// stack.
435    ///
436    /// The [`IntoOutput`] trait primarily allows for deferring a computation
437    /// since it's implemented by [`FnOnce`]. However, you must take care that
438    /// any side effects calling a function may have are executed outside of the
439    /// call to `store`. Like if the function would error.
440    ///
441    /// # Examples
442    ///
443    /// ```
444    /// use rune::runtime::{Output, Memory, ToValue, VmError, Address};
445    /// use rune::vm_try;
446    ///
447    /// fn sum(memory: &mut dyn Memory, addr: Address, args: usize, out: Output) -> Result<(), VmError> {
448    ///     let mut number = 0;
449    ///
450    ///     for value in memory.slice_at(addr, args)? {
451    ///         number += value.as_integer::<i64>()?;
452    ///     }
453    ///
454    ///     memory.store(out, number)?;
455    ///     Ok(())
456    /// }
457    /// ```
458    #[inline(always)]
459    pub fn store<O>(&mut self, out: Output, o: O) -> Result<(), StoreError<O::Error>>
460    where
461        O: IntoOutput,
462    {
463        // Whoever writes to a stack without a machine behind them has no
464        // worklist to take the value which is replaced apart over, so they get
465        // one which is empty. It costs nothing until it is handed a value made
466        // of other values.
467        self.store_with(out, o, &mut Worklist::new())
468    }
469
470    /// Write output using the provided [`IntoOutput`] implementation onto the
471    /// stack, taking the value which was there apart over the given worklist.
472    ///
473    /// The worklist is handed in by the machine, which keeps one for as long as
474    /// it lives, so that whatever memory taking values apart needs is grown
475    /// once rather than for every value which is written over.
476    #[inline(always)]
477    pub(crate) fn store_with<O>(
478        &mut self,
479        out: Output,
480        o: O,
481        work: &mut Worklist,
482    ) -> Result<(), StoreError<O::Error>>
483    where
484        O: IntoOutput,
485    {
486        if let Some(addr) = out.as_addr() {
487            let value = o.into_output().map_err(StoreError::error)?;
488
489            // The value which is replaced is taken apart rather than left to
490            // its destructor, so that the machine does not descend into it.
491            work.replace(self.at_mut(addr)?, value);
492        }
493
494        Ok(())
495    }
496
497    /// The current top address of the stack.
498    #[inline]
499    pub(crate) const fn addr(&self) -> Address {
500        Address::new(self.stack.len().saturating_sub(self.top))
501    }
502
503    /// Try to resize the stack with space for the given size.
504    #[inline]
505    pub(crate) fn resize(&mut self, size: usize) -> alloc::Result<()> {
506        if size == 0 {
507            return Ok(());
508        }
509
510        self.stack.try_resize_with(self.top + size, Value::empty)?;
511        Ok(())
512    }
513
514    /// Construct a new stack with the given capacity pre-allocated.
515    #[inline]
516    pub(crate) fn with_capacity(capacity: usize) -> alloc::Result<Self> {
517        Ok(Self {
518            stack: Vec::try_with_capacity(capacity)?,
519            top: 0,
520        })
521    }
522
523    /// Perform a raw access over the stack.
524    ///
525    /// This ignores [top] and will just check that the given slice
526    /// index is within range.
527    ///
528    /// [top]: Self::top()
529    #[cfg(feature = "cli")]
530    #[inline]
531    pub(crate) fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[Value]>>::Output>
532    where
533        I: SliceIndex<[Value]>,
534    {
535        self.stack.get(index)
536    }
537
538    /// Push a value onto the stack.
539    #[inline]
540    pub(crate) fn push<T>(&mut self, value: T) -> alloc::Result<()>
541    where
542        T: TryInto<Value, Error: Into<alloc::Error>>,
543    {
544        self.stack.try_push(value.try_into().map_err(Into::into)?)?;
545        Ok(())
546    }
547
548    /// Truncate the stack at the given address, taking the values which are
549    /// discarded apart rather than leaving them to their destructors.
550    ///
551    /// This is what the machine uses, so that the values share the worklist it
552    /// keeps - see [`Worklist::dismantle`].
553    #[inline]
554    pub(crate) fn dismantle_to(&mut self, addr: Address, work: &mut Worklist) {
555        let Some(len) = self.top.checked_add(addr.offset()) else {
556            return;
557        };
558
559        if let Some(values) = self.stack.get_mut(len..) {
560            work.dismantle_all(values.iter_mut());
561        }
562
563        self.stack.truncate(len);
564    }
565
566    /// Open a new call frame at the top of the stack holding the values which
567    /// `other` was working over.
568    ///
569    /// This is what splicing an unstarted execution into the machine which
570    /// awaits it uses - the values the awaited machine holds become the
571    /// arguments of an ordinary call frame here. Returns the old stack top, for
572    /// the [`CallFrame`] the caller pushes.
573    ///
574    /// [`CallFrame`]: crate::runtime::CallFrame
575    #[inline]
576    pub(crate) fn push_frame_from(&mut self, other: &mut Stack) -> alloc::Result<usize> {
577        let old_len = self.stack.len();
578        self.stack.try_reserve(other.stack.len())?;
579
580        for value in other.stack.drain(..) {
581            self.stack.try_push(value)?;
582        }
583
584        other.top = 0;
585        Ok(replace(&mut self.top, old_len))
586    }
587
588    /// Drain the current stack down to the current stack bottom.
589    #[inline]
590    pub(crate) fn drain(&mut self) -> impl DoubleEndedIterator<Item = Value> + '_ {
591        self.stack.drain(self.top..)
592    }
593
594    /// Clear the current stack.
595    #[inline]
596    pub(crate) fn clear(&mut self) {
597        self.stack.clear();
598        self.top = 0;
599    }
600
601    /// Clear the current stack, taking the values it held apart rather than
602    /// leaving them to their destructors.
603    ///
604    /// This is what the machine uses - see [`Worklist::dismantle`].
605    #[inline]
606    pub(crate) fn dismantle_clear(&mut self, work: &mut Worklist) {
607        work.dismantle_all(self.stack.iter_mut());
608        self.stack.clear();
609        self.top = 0;
610    }
611
612    /// Get the offset that corresponds to the bottom of the stack right now.
613    ///
614    /// The stack is partitioned into call frames, and once we enter a call
615    /// frame the bottom of the stack corresponds to the bottom of the current
616    /// call frame.
617    #[cfg_attr(not(feature = "tracing"), allow(unused))]
618    #[inline]
619    pub(crate) const fn top(&self) -> usize {
620        self.top
621    }
622
623    /// Get the length of the stack.
624    #[cfg_attr(not(feature = "tracing"), allow(unused))]
625    #[inline]
626    pub(crate) const fn len(&self) -> usize {
627        self.stack.len()
628    }
629
630    /// Swap the value at position a with the value at position b.
631    pub(crate) fn swap(&mut self, a: Address, b: Address) -> Result<(), StackError> {
632        if a == b {
633            return Ok(());
634        }
635
636        let a = self
637            .top
638            .checked_add(a.offset())
639            .filter(|&n| n < self.stack.len())
640            .ok_or(StackError { addr: a })?;
641
642        let b = self
643            .top
644            .checked_add(b.offset())
645            .filter(|&n| n < self.stack.len())
646            .ok_or(StackError { addr: b })?;
647
648        self.stack.swap(a, b);
649        Ok(())
650    }
651
652    /// Modify stack top by subtracting the given count from it while checking
653    /// that it is in bounds of the stack.
654    ///
655    /// This is used internally when returning from a call frame.
656    ///
657    /// Returns the old stack top.
658    #[tracing::instrument(skip_all)]
659    pub(crate) fn swap_top(&mut self, addr: Address, len: usize) -> Result<usize, VmErrorKind> {
660        let old_len = self.stack.len();
661
662        if len == 0 {
663            return Ok(replace(&mut self.top, old_len));
664        }
665
666        let Some(start) = self.top.checked_add(addr.offset()) else {
667            return Err(VmErrorKind::StackError {
668                error: StackError { addr },
669            });
670        };
671
672        let Some(new_len) = old_len.checked_add(len) else {
673            return Err(VmErrorKind::StackError {
674                error: StackError { addr },
675            });
676        };
677
678        if old_len < start + len {
679            return Err(VmErrorKind::StackError {
680                error: StackError { addr },
681            });
682        }
683
684        self.stack.try_reserve(len)?;
685
686        // SAFETY: We've ensured that the collection has space for the new
687        // values. It is also guaranteed to be non-overlapping.
688        unsafe {
689            let ptr = self.stack.as_mut_ptr();
690            let from = slice::from_raw_parts_mut(ptr.add(start), len);
691
692            for (value, n) in from.iter_mut().zip(old_len..) {
693                ptr.add(n).write(replace(value, Value::empty()));
694            }
695
696            self.stack.set_len(new_len);
697        }
698
699        Ok(replace(&mut self.top, old_len))
700    }
701
702    /// Pop the current stack top and modify it to a different one.
703    ///
704    /// The values which the call frame being left was working over are taken
705    /// apart rather than left to their destructors, since this is the machine
706    /// working - see [`Worklist::dismantle`].
707    #[inline]
708    #[tracing::instrument(skip_all)]
709    pub(crate) fn pop_stack_top(&mut self, top: usize, work: &mut Worklist) {
710        tracing::trace!(stack = self.stack.len(), self.top);
711
712        if let Some(values) = self.stack.get_mut(self.top..) {
713            work.dismantle_all(values.iter_mut());
714        }
715
716        self.stack.truncate(self.top);
717        self.top = top;
718    }
719
720    /// Copy the value at the given address to the output.
721    ///
722    /// The value which is written over is taken apart rather than left to its
723    /// destructor, since this is the machine working - see
724    /// [`Worklist::dismantle`].
725    pub(crate) fn copy(
726        &mut self,
727        from: Address,
728        out: Output,
729        work: &mut Worklist,
730    ) -> Result<(), StoreError<Infallible>> {
731        let Some(to) = out.as_addr() else {
732            return Ok(());
733        };
734
735        if from == to {
736            return Ok(());
737        }
738
739        let from = self.top.wrapping_add(from.offset());
740        let to = self.top.wrapping_add(to.offset());
741
742        if from.max(to) >= self.stack.len() {
743            return Err(StackError {
744                addr: Address::new(from.max(to).wrapping_sub(self.top)),
745            }
746            .into());
747        }
748
749        // SAFETY: We've checked that both addresses are in-bound and different
750        // just above.
751        let old = unsafe {
752            let ptr = self.stack.as_mut_ptr();
753            let value = (*ptr.add(from).cast_const()).clone();
754            replace(&mut *ptr.add(to), value)
755        };
756
757        work.dismantle(old);
758        Ok(())
759    }
760
761    /// Get a pair of addresses.
762    pub(crate) fn pair(&mut self, a: Address, b: Address) -> Result<Pair<'_>, StackError> {
763        if a == b {
764            return Ok(Pair::Same(self.at_mut(a)?));
765        }
766
767        let a = self
768            .top
769            .checked_add(a.offset())
770            .filter(|&n| n < self.stack.len())
771            .ok_or(StackError { addr: a })?;
772
773        let b = self
774            .top
775            .checked_add(b.offset())
776            .filter(|&n| n < self.stack.len())
777            .ok_or(StackError { addr: b })?;
778
779        let pair = unsafe {
780            let ptr = self.stack.as_mut_ptr();
781            Pair::Pair(&mut *ptr.add(a), &*ptr.add(b).cast_const())
782        };
783
784        Ok(pair)
785    }
786}
787
788/// A stack holds the values a machine is working over, so a machine which is
789/// suspended - a generator or a stream - holds a graph of values through it.
790impl Dismantle for Stack {
791    fn dismantle(&mut self, out: &mut Handover<'_>) {
792        // The values are being taken out of the machine, so which of them made
793        // up its current call frame no longer means anything.
794        self.top = 0;
795
796        // Every value is handed over in one pass over the stack, and what is
797        // left of it is dropped as soon as this returns.
798        for value in self.stack.drain(..) {
799            out.push(value);
800        }
801    }
802}
803
804impl Memory for Stack {
805    #[inline]
806    fn slice_at(&self, addr: Address, len: usize) -> Result<&[Value], SliceError> {
807        Stack::slice_at(self, addr, len)
808    }
809
810    #[inline]
811    fn slice_at_mut(&mut self, addr: Address, len: usize) -> Result<&mut [Value], SliceError> {
812        Stack::slice_at_mut(self, addr, len)
813    }
814
815    #[inline]
816    fn at_mut(&mut self, addr: Address) -> Result<&mut Value, StackError> {
817        Stack::at_mut(self, addr)
818    }
819}
820
821#[inline(always)]
822fn inner_slice_at(values: &[Value], top: usize, addr: Address, len: usize) -> Option<&[Value]> {
823    if len == 0 {
824        return Some(&[]);
825    }
826
827    let start = top.checked_add(addr.offset())?;
828    let end = start.checked_add(len)?;
829    values.get(start..end)
830}
831
832#[inline(always)]
833fn inner_slice_at_mut(
834    values: &mut [Value],
835    top: usize,
836    addr: Address,
837    len: usize,
838) -> Option<&mut [Value]> {
839    if len == 0 {
840        return Some(&mut []);
841    }
842
843    let start = top.checked_add(addr.offset())?;
844    let end = start.checked_add(len)?;
845    values.get_mut(start..end)
846}
847
848#[inline(always)]
849fn slice_error(stack: usize, bottom: usize, addr: Address, len: usize) -> SliceError {
850    SliceError {
851        addr,
852        len,
853        stack: stack.saturating_sub(bottom),
854    }
855}
856
857impl TryClone for Stack {
858    #[inline]
859    fn try_clone(&self) -> alloc::Result<Self> {
860        Ok(Self {
861            stack: self.stack.try_clone()?,
862            top: self.top,
863        })
864    }
865}
866
867impl TryFromIteratorIn<Value, Global> for Stack {
868    #[inline]
869    fn try_from_iter_in<T: IntoIterator<Item = Value>>(
870        iter: T,
871        alloc: Global,
872    ) -> alloc::Result<Self> {
873        Ok(Self {
874            stack: iter.into_iter().try_collect_in(alloc)?,
875            top: 0,
876        })
877    }
878}
879
880impl From<Vec<Value>> for Stack {
881    #[inline]
882    fn from(stack: Vec<Value>) -> Self {
883        Self { stack, top: 0 }
884    }
885}