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}