wadec 0.0.1

A library for decoding WebAssembly modules.
Documentation
use crate::core::types::valtype::ValType;
use crate::decode::helpers::{DecodeVectorError, ParseExpressionError};
use crate::decode::helpers::{decode_expr, decode_vector};
use crate::decode::integer::{DecodeU32Error, decode_u32};
use crate::decode::types::DecodeValTypeError;
use std::io::Read;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum DecodeCodeSectionError {
    #[error("failed decoding Code section")]
    DecodeVector(#[from] DecodeVectorError<DecodeCodeError>),
}

pub(crate) fn decode_code_section<R: Read + ?Sized>(
    reader: &mut R,
) -> Result<Vec<Code>, DecodeCodeSectionError> {
    Ok(decode_vector(reader, parse_code)?)
}

#[derive(Debug, PartialEq)]
pub(crate) struct Code {
    size: u32,
    pub(crate) locals: Vec<Local>,
    pub(crate) expr: crate::Expr,
}

#[derive(Debug, Error)]
pub enum DecodeCodeError {
    #[error("failed decoding size of function code")]
    DecodeFunctionSize(DecodeU32Error),

    #[error("failed decoding locals vector")]
    DecodeLocalsVector(#[from] DecodeVectorError<DecodeCodeLocalsError>),

    #[error("failed decoding function body expression")]
    DecodeFunctionBody(#[from] ParseExpressionError),

    #[error(
        "Code entry size mismatch: declared {declared_bytes} bytes; consumed {consumed_bytes} (leftover: {leftover_bytes})"
    )]
    EntrySizeMismatch {
        declared_bytes: u32,
        leftover_bytes: u64,
        consumed_bytes: u64,
    },
}

fn parse_code<R: Read + ?Sized>(reader: &mut R) -> Result<Code, DecodeCodeError> {
    let size = decode_u32(reader).map_err(DecodeCodeError::DecodeFunctionSize)?;

    let mut reader = reader.take(size.into());
    let mut expanded_locals: u64 = 0;
    let max_locals = u64::from(u32::MAX);

    let locals = decode_vector(&mut reader, |r| {
        parse_code_local(r, &mut expanded_locals, max_locals)
    })?;

    let expr = decode_expr(&mut reader)?;

    if reader.limit() != 0 {
        return Err(DecodeCodeError::EntrySizeMismatch {
            declared_bytes: size,
            leftover_bytes: reader.limit(),
            consumed_bytes: u64::from(size) - reader.limit(),
        });
    }

    Ok(Code { size, locals, expr })
}

#[derive(Debug, PartialEq)]
pub(crate) struct Local {
    pub(crate) count: u32,
    pub(crate) t: ValType,
}

#[derive(Debug, Error)]
pub enum DecodeCodeLocalsError {
    #[error("failed decoding count of function locals")]
    DecodeLocalsCount(DecodeU32Error),

    #[error("too many locals: expected at most {max_locals}; got {actual_locals}")]
    LocalsCountOutOfBound { max_locals: u64, actual_locals: u64 },

    #[error("failed decoding local Value type")]
    DecodeLocalValType(#[from] DecodeValTypeError),
}

fn parse_code_local<R: Read + ?Sized>(
    reader: &mut R,
    expanded_locals: &mut u64,
    max_locals: u64,
) -> Result<Local, DecodeCodeLocalsError> {
    let count = decode_u32(reader).map_err(DecodeCodeLocalsError::DecodeLocalsCount)?;

    *expanded_locals += u64::from(count);
    if *expanded_locals > max_locals {
        return Err(DecodeCodeLocalsError::LocalsCountOutOfBound {
            max_locals,
            actual_locals: *expanded_locals,
        });
    }

    Ok(Local {
        count,
        t: ValType::decode(reader)?,
    })
}