Skip to content

Commit 397bdd4

Browse files
wip
1 parent 91cb681 commit 397bdd4

File tree

2 files changed

+152
-10
lines changed

2 files changed

+152
-10
lines changed

tests/specification/reports.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::any::Any;
22

3-
use wasm::{RuntimeError, TrapError};
3+
use wasm::{RuntimeError, TrapError, ValidationError};
44

55
use super::test_errors::AssertEqError;
66

@@ -44,6 +44,8 @@ pub enum WastError {
4444
Wast(#[from] wast::Error),
4545
#[error("Runtime error not represented in WAST")]
4646
UnrepresentedRuntimeError,
47+
#[error("Validation error not represented in WAST: {0}")]
48+
UnrepresentedValidationError(ValidationError),
4749
#[error("{0}")]
4850
Io(#[from] std::io::Error),
4951
#[error("Some directive either referenced a non-existing Wasm module by its id or it did not specify an id at all and there was no other module defined prior to this directive.")]
@@ -54,6 +56,11 @@ pub enum WastError {
5456
UnknownGlobalReferenced,
5557
#[error("Module failed to link")]
5658
FailedToLink,
59+
#[error("Got unexpected validation error {actual}, expected {expected}")]
60+
UnexpectedValidationError {
61+
expected: String,
62+
actual: ValidationError,
63+
},
5764
}
5865

5966
impl From<wasm::ValidationError> for WastError {

tests/specification/run.rs

Lines changed: 144 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use wasm::RuntimeError;
2020
use wasm::TableType;
2121
use wasm::TrapError;
2222
use wasm::ValType;
23+
use wasm::ValidationError;
2324
use wast::core::WastArgCore;
2425
use wast::core::WastRetCore;
2526
use wast::{QuoteWat, WastArg, WastDirective, WastRet, Wat};
@@ -53,7 +54,110 @@ pub fn error_to_wasm_testsuite_string(runtime_error: &RuntimeError) -> Result<St
5354
RuntimeError::HostFunctionSignatureMismatch => Ok("host function signature mismatch"),
5455
_ => Err(WastError::UnrepresentedRuntimeError),
5556
}
56-
.map(ToOwned::to_owned)
57+
.map(str::to_owned)
58+
}
59+
60+
pub fn validation_error_to_wasm_testsuite_string(
61+
validation_error: &ValidationError,
62+
) -> Result<String, WastError> {
63+
std::panic::catch_unwind(|| {
64+
match validation_error {
65+
// _ => Err(WastError::UnrepresentedValidationError(
66+
// validation_error.clone(),
67+
// )),
68+
ValidationError::InvalidMagic => Ok("magic header not detected"),
69+
ValidationError::InvalidBinaryFormatVersion => Ok("unknown binary version"),
70+
ValidationError::Eof => todo!(),
71+
ValidationError::MalformedUtf8(_) => Ok("malformed UTF-8 encoding"),
72+
ValidationError::MalformedSectionTypeDiscriminator(_) => Ok("malformed section id"),
73+
ValidationError::MalformedNumTypeDiscriminator(_) => Ok("malformed number type"),
74+
ValidationError::MalformedVecTypeDiscriminator(_) => Ok("malformed vector type"),
75+
ValidationError::MalformedFuncTypeDiscriminator(_) => Ok("malformed function type"),
76+
ValidationError::MalformedRefTypeDiscriminator(_) => Ok("malformed reference type"),
77+
ValidationError::MalformedValType => todo!(),
78+
ValidationError::MalformedExportDescDiscriminator(_) => Ok("malformed export kind"),
79+
ValidationError::MalformedImportDescDiscriminator(_) => Ok("malformed import kind"),
80+
ValidationError::MalformedLimitsDiscriminator(_) => todo!(),
81+
ValidationError::MalformedLimitsMinLargerThanMax { .. } => todo!(),
82+
ValidationError::MalformedMutDiscriminator(_) => Ok("malformed mutability"),
83+
ValidationError::MalformedVariableLengthInteger => todo!(),
84+
ValidationError::MalformedElemKindDiscriminator(_) => Ok("malformed element kind"),
85+
ValidationError::InvalidTypeIdx(_) => Ok("unknown type"),
86+
ValidationError::InvalidFuncIdx(_) => Ok("unknown function"),
87+
ValidationError::InvalidTableIdx(_) => Ok("unknown table"),
88+
ValidationError::InvalidMemIdx(_) => Ok("unknown memory"),
89+
ValidationError::InvalidGlobalIdx(_) => Ok("unknown global"),
90+
ValidationError::InvalidElemIdx(_) => Ok("unknown elem segment"),
91+
ValidationError::InvalidDataIdx(_) => Ok("unknown data segment"),
92+
ValidationError::InvalidLocalIdx(_) => Ok("unknown local"),
93+
ValidationError::InvalidLabelIdx(_) => Ok("unknown label"),
94+
ValidationError::InvalidLaneIdx(_) => {
95+
todo!("this can be \"malformed lane index\" or \"invalid lane index\"")
96+
}
97+
ValidationError::UnexpectedContentAfterLastSection => {
98+
Ok("unexpected content after last section")
99+
}
100+
ValidationError::InvalidCustomSectionLength => todo!(),
101+
ValidationError::ExprMissingEnd => todo!(),
102+
ValidationError::InvalidInstr(byte) => return Ok(format!("illegal opcode {byte:02x}")),
103+
ValidationError::InvalidMultiByteInstr(byte, i) => {
104+
return Ok(format!("illegal opcode {byte:02x} {i:02x}"))
105+
}
106+
ValidationError::EndInvalidValueStack => todo!(),
107+
ValidationError::InvalidValidationStackValType(_) => todo!(),
108+
ValidationError::ExpectedAnOperand => todo!(),
109+
ValidationError::MemoryTooLarge => Ok("memory size must be at most 65536 pages (4GiB)"),
110+
ValidationError::MutationOfConstGlobal => Ok("global is immutable"),
111+
ValidationError::ErroneousAlignment { .. } => {
112+
Ok("alignment must not be larger than natural")
113+
}
114+
ValidationError::ValidationCtrlStackEmpty => todo!(),
115+
ValidationError::ElseWithoutMatchingIf => todo!(),
116+
ValidationError::IfWithoutMatchingElse => todo!(),
117+
ValidationError::MismatchedRefTypesDuringTableInit { .. } => todo!(),
118+
ValidationError::MismatchedRefTypesDuringTableCopy { .. } => todo!(),
119+
ValidationError::MismatchedRefTypesOnValidationStack { .. } => todo!(),
120+
ValidationError::IndirectCallToNonFuncRefTable(_) => todo!(),
121+
ValidationError::ExpectedReferenceTypeOnStack(_) => todo!(),
122+
ValidationError::ReferencingAnUnreferencedFunction(_) => todo!(),
123+
ValidationError::InvalidSelectTypeVectorLength(_) => todo!(),
124+
ValidationError::TooManyLocals(_) => Ok("too many locals"),
125+
ValidationError::DuplicateExportName => Ok("duplicate export name"),
126+
ValidationError::UnsupportedMultipleMemoriesProposal => {
127+
Ok("multiple memories are not allowed (yet)")
128+
}
129+
ValidationError::CodeExprHasTrailingInstructions => todo!(),
130+
ValidationError::FunctionAndCodeSectionsHaveDifferentLengths => {
131+
Ok("function and code section have inconsistent lengths")
132+
}
133+
ValidationError::DataCountAndDataSectionsLengthAreDifferent => {
134+
Ok("data count and data section have inconsistent lengths")
135+
}
136+
ValidationError::InvalidStartFunctionSignature => todo!(),
137+
ValidationError::ActiveElementSegmentTypeMismatch => todo!(),
138+
ValidationError::I33IsNegative => todo!(),
139+
// TODO double check if our ValidationError::MissingDataCountSection
140+
// is really the same as "data count section required" in the
141+
// reference interpreter. Because we require the data section if
142+
// there are instructions that use it. I'm unsure about the
143+
// condition that is specfied in the reference interpreter.
144+
ValidationError::MissingDataCountSection => Ok("data count section required"),
145+
ValidationError::InvalidDataSegmentMode(_) => Ok("malformed data segement kind"),
146+
ValidationError::InvalidElementMode(_) => Ok("malformed elements segment kind"),
147+
148+
// These exist only in our interpreter, because they are rare edge
149+
// cases.
150+
ValidationError::TooManyFunctions
151+
| ValidationError::TooManyTables
152+
| ValidationError::TooManyMemories
153+
| ValidationError::TooManyGlobals => Err(WastError::UnrepresentedValidationError(
154+
validation_error.clone(),
155+
)),
156+
}
157+
.map(str::to_owned)
158+
})
159+
.map_err(WastError::Panic)
160+
.flatten()
57161
}
58162

59163
/// Clear the bytes and runtime instance before calling this function
@@ -280,22 +384,36 @@ fn run_directive<'a>(
280384
wast::WastDirective::AssertMalformed {
281385
span,
282386
module: mut modulee,
283-
message: _,
387+
message,
284388
}
285389
| wast::WastDirective::AssertInvalid {
286390
span,
287391
module: mut modulee,
288-
message: _,
392+
message,
289393
} => {
290394
let line_number = get_linenum(contents, span);
291395
let cmd = get_command(contents, span);
292-
let result = encode(&mut modulee).and_then(|bytes| {
293-
let bytes = arena.alloc_slice_clone(&bytes);
294-
validate_instantiate(store, bytes, linker, last_instantiated_module)
295-
});
396+
397+
// Note: We can only run this assertion, if we can re-encode this
398+
// module into its binary format. This is because we cannot parse
399+
// the text format.
400+
// TODO We could compare the error message returned by the wast
401+
// crate and `message`
402+
let Ok(bytes) = encode(&mut modulee) else {
403+
return Ok(Some(AssertOutcome {
404+
line_number,
405+
command: cmd.to_owned(),
406+
maybe_error: None,
407+
}));
408+
};
409+
410+
let bytes = arena.alloc_slice_clone(&bytes);
411+
let result = validate_instantiate(store, bytes, linker, last_instantiated_module);
296412

297413
let maybe_assert_error = match result {
298-
Ok(_module) => Some(WastError::AssertInvalidButValid),
414+
Ok(_) | Err(WastError::FailedToLink | WastError::WasmRuntimeError(_)) => {
415+
Some(WastError::AssertInvalidButValid)
416+
}
299417
Err(panic_err @ WastError::Panic(_)) => {
300418
return Err(ScriptError::new(
301419
filepath,
@@ -305,7 +423,24 @@ fn run_directive<'a>(
305423
cmd,
306424
))
307425
}
308-
Err(_other) => None,
426+
Err(WastError::WasmError(validation_error)) => {
427+
match validation_error_to_wasm_testsuite_string(&validation_error) {
428+
Ok(actual_message) => {
429+
if actual_message == message {
430+
None
431+
} else {
432+
Some(WastError::UnexpectedValidationError {
433+
expected: message.to_owned(),
434+
actual: validation_error,
435+
})
436+
}
437+
}
438+
Err(err) => Some(err),
439+
}
440+
}
441+
Err(other_err) => {
442+
unreachable!("no other errors should be possible here, got: {other_err}")
443+
}
309444
};
310445

311446
Ok(Some(AssertOutcome {

0 commit comments

Comments
 (0)