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