Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up the error_mapping code #297

Merged
merged 1 commit into from
Jan 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 128 additions & 146 deletions tests-integration/src/spec/error_mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use {
error::{CmdError, TestFailureError},
runner::{CmdResult, TestResult},
},
wrausmt_common::true_or::TrueOr,
wrausmt_format::{
binary::{
error::{BinaryParseError, BinaryParseErrorKind},
Expand All @@ -21,201 +22,182 @@ use {

pub fn verify_failure<T>(result: CmdResult<T>, failure: &str) -> TestResult<()> {
match result {
Err(CmdError::InvocationError(re)) if matches_runtime_error(failure, &re.kind) => Ok(()),
Err(CmdError::LoaderError(LoaderError::ParseError(ParseError { kind, .. })))
if matches_parse_error(failure, &kind) =>
{
Ok(())
Err(e) => {
matches_cmd_error(failure, &e).true_or(TestFailureError::failure_mismatch(failure, e))
}
Err(CmdError::LoaderError(LoaderError::BinaryParseError(BinaryParseError {
kind,
..
}))) if matches_bin_parse_error(failure, &kind) => Ok(()),
Err(CmdError::LoaderError(LoaderError::ValidationError(ve)))
if matches_validation_error(failure, &ve) =>
{
Ok(())
}
Err(e) => Err(TestFailureError::failure_mismatch(failure, e)),
_ => Err(TestFailureError::failure_missing(failure)),
Ok(_) => Err(TestFailureError::failure_missing(failure))?,
}
}

fn matches_cmd_error(failure: &str, error: &CmdError) -> bool {
match &error {
CmdError::InvocationError(re) => matches_runtime_error(failure, &re.kind),
CmdError::LoaderError(le) => matches_loader_error(failure, le),
_ => false,
}
}

fn matches_loader_error(failure: &str, error: &LoaderError) -> bool {
match &error {
LoaderError::BinaryParseError(bpe) => matches_bin_parse_error(failure, bpe),
LoaderError::ParseError(pe) => matches_parse_error(failure, pe),
LoaderError::ValidationError(ve) => matches_validation_error(failure, ve),
_ => false,
}
}

fn matches_runtime_error(failure: &str, error: &RuntimeErrorKind) -> bool {
match error {
RuntimeErrorKind::Trap(trap_kind) => matches_trap(failure, trap_kind),
RuntimeErrorKind::CallStackExhaustion => failure == "call stack exhausted",
RuntimeErrorKind::ImportMismatch(..) => failure == "incompatible import type",
RuntimeErrorKind::ImportNotFound(..) => failure == "unknown import",
RuntimeErrorKind::Trap(trap_kind) => matches_trap(failure, trap_kind),
_ => false,
}
}

fn matches_trap(failure: &str, trap: &TrapKind) -> bool {
match failure {
"invalid conversion to integer" => matches!(trap, TrapKind::InvalidConversionToInteger),
"out of bounds memory access" => {
matches!(trap, TrapKind::OutOfBoundsMemoryAccess(..))
match trap {
TrapKind::CallIndirectTypeMismatch => failure == "indirect call type mismatch",
TrapKind::IntegerDivideByZero => failure == "integer divide by zero",
TrapKind::IntegerOverflow => failure == "integer overflow",
TrapKind::InvalidConversionToInteger => failure == "invalid conversion to integer",
TrapKind::OutOfBoundsMemoryAccess(..) => failure == "out of bounds memory access",
TrapKind::OutOfBoundsTableAccess(..) => {
["out of bounds table access", "undefined element"].contains(&failure)
}
"out of bounds table access" => matches!(trap, TrapKind::OutOfBoundsTableAccess(..)),
"indirect call type mismatch" => matches!(trap, TrapKind::CallIndirectTypeMismatch),
"integer divide by zero" => matches!(trap, TrapKind::IntegerDivideByZero),
"integer overflow" => matches!(trap, TrapKind::IntegerOverflow),
"unreachable" => matches!(trap, TrapKind::Unreachable),
"undefined element" => matches!(trap, TrapKind::OutOfBoundsTableAccess(..)),
"uninitialized element" => matches!(trap, TrapKind::UninitializedElement),
"uninitialized element 2" => matches!(trap, TrapKind::UninitializedElement),
TrapKind::Unreachable => failure == "unreachable",
TrapKind::UninitializedElement => failure.starts_with("uninitialized element"),
_ => false,
}
}

fn matches_bin_parse_error(failure: &str, bin_parse_err: &BinaryParseErrorKind) -> bool {
match failure {
fn matches_bin_parse_error(failure: &str, bin_parse_err: &BinaryParseError) -> bool {
match &bin_parse_err.kind {
// TODO - clean up code to simplify to 1:1 mapping.
"integer too large" => {
matches!(
bin_parse_err,
BinaryParseErrorKind::LEB128Error(LEB128Error::Overflow(_))
) || matches!(bin_parse_err, BinaryParseErrorKind::InvalidBoolValue(_))
BinaryParseErrorKind::CodeTooLong => {
["section size mismatch", "END opcode expected"].contains(&failure)
}
// TODO - remove the need for InvalidFuncType
"integer representation too long" => matches!(
bin_parse_err,
BinaryParseErrorKind::InvalidBoolValue(_)
| BinaryParseErrorKind::LEB128Error(LEB128Error::Unterminated(_))
| BinaryParseErrorKind::InvalidFuncType(_)
),
"malformed mutability" => {
matches!(bin_parse_err, BinaryParseErrorKind::InvalidBoolValue(_))
}
"magic header not detected" => {
matches!(bin_parse_err, BinaryParseErrorKind::IncorrectMagic(_))
}
"unknown binary version" => {
matches!(bin_parse_err, BinaryParseErrorKind::IncorrectVersion(_))
}
"malformed UTF-8 encoding" => matches!(bin_parse_err, BinaryParseErrorKind::Utf8Error(_e)),
"malformed section id" => {
matches!(bin_parse_err, BinaryParseErrorKind::MalformedSectionId(_))
}
"malformed import kind" => {
matches!(bin_parse_err, BinaryParseErrorKind::MalformedImportKind(_))
}
"malformed reference type" => {
matches!(bin_parse_err, BinaryParseErrorKind::MalformedRefType(_))
}
"section size mismatch" => matches!(
bin_parse_err,
BinaryParseErrorKind::SectionTooShort
| BinaryParseErrorKind::SectionTooLong
| BinaryParseErrorKind::CodeTooLong
),
// TODO - remove the need for UnxpectedEndOfSectionOrFunction
"unexpected end" => matches!(
bin_parse_err,
BinaryParseErrorKind::UnexpectedEnd
| BinaryParseErrorKind::UnxpectedEndOfSectionOrFunction
),

"unexpected end of section or function" | "length out of bounds" => matches!(
bin_parse_err,
BinaryParseErrorKind::UnxpectedEndOfSectionOrFunction
| BinaryParseErrorKind::UnexpectedEnd
),
"too many locals" => matches!(bin_parse_err, BinaryParseErrorKind::TooManyLocals),
"END opcode expected" => matches!(bin_parse_err, BinaryParseErrorKind::CodeTooLong),
"illegal opcode" => matches!(bin_parse_err, BinaryParseErrorKind::InvalidOpcode(_)),
"function and code section have inconsistent lengths" => {
matches!(bin_parse_err, BinaryParseErrorKind::FuncSizeMismatch)
}
"data count and data section have inconsistent lengths" => {
matches!(bin_parse_err, BinaryParseErrorKind::DataCountMismatch)
BinaryParseErrorKind::DataCountMismatch => {
failure == "data count and data section have inconsistent lengths"
}
"data count section required" => {
matches!(bin_parse_err, BinaryParseErrorKind::DataCountMissing)
BinaryParseErrorKind::DataCountMissing => failure == "data count section required",
BinaryParseErrorKind::FuncSizeMismatch => {
failure == "function and code section have inconsistent lengths"
}
"zero byte expected" => matches!(bin_parse_err, BinaryParseErrorKind::ZeroByteExpected),
"unexpected content after last section" => {
matches!(
bin_parse_err,
BinaryParseErrorKind::UnexpectedContentAfterEnd
)
BinaryParseErrorKind::InvalidBoolValue(_) => [
"integer representation too long",
"integer too large",
"malformed mutability",
]
.contains(&failure),
BinaryParseErrorKind::IncorrectMagic(_) => failure == "magic header not detected",
BinaryParseErrorKind::IncorrectVersion(_) => failure == "unknown binary version",
// TODO -- remove the need for matching InvalidFuncType
BinaryParseErrorKind::InvalidFuncType(_) => failure == "integer represetation too long",
BinaryParseErrorKind::InvalidOpcode(_) => failure == "illegal opcode",
BinaryParseErrorKind::LEB128Error(le) => matches_leb_error(failure, le),
BinaryParseErrorKind::MalformedImportKind(_) => failure == "malformed import kind",
BinaryParseErrorKind::MalformedRefType(_) => failure == "malformed reference type",
BinaryParseErrorKind::MalformedSectionId(_) => failure == "malformed section id",
BinaryParseErrorKind::SectionTooLong => failure == "section size mismatch",
BinaryParseErrorKind::SectionTooShort => failure == "section size mismatch",
BinaryParseErrorKind::TooManyLocals => failure == "too many locals",
BinaryParseErrorKind::UnexpectedContentAfterEnd => {
failure == "unexpected content after last section"
}
BinaryParseErrorKind::UnexpectedEnd => [
"unexpected end",
"unexpected end of section or function",
"length out of bounds",
]
.contains(&failure),
// TODO - remove "unexpected end" from this.
BinaryParseErrorKind::UnxpectedEndOfSectionOrFunction => [
"unexpected end",
"unexpected end of section or function",
"length out of bounds",
]
.contains(&failure),
BinaryParseErrorKind::Utf8Error(_e) => failure == "malformed UTF-8 encoding",
BinaryParseErrorKind::ZeroByteExpected => failure == "zero byte expected",
_ => false,
}
}

fn matches_parse_error(failure: &str, parse_err: &ParseErrorKind) -> bool {
if let ParseErrorKind::ResolveError(re) = parse_err {
return matches_resolve_error(failure, re);
fn matches_leb_error(failure: &str, error: &LEB128Error) -> bool {
match &error {
LEB128Error::Unterminated(_) => failure == "integer representation too long",
LEB128Error::Overflow(_) => failure == "integer too large",
_ => false,
}
}

match failure {
"alignment" => matches!(parse_err, ParseErrorKind::InvalidAlignment(_)),
"i32 constant" => matches!(parse_err, ParseErrorKind::ConstantOutOfRange),
"i32 constant out of range" => matches!(parse_err, ParseErrorKind::ConstantOutOfRange),
"constant out of range" => matches!(parse_err, ParseErrorKind::ConstantOutOfRange),
"unknown label" => matches!(parse_err, ParseErrorKind::ResolveError(_)),
"unexpected token" => matches!(
parse_err,
// This should really only be unexpected token, but blocks end up parsing
// out-of-order param/result/type as instructions. One approach to improve this
// could be to define all of the non-instruction keywords as their own tokens.
ParseErrorKind::UnexpectedToken(_) | ParseErrorKind::UnrecognizedInstruction(_)
),
_ if failure.starts_with("unknown operator") => matches!(
parse_err,
// TODO - remove the need for UnexpectedToken
ParseErrorKind::UnexpectedToken(_) | ParseErrorKind::UnrecognizedInstruction(_)
),
"malformed UTF-8 encoding" => matches!(parse_err, ParseErrorKind::Utf8Error(_)),
fn matches_parse_error(failure: &str, parse_err: &ParseError) -> bool {
match &parse_err.kind {
ParseErrorKind::InvalidAlignment(_) => failure == "alignment",
ParseErrorKind::ConstantOutOfRange => [
"i32 constant",
"i32 constant out of range",
"constant out of range",
]
.contains(&failure),
ParseErrorKind::ResolveError(re) => matches_resolve_error(failure, re),
// This should really only be unexpected token, but blocks end up parsing
// out-of-order param/result/type as instructions. One approach to improve this
// could be to define all of the non-instruction keywords as their own tokens.
ParseErrorKind::UnexpectedToken(_) | ParseErrorKind::UnrecognizedInstruction(_) => {
failure == "unexpected token" || failure.starts_with("unknown operator")
}
ParseErrorKind::Utf8Error(_) => failure == "malformed UTF-8 encoding",

"mismatching label" => matches!(parse_err, ParseErrorKind::LabelMismatch(_, _)),
ParseErrorKind::LabelMismatch(..) => failure == "mismatching label",
_ => false,
}
}

fn matches_resolve_error(failure: &str, err: &ResolveError) -> bool {
match failure {
"inline function type" => matches!(err, ResolveError::DuplicateTypeIndex(_)),
"import after function" => matches!(err, ResolveError::ImportAfterFunction),
"import after global" => matches!(err, ResolveError::ImportAfterGlobal),
"import after table" => matches!(err, ResolveError::ImportAfterTable),
"import after memory" => matches!(err, ResolveError::ImportAfterMemory),
"duplicate func" => matches!(err, ResolveError::DuplicateFunc(_)),
"duplicate table" => matches!(err, ResolveError::DuplicateTable(_)),
"duplicate memory" => matches!(err, ResolveError::DuplicateMem(_)),
"duplicate global" => matches!(err, ResolveError::DuplicateGlobal(_)),
"duplicate elem" => matches!(err, ResolveError::DuplicateElem(_)),
"duplicate data" => matches!(err, ResolveError::DuplicateData(_)),
"duplicate local" => matches!(err, ResolveError::DuplicateLocal(_)),
"multiple start sections" => matches!(err, ResolveError::MultipleStartSections),
"unknown label" => matches!(err, ResolveError::UnresolvedLabel(_)),
"unknown type" => matches!(err, ResolveError::UnresolvedType(_)),
match err {
ResolveError::DuplicateData(_) => failure == "duplicate data",
ResolveError::DuplicateElem(_) => failure == "duplicate elem",
ResolveError::DuplicateFunc(_) => failure == "duplicate func",
ResolveError::DuplicateGlobal(_) => failure == "duplicate global",
ResolveError::DuplicateMem(_) => failure == "duplicate memory",
ResolveError::DuplicateTable(_) => failure == "duplicate table",
ResolveError::DuplicateTypeIndex(_) => failure == "inline function type",
ResolveError::DuplicateLocal(_) => failure == "duplicate local",
ResolveError::ImportAfterFunction => failure == "import after function",
ResolveError::ImportAfterGlobal => failure == "import after global",
ResolveError::ImportAfterMemory => failure == "import after memory",
ResolveError::ImportAfterTable => failure == "import after table",
ResolveError::MultipleStartSections => failure == "multiple start sections",
ResolveError::UnresolvedLabel(_) => failure == "unknown label",
ResolveError::UnresolvedType(_) => failure == "unknown type",
_ => false,
}
}

fn matches_validation_error(failure: &str, err: &ValidationError) -> bool {
match err.kind() {
ValidationErrorKind::BreakTypeMismatch => ["type mismatch"].contains(&failure),
ValidationErrorKind::ExpectedNum { .. } => ["type mismatch"].contains(&failure),
ValidationErrorKind::ExpectedRef { .. } => ["type mismatch"].contains(&failure),
ValidationErrorKind::InvalidConstantGlobal => ["unknown global"].contains(&failure),
ValidationErrorKind::ImmutableGlobal => ["global is immutable"].contains(&failure),
ValidationErrorKind::TypeMismatch { .. } => ["type mismatch"].contains(&failure),
ValidationErrorKind::BreakTypeMismatch => failure == "type mismatch",
ValidationErrorKind::ExpectedNum { .. } => failure == "type mismatch",
ValidationErrorKind::ExpectedRef { .. } => failure == "type mismatch",
ValidationErrorKind::InvalidConstantGlobal => failure == "unknown global",
ValidationErrorKind::ImmutableGlobal => failure == "global is immutable",
ValidationErrorKind::TypeMismatch { .. } => failure == "type mismatch",
ValidationErrorKind::UnknownData => failure.starts_with("unknown data segment"),
ValidationErrorKind::UnknownElem => failure.starts_with("unknown elem segment"),
ValidationErrorKind::UnknownFunc => failure.starts_with("unknown func"),
ValidationErrorKind::UnknownGlobal => failure.starts_with("unknown global"),
ValidationErrorKind::UnknownLabel => ["unknown label"].contains(&failure),
ValidationErrorKind::UnknownLocal { .. } => ["unknown local"].contains(&failure),
ValidationErrorKind::UnknownLabel => failure == "unknown label",
ValidationErrorKind::UnknownLocal { .. } => failure == "unknown local",
ValidationErrorKind::UnknownMemory => failure.starts_with("unknown memory"),
ValidationErrorKind::UnknownTable => failure.starts_with("unknown table"),
ValidationErrorKind::UnknownType => ["unknown type"].contains(&failure),
ValidationErrorKind::UnusedValues => ["type mismatch"].contains(&failure),
ValidationErrorKind::ValStackUnderflow => ["type mismatch"].contains(&failure),
ValidationErrorKind::WrongTableType => ["wrong table type"].contains(&failure),
ValidationErrorKind::UnknownType => failure == "unknown type",
ValidationErrorKind::UnusedValues => failure == "type mismatch",
ValidationErrorKind::ValStackUnderflow => failure == "type mismatch",
ValidationErrorKind::WrongTableType => failure == "wrong table type",
_ => false,
}
}
Loading