use crate::{
FrameInstPos, FrameStackShape, FrameStateSlotOffset, FrameTableDescriptorIndex, FrameValType,
FuncKey, WasmHeapTopType, WasmValType, prelude::*,
};
use object::{LittleEndian, U32Bytes};
use std::collections::{HashMap, hash_map::Entry};
pub struct FrameStateSlotBuilder {
func_key: FuncKey,
pointer_size: u32,
locals: Vec<(FrameValType, FrameStateSlotOffset)>,
stacks: Vec<(Option<FrameStackShape>, FrameValType, FrameStateSlotOffset)>,
stacks_dedup:
HashMap<(Option<FrameStackShape>, FrameValType, FrameStateSlotOffset), FrameStackShape>,
vmctx_size: u32,
locals_size: u32,
slot_size: u32,
}
impl From<WasmValType> for FrameValType {
fn from(ty: WasmValType) -> FrameValType {
match ty {
WasmValType::I32 => FrameValType::I32,
WasmValType::I64 => FrameValType::I64,
WasmValType::F32 => FrameValType::F32,
WasmValType::F64 => FrameValType::F64,
WasmValType::V128 => FrameValType::V128,
WasmValType::Ref(r) => match r.heap_type.top() {
WasmHeapTopType::Any => FrameValType::AnyRef,
WasmHeapTopType::Extern => FrameValType::ExternRef,
WasmHeapTopType::Func => FrameValType::FuncRef,
WasmHeapTopType::Exn => FrameValType::ExnRef,
WasmHeapTopType::Cont => FrameValType::ContRef,
},
}
}
}
impl FrameStateSlotBuilder {
pub fn new(func_key: FuncKey, pointer_size: u32) -> FrameStateSlotBuilder {
FrameStateSlotBuilder {
func_key,
pointer_size,
locals: vec![],
stacks: vec![],
stacks_dedup: HashMap::new(),
vmctx_size: pointer_size,
locals_size: 0,
slot_size: pointer_size,
}
}
pub fn add_local(&mut self, ty: FrameValType) -> FrameStateSlotOffset {
let offset = FrameStateSlotOffset(self.vmctx_size + self.locals_size);
let size = ty.storage_size(self.pointer_size);
self.locals_size += size;
self.slot_size += size;
self.locals.push((ty, offset));
offset
}
pub fn local_offset(&self, local: u32) -> FrameStateSlotOffset {
let index = usize::try_from(local).unwrap();
self.locals[index].1
}
pub fn push_stack(
&mut self,
parent: Option<FrameStackShape>,
ty: FrameValType,
) -> (FrameStackShape, FrameStateSlotOffset) {
let offset = parent
.map(|parent| {
let (_, ty, offset) = self.stacks[parent.index()];
offset.add(ty.storage_size(self.pointer_size))
})
.unwrap_or(FrameStateSlotOffset(self.vmctx_size + self.locals_size));
self.slot_size = core::cmp::max(
self.slot_size,
offset.0 + ty.storage_size(self.pointer_size),
);
let shape = match self.stacks_dedup.entry((parent, ty, offset)) {
Entry::Occupied(o) => *o.get(),
Entry::Vacant(v) => {
let shape = FrameStackShape(u32::try_from(self.stacks.len()).unwrap());
self.stacks.push((parent, ty, offset));
*v.insert(shape)
}
};
(shape, offset)
}
pub fn stack_last_offset(&self, shape: FrameStackShape) -> FrameStateSlotOffset {
self.stacks[shape.index()].2
}
pub fn serialize(&self) -> Vec<u8> {
let mut buffer = vec![];
let (func_key_namespace, func_key_index) = self.func_key.into_parts();
buffer.extend_from_slice(&u32::to_le_bytes(func_key_namespace.into_raw()));
buffer.extend_from_slice(&u32::to_le_bytes(func_key_index.into_raw()));
buffer.extend_from_slice(&u32::to_le_bytes(u32::try_from(self.locals.len()).unwrap()));
buffer.extend_from_slice(&u32::to_le_bytes(u32::try_from(self.stacks.len()).unwrap()));
for (_, offset) in &self.locals {
buffer.extend_from_slice(&u32::to_le_bytes(offset.0));
}
for (parent, _, _) in &self.stacks {
let parent = parent.map(|p| p.0).unwrap_or(u32::MAX);
buffer.extend_from_slice(&u32::to_le_bytes(parent));
}
for (_, _, offset) in &self.stacks {
buffer.extend_from_slice(&u32::to_le_bytes(offset.0));
}
for (ty, _) in &self.locals {
buffer.push(*ty as u8);
}
for (_, ty, _) in &self.stacks {
buffer.push(*ty as u8);
}
buffer
}
pub fn size(&self) -> u32 {
self.slot_size
}
}
#[derive(Default)]
pub struct FrameTableBuilder {
frame_descriptor_ranges: Vec<U32Bytes<LittleEndian>>,
frame_descriptor_data: Vec<u8>,
frame_descriptor_fp_offsets: Vec<U32Bytes<LittleEndian>>,
progpoint_pcs: Vec<U32Bytes<LittleEndian>>,
progpoint_descriptor_offsets: Vec<U32Bytes<LittleEndian>>,
progpoint_descriptor_data: Vec<U32Bytes<LittleEndian>>,
breakpoint_pcs: Vec<U32Bytes<LittleEndian>>,
breakpoint_patch_offsets: Vec<U32Bytes<LittleEndian>>,
breakpoint_patch_data_ends: Vec<U32Bytes<LittleEndian>>,
breakpoint_patch_data: Vec<u8>,
}
impl FrameTableBuilder {
pub fn add_frame_descriptor(
&mut self,
slot_to_fp_offset: u32,
data: &[u8],
) -> FrameTableDescriptorIndex {
let start = u32::try_from(self.frame_descriptor_data.len()).unwrap();
self.frame_descriptor_data.extend(data.iter().cloned());
let end = u32::try_from(self.frame_descriptor_data.len()).unwrap();
let index = FrameTableDescriptorIndex(
u32::try_from(self.frame_descriptor_fp_offsets.len()).unwrap(),
);
self.frame_descriptor_fp_offsets
.push(U32Bytes::new(LittleEndian, slot_to_fp_offset));
self.frame_descriptor_ranges
.push(U32Bytes::new(LittleEndian, start));
self.frame_descriptor_ranges
.push(U32Bytes::new(LittleEndian, end));
index
}
pub fn add_program_point(
&mut self,
native_pc: u32,
pos: FrameInstPos,
frames: &[(u32, FrameTableDescriptorIndex, FrameStackShape)],
) {
let pc_and_pos = FrameInstPos::encode(native_pc, pos);
while let Some(last) = self.progpoint_pcs.last()
&& last.get(LittleEndian) == pc_and_pos
{
self.progpoint_pcs.pop();
self.progpoint_descriptor_offsets.pop();
self.progpoint_descriptor_data
.truncate(self.progpoint_descriptor_data.len() - 3);
}
let start = u32::try_from(self.progpoint_descriptor_data.len()).unwrap();
self.progpoint_pcs
.push(U32Bytes::new(LittleEndian, pc_and_pos));
self.progpoint_descriptor_offsets
.push(U32Bytes::new(LittleEndian, start));
for (i, &(wasm_pc, frame_descriptor, stack_shape)) in frames.iter().enumerate() {
debug_assert!(wasm_pc < 0x8000_0000);
let not_last = i < (frames.len() - 1);
let wasm_pc = wasm_pc | if not_last { 0x8000_0000 } else { 0 };
self.progpoint_descriptor_data
.push(U32Bytes::new(LittleEndian, wasm_pc));
self.progpoint_descriptor_data
.push(U32Bytes::new(LittleEndian, frame_descriptor.0));
self.progpoint_descriptor_data
.push(U32Bytes::new(LittleEndian, stack_shape.0));
}
}
pub fn add_breakpoint_patch(&mut self, wasm_pc: u32, patch_start_native_pc: u32, patch: &[u8]) {
self.breakpoint_pcs
.push(U32Bytes::new(LittleEndian, wasm_pc));
self.breakpoint_patch_offsets
.push(U32Bytes::new(LittleEndian, patch_start_native_pc));
self.breakpoint_patch_data.extend(patch.iter().cloned());
let end = u32::try_from(self.breakpoint_patch_data.len()).unwrap();
self.breakpoint_patch_data_ends
.push(U32Bytes::new(LittleEndian, end));
}
pub fn serialize<F: FnMut(&[u8])>(&mut self, mut f: F) {
while self.frame_descriptor_data.len() & 3 != 0 {
self.frame_descriptor_data.push(0);
}
let num_frame_descriptors = u32::try_from(self.frame_descriptor_fp_offsets.len()).unwrap();
f(&num_frame_descriptors.to_le_bytes());
let num_prog_points = u32::try_from(self.progpoint_pcs.len()).unwrap();
f(&num_prog_points.to_le_bytes());
let num_breakpoints = u32::try_from(self.breakpoint_pcs.len()).unwrap();
f(&num_breakpoints.to_le_bytes());
let frame_descriptor_pool_length = u32::try_from(self.frame_descriptor_data.len()).unwrap();
f(&frame_descriptor_pool_length.to_le_bytes());
let progpoint_descriptor_pool_length =
u32::try_from(self.progpoint_descriptor_data.len()).unwrap();
f(&progpoint_descriptor_pool_length.to_le_bytes());
let breakpoint_patch_pool_length = u32::try_from(self.breakpoint_patch_data.len()).unwrap();
f(&breakpoint_patch_pool_length.to_le_bytes());
f(object::bytes_of_slice(&self.frame_descriptor_ranges));
f(object::bytes_of_slice(&self.frame_descriptor_fp_offsets));
f(object::bytes_of_slice(&self.progpoint_pcs));
f(object::bytes_of_slice(&self.progpoint_descriptor_offsets));
f(object::bytes_of_slice(&self.breakpoint_pcs));
f(object::bytes_of_slice(&self.breakpoint_patch_offsets));
f(object::bytes_of_slice(&self.breakpoint_patch_data_ends));
f(&self.frame_descriptor_data);
f(object::bytes_of_slice(&self.progpoint_descriptor_data));
f(&self.breakpoint_patch_data);
}
}