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}