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::{rc::Rc, string::String, vec::Vec};
7
8/// Non-empty, item-pooling Vec.
9#[derive(Debug, Clone)]
10pub struct FrameStack<T> {
11    stack: Vec<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: Default> FrameStack<T> {
22    /// Creates a new stack with preallocated items by calling `T::default()` `len` times.
23    /// Index will still be `None` until `end_init` is called.
24    pub fn new_prealloc(len: usize) -> Self {
25        let mut stack = Vec::with_capacity(len);
26        for _ in 0..len {
27            stack.push(T::default());
28        }
29        Self { stack, index: None }
30    }
31}
32
33impl<T> FrameStack<T> {
34    /// Creates a new, empty stack. It must be initialized with init before use.
35    pub fn new() -> Self {
36        // Init N amount of frames to allocate the stack.
37        Self {
38            stack: Vec::with_capacity(8),
39            index: None,
40        }
41    }
42
43    /// Initializes the stack with a single item.
44    #[inline]
45    pub fn start_init(&mut self) -> OutFrame<'_, T> {
46        self.index = None;
47        if self.stack.is_empty() {
48            self.stack.reserve(8);
49        }
50        self.out_frame_at(0)
51    }
52
53    /// Finishes initialization.
54    ///
55    /// # Safety
56    ///
57    /// This method is unsafe because it assumes that the `token` is initialized from this FrameStack object.
58    #[inline]
59    pub unsafe fn end_init(&mut self, token: FrameToken) {
60        token.assert();
61        if self.stack.is_empty() {
62            unsafe { self.stack.set_len(1) };
63        }
64        self.index = Some(0);
65    }
66
67    /// Returns the current index of the stack.
68    #[inline]
69    pub fn index(&self) -> Option<usize> {
70        self.index
71    }
72
73    /// Increments the index.
74    ///
75    /// # Safety
76    ///
77    /// This method is unsafe because it assumes that the `token` is obtained from `get_next` and
78    /// that `end_init` is called to initialize the FrameStack.
79    #[inline]
80    pub unsafe fn push(&mut self, token: FrameToken) {
81        token.assert();
82        let index = self.index.as_mut().unwrap();
83        *index += 1;
84        // capacity of stack is incremented in `get_next`
85        debug_assert!(
86            *index < self.stack.capacity(),
87            "Stack capacity is not enough for index"
88        );
89        // If the index is the last one, we need to increase the length.
90        if *index == self.stack.len() {
91            unsafe { self.stack.set_len(self.stack.len() + 1) };
92        }
93    }
94
95    /// Clears the stack by setting the index to 0.
96    /// It does not destroy the stack.
97    #[inline]
98    pub fn clear(&mut self) {
99        self.index = None;
100    }
101
102    /// Decrements the index.
103    #[inline]
104    pub fn pop(&mut self) {
105        self.index = self.index.unwrap_or(0).checked_sub(1);
106    }
107
108    /// Returns the current item.
109    #[inline]
110    pub fn get(&mut self) -> &mut T {
111        debug_assert!(
112            self.stack.capacity() > self.index.unwrap(),
113            "Stack capacity is not enough for index"
114        );
115        unsafe { &mut *self.stack.as_mut_ptr().add(self.index.unwrap()) }
116    }
117
118    /// Get next uninitialized item.
119    #[inline]
120    pub fn get_next(&mut self) -> OutFrame<'_, T> {
121        if self.index.unwrap() + 1 == self.stack.capacity() {
122            // allocate 8 more items
123            self.stack.reserve(8);
124        }
125        self.out_frame_at(self.index.unwrap() + 1)
126    }
127
128    fn out_frame_at(&mut self, idx: usize) -> OutFrame<'_, T> {
129        unsafe {
130            OutFrame::new_maybe_uninit(self.stack.as_mut_ptr().add(idx), idx < self.stack.len())
131        }
132    }
133}
134
135/// A potentially initialized frame. Used when initializing a new frame in the main loop.
136#[allow(missing_debug_implementations)]
137pub struct OutFrame<'a, T> {
138    ptr: *mut T,
139    init: bool,
140    lt: core::marker::PhantomData<&'a mut T>,
141}
142
143impl<'a, T> OutFrame<'a, T> {
144    /// Creates a new initialized `OutFrame` from a mutable reference to a type `T`.
145    pub fn new_init(slot: &'a mut T) -> Self {
146        unsafe { Self::new_maybe_uninit(slot, true) }
147    }
148
149    /// Creates a new uninitialized `OutFrame` from a mutable reference to a `MaybeUninit<T>`.
150    pub fn new_uninit(slot: &'a mut core::mem::MaybeUninit<T>) -> Self {
151        unsafe { Self::new_maybe_uninit(slot.as_mut_ptr(), false) }
152    }
153
154    /// Creates a new `OutFrame` from a raw pointer to a type `T`.
155    ///
156    /// # Safety
157    ///
158    /// This method is unsafe because it assumes that the pointer is valid and points to a location
159    /// where a type `T` can be stored. It also assumes that the `init` flag correctly reflects whether
160    /// the type `T` has been initialized or not.
161    pub unsafe fn new_maybe_uninit(ptr: *mut T, init: bool) -> Self {
162        Self {
163            ptr,
164            init,
165            lt: Default::default(),
166        }
167    }
168
169    /// Returns a mutable reference to the type `T`, initializing it if it hasn't been initialized yet.
170    pub fn get(&mut self, f: impl FnOnce() -> T) -> &mut T {
171        if !self.init {
172            self.do_init(f);
173        }
174        unsafe { &mut *self.ptr }
175    }
176
177    #[inline(never)]
178    #[cold]
179    fn do_init(&mut self, f: impl FnOnce() -> T) {
180        unsafe {
181            self.init = true;
182            self.ptr.write(f());
183        }
184    }
185
186    /// Returns a mutable reference to the type `T`, without checking if it has been initialized.
187    ///
188    /// # Safety
189    ///
190    /// This method is unsafe because it assumes that the `OutFrame` has been initialized before use.
191    pub unsafe fn get_unchecked(&mut self) -> &mut T {
192        debug_assert!(self.init, "OutFrame must be initialized before use");
193        unsafe { &mut *self.ptr }
194    }
195
196    /// Consumes the `OutFrame`, returning a `FrameToken` that indicates the frame has been initialized.
197    pub fn consume(self) -> FrameToken {
198        FrameToken(self.init)
199    }
200}
201
202/// Used to guarantee that a frame is initialized before use.
203#[allow(missing_debug_implementations)]
204pub struct FrameToken(bool);
205
206impl FrameToken {
207    /// Asserts that the frame token is initialized.
208    #[cfg_attr(debug_assertions, track_caller)]
209    pub fn assert(self) {
210        assert!(self.0, "FrameToken must be initialized before use");
211    }
212}
213
214/// Local context used for caching initcode from Initcode transactions.
215pub trait LocalContextTr {
216    /// Interpreter shared memory buffer. A reused memory buffer for calls.
217    fn shared_memory_buffer(&self) -> &Rc<RefCell<Vec<u8>>>;
218
219    /// Slice of the shared memory buffer returns None if range is not valid or buffer can't be borrowed.
220    fn shared_memory_buffer_slice(&self, range: Range<usize>) -> Option<Ref<'_, [u8]>> {
221        let buffer = self.shared_memory_buffer();
222        buffer.borrow().get(range.clone())?;
223        Some(Ref::map(buffer.borrow(), |b| {
224            b.get(range).unwrap_or_default()
225        }))
226    }
227
228    /// Clear the local context.
229    fn clear(&mut self);
230
231    /// Set the error message for a precompile error, if any.
232    ///
233    /// This is used to bubble up precompile error messages when the
234    /// transaction directly targets a precompile (depth == 1).
235    fn set_precompile_error_context(&mut self, _output: String);
236
237    /// Take and clear the precompile error context, if present.
238    ///
239    /// Returns `Some(String)` if a precompile error message was recorded.
240    fn take_precompile_error_context(&mut self) -> Option<String>;
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246
247    #[test]
248    fn frame_stack() {
249        let mut stack = FrameStack::new_prealloc(1);
250        let mut frame = stack.start_init();
251        // it is already initialized to zero.
252        *frame.get(|| 2) += 1;
253
254        let token = frame.consume();
255        unsafe { stack.end_init(token) };
256
257        assert_eq!(stack.index(), Some(0));
258        assert_eq!(stack.stack.len(), 1);
259
260        let a = stack.get();
261        assert_eq!(a, &mut 1);
262        let mut b = stack.get_next();
263        assert!(!b.init);
264        assert_eq!(b.get(|| 2), &mut 2);
265        let token = b.consume(); // TODO: remove
266        unsafe { stack.push(token) };
267
268        assert_eq!(stack.index(), Some(1));
269        assert_eq!(stack.stack.len(), 2);
270        let a = stack.get();
271        assert_eq!(a, &mut 2);
272        let b = stack.get_next();
273        assert!(!b.init);
274
275        stack.pop();
276
277        assert_eq!(stack.index(), Some(0));
278        assert_eq!(stack.stack.len(), 2);
279        let a = stack.get();
280        assert_eq!(a, &mut 1);
281        let mut b = stack.get_next();
282        assert!(b.init);
283        assert_eq!(unsafe { b.get_unchecked() }, &mut 2);
284    }
285}