revm_context_interface/
local.rs

1//! Local context trait [`LocalContextTr`] and related types.
2use core::{
3    cell::{Ref, RefCell},
4    ops::Range,
5};
6use std::{boxed::Box, rc::Rc, vec::Vec};
7
8/// Non-empty, item-pooling Vec.
9#[derive(Debug, Clone)]
10pub struct FrameStack<T> {
11    stack: Vec<Box<T>>,
12    index: Option<usize>,
13}
14
15impl<T> Default for FrameStack<T> {
16    fn default() -> Self {
17        Self::new()
18    }
19}
20
21impl<T> FrameStack<T> {
22    /// Creates a new, empty stack. It must be initialized with init before use.
23    #[inline]
24    pub fn new() -> Self {
25        Self {
26            stack: Vec::with_capacity(1025),
27            index: None,
28        }
29    }
30
31    /// Initializes the stack with a single item.
32    #[inline]
33    pub fn start_init(&mut self) -> OutFrame<'_, T> {
34        self.index = None;
35        if self.stack.is_empty() {
36            self.stack.reserve(1);
37        }
38        self.out_frame_at(0)
39    }
40
41    /// Finishes initialization.
42    #[inline]
43    pub fn end_init(&mut self, token: FrameToken) {
44        token.assert();
45        if self.stack.is_empty() {
46            unsafe { self.stack.set_len(1) };
47        }
48        self.index = Some(0);
49    }
50
51    /// Returns the current index of the stack.
52    #[inline]
53    pub fn index(&self) -> Option<usize> {
54        self.index
55    }
56
57    /// Increments the index.
58    #[inline]
59    pub fn push(&mut self, token: FrameToken) {
60        token.assert();
61        let index = self.index.as_mut().unwrap();
62        if *index + 1 == self.stack.len() {
63            unsafe { self.stack.set_len(self.stack.len() + 1) };
64            self.stack.reserve(1);
65        }
66        *index += 1;
67    }
68
69    /// Clears the stack by setting the index to 0.
70    /// It does not destroy the stack.
71    #[inline]
72    pub fn clear(&mut self) {
73        self.index = None;
74    }
75
76    /// Decrements the index.
77    #[inline]
78    pub fn pop(&mut self) {
79        self.index = self.index.unwrap_or(0).checked_sub(1);
80    }
81
82    /// Returns the current item.
83    #[inline]
84    pub fn get(&mut self) -> &mut T {
85        debug_assert!(self.stack.capacity() > self.index.unwrap() + 1);
86        unsafe { &mut *self.stack.as_mut_ptr().add(self.index.unwrap()) }
87    }
88
89    /// Get next uninitialized item.
90    #[inline]
91    pub fn get_next(&mut self) -> OutFrame<'_, T> {
92        self.out_frame_at(self.index.unwrap() + 1)
93    }
94
95    fn out_frame_at(&mut self, idx: usize) -> OutFrame<'_, T> {
96        unsafe {
97            OutFrame::new_maybe_uninit(self.stack.as_mut_ptr().add(idx), idx < self.stack.len())
98        }
99    }
100}
101
102/// A potentially initialized frame. Used when initializing a new frame in the main loop.
103#[allow(missing_debug_implementations)]
104pub struct OutFrame<'a, T> {
105    ptr: *mut Box<T>,
106    init: bool,
107    lt: core::marker::PhantomData<&'a mut T>,
108}
109
110impl<'a, T> OutFrame<'a, T> {
111    /// Creates a new initialized `OutFrame` from a mutable reference to a type `T`.
112    pub fn new_init(slot: &'a mut Box<T>) -> Self {
113        unsafe { Self::new_maybe_uninit(slot, true) }
114    }
115
116    /// Creates a new uninitialized `OutFrame` from a mutable reference to a `MaybeUninit<T>`.
117    pub fn new_uninit(slot: &'a mut core::mem::MaybeUninit<Box<T>>) -> Self {
118        unsafe { Self::new_maybe_uninit(slot.as_mut_ptr(), false) }
119    }
120
121    /// Creates a new `OutFrame` from a raw pointer to a type `T`.
122    ///
123    /// # Safety
124    ///
125    /// This method is unsafe because it assumes that the pointer is valid and points to a location
126    /// where a type `T` can be stored. It also assumes that the `init` flag correctly reflects whether
127    /// the type `T` has been initialized or not.
128    pub unsafe fn new_maybe_uninit(ptr: *mut Box<T>, init: bool) -> Self {
129        Self {
130            ptr,
131            init,
132            lt: Default::default(),
133        }
134    }
135
136    /// Returns a mutable reference to the type `T`, initializing it if it hasn't been initialized yet.
137    pub fn get(&mut self, f: impl FnOnce() -> T) -> &mut T {
138        if !self.init {
139            self.do_init(f);
140        }
141        unsafe { &mut *self.ptr }
142    }
143
144    #[cold]
145    fn do_init(&mut self, f: impl FnOnce() -> T) {
146        unsafe {
147            self.init = true;
148            self.ptr.write(Box::new(f()));
149        }
150    }
151
152    /// Returns a mutable reference to the type `T`, without checking if it has been initialized.
153    ///
154    /// # Safety
155    ///
156    /// This method is unsafe because it assumes that the `OutFrame` has been initialized before use.
157    pub unsafe fn get_unchecked(&mut self) -> &mut T {
158        debug_assert!(self.init, "OutFrame must be initialized before use");
159        unsafe { &mut *self.ptr }
160    }
161
162    /// Consumes the `OutFrame`, returning a `FrameToken` that indicates the frame has been initialized.
163    pub fn consume(self) -> FrameToken {
164        FrameToken(self.init)
165    }
166}
167
168/// Used to guarantee that a frame is initialized before use.
169#[allow(missing_debug_implementations)]
170pub struct FrameToken(bool);
171
172impl FrameToken {
173    /// Asserts that the frame token is initialized.
174    #[cfg_attr(debug_assertions, track_caller)]
175    pub fn assert(self) {
176        assert!(self.0, "FrameToken must be initialized before use");
177    }
178}
179
180/// Local context used for caching initcode from Initcode transactions.
181pub trait LocalContextTr {
182    /// Interpreter shared memory buffer. A reused memory buffer for calls.
183    fn shared_memory_buffer(&self) -> &Rc<RefCell<Vec<u8>>>;
184
185    /// Slice of the shared memory buffer returns None if range is not valid or buffer can't be borrowed.
186    fn shared_memory_buffer_slice(&self, range: Range<usize>) -> Option<Ref<'_, [u8]>> {
187        let buffer = self.shared_memory_buffer();
188        buffer.borrow().get(range.clone())?;
189        Some(Ref::map(buffer.borrow(), |b| {
190            b.get(range).unwrap_or_default()
191        }))
192    }
193
194    /// Clear the local context.
195    fn clear(&mut self);
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn frame_stack() {
204        let mut stack = FrameStack::new();
205        let mut frame = stack.start_init();
206        frame.get(|| 1);
207        let token = frame.consume();
208        stack.end_init(token);
209
210        assert_eq!(stack.index(), Some(0));
211        assert_eq!(stack.stack.len(), 1);
212
213        let a = stack.get();
214        assert_eq!(a, &mut 1);
215        let mut b = stack.get_next();
216        assert!(!b.init);
217        assert_eq!(b.get(|| 2), &mut 2);
218        let token = b.consume(); // TODO: remove
219        stack.push(token);
220
221        assert_eq!(stack.index(), Some(1));
222        assert_eq!(stack.stack.len(), 2);
223        let a = stack.get();
224        assert_eq!(a, &mut 2);
225        let b = stack.get_next();
226        assert!(!b.init);
227
228        stack.pop();
229
230        assert_eq!(stack.index(), Some(0));
231        assert_eq!(stack.stack.len(), 2);
232        let a = stack.get();
233        assert_eq!(a, &mut 1);
234        let mut b = stack.get_next();
235        assert!(b.init);
236        assert_eq!(unsafe { b.get_unchecked() }, &mut 2);
237    }
238}