Make validation tests work
This commit is contained in:
parent
e78f8ad37e
commit
584b1fd2e9
|
@ -0,0 +1,743 @@
|
|||
|
||||
|
||||
|
||||
fn validate(wat: &str) -> ValidatedModule {
|
||||
let wasm = wabt::wat2wasm(wat).unwrap();
|
||||
let module = deserialize_buffer::<Module>(&wasm).unwrap();
|
||||
let validated_module = validate_module(module).unwrap();
|
||||
validated_module
|
||||
}
|
||||
|
||||
fn compile(module: &ValidatedModule) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||
let code = &module.code_map[0];
|
||||
let mut instructions = Vec::new();
|
||||
let mut pcs = Vec::new();
|
||||
let mut iter = code.iterate_from(0);
|
||||
loop {
|
||||
let pc = iter.position();
|
||||
if let Some(instruction) = iter.next() {
|
||||
instructions.push(instruction.clone());
|
||||
pcs.push(pc);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(instructions, pcs)
|
||||
}
|
||||
|
||||
macro_rules! targets {
|
||||
($($target:expr),*) => {
|
||||
::isa::BrTargets::from_internal(
|
||||
&[$($target,)*]
|
||||
.iter()
|
||||
.map(|&target| ::isa::InstructionInternal::BrTableTarget(target))
|
||||
.collect::<Vec<_>>()[..]
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_no_value() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
})]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_with_value() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (result i32)
|
||||
i32.const 0
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_param() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_local() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_return() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
return
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_params() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (param i32) (result i32)
|
||||
get_local 0
|
||||
get_local 1
|
||||
i32.add
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
// This is tricky. Locals are now loaded from the stack. The load
|
||||
// happens from address relative of the current stack pointer. The first load
|
||||
// takes the value below the previous one (i.e the second argument) and then, it increments
|
||||
// the stack pointer. And then the same thing hapens with the value below the previous one
|
||||
// (which happens to be the value loaded by the first get_local).
|
||||
isa::Instruction::GetLocal(2),
|
||||
isa::Instruction::GetLocal(2),
|
||||
isa::Instruction::I32Add,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 2,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_locals() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
(local i32)
|
||||
get_local 0
|
||||
set_local 1
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(2),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 2,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_without_else() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
i32.const 1
|
||||
if
|
||||
i32.const 2
|
||||
return
|
||||
end
|
||||
i32.const 3
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1, // 1 param
|
||||
keep: isa::Keep::Single, // 1 result
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
(local i32)
|
||||
i32.const 1
|
||||
if
|
||||
i32.const 2
|
||||
set_local 0
|
||||
else
|
||||
i32.const 3
|
||||
set_local 0
|
||||
end
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[5],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[7],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_returns_result() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
if (result i32)
|
||||
i32.const 2
|
||||
else
|
||||
i32.const 3
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[5],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_branch_from_true_branch() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
if (result i32)
|
||||
i32.const 1
|
||||
i32.const 1
|
||||
br_if 0
|
||||
drop
|
||||
i32.const 2
|
||||
else
|
||||
i32.const 3
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[8],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_branch_from_false_branch() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
if (result i32)
|
||||
i32.const 1
|
||||
else
|
||||
i32.const 2
|
||||
i32.const 1
|
||||
br_if 0
|
||||
drop
|
||||
i32.const 3
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop (result i32)
|
||||
i32.const 1
|
||||
br_if 0
|
||||
i32.const 2
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: 0,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_empty() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop
|
||||
end
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_as_br_if_value_cond() {
|
||||
use self::isa::Instruction::*;
|
||||
|
||||
let module = validate(
|
||||
r#"
|
||||
(func (export "as-br_if-value-cond") (result i32)
|
||||
(block (result i32)
|
||||
(drop
|
||||
(br_if 0
|
||||
(i32.const 6)
|
||||
(br_table 0 0
|
||||
(i32.const 9)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.const 7)
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
I32Const(6),
|
||||
I32Const(9),
|
||||
I32Const(0),
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: 9,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single
|
||||
}
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: 9,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single
|
||||
}
|
||||
}
|
||||
]),
|
||||
BrIfNez(isa::Target {
|
||||
dst_pc: 9,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single
|
||||
}
|
||||
}),
|
||||
Drop,
|
||||
I32Const(7),
|
||||
Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1
|
||||
loop $2
|
||||
i32.const 0
|
||||
br_table $2 $1
|
||||
end
|
||||
end
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: 0,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[2],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}
|
||||
]),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brtable_returns_result() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1 (result i32)
|
||||
block $2 (result i32)
|
||||
i32.const 0
|
||||
i32.const 1
|
||||
br_table $2 $1
|
||||
end
|
||||
unreachable
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
println!("{:?}", (&code, &pcs));
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: pcs[3],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
keep: isa::Keep::Single,
|
||||
drop: 0,
|
||||
},
|
||||
}
|
||||
]),
|
||||
isa::Instruction::Unreachable,
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wabt_example() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
block $exit
|
||||
get_local 0
|
||||
br_if $exit
|
||||
i32.const 1
|
||||
return
|
||||
end
|
||||
i32.const 2
|
||||
return
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1, // 1 parameter
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
2
test.sh
2
test.sh
|
@ -4,6 +4,6 @@ set -eux
|
|||
|
||||
cd $(dirname $0)
|
||||
|
||||
time cargo test
|
||||
time cargo test --all
|
||||
|
||||
cd -
|
||||
|
|
|
@ -9,6 +9,9 @@ parity-wasm = { version = "0.31", default-features = false }
|
|||
memory_units_crate = { package = "memory_units", version = "0.3.0" }
|
||||
hashbrown = { version = "0.1.8", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.1"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
std = ["parity-wasm/std"]
|
||||
|
|
|
@ -50,9 +50,8 @@ pub mod context;
|
|||
pub mod func;
|
||||
pub mod util;
|
||||
|
||||
// TODO: Uncomment
|
||||
// #[cfg(test)]
|
||||
// mod tests;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
// TODO: Consider using a type other than String, because
|
||||
// of formatting machinary is not welcomed in substrate runtimes.
|
||||
|
@ -101,6 +100,44 @@ pub trait FunctionValidator {
|
|||
fn finish(self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl Validation for () {
|
||||
type Output = ();
|
||||
type FunctionValidator = ();
|
||||
fn new(module: &Module) -> () {
|
||||
()
|
||||
}
|
||||
fn on_function_validated(
|
||||
&mut self,
|
||||
index: u32,
|
||||
output: <<Self as Validation>::FunctionValidator as FunctionValidator>::Output,
|
||||
) -> () {
|
||||
()
|
||||
}
|
||||
fn finish(self) -> () {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl FunctionValidator for () {
|
||||
type Output = ();
|
||||
|
||||
fn new(ctx: &func::FunctionValidationContext) -> () {
|
||||
()
|
||||
}
|
||||
|
||||
fn next_instruction(
|
||||
&mut self,
|
||||
ctx: &mut func::FunctionValidationContext,
|
||||
instruction: &Instruction,
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn finish(self) -> () {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_module<V: Validation>(module: &Module) -> Result<V::Output, Error> {
|
||||
let mut context_builder = ModuleContextBuilder::new();
|
||||
let mut imported_globals = Vec::new();
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
use super::{validate_module, ValidatedModule};
|
||||
use isa;
|
||||
use crate::Error;
|
||||
use parity_wasm::builder::module;
|
||||
use parity_wasm::elements::{
|
||||
deserialize_buffer, BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr,
|
||||
Instruction, Instructions, MemoryType, Module, TableType, ValueType,
|
||||
BlockType, External, GlobalEntry, GlobalType, ImportEntry, InitExpr, Instruction, Instructions,
|
||||
MemoryType, Module, TableType, ValueType,
|
||||
};
|
||||
use wabt;
|
||||
|
||||
fn validate_module(module: &Module) -> Result<(), Error> {
|
||||
super::validate_module::<()>(module)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_is_valid() {
|
||||
let module = module().build();
|
||||
assert!(validate_module(module).is_ok());
|
||||
assert!(validate_module(&module).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -27,7 +29,7 @@ fn limits() {
|
|||
for (min, max, is_valid) in test_cases {
|
||||
// defined table
|
||||
let m = module().table().with_min(min).with_max(max).build().build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
assert_eq!(validate_module(&m).is_ok(), is_valid);
|
||||
|
||||
// imported table
|
||||
let m = module()
|
||||
|
@ -37,7 +39,7 @@ fn limits() {
|
|||
External::Table(TableType::new(min, max)),
|
||||
))
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
assert_eq!(validate_module(&m).is_ok(), is_valid);
|
||||
|
||||
// defined memory
|
||||
let m = module()
|
||||
|
@ -46,7 +48,7 @@ fn limits() {
|
|||
.with_max(max)
|
||||
.build()
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
assert_eq!(validate_module(&m).is_ok(), is_valid);
|
||||
|
||||
// imported table
|
||||
let m = module()
|
||||
|
@ -56,7 +58,7 @@ fn limits() {
|
|||
External::Memory(MemoryType::new(min, max)),
|
||||
))
|
||||
.build();
|
||||
assert_eq!(validate_module(m).is_ok(), is_valid);
|
||||
assert_eq!(validate_module(&m).is_ok(), is_valid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +70,7 @@ fn global_init_const() {
|
|||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
assert!(validate_module(&m).is_ok());
|
||||
|
||||
// init expr type differs from declared global type
|
||||
let m = module()
|
||||
|
@ -77,7 +79,7 @@ fn global_init_const() {
|
|||
InitExpr::new(vec![Instruction::I32Const(42), Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -93,7 +95,7 @@ fn global_init_global() {
|
|||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
assert!(validate_module(&m).is_ok());
|
||||
|
||||
// get_global can reference only previously defined globals
|
||||
let m = module()
|
||||
|
@ -102,7 +104,7 @@ fn global_init_global() {
|
|||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
|
||||
// get_global can reference only const globals
|
||||
let m = module()
|
||||
|
@ -116,7 +118,7 @@ fn global_init_global() {
|
|||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
|
||||
// get_global in init_expr can only refer to imported globals.
|
||||
let m = module()
|
||||
|
@ -129,7 +131,7 @@ fn global_init_global() {
|
|||
InitExpr::new(vec![Instruction::GetGlobal(0), Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -141,7 +143,7 @@ fn global_init_misc() {
|
|||
InitExpr::new(vec![Instruction::I32Const(42)]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
|
||||
// empty init expr
|
||||
let m = module()
|
||||
|
@ -150,7 +152,7 @@ fn global_init_misc() {
|
|||
InitExpr::new(vec![Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
|
||||
// not an constant opcode used
|
||||
let m = module()
|
||||
|
@ -159,7 +161,7 @@ fn global_init_misc() {
|
|||
InitExpr::new(vec![Instruction::Unreachable, Instruction::End]),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -175,7 +177,7 @@ fn module_limits_validity() {
|
|||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
|
||||
// module cannot contain more than 1 table atm.
|
||||
let m = module()
|
||||
|
@ -188,7 +190,7 @@ fn module_limits_validity() {
|
|||
.with_min(10)
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -220,7 +222,7 @@ fn funcs() {
|
|||
.build()
|
||||
.build()
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
assert!(validate_module(&m).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -233,7 +235,7 @@ fn globals() {
|
|||
External::Global(GlobalType::new(ValueType::I32, false)),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_ok());
|
||||
assert!(validate_module(&m).is_ok());
|
||||
|
||||
// import mutable global is invalid.
|
||||
let m = module()
|
||||
|
@ -243,7 +245,7 @@ fn globals() {
|
|||
External::Global(GlobalType::new(ValueType::I32, true)),
|
||||
))
|
||||
.build();
|
||||
assert!(validate_module(m).is_err());
|
||||
assert!(validate_module(&m).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -269,746 +271,5 @@ fn if_else_with_return_type_validation() {
|
|||
.build()
|
||||
.build()
|
||||
.build();
|
||||
validate_module(m).unwrap();
|
||||
}
|
||||
|
||||
fn validate(wat: &str) -> ValidatedModule {
|
||||
let wasm = wabt::wat2wasm(wat).unwrap();
|
||||
let module = deserialize_buffer::<Module>(&wasm).unwrap();
|
||||
let validated_module = validate_module(module).unwrap();
|
||||
validated_module
|
||||
}
|
||||
|
||||
fn compile(module: &ValidatedModule) -> (Vec<isa::Instruction>, Vec<u32>) {
|
||||
let code = &module.code_map[0];
|
||||
let mut instructions = Vec::new();
|
||||
let mut pcs = Vec::new();
|
||||
let mut iter = code.iterate_from(0);
|
||||
loop {
|
||||
let pc = iter.position();
|
||||
if let Some(instruction) = iter.next() {
|
||||
instructions.push(instruction.clone());
|
||||
pcs.push(pc);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(instructions, pcs)
|
||||
}
|
||||
|
||||
macro_rules! targets {
|
||||
($($target:expr),*) => {
|
||||
::isa::BrTargets::from_internal(
|
||||
&[$($target,)*]
|
||||
.iter()
|
||||
.map(|&target| ::isa::InstructionInternal::BrTableTarget(target))
|
||||
.collect::<Vec<_>>()[..]
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_no_value() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
})]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_with_value() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (result i32)
|
||||
i32.const 0
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn implicit_return_param() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_local() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_return() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
get_local 0
|
||||
return
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_params() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (param i32) (result i32)
|
||||
get_local 0
|
||||
get_local 1
|
||||
i32.add
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
// This is tricky. Locals are now loaded from the stack. The load
|
||||
// happens from address relative of the current stack pointer. The first load
|
||||
// takes the value below the previous one (i.e the second argument) and then, it increments
|
||||
// the stack pointer. And then the same thing hapens with the value below the previous one
|
||||
// (which happens to be the value loaded by the first get_local).
|
||||
isa::Instruction::GetLocal(2),
|
||||
isa::Instruction::GetLocal(2),
|
||||
isa::Instruction::I32Add,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 2,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_locals() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32)
|
||||
(local i32)
|
||||
get_local 0
|
||||
set_local 1
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(2),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 2,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_without_else() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
i32.const 1
|
||||
if
|
||||
i32.const 2
|
||||
return
|
||||
end
|
||||
i32.const 3
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1, // 1 param
|
||||
keep: isa::Keep::Single, // 1 result
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
(local i32)
|
||||
i32.const 1
|
||||
if
|
||||
i32.const 2
|
||||
set_local 0
|
||||
else
|
||||
i32.const 3
|
||||
set_local 0
|
||||
end
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[5],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[7],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::SetLocal(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_returns_result() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
if (result i32)
|
||||
i32.const 2
|
||||
else
|
||||
i32.const 3
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[5],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_branch_from_true_branch() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
if (result i32)
|
||||
i32.const 1
|
||||
i32.const 1
|
||||
br_if 0
|
||||
drop
|
||||
i32.const 2
|
||||
else
|
||||
i32.const 3
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[8],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_else_branch_from_false_branch() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
i32.const 1
|
||||
if (result i32)
|
||||
i32.const 1
|
||||
else
|
||||
i32.const 2
|
||||
i32.const 1
|
||||
br_if 0
|
||||
drop
|
||||
i32.const 3
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfEqz(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::Br(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: pcs[9],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::I32Const(3),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop (result i32)
|
||||
i32.const 1
|
||||
br_if 0
|
||||
i32.const 2
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: 0,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn loop_empty() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
loop
|
||||
end
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spec_as_br_if_value_cond() {
|
||||
use self::isa::Instruction::*;
|
||||
|
||||
let module = validate(
|
||||
r#"
|
||||
(func (export "as-br_if-value-cond") (result i32)
|
||||
(block (result i32)
|
||||
(drop
|
||||
(br_if 0
|
||||
(i32.const 6)
|
||||
(br_table 0 0
|
||||
(i32.const 9)
|
||||
(i32.const 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
(i32.const 7)
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, _) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
I32Const(6),
|
||||
I32Const(9),
|
||||
I32Const(0),
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: 9,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single
|
||||
}
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: 9,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single
|
||||
}
|
||||
}
|
||||
]),
|
||||
BrIfNez(isa::Target {
|
||||
dst_pc: 9,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single
|
||||
}
|
||||
}),
|
||||
Drop,
|
||||
I32Const(7),
|
||||
Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brtable() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1
|
||||
loop $2
|
||||
i32.const 0
|
||||
br_table $2 $1
|
||||
end
|
||||
end
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: 0,
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[2],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}
|
||||
]),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn brtable_returns_result() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call")
|
||||
block $1 (result i32)
|
||||
block $2 (result i32)
|
||||
i32.const 0
|
||||
i32.const 1
|
||||
br_table $2 $1
|
||||
end
|
||||
unreachable
|
||||
end
|
||||
drop
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
println!("{:?}", (&code, &pcs));
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::I32Const(0),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::BrTable(targets![
|
||||
isa::Target {
|
||||
dst_pc: pcs[3],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::Single,
|
||||
},
|
||||
},
|
||||
isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
keep: isa::Keep::Single,
|
||||
drop: 0,
|
||||
},
|
||||
}
|
||||
]),
|
||||
isa::Instruction::Unreachable,
|
||||
isa::Instruction::Drop,
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
}),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wabt_example() {
|
||||
let module = validate(
|
||||
r#"
|
||||
(module
|
||||
(func (export "call") (param i32) (result i32)
|
||||
block $exit
|
||||
get_local 0
|
||||
br_if $exit
|
||||
i32.const 1
|
||||
return
|
||||
end
|
||||
i32.const 2
|
||||
return
|
||||
)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
let (code, pcs) = compile(&module);
|
||||
assert_eq!(
|
||||
code,
|
||||
vec![
|
||||
isa::Instruction::GetLocal(1),
|
||||
isa::Instruction::BrIfNez(isa::Target {
|
||||
dst_pc: pcs[4],
|
||||
drop_keep: isa::DropKeep {
|
||||
drop: 0,
|
||||
keep: isa::Keep::None,
|
||||
},
|
||||
}),
|
||||
isa::Instruction::I32Const(1),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1, // 1 parameter
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
isa::Instruction::I32Const(2),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
isa::Instruction::Return(isa::DropKeep {
|
||||
drop: 1,
|
||||
keep: isa::Keep::Single,
|
||||
}),
|
||||
]
|
||||
)
|
||||
validate_module(&m).unwrap();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ use crate::Error;
|
|||
use alloc::prelude::v1::*;
|
||||
use parity_wasm::elements::{Local, ValueType};
|
||||
|
||||
#[cfg(test)]
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
/// Locals are the concatenation of a slice of function parameters
|
||||
/// with function declared local variables.
|
||||
///
|
||||
|
|
Loading…
Reference in New Issue