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