mirror of
https://github.com/rustwasm/wasm-bindgen.git
synced 2024-12-28 12:32:37 +03:00
Merge pull request #996 from alexcrichton/gc-tests
Add tests for the wasm-gc crate
This commit is contained in:
commit
0d5f514709
10
.travis.yml
10
.travis.yml
@ -167,6 +167,16 @@ matrix:
|
||||
script: cargo test -p ui-tests
|
||||
if: branch = master
|
||||
|
||||
# wasm-gc tests work alright
|
||||
- name: "test wasm-bindgen-gc crate"
|
||||
install:
|
||||
- git clone https://github.com/WebAssembly/wabt
|
||||
- mkdir -p wabt/build
|
||||
- (cd wabt/build && cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=sccache -DCMAKE_CXX_COMPILER_ARG1=c++ -DBUILD_TESTS=OFF && cmake --build . -- -j4)
|
||||
- export PATH=$PATH:`pwd`/wabt/build
|
||||
script: cargo test -p wasm-bindgen-gc
|
||||
if: branch = master
|
||||
|
||||
# Dist linux binary
|
||||
- name: "dist: Linux (x86_64-unknown-linux-musl)"
|
||||
env: JOB=dist-linux TARGET=x86_64-unknown-linux-musl
|
||||
|
@ -14,3 +14,14 @@ Support for removing unused items from a wasm executable
|
||||
parity-wasm = "0.35.1"
|
||||
log = "0.4"
|
||||
rustc-demangle = "0.1.9"
|
||||
|
||||
[dev-dependencies]
|
||||
rayon = "1.0.2"
|
||||
tempfile = "3.0.4"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[[test]]
|
||||
name = 'all'
|
||||
harness = false
|
||||
|
@ -29,6 +29,15 @@ impl BitSet {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, i: &u32) {
|
||||
let i = *i as usize;
|
||||
let idx = i / BITS;
|
||||
let bit = 1 << (i % BITS);
|
||||
if let Some(slot) = self.bits.get_mut(idx) {
|
||||
*slot &= !bit;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(&self, i: &u32) -> bool {
|
||||
let i = *i as usize;
|
||||
let idx = i / BITS;
|
||||
|
@ -51,17 +51,18 @@ impl Config {
|
||||
}
|
||||
|
||||
fn run(config: &mut Config, module: &mut Module) {
|
||||
let analysis = {
|
||||
let mut analysis = {
|
||||
let mut cx = LiveContext::new(&module);
|
||||
cx.blacklist.insert("rust_eh_personality");
|
||||
cx.blacklist.insert("__indirect_function_table");
|
||||
cx.blacklist.insert("__heap_base");
|
||||
cx.blacklist.insert("__data_end");
|
||||
|
||||
// always treat memory as a root
|
||||
// Always treat memory as a root. In theory we could actually gc this
|
||||
// away in some circumstances, but it's probably not worth the effort.
|
||||
cx.add_memory(0);
|
||||
|
||||
// All exports are a root
|
||||
// All non-blacklisted exports are roots
|
||||
if let Some(section) = module.export_section() {
|
||||
for (i, entry) in section.entries().iter().enumerate() {
|
||||
if cx.blacklist.contains(entry.field()) {
|
||||
@ -71,22 +72,7 @@ fn run(config: &mut Config, module: &mut Module) {
|
||||
}
|
||||
}
|
||||
|
||||
// Pessimistically assume all data and table segments are
|
||||
// required
|
||||
//
|
||||
// TODO: this shouldn't assume this!
|
||||
if let Some(section) = module.data_section() {
|
||||
for entry in section.entries() {
|
||||
cx.add_data_segment(entry);
|
||||
}
|
||||
}
|
||||
if let Some(elements) = module.elements_section() {
|
||||
for seg in elements.entries() {
|
||||
cx.add_element_segment(seg);
|
||||
}
|
||||
}
|
||||
|
||||
// The start function is also a root
|
||||
// ... and finally, the start function
|
||||
if let Some(i) = module.start_section() {
|
||||
cx.add_function(i);
|
||||
}
|
||||
@ -94,7 +80,7 @@ fn run(config: &mut Config, module: &mut Module) {
|
||||
cx.analysis
|
||||
};
|
||||
|
||||
let cx = RemapContext::new(&module, &analysis, config);
|
||||
let cx = RemapContext::new(&module, &mut analysis, config);
|
||||
for i in (0..module.sections().len()).rev() {
|
||||
let retain = match module.sections_mut()[i] {
|
||||
Section::Unparsed { .. } => {
|
||||
@ -144,6 +130,8 @@ struct Analysis {
|
||||
imports: BitSet,
|
||||
exports: BitSet,
|
||||
functions: BitSet,
|
||||
elements: BitSet,
|
||||
data_segments: BitSet,
|
||||
imported_functions: u32,
|
||||
imported_globals: u32,
|
||||
imported_memories: u32,
|
||||
@ -235,10 +223,12 @@ impl<'a> LiveContext<'a> {
|
||||
|
||||
// Add all element segments that initialize this table
|
||||
if let Some(elements) = self.element_section {
|
||||
for entry in elements.entries().iter().filter(|d| !d.passive()) {
|
||||
if entry.index() == idx {
|
||||
self.add_element_segment(entry);
|
||||
}
|
||||
let iter = elements.entries()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, d)| !d.passive() && d.index() == idx);
|
||||
for (i, _) in iter {
|
||||
self.add_element_segment(i as u32);
|
||||
}
|
||||
}
|
||||
|
||||
@ -273,10 +263,12 @@ impl<'a> LiveContext<'a> {
|
||||
|
||||
// Add all data segments that initialize this memory
|
||||
if let Some(data) = self.data_section {
|
||||
for entry in data.entries().iter().filter(|d| !d.passive()) {
|
||||
if entry.index() == idx {
|
||||
self.add_data_segment(entry);
|
||||
}
|
||||
let iter = data.entries()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(_, d)| !d.passive() && d.index() == idx);
|
||||
for (i, _) in iter {
|
||||
self.add_data_segment(i as u32);
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,6 +387,16 @@ impl<'a> LiveContext<'a> {
|
||||
}
|
||||
Instruction::GetGlobal(i) |
|
||||
Instruction::SetGlobal(i) => self.add_global(i),
|
||||
Instruction::MemoryInit(i) |
|
||||
Instruction::MemoryDrop(i) => {
|
||||
self.add_memory(0);
|
||||
self.add_data_segment(i);
|
||||
}
|
||||
Instruction::TableInit(i) |
|
||||
Instruction::TableDrop(i) => {
|
||||
self.add_table(0);
|
||||
self.add_element_segment(i);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -431,20 +433,32 @@ impl<'a> LiveContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_data_segment(&mut self, data: &DataSegment) {
|
||||
if let Some(offset) = data.offset() {
|
||||
self.add_memory(data.index());
|
||||
self.add_init_expr(offset);
|
||||
fn add_data_segment(&mut self, idx: u32) {
|
||||
if !self.analysis.data_segments.insert(idx) {
|
||||
return
|
||||
}
|
||||
let data = &self.data_section.unwrap().entries()[idx as usize];
|
||||
if !data.passive() {
|
||||
if let Some(offset) = data.offset() {
|
||||
self.add_memory(data.index());
|
||||
self.add_init_expr(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_element_segment(&mut self, seg: &ElementSegment) {
|
||||
fn add_element_segment(&mut self, idx: u32) {
|
||||
if !self.analysis.elements.insert(idx) {
|
||||
return
|
||||
}
|
||||
let seg = &self.element_section.unwrap().entries()[idx as usize];
|
||||
for member in seg.members() {
|
||||
self.add_function(*member);
|
||||
}
|
||||
if let Some(offset) = seg.offset() {
|
||||
self.add_table(seg.index());
|
||||
self.add_init_expr(offset);
|
||||
if !seg.passive() {
|
||||
if let Some(offset) = seg.offset() {
|
||||
self.add_table(seg.index());
|
||||
self.add_init_expr(offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -457,10 +471,12 @@ struct RemapContext<'a> {
|
||||
types: Vec<u32>,
|
||||
tables: Vec<u32>,
|
||||
memories: Vec<u32>,
|
||||
elements: Vec<u32>,
|
||||
data_segments: Vec<u32>,
|
||||
}
|
||||
|
||||
impl<'a> RemapContext<'a> {
|
||||
fn new(m: &Module, analysis: &'a Analysis, config: &'a Config) -> RemapContext<'a> {
|
||||
fn new(m: &Module, analysis: &'a mut Analysis, config: &'a Config) -> RemapContext<'a> {
|
||||
let mut nfunctions = 0;
|
||||
let mut functions = Vec::new();
|
||||
let mut nglobals = 0;
|
||||
@ -478,6 +494,7 @@ impl<'a> RemapContext<'a> {
|
||||
if analysis.types.contains(&(i as u32)) {
|
||||
if let Some(prev) = map.get(&ty) {
|
||||
types.push(*prev);
|
||||
analysis.types.remove(&(i as u32));
|
||||
continue
|
||||
}
|
||||
map.insert(ty, ntypes);
|
||||
@ -530,8 +547,7 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
if let Some(s) = m.table_section() {
|
||||
for i in 0..(s.entries().len() as u32) {
|
||||
// TODO: should remove `|| true` here when this is better tested
|
||||
if analysis.tables.contains(&(i + analysis.imported_tables)) || true {
|
||||
if analysis.tables.contains(&(i + analysis.imported_tables)) {
|
||||
tables.push(ntables);
|
||||
ntables += 1;
|
||||
} else {
|
||||
@ -542,8 +558,7 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
if let Some(s) = m.memory_section() {
|
||||
for i in 0..(s.entries().len() as u32) {
|
||||
// TODO: should remove `|| true` here when this is better tested
|
||||
if analysis.memories.contains(&(i + analysis.imported_memories)) || true {
|
||||
if analysis.memories.contains(&(i + analysis.imported_memories)) {
|
||||
memories.push(nmemories);
|
||||
nmemories += 1;
|
||||
} else {
|
||||
@ -553,6 +568,34 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut elements = Vec::new();
|
||||
if let Some(s) = m.elements_section() {
|
||||
let mut nelements = 0;
|
||||
for i in 0..(s.entries().len() as u32) {
|
||||
if analysis.elements.contains(&i) {
|
||||
elements.push(nelements);
|
||||
nelements += 1;
|
||||
} else {
|
||||
debug!("gc element segment {}", i);
|
||||
elements.push(u32::max_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut data_segments = Vec::new();
|
||||
if let Some(s) = m.data_section() {
|
||||
let mut ndata_segments = 0;
|
||||
for i in 0..(s.entries().len() as u32) {
|
||||
if analysis.data_segments.contains(&i) {
|
||||
data_segments.push(ndata_segments);
|
||||
ndata_segments += 1;
|
||||
} else {
|
||||
debug!("gc data segment {}", i);
|
||||
data_segments.push(u32::max_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RemapContext {
|
||||
analysis,
|
||||
functions,
|
||||
@ -561,6 +604,8 @@ impl<'a> RemapContext<'a> {
|
||||
tables,
|
||||
types,
|
||||
config,
|
||||
elements,
|
||||
data_segments,
|
||||
}
|
||||
}
|
||||
|
||||
@ -713,6 +758,7 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
|
||||
fn remap_element_section(&self, s: &mut ElementSection) -> bool {
|
||||
self.retain(&self.analysis.elements, s.entries_mut(), "element", 0);
|
||||
for s in s.entries_mut() {
|
||||
self.remap_element_segment(s);
|
||||
}
|
||||
@ -720,9 +766,11 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
|
||||
fn remap_element_segment(&self, s: &mut ElementSegment) {
|
||||
let mut i = s.index();
|
||||
self.remap_table_idx(&mut i);
|
||||
assert_eq!(s.index(), i);
|
||||
if !s.passive() {
|
||||
let mut i = s.index();
|
||||
self.remap_table_idx(&mut i);
|
||||
assert_eq!(s.index(), i);
|
||||
}
|
||||
for m in s.members_mut() {
|
||||
self.remap_function_idx(m);
|
||||
}
|
||||
@ -763,6 +811,10 @@ impl<'a> RemapContext<'a> {
|
||||
Instruction::CallIndirect(ref mut t, _) => self.remap_type_idx(t),
|
||||
Instruction::GetGlobal(ref mut i) |
|
||||
Instruction::SetGlobal(ref mut i) => self.remap_global_idx(i),
|
||||
Instruction::TableInit(ref mut i) |
|
||||
Instruction::TableDrop(ref mut i) => self.remap_element_idx(i),
|
||||
Instruction::MemoryInit(ref mut i) |
|
||||
Instruction::MemoryDrop(ref mut i) => self.remap_data_idx(i),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -775,6 +827,7 @@ impl<'a> RemapContext<'a> {
|
||||
}
|
||||
|
||||
fn remap_data_section(&self, s: &mut DataSection) -> bool {
|
||||
self.retain(&self.analysis.data_segments, s.entries_mut(), "data", 0);
|
||||
for data in s.entries_mut() {
|
||||
self.remap_data_segment(data);
|
||||
}
|
||||
@ -816,6 +869,16 @@ impl<'a> RemapContext<'a> {
|
||||
assert!(*i != u32::max_value());
|
||||
}
|
||||
|
||||
fn remap_element_idx(&self, i: &mut u32) {
|
||||
*i = self.elements[*i as usize];
|
||||
assert!(*i != u32::max_value());
|
||||
}
|
||||
|
||||
fn remap_data_idx(&self, i: &mut u32) {
|
||||
*i = self.data_segments[*i as usize];
|
||||
assert!(*i != u32::max_value());
|
||||
}
|
||||
|
||||
fn remap_name_section(&self, s: &mut NameSection) {
|
||||
match *s {
|
||||
NameSection::Module(_) => {}
|
||||
|
148
crates/gc/tests/all.rs
Normal file
148
crates/gc/tests/all.rs
Normal file
@ -0,0 +1,148 @@
|
||||
extern crate parity_wasm;
|
||||
extern crate rayon;
|
||||
extern crate tempfile;
|
||||
extern crate wasm_bindgen_gc;
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use rayon::prelude::*;
|
||||
use parity_wasm::elements::Module;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
struct Test {
|
||||
input: PathBuf,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut tests = Vec::new();
|
||||
find_tests(&mut tests, "tests/wat".as_ref());
|
||||
|
||||
run_tests(&tests);
|
||||
}
|
||||
|
||||
fn find_tests(tests: &mut Vec<Test>, path: &Path) {
|
||||
for entry in path.read_dir().unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
if entry.file_type().unwrap().is_dir() {
|
||||
find_tests(tests, &path);
|
||||
continue
|
||||
}
|
||||
|
||||
if path.extension().and_then(|s| s.to_str()) == Some("wat") {
|
||||
tests.push(Test {
|
||||
input: path,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_tests(tests: &[Test]) {
|
||||
println!("");
|
||||
|
||||
let results = tests.par_iter()
|
||||
.map(|test| {
|
||||
run_test(test).map_err(|e| (test, e.to_string()))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut bad = false;
|
||||
for result in results {
|
||||
let (test, err) = match result {
|
||||
Ok(()) => continue,
|
||||
Err(p) => p,
|
||||
};
|
||||
println!("fail: {} - {}", test.input.display(), err);
|
||||
bad = true;
|
||||
}
|
||||
if bad {
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
println!("\nall good!");
|
||||
}
|
||||
|
||||
fn run_test(test: &Test) -> Result<(), Box<Error>> {
|
||||
println!("test {}", test.input.display());
|
||||
|
||||
let f = NamedTempFile::new()?;
|
||||
let input = fs::read_to_string(&test.input)?;
|
||||
let expected = extract_expected(&input);
|
||||
let status = Command::new("wat2wasm")
|
||||
.arg("--debug-names")
|
||||
.arg("--enable-bulk-memory")
|
||||
.arg(&test.input)
|
||||
.arg("-o")
|
||||
.arg(f.path())
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "failed to run wat2wasm").into())
|
||||
}
|
||||
|
||||
let wasm = fs::read(f.path())?;
|
||||
let mut module: Module = parity_wasm::deserialize_buffer(&wasm)?;
|
||||
module = match module.parse_names() {
|
||||
Ok(m) => m,
|
||||
Err((_, m)) => m,
|
||||
};
|
||||
wasm_bindgen_gc::Config::new().run(&mut module);
|
||||
let wasm = parity_wasm::serialize(module)?;
|
||||
fs::write(f.path(), wasm)?;
|
||||
|
||||
let status = Command::new("wasm2wat")
|
||||
.arg("--enable-bulk-memory")
|
||||
.arg(&f.path())
|
||||
.stderr(Stdio::inherit())
|
||||
.output()?;
|
||||
if !status.status.success() {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "failed to run wasm2wat").into())
|
||||
}
|
||||
let actual = String::from_utf8(status.stdout)?;
|
||||
let actual = actual.trim();
|
||||
|
||||
if env::var("BLESS_TESTS").is_ok() {
|
||||
fs::write(&test.input, generate_blesssed(&input, &actual))?;
|
||||
} else {
|
||||
if actual != expected {
|
||||
println!("{:?} {:?}", actual, expected);
|
||||
return Err(io::Error::new(io::ErrorKind::Other,
|
||||
"test failed").into())
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn extract_expected(input: &str) -> String {
|
||||
input.lines()
|
||||
.filter(|l| l.starts_with(";; "))
|
||||
.skip_while(|l| !l.contains("STDOUT"))
|
||||
.skip(1)
|
||||
.take_while(|l| !l.contains("STDOUT"))
|
||||
.map(|l| &l[3..])
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn generate_blesssed(input: &str, actual: &str) -> String {
|
||||
let mut input = input.lines()
|
||||
.filter(|l| !l.starts_with(";;"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
.trim()
|
||||
.to_string();
|
||||
input.push_str("\n\n");
|
||||
input.push_str(";; STDOUT (update this section with `BLESS_TESTS=1` while running tests)\n");
|
||||
for line in actual.lines() {
|
||||
input.push_str(";; ");
|
||||
input.push_str(line);
|
||||
input.push_str("\n");
|
||||
}
|
||||
input.push_str(";; STDOUT\n");
|
||||
return input
|
||||
}
|
11
crates/gc/tests/wat/dont-remove-func1.wat
Normal file
11
crates/gc/tests/wat/dont-remove-func1.wat
Normal file
@ -0,0 +1,11 @@
|
||||
(module
|
||||
(func $foo)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0))
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
16
crates/gc/tests/wat/dont-remove-func2.wat
Normal file
16
crates/gc/tests/wat/dont-remove-func2.wat
Normal file
@ -0,0 +1,16 @@
|
||||
(module
|
||||
(func $foo
|
||||
call $bar
|
||||
)
|
||||
(func $bar)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; call $bar)
|
||||
;; (func $bar (type 0))
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
21
crates/gc/tests/wat/import-and-type.wat
Normal file
21
crates/gc/tests/wat/import-and-type.wat
Normal file
@ -0,0 +1,21 @@
|
||||
(module
|
||||
(import "" "" (func (param i32)))
|
||||
|
||||
(func $foo
|
||||
i32.const 0
|
||||
call 0
|
||||
)
|
||||
|
||||
(start $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func (param i32)))
|
||||
;; (type (;1;) (func))
|
||||
;; (import "" "" (func (;0;) (type 0)))
|
||||
;; (func $foo (type 1)
|
||||
;; i32.const 0
|
||||
;; call 0)
|
||||
;; (start 1))
|
||||
;; STDOUT
|
8
crates/gc/tests/wat/import-memory.wat
Normal file
8
crates/gc/tests/wat/import-memory.wat
Normal file
@ -0,0 +1,8 @@
|
||||
(module
|
||||
(import "" "a" (memory 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (import "" "a" (memory (;0;) 0)))
|
||||
;; STDOUT
|
10
crates/gc/tests/wat/keep-data.wat
Normal file
10
crates/gc/tests/wat/keep-data.wat
Normal file
@ -0,0 +1,10 @@
|
||||
(module
|
||||
(memory 0 1)
|
||||
(data (i32.const 0) "foo")
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (memory (;0;) 0 1)
|
||||
;; (data (;0;) (i32.const 0) "foo"))
|
||||
;; STDOUT
|
11
crates/gc/tests/wat/keep-global.wat
Normal file
11
crates/gc/tests/wat/keep-global.wat
Normal file
@ -0,0 +1,11 @@
|
||||
(module
|
||||
(global i32 (i32.const 0))
|
||||
|
||||
(export "foo" (global 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (global (;0;) i32 (i32.const 0))
|
||||
;; (export "foo" (global 0)))
|
||||
;; STDOUT
|
8
crates/gc/tests/wat/keep-memory.wat
Normal file
8
crates/gc/tests/wat/keep-memory.wat
Normal file
@ -0,0 +1,8 @@
|
||||
(module
|
||||
(memory 0 17)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (memory (;0;) 0 17))
|
||||
;; STDOUT
|
27
crates/gc/tests/wat/keep-passive-memory-segment.wat
Normal file
27
crates/gc/tests/wat/keep-passive-memory-segment.wat
Normal file
@ -0,0 +1,27 @@
|
||||
(module
|
||||
(memory 0 10)
|
||||
|
||||
(func $foo
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
memory.init 0
|
||||
)
|
||||
|
||||
(data passive "wut")
|
||||
|
||||
(start $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; memory.init 0)
|
||||
;; (memory (;0;) 0 10)
|
||||
;; (start 0)
|
||||
;; (data (;0;) passive "wut"))
|
||||
;; STDOUT
|
30
crates/gc/tests/wat/keep-passive-segment.wat
Normal file
30
crates/gc/tests/wat/keep-passive-segment.wat
Normal file
@ -0,0 +1,30 @@
|
||||
(module
|
||||
(import "" "" (table 0 1 anyfunc))
|
||||
|
||||
(func $foo
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
table.init 0
|
||||
)
|
||||
|
||||
(func $bar)
|
||||
|
||||
(elem passive $bar)
|
||||
|
||||
(start $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (import "" "" (table (;0;) 0 1 anyfunc))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; table.init 0)
|
||||
;; (func $bar (type 0))
|
||||
;; (start 0)
|
||||
;; (elem (;0;) passive $bar))
|
||||
;; STDOUT
|
23
crates/gc/tests/wat/keep-set-global.wat
Normal file
23
crates/gc/tests/wat/keep-set-global.wat
Normal file
@ -0,0 +1,23 @@
|
||||
(module
|
||||
|
||||
(global (mut i32) (i32.const 0))
|
||||
|
||||
(start $foo)
|
||||
|
||||
(func $bar)
|
||||
(func $foo
|
||||
i32.const 1
|
||||
set_global 0
|
||||
)
|
||||
(func $baz)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 1
|
||||
;; set_global 0)
|
||||
;; (global (;0;) (mut i32) (i32.const 0))
|
||||
;; (start 0))
|
||||
;; STDOUT
|
14
crates/gc/tests/wat/keep-start.wat
Normal file
14
crates/gc/tests/wat/keep-start.wat
Normal file
@ -0,0 +1,14 @@
|
||||
(module
|
||||
(start $foo)
|
||||
|
||||
(func $bar)
|
||||
(func $foo)
|
||||
(func $baz)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0))
|
||||
;; (start 0))
|
||||
;; STDOUT
|
10
crates/gc/tests/wat/keep-table.wat
Normal file
10
crates/gc/tests/wat/keep-table.wat
Normal file
@ -0,0 +1,10 @@
|
||||
(module
|
||||
(table 0 17 anyfunc)
|
||||
(export "foo" (table 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (table (;0;) 0 17 anyfunc)
|
||||
;; (export "foo" (table 0)))
|
||||
;; STDOUT
|
17
crates/gc/tests/wat/keep-table2.wat
Normal file
17
crates/gc/tests/wat/keep-table2.wat
Normal file
@ -0,0 +1,17 @@
|
||||
(module
|
||||
(table 0 17 anyfunc)
|
||||
(func $foo
|
||||
i32.const 0
|
||||
call_indirect)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 0
|
||||
;; call_indirect (type 0))
|
||||
;; (table (;0;) 0 17 anyfunc)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
39
crates/gc/tests/wat/locals-compressed.wat
Normal file
39
crates/gc/tests/wat/locals-compressed.wat
Normal file
@ -0,0 +1,39 @@
|
||||
(module
|
||||
(func $foo
|
||||
(local i32 f32 i32 f64 i64 i32 f32 i64 i32 f32 f64)
|
||||
|
||||
get_local 0
|
||||
get_local 1
|
||||
get_local 2
|
||||
get_local 3
|
||||
get_local 4
|
||||
get_local 5
|
||||
get_local 6
|
||||
get_local 7
|
||||
get_local 8
|
||||
get_local 9
|
||||
get_local 10
|
||||
unreachable
|
||||
)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; (local i32 i32 i32 i32 f32 f32 f32 f64 f64 i64 i64)
|
||||
;; get_local 0
|
||||
;; get_local 4
|
||||
;; get_local 1
|
||||
;; get_local 7
|
||||
;; get_local 9
|
||||
;; get_local 2
|
||||
;; get_local 5
|
||||
;; get_local 10
|
||||
;; get_local 3
|
||||
;; get_local 6
|
||||
;; get_local 8
|
||||
;; unreachable)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
16
crates/gc/tests/wat/preserve-local1.wat
Normal file
16
crates/gc/tests/wat/preserve-local1.wat
Normal file
@ -0,0 +1,16 @@
|
||||
(module
|
||||
(func $foo (result i32)
|
||||
(local i32)
|
||||
get_local 0
|
||||
)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func (result i32)))
|
||||
;; (func $foo (type 0) (result i32)
|
||||
;; (local i32)
|
||||
;; get_local 0)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
18
crates/gc/tests/wat/preserve-local2.wat
Normal file
18
crates/gc/tests/wat/preserve-local2.wat
Normal file
@ -0,0 +1,18 @@
|
||||
(module
|
||||
(func $foo (param i32)
|
||||
(local i32)
|
||||
get_local 0
|
||||
set_local 1
|
||||
)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func (param i32)))
|
||||
;; (func $foo (type 0) (param i32)
|
||||
;; (local i32)
|
||||
;; get_local 0
|
||||
;; set_local 1)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
18
crates/gc/tests/wat/preserve-local3.wat
Normal file
18
crates/gc/tests/wat/preserve-local3.wat
Normal file
@ -0,0 +1,18 @@
|
||||
(module
|
||||
(func $foo (param i32) (result i32)
|
||||
(local i32)
|
||||
get_local 0
|
||||
tee_local 1
|
||||
)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func (param i32) (result i32)))
|
||||
;; (func $foo (type 0) (param i32) (result i32)
|
||||
;; (local i32)
|
||||
;; get_local 0
|
||||
;; tee_local 1)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
7
crates/gc/tests/wat/remove-func.wat
Normal file
7
crates/gc/tests/wat/remove-func.wat
Normal file
@ -0,0 +1,7 @@
|
||||
(module
|
||||
(func $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
9
crates/gc/tests/wat/remove-heap-base.wat
Normal file
9
crates/gc/tests/wat/remove-heap-base.wat
Normal file
@ -0,0 +1,9 @@
|
||||
(module
|
||||
(global i32 (i32.const 0))
|
||||
|
||||
(export "__heap_base" (global 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
26
crates/gc/tests/wat/remove-import-function.wat
Normal file
26
crates/gc/tests/wat/remove-import-function.wat
Normal file
@ -0,0 +1,26 @@
|
||||
(module
|
||||
(import "" "a" (func $i1))
|
||||
(import "" "b" (func $i2))
|
||||
(import "" "c" (func $i3))
|
||||
|
||||
(func $bar)
|
||||
|
||||
(func $foo
|
||||
call $i1
|
||||
call $i3)
|
||||
|
||||
(func $baz)
|
||||
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (import "" "a" (func $i1 (type 0)))
|
||||
;; (import "" "c" (func $i3 (type 0)))
|
||||
;; (func $foo (type 0)
|
||||
;; call $i1
|
||||
;; call $i3)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
7
crates/gc/tests/wat/remove-import-global.wat
Normal file
7
crates/gc/tests/wat/remove-import-global.wat
Normal file
@ -0,0 +1,7 @@
|
||||
(module
|
||||
(import "" "" (global i32))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
35
crates/gc/tests/wat/remove-import-globals.wat
Normal file
35
crates/gc/tests/wat/remove-import-globals.wat
Normal file
@ -0,0 +1,35 @@
|
||||
(module
|
||||
(import "" "a" (global i32))
|
||||
(import "" "b" (global i32))
|
||||
(import "" "c" (global i32))
|
||||
|
||||
(global i32 (i32.const 1))
|
||||
(global i32 (i32.const 2))
|
||||
|
||||
(func $foo
|
||||
get_global 0
|
||||
drop
|
||||
get_global 2
|
||||
drop
|
||||
get_global 4
|
||||
drop
|
||||
)
|
||||
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (import "" "a" (global (;0;) i32))
|
||||
;; (import "" "c" (global (;1;) i32))
|
||||
;; (func $foo (type 0)
|
||||
;; get_global 0
|
||||
;; drop
|
||||
;; get_global 1
|
||||
;; drop
|
||||
;; get_global 2
|
||||
;; drop)
|
||||
;; (global (;2;) i32 (i32.const 2))
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
11
crates/gc/tests/wat/remove-import-table.wat
Normal file
11
crates/gc/tests/wat/remove-import-table.wat
Normal file
@ -0,0 +1,11 @@
|
||||
(module
|
||||
(import "" "" (table 0 1 anyfunc))
|
||||
|
||||
(func $foo)
|
||||
|
||||
(elem (i32.const 1) $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
13
crates/gc/tests/wat/remove-local.wat
Normal file
13
crates/gc/tests/wat/remove-local.wat
Normal file
@ -0,0 +1,13 @@
|
||||
(module
|
||||
(func $foo
|
||||
(local i32)
|
||||
)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0))
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
18
crates/gc/tests/wat/remove-passive-memory-segment.wat
Normal file
18
crates/gc/tests/wat/remove-passive-memory-segment.wat
Normal file
@ -0,0 +1,18 @@
|
||||
(module
|
||||
(memory 0 10)
|
||||
|
||||
(func $foo
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
memory.init 0
|
||||
)
|
||||
|
||||
(data passive "wut")
|
||||
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (memory (;0;) 0 10))
|
||||
;; STDOUT
|
7
crates/gc/tests/wat/remove-table.wat
Normal file
7
crates/gc/tests/wat/remove-table.wat
Normal file
@ -0,0 +1,7 @@
|
||||
(module
|
||||
(table 0 17 anyfunc)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
11
crates/gc/tests/wat/remove-unused-passive-segment.wat
Normal file
11
crates/gc/tests/wat/remove-unused-passive-segment.wat
Normal file
@ -0,0 +1,11 @@
|
||||
(module
|
||||
(import "" "" (table 0 1 anyfunc))
|
||||
|
||||
(func $foo)
|
||||
|
||||
(elem passive $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
34
crates/gc/tests/wat/remove-unused-type.wat
Normal file
34
crates/gc/tests/wat/remove-unused-type.wat
Normal file
@ -0,0 +1,34 @@
|
||||
(module
|
||||
(type (func))
|
||||
(type (func (param i32)))
|
||||
(type (func (param i32)))
|
||||
(type (func (result i32)))
|
||||
|
||||
(func $f1 (type 0))
|
||||
(func $f2 (type 1))
|
||||
(func $f3 (type 2))
|
||||
(func $f4 (type 3)
|
||||
i32.const 0
|
||||
)
|
||||
|
||||
(export "a" (func $f1))
|
||||
(export "b" (func $f2))
|
||||
(export "c" (func $f3))
|
||||
(export "d" (func $f4))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (type (;1;) (func (param i32)))
|
||||
;; (type (;2;) (func (result i32)))
|
||||
;; (func $f1 (type 0))
|
||||
;; (func $f2 (type 1) (param i32))
|
||||
;; (func $f3 (type 1) (param i32))
|
||||
;; (func $f4 (type 2) (result i32)
|
||||
;; i32.const 0)
|
||||
;; (export "a" (func $f1))
|
||||
;; (export "b" (func $f2))
|
||||
;; (export "c" (func $f3))
|
||||
;; (export "d" (func $f4)))
|
||||
;; STDOUT
|
29
crates/gc/tests/wat/renumber-data-segment.wat
Normal file
29
crates/gc/tests/wat/renumber-data-segment.wat
Normal file
@ -0,0 +1,29 @@
|
||||
(module
|
||||
(memory 0 10)
|
||||
|
||||
(func $foo
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
memory.init 1
|
||||
)
|
||||
|
||||
(data passive "wut")
|
||||
(data passive "wut2")
|
||||
(data passive "wut3")
|
||||
|
||||
(start $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; memory.init 0)
|
||||
;; (memory (;0;) 0 10)
|
||||
;; (start 0)
|
||||
;; (data (;0;) passive "wut2"))
|
||||
;; STDOUT
|
16
crates/gc/tests/wat/renumber-functions.wat
Normal file
16
crates/gc/tests/wat/renumber-functions.wat
Normal file
@ -0,0 +1,16 @@
|
||||
(module
|
||||
(func
|
||||
call 2)
|
||||
(func)
|
||||
(func)
|
||||
(export "foo" (func 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func (;0;) (type 0)
|
||||
;; call 1)
|
||||
;; (func (;1;) (type 0))
|
||||
;; (export "foo" (func 0)))
|
||||
;; STDOUT
|
16
crates/gc/tests/wat/renumber-globals.wat
Normal file
16
crates/gc/tests/wat/renumber-globals.wat
Normal file
@ -0,0 +1,16 @@
|
||||
(module
|
||||
(global i32 (i32.const 0))
|
||||
(global i32 (i32.const 0))
|
||||
(func (result i32)
|
||||
get_global 1)
|
||||
(export "foo" (func 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func (result i32)))
|
||||
;; (func (;0;) (type 0) (result i32)
|
||||
;; get_global 0)
|
||||
;; (global (;0;) i32 (i32.const 0))
|
||||
;; (export "foo" (func 0)))
|
||||
;; STDOUT
|
16
crates/gc/tests/wat/renumber-locals.wat
Normal file
16
crates/gc/tests/wat/renumber-locals.wat
Normal file
@ -0,0 +1,16 @@
|
||||
(module
|
||||
(func $foo (result i32)
|
||||
(local i32 i32)
|
||||
get_local 1
|
||||
)
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func (result i32)))
|
||||
;; (func $foo (type 0) (result i32)
|
||||
;; (local i32)
|
||||
;; get_local 0)
|
||||
;; (export "foo" (func $foo)))
|
||||
;; STDOUT
|
32
crates/gc/tests/wat/renumber-passive-segment.wat
Normal file
32
crates/gc/tests/wat/renumber-passive-segment.wat
Normal file
@ -0,0 +1,32 @@
|
||||
(module
|
||||
(import "" "" (table 0 1 anyfunc))
|
||||
|
||||
(func $foo
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
table.init 1
|
||||
)
|
||||
|
||||
(func $bar)
|
||||
(func $bar2)
|
||||
|
||||
(elem passive $bar)
|
||||
(elem passive $bar2)
|
||||
|
||||
(start $foo)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (import "" "" (table (;0;) 0 1 anyfunc))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; i32.const 0
|
||||
;; table.init 0)
|
||||
;; (func $bar2 (type 0))
|
||||
;; (start 0)
|
||||
;; (elem (;0;) passive $bar2))
|
||||
;; STDOUT
|
13
crates/gc/tests/wat/renumber-types.wat
Normal file
13
crates/gc/tests/wat/renumber-types.wat
Normal file
@ -0,0 +1,13 @@
|
||||
(module
|
||||
(type (func (result i32)))
|
||||
(type (func))
|
||||
(func (type 1))
|
||||
(export "foo" (func 0))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func (;0;) (type 0))
|
||||
;; (export "foo" (func 0)))
|
||||
;; STDOUT
|
5
crates/gc/tests/wat/smoke.wat
Normal file
5
crates/gc/tests/wat/smoke.wat
Normal file
@ -0,0 +1,5 @@
|
||||
(module)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
15
crates/gc/tests/wat/table-elem.wat
Normal file
15
crates/gc/tests/wat/table-elem.wat
Normal file
@ -0,0 +1,15 @@
|
||||
(module
|
||||
(func $foo
|
||||
i32.const 0
|
||||
call_indirect
|
||||
)
|
||||
|
||||
(func $bar)
|
||||
|
||||
(table 0 10 anyfunc)
|
||||
(elem (i32.const 0) $bar)
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module)
|
||||
;; STDOUT
|
25
crates/gc/tests/wat/table-elem2.wat
Normal file
25
crates/gc/tests/wat/table-elem2.wat
Normal file
@ -0,0 +1,25 @@
|
||||
(module
|
||||
(func $foo
|
||||
i32.const 0
|
||||
call_indirect
|
||||
)
|
||||
|
||||
(func $bar)
|
||||
|
||||
(table 0 10 anyfunc)
|
||||
(elem (i32.const 0) $bar)
|
||||
|
||||
(export "foo" (func $foo))
|
||||
)
|
||||
|
||||
;; STDOUT (update this section with `BLESS_TESTS=1` while running tests)
|
||||
;; (module
|
||||
;; (type (;0;) (func))
|
||||
;; (func $foo (type 0)
|
||||
;; i32.const 0
|
||||
;; call_indirect (type 0))
|
||||
;; (func $bar (type 0))
|
||||
;; (table (;0;) 0 10 anyfunc)
|
||||
;; (export "foo" (func $foo))
|
||||
;; (elem (;0;) (i32.const 0) $bar))
|
||||
;; STDOUT
|
Loading…
Reference in New Issue
Block a user