Path: blob/main/crates/wizer/tests/all/tests.rs
3073 views
use std::process::Command;1use wasm_encoder::ConstExpr;2use wasmtime::{3Config, Engine, Instance, Linker, Module, Result, Store, ToWasmtimeResult as _,4error::Context as _,5};6use wasmtime_wasi::{WasiCtxBuilder, p1};7use wasmtime_wizer::Wizer;8use wat::parse_str as wat_to_wasm;910async fn run_wat(args: &[wasmtime::Val], expected: i32, wat: &str) -> Result<()> {11let _ = env_logger::try_init();12let wasm = wat_to_wasm(wat)?;13wizen_and_run_wasm(args, expected, &wasm, get_wizer()).await14}1516fn get_wizer() -> Wizer {17Wizer::new()18}1920fn store() -> Result<Store<p1::WasiP1Ctx>> {21let mut wasi = WasiCtxBuilder::new();22let mut config = Config::new();23config.relaxed_simd_deterministic(true);24let engine = Engine::new(&config)?;25Ok(Store::new(&engine, wasi.build_p1()))26}2728async fn instantiate(store: &mut Store<p1::WasiP1Ctx>, module: &Module) -> Result<Instance> {29let mut linker = Linker::new(store.engine());30p1::add_to_linker_async(&mut linker, |x| x)?;31linker.define_unknown_imports_as_traps(module)?;32linker.instantiate_async(store, module).await33}3435async fn wizen_and_run_wasm(36args: &[wasmtime::Val],37expected: i32,38wasm: &[u8],39wizer: Wizer,40) -> Result<()> {41let _ = env_logger::try_init();4243log::debug!(44"=== PreWizened Wasm ==========================================================\n\45{}\n\46===========================================================================",47wasmprinter::print_bytes(&wasm).unwrap()48);49let mut store = store()?;50let wasm = wizer.run(&mut store, &wasm, instantiate).await?;51log::debug!(52"=== Wizened Wasm ==========================================================\n\53{}\n\54===========================================================================",55wasmprinter::print_bytes(&wasm).unwrap()56);57if log::log_enabled!(log::Level::Debug) {58std::fs::write("test.wasm", &wasm).unwrap();59}6061let module =62wasmtime::Module::new(store.engine(), wasm).context("Wasm test case failed to compile")?;6364let mut linker = wasmtime::Linker::new(store.engine());65let thunk = wasmtime::Func::wrap(&mut store, || {});66linker67.define_name(&mut store, "dummy_func", thunk)?68.define(&mut store, "env", "f", thunk)?69.define_name(&mut store, "f", thunk)?70.define(&mut store, "x", "f", thunk)?;7172p1::add_to_linker_async(&mut linker, |wasi| wasi)?;7374let instance = linker.instantiate_async(&mut store, &module).await?;7576let run = instance.get_func(&mut store, "run").ok_or_else(|| {77wasmtime::format_err!("the test Wasm module does not export a `run` function")78})?;7980let mut actual = vec![wasmtime::Val::I32(0)];81run.call_async(&mut store, args, &mut actual).await?;82wasmtime::ensure!(actual.len() == 1, "expected one result");83let actual = match actual[0] {84wasmtime::Val::I32(x) => x,85_ => wasmtime::bail!("expected an i32 result"),86};87wasmtime::ensure!(88expected == actual,89"expected `{expected}`, found `{actual}`",90);9192Ok(())93}9495async fn fails_wizening(wat: &str) -> Result<()> {96let _ = env_logger::try_init();9798let wasm = wat_to_wasm(wat)?;99100let mut features = wasmparser::WasmFeatures::WASM2;101features.set(wasmparser::WasmFeatures::MULTI_MEMORY, true);102103let mut validator = wasmparser::Validator::new_with_features(features);104validator105.validate_all(&wasm)106.context("initial Wasm should be valid")?;107108wasmtime::ensure!(109get_wizer()110.run(&mut store()?, &wasm, instantiate)111.await112.is_err(),113"Expected an error when wizening, but didn't get one"114);115Ok(())116}117118#[tokio::test]119async fn basic_global() -> Result<()> {120run_wat(121&[],12242,123r#"124(module125(global $g (mut i32) i32.const 0)126(func (export "wizer-initialize")127i32.const 42128global.set $g)129(func (export "run") (result i32)130global.get $g))131"#,132)133.await134}135136#[tokio::test]137async fn basic_memory() -> Result<()> {138run_wat(139&[],14042,141r#"142(module143(memory 1)144(func (export "wizer-initialize")145i32.const 0146i32.const 42147i32.store offset=1337)148(func (export "run") (result i32)149i32.const 0150i32.load offset=1337))151"#,152)153.await154}155156#[tokio::test]157async fn multi_memory() -> Result<()> {158run_wat(159&[],16042,161r#"162(module163(memory $m1 1)164(memory $m2 1)165(func (export "wizer-initialize")166i32.const 0167i32.const 41168i32.store $m1 offset=1337169i32.const 0170i32.const 1171i32.store $m2 offset=1337)172(func (export "run") (result i32)173i32.const 0174i32.load $m1 offset=1337175i32.const 0176i32.load $m2 offset=1337177i32.add))178"#,179)180.await181}182#[tokio::test]183async fn reject_imported_memory() -> Result<()> {184fails_wizening(185r#"186(module187(import "" "" (memory 1)))188"#,189)190.await191}192193#[tokio::test]194async fn reject_imported_global() -> Result<()> {195fails_wizening(196r#"197(module198(import "" "" (global i32)))199"#,200)201.await202}203204#[tokio::test]205async fn reject_imported_table() -> Result<()> {206fails_wizening(207r#"208(module209(import "" "" (table 0 externref)))210"#,211)212.await213}214215#[tokio::test]216async fn reject_table_copy() -> Result<()> {217let result = run_wat(218&[],21942,220r#"221(module222(table 3 funcref)223224(func $f (result i32) (i32.const 0))225(func $g (result i32) (i32.const 0))226(func $h (result i32) (i32.const 0))227228(func (export "main")229i32.const 0230i32.const 1231i32.const 1232table.copy)233234(elem (i32.const 0) $f $g $h)235)236"#,237)238.await;239assert!(result.is_err());240241let err = result.unwrap_err();242assert!(243err.to_string()244.contains("unsupported `table.copy` instruction")245);246247Ok(())248}249250#[tokio::test]251async fn reject_table_get_set() -> Result<()> {252let wat = r#"253(module254(table 3 funcref)255256(func $f (result i32) (i32.const 0))257(func $g (result i32) (i32.const 0))258(func $h (result i32) (i32.const 0))259260(func (export "main")261i32.const 0262i32.const 1263table.get264table.set)265266(elem (i32.const 0) $f $g $h)267)268"#;269270let _ = env_logger::try_init();271let wizer = Wizer::new();272let wasm = wat_to_wasm(wat)?;273let result = wizen_and_run_wasm(&[], 42, &wasm, wizer).await;274275assert!(result.is_err());276277let err = result.unwrap_err();278assert!(279err.to_string()280.contains("unsupported `table.set` instruction"),281"bad error: {err}",282);283284Ok(())285}286287#[tokio::test]288async fn reject_table_get_set_with_reference_types_enabled() -> Result<()> {289let result = run_wat(290&[],29142,292r#"293(module294(table 3 funcref)295296(func $f (result i32) (i32.const 0))297(func $g (result i32) (i32.const 0))298(func $h (result i32) (i32.const 0))299300(func (export "main")301i32.const 0302i32.const 1303table.get304table.set)305306(elem (i32.const 0) $f $g $h)307)"#,308)309.await;310assert!(result.is_err());311312let err = result.unwrap_err();313assert!(314err.to_string()315.contains("unsupported `table.set` instruction"),316);317318Ok(())319}320321#[tokio::test]322async fn reject_table_grow_with_reference_types_enabled() -> wasmtime::Result<()> {323let wat = r#"324(module325(elem declare func $f)326(func $f)327(table 0 funcref)328(func (export "_initialize") (result i32)329ref.func $f330i32.const 1331table.grow332)333)"#;334335let _ = env_logger::try_init();336let wizer = Wizer::new();337let wasm = wat_to_wasm(wat)?;338let result = wizen_and_run_wasm(&[], 42, &wasm, wizer).await;339340assert!(result.is_err());341342let err = result.unwrap_err();343assert!(344err.to_string()345.contains("unsupported `table.grow` instruction")346);347348Ok(())349}350351#[tokio::test]352async fn indirect_call_with_reference_types() -> wasmtime::Result<()> {353let wat = r#"354(module355(type $sig (func (result i32)))356(table 0 funcref)357(table $table1 1 funcref)358(elem (table $table1) (i32.const 0) func $f)359(func $f (type $sig)360i32.const 42361)362(func (export "wizer-initialize"))363(func (export "run") (result i32)364i32.const 0365call_indirect $table1 (type $sig)366)367)"#;368369let _ = env_logger::try_init();370let wizer = Wizer::new();371let wasm = wat_to_wasm(wat)?;372wizen_and_run_wasm(&[], 42, &wasm, wizer).await373}374375#[tokio::test]376async fn reject_table_init() -> Result<()> {377let result = run_wat(378&[],37942,380r#"381(module382(table 3 funcref)383384(func $f (result i32) (i32.const 0))385(func $g (result i32) (i32.const 0))386(func $h (result i32) (i32.const 0))387388(elem $elem func $f $g $h)389390(func (export "main")391i32.const 0392i32.const 0393i32.const 3394table.init $elem)395)396"#,397)398.await;399assert!(result.is_err());400401let err = result.unwrap_err();402assert!(403err.to_string()404.contains("unsupported `table.init` instruction")405);406407Ok(())408}409410#[tokio::test]411async fn reject_elem_drop() -> Result<()> {412let result = run_wat(413&[],41442,415r#"416(module417(table 3 funcref)418419(func $f (result i32) (i32.const 0))420(func $g (result i32) (i32.const 0))421(func $h (result i32) (i32.const 0))422423(elem $elem func $f $g $h)424425(func (export "main")426elem.drop $elem)427)428"#,429)430.await;431assert!(result.is_err());432433let err = result.unwrap_err();434assert!(435err.to_string()436.contains("unsupported `elem.drop` instruction")437);438439Ok(())440}441442#[tokio::test]443async fn reject_data_drop() -> Result<()> {444let result = run_wat(445&[],44642,447r#"448(module449(memory 1)450(data $data "hello, wizer!")451452(func (export "main")453data.drop $data)454)455"#,456)457.await;458assert!(result.is_err());459460let err = result.unwrap_err();461assert!(462err.to_string()463.contains("unsupported `data.drop` instruction")464);465466Ok(())467}468469#[tokio::test]470async fn rust_regex() -> Result<()> {471let status = Command::new("cargo")472.args(&["build", "--target=wasm32-wasip1", "-q"])473.current_dir("./tests/regex-test")474.env_remove("CARGO_ENCODED_RUSTFLAGS")475.env_remove("RUSTFLAGS")476.status()477.expect("failed to build regex test case");478assert!(status.success());479wizen_and_run_wasm(480&[wasmtime::Val::I32(13)],48142,482&std::fs::read("../../target/wasm32-wasip1/debug/regex_test.wasm")483.expect("failed to read regex test case"),484get_wizer(),485)486.await487}488489#[tokio::test]490async fn data_segment_at_end_of_memory() -> Result<()> {491// Test that we properly synthesize data segments for data at the end of492// memory.493run_wat(494&[],49542,496r#"497(module498(memory 1)499(func (export "wizer-initialize")500;; Store 42 to the last byte in memory.501i32.const 0502i32.const 42503i32.store8 offset=65535504)505(func (export "run") (result i32)506i32.const 0507i32.load8_u offset=65535508)509)510"#,511)512.await513}514515#[tokio::test]516async fn too_many_data_segments_for_engines() -> Result<()> {517run_wat(518&[],51942,520r#"521(module522;; Enough memory to create more segments than engines will allow:523;;524;; // The maximum number of segments that engines will allow a module to525;; // have.526;; let max_segments = 100_000;527;;528;; // The minimum gap that Wizer won't automatically merge two data529;; // segments (see `MIN_ACTIVE_SEGMENT_OVERHEAD`).530;; let wizer_min_gap = 6;531;;532;; // Wasm page size.533;; let wasm_page_size = 65_536;534;;535;; let num_pages = round_up(max_segments * wizer_min_gap / wasm_page_size);536(memory 10)537538(func (export "wizer-initialize")539(local i32)540loop541(i32.ge_u (local.get 0) (i32.const 655360)) ;; 10 * wasm_page_size542if543return544end545546(i32.store8 (local.get 0) (i32.const 42))547(local.set 0 (i32.add (local.get 0) (i32.const 6)))548br 0549end550)551(func (export "run") (result i32)552i32.const 0553i32.load8_u554)555)556"#,557)558.await559}560561#[tokio::test]562async fn rename_functions() -> Result<()> {563let wat = r#"564(module565(func (export "wizer-initialize"))566(func (export "func_a") (result i32)567i32.const 1)568(func (export "func_b") (result i32)569i32.const 2)570(func (export "func_c") (result i32)571i32.const 3))572"#;573574let wasm = wat_to_wasm(wat)?;575let mut wizer = Wizer::new();576wizer.func_rename("func_a", "func_b");577wizer.func_rename("func_b", "func_c");578let wasm = wizer.run(&mut store()?, &wasm, instantiate).await?;579let wat = wasmprinter::print_bytes(&wasm).to_wasmtime_result()?;580581let expected_wat = r#"582(module583(type (;0;) (func))584(type (;1;) (func (result i32)))585(export "func_a" (func 2))586(export "func_b" (func 3))587(func (;0;) (type 0))588(func (;1;) (type 1) (result i32)589i32.const 1590)591(func (;2;) (type 1) (result i32)592i32.const 2593)594(func (;3;) (type 1) (result i32)595i32.const 3596)597)598"#;599600assert_eq!(wat.trim(), expected_wat.trim());601Ok(())602}603604#[tokio::test]605async fn wasi_reactor() -> wasmtime::Result<()> {606run_wat(607&[],60842,609r#"610(module611(global $g (mut i32) i32.const 0)612(func (export "_initialize")613i32.const 6614global.set $g615)616(func (export "wizer-initialize")617global.get $g618i32.const 7619i32.mul620global.set $g)621(func (export "run") (result i32)622global.get $g623)624)625"#,626)627.await628}629630#[tokio::test]631async fn wasi_reactor_initializer_as_init_func() -> wasmtime::Result<()> {632let wat = r#"633(module634(global $g (mut i32) i32.const 0)635(func (export "_initialize")636global.get $g637i32.const 1638i32.add639global.set $g640)641(func (export "run") (result i32)642global.get $g643)644)"#;645646let _ = env_logger::try_init();647let mut wizer = Wizer::new();648wizer.init_func("_initialize");649let wasm = wat_to_wasm(wat)?;650// we expect `_initialize` to be called _exactly_ once651wizen_and_run_wasm(&[], 1, &wasm, wizer).await652}653654#[tokio::test]655async fn wasi_reactor_initializer_with_keep_init() -> wasmtime::Result<()> {656let wat = r#"657(module658(global $g (mut i32) i32.const 0)659(func (export "_initialize")660i32.const 1661global.set $g662)663(func (export "wizer-initialize")664i32.const 2665global.set $g)666(func (export "run") (result i32)667global.get $g668)669)"#;670671let _ = env_logger::try_init();672let mut wizer = Wizer::new();673wizer.keep_init_func(true);674let wasm = wat_to_wasm(wat)?;675// we expect `_initialize` to be un-exported and not called at run676wizen_and_run_wasm(&[], 2, &wasm, wizer).await677}678679#[tokio::test]680async fn call_undefined_import_function_during_init() -> Result<()> {681fails_wizening(682r#"683(module684(import "x" "f" (func $import))685(func (export "wizer-initialize")686(call $import)687)688)689"#,690)691.await692}693694#[tokio::test]695async fn allow_undefined_import_function() -> Result<()> {696run_wat(697&[],69842,699r#"700(module701(import "x" "f" (func $import))702(func (export "wizer-initialize"))703(func (export "run") (result i32)704i32.const 42705)706)707"#,708)709.await710}711712#[tokio::test]713async fn accept_bulk_memory_copy() -> Result<()> {714run_wat(715&[],716('h' as i32) + ('w' as i32),717r#"718(module719(memory $memory (data "hello, wizer!"))720(func (export "wizer-initialize")721i32.const 42 ;; dst722i32.const 0 ;; src723i32.const 13 ;; size724memory.copy)725(func (export "run") (result i32)726i32.const 42727i32.load8_u728i32.const 42729i32.load8_u offset=7730i32.add))731"#,732)733.await734}735736#[tokio::test]737async fn accept_bulk_memory_data_count() -> Result<()> {738let mut module = wasm_encoder::Module::new();739let mut types = wasm_encoder::TypeSection::new();740types.ty().func_type(&wasm_encoder::FuncType::new(741vec![],742vec![wasm_encoder::ValType::I32],743));744types745.ty()746.func_type(&wasm_encoder::FuncType::new(vec![], vec![]));747module.section(&types);748749let mut functions = wasm_encoder::FunctionSection::new();750functions.function(0);751functions.function(1);752module.section(&functions);753754let mut memory = wasm_encoder::MemorySection::new();755memory.memory(wasm_encoder::MemoryType {756minimum: 1,757maximum: Some(1),758memory64: false,759shared: false,760page_size_log2: None,761});762module.section(&memory);763764let mut exports = wasm_encoder::ExportSection::new();765exports.export("run", wasm_encoder::ExportKind::Func, 0);766exports.export("wizer-initialize", wasm_encoder::ExportKind::Func, 1);767module.section(&exports);768769module.section(&wasm_encoder::DataCountSection { count: 2 });770771let mut code = wasm_encoder::CodeSection::new();772let mut func = wasm_encoder::Function::new(vec![]);773func.instruction(&wasm_encoder::Instruction::I32Const(42));774func.instruction(&wasm_encoder::Instruction::End);775code.function(&func);776777let mut func = wasm_encoder::Function::new(vec![]);778func.instruction(&wasm_encoder::Instruction::End);779code.function(&func);780781module.section(&code);782783// We're expecting these two data segments to be merge into one, which will exercise wizer's784// ability to output the correct data count (1 instead of 2 above).785let mut data = wasm_encoder::DataSection::new();786data.active(0, &ConstExpr::i32_const(0), vec![0, 1, 2, 3]);787data.active(0, &ConstExpr::i32_const(4), vec![5, 6, 7, 8]);788module.section(&data);789790wizen_and_run_wasm(&[], 42, &module.finish(), get_wizer())791.await792.unwrap();793Ok(())794}795796#[tokio::test]797async fn accept_bulk_memory_fill() -> Result<()> {798run_wat(799&[],80077 + 77,801r#"802(module803(memory 1)804(func (export "wizer-initialize")805i32.const 42 ;; dst806i32.const 77 ;; value807i32.const 13 ;; size808memory.fill)809(func (export "run") (result i32)810i32.const 42811i32.load8_u812i32.const 42813i32.load8_u offset=7814i32.add))815"#,816)817.await818}819820#[tokio::test]821async fn accept_bulk_memory_init() -> Result<()> {822run_wat(823&[],824('h' as i32) + ('w' as i32),825r#"826(module827(memory 1)828(data $data "hello, wizer!")829(func (export "wizer-initialize")830i32.const 42 ;; dst831i32.const 0 ;; offset832i32.const 13 ;; size833memory.init $data)834(func (export "run") (result i32)835i32.const 42836i32.load8_u837i32.const 42838i32.load8_u offset=7839i32.add))840"#,841)842.await843}844845#[tokio::test]846async fn accept_simd128() -> Result<()> {847run_wat(848&[],84949,850r#"851(module852(global $g (mut v128) (v128.const i32x4 2 3 5 7))853(func (export "wizer-initialize")854global.get $g855global.get $g856i32x4.mul857global.set $g)858(func (export "run") (result i32)859global.get $g860i32x4.extract_lane 3))861"#,862)863.await864}865866#[tokio::test]867async fn relaxed_simd_deterministic() -> Result<()> {868let _ = env_logger::try_init();869let wasm = wat_to_wasm(870r#"871(module872(global $g (mut i32) i32.const 0)873(func (export "wizer-initialize")874(v128.const f32x4 2796203.5 0.0 0.0 0.0)875(v128.const f32x4 3.0 0.0 0.0 0.0)876(v128.const f32x4 8388611.0 0.0 0.0 0.0)877f32x4.relaxed_madd878f32x4.extract_lane 0879i32.reinterpret_f32880global.set $g)881(func (export "run") (result i32)882global.get $g883)884)885"#,886)?;887let wizer = get_wizer();888889// We'll get 0x4b000003 if we have the deterministic `relaxed_madd`890// semantics. We might get 0x4b000002 if we don't.891wizen_and_run_wasm(&[], 0x4b800003, &wasm, wizer).await892}893894#[tokio::test]895async fn reject_mutable_globals_of_reference_types() -> Result<()> {896// Non-mutable globals are fine897run_wat(898&[],89942,900r#"901(module902(global funcref (ref.null func))903(func (export "wizer-initialize"))904(func (export "run") (result i32) i32.const 42)905)906"#,907)908.await?;909910// Mutable globals are not fine911fails_wizening(912r#"913(module914(global (mut funcref) (ref.null func))915(func (export "wizer-initialize"))916(func (export "run") (result i32) i32.const 42)917)918"#,919)920.await?;921Ok(())922}923924#[tokio::test]925async fn mixture_of_globals() -> Result<()> {926let _ = env_logger::try_init();927let wasm = wat_to_wasm(928r#"929(module930(global $g1 (mut i32) i32.const 1)931(global $g2 i32 i32.const 2)932(global $g3 (mut i32) i32.const 3)933(global $g4 i32 i32.const 4)934(func (export "wizer-initialize")935(global.set $g1 (i32.const 42))936(global.set $g3 (i32.const 43))937)938(func (export "run") (result i32)939global.get $g1940global.get $g2941global.get $g3942global.get $g4943i32.add944i32.add945i32.add946)947)948"#,949)?;950let wizer = get_wizer();951wizen_and_run_wasm(&[], 42 + 2 + 43 + 4, &wasm, wizer).await952}953954#[tokio::test]955async fn memory_init_and_data_segments() -> Result<()> {956let _ = env_logger::try_init();957let wasm = wat_to_wasm(958r#"959(module960(memory 1)961962(func (export "wizer-initialize")963i32.const 2964i32.const 0965i32.const 2966memory.init $a967)968969(func (export "run") (result i32)970i32.const 4971i32.const 0972i32.const 2973memory.init $a974i32.const 6975i32.const 0976i32.const 2977memory.init $c978979i32.const 0980i32.load981i32.const 4982i32.load983i32.add984)985986(data $a "\01\02")987(data $b (i32.const 0) "\03\04")988(data $c "\05\06")989)990"#,991)?;992let wizer = get_wizer();993wizen_and_run_wasm(&[], 0x02010403 + 0x06050201, &wasm, wizer).await994}995996#[tokio::test]997async fn memory64() -> Result<()> {998let _ = env_logger::try_init();999let wasm = wat_to_wasm(1000r#"1001(module1002(memory i64 1)10031004(func (export "wizer-initialize")1005i64.const 01006i32.const 101007i32.store1008)10091010(func (export "run") (result i32)1011i64.const 01012i32.load1013)1014)1015"#,1016)?;1017let wizer = get_wizer();1018wizen_and_run_wasm(&[], 10, &wasm, wizer).await1019}102010211022