wasm_module: create WasmModule::new for testing

This commit is contained in:
Brian Carroll 2022-11-21 18:24:49 +00:00
parent 2ca74e5070
commit 26cce05bbe
No known key found for this signature in database
GPG key ID: 5C7B2EC4101703C0
5 changed files with 133 additions and 6 deletions

View file

@ -50,6 +50,26 @@ pub struct WasmModule<'a> {
impl<'a> WasmModule<'a> {
pub const WASM_VERSION: u32 = 1;
pub fn new(arena: &'a Bump) -> Self {
WasmModule {
types: TypeSection::new(arena),
import: ImportSection::new(arena),
function: FunctionSection::new(arena),
table: TableSection::new(),
memory: MemorySection::new(arena, 0),
global: GlobalSection::new(arena),
export: ExportSection::new(arena),
start: OpaqueSection::new(),
element: ElementSection::new(arena),
code: CodeSection::new(arena),
data: DataSection::new(arena),
linking: LinkingSection::new(arena),
reloc_code: RelocationSection::new(arena, "reloc.CODE"),
reloc_data: RelocationSection::new(arena, "reloc.DATA"),
names: NameSection::new(arena),
}
}
/// Create entries in the Type and Function sections for a function signature
pub fn add_function_signature(&mut self, signature: Signature<'a>) {
let index = self.types.insert(signature);

View file

@ -158,7 +158,7 @@ pub struct RelocationSection<'a> {
}
impl<'a> RelocationSection<'a> {
fn new(arena: &'a Bump, name: &'a str) -> Self {
pub(crate) fn new(arena: &'a Bump, name: &'a str) -> Self {
RelocationSection {
name,
target_section_index: 0,

View file

@ -1,4 +1,4 @@
use super::serialize::MAX_SIZE_ENCODED_U32;
use super::serialize::{MAX_SIZE_ENCODED_U32, MAX_SIZE_ENCODED_U64};
use bumpalo::collections::vec::Vec;
use bumpalo::Bump;
@ -92,6 +92,43 @@ impl Parse<()> for i32 {
}
}
/// Decode a signed 64-bit integer from the provided buffer in LEB-128 format
/// Return the integer itself and the offset after it ends
fn decode_i64(bytes: &[u8]) -> Result<(i64, usize), ()> {
let mut value = 0;
let mut shift = 0;
for (i, byte) in bytes.iter().take(MAX_SIZE_ENCODED_U64).enumerate() {
value |= ((byte & 0x7f) as i64) << shift;
if (byte & 0x80) == 0 {
let is_negative = byte & 0x40 != 0;
if shift < MAX_SIZE_ENCODED_U64 && is_negative {
value |= -1 << shift;
}
return Ok((value, i + 1));
}
shift += 7;
}
Err(())
}
impl Parse<()> for i64 {
fn parse(_ctx: (), bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
match decode_i64(&bytes[*cursor..]) {
Ok((value, len)) => {
*cursor += len;
Ok(value)
}
Err(()) => Err(ParseError {
offset: *cursor,
message: format!(
"Failed to decode i64 as LEB-128 from bytes: {:2x?}",
&bytes[*cursor..][..MAX_SIZE_ENCODED_U64]
),
}),
}
}
}
impl<'a> Parse<&'a Bump> for &'a str {
fn parse(arena: &'a Bump, bytes: &[u8], cursor: &mut usize) -> Result<Self, ParseError> {
let len = u32::parse((), bytes, cursor)?;

View file

@ -220,6 +220,14 @@ pub struct TypeSection<'a> {
}
impl<'a> TypeSection<'a> {
pub fn new(arena: &'a Bump) -> Self {
TypeSection {
arena,
bytes: Vec::new_in(arena),
offsets: Vec::new_in(arena),
}
}
/// Find a matching signature or insert a new one. Return the index.
pub fn insert(&mut self, signature: Signature<'a>) -> u32 {
let mut sig_bytes = Vec::with_capacity_in(signature.param_types.len() + 4, self.arena);
@ -425,6 +433,12 @@ pub struct ImportSection<'a> {
impl<'a> ImportSection<'a> {
const ID: SectionId = SectionId::Import;
pub fn new(arena: &'a Bump) -> Self {
ImportSection {
imports: Vec::new_in(arena),
}
}
pub fn size(&self) -> usize {
self.imports.iter().map(|imp| imp.size()).sum()
}
@ -488,6 +502,12 @@ pub struct FunctionSection<'a> {
}
impl<'a> FunctionSection<'a> {
pub fn new(arena: &'a Bump) -> Self {
FunctionSection {
signatures: Vec::new_in(arena),
}
}
pub fn add_sig(&mut self, sig_id: u32) {
self.signatures.push(sig_id);
}
@ -590,6 +610,15 @@ pub struct TableSection {
impl TableSection {
const ID: SectionId = SectionId::Table;
pub fn new() -> Self {
TableSection {
function_table: TableType {
ref_type: RefType::Func,
limits: Limits::Min(0),
},
}
}
pub fn size(&self) -> usize {
let section_id_bytes = 1;
let section_length_bytes = 1;
@ -880,6 +909,13 @@ pub struct GlobalSection<'a> {
}
impl<'a> GlobalSection<'a> {
pub fn new(arena: &'a Bump) -> Self {
GlobalSection {
count: 0,
bytes: Vec::new_in(arena),
}
}
pub fn parse_u32_at_index(&self, index: u32) -> Result<u32, ParseError> {
let mut cursor = 0;
for _ in 0..index {
@ -961,6 +997,12 @@ pub struct ExportSection<'a> {
impl<'a> ExportSection<'a> {
const ID: SectionId = SectionId::Export;
pub fn new(arena: &'a Bump) -> Self {
ExportSection {
exports: Vec::new_in(arena),
}
}
pub fn append(&mut self, export: Export<'a>) {
self.exports.push(export);
}
@ -1086,6 +1128,12 @@ pub struct ElementSection<'a> {
impl<'a> ElementSection<'a> {
const ID: SectionId = SectionId::Element;
pub fn new(arena: &'a Bump) -> Self {
ElementSection {
segments: Vec::new_in(arena),
}
}
/// Get a table index for a function (equivalent to a function pointer)
/// The function will be inserted into the table if it's not already there.
/// This index is what the call_indirect instruction expects.
@ -1179,6 +1227,15 @@ pub struct CodeSection<'a> {
}
impl<'a> CodeSection<'a> {
pub fn new(arena: &'a Bump) -> Self {
CodeSection {
function_count: 0,
bytes: Vec::new_in(arena),
function_offsets: Vec::new_in(arena),
dead_import_dummy_count: 0,
}
}
pub fn size(&self) -> usize {
MAX_SIZE_SECTION_HEADER + self.bytes.len()
}
@ -1329,6 +1386,14 @@ pub struct DataSection<'a> {
impl<'a> DataSection<'a> {
const ID: SectionId = SectionId::Data;
pub fn new(arena: &'a Bump) -> Self {
DataSection {
end_addr: 0,
count: 0,
bytes: Vec::new_in(arena),
}
}
pub fn size(&self) -> usize {
MAX_SIZE_SECTION_HEADER + self.bytes.len()
}
@ -1394,6 +1459,10 @@ pub struct OpaqueSection<'a> {
}
impl<'a> OpaqueSection<'a> {
pub fn new() -> Self {
OpaqueSection { bytes: &[] }
}
pub fn size(&self) -> usize {
self.bytes.len()
}
@ -1464,7 +1533,7 @@ impl<'a> NameSection<'a> {
self.function_names.push((index, name));
}
pub fn empty(arena: &'a Bump) -> Self {
pub fn new(arena: &'a Bump) -> Self {
NameSection {
function_names: bumpalo::vec![in arena],
}
@ -1502,12 +1571,12 @@ impl<'a> Parse<&'a Bump> for NameSection<'a> {
// If we're already past the end of the preloaded file then there is no Name section
if *cursor >= module_bytes.len() {
return Ok(Self::empty(arena));
return Ok(Self::new(arena));
}
// Custom section ID
if module_bytes[*cursor] != Self::ID as u8 {
return Ok(Self::empty(arena));
return Ok(Self::new(arena));
}
*cursor += 1;
@ -1520,7 +1589,7 @@ impl<'a> Parse<&'a Bump> for NameSection<'a> {
// This is a different Custom section. This host has no debug info.
// Not a parse error, just an empty section.
*cursor = cursor_start;
return Ok(Self::empty(arena));
return Ok(Self::new(arena));
}
// Find function names subsection

View file

@ -6,6 +6,7 @@ use std::fmt::Debug;
/// In practice, this saves space, since small numbers used more often than large numbers.
/// Of course there is a price for this - an encoded U32 can be up to 5 bytes wide.
pub const MAX_SIZE_ENCODED_U32: usize = 5;
pub const MAX_SIZE_ENCODED_U64: usize = 10;
pub trait Serialize {
fn serialize<T: SerialBuffer>(&self, buffer: &mut T);