soroban_wasmi/engine/
mod.rs

1//! The Wasmi interpreter.
2
3mod block_type;
4pub mod bytecode;
5mod code_map;
6mod config;
7mod executor;
8mod func_args;
9mod func_types;
10mod limits;
11mod resumable;
12mod traits;
13mod translator;
14
15#[cfg(test)]
16mod tests;
17
18#[cfg(test)]
19use self::bytecode::RegisterSpan;
20
21#[cfg(test)]
22use code_map::CompiledFuncRef;
23
24pub(crate) use self::{
25    block_type::BlockType,
26    executor::Stack,
27    func_args::{FuncFinished, FuncParams, FuncResults},
28    func_types::DedupFuncType,
29    translator::{
30        FuncTranslationDriver,
31        FuncTranslator,
32        FuncTranslatorAllocations,
33        LazyFuncTranslator,
34        ValidatingFuncTranslator,
35        WasmTranslator,
36    },
37};
38use self::{
39    code_map::{CodeMap, CompiledFuncEntity},
40    func_types::FuncTypeRegistry,
41    resumable::ResumableCallBase,
42};
43pub use self::{
44    code_map::{EngineFunc, EngineFuncSpan, EngineFuncSpanIter},
45    config::{CompilationMode, Config, FuelCosts},
46    executor::ResumableHostError,
47    limits::{EnforcedLimits, EnforcedLimitsError, StackLimits},
48    resumable::{ResumableCall, ResumableInvocation, TypedResumableCall, TypedResumableInvocation},
49    traits::{CallParams, CallResults},
50    translator::{Instr, TranslationError},
51};
52use crate::{
53    collections::arena::{ArenaIndex, GuardedEntity},
54    module::{FuncIdx, ModuleHeader},
55    Error,
56    Func,
57    FuncType,
58    StoreContextMut,
59};
60use core::sync::atomic::{AtomicU32, Ordering};
61use spin::{Mutex, RwLock};
62use std::{
63    sync::{Arc, Weak},
64    vec::Vec,
65};
66use wasmparser::{FuncToValidate, FuncValidatorAllocations, ValidatorResources};
67
68#[cfg(test)]
69use self::bytecode::Instruction;
70
71#[cfg(test)]
72use crate::core::UntypedVal;
73
74#[cfg(doc)]
75use crate::Store;
76
77/// A unique engine index.
78///
79/// # Note
80///
81/// Used to protect against invalid entity indices.
82#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
83pub struct EngineIdx(u32);
84
85impl ArenaIndex for EngineIdx {
86    fn into_usize(self) -> usize {
87        self.0 as _
88    }
89
90    fn from_usize(value: usize) -> Self {
91        let value = value.try_into().unwrap_or_else(|error| {
92            panic!("index {value} is out of bounds as engine index: {error}")
93        });
94        Self(value)
95    }
96}
97
98impl EngineIdx {
99    /// Returns a new unique [`EngineIdx`].
100    fn new() -> Self {
101        /// A static store index counter.
102        static CURRENT_STORE_IDX: AtomicU32 = AtomicU32::new(0);
103        let next_idx = CURRENT_STORE_IDX.fetch_add(1, Ordering::AcqRel);
104        Self(next_idx)
105    }
106}
107
108/// An entity owned by the [`Engine`].
109type Guarded<Idx> = GuardedEntity<EngineIdx, Idx>;
110
111/// The Wasmi interpreter.
112///
113/// # Note
114///
115/// - The current Wasmi engine implements a bytecode interpreter.
116/// - This structure is intentionally cheap to copy.
117///   Most of its API has a `&self` receiver, so can be shared easily.
118#[derive(Debug, Clone)]
119pub struct Engine {
120    inner: Arc<EngineInner>,
121}
122
123/// A weak reference to an [`Engine`].
124#[derive(Debug, Clone)]
125pub struct EngineWeak {
126    inner: Weak<EngineInner>,
127}
128
129impl EngineWeak {
130    /// Upgrades the [`EngineWeak`] to an [`Engine`].
131    ///
132    /// Returns `None` if strong references (the [`Engine`] itself) no longer exist.
133    pub fn upgrade(&self) -> Option<Engine> {
134        let inner = self.inner.upgrade()?;
135        Some(Engine { inner })
136    }
137}
138
139impl Default for Engine {
140    fn default() -> Self {
141        Self::new(&Config::default())
142    }
143}
144
145impl Engine {
146    /// Creates a new [`Engine`] with default configuration.
147    ///
148    /// # Note
149    ///
150    /// Users should ues [`Engine::default`] to construct a default [`Engine`].
151    pub fn new(config: &Config) -> Self {
152        Self {
153            inner: Arc::new(EngineInner::new(config)),
154        }
155    }
156
157    /// Creates an [`EngineWeak`] from the given [`Engine`].
158    pub fn weak(&self) -> EngineWeak {
159        EngineWeak {
160            inner: Arc::downgrade(&self.inner),
161        }
162    }
163
164    /// Returns a shared reference to the [`Config`] of the [`Engine`].
165    pub fn config(&self) -> &Config {
166        self.inner.config()
167    }
168
169    /// Returns `true` if both [`Engine`] references `a` and `b` refer to the same [`Engine`].
170    pub fn same(a: &Engine, b: &Engine) -> bool {
171        Arc::ptr_eq(&a.inner, &b.inner)
172    }
173
174    /// Allocates a new function type to the [`Engine`].
175    pub(super) fn alloc_func_type(&self, func_type: FuncType) -> DedupFuncType {
176        self.inner.alloc_func_type(func_type)
177    }
178
179    /// Resolves a deduplicated function type into a [`FuncType`] entity.
180    ///
181    /// # Panics
182    ///
183    /// - If the deduplicated function type is not owned by the engine.
184    /// - If the deduplicated function type cannot be resolved to its entity.
185    pub(super) fn resolve_func_type<F, R>(&self, func_type: &DedupFuncType, f: F) -> R
186    where
187        F: FnOnce(&FuncType) -> R,
188    {
189        self.inner.resolve_func_type(func_type, f)
190    }
191
192    /// Allocates `amount` new uninitialized [`EngineFunc`] to the [`CodeMap`].
193    ///
194    /// Returns a range of [`EngineFunc`]s to allow accessing the allocated [`EngineFunc`].
195    pub(super) fn alloc_funcs(&self, amount: usize) -> EngineFuncSpan {
196        self.inner.alloc_funcs(amount)
197    }
198
199    /// Translates the Wasm function using the [`Engine`].
200    ///
201    /// - Uses the internal [`Config`] to drive the function translation as mandated.
202    /// - Reuses translation and validation allocations to be more efficient when used for many translation units.
203    ///
204    /// # Parameters
205    ///
206    /// - `func_index`: The index of the translated function within its Wasm module.
207    /// - `engine_func`: The index of the translated function in the [`Engine`].
208    /// - `offset`: The global offset of the Wasm function body within the Wasm binary.
209    /// - `bytes`: The bytes that make up the Wasm encoded function body of the translated function.
210    /// - `module`: The module header information of the Wasm module of the translated function.
211    /// - `func_to_validate`: Optionally validates the translated function.
212    ///
213    /// # Errors
214    ///
215    /// - If function translation fails.
216    /// - If function validation fails.
217    pub(crate) fn translate_func(
218        &self,
219        func_index: FuncIdx,
220        engine_func: EngineFunc,
221        offset: usize,
222        bytes: &[u8],
223        module: ModuleHeader,
224        func_to_validate: Option<FuncToValidate<ValidatorResources>>,
225    ) -> Result<(), Error> {
226        match (self.config().get_compilation_mode(), func_to_validate) {
227            (CompilationMode::Eager, Some(func_to_validate)) => {
228                let (translation_allocs, validation_allocs) = self.inner.get_allocs();
229                let validator = func_to_validate.into_validator(validation_allocs);
230                let translator = FuncTranslator::new(func_index, module, translation_allocs)?;
231                let translator = ValidatingFuncTranslator::new(validator, translator)?;
232                let allocs = FuncTranslationDriver::new(offset, bytes, translator)?
233                    .translate(|func_entity| self.inner.init_func(engine_func, func_entity))?;
234                self.inner
235                    .recycle_allocs(allocs.translation, allocs.validation);
236            }
237            (CompilationMode::Eager, None) => {
238                let allocs = self.inner.get_translation_allocs();
239                let translator = FuncTranslator::new(func_index, module, allocs)?;
240                let allocs = FuncTranslationDriver::new(offset, bytes, translator)?
241                    .translate(|func_entity| self.inner.init_func(engine_func, func_entity))?;
242                self.inner.recycle_translation_allocs(allocs);
243            }
244            (CompilationMode::LazyTranslation, Some(func_to_validate)) => {
245                let allocs = self.inner.get_validation_allocs();
246                let translator = LazyFuncTranslator::new(func_index, engine_func, module, None);
247                let validator = func_to_validate.into_validator(allocs);
248                let translator = ValidatingFuncTranslator::new(validator, translator)?;
249                let allocs = FuncTranslationDriver::new(offset, bytes, translator)?
250                    .translate(|func_entity| self.inner.init_func(engine_func, func_entity))?;
251                self.inner.recycle_validation_allocs(allocs.validation);
252            }
253            (CompilationMode::Lazy | CompilationMode::LazyTranslation, func_to_validate) => {
254                let translator =
255                    LazyFuncTranslator::new(func_index, engine_func, module, func_to_validate);
256                FuncTranslationDriver::new(offset, bytes, translator)?
257                    .translate(|func_entity| self.inner.init_func(engine_func, func_entity))?;
258            }
259        }
260        Ok(())
261    }
262
263    /// Returns reusable [`FuncTranslatorAllocations`] from the [`Engine`].
264    pub(crate) fn get_translation_allocs(&self) -> FuncTranslatorAllocations {
265        self.inner.get_translation_allocs()
266    }
267
268    /// Returns reusable [`FuncTranslatorAllocations`] and [`FuncValidatorAllocations`] from the [`Engine`].
269    pub(crate) fn get_allocs(&self) -> (FuncTranslatorAllocations, FuncValidatorAllocations) {
270        self.inner.get_allocs()
271    }
272
273    /// Recycles the given [`FuncTranslatorAllocations`] in the [`Engine`].
274    pub(crate) fn recycle_translation_allocs(&self, allocs: FuncTranslatorAllocations) {
275        self.inner.recycle_translation_allocs(allocs)
276    }
277
278    /// Recycles the given [`FuncTranslatorAllocations`] and [`FuncValidatorAllocations`] in the [`Engine`].
279    pub(crate) fn recycle_allocs(
280        &self,
281        translation: FuncTranslatorAllocations,
282        validation: FuncValidatorAllocations,
283    ) {
284        self.inner.recycle_allocs(translation, validation)
285    }
286
287    /// Initializes the uninitialized [`EngineFunc`] for the [`Engine`].
288    ///
289    /// # Note
290    ///
291    /// The initialized function will not be compiled after this call and instead
292    /// be prepared to be compiled on the fly when it is called the first time.
293    ///
294    /// # Panics
295    ///
296    /// - If `func` is an invalid [`EngineFunc`] reference for this [`CodeMap`].
297    /// - If `func` refers to an already initialized [`EngineFunc`].
298    fn init_lazy_func(
299        &self,
300        func_idx: FuncIdx,
301        func: EngineFunc,
302        bytes: &[u8],
303        module: &ModuleHeader,
304        func_to_validate: Option<FuncToValidate<ValidatorResources>>,
305    ) {
306        self.inner
307            .init_lazy_func(func_idx, func, bytes, module, func_to_validate)
308    }
309
310    /// Resolves the [`EngineFunc`] to the underlying Wasmi bytecode instructions.
311    ///
312    /// # Note
313    ///
314    /// - This is a variant of [`Engine::resolve_instr`] that returns register
315    ///   machine based bytecode instructions.
316    /// - This API is mainly intended for unit testing purposes and shall not be used
317    ///   outside of this context. The function bodies are intended to be data private
318    ///   to the Wasmi interpreter.
319    ///
320    /// # Errors
321    ///
322    /// If the `func` fails Wasm to Wasmi bytecode translation after it was lazily initialized.
323    ///
324    /// # Panics
325    ///
326    /// - If the [`EngineFunc`] is invalid for the [`Engine`].
327    /// - If register machine bytecode translation is disabled.
328    #[cfg(test)]
329    pub(crate) fn resolve_instr(
330        &self,
331        func: EngineFunc,
332        index: usize,
333    ) -> Result<Option<Instruction>, Error> {
334        self.inner.resolve_instr(func, index)
335    }
336
337    /// Resolves the function local constant of [`EngineFunc`] at `index` if any.
338    ///
339    /// # Note
340    ///
341    /// This API is intended for unit testing purposes and shall not be used
342    /// outside of this context. The function bodies are intended to be data
343    /// private to the Wasmi interpreter.
344    ///
345    /// # Errors
346    ///
347    /// If the `func` fails Wasm to Wasmi bytecode translation after it was lazily initialized.
348    ///
349    /// # Panics
350    ///
351    /// - If the [`EngineFunc`] is invalid for the [`Engine`].
352    /// - If register machine bytecode translation is disabled.
353    #[cfg(test)]
354    fn get_func_const(&self, func: EngineFunc, index: usize) -> Result<Option<UntypedVal>, Error> {
355        self.inner.get_func_const(func, index)
356    }
357
358    /// Executes the given [`Func`] with parameters `params`.
359    ///
360    /// Stores the execution result into `results` upon a successful execution.
361    ///
362    /// # Note
363    ///
364    /// - Assumes that the `params` and `results` are well typed.
365    ///   Type checks are done at the [`Func::call`] API or when creating
366    ///   a new [`TypedFunc`] instance via [`Func::typed`].
367    /// - The `params` out parameter is in a valid but unspecified state if this
368    ///   function returns with an error.
369    ///
370    /// # Errors
371    ///
372    /// - If `params` are overflowing or underflowing the expected amount of parameters.
373    /// - If the given `results` do not match the the length of the expected results of `func`.
374    /// - When encountering a Wasm or host trap during the execution of `func`.
375    ///
376    /// [`TypedFunc`]: [`crate::TypedFunc`]
377    #[inline]
378    pub(crate) fn execute_func<T, Results>(
379        &self,
380        ctx: StoreContextMut<T>,
381        func: &Func,
382        params: impl CallParams,
383        results: Results,
384    ) -> Result<<Results as CallResults>::Results, Error>
385    where
386        Results: CallResults,
387    {
388        self.inner.execute_func(ctx, func, params, results)
389    }
390
391    /// Executes the given [`Func`] resumably with parameters `params` and returns.
392    ///
393    /// Stores the execution result into `results` upon a successful execution.
394    /// If the execution encounters a host trap it will return a handle to the user
395    /// that allows to resume the execution at that point.
396    ///
397    /// # Note
398    ///
399    /// - Assumes that the `params` and `results` are well typed.
400    ///   Type checks are done at the [`Func::call`] API or when creating
401    ///   a new [`TypedFunc`] instance via [`Func::typed`].
402    /// - The `params` out parameter is in a valid but unspecified state if this
403    ///   function returns with an error.
404    ///
405    /// # Errors
406    ///
407    /// - If `params` are overflowing or underflowing the expected amount of parameters.
408    /// - If the given `results` do not match the the length of the expected results of `func`.
409    /// - When encountering a Wasm trap during the execution of `func`.
410    /// - When `func` is a host function that traps.
411    ///
412    /// [`TypedFunc`]: [`crate::TypedFunc`]
413    #[inline]
414    pub(crate) fn execute_func_resumable<T, Results>(
415        &self,
416        ctx: StoreContextMut<T>,
417        func: &Func,
418        params: impl CallParams,
419        results: Results,
420    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
421    where
422        Results: CallResults,
423    {
424        self.inner
425            .execute_func_resumable(ctx, func, params, results)
426    }
427
428    /// Resumes the given `invocation` given the `params`.
429    ///
430    /// Stores the execution result into `results` upon a successful execution.
431    /// If the execution encounters a host trap it will return a handle to the user
432    /// that allows to resume the execution at that point.
433    ///
434    /// # Note
435    ///
436    /// - Assumes that the `params` and `results` are well typed.
437    ///   Type checks are done at the [`Func::call`] API or when creating
438    ///   a new [`TypedFunc`] instance via [`Func::typed`].
439    /// - The `params` out parameter is in a valid but unspecified state if this
440    ///   function returns with an error.
441    ///
442    /// # Errors
443    ///
444    /// - If `params` are overflowing or underflowing the expected amount of parameters.
445    /// - If the given `results` do not match the the length of the expected results of `func`.
446    /// - When encountering a Wasm trap during the execution of `func`.
447    /// - When `func` is a host function that traps.
448    ///
449    /// [`TypedFunc`]: [`crate::TypedFunc`]
450    #[inline]
451    pub(crate) fn resume_func<T, Results>(
452        &self,
453        ctx: StoreContextMut<T>,
454        invocation: ResumableInvocation,
455        params: impl CallParams,
456        results: Results,
457    ) -> Result<ResumableCallBase<<Results as CallResults>::Results>, Error>
458    where
459        Results: CallResults,
460    {
461        self.inner.resume_func(ctx, invocation, params, results)
462    }
463
464    /// Recycles the given [`Stack`] for reuse in the [`Engine`].
465    pub(crate) fn recycle_stack(&self, stack: Stack) {
466        self.inner.recycle_stack(stack)
467    }
468}
469
470/// The internal state of the Wasmi [`Engine`].
471#[derive(Debug)]
472pub struct EngineInner {
473    /// The [`Config`] of the engine.
474    config: Config,
475    /// Stores information about all compiled functions.
476    code_map: CodeMap,
477    /// Deduplicated function types.
478    ///
479    /// # Note
480    ///
481    /// The engine deduplicates function types to make the equality
482    /// comparison very fast. This helps to speed up indirect calls.
483    func_types: RwLock<FuncTypeRegistry>,
484    /// Reusable allocation stacks.
485    allocs: Mutex<ReusableAllocationStack>,
486    /// Reusable engine stacks for Wasm execution.
487    ///
488    /// Concurrently executing Wasm executions each require their own stack to
489    /// operate on. Therefore a Wasm engine is required to provide stacks and
490    /// ideally recycles old ones since creation of a new stack is rather expensive.
491    stacks: Mutex<EngineStacks>,
492}
493
494/// Stacks to hold and distribute reusable allocations.
495pub struct ReusableAllocationStack {
496    /// The maximum height of each of the allocations stacks.
497    max_height: usize,
498    /// Allocations required by Wasm function translators.
499    translation: Vec<FuncTranslatorAllocations>,
500    /// Allocations required by Wasm function validators.
501    validation: Vec<FuncValidatorAllocations>,
502}
503
504impl Default for ReusableAllocationStack {
505    fn default() -> Self {
506        Self {
507            max_height: 1,
508            translation: Vec::new(),
509            validation: Vec::new(),
510        }
511    }
512}
513
514impl core::fmt::Debug for ReusableAllocationStack {
515    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
516        f.debug_struct("ReusableAllocationStack")
517            .field("translation", &self.translation)
518            // Note: FuncValidatorAllocations is missing Debug impl at the time of writing this commit.
519            //       We should derive Debug as soon as FuncValidatorAllocations has a Debug impl in future
520            //       wasmparser versions.
521            .field("validation", &self.validation.len())
522            .finish()
523    }
524}
525
526impl ReusableAllocationStack {
527    /// Returns reusable [`FuncTranslatorAllocations`] from the [`Engine`].
528    pub fn get_translation_allocs(&mut self) -> FuncTranslatorAllocations {
529        self.translation.pop().unwrap_or_default()
530    }
531
532    /// Returns reusable [`FuncValidatorAllocations`] from the [`Engine`].
533    pub fn get_validation_allocs(&mut self) -> FuncValidatorAllocations {
534        self.validation.pop().unwrap_or_default()
535    }
536
537    /// Recycles the given [`FuncTranslatorAllocations`] in the [`Engine`].
538    pub fn recycle_translation_allocs(&mut self, recycled: FuncTranslatorAllocations) {
539        debug_assert!(self.translation.len() <= self.max_height);
540        if self.translation.len() >= self.max_height {
541            return;
542        }
543        self.translation.push(recycled);
544    }
545
546    /// Recycles the given [`FuncValidatorAllocations`] in the [`Engine`].
547    pub fn recycle_validation_allocs(&mut self, recycled: FuncValidatorAllocations) {
548        debug_assert!(self.validation.len() <= self.max_height);
549        if self.validation.len() >= self.max_height {
550            return;
551        }
552        self.validation.push(recycled);
553    }
554}
555
556/// The engine's stacks for reuse.
557///
558/// Rquired for efficient concurrent Wasm executions.
559#[derive(Debug)]
560pub struct EngineStacks {
561    /// Stacks to be (re)used.
562    stacks: Vec<Stack>,
563    /// Stack limits for newly constructed engine stacks.
564    limits: StackLimits,
565    /// How many stacks should be kept for reuse at most.
566    keep: usize,
567}
568
569impl EngineStacks {
570    /// Creates new [`EngineStacks`] with the given [`StackLimits`].
571    pub fn new(config: &Config) -> Self {
572        Self {
573            stacks: Vec::new(),
574            limits: config.stack_limits(),
575            keep: config.cached_stacks(),
576        }
577    }
578
579    /// Reuse or create a new [`Stack`] if none was available.
580    pub fn reuse_or_new(&mut self) -> Stack {
581        match self.stacks.pop() {
582            Some(stack) => stack,
583            None => Stack::new(self.limits),
584        }
585    }
586
587    /// Disose and recycle the `stack`.
588    pub fn recycle(&mut self, stack: Stack) {
589        if stack.capacity() > 0 && self.stacks.len() < self.keep {
590            self.stacks.push(stack);
591        }
592    }
593}
594
595impl EngineInner {
596    /// Creates a new [`EngineInner`] with the given [`Config`].
597    fn new(config: &Config) -> Self {
598        let engine_idx = EngineIdx::new();
599        Self {
600            config: *config,
601            code_map: CodeMap::new(config),
602            func_types: RwLock::new(FuncTypeRegistry::new(engine_idx)),
603            allocs: Mutex::new(ReusableAllocationStack::default()),
604            stacks: Mutex::new(EngineStacks::new(config)),
605        }
606    }
607
608    /// Returns a shared reference to the [`Config`] of the [`EngineInner`].
609    fn config(&self) -> &Config {
610        &self.config
611    }
612
613    /// Allocates a new function type to the [`EngineInner`].
614    fn alloc_func_type(&self, func_type: FuncType) -> DedupFuncType {
615        self.func_types.write().alloc_func_type(func_type)
616    }
617
618    /// Resolves a deduplicated function type into a [`FuncType`] entity.
619    ///
620    /// # Panics
621    ///
622    /// - If the deduplicated function type is not owned by the engine.
623    /// - If the deduplicated function type cannot be resolved to its entity.
624    fn resolve_func_type<F, R>(&self, func_type: &DedupFuncType, f: F) -> R
625    where
626        F: FnOnce(&FuncType) -> R,
627    {
628        f(self.func_types.read().resolve_func_type(func_type))
629    }
630
631    /// Allocates `amount` new uninitialized [`EngineFunc`] to the [`CodeMap`].
632    ///
633    /// Returns a range of [`EngineFunc`]s to allow accessing the allocated [`EngineFunc`].
634    fn alloc_funcs(&self, amount: usize) -> EngineFuncSpan {
635        self.code_map.alloc_funcs(amount)
636    }
637
638    /// Returns reusable [`FuncTranslatorAllocations`] from the [`Engine`].
639    fn get_translation_allocs(&self) -> FuncTranslatorAllocations {
640        self.allocs.lock().get_translation_allocs()
641    }
642
643    /// Returns reusable [`FuncValidatorAllocations`] from the [`Engine`].
644    fn get_validation_allocs(&self) -> FuncValidatorAllocations {
645        self.allocs.lock().get_validation_allocs()
646    }
647
648    /// Returns reusable [`FuncTranslatorAllocations`] and [`FuncValidatorAllocations`] from the [`Engine`].
649    ///
650    /// # Note
651    ///
652    /// This method is a bit more efficient than calling both
653    /// - [`EngineInner::get_translation_allocs`]
654    /// - [`EngineInner::get_validation_allocs`]
655    fn get_allocs(&self) -> (FuncTranslatorAllocations, FuncValidatorAllocations) {
656        let mut allocs = self.allocs.lock();
657        let translation = allocs.get_translation_allocs();
658        let validation = allocs.get_validation_allocs();
659        (translation, validation)
660    }
661
662    /// Recycles the given [`FuncTranslatorAllocations`] in the [`Engine`].
663    fn recycle_translation_allocs(&self, allocs: FuncTranslatorAllocations) {
664        self.allocs.lock().recycle_translation_allocs(allocs)
665    }
666
667    /// Recycles the given [`FuncValidatorAllocations`] in the [`Engine`].
668    fn recycle_validation_allocs(&self, allocs: FuncValidatorAllocations) {
669        self.allocs.lock().recycle_validation_allocs(allocs)
670    }
671
672    /// Recycles the given [`FuncTranslatorAllocations`] and [`FuncValidatorAllocations`] in the [`Engine`].
673    ///
674    /// # Note
675    ///
676    /// This method is a bit more efficient than calling both
677    /// - [`EngineInner::recycle_translation_allocs`]
678    /// - [`EngineInner::recycle_validation_allocs`]
679    fn recycle_allocs(
680        &self,
681        translation: FuncTranslatorAllocations,
682        validation: FuncValidatorAllocations,
683    ) {
684        let mut allocs = self.allocs.lock();
685        allocs.recycle_translation_allocs(translation);
686        allocs.recycle_validation_allocs(validation);
687    }
688
689    /// Initializes the uninitialized [`EngineFunc`] for the [`EngineInner`].
690    ///
691    /// # Note
692    ///
693    /// The initialized function will be compiled and ready to be executed after this call.
694    ///
695    /// # Panics
696    ///
697    /// - If `func` is an invalid [`EngineFunc`] reference for this [`CodeMap`].
698    /// - If `func` refers to an already initialized [`EngineFunc`].
699    fn init_func(&self, engine_func: EngineFunc, func_entity: CompiledFuncEntity) {
700        self.code_map
701            .init_func_as_compiled(engine_func, func_entity)
702    }
703
704    /// Initializes the uninitialized [`EngineFunc`] for the [`Engine`].
705    ///
706    /// # Note
707    ///
708    /// The initialized function will not be compiled after this call and instead
709    /// be prepared to be compiled on the fly when it is called the first time.
710    ///
711    /// # Panics
712    ///
713    /// - If `func` is an invalid [`EngineFunc`] reference for this [`CodeMap`].
714    /// - If `func` refers to an already initialized [`EngineFunc`].
715    fn init_lazy_func(
716        &self,
717        func_idx: FuncIdx,
718        func: EngineFunc,
719        bytes: &[u8],
720        module: &ModuleHeader,
721        func_to_validate: Option<FuncToValidate<ValidatorResources>>,
722    ) {
723        self.code_map
724            .init_func_as_uncompiled(func, func_idx, bytes, module, func_to_validate)
725    }
726
727    /// Resolves the [`InternalFuncEntity`] for [`EngineFunc`] and applies `f` to it.
728    ///
729    /// # Panics
730    ///
731    /// If [`EngineFunc`] is invalid for [`Engine`].
732    #[cfg(test)]
733    pub(super) fn resolve_func<'a, F, R>(&'a self, func: EngineFunc, f: F) -> Result<R, Error>
734    where
735        F: FnOnce(CompiledFuncRef<'a>) -> R,
736    {
737        // Note: We use `None` so this test-only function will never charge for compilation fuel.
738        Ok(f(self.code_map.get(None, func)?))
739    }
740
741    /// Returns the [`Instruction`] of `func` at `index`.
742    ///
743    /// Returns `None` if the function has no instruction at `index`.
744    ///
745    /// # Errors
746    ///
747    /// If the `func` fails Wasm to Wasmi bytecode translation after it was lazily initialized.
748    ///
749    /// # Pancis
750    ///
751    /// If `func` cannot be resolved to a function for the [`EngineInner`].
752    #[cfg(test)]
753    pub(crate) fn resolve_instr(
754        &self,
755        func: EngineFunc,
756        index: usize,
757    ) -> Result<Option<Instruction>, Error> {
758        self.resolve_func(func, |func| func.instrs().get(index).copied())
759    }
760
761    /// Returns the function local constant value of `func` at `index`.
762    ///
763    /// Returns `None` if the function has no function local constant at `index`.
764    ///
765    /// # Errors
766    ///
767    /// If the `func` fails Wasm to Wasmi bytecode translation after it was lazily initialized.
768    ///
769    /// # Pancis
770    ///
771    /// If `func` cannot be resolved to a function for the [`EngineInner`].
772    #[cfg(test)]
773    fn get_func_const(&self, func: EngineFunc, index: usize) -> Result<Option<UntypedVal>, Error> {
774        // Function local constants are stored in reverse order of their indices since
775        // they are allocated in reverse order to their absolute indices during function
776        // translation. That is why we need to access them in reverse order.
777        self.resolve_func(func, |func| func.consts().iter().rev().nth(index).copied())
778    }
779
780    /// Recycles the given [`Stack`].
781    fn recycle_stack(&self, stack: Stack) {
782        self.stacks.lock().recycle(stack)
783    }
784}