use rwasm::{CompilationConfig, CompilationError, RwasmModule};
fn compile(wat_str: &str) -> Result<(), CompilationError> {
let wasm = wat::parse_str(wat_str).expect("valid WAT");
let config = CompilationConfig::default().with_entrypoint_name("main".into());
RwasmModule::compile(config, &wasm).map(|_| ())
}
#[test]
fn test_simd_operator_is_rejected_instead_of_desyncing_the_stack() {
let error = compile(
r#"
(module
(memory (export "memory") 1)
(func (export "main") (result i32)
v128.const i32x4 1 2 3 4
i32x4.extract_lane 0))
"#,
)
.expect_err("a SIMD operator must not compile");
assert!(
matches!(error, CompilationError::NotSupportedOpcode),
"unexpected error: {error:?}"
);
}
#[test]
fn test_disabled_proposals_are_rejected() {
let cases = [
(
"simd (v128 local)",
r#"(module (func (export "main") (local v128) nop))"#,
"SIMD support is not enabled",
),
(
"simd (v128 parameter)",
r#"(module (func (export "main") (param v128) nop))"#,
"SIMD support is not enabled",
),
(
"simd (v128 global)",
r#"(module (global v128 (v128.const i32x4 0 0 0 0)) (func (export "main") nop))"#,
"SIMD support is not enabled",
),
(
"relaxed_simd",
r#"(module (func (export "main") (param v128) (result v128)
local.get 0 local.get 0 i32x4.relaxed_trunc_f32x4_s))"#,
"SIMD support is not enabled",
),
(
"threads",
r#"(module (memory 1 1 shared) (func (export "main") (result i32)
i32.const 0 i32.atomic.load))"#,
"threads must be enabled for shared memories",
),
(
"multi_memory",
r#"(module (memory 1) (memory 1) (func (export "main") nop))"#,
"multiple memories",
),
(
"memory64",
r#"(module (memory i64 1) (func (export "main") nop))"#,
"memory64 must be enabled for 64-bit memories",
),
(
"exceptions",
r#"(module (tag $e (param i32)) (func (export "main") nop))"#,
"exceptions proposal not enabled",
),
];
for (proposal, wat_str, expected) in cases {
let error = compile(wat_str)
.err()
.unwrap_or_else(|| panic!("`{proposal}` must not compile"));
let message = format!("{error}");
assert!(
message.contains(expected),
"`{proposal}` was rejected, but not for the expected reason: {message}"
);
}
}
#[test]
fn test_wasm_features_denies_every_unimplemented_proposal() {
let features = CompilationConfig::default().wasm_features();
assert!(!features.simd, "simd must stay disabled");
assert!(!features.relaxed_simd, "relaxed_simd must stay disabled");
assert!(!features.threads, "threads must stay disabled");
assert!(!features.multi_memory, "multi_memory must stay disabled");
assert!(!features.memory64, "memory64 must stay disabled");
assert!(!features.exceptions, "exceptions must stay disabled");
assert!(
!features.component_model,
"component_model must stay disabled"
);
assert!(
!features.memory_control,
"memory_control must stay disabled"
);
assert!(features.mutable_global);
assert!(features.saturating_float_to_int);
assert!(features.sign_extension);
assert!(features.multi_value);
assert!(features.bulk_memory);
assert!(features.reference_types);
assert!(features.tail_call);
assert!(features.extended_const);
assert!(features.floats);
}
#[test]
fn test_supported_proposals_still_compile() {
compile(
r#"
(module
(memory (export "memory") 1)
(table 1 funcref)
(func $pair (result i32 i32) i32.const 1 i32.const 2)
(func (export "main") (result i32)
i32.const 0 i32.const 0 i32.const 0 memory.fill
i32.const 7 i32.extend8_s
f32.const 1.5 i32.trunc_sat_f32_s
i32.add
call $pair
i32.add
i32.add))
"#,
)
.expect("supported proposals must still compile");
}
#[test]
fn test_tail_call_still_compiles() {
compile(
r#"
(module
(func $callee (result i32) i32.const 1)
(func (export "main") (result i32) return_call $callee))
"#,
)
.expect("tail calls must still compile");
}