Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/environ/src/frame_table.rs
3054 views
1
//! Frame-table parser and lookup logic.
2
//!
3
//! This module contains utilities to interpret the `.wasmtime.frame`
4
//! section in a compiled artifact as produced by
5
//! [`crate::compile::FrameTableBuilder`].
6
7
use crate::FuncKey;
8
use alloc::vec::Vec;
9
use object::{Bytes, LittleEndian, U32Bytes};
10
11
/// An index into the table of stack shapes.
12
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
13
pub struct FrameStackShape(pub(crate) u32);
14
impl FrameStackShape {
15
pub(crate) fn index(self) -> usize {
16
usize::try_from(self.0).unwrap()
17
}
18
19
/// Get the raw stack-shape index suitable for serializing into
20
/// metadata.
21
pub fn raw(self) -> u32 {
22
self.0
23
}
24
25
/// Wrap a raw stack shape index (e.g. from debug tags) into a FrameStackShape.
26
pub fn from_raw(index: u32) -> FrameStackShape {
27
FrameStackShape(index)
28
}
29
}
30
31
/// An index to a frame descriptor that can be referenced from a
32
/// program point descriptor.
33
#[derive(Clone, Copy, Debug)]
34
pub struct FrameTableDescriptorIndex(pub(crate) u32);
35
impl FrameTableDescriptorIndex {
36
fn index(self) -> usize {
37
usize::try_from(self.0).unwrap()
38
}
39
}
40
41
/// A parser for a frame-table section.
42
///
43
/// This parser holds slices to the in-memory section data, and is
44
/// cheap to construct: it reads some header fields but does not
45
/// interpret or validate content data until queried.
46
pub 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
breakpoint_pcs: &'a [U32Bytes<LittleEndian>],
57
breakpoint_patch_offsets: &'a [U32Bytes<LittleEndian>],
58
breakpoint_patch_data_ends: &'a [U32Bytes<LittleEndian>],
59
breakpoint_patch_data: &'a [u8],
60
61
original_text: &'a [u8],
62
}
63
64
impl<'a> FrameTable<'a> {
65
/// Parse a frame table section from a byte-slice as produced by
66
/// [`crate::compile::FrameTableBuilder`].
67
pub fn parse(data: &'a [u8], original_text: &'a [u8]) -> anyhow::Result<FrameTable<'a>> {
68
let mut data = Bytes(data);
69
let num_frame_descriptors = data
70
.read::<U32Bytes<LittleEndian>>()
71
.map_err(|_| anyhow::anyhow!("Unable to read frame descriptor count prefix"))?;
72
let num_frame_descriptors = usize::try_from(num_frame_descriptors.get(LittleEndian))?;
73
let num_progpoint_descriptors = data
74
.read::<U32Bytes<LittleEndian>>()
75
.map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor count prefix"))?;
76
let num_progpoint_descriptors =
77
usize::try_from(num_progpoint_descriptors.get(LittleEndian))?;
78
let num_breakpoints = data
79
.read::<U32Bytes<LittleEndian>>()
80
.map_err(|_| anyhow::anyhow!("Unable to read breakpoint count prefix"))?;
81
let num_breakpoints = usize::try_from(num_breakpoints.get(LittleEndian))?;
82
83
let frame_descriptor_pool_length = data
84
.read::<U32Bytes<LittleEndian>>()
85
.map_err(|_| anyhow::anyhow!("Unable to read frame descriptor pool length"))?;
86
let frame_descriptor_pool_length =
87
usize::try_from(frame_descriptor_pool_length.get(LittleEndian))?;
88
let progpoint_descriptor_pool_length = data
89
.read::<U32Bytes<LittleEndian>>()
90
.map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool length"))?;
91
let progpoint_descriptor_pool_length =
92
usize::try_from(progpoint_descriptor_pool_length.get(LittleEndian))?;
93
let breakpoint_patch_pool_length = data
94
.read::<U32Bytes<LittleEndian>>()
95
.map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch pool length"))?;
96
let breakpoint_patch_pool_length =
97
usize::try_from(breakpoint_patch_pool_length.get(LittleEndian))?;
98
99
let (frame_descriptor_ranges, data) =
100
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data.0, 2 * num_frame_descriptors)
101
.map_err(|_| anyhow::anyhow!("Unable to read frame descriptor ranges slice"))?;
102
let (frame_descriptor_fp_offsets, data) =
103
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_frame_descriptors)
104
.map_err(|_| anyhow::anyhow!("Unable to read frame descriptor FP offset slice"))?;
105
106
let (progpoint_pcs, data) =
107
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_progpoint_descriptors)
108
.map_err(|_| anyhow::anyhow!("Unable to read progpoint PC slice"))?;
109
let (progpoint_descriptor_offsets, data) =
110
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_progpoint_descriptors)
111
.map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor offset slice"))?;
112
let (breakpoint_pcs, data) =
113
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_breakpoints)
114
.map_err(|_| anyhow::anyhow!("Unable to read breakpoint PC slice"))?;
115
let (breakpoint_patch_offsets, data) =
116
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_breakpoints)
117
.map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch offsets slice"))?;
118
let (breakpoint_patch_data_ends, data) =
119
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_breakpoints)
120
.map_err(|_| anyhow::anyhow!("Unable to read breakpoint patch data ends slice"))?;
121
122
let (frame_descriptor_data, data) = data
123
.split_at_checked(frame_descriptor_pool_length)
124
.ok_or_else(|| anyhow::anyhow!("Unable to read frame descriptor pool"))?;
125
126
let (progpoint_descriptor_data, data) = object::slice_from_bytes::<U32Bytes<LittleEndian>>(
127
data,
128
progpoint_descriptor_pool_length,
129
)
130
.map_err(|_| anyhow::anyhow!("Unable to read progpoint descriptor pool"))?;
131
132
let (breakpoint_patch_data, _) = data
133
.split_at_checked(breakpoint_patch_pool_length)
134
.ok_or_else(|| anyhow::anyhow!("Unable to read breakpoint patch pool"))?;
135
136
Ok(FrameTable {
137
frame_descriptor_ranges,
138
frame_descriptor_data,
139
frame_descriptor_fp_offsets,
140
progpoint_pcs,
141
progpoint_descriptor_offsets,
142
progpoint_descriptor_data,
143
breakpoint_pcs,
144
breakpoint_patch_offsets,
145
breakpoint_patch_data_ends,
146
breakpoint_patch_data,
147
original_text,
148
})
149
}
150
151
/// Get raw frame descriptor data and slot-to-FP-offset for a
152
/// given frame descriptor.
153
pub fn frame_descriptor(
154
&self,
155
frame_descriptor: FrameTableDescriptorIndex,
156
) -> Option<(&'a [u8], u32)> {
157
let range_start = self
158
.frame_descriptor_ranges
159
.get(frame_descriptor.index() * 2)?
160
.get(LittleEndian);
161
let range_end = self
162
.frame_descriptor_ranges
163
.get(frame_descriptor.index() * 2 + 1)?
164
.get(LittleEndian);
165
let range_start = usize::try_from(range_start).unwrap();
166
let range_end = usize::try_from(range_end).unwrap();
167
if range_end < range_start || range_end > self.frame_descriptor_data.len() {
168
return None;
169
}
170
let descriptor = &self.frame_descriptor_data[range_start..range_end];
171
let slot_to_fp_offset = self
172
.frame_descriptor_fp_offsets
173
.get(frame_descriptor.index())?
174
.get(LittleEndian);
175
Some((descriptor, slot_to_fp_offset))
176
}
177
178
/// Get frames for the program point at the PC upper-bounded by a
179
/// given search PC (offset in text section).
180
pub fn find_program_point(
181
&self,
182
search_pc: u32,
183
search_pos: FrameInstPos,
184
) -> Option<impl Iterator<Item = (u32, FrameTableDescriptorIndex, FrameStackShape)>> {
185
let key = FrameInstPos::encode(search_pc, search_pos);
186
let index = match self
187
.progpoint_pcs
188
.binary_search_by_key(&key, |entry| entry.get(LittleEndian))
189
{
190
Ok(idx) => idx,
191
Err(idx) if idx > 0 => idx - 1,
192
Err(_) => return None,
193
};
194
195
Some(self.program_point_frame_iter(index))
196
}
197
198
/// Get all program point records with iterators over
199
/// corresponding frames for each.
200
pub fn into_program_points(
201
self,
202
) -> impl Iterator<
203
Item = (
204
u32,
205
FrameInstPos,
206
Vec<(u32, FrameTableDescriptorIndex, FrameStackShape)>,
207
),
208
> + 'a {
209
self.progpoint_pcs.iter().enumerate().map(move |(i, pc)| {
210
let pc_and_pos = pc.get(LittleEndian);
211
let (pc, pos) = FrameInstPos::decode(pc_and_pos);
212
(
213
pc,
214
pos,
215
self.program_point_frame_iter(i).collect::<Vec<_>>(),
216
)
217
})
218
}
219
220
fn program_point_frame_iter(
221
&self,
222
index: usize,
223
) -> impl Iterator<Item = (u32, FrameTableDescriptorIndex, FrameStackShape)> {
224
let offset =
225
usize::try_from(self.progpoint_descriptor_offsets[index].get(LittleEndian)).unwrap();
226
let mut data = &self.progpoint_descriptor_data[offset..];
227
228
core::iter::from_fn(move || {
229
if data.len() < 3 {
230
return None;
231
}
232
let wasm_pc = data[0].get(LittleEndian);
233
let frame_descriptor = FrameTableDescriptorIndex(data[1].get(LittleEndian));
234
let stack_shape = FrameStackShape(data[2].get(LittleEndian));
235
data = &data[3..];
236
let not_last = wasm_pc & 0x8000_0000 != 0;
237
let wasm_pc = wasm_pc & 0x7fff_ffff;
238
if !not_last {
239
data = &[];
240
}
241
Some((wasm_pc, frame_descriptor, stack_shape))
242
})
243
}
244
245
/// For a given breakpoint index, return the patch offset in text,
246
/// the patch data, and the original data.
247
fn breakpoint_patch(&self, i: usize) -> FrameTableBreakpointData<'_> {
248
let patch_pool_start = if i == 0 {
249
0
250
} else {
251
self.breakpoint_patch_data_ends[i - 1].get(LittleEndian)
252
};
253
let patch_pool_end = self.breakpoint_patch_data_ends[i].get(LittleEndian);
254
let patch_pool_start = usize::try_from(patch_pool_start).unwrap();
255
let patch_pool_end = usize::try_from(patch_pool_end).unwrap();
256
let len = patch_pool_end - patch_pool_start;
257
let offset = self.breakpoint_patch_offsets[i].get(LittleEndian);
258
let offset = usize::try_from(offset).unwrap();
259
let original_data = &self.original_text[offset..offset + len];
260
FrameTableBreakpointData {
261
offset,
262
enable: &self.breakpoint_patch_data[patch_pool_start..patch_pool_end],
263
disable: original_data,
264
}
265
}
266
267
/// Find a list of breakpoint patches for a given Wasm PC.
268
pub fn lookup_breakpoint_patches_by_pc(
269
&self,
270
pc: u32,
271
) -> impl Iterator<Item = FrameTableBreakpointData<'_>> + '_ {
272
// Find *some* entry with a matching Wasm PC. Note that there
273
// may be multiple entries for one PC.
274
let range = match self
275
.breakpoint_pcs
276
.binary_search_by_key(&pc, |p| p.get(LittleEndian))
277
{
278
Ok(mut i) => {
279
// Scan backward to first index with this PC.
280
while i > 0 && self.breakpoint_pcs[i - 1].get(LittleEndian) == pc {
281
i -= 1;
282
}
283
284
// Scan forward to find the end of the range.
285
let mut end = i;
286
while end < self.breakpoint_pcs.len()
287
&& self.breakpoint_pcs[end].get(LittleEndian) == pc
288
{
289
end += 1;
290
}
291
292
i..end
293
}
294
Err(_) => 0..0,
295
};
296
297
range.map(|i| self.breakpoint_patch(i))
298
}
299
300
/// Return an iterator over all breakpoint patches.
301
///
302
/// Returned tuples are (Wasm PC, breakpoint data).
303
pub fn breakpoint_patches(
304
&self,
305
) -> impl Iterator<Item = (u32, FrameTableBreakpointData<'_>)> + '_ {
306
self.breakpoint_pcs.iter().enumerate().map(|(i, wasm_pc)| {
307
let wasm_pc = wasm_pc.get(LittleEndian);
308
let data = self.breakpoint_patch(i);
309
(wasm_pc, data)
310
})
311
}
312
}
313
314
/// Data describing how to patch code to enable or disable one
315
/// breakpoint.
316
pub struct FrameTableBreakpointData<'a> {
317
/// Offset in the code image's text section.
318
pub offset: usize,
319
/// Code bytes to patch in to enable the breakpoint.
320
pub enable: &'a [u8],
321
/// Code bytes to patch in to disable the breakpoint.
322
pub disable: &'a [u8],
323
}
324
325
/// An instruction position for a program point.
326
///
327
/// We attach debug metadata to a *position* on an offset in the text
328
/// (code) section, either "post" or "pre". The "post" position
329
/// logically comes first, and is associated with the instruction that
330
/// ends at this offset (i.e., the previous instruction). The "pre"
331
/// position comes next, and is associated with the instruction that
332
/// begins at this offset (i.e., the next instruction).
333
///
334
/// We make this distinction because metadata lookups sometimes occur
335
/// with a PC that is after the instruction (e.g., the return address
336
/// after a call instruction), and sometimes at the instruction (e.g.,
337
/// a trapping PC address). The lookup context will know which one to
338
/// use -- e.g., when walking the stack, "pre" for a trapping PC and
339
/// "post" for every frame after that -- so we simply encode it as
340
/// part of the position and allow searching on it.
341
///
342
/// The need for this distinction can be understood by way of an
343
/// example; say we have:
344
///
345
/// ```plain
346
/// call ...
347
/// trapping_store ...
348
/// ```
349
///
350
/// where both instructions have debug metadata. We might look up the
351
/// PC of `trapping_store` once as we walk the stack from within the
352
/// call (we will get this PC because it is the return address) and
353
/// once when `trapping_store` itself traps; and we want different
354
/// metadata in each case.
355
///
356
/// An alternative is to universally attach tags to the end offset of
357
/// an instruction, which allows us to handle return addresses
358
/// naturally but requires traps to adjust their PC. However, this
359
/// requires trap handlers to know the length of the trapping
360
/// instruction, which is not always easy -- in the most general case,
361
/// on variable-length instruction sets, it requires a full
362
/// instruction decoder.
363
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
364
pub enum FrameInstPos {
365
/// The "post" position at an offset attaches to the instruction
366
/// that ends at this offset, i.e., came previously.
367
Post,
368
/// The "pre" position at an offset attaches to the instruction
369
/// that begins at this offset, i.e., comes next.
370
Pre,
371
}
372
373
impl FrameInstPos {
374
pub(crate) fn encode(pc: u32, pos: FrameInstPos) -> u32 {
375
let lsb = match pos {
376
Self::Post => 0,
377
Self::Pre => 1,
378
};
379
debug_assert!(pc < 0x8000_0000);
380
(pc << 1) | lsb
381
}
382
pub(crate) fn decode(bits: u32) -> (u32, FrameInstPos) {
383
let pos = match bits & 1 {
384
0 => Self::Post,
385
1 => Self::Pre,
386
_ => unreachable!(),
387
};
388
let pc = bits >> 1;
389
(pc, pos)
390
}
391
}
392
393
/// An offset into the state slot.
394
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
395
pub struct FrameStateSlotOffset(pub(crate) u32);
396
impl FrameStateSlotOffset {
397
#[cfg(feature = "compile")]
398
pub(crate) fn add(self, offset: u32) -> FrameStateSlotOffset {
399
FrameStateSlotOffset(self.0 + offset)
400
}
401
402
/// Get the offset into the state stackslot, suitable for use in a
403
/// `stack_store`/`stack_load` instruction.
404
pub fn offset(self) -> i32 {
405
i32::try_from(self.0).unwrap()
406
}
407
}
408
409
/// A type stored in a frame.
410
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
411
#[allow(missing_docs, reason = "self-describing variants")]
412
pub enum FrameValType {
413
I32,
414
I64,
415
F32,
416
F64,
417
V128,
418
AnyRef,
419
FuncRef,
420
ExternRef,
421
ExnRef,
422
ContRef,
423
}
424
425
impl FrameValType {
426
#[cfg(feature = "compile")]
427
pub(crate) fn storage_size(&self, pointer_size: u32) -> u32 {
428
match self {
429
FrameValType::I32 => 4,
430
FrameValType::I64 => 8,
431
FrameValType::F32 => 4,
432
FrameValType::F64 => 8,
433
FrameValType::V128 => 16,
434
FrameValType::AnyRef | FrameValType::ExternRef | FrameValType::ExnRef => 4,
435
FrameValType::FuncRef => pointer_size,
436
FrameValType::ContRef => 2 * pointer_size,
437
}
438
}
439
}
440
441
impl From<FrameValType> for u8 {
442
fn from(value: FrameValType) -> u8 {
443
match value {
444
FrameValType::I32 => 0,
445
FrameValType::I64 => 1,
446
FrameValType::F32 => 2,
447
FrameValType::F64 => 3,
448
FrameValType::V128 => 4,
449
FrameValType::AnyRef => 5,
450
FrameValType::FuncRef => 6,
451
FrameValType::ExternRef => 7,
452
FrameValType::ExnRef => 8,
453
FrameValType::ContRef => 9,
454
}
455
}
456
}
457
458
impl TryFrom<u8> for FrameValType {
459
type Error = anyhow::Error;
460
fn try_from(value: u8) -> anyhow::Result<Self> {
461
match value {
462
0 => Ok(Self::I32),
463
1 => Ok(Self::I64),
464
2 => Ok(Self::F32),
465
3 => Ok(Self::F64),
466
4 => Ok(Self::V128),
467
5 => Ok(Self::AnyRef),
468
6 => Ok(Self::FuncRef),
469
7 => Ok(Self::ExternRef),
470
8 => Ok(Self::ExnRef),
471
9 => Ok(Self::ContRef),
472
_ => Err(anyhow::anyhow!("Invalid type")),
473
}
474
}
475
}
476
477
/// Parser for a frame state slot descriptor.
478
///
479
/// This provides the ability to extract offsets and types for locals
480
/// and for the stack given a stack shape.
481
pub struct FrameStateSlot<'a> {
482
func_key: FuncKey,
483
local_offsets: &'a [U32Bytes<LittleEndian>],
484
stack_shape_parents: &'a [U32Bytes<LittleEndian>],
485
stack_shape_offsets: &'a [U32Bytes<LittleEndian>],
486
local_types: &'a [u8],
487
stack_shape_types: &'a [u8],
488
}
489
490
impl<'a> FrameStateSlot<'a> {
491
/// Parse a slot descriptor.
492
///
493
/// This parses the descriptor bytes as provided by
494
/// [`FrameTable::frame_descriptor`].
495
pub fn parse(descriptor: &'a [u8]) -> anyhow::Result<FrameStateSlot<'a>> {
496
let mut data = Bytes(descriptor);
497
let func_key_namespace = data
498
.read::<U32Bytes<LittleEndian>>()
499
.map_err(|_| anyhow::anyhow!("Unable to read func key namespace"))?
500
.get(LittleEndian);
501
let func_key_index = data
502
.read::<U32Bytes<LittleEndian>>()
503
.map_err(|_| anyhow::anyhow!("Unable to read func key index"))?
504
.get(LittleEndian);
505
let func_key = FuncKey::from_raw_parts(func_key_namespace, func_key_index);
506
507
let num_locals = data
508
.read::<U32Bytes<LittleEndian>>()
509
.map_err(|_| anyhow::anyhow!("Unable to read num_locals"))?
510
.get(LittleEndian);
511
let num_locals = usize::try_from(num_locals)?;
512
let num_stack_shapes = data
513
.read::<U32Bytes<LittleEndian>>()
514
.map_err(|_| anyhow::anyhow!("Unable to read num_stack_shapes"))?
515
.get(LittleEndian);
516
let num_stack_shapes = usize::try_from(num_stack_shapes)?;
517
518
let (local_offsets, data) =
519
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data.0, num_locals)
520
.map_err(|_| anyhow::anyhow!("Unable to read local_offsets slice"))?;
521
let (stack_shape_parents, data) =
522
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_stack_shapes)
523
.map_err(|_| anyhow::anyhow!("Unable to read stack_shape_parents slice"))?;
524
let (stack_shape_offsets, data) =
525
object::slice_from_bytes::<U32Bytes<LittleEndian>>(data, num_stack_shapes)
526
.map_err(|_| anyhow::anyhow!("Unable to read stack_shape_offsets slice"))?;
527
let (local_types, data) = data
528
.split_at_checked(num_locals)
529
.ok_or_else(|| anyhow::anyhow!("Unable to read local_types slice"))?;
530
let (stack_shape_types, _) = data
531
.split_at_checked(num_stack_shapes)
532
.ok_or_else(|| anyhow::anyhow!("Unable to read stack_shape_types slice"))?;
533
534
Ok(FrameStateSlot {
535
func_key,
536
local_offsets,
537
stack_shape_parents,
538
stack_shape_offsets,
539
local_types,
540
stack_shape_types,
541
})
542
}
543
544
/// Get the FuncKey for the function that produced this frame
545
/// slot.
546
pub fn func_key(&self) -> FuncKey {
547
self.func_key
548
}
549
550
/// Get the local offsets and types.
551
pub fn locals(&self) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
552
(0..self.num_locals()).map(|i| self.local(i).unwrap())
553
}
554
555
/// Get the type and offset for a given local.
556
pub fn local(&self, index: usize) -> Option<(FrameStateSlotOffset, FrameValType)> {
557
let offset = FrameStateSlotOffset(self.local_offsets.get(index)?.get(LittleEndian));
558
let ty = FrameValType::try_from(*self.local_types.get(index)?).expect("Invalid type");
559
Some((offset, ty))
560
}
561
562
/// Get the number of locals in the frame.
563
pub fn num_locals(&self) -> usize {
564
self.local_offsets.len()
565
}
566
567
/// Get the offsets and types for operand stack values, from top
568
/// of stack (most recently pushed) down.
569
pub fn stack(
570
&self,
571
shape: FrameStackShape,
572
) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> {
573
fn unpack_option_shape(shape: FrameStackShape) -> Option<FrameStackShape> {
574
if shape.0 == u32::MAX {
575
None
576
} else {
577
Some(shape)
578
}
579
}
580
581
let mut shape = unpack_option_shape(shape);
582
core::iter::from_fn(move || {
583
shape.map(|s| {
584
let parent = FrameStackShape(self.stack_shape_parents[s.index()].get(LittleEndian));
585
let parent = unpack_option_shape(parent);
586
let offset =
587
FrameStateSlotOffset(self.stack_shape_offsets[s.index()].get(LittleEndian));
588
let ty = FrameValType::try_from(self.stack_shape_types[s.index()])
589
.expect("Invalid type");
590
shape = parent;
591
(offset, ty)
592
})
593
})
594
}
595
596
/// Returns an iterator over all storage in this frame.
597
pub fn stack_and_locals(
598
&self,
599
shape: FrameStackShape,
600
) -> impl Iterator<Item = (FrameStateSlotOffset, FrameValType)> + '_ {
601
self.locals().chain(self.stack(shape))
602
}
603
}
604
605