wasmtime/runtime/vm/
throw.rs1use super::{VMContext, VMStore};
4use crate::{store::AutoAssertNoGc, vm::Instance};
5use core::ptr::NonNull;
6use wasmtime_environ::TagIndex;
7use wasmtime_unwinder::{Frame, Handler};
8
9pub unsafe fn compute_handler(store: &mut dyn VMStore) -> Option<Handler> {
16 let mut nogc = AutoAssertNoGc::new(store.store_opaque_mut());
17
18 let exnref = nogc
22 .take_pending_exception()
23 .expect("Only invoked when an exception is pending");
24 let (throwing_tag_instance_id, throwing_tag_defined_tag_index) =
25 exnref.tag(&mut nogc).expect("cannot read tag");
26 nogc.set_pending_exception(exnref);
27 log::trace!(
28 "throwing: tag defined in instance {throwing_tag_instance_id:?} defined-tag {throwing_tag_defined_tag_index:?}"
29 );
30
31 let (exit_pc, exit_fp, entry_fp) = unsafe {
33 (
34 *nogc.vm_store_context().last_wasm_exit_pc.get(),
35 nogc.vm_store_context().last_wasm_exit_fp(),
36 *nogc.vm_store_context().last_wasm_entry_fp.get(),
37 )
38 };
39
40 if exit_fp == 0 {
45 return None;
46 }
47
48 let handler_lookup = |frame: &Frame| -> Option<(usize, usize)> {
52 log::trace!(
53 "exception-throw stack walk: frame at FP={:x} PC={:x}",
54 frame.fp(),
55 frame.pc()
56 );
57 let module = nogc.modules().lookup_module_by_pc(frame.pc())?;
58 let base = module.code_object().code_memory().text().as_ptr() as usize;
59 let rel_pc = u32::try_from(frame.pc().wrapping_sub(base)).expect("Module larger than 4GiB");
60 let et = module.exception_table();
61 let (frame_offset, handlers) = et.lookup_pc(rel_pc);
62 let fp_to_sp = frame_offset.map(|frame_offset| -isize::try_from(frame_offset).unwrap());
63 for handler in handlers {
64 log::trace!("-> checking handler: {handler:?}");
65 let is_match = match handler.tag {
66 None => true,
68 Some(module_local_tag_index) => {
69 let fp_to_sp =
70 fp_to_sp.expect("frame offset is necessary for exception unwind");
71 let fp_offset = fp_to_sp
72 + isize::try_from(
73 handler
74 .context_sp_offset
75 .expect("dynamic context not present for handler record"),
76 )
77 .unwrap();
78 let frame_vmctx = unsafe { frame.read_slot_from_fp(fp_offset) };
79 log::trace!("-> read vmctx from frame: {frame_vmctx:x}");
80 let frame_vmctx =
81 NonNull::new(frame_vmctx as *mut VMContext).expect("null vmctx in frame");
82
83 let (handler_tag_instance, handler_tag_index) = unsafe {
97 let store_id = nogc.id();
98 let instance = Instance::from_vmctx(frame_vmctx);
99 let tag = instance
100 .as_ref()
101 .get_exported_tag(store_id, TagIndex::from_u32(module_local_tag_index));
102 tag.to_raw_indices()
103 };
104 log::trace!(
105 "-> handler's tag {module_local_tag_index:?} resolves to instance {handler_tag_instance:?} defined-tag {handler_tag_index:?}"
106 );
107
108 handler_tag_instance == throwing_tag_instance_id
109 && handler_tag_index == throwing_tag_defined_tag_index
110 }
111 };
112 if is_match {
113 let fp_to_sp = fp_to_sp.expect("frame offset must be known if we found a handler");
114 return Some((
115 base.wrapping_add(
116 usize::try_from(handler.handler_offset).expect("Module larger than usize"),
117 ),
118 frame.fp().wrapping_add_signed(fp_to_sp),
119 ));
120 }
121 }
122 None
123 };
124 let unwinder = nogc.unwinder();
125 let action = unsafe { Handler::find(unwinder, handler_lookup, exit_pc, exit_fp, entry_fp) };
126 log::trace!("throw action: {action:?}");
127 action
128}