Compute the target of the pending exception on the store. # Safety The stored last-exit state in `store` either must be valid, or must have a zeroed exit FP if no Wasm is on the stack.
(
store: &mut StoreOpaque,
throwing_tag_instance_id: InstanceId,
throwing_tag_defined_tag_index: DefinedTagIndex,
)
| 16 | /// The stored last-exit state in `store` either must be valid, or |
| 17 | /// must have a zeroed exit FP if no Wasm is on the stack. |
| 18 | pub unsafe fn compute_handler( |
| 19 | store: &mut StoreOpaque, |
| 20 | throwing_tag_instance_id: InstanceId, |
| 21 | throwing_tag_defined_tag_index: DefinedTagIndex, |
| 22 | ) -> Option<Handler> { |
| 23 | log::trace!( |
| 24 | "throwing: tag defined in instance {throwing_tag_instance_id:?} defined-tag {throwing_tag_defined_tag_index:?}" |
| 25 | ); |
| 26 | |
| 27 | // Get the state needed for a stack walk. |
| 28 | let (exit_pc, exit_fp, entry_fp) = unsafe { |
| 29 | ( |
| 30 | *store.vm_store_context().last_wasm_exit_pc.get(), |
| 31 | store.vm_store_context().last_wasm_exit_fp(), |
| 32 | *store.vm_store_context().last_wasm_entry_fp.get(), |
| 33 | ) |
| 34 | }; |
| 35 | |
| 36 | // Early out: if there is no exit FP -- which can happen if a host |
| 37 | // func, wrapped up as a `Func`, is called directly via |
| 38 | // `Func::call` -- then the only possible action we can take is |
| 39 | // `None` (i.e., no handler, unwind to entry from host). |
| 40 | if exit_fp == 0 { |
| 41 | return None; |
| 42 | } |
| 43 | |
| 44 | // Walk the stack, looking up the module with each PC, and using |
| 45 | // that module to resolve local tag indices into (instance, tag) |
| 46 | // tuples. |
| 47 | let handler_lookup = |frame: &Frame| -> Option<(usize, usize)> { |
| 48 | log::trace!( |
| 49 | "exception-throw stack walk: frame at FP={:x} PC={:x}", |
| 50 | frame.fp(), |
| 51 | frame.pc() |
| 52 | ); |
| 53 | let (store_code, rel_pc) = store.modules().store_code_by_pc(frame.pc())?; |
| 54 | let et = ExceptionTable::parse(store_code.code_memory().exception_tables()) |
| 55 | .expect("Exception tables were validated on module load"); |
| 56 | let (frame_offset, handlers) = et.lookup_pc(u32::try_from(rel_pc).unwrap()); |
| 57 | let fp_to_sp = frame_offset.map(|frame_offset| -isize::try_from(frame_offset).unwrap()); |
| 58 | for handler in handlers { |
| 59 | log::trace!("-> checking handler: {handler:?}"); |
| 60 | let is_match = match handler.tag { |
| 61 | // Catch-all/default handler. Always come last in sequence. |
| 62 | None => true, |
| 63 | Some(module_local_tag_index) => { |
| 64 | let fp_to_sp = |
| 65 | fp_to_sp.expect("frame offset is necessary for exception unwind"); |
| 66 | let fp_offset = fp_to_sp |
| 67 | + isize::try_from( |
| 68 | handler |
| 69 | .context_sp_offset |
| 70 | .expect("dynamic context not present for handler record"), |
| 71 | ) |
| 72 | .unwrap(); |
| 73 | let frame_vmctx = unsafe { frame.read_slot_from_fp(fp_offset) }; |
| 74 | log::trace!("-> read vmctx from frame: {frame_vmctx:x}"); |
| 75 | let frame_vmctx = |
no test coverage detected