1use crate::FuncKey;
8use alloc::vec::Vec;
9use object::{Bytes, LittleEndian, U32Bytes};
10
11#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13pub struct FrameStackShape(pub(crate) u32);
14impl FrameStackShape {
15 pub(crate) fn index(self) -> usize {
16 usize::try_from(self.0).unwrap()
17 }
18
19 pub fn raw(self) -> u32 {
22 self.0
23 }
24
25 pub fn from_raw(index: u32) -> FrameStackShape {
27 FrameStackShape(index)
28 }
29}
30
31#[derive(Clone, Copy, Debug)]
34pub struct FrameTableDescriptorIndex(pub(crate) u32);
35impl FrameTableDescriptorIndex {
36 fn index(self) -> usize {
37 usize::try_from(self.0).unwrap()
38 }
39}
40
41pub struct FrameTable<'a> {
47 frame_descriptor_ranges: &'a [U32Bytes<LittleEndian>],
48 frame_descriptor_data: &'a [u8],
49
50 frame_descriptor_fp_offsets: &'a [U32Bytes<LittleEndian>],
51
52 progpoint_pcs: &'a [U32Bytes<LittleEndian>],
53 progpoint_descriptor_offsets: &'a [U32Bytes<LittleEndian>],
54 progpoint_descriptor_data: &'a [U32Bytes<LittleEndian>],
55}
56
57impl<'a> FrameTable<'a> {
58 pub fn parse(data: &'a [u8]) -> anyhow::Result<FrameTable<'a>> {
61 let mut data = Bytes(data);
62 let num_frame_descriptors = data
63 .read::<U32Bytes<LittleEndian>>()
64 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor count prefix"))?;
65 let num_frame_descriptors = usize::try_from(num_frame_descriptors.get(LittleEndian))?;
66 let num_progpoint_descriptors = data
67 .read::<U32Bytes<LittleEndian>>()
68 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor count prefix"))?;
69 let num_progpoint_descriptors =
70 usize::try_from(num_progpoint_descriptors.get(LittleEndian))?;
71 let frame_descriptor_pool_length = data
72 .read::<U32Bytes<LittleEndian>>()
73 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor pool length"))?;
74 let frame_descriptor_pool_length =
75 usize::try_from(frame_descriptor_pool_length.get(LittleEndian))?;
76 let progpoint_descriptor_pool_length = data
77 .read::<U32Bytes<LittleEndian>>()
78 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool length"))?;
79 let progpoint_descriptor_pool_length =
80 usize::try_from(progpoint_descriptor_pool_length.get(LittleEndian))?;
81
82 let (frame_descriptor_ranges, data) =
83 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data.0, 2 * num_frame_descriptors)
84 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor ranges slice"))?;
85 let (frame_descriptor_fp_offsets, data) =
86 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_frame_descriptors)
87 .map_err(|_| anyhow::anyhow!("Unable to read frame descriptor FP offset slice"))?;
88
89 let (progpoint_pcs, data) =
90 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_progpoint_descriptors)
91 .map_err(|_| anyhow::anyhow!("Unable to read progpoint PC slice"))?;
92 let (progpoint_descriptor_offsets, data) =
93 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_progpoint_descriptors)
94 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor offset slice"))?;
95
96 let (frame_descriptor_data, data) = data
97 .split_at_checked(frame_descriptor_pool_length)
98 .ok_or_else(|| anyhow::anyhow!("Unable to read frame descriptor pool"))?;
99
100 let (progpoint_descriptor_data, _) = object::slice_from_bytes::<U32Bytes<LittleEndian>>(
101 data,
102 progpoint_descriptor_pool_length,
103 )
104 .map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?;
105
106 Ok(FrameTable {
107 frame_descriptor_ranges,
108 frame_descriptor_data,
109 frame_descriptor_fp_offsets,
110 progpoint_pcs,
111 progpoint_descriptor_offsets,
112 progpoint_descriptor_data,
113 })
114 }
115
116 pub fn frame_descriptor(
119 &self,
120 frame_descriptor: FrameTableDescriptorIndex,
121 ) -> Option<(&'a [u8], u32)> {
122 let range_start = self
123 .frame_descriptor_ranges
124 .get(frame_descriptor.index() * 2)?
125 .get(LittleEndian);
126 let range_end = self
127 .frame_descriptor_ranges
128 .get(frame_descriptor.index() * 2 + 1)?
129 .get(LittleEndian);
130 let range_start = usize::try_from(range_start).unwrap();
131 let range_end = usize::try_from(range_end).unwrap();
132 if range_end < range_start || range_end > self.frame_descriptor_data.len() {
133 return None;
134 }
135 let descriptor = &self.frame_descriptor_data[range_start..range_end];
136 let slot_to_fp_offset = self
137 .frame_descriptor_fp_offsets
138 .get(frame_descriptor.index())?
139 .get(LittleEndian);
140 Some((descriptor, slot_to_fp_offset))
141 }
142
143 pub fn find_program_point(
146 &self,
147 search_pc: u32,
148 search_pos: FrameInstPos,
149 ) -> Option<impl Iterator<Item = (u32, FrameTableDescriptorIndex, FrameStackShape)>> {
150 let key = FrameInstPos::encode(search_pc, search_pos);
151 let index = match self
152 .progpoint_pcs
153 .binary_search_by_key(&key, |entry| entry.get(LittleEndian))
154 {
155 Ok(idx) => idx,
156 Err(idx) if idx > 0 => idx - 1,
157 Err(_) => return None,
158 };
159
160 Some(self.program_point_frame_iter(index))
161 }
162
163 pub fn into_program_points(
166 self,
167 ) -> impl Iterator<
168 Item = (
169 u32,
170 FrameInstPos,
171 Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>,
172 ),
173 > + 'a {
174 self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| {
175 let pc_and_pos = pc.get(LittleEndian);
176 let (pc, pos) = FrameInstPos::decode(pc_and_pos);
177 (
178 pc,
179 pos,
180 self.program_point_frame_iter(i).collect::<Vec<_>>(),
181 )
182 })
183 }
184
185 fn program_point_frame_iter(
186 &self,
187 index: usize,
188 ) -> impl Iterator<Item = (u32, FrameTableDescriptorIndex, FrameStackShape)> {
189 let offset =
190 usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap();
191 let mut data = &self.progpoint_descriptor_data[offset..];
192
193 core::iter::from_fn(move || {
194 if data.len() < 3 {
195 return None;
196 }
197 let wasm_pc = data[0].get(LittleEndian);
198 let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian));
199 let stack_shape = FrameStackShape(data[2].get(LittleEndian));
200 data = &data[3..];
201 let not_last = wasm_pc & 0x8000_0000 != 0;
202 let wasm_pc = wasm_pc & 0x7fff_ffff;
203 if !not_last {
204 data = &[];
205 }
206 Some((wasm_pc, frame_descriptor, stack_shape))
207 })
208 }
209}
210
211#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
250pub enum FrameInstPos {
251 Post,
254 Pre,
257}
258
259impl FrameInstPos {
260 pub(crate) fn encode(pc: u32, pos: FrameInstPos) -> u32 {
261 let lsb = match pos {
262 Self::Post => 0,
263 Self::Pre => 1,
264 };
265 debug_assert!(pc < 0x8000_0000);
266 (pc << 1) | lsb
267 }
268 pub(crate) fn decode(bits: u32) -> (u32, FrameInstPos) {
269 let pos = match bits & 1 {
270 0 => Self::Post,
271 1 => Self::Pre,
272 _ => unreachable!(),
273 };
274 let pc = bits >> 1;
275 (pc, pos)
276 }
277}
278
279#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
281pub struct FrameStateSlotOffset(pub(crate) u32);
282impl FrameStateSlotOffset {
283 #[cfg(feature = "compile")]
284 pub(crate) fn add(self, offset: u32) -> FrameStateSlotOffset {
285 FrameStateSlotOffset(self.0 + offset)
286 }
287
288 pub fn offset(self) -> i32 {
291 i32::try_from(self.0).unwrap()
292 }
293}
294
295#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
297#[allow(missing_docs, reason = "self-describing variants")]
298pub enum FrameValType {
299 I32,
300 I64,
301 F32,
302 F64,
303 V128,
304 AnyRef,
305 FuncRef,
306 ExternRef,
307 ExnRef,
308 ContRef,
309}
310
311impl FrameValType {
312 #[cfg(feature = "compile")]
313 pub(crate) fn storage_size(&self, pointer_size: u32) -> u32 {
314 match self {
315 FrameValType::I32 => 4,
316 FrameValType::I64 => 8,
317 FrameValType::F32 => 4,
318 FrameValType::F64 => 8,
319 FrameValType::V128 => 16,
320 FrameValType::AnyRef | FrameValType::ExternRef | FrameValType::ExnRef => 4,
321 FrameValType::FuncRef => pointer_size,
322 FrameValType::ContRef => 2 * pointer_size,
323 }
324 }
325}
326
327impl From<FrameValType> for u8 {
328 fn from(value: FrameValType) -> u8 {
329 match value {
330 FrameValType::I32 => 0,
331 FrameValType::I64 => 1,
332 FrameValType::F32 => 2,
333 FrameValType::F64 => 3,
334 FrameValType::V128 => 4,
335 FrameValType::AnyRef => 5,
336 FrameValType::FuncRef => 6,
337 FrameValType::ExternRef => 7,
338 FrameValType::ExnRef => 8,
339 FrameValType::ContRef => 9,
340 }
341 }
342}
343
344impl TryFrom<u8> for FrameValType {
345 type Error = anyhow::Error;
346 fn try_from(value: u8) -> anyhow::Result<Self> {
347 match value {
348 0 => Ok(Self::I32),
349 1 => Ok(Self::I64),
350 2 => Ok(Self::F32),
351 3 => Ok(Self::F64),
352 4 => Ok(Self::V128),
353 5 => Ok(Self::AnyRef),
354 6 => Ok(Self::FuncRef),
355 7 => Ok(Self::ExternRef),
356 8 => Ok(Self::ExnRef),
357 9 => Ok(Self::ContRef),
358 _ => Err(anyhow::anyhow!("Invalid type")),
359 }
360 }
361}
362
363pub struct FrameStateSlot<'a> {
368 func_key: FuncKey,
369 local_offsets: &'a [U32Bytes<LittleEndian>],
370 stack_shape_parents: &'a [U32Bytes<LittleEndian>],
371 stack_shape_offsets: &'a [U32Bytes<LittleEndian>],
372 local_types: &'a [u8],
373 stack_shape_types: &'a [u8],
374}
375
376impl<'a> FrameStateSlot<'a> {
377 pub fn parse(descriptor: &'a [u8]) -> anyhow::Result<FrameStateSlot<'a>> {
382 let mut data = Bytes(descriptor);
383 let func_key_namespace = data
384 .read::<U32Bytes<LittleEndian>>()
385 .map_err(|_| anyhow::anyhow!("Unable to read func key namespace"))?
386 .get(LittleEndian);
387 let func_key_index = data
388 .read::<U32Bytes<LittleEndian>>()
389 .map_err(|_| anyhow::anyhow!("Unable to read func key index"))?
390 .get(LittleEndian);
391 let func_key = FuncKey::from_raw_parts(func_key_namespace, func_key_index);
392
393 let num_locals = data
394 .read::<U32Bytes<LittleEndian>>()
395 .map_err(|_| anyhow::anyhow!("Unable to read num_locals"))?
396 .get(LittleEndian);
397 let num_locals = usize::try_from(num_locals)?;
398 let num_stack_shapes = data
399 .read::<U32Bytes<LittleEndian>>()
400 .map_err(|_| anyhow::anyhow!("Unable to read num_stack_shapes"))?
401 .get(LittleEndian);
402 let num_stack_shapes = usize::try_from(num_stack_shapes)?;
403
404 let (local_offsets, data) =
405 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data.0, num_locals)
406 .map_err(|_| anyhow::anyhow!("Unable to read local_offsets slice"))?;
407 let (stack_shape_parents, data) =
408 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_stack_shapes)
409 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_parents slice"))?;
410 let (stack_shape_offsets, data) =
411 object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_stack_shapes)
412 .map_err(|_| anyhow::anyhow!("Unable to read stack_shape_offsets slice"))?;
413 let (local_types, data) = data
414 .split_at_checked(num_locals)
415 .ok_or_else(|| anyhow::anyhow!("Unable to read local_types slice"))?;
416 let (stack_shape_types, _) = data
417 .split_at_checked(num_stack_shapes)
418 .ok_or_else(|| anyhow::anyhow!("Unable to read stack_shape_types slice"))?;
419
420 Ok(FrameStateSlot {
421 func_key,
422 local_offsets,
423 stack_shape_parents,
424 stack_shape_offsets,
425 local_types,
426 stack_shape_types,
427 })
428 }
429
430 pub fn func_key(&self) -> FuncKey {
433 self.func_key
434 }
435
436 pub fn locals(&self) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
438 (0..self.num_locals()).map(|i| self.local(i).unwrap())
439 }
440
441 pub fn local(&self, index: usize) -> Option<(FrameStateSlotOffset, FrameValType)> {
443 let offset = FrameStateSlotOffset(self.local_offsets.get(index)?.get(LittleEndian));
444 let ty = FrameValType::try_from(*self.local_types.get(index)?).expect("Invalid type");
445 Some((offset, ty))
446 }
447
448 pub fn num_locals(&self) -> usize {
450 self.local_offsets.len()
451 }
452
453 pub fn stack(
456 &self,
457 shape: FrameStackShape,
458 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
459 fn unpack_option_shape(shape: FrameStackShape) -> Option<FrameStackShape> {
460 if shape.0 == u32::MAX {
461 None
462 } else {
463 Some(shape)
464 }
465 }
466
467 let mut shape = unpack_option_shape(shape);
468 core::iter::from_fn(move || {
469 shape.map(|s| {
470 let parent = FrameStackShape(self.stack_shape_parents[s.index()].get(LittleEndian));
471 let parent = unpack_option_shape(parent);
472 let offset =
473 FrameStateSlotOffset(self.stack_shape_offsets[s.index()].get(LittleEndian));
474 let ty = FrameValType::try_from(self.stack_shape_types[s.index()])
475 .expect("Invalid type");
476 shape = parent;
477 (offset, ty)
478 })
479 })
480 }
481
482 pub fn stack_and_locals(
484 &self,
485 shape: FrameStackShape,
486 ) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> + '_ {
487 self.locals().chain(self.stack(shape))
488 }
489}