Path: blob/main/crates/wizer/tests/all/tests.rs
2459 views
use anyhow::{Context, Result};1use std::process::Command;2use wasm_encoder::ConstExpr;3use wasmtime::{Config, Engine, Instance, Linker, Module, Store};4use wasmtime_wasi::{WasiCtxBuilder, p1};5use wasmtime_wizer::Wizer;6use wat::parse_str as wat_to_wasm;78async fn run_wat(args: &[wasmtime::Val], expected: i32, wat: &str) -> Result<()> {9let _ = env_logger::try_init();10let wasm = wat_to_wasm(wat)?;11wizen_and_run_wasm(args, expected, &wasm, get_wizer()).await12}1314fn get_wizer() -> Wizer {15Wizer::new()16}1718fn store() -> Result<Store<p1::WasiP1Ctx>> {19let mut wasi = WasiCtxBuilder::new();20let mut config = Config::new();21config.async_support(true);22config.relaxed_simd_deterministic(true);23let engine = Engine::new(&config)?;24Ok(Store::new(&engine, wasi.build_p1()))25}2627async fn instantiate(store: &mut Store<p1::WasiP1Ctx>, module: &Module) -> Result<Instance> {28let mut linker = Linker::new(store.engine());29p1::add_to_linker_async(&mut linker, |x| x)?;30linker.define_unknown_imports_as_traps(module)?;31linker.instantiate_async(store, module).await32}3334async fn wizen_and_run_wasm(35args: &[wasmtime::Val],36expected: i32,37wasm: &[u8],38wizer: Wizer,39) -> Result<()> {40let _ = env_logger::try_init();4142log::debug!(43"=== PreWizened Wasm ==========================================================\n\44{}\n\45===========================================================================",46wasmprinter::print_bytes(&wasm).unwrap()47);48let mut store = store()?;49let wasm = wizer.run(&mut store, &wasm, instantiate).await?;50log::debug!(51"=== Wizened Wasm ==========================================================\n\52{}\n\53===========================================================================",54wasmprinter::print_bytes(&wasm).unwrap()55);56if log::log_enabled!(log::Level::Debug) {57std::fs::write("test.wasm", &wasm).unwrap();58}5960let module =61wasmtime::Module::new(store.engine(), wasm).context("Wasm test case failed to compile")?;6263let mut linker = wasmtime::Linker::new(store.engine());64let thunk = wasmtime::Func::wrap(&mut store, || {});65linker66.define_name(&mut store, "dummy_func", thunk)?67.define(&mut store, "env", "f", thunk)?68.define_name(&mut store, "f", thunk)?69.define(&mut store, "x", "f", thunk)?;7071p1::add_to_linker_async(&mut linker, |wasi| wasi)?;7273let instance = linker.instantiate_async(&mut store, &module).await?;7475let run = instance76.get_func(&mut store, "run")77.ok_or_else(|| anyhow::anyhow!("the test Wasm module does not export a `run` function"))?;7879let mut actual = vec![wasmtime::Val::I32(0)];80run.call_async(&mut store, args, &mut actual).await?;81anyhow::ensure!(actual.len() == 1, "expected one result");82let actual = match actual[0] {83wasmtime::Val::I32(x) => x,84_ => anyhow::bail!("expected an i32 result"),85};86anyhow::ensure!(87expected == actual,88"expected `{expected}`, found `{actual}`",89);9091Ok(())92}9394async fn fails_wizening(wat: &str) -> Result<()> {95let _ = env_logger::try_init();9697let wasm = wat_to_wasm(wat)?;9899let mut features = wasmparser::WasmFeatures::WASM2;100features.set(wasmparser::WasmFeatures::MULTI_MEMORY, true);101102let mut validator = wasmparser::Validator::new_with_features(features);103validator104.validate_all(&wasm)105.context("initial Wasm should be valid")?;106107anyhow::ensure!(108get_wizer()109.run(&mut store()?, &wasm, instantiate)110.await111.is_err(),112"Expected an error when wizening, but didn't get one"113);114Ok(())115}116117#[tokio::test]118async fn basic_global() -> Result<()> {119run_wat(120&[],12142,122r#"123(module124(global $g (mut i32) i32.const 0)125(func (export "wizer-initialize")126i32.const 42127global.set $g)128(func (export "run") (result i32)129global.get $g))130"#,131)132.await133}134135#[tokio::test]136async fn basic_memory() -> Result<()> {137run_wat(138&[],13942,140r#"141(module142(memory 1)143(func (export "wizer-initialize")144i32.const 0145i32.const 42146i32.store offset=1337)147(func (export "run") (result i32)148i32.const 0149i32.load offset=1337))150"#,151)152.await153}154155#[tokio::test]156async fn multi_memory() -> Result<()> {157run_wat(158&[],15942,160r#"161(module162(memory $m1 1)163(memory $m2 1)164(func (export "wizer-initialize")165i32.const 0166i32.const 41167i32.store $m1 offset=1337168i32.const 0169i32.const 1170i32.store $m2 offset=1337)171(func (export "run") (result i32)172i32.const 0173i32.load $m1 offset=1337174i32.const 0175i32.load $m2 offset=1337176i32.add))177"#,178)179.await180}181#[tokio::test]182async fn reject_imported_memory() -> Result<()> {183fails_wizening(184r#"185(module186(import "" "" (memory 1)))187"#,188)189.await190}191192#[tokio::test]193async fn reject_imported_global() -> Result<()> {194fails_wizening(195r#"196(module197(import "" "" (global i32)))198"#,199)200.await201}202203#[tokio::test]204async fn reject_imported_table() -> Result<()> {205fails_wizening(206r#"207(module208(import "" "" (table 0 externref)))209"#,210)211.await212}213214#[tokio::test]215async fn reject_table_copy() -> Result<()> {216let result = run_wat(217&[],21842,219r#"220(module221(table 3 funcref)222223(func $f (result i32) (i32.const 0))224(func $g (result i32) (i32.const 0))225(func $h (result i32) (i32.const 0))226227(func (export "main")228i32.const 0229i32.const 1230i32.const 1231table.copy)232233(elem (i32.const 0) $f $g $h)234)235"#,236)237.await;238assert!(result.is_err());239240let err = result.unwrap_err();241assert!(242err.to_string()243.contains("unsupported `table.copy` instruction")244);245246Ok(())247}248249#[tokio::test]250async fn reject_table_get_set() -> Result<()> {251let wat = r#"252(module253(table 3 funcref)254255(func $f (result i32) (i32.const 0))256(func $g (result i32) (i32.const 0))257(func $h (result i32) (i32.const 0))258259(func (export "main")260i32.const 0261i32.const 1262table.get263table.set)264265(elem (i32.const 0) $f $g $h)266)267"#;268269let _ = env_logger::try_init();270let wizer = Wizer::new();271let wasm = wat_to_wasm(wat)?;272let result = wizen_and_run_wasm(&[], 42, &wasm, wizer).await;273274assert!(result.is_err());275276let err = result.unwrap_err();277assert!(278err.to_string()279.contains("unsupported `table.set` instruction"),280"bad error: {err}",281);282283Ok(())284}285286#[tokio::test]287async fn reject_table_get_set_with_reference_types_enabled() -> Result<()> {288let result = run_wat(289&[],29042,291r#"292(module293(table 3 funcref)294295(func $f (result i32) (i32.const 0))296(func $g (result i32) (i32.const 0))297(func $h (result i32) (i32.const 0))298299(func (export "main")300i32.const 0301i32.const 1302table.get303table.set)304305(elem (i32.const 0) $f $g $h)306)"#,307)308.await;309assert!(result.is_err());310311let err = result.unwrap_err();312assert!(313err.to_string()314.contains("unsupported `table.set` instruction"),315);316317Ok(())318}319320#[tokio::test]321async fn reject_table_grow_with_reference_types_enabled() -> anyhow::Result<()> {322let wat = r#"323(module324(elem declare func $f)325(func $f)326(table 0 funcref)327(func (export "_initialize") (result i32)328ref.func $f329i32.const 1330table.grow331)332)"#;333334let _ = env_logger::try_init();335let wizer = Wizer::new();336let wasm = wat_to_wasm(wat)?;337let result = wizen_and_run_wasm(&[], 42, &wasm, wizer).await;338339assert!(result.is_err());340341let err = result.unwrap_err();342assert!(343err.to_string()344.contains("unsupported `table.grow` instruction")345);346347Ok(())348}349350#[tokio::test]351async fn indirect_call_with_reference_types() -> anyhow::Result<()> {352let wat = r#"353(module354(type $sig (func (result i32)))355(table 0 funcref)356(table $table1 1 funcref)357(elem (table $table1) (i32.const 0) func $f)358(func $f (type $sig)359i32.const 42360)361(func (export "wizer-initialize"))362(func (export "run") (result i32)363i32.const 0364call_indirect $table1 (type $sig)365)366)"#;367368let _ = env_logger::try_init();369let wizer = Wizer::new();370let wasm = wat_to_wasm(wat)?;371wizen_and_run_wasm(&[], 42, &wasm, wizer).await372}373374#[tokio::test]375async fn reject_table_init() -> Result<()> {376let result = run_wat(377&[],37842,379r#"380(module381(table 3 funcref)382383(func $f (result i32) (i32.const 0))384(func $g (result i32) (i32.const 0))385(func $h (result i32) (i32.const 0))386387(elem $elem func $f $g $h)388389(func (export "main")390i32.const 0391i32.const 0392i32.const 3393table.init $elem)394)395"#,396)397.await;398assert!(result.is_err());399400let err = result.unwrap_err();401assert!(402err.to_string()403.contains("unsupported `table.init` instruction")404);405406Ok(())407}408409#[tokio::test]410async fn reject_elem_drop() -> Result<()> {411let result = run_wat(412&[],41342,414r#"415(module416(table 3 funcref)417418(func $f (result i32) (i32.const 0))419(func $g (result i32) (i32.const 0))420(func $h (result i32) (i32.const 0))421422(elem $elem func $f $g $h)423424(func (export "main")425elem.drop $elem)426)427"#,428)429.await;430assert!(result.is_err());431432let err = result.unwrap_err();433assert!(434err.to_string()435.contains("unsupported `elem.drop` instruction")436);437438Ok(())439}440441#[tokio::test]442async fn reject_data_drop() -> Result<()> {443let result = run_wat(444&[],44542,446r#"447(module448(memory 1)449(data $data "hello, wizer!")450451(func (export "main")452data.drop $data)453)454"#,455)456.await;457assert!(result.is_err());458459let err = result.unwrap_err();460assert!(461err.to_string()462.contains("unsupported `data.drop` instruction")463);464465Ok(())466}467468#[tokio::test]469async fn rust_regex() -> Result<()> {470let status = Command::new("cargo")471.args(&["build", "--target=wasm32-wasip1", "-q"])472.current_dir("./tests/regex-test")473.env_remove("CARGO_ENCODED_RUSTFLAGS")474.env_remove("RUSTFLAGS")475.status()476.expect("failed to build regex test case");477assert!(status.success());478wizen_and_run_wasm(479&[wasmtime::Val::I32(13)],48042,481&std::fs::read("../../target/wasm32-wasip1/debug/regex_test.wasm")482.expect("failed to read regex test case"),483get_wizer(),484)485.await486}487488#[tokio::test]489async fn data_segment_at_end_of_memory() -> Result<()> {490// Test that we properly synthesize data segments for data at the end of491// memory.492run_wat(493&[],49442,495r#"496(module497(memory 1)498(func (export "wizer-initialize")499;; Store 42 to the last byte in memory.500i32.const 0501i32.const 42502i32.store8 offset=65535503)504(func (export "run") (result i32)505i32.const 0506i32.load8_u offset=65535507)508)509"#,510)511.await512}513514#[tokio::test]515async fn too_many_data_segments_for_engines() -> Result<()> {516run_wat(517&[],51842,519r#"520(module521;; Enough memory to create more segments than engines will allow:522;;523;; // The maximum number of segments that engines will allow a module to524;; // have.525;; let max_segments = 100_000;526;;527;; // The minimum gap that Wizer won't automatically merge two data528;; // segments (see `MIN_ACTIVE_SEGMENT_OVERHEAD`).529;; let wizer_min_gap = 6;530;;531;; // Wasm page size.532;; let wasm_page_size = 65_536;533;;534;; let num_pages = round_up(max_segments * wizer_min_gap / wasm_page_size);535(memory 10)536537(func (export "wizer-initialize")538(local i32)539loop540(i32.ge_u (local.get 0) (i32.const 655360)) ;; 10 * wasm_page_size541if542return543end544545(i32.store8 (local.get 0) (i32.const 42))546(local.set 0 (i32.add (local.get 0) (i32.const 6)))547br 0548end549)550(func (export "run") (result i32)551i32.const 0552i32.load8_u553)554)555"#,556)557.await558}559560#[tokio::test]561async fn rename_functions() -> Result<()> {562let wat = r#"563(module564(func (export "wizer-initialize"))565(func (export "func_a") (result i32)566i32.const 1)567(func (export "func_b") (result i32)568i32.const 2)569(func (export "func_c") (result i32)570i32.const 3))571"#;572573let wasm = wat_to_wasm(wat)?;574let mut wizer = Wizer::new();575wizer.func_rename("func_a", "func_b");576wizer.func_rename("func_b", "func_c");577let wasm = wizer.run(&mut store()?, &wasm, instantiate).await?;578let wat = wasmprinter::print_bytes(&wasm)?;579580let expected_wat = r#"581(module582(type (;0;) (func))583(type (;1;) (func (result i32)))584(export "func_a" (func 2))585(export "func_b" (func 3))586(func (;0;) (type 0))587(func (;1;) (type 1) (result i32)588i32.const 1589)590(func (;2;) (type 1) (result i32)591i32.const 2592)593(func (;3;) (type 1) (result i32)594i32.const 3595)596)597"#;598599assert_eq!(wat.trim(), expected_wat.trim());600Ok(())601}602603#[tokio::test]604async fn wasi_reactor() -> anyhow::Result<()> {605run_wat(606&[],60742,608r#"609(module610(global $g (mut i32) i32.const 0)611(func (export "_initialize")612i32.const 6613global.set $g614)615(func (export "wizer-initialize")616global.get $g617i32.const 7618i32.mul619global.set $g)620(func (export "run") (result i32)621global.get $g622)623)624"#,625)626.await627}628629#[tokio::test]630async fn wasi_reactor_initializer_as_init_func() -> anyhow::Result<()> {631let wat = r#"632(module633(global $g (mut i32) i32.const 0)634(func (export "_initialize")635global.get $g636i32.const 1637i32.add638global.set $g639)640(func (export "run") (result i32)641global.get $g642)643)"#;644645let _ = env_logger::try_init();646let mut wizer = Wizer::new();647wizer.init_func("_initialize");648let wasm = wat_to_wasm(wat)?;649// we expect `_initialize` to be called _exactly_ once650wizen_and_run_wasm(&[], 1, &wasm, wizer).await651}652653#[tokio::test]654async fn wasi_reactor_initializer_with_keep_init() -> anyhow::Result<()> {655let wat = r#"656(module657(global $g (mut i32) i32.const 0)658(func (export "_initialize")659i32.const 1660global.set $g661)662(func (export "wizer-initialize")663i32.const 2664global.set $g)665(func (export "run") (result i32)666global.get $g667)668)"#;669670let _ = env_logger::try_init();671let mut wizer = Wizer::new();672wizer.keep_init_func(true);673let wasm = wat_to_wasm(wat)?;674// we expect `_initialize` to be un-exported and not called at run675wizen_and_run_wasm(&[], 2, &wasm, wizer).await676}677678#[tokio::test]679async fn call_undefined_import_function_during_init() -> Result<()> {680fails_wizening(681r#"682(module683(import "x" "f" (func $import))684(func (export "wizer-initialize")685(call $import)686)687)688"#,689)690.await691}692693#[tokio::test]694async fn allow_undefined_import_function() -> Result<()> {695run_wat(696&[],69742,698r#"699(module700(import "x" "f" (func $import))701(func (export "wizer-initialize"))702(func (export "run") (result i32)703i32.const 42704)705)706"#,707)708.await709}710711#[tokio::test]712async fn accept_bulk_memory_copy() -> Result<()> {713run_wat(714&[],715('h' as i32) + ('w' as i32),716r#"717(module718(memory $memory (data "hello, wizer!"))719(func (export "wizer-initialize")720i32.const 42 ;; dst721i32.const 0 ;; src722i32.const 13 ;; size723memory.copy)724(func (export "run") (result i32)725i32.const 42726i32.load8_u727i32.const 42728i32.load8_u offset=7729i32.add))730"#,731)732.await733}734735#[tokio::test]736async fn accept_bulk_memory_data_count() -> Result<()> {737let mut module = wasm_encoder::Module::new();738let mut types = wasm_encoder::TypeSection::new();739types.ty().func_type(&wasm_encoder::FuncType::new(740vec![],741vec![wasm_encoder::ValType::I32],742));743types744.ty()745.func_type(&wasm_encoder::FuncType::new(vec![], vec![]));746module.section(&types);747748let mut functions = wasm_encoder::FunctionSection::new();749functions.function(0);750functions.function(1);751module.section(&functions);752753let mut memory = wasm_encoder::MemorySection::new();754memory.memory(wasm_encoder::MemoryType {755minimum: 1,756maximum: Some(1),757memory64: false,758shared: false,759page_size_log2: None,760});761module.section(&memory);762763let mut exports = wasm_encoder::ExportSection::new();764exports.export("run", wasm_encoder::ExportKind::Func, 0);765exports.export("wizer-initialize", wasm_encoder::ExportKind::Func, 1);766module.section(&exports);767768module.section(&wasm_encoder::DataCountSection { count: 2 });769770let mut code = wasm_encoder::CodeSection::new();771let mut func = wasm_encoder::Function::new(vec![]);772func.instruction(&wasm_encoder::Instruction::I32Const(42));773func.instruction(&wasm_encoder::Instruction::End);774code.function(&func);775776let mut func = wasm_encoder::Function::new(vec![]);777func.instruction(&wasm_encoder::Instruction::End);778code.function(&func);779780module.section(&code);781782// We're expecting these two data segments to be merge into one, which will exercise wizer's783// ability to output the correct data count (1 instead of 2 above).784let mut data = wasm_encoder::DataSection::new();785data.active(0, &ConstExpr::i32_const(0), vec![0, 1, 2, 3]);786data.active(0, &ConstExpr::i32_const(4), vec![5, 6, 7, 8]);787module.section(&data);788789wizen_and_run_wasm(&[], 42, &module.finish(), get_wizer())790.await791.unwrap();792Ok(())793}794795#[tokio::test]796async fn accept_bulk_memory_fill() -> Result<()> {797run_wat(798&[],79977 + 77,800r#"801(module802(memory 1)803(func (export "wizer-initialize")804i32.const 42 ;; dst805i32.const 77 ;; value806i32.const 13 ;; size807memory.fill)808(func (export "run") (result i32)809i32.const 42810i32.load8_u811i32.const 42812i32.load8_u offset=7813i32.add))814"#,815)816.await817}818819#[tokio::test]820async fn accept_bulk_memory_init() -> Result<()> {821run_wat(822&[],823('h' as i32) + ('w' as i32),824r#"825(module826(memory 1)827(data $data "hello, wizer!")828(func (export "wizer-initialize")829i32.const 42 ;; dst830i32.const 0 ;; offset831i32.const 13 ;; size832memory.init $data)833(func (export "run") (result i32)834i32.const 42835i32.load8_u836i32.const 42837i32.load8_u offset=7838i32.add))839"#,840)841.await842}843844#[tokio::test]845async fn accept_simd128() -> Result<()> {846run_wat(847&[],84849,849r#"850(module851(global $g (mut v128) (v128.const i32x4 2 3 5 7))852(func (export "wizer-initialize")853global.get $g854global.get $g855i32x4.mul856global.set $g)857(func (export "run") (result i32)858global.get $g859i32x4.extract_lane 3))860"#,861)862.await863}864865#[tokio::test]866async fn relaxed_simd_deterministic() -> Result<()> {867let _ = env_logger::try_init();868let wasm = wat_to_wasm(869r#"870(module871(global $g (mut i32) i32.const 0)872(func (export "wizer-initialize")873(v128.const f32x4 2796203.5 0.0 0.0 0.0)874(v128.const f32x4 3.0 0.0 0.0 0.0)875(v128.const f32x4 8388611.0 0.0 0.0 0.0)876f32x4.relaxed_madd877f32x4.extract_lane 0878i32.reinterpret_f32879global.set $g)880(func (export "run") (result i32)881global.get $g882)883)884"#,885)?;886let wizer = get_wizer();887888// We'll get 0x4b000003 if we have the deterministic `relaxed_madd`889// semantics. We might get 0x4b000002 if we don't.890wizen_and_run_wasm(&[], 0x4b800003, &wasm, wizer).await891}892893#[tokio::test]894async fn reject_mutable_globals_of_reference_types() -> Result<()> {895// Non-mutable globals are fine896run_wat(897&[],89842,899r#"900(module901(global funcref (ref.null func))902(func (export "wizer-initialize"))903(func (export "run") (result i32) i32.const 42)904)905"#,906)907.await?;908909// Mutable globals are not fine910fails_wizening(911r#"912(module913(global (mut funcref) (ref.null func))914(func (export "wizer-initialize"))915(func (export "run") (result i32) i32.const 42)916)917"#,918)919.await?;920Ok(())921}922923#[tokio::test]924async fn mixture_of_globals() -> Result<()> {925let _ = env_logger::try_init();926let wasm = wat_to_wasm(927r#"928(module929(global $g1 (mut i32) i32.const 1)930(global $g2 i32 i32.const 2)931(global $g3 (mut i32) i32.const 3)932(global $g4 i32 i32.const 4)933(func (export "wizer-initialize")934(global.set $g1 (i32.const 42))935(global.set $g3 (i32.const 43))936)937(func (export "run") (result i32)938global.get $g1939global.get $g2940global.get $g3941global.get $g4942i32.add943i32.add944i32.add945)946)947"#,948)?;949let wizer = get_wizer();950wizen_and_run_wasm(&[], 42 + 2 + 43 + 4, &wasm, wizer).await951}952953#[tokio::test]954async fn memory_init_and_data_segments() -> Result<()> {955let _ = env_logger::try_init();956let wasm = wat_to_wasm(957r#"958(module959(memory 1)960961(func (export "wizer-initialize")962i32.const 2963i32.const 0964i32.const 2965memory.init $a966)967968(func (export "run") (result i32)969i32.const 4970i32.const 0971i32.const 2972memory.init $a973i32.const 6974i32.const 0975i32.const 2976memory.init $c977978i32.const 0979i32.load980i32.const 4981i32.load982i32.add983)984985(data $a "\01\02")986(data $b (i32.const 0) "\03\04")987(data $c "\05\06")988)989"#,990)?;991let wizer = get_wizer();992wizen_and_run_wasm(&[], 0x02010403 + 0x06050201, &wasm, wizer).await993}994995#[tokio::test]996async fn memory64() -> Result<()> {997let _ = env_logger::try_init();998let wasm = wat_to_wasm(999r#"1000(module1001(memory i64 1)10021003(func (export "wizer-initialize")1004i64.const 01005i32.const 101006i32.store1007)10081009(func (export "run") (result i32)1010i64.const 01011i32.load1012)1013)1014"#,1015)?;1016let wizer = get_wizer();1017wizen_and_run_wasm(&[], 10, &wasm, wizer).await1018}101910201021