diff --git a/docs/src/devs/testing.md b/docs/src/devs/testing.md index 8c9d4c98..5113ae3d 100644 --- a/docs/src/devs/testing.md +++ b/docs/src/devs/testing.md @@ -27,6 +27,7 @@ Below is an example `.wast` test: ```webassembly ;; Test `wasm:opcode:call` event +;; @instrument (module ;; Auxiliary definitions (func $other (param i32) (result i32) (local.get 1)) @@ -45,8 +46,51 @@ Below is an example `.wast` test: (assert_return (invoke "instrument_me") (i32.const 1)) ;; will be run with the above WHAMM instrumentation ``` +Below is an example `.wast` test using imports: +```webassembly +(module + (func (export "dummy") (param i32) (result i32) + local.get 0 + ) +) + +(register "test") + +;; @instrument +(module + ;; Imports + (type (;0;) (func (param i32) (result i32))) + (import "test" "dummy" (func $dummy (type 0))) + + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Test case functions + (func $foo + (call $dummy (i32.const 0)) + global.set $var + ) + + (start $foo) + (export "foo" (func $foo)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) + ) + +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg0 == 0 / { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) +``` + There are several conventions to follow when writing `.wast` test cases for `whamm`. -1. Only one `module` per `.wast` file. +1. Only one `module`-to-instrument per `.wast` file. + - The test setup goes at the top (which can include multiple modules when considering testing imports). + - The `module`-to-instrument is the final part of the setup and is marked by `;; @instrument` above the module. 2. Use comment to specify the `whamm!` script, syntax: `;; WHAMM --> ` - The scripts are run on the `module` in the `.wast` file. - If there are multiple `asserts` under a `whamm!` comment, they are all run against the instrumented variation of the `module` that results from that `whamm!` script. diff --git a/src/cli.rs b/src/cli.rs index 6ba3c525..0b368ed6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -58,10 +58,6 @@ pub struct InstrArgs { /// Whether to emit Virgil code as the instrumentation code #[arg(short, long, action, default_value = "false")] pub virgil: bool, - - /// Whether to run the verifier on the specified script - #[arg(long, short, action, default_value = "true")] - pub run_verifier: bool, } // pub fn print_completion(gen: G, app: &mut App) { diff --git a/src/emitter/mod.rs b/src/emitter/mod.rs index 54df582c..afa57b47 100644 --- a/src/emitter/mod.rs +++ b/src/emitter/mod.rs @@ -5,7 +5,7 @@ pub mod tests; use crate::common::error::WhammError; use crate::emitter::rewriting::rules::{Arg, LocInfo, WhammProvider}; -use crate::parser::types::{DataType, Expr, Fn, ProbeSpec, Statement, Value}; +use crate::parser::types::{Block, DataType, Expr, Fn, ProbeSpec, Statement, Value}; use orca::ir::types::DataType as OrcaType; use wasmparser::Operator; @@ -46,7 +46,7 @@ pub trait ModuleEmitter { /// Will configure the emitter to emit subsequent statements in the outer block of some branching logic fn finish_branch(&mut self) -> bool; fn emit_global_stmts(&mut self, stmts: &mut Vec) -> Result>; - fn emit_body(&mut self, body: &mut Vec) -> Result>; + fn emit_body(&mut self, body: &mut Block) -> Result>; fn emit_stmt(&mut self, stmt: &mut Statement) -> Result>; fn dump_to_file(&mut self, output_wasm_path: String) -> Result>; @@ -95,7 +95,7 @@ pub trait VisitingEmitter { /// Will configure the emitter to emit subsequent statements in the outer block of some branching logic fn finish_branch(&mut self) -> bool; fn emit_global_stmts(&mut self, stmts: &mut Vec) -> Result>; - fn emit_body(&mut self, body: &mut Vec) -> Result>; + fn emit_body(&mut self, body: &mut Block) -> Result>; fn emit_stmt(&mut self, stmt: &mut Statement) -> Result>; fn dump_to_file(&mut self, output_wasm_path: String) -> Result>; diff --git a/src/emitter/rewriting/mod.rs b/src/emitter/rewriting/mod.rs index 11946e87..8855189b 100644 --- a/src/emitter/rewriting/mod.rs +++ b/src/emitter/rewriting/mod.rs @@ -3,7 +3,7 @@ pub mod rules; pub mod visiting_emitter; use crate::common::error::{ErrorGen, WhammError}; -use crate::parser::types::{BinOp, DataType, Expr, Statement, UnOp, Value}; +use crate::parser::types::{BinOp, Block, DataType, Expr, Statement, UnOp, Value}; use crate::verifier::types::{Record, SymbolTable, VarAddr}; use crate::emitter::rewriting::module_emitter::MemoryTracker; @@ -14,7 +14,7 @@ use orca::{InitExpr, ModuleBuilder}; use wasmparser::{BlockType, ValType}; pub trait Emitter { - fn emit_body(&mut self, body: &mut [Statement]) -> Result>; + fn emit_body(&mut self, body: &mut Block) -> Result>; fn emit_stmt(&mut self, stmt: &mut Statement) -> Result>; fn emit_expr(&mut self, expr: &mut Expr) -> Result>; } @@ -31,13 +31,13 @@ pub trait Emitter { // ================================================================== fn emit_body<'a, T: Opcode<'a> + ModuleBuilder>( - body: &mut [Statement], + body: &mut Block, injector: &mut T, table: &mut SymbolTable, mem_tracker: &MemoryTracker, err_msg: &str, ) -> Result> { - for stmt in body.iter_mut() { + for stmt in body.stmts.iter_mut() { emit_stmt(stmt, injector, table, mem_tracker, err_msg)?; } Ok(true) @@ -53,32 +53,18 @@ fn emit_stmt<'a, T: Opcode<'a> + ModuleBuilder>( match stmt { Statement::Decl { .. } => emit_decl_stmt(stmt, injector, table, err_msg), Statement::Assign { .. } => emit_assign_stmt(stmt, injector, table, mem_tracker, err_msg), - Statement::Expr { expr, .. } => emit_expr(expr, injector, table, mem_tracker, err_msg), + Statement::Expr { expr, .. } | Statement::Return { expr, .. } => { + emit_expr(expr, injector, table, mem_tracker, err_msg) + } Statement::If { cond, conseq, alt, .. } => { if alt.stmts.is_empty() { - emit_if( - cond, - conseq.stmts.as_mut_slice(), - injector, - table, - mem_tracker, - err_msg, - ) + emit_if(cond, conseq, injector, table, mem_tracker, err_msg) } else { - emit_if_else( - cond, - conseq.stmts.as_mut_slice(), - alt.stmts.as_mut_slice(), - injector, - table, - mem_tracker, - err_msg, - ) + emit_if_else(cond, conseq, alt, injector, table, mem_tracker, err_msg) } } - Statement::Return { .. } => unimplemented!(), } } @@ -287,6 +273,15 @@ pub fn whamm_type_to_wasm(ty: &DataType) -> Global { DataType::AssumeGood => unimplemented!(), } } +pub fn block_type_to_wasm(block: &Block) -> BlockType { + match &block.return_ty { + None => BlockType::Empty, + Some(return_ty) => { + let wasm_ty = whamm_type_to_wasm(return_ty).ty.content_type; + BlockType::Type(wasm_ty) + } + } +} fn emit_set<'a, T: Opcode<'a>>( var_id: &mut Expr, @@ -345,7 +340,7 @@ fn emit_set<'a, T: Opcode<'a>>( fn emit_if_preamble<'a, T: Opcode<'a> + ModuleBuilder>( condition: &mut Expr, - conseq: &mut [Statement], + conseq: &mut Block, injector: &mut T, table: &mut SymbolTable, mem_tracker: &MemoryTracker, @@ -356,7 +351,7 @@ fn emit_if_preamble<'a, T: Opcode<'a> + ModuleBuilder>( // emit the condition of the `if` expression is_success &= emit_expr(condition, injector, table, mem_tracker, err_msg)?; // emit the beginning of the if block - injector.if_stmt(BlockType::Empty); + injector.if_stmt(block_type_to_wasm(conseq)); // emit the consequent body is_success &= emit_body(conseq, injector, table, mem_tracker, err_msg)?; @@ -368,8 +363,8 @@ fn emit_if_preamble<'a, T: Opcode<'a> + ModuleBuilder>( fn emit_if_else_preamble<'a, T: Opcode<'a> + ModuleBuilder>( condition: &mut Expr, - conseq: &mut [Statement], - alternate: &mut [Statement], + conseq: &mut Block, + alternate: &mut Block, injector: &mut T, table: &mut SymbolTable, mem_tracker: &MemoryTracker, @@ -392,7 +387,7 @@ fn emit_if_else_preamble<'a, T: Opcode<'a> + ModuleBuilder>( fn emit_if<'a, T: Opcode<'a> + ModuleBuilder>( condition: &mut Expr, - conseq: &mut [Statement], + conseq: &mut Block, injector: &mut T, table: &mut SymbolTable, mem_tracker: &MemoryTracker, @@ -409,8 +404,8 @@ fn emit_if<'a, T: Opcode<'a> + ModuleBuilder>( fn emit_if_else<'a, T: Opcode<'a> + ModuleBuilder>( condition: &mut Expr, - conseq: &mut [Statement], - alternate: &mut [Statement], + conseq: &mut Block, + alternate: &mut Block, injector: &mut T, table: &mut SymbolTable, mem_tracker: &MemoryTracker, @@ -458,14 +453,22 @@ fn emit_expr<'a, T: Opcode<'a> + ModuleBuilder>( // change conseq and alt types to stmt for easier API call is_success &= emit_if_else( cond, - &mut vec![Statement::Expr { - expr: (**conseq).clone(), + &mut Block { + stmts: vec![Statement::Expr { + expr: (**conseq).clone(), + loc: None, + }], + return_ty: None, loc: None, - }], - &mut vec![Statement::Expr { - expr: (**alt).clone(), + }, + &mut Block { + stmts: vec![Statement::Expr { + expr: (**alt).clone(), + loc: None, + }], + return_ty: None, loc: None, - }], + }, injector, table, mem_tracker, diff --git a/src/emitter/rewriting/module_emitter.rs b/src/emitter/rewriting/module_emitter.rs index c32f7748..fa895cfa 100644 --- a/src/emitter/rewriting/module_emitter.rs +++ b/src/emitter/rewriting/module_emitter.rs @@ -1,11 +1,11 @@ use crate::common::error::{ErrorGen, WhammError}; -use crate::parser::types::{DataType, Definition, Expr, Fn, Statement, Value}; +use crate::parser::types::{Block, DataType, Definition, Expr, Fn, Statement, Value}; use crate::verifier::types::{Record, SymbolTable, VarAddr}; use orca::{DataSegment, DataSegmentKind, InitExpr}; use std::collections::HashMap; use orca::ir::types::{DataType as OrcaType, Value as OrcaValue}; -use wasmparser::BlockType; +use wasmparser::{BlockType, GlobalType}; use crate::emitter::rewriting::{emit_body, emit_expr, emit_stmt, whamm_type_to_wasm, Emitter}; use orca::ir::function::FunctionBuilder; @@ -292,6 +292,26 @@ impl<'a, 'b, 'c, 'd> ModuleEmitter<'a, 'b, 'c, 'd> { } } + pub(crate) fn emit_global_getter( + &mut self, + global_id: &u32, + name: String, + ty: &GlobalType, + ) -> Result> { + let getter_params = vec![]; + let getter_res = vec![OrcaType::from(ty.content_type)]; + + let mut getter = FunctionBuilder::new(&getter_params, &getter_res); + getter.global_get(*global_id); + + let getter_id = getter.finish(self.app_wasm); + + let fn_name = format!("get_{name}"); + self.app_wasm.add_export_func(fn_name.leak(), getter_id); + + Ok(true) + } + pub(crate) fn emit_global( &mut self, name: String, @@ -313,33 +333,39 @@ impl<'a, 'b, 'c, 'd> ModuleEmitter<'a, 'b, 'c, 'd> { }; let rec = self.table.get_record_mut(&rec_id); - match rec { + let (global_id, ty) = match rec { Some(Record::Var { ref mut addr, .. }) => { // emit global variable and set addr in symbol table // this is used for user-defined global vars in the script... let default_global = whamm_type_to_wasm(&ty); - let global_id = self.app_wasm.add_global(default_global); + let global_id = self.app_wasm.add_global(default_global.clone()); *addr = Some(VarAddr::Global { addr: global_id }); - Ok(true) + (global_id, default_global.ty) } - Some(&mut ref ty) => Err(Box::new(ErrorGen::get_unexpected_error( - true, - Some(format!( - "{UNEXPECTED_ERR_MSG} \ + Some(&mut ref ty) => { + return Err(Box::new(ErrorGen::get_unexpected_error( + true, + Some(format!( + "{UNEXPECTED_ERR_MSG} \ Incorrect global variable record, expected Record::Var, found: {:?}", - ty - )), - None, - ))), - None => Err(Box::new(ErrorGen::get_unexpected_error( - true, - Some(format!( - "{UNEXPECTED_ERR_MSG} \ + ty + )), + None, + ))) + } + None => { + return Err(Box::new(ErrorGen::get_unexpected_error( + true, + Some(format!( + "{UNEXPECTED_ERR_MSG} \ Global variable symbol does not exist!" - )), - None, - ))), - } + )), + None, + ))) + } + }; + + self.emit_global_getter(&global_id, name, &ty) } pub fn emit_global_stmts(&mut self, stmts: &mut [Statement]) -> Result> { @@ -389,7 +415,7 @@ impl<'a, 'b, 'c, 'd> ModuleEmitter<'a, 'b, 'c, 'd> { } } impl Emitter for ModuleEmitter<'_, '_, '_, '_> { - fn emit_body(&mut self, body: &mut [Statement]) -> Result> { + fn emit_body(&mut self, body: &mut Block) -> Result> { if let Some(emitting_func) = &mut self.emitting_func { emit_body( body, diff --git a/src/emitter/rewriting/visiting_emitter.rs b/src/emitter/rewriting/visiting_emitter.rs index 505522ac..cc1010b9 100644 --- a/src/emitter/rewriting/visiting_emitter.rs +++ b/src/emitter/rewriting/visiting_emitter.rs @@ -1,10 +1,10 @@ use crate::common::error::{ErrorGen, WhammError}; -use crate::emitter::rewriting::emit_expr; use crate::emitter::rewriting::module_emitter::MemoryTracker; use crate::emitter::rewriting::rules::{Arg, LocInfo, Provider, WhammProvider}; +use crate::emitter::rewriting::{block_type_to_wasm, emit_expr}; use crate::emitter::rewriting::{emit_stmt, Emitter}; use crate::generator::types::ExprFolder; -use crate::parser::types::{DataType, Definition, Expr, ProbeSpec, Statement, Value}; +use crate::parser::types::{Block, DataType, Definition, Expr, ProbeSpec, Statement, Value}; use crate::verifier::types::{Record, SymbolTable, VarAddr}; use orca::ir::module::Module; use orca::iterator::iterator_trait::Iterator as OrcaIterator; @@ -12,7 +12,6 @@ use orca::iterator::module_iterator::ModuleIterator; use orca::opcode::Opcode; use orca::ModuleBuilder; use std::iter::Iterator; -use wasmparser::BlockType; const UNEXPECTED_ERR_MSG: &str = "VisitingEmitter: Looks like you've found a bug...please report this behavior!"; @@ -87,6 +86,8 @@ impl<'a, 'b, 'c, 'd> VisitingEmitter<'a, 'b, 'c, 'd> { // So, we can just save off the first * items in the stack as the args // to the call. let mut arg_recs: Vec<(String, usize)> = vec![]; // vec to retain order! + + let mut arg_locals: Vec<(String, u32)> = vec![]; args.iter().for_each( |Arg { name: arg_name, @@ -94,9 +95,17 @@ impl<'a, 'b, 'c, 'd> VisitingEmitter<'a, 'b, 'c, 'd> { }| { // create local for the param in the module let arg_local_id = self.app_iter.add_local(arg_ty.clone()); + arg_locals.push((arg_name.to_string(), arg_local_id)); + }, + ); + // Save args in reverse order (the leftmost arg is at the bottom of the stack) + arg_locals + .iter() + .rev() + .for_each(|(arg_name, arg_local_id)| { // emit an opcode in the event to assign the ToS to this new local - self.app_iter.local_set(arg_local_id); + self.app_iter.local_set(*arg_local_id); // place in symbol table with var addr for future reference let id = self.table.put( @@ -106,13 +115,14 @@ impl<'a, 'b, 'c, 'd> VisitingEmitter<'a, 'b, 'c, 'd> { name: arg_name.to_string(), value: None, is_comp_provided: false, - addr: Some(VarAddr::Local { addr: arg_local_id }), + addr: Some(VarAddr::Local { + addr: *arg_local_id, + }), loc: None, }, ); - arg_recs.push((arg_name.to_string(), id)); - }, - ); + arg_recs.insert(0, (arg_name.to_string(), id)); + }); self.instr_created_args = arg_recs; true } @@ -201,14 +211,14 @@ impl<'a, 'b, 'c, 'd> VisitingEmitter<'a, 'b, 'c, 'd> { pub fn emit_if( &mut self, condition: &mut Expr, - conseq: &mut [Statement], + conseq: &mut Block, ) -> Result> { let mut is_success = true; // emit the condition of the `if` expression is_success &= self.emit_expr(condition)?; // emit the beginning of the if block - self.app_iter.if_stmt(BlockType::Empty); + self.app_iter.if_stmt(block_type_to_wasm(conseq)); is_success &= self.emit_body(conseq)?; @@ -220,14 +230,14 @@ impl<'a, 'b, 'c, 'd> VisitingEmitter<'a, 'b, 'c, 'd> { pub(crate) fn emit_if_with_orig_as_else( &mut self, condition: &mut Expr, - conseq: &mut [Statement], + conseq: &mut Block, ) -> Result> { let mut is_success = true; // emit the condition of the `if` expression is_success &= self.emit_expr(condition)?; // emit the beginning of the if block - self.app_iter.if_stmt(BlockType::Empty); + self.app_iter.if_stmt(block_type_to_wasm(conseq)); is_success &= self.emit_body(conseq)?; @@ -315,9 +325,9 @@ impl<'a, 'b, 'c, 'd> VisitingEmitter<'a, 'b, 'c, 'd> { } } impl Emitter for VisitingEmitter<'_, '_, '_, '_> { - fn emit_body(&mut self, body: &mut [Statement]) -> Result> { + fn emit_body(&mut self, body: &mut Block) -> Result> { let mut is_success = true; - for stmt in body.iter_mut() { + for stmt in body.stmts.iter_mut() { is_success &= self.emit_stmt(stmt)?; } Ok(is_success) diff --git a/src/generator/init_generator.rs b/src/generator/init_generator.rs index 52005db5..668b79e1 100644 --- a/src/generator/init_generator.rs +++ b/src/generator/init_generator.rs @@ -255,7 +255,7 @@ impl WhammVisitorMut for InitGenerator<'_, '_, '_, '_, '_> { is_success &= self.visit_expr(pred); } if let Some(body) = probe.body_mut() { - is_success &= self.visit_stmts(body); + is_success &= self.visit_stmts(body.stmts.as_mut_slice()); } trace!("Exiting: CodeGenerator::visit_probe"); diff --git a/src/generator/instr_generator.rs b/src/generator/instr_generator.rs index d3b01210..9243f8d6 100644 --- a/src/generator/instr_generator.rs +++ b/src/generator/instr_generator.rs @@ -4,7 +4,7 @@ use crate::emitter::rewriting::visiting_emitter::VisitingEmitter; use crate::emitter::rewriting::Emitter; use crate::generator::simple_ast::SimpleAST; use crate::generator::types::ExprFolder; -use crate::parser::types::{Expr, Statement}; +use crate::parser::types::{Block, Expr}; const UNEXPECTED_ERR_MSG: &str = "InstrGenerator: Looks like you've found a bug...please report this behavior!"; @@ -30,7 +30,7 @@ pub struct InstrGenerator<'a, 'b, 'c, 'd, 'e> { curr_instr_args: Vec, curr_probe_mode: String, /// The current probe's body and predicate - curr_probe: Option<(Option>, Option)>, + curr_probe: Option<(Option, Option)>, } impl<'a, 'b, 'c, 'd, 'e> InstrGenerator<'a, 'b, 'c, 'd, 'e> { pub fn new( @@ -150,25 +150,16 @@ impl InstrGenerator<'_, '_, '_, '_, '_> { if self.pred_is_true() { // The predicate has been reduced to a 'true', emit un-predicated body self.emit_body(); - // Place the original arguments back on the stack. - if let Err(e) = self.emitter.emit_args() { - self.err.add_error(*e); - return false; + if self.curr_probe_mode != "alt" { + self.replace_args(); } } else { // The predicate still has some conditionals (remember we already checked for // it being false in run() above) match self.curr_probe_mode.as_str() { - "before" => { - is_success &= self.emit_probe_as_if(); - // Place the original arguments back on the stack. - if let Err(e) = self.emitter.emit_args() { - self.err.add_error(*e); - return false; - } - } - "after" => { + "before" | "after" => { is_success &= self.emit_probe_as_if(); + self.replace_args(); } "alt" => { is_success &= self.emit_probe_as_if_else(); @@ -200,6 +191,15 @@ impl InstrGenerator<'_, '_, '_, '_, '_> { true } } + fn replace_args(&mut self) -> bool { + // Place the original arguments back on the stack. + self.emitter.before(); + if let Err(e) = self.emitter.emit_args() { + self.err.add_error(*e); + return false; + } + true + } fn pred_is_true(&mut self) -> bool { if let Some((.., pred)) = &self.curr_probe { diff --git a/src/generator/simple_ast.rs b/src/generator/simple_ast.rs index d7b54ecb..d41eea22 100644 --- a/src/generator/simple_ast.rs +++ b/src/generator/simple_ast.rs @@ -63,7 +63,7 @@ pub type SimpleAstProbes = pub struct SimpleProbe { pub script_id: String, pub predicate: Option, - pub body: Option>, + pub body: Option, } impl SimpleProbe { fn new(script_id: String, probe: &dyn Probe) -> Self { diff --git a/src/main.rs b/src/main.rs index f27c45e0..b4af307f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -59,13 +59,7 @@ fn try_main() -> Result<(), failure::Error> { run_info(spec, globals, functions); } Cmd::Instr(args) => { - run_instr( - args.app, - args.script, - args.output_path, - args.virgil, - args.run_verifier, - ); + run_instr(args.app, args.script, args.output_path, args.virgil); } } @@ -92,14 +86,13 @@ fn run_instr( script_path: String, output_wasm_path: String, _emit_virgil: bool, - run_verifier: bool, ) { // Set up error reporting mechanism let mut err = ErrorGen::new(script_path.clone(), "".to_string(), MAX_ERRORS); // Process the script let mut whamm = get_script_ast(&script_path, &mut err); - let mut symbol_table = get_symbol_table(&mut whamm, run_verifier, &mut err); + let mut symbol_table = get_symbol_table(&mut whamm, &mut err); let simple_ast = build_simple_ast(&whamm, &mut err); err.check_too_many(); @@ -160,15 +153,15 @@ fn run_instr( err.check_has_errors(); } -fn get_symbol_table(ast: &mut Whamm, run_verifier: bool, err: &mut ErrorGen) -> SymbolTable { +fn get_symbol_table(ast: &mut Whamm, err: &mut ErrorGen) -> SymbolTable { let mut st = build_symbol_table(ast, err); err.check_too_many(); - verify_ast(ast, &mut st, run_verifier, err); + verify_ast(ast, &mut st, err); st } -fn verify_ast(ast: &Whamm, st: &mut SymbolTable, run_verifier: bool, err: &mut ErrorGen) { - if run_verifier && !type_check(ast, st, err) { +fn verify_ast(ast: &mut Whamm, st: &mut SymbolTable, err: &mut ErrorGen) { + if !type_check(ast, st, err) { error!("AST failed verification!"); } err.check_too_many(); diff --git a/src/parser/print_visitor.rs b/src/parser/print_visitor.rs index a87d4207..a3aad7e4 100644 --- a/src/parser/print_visitor.rs +++ b/src/parser/print_visitor.rs @@ -316,7 +316,7 @@ impl WhammVisitor for AsStrVisitor { self.increase_indent(); match probe.body() { Some(b) => { - for stmt in b { + for stmt in b.stmts.iter() { s += &format!("{} {};{}", self.get_indent(), self.visit_stmt(stmt), NL) } } diff --git a/src/parser/rules/core.rs b/src/parser/rules/core.rs index 365740da..e95a9640 100644 --- a/src/parser/rules/core.rs +++ b/src/parser/rules/core.rs @@ -2,9 +2,7 @@ use crate::parser::rules::{ event_factory, mode_factory, print_mode_docs, Event, EventInfo, FromStr, Mode, ModeInfo, NameOptions, Package, PackageInfo, Probe, }; -use crate::parser::types::{ - Expr, Location, ProbeSpec, ProvidedFunction, ProvidedGlobal, Statement, -}; +use crate::parser::types::{Block, Expr, Location, ProbeSpec, ProvidedFunction, ProvidedGlobal}; use std::collections::HashMap; use termcolor::Buffer; @@ -137,7 +135,7 @@ impl Package for CorePackage { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool) { match self { @@ -257,7 +255,7 @@ impl Event for CoreEvent { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, ) -> bool { let mut matched_modes = false; let probes = self.probes_mut(); @@ -364,7 +362,7 @@ struct CoreProbe { pub loc: Option, pub predicate: Option, - pub body: Option>, + pub body: Option, } impl Probe for CoreProbe { fn mode_name(&self) -> String { @@ -377,11 +375,11 @@ impl Probe for CoreProbe { &mut self.predicate } - fn body(&self) -> &Option> { + fn body(&self) -> &Option { &self.body } - fn body_mut(&mut self) -> &mut Option> { + fn body_mut(&mut self) -> &mut Option { &mut self.body } @@ -412,7 +410,7 @@ impl CoreProbe { mode: CoreMode, loc: Option, predicate: Option, - body: Option>, + body: Option, ) -> Self { Self { mode, diff --git a/src/parser/rules/mod.rs b/src/parser/rules/mod.rs index ee5d26ab..7a4ca1ae 100644 --- a/src/parser/rules/mod.rs +++ b/src/parser/rules/mod.rs @@ -6,8 +6,8 @@ use crate::common::terminal::{magenta_italics, white}; use crate::parser::rules::core::CorePackage; use crate::parser::rules::wasm::WasmPackage; use crate::parser::types::{ - print_fns, print_global_vars, Expr, Location, ProbeSpec, ProvidedFunction, ProvidedGlobal, - SpecPart, Statement, + print_fns, print_global_vars, Block, Expr, Location, ProbeSpec, ProvidedFunction, + ProvidedGlobal, SpecPart, }; use glob::Pattern; use std::collections::HashMap; @@ -60,7 +60,7 @@ pub trait Provider { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool, bool); } @@ -74,7 +74,7 @@ pub fn provider_factory( probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> Result<(bool, bool, bool, bool), Box> { if let Some(SpecPart { @@ -273,7 +273,7 @@ pub trait Package { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool); } @@ -299,7 +299,7 @@ fn package_factory( probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool, bool) { if let Some(SpecPart { @@ -393,8 +393,8 @@ pub trait Probe { fn mode_name(&self) -> String; fn predicate(&self) -> &Option; fn predicate_mut(&mut self) -> &mut Option; - fn body(&self) -> &Option>; - fn body_mut(&mut self) -> &mut Option>; + fn body(&self) -> &Option; + fn body_mut(&mut self) -> &mut Option; fn print_mode_docs( &self, print_globals: bool, @@ -427,7 +427,7 @@ pub trait Event { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, ) -> bool; } @@ -450,7 +450,7 @@ fn event_factory( probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool) { if let Some(SpecPart { @@ -781,7 +781,7 @@ impl Provider for WhammProvider { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool, bool) { match self { @@ -935,7 +935,7 @@ pub struct WhammProbe { pub loc: Option, pub predicate: Option, - pub body: Option>, + pub body: Option, } impl Probe for WhammProbe { fn mode_name(&self) -> String { @@ -948,11 +948,11 @@ impl Probe for WhammProbe { &mut self.predicate } - fn body(&self) -> &Option> { + fn body(&self) -> &Option { &self.body } - fn body_mut(&mut self) -> &mut Option> { + fn body_mut(&mut self) -> &mut Option { &mut self.body } @@ -983,7 +983,7 @@ impl WhammProbe { mode: WhammMode, loc: Option, predicate: Option, - body: Option>, + body: Option, ) -> Self { Self { mode, diff --git a/src/parser/rules/wasm.rs b/src/parser/rules/wasm.rs index 3f2ecdaf..938d652a 100644 --- a/src/parser/rules/wasm.rs +++ b/src/parser/rules/wasm.rs @@ -3,7 +3,7 @@ use crate::parser::rules::{ PackageInfo, Probe, WhammMode, WhammProbe, }; use crate::parser::types::{ - DataType, Expr, Location, ProbeSpec, ProvidedFunction, ProvidedGlobal, Statement, + Block, DataType, Expr, Location, ProbeSpec, ProvidedFunction, ProvidedGlobal, }; use std::collections::HashMap; use termcolor::Buffer; @@ -152,7 +152,7 @@ impl Package for WasmPackage { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, printing_info: bool, ) -> (bool, bool) { match self { @@ -477,6 +477,9 @@ impl OpcodeEvent { } } fn call(loc: Option) -> Self { + // TODO add the following functionality: + // - `result` global + // - let fns = vec![ProvidedFunction::new( "alt_call_by_id".to_string(), "Insert an alternate call (targeting the passed function ID) into the Wasm bytecode. Will also emit the original parameters onto the stack.".to_string(), @@ -1582,7 +1585,7 @@ impl Event for OpcodeEvent { probe_spec: &ProbeSpec, loc: Option, predicate: Option, - body: Option>, + body: Option, ) -> bool { let mut matched_modes = false; let probes = self.probes_mut(); diff --git a/src/parser/tests.rs b/src/parser/tests.rs index 1e968a0a..7627ca85 100644 --- a/src/parser/tests.rs +++ b/src/parser/tests.rs @@ -493,7 +493,7 @@ wasm::call:alt / // probe body assert!(&probe.body().is_some()); - assert_eq!(1, probe.body().as_ref().unwrap().len()); + assert_eq!(1, probe.body().as_ref().unwrap().stmts.len()); print_ast(&ast); diff --git a/src/parser/types.rs b/src/parser/types.rs index df1a12a0..0e0c7d88 100644 --- a/src/parser/types.rs +++ b/src/parser/types.rs @@ -186,6 +186,7 @@ pub enum Value { #[derive(Clone, Debug)] pub struct Block { pub stmts: Vec, + pub return_ty: Option, pub loc: Option, } impl Block { @@ -795,7 +796,7 @@ impl Script { &mut self, probe_spec: &ProbeSpec, predicate: Option, - body: Option>, + body: Option, ) -> Result<(), Box> { let (matched_providers, matched_packages, matched_events, matched_modes): ( bool, @@ -967,6 +968,7 @@ impl ProvidedFunction { return_ty, body: Block { stmts: vec![], + return_ty: None, loc: None, }, }, diff --git a/src/parser/whamm_parser.rs b/src/parser/whamm_parser.rs index c66ffe97..85dc4187 100644 --- a/src/parser/whamm_parser.rs +++ b/src/parser/whamm_parser.rs @@ -188,13 +188,17 @@ pub fn process_pair(whamm: &mut Whamm, script_count: usize, pair: Pair, er None => (None, None), }; - let this_body: Option> = this_body.map(|b| { + let this_body: Option = this_body.map(|b| { let mut stmts = vec![]; //each stmt_from_rule returns a vector for stmt in b { stmts.push(stmt); } - stmts + Block { + stmts, + return_ty: None, + loc: None, + } }); // Add probe definition to the script @@ -216,6 +220,7 @@ pub fn process_pair(whamm: &mut Whamm, script_count: usize, pair: Pair, er let mut return_ty = DataType::Tuple { ty_info: vec![] }; let mut body = Block { stmts: vec![], + return_ty: None, loc: Some(Location { line_col: fn_name_line_col.clone(), path: None, @@ -346,6 +351,7 @@ pub fn block_from_rule(pair: Pair, err: &mut ErrorGen) -> Block { //create the block object and return it in the wrapper with result Block { stmts: body_vec, + return_ty: None, loc: Some(Location { line_col: block_loc, path: None, @@ -440,6 +446,7 @@ fn alt_from_rule(pair: Pair, err: &mut ErrorGen) -> Block { err.add_errors(errors); return Block { stmts: vec![], + return_ty: None, loc: Some(Location { line_col: alt_loc, path: None, @@ -457,6 +464,7 @@ fn alt_from_rule(pair: Pair, err: &mut ErrorGen) -> Block { conseq: inner_block, alt: Block { stmts: vec![], + return_ty: None, loc: Some(Location { line_col: alt_loc.clone(), path: None, @@ -467,6 +475,7 @@ fn alt_from_rule(pair: Pair, err: &mut ErrorGen) -> Block { path: None, }), }], + return_ty: None, loc: Some(Location { line_col: alt_loc.clone(), path: None, @@ -484,6 +493,7 @@ fn alt_from_rule(pair: Pair, err: &mut ErrorGen) -> Block { path: None, }), }], + return_ty: None, loc: Some(Location { line_col: alt_loc.clone(), path: None, @@ -500,6 +510,7 @@ fn alt_from_rule(pair: Pair, err: &mut ErrorGen) -> Block { ); Block { stmts: vec![], + return_ty: None, loc: Some(Location { line_col: alt_loc, path: None, @@ -867,6 +878,7 @@ fn stmt_from_rule(pair: Pair, err: &mut ErrorGen) -> Vec { conseq: block, alt: Block { stmts: vec![], + return_ty: None, loc: Some(Location { line_col: if_stmt_line_col.clone(), path: None, diff --git a/src/verifier/tests.rs b/src/verifier/tests.rs index b41e46fb..b39d0c25 100644 --- a/src/verifier/tests.rs +++ b/src/verifier/tests.rs @@ -372,7 +372,7 @@ fn is_valid_script(script: &str, err: &mut ErrorGen) -> bool { match tests::get_ast(script, err) { Some(mut ast) => { let mut table = verifier::build_symbol_table(&mut ast, err); - verifier::type_check(&ast, &mut table, err) + verifier::type_check(&mut ast, &mut table, err) } None => { error!("Should fail at type checking, not parsing: {}", script); @@ -415,7 +415,7 @@ pub fn test_template() { match tests::get_ast(script, &mut err) { Some(mut ast) => { let mut table = verifier::build_symbol_table(&mut ast, &mut err); - let res = verifier::type_check(&ast, &mut table, &mut err); + let res = verifier::type_check(&mut ast, &mut table, &mut err); err.report(); assert!(!err.has_errors); assert!(res); @@ -460,7 +460,7 @@ pub fn expect_fatal_error() { match tests::get_ast(script, &mut err) { Some(mut ast) => { let mut table = verifier::build_symbol_table(&mut ast, &mut err); - let res = verifier::type_check(&ast, &mut table, &mut err); + let res = verifier::type_check(&mut ast, &mut table, &mut err); err.report(); assert!(err.has_errors); assert!(!res); @@ -490,7 +490,7 @@ pub fn test_recursive_calls() { match tests::get_ast(script, &mut err) { Some(mut ast) => { let mut table = verifier::build_symbol_table(&mut ast, &mut err); - let res = verifier::type_check(&ast, &mut table, &mut err); + let res = verifier::type_check(&mut ast, &mut table, &mut err); err.report(); assert!(!err.has_errors); assert!(res); diff --git a/src/verifier/verifier.rs b/src/verifier/verifier.rs index 7a88a6d5..5e0c4353 100644 --- a/src/verifier/verifier.rs +++ b/src/verifier/verifier.rs @@ -4,7 +4,7 @@ use crate::common::error::ErrorGen; use crate::parser::rules::{Event, Package, Probe, Provider}; use crate::parser::types::{ BinOp, Block, DataType, Definition, Expr, Fn, Location, Script, Statement, UnOp, Value, Whamm, - WhammVisitor, WhammVisitorMut, + WhammVisitorMut, }; use crate::verifier::builder_visitor::SymbolTableBuilder; use crate::verifier::types::{Record, SymbolTable}; @@ -111,8 +111,8 @@ impl TypeChecker<'_> { } } -impl WhammVisitor> for TypeChecker<'_> { - fn visit_whamm(&mut self, whamm: &Whamm) -> Option { +impl WhammVisitorMut> for TypeChecker<'_> { + fn visit_whamm(&mut self, whamm: &mut Whamm) -> Option { // not printing events and globals now self.table.reset(); @@ -123,25 +123,25 @@ impl WhammVisitor> for TypeChecker<'_> { // skip the compiler provided functions // we only need to type check user provided functions - whamm.scripts.iter().for_each(|script| { + whamm.scripts.iter_mut().for_each(|script| { self.visit_script(script); }); None } - fn visit_script(&mut self, script: &Script) -> Option { + fn visit_script(&mut self, script: &mut Script) -> Option { self.table.enter_named_scope(&script.name); self.in_script_global = true; - script.global_stmts.iter().for_each(|stmt| { + script.global_stmts.iter_mut().for_each(|stmt| { self.visit_stmt(stmt); }); self.in_script_global = false; - script.fns.iter().for_each(|function| { + script.fns.iter_mut().for_each(|function| { self.visit_fn(function); }); - script.providers.iter().for_each(|(_, provider)| { + script.providers.iter_mut().for_each(|(_, provider)| { self.visit_provider(provider); }); @@ -149,10 +149,10 @@ impl WhammVisitor> for TypeChecker<'_> { None } - fn visit_provider(&mut self, provider: &Box) -> Option { + fn visit_provider(&mut self, provider: &mut Box) -> Option { let _ = self.table.enter_scope(); - provider.packages().for_each(|package| { + provider.packages_mut().for_each(|package| { self.visit_package(package); }); @@ -160,10 +160,10 @@ impl WhammVisitor> for TypeChecker<'_> { None } - fn visit_package(&mut self, package: &dyn Package) -> Option { + fn visit_package(&mut self, package: &mut dyn Package) -> Option { let _ = self.table.enter_scope(); - package.events().for_each(|event| { + package.events_mut().for_each(|event| { self.visit_event(event); }); @@ -172,11 +172,11 @@ impl WhammVisitor> for TypeChecker<'_> { None } - fn visit_event(&mut self, event: &dyn Event) -> Option { + fn visit_event(&mut self, event: &mut dyn Event) -> Option { let _ = self.table.enter_scope(); - event.probes().iter().for_each(|(_, probe)| { - probe.iter().for_each(|probe| { + event.probes_mut().iter_mut().for_each(|(_, probe)| { + probe.iter_mut().for_each(|probe| { self.visit_probe(probe); }); }); @@ -186,11 +186,11 @@ impl WhammVisitor> for TypeChecker<'_> { None } - fn visit_probe(&mut self, probe: &Box) -> Option { + fn visit_probe(&mut self, probe: &mut Box) -> Option { let _ = self.table.enter_scope(); // type check predicate - if let Some(predicate) = &probe.predicate() { + if let Some(predicate) = &mut probe.predicate_mut() { let predicate_loc = predicate.loc().clone().unwrap(); if let Some(ty) = self.visit_expr(predicate) { if ty != DataType::Boolean { @@ -204,10 +204,8 @@ impl WhammVisitor> for TypeChecker<'_> { } // type check action - if let Some(body) = &probe.body() { - for stmt in body { - self.visit_stmt(stmt); - } + if let Some(body) = &mut probe.body_mut() { + self.visit_block(body); } let _ = self.table.exit_scope(); @@ -215,12 +213,12 @@ impl WhammVisitor> for TypeChecker<'_> { None } - fn visit_fn(&mut self, function: &Fn) -> Option { + fn visit_fn(&mut self, function: &mut Fn) -> Option { // TODO: not typechecking user provided functions yet // type check body self.table.enter_named_scope(&function.name.name); - if let Some(check_ret_type) = self.visit_block(&function.body) { + if let Some(check_ret_type) = self.visit_block(&mut function.body) { //figure out how to deal with void functions (return type is ()) if check_ret_type != function.return_ty { self.err.type_check_error( @@ -239,16 +237,16 @@ impl WhammVisitor> for TypeChecker<'_> { Some(function.return_ty.clone()) } - fn visit_formal_param(&mut self, _param: &(Expr, DataType)) -> Option { + fn visit_formal_param(&mut self, _param: &mut (Expr, DataType)) -> Option { unimplemented!() } - fn visit_block(&mut self, block: &Block) -> Option { + fn visit_block(&mut self, block: &mut Block) -> Option { let mut ret_type = None; let num_statements = block.stmts.len(); let start_of_range: usize; for i in 0..num_statements { - let temp = self.visit_stmt(&block.stmts[i]); + let temp = self.visit_stmt(&mut block.stmts[i]); if temp.is_some() && ret_type.is_none() { ret_type = temp; } else if ret_type.is_some() { @@ -268,14 +266,16 @@ impl WhammVisitor> for TypeChecker<'_> { .to_string(), Some(loc.line_col), ); + block.return_ty = ret_type.clone(); return ret_type; } } + block.return_ty = ret_type.clone(); //add a check for return statement type matching the function return type if provided ret_type } - fn visit_stmt(&mut self, stmt: &Statement) -> Option { + fn visit_stmt(&mut self, stmt: &mut Statement) -> Option { if self.in_script_global { match stmt { //allow declarations and assignment @@ -399,7 +399,7 @@ impl WhammVisitor> for TypeChecker<'_> { } } - fn visit_expr(&mut self, expr: &Expr) -> Option { + fn visit_expr(&mut self, expr: &mut Expr) -> Option { match expr { Expr::Primitive { val, .. } => self.visit_value(val), Expr::BinOp { lhs, rhs, op, .. } => { @@ -717,19 +717,19 @@ impl WhammVisitor> for TypeChecker<'_> { } } - fn visit_unop(&mut self, _unop: &UnOp) -> Option { + fn visit_unop(&mut self, _unop: &mut UnOp) -> Option { unimplemented!() } - fn visit_binop(&mut self, _binop: &BinOp) -> Option { + fn visit_binop(&mut self, _binop: &mut BinOp) -> Option { unimplemented!() } - fn visit_datatype(&mut self, _datatype: &DataType) -> Option { + fn visit_datatype(&mut self, _datatype: &mut DataType) -> Option { unimplemented!() } - fn visit_value(&mut self, val: &Value) -> Option { + fn visit_value(&mut self, val: &mut Value) -> Option { match val { Value::Integer { .. } => Some(DataType::I32), Value::Str { .. } => Some(DataType::Str), @@ -738,7 +738,7 @@ impl WhammVisitor> for TypeChecker<'_> { // this ty does not contain the DataType in ty_info // Whamm parser doesn't give the ty_info for Tuples let tys = vals - .iter() + .iter_mut() .map(|val| self.visit_expr(val)) .collect::>(); @@ -762,7 +762,7 @@ impl WhammVisitor> for TypeChecker<'_> { } } -pub fn type_check(ast: &Whamm, st: &mut SymbolTable, err: &mut ErrorGen) -> bool { +pub fn type_check(ast: &mut Whamm, st: &mut SymbolTable, err: &mut ErrorGen) -> bool { let mut type_checker = TypeChecker { table: st, err, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 9d1b6079..dcd8213c 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -17,7 +17,7 @@ use whamm::emitter::rewriting::visiting_emitter::VisitingEmitter; use whamm::generator::init_generator::InitGenerator; use whamm::generator::instr_generator::InstrGenerator; use whamm::generator::simple_ast::build_simple_ast; -use whamm::verifier::verifier::build_symbol_table; +use whamm::verifier::verifier::{build_symbol_table, type_check}; // ==================== // = Helper Functions = @@ -91,11 +91,14 @@ pub fn run_whamm( let mut whamm = ast_res.unwrap(); err.fatal_report("IntegrationTest"); - // Build the behavior tree from the AST - let simple_ast = build_simple_ast(&whamm, &mut err); - + // Verify phase let mut symbol_table = build_symbol_table(&mut whamm, &mut err); symbol_table.reset(); + type_check(&mut whamm, &mut symbol_table, &mut err); + err.fatal_report("IntegrationTest"); + + // Translate to the simple AST + let simple_ast = build_simple_ast(&whamm, &mut err); let mut app_wasm = WasmModule::parse(wasm_module_bytes, false).expect("Failed to parse Wasm module"); diff --git a/tests/common/wast_harness.rs b/tests/common/wast_harness.rs index ce9dc14d..a8e3e81f 100644 --- a/tests/common/wast_harness.rs +++ b/tests/common/wast_harness.rs @@ -1,15 +1,19 @@ use crate::common::{run_whamm, setup_logger, try_path}; use log::{debug, error}; -use std::fs::File; +use std::fs::{remove_dir_all, File}; use std::io::{BufRead, BufReader, Write}; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; use wabt::wat2wasm; + +const OUTPUT_DIR: &str = "output/tests/wast_suite"; const OUTPUT_WHAMMED_WAST: &str = "output/tests/wast_suite/should_pass"; const OUTPUT_UNINSTR_WAST: &str = "output/tests/wast_suite/should_fail"; pub fn main() -> Result<(), std::io::Error> { setup_logger(); + clean(); + // Find all the wast files to run as tests let wast_tests = find_wast_tests(); @@ -19,34 +23,17 @@ pub fn main() -> Result<(), std::io::Error> { let f = File::open(test.clone())?; let mut reader = BufReader::new(f); - // Convention: Only one module per wast! - let module_wat = get_wasm_module(&mut reader)?; - if module_wat.is_empty() { - panic!( - "Could not find the Wasm module in the wast file: {:?}", - test.clone() - ); - } - let module_wasm = match wat2wasm(module_wat.as_bytes()) { - Err(e) => { - panic!( - "Unable to convert wat to wasm for module: {}\nDue to error: {:?}", - module_wat, e - ); - } - Ok(res) => res, - }; + let test_setup = get_test_setup(&mut reader, &test)?; // Get the `whamm!` scripts and corresponding test cases for this module let test_cases = get_test_cases(reader); - debug!("{module_wat}\n"); - for test_case in test_cases.iter() { test_case.print(); } - match generate_should_fail_bin_wast(&module_wasm, &test_cases, &test) { + // TODO -- retain original dir structure + match generate_should_fail_bin_wast(&test_setup, &test_cases, &test) { Err(e) => { panic!( "Unable to write UN-instrumented wast file due to error: {:?}", @@ -58,7 +45,8 @@ pub fn main() -> Result<(), std::io::Error> { } }; - match generate_instrumented_bin_wast(&module_wasm, &test_cases, &test) { + // TODO -- retain original dir structure + match generate_instrumented_bin_wast(&test_setup, &test_cases, &test) { Err(e) => { panic!( "Unable to write instrumented wast file due to error: {:?}", @@ -74,6 +62,11 @@ pub fn main() -> Result<(), std::io::Error> { Ok(()) } +/// Clear out the previous test directory +fn clean() { + remove_dir_all(Path::new(OUTPUT_DIR)).ok(); +} + fn run_wast_tests(wast_should_fail: Vec, wast_should_pass: Vec) { let inters = get_available_interpreters(); assert!(!inters.is_empty(), "No supported interpreters are configured, fail!\n\ @@ -157,21 +150,34 @@ fn get_available_interpreters() -> Vec { // ============================== fn generate_should_fail_bin_wast( - module_wasm: &Vec, + test_setup: &WastTestSetup, test_cases: &[WastTestCase], wast_path: &Path, ) -> Result, std::io::Error> { let mut created_wast_files = vec![]; for (test_idx, test_case) in test_cases.iter().enumerate() { for (assertion_idx, assertion) in test_case.assertions.iter().enumerate() { + if assertion.passes_uninstrumented { + continue; + } // create the wast // call.wast -> call.idx.bin.wast - let file_name = new_wast_name(wast_path, test_idx, Some(assertion_idx)); - let new_file_path = format!("{OUTPUT_UNINSTR_WAST}/{file_name}"); - try_path(&new_file_path); + let new_file_path = new_wast_path( + wast_path, + test_idx, + Some(assertion_idx), + OUTPUT_UNINSTR_WAST, + ); // Write new wast files, one assertion at a time - write_bin_wast_file(&new_file_path, module_wasm, &[assertion.clone()])?; + write_bin_wast_file( + &new_file_path, + &test_setup.support_modules, + &test_setup.support_stmts, + &test_setup.target_module, + &"None".to_string(), + &[assertion.clone()], + )?; created_wast_files.push(new_file_path); } } @@ -179,7 +185,7 @@ fn generate_should_fail_bin_wast( } fn generate_instrumented_bin_wast( - module_wasm: &[u8], + test_setup: &WastTestSetup, test_cases: &[WastTestCase], wast_path: &Path, ) -> Result, std::io::Error> { @@ -188,7 +194,7 @@ fn generate_instrumented_bin_wast( // instrument A COPY OF the module with the whamm script // copy, so you don't accidentally manipulate the core module // (which is then instrumented in subsequent tests) - let cloned_module = module_wasm.to_vec(); + let cloned_module = test_setup.target_module.to_vec(); let module_to_instrument = cloned_module.as_slice(); let (instrumented_module_wasm, instrumented_module_wat) = run_whamm( module_to_instrument, @@ -201,13 +207,14 @@ fn generate_instrumented_bin_wast( // create the wast // call.wast -> call.idx.bin.wast - let file_name = new_wast_name(wast_path, idx, None); - let new_file_path = format!("{OUTPUT_WHAMMED_WAST}/{file_name}"); - try_path(&new_file_path); + let new_file_path = new_wast_path(wast_path, idx, None, OUTPUT_WHAMMED_WAST); write_bin_wast_file( &new_file_path, + &test_setup.support_modules, + &test_setup.support_stmts, &instrumented_module_wasm, + &test_case.whamm_script, &test_case.assertions, )?; created_wast_files.push(new_file_path); @@ -217,19 +224,38 @@ fn generate_instrumented_bin_wast( fn write_bin_wast_file( file_path: &String, - module_wasm: &Vec, - assertions: &[String], + support_modules: &Vec>, + support_stmts: &Vec, + target_module: &Vec, + whamm_script: &String, + assertions: &[Assertion], ) -> Result<(), std::io::Error> { let mut wast_file = File::create(file_path)?; - // output the module binary with format: (module binary "") + // output the support modules with format: (module binary "") + for module in support_modules { + wast_file.write_all("(module binary ".as_bytes())?; + wast_file.write_all(vec_as_hex(module.as_slice()).as_bytes())?; + wast_file.write_all(")\n\n".as_bytes())?; + } + + // output the support statements + for stmt in support_stmts { + wast_file.write_all(stmt.as_bytes())?; + wast_file.write_all(&[b'\n'])?; + } + + // output the target module binary with format: (module binary "") wast_file.write_all("(module binary ".as_bytes())?; - wast_file.write_all(vec_as_hex(module_wasm.as_slice()).as_bytes())?; + wast_file.write_all(vec_as_hex(target_module.as_slice()).as_bytes())?; wast_file.write_all(")\n\n".as_bytes())?; + // output the whamm script + wast_file.write_all(format!("{} {}\n", WHAMM_PREFIX_PATTERN, whamm_script).as_bytes())?; + // output the associated assertions (line by line) for assert in assertions.iter() { - wast_file.write_all(assert.as_bytes())?; + wast_file.write_all(assert.str.as_bytes())?; wast_file.write_all(&[b'\n'])?; } wast_file.write_all(&[b'\n'])?; @@ -248,6 +274,8 @@ const WAST_SUITE_DIR: &str = "tests/wast_suite"; const MODULE_PREFIX_PATTERN: &str = "(module"; const ASSERT_PREFIX_PATTERN: &str = "(assert"; const WHAMM_PREFIX_PATTERN: &str = ";; WHAMM --> "; +const PASSES_UNINSTR_PATTERN: &str = ";; @passes_uninstr"; +const TO_INSTR_PATTERN: &str = ";; @instrument"; /// Recursively finds all tests in a specified directory fn find_wast_tests() -> Vec { @@ -278,38 +306,106 @@ fn find_wast_tests() -> Vec { wast_tests } -/// Parses the wasm module from the wast file passed as a buffer. -fn get_wasm_module(reader: &mut BufReader) -> Result { - let mut module = "".to_string(); - let mut num_left_parens = 0; - let mut num_right_parens = 0; - let mut is_module = false; +/// Holds the setup for a single test case encoded in the wast. +#[derive(Default)] +struct WastTestSetup { + target_module: Vec, + support_modules: Vec>, + support_stmts: Vec, +} +/// Parses the setup information from the wast file passed as a buffer. +/// This is necessary to support testing imports, e.g.: +/// (module +/// (func (export "log")) +/// ) +/// (register "test") +/// ;; @instrument +/// (module ) +fn get_test_setup( + reader: &mut BufReader, + file_path: &PathBuf, +) -> Result { + let mut mod_to_instr = false; + + let mut setup = WastTestSetup::default(); let mut line = String::new(); while reader.read_line(&mut line)? > 0 { - if line.starts_with(MODULE_PREFIX_PATTERN) { + if line.starts_with(TO_INSTR_PATTERN) { + mod_to_instr = true; + } else if line.starts_with(MODULE_PREFIX_PATTERN) { // this is the beginning of the module - is_module = true; + let module = get_wasm_module(&line, reader)?; + if mod_to_instr { + if module.is_empty() { + panic!( + "Could not find the Wasm module-to-instrument in the wast file: {:?}", + file_path.clone() + ); + } + + debug!("{module}\n"); + setup.target_module = match wat2wasm(module.as_bytes()) { + Err(e) => { + panic!( + "Unable to convert wat to wasm for module: {}\nDue to error: {:?}", + module, e + ); + } + Ok(res) => res, + }; + // When we get to the target module, we know the setup is done! + break; + } else { + setup + .support_modules + .push(match wat2wasm(module.as_bytes()) { + Err(e) => { + panic!( + "Unable to convert wat to wasm for module: {}\nDue to error: {:?}", + module, e + ); + } + Ok(res) => res, + }); + } + mod_to_instr = false; + } else if line.starts_with('(') { + setup.support_stmts.push(line.clone()); } + line.clear(); + } + Ok(setup) +} - if is_module { - // Add the line to the module string - module += &line; +/// Parses the wasm module from the wast file passed as a buffer. +fn get_wasm_module( + start_line: &str, + reader: &mut BufReader, +) -> Result { + let mut module: String = start_line.to_string(); + let mut num_left_parens = count_matched_chars(&module, &'('); + let mut num_right_parens = count_matched_chars(&module, &')'); - // count the number of left/right parens (to know when finished parsing module) - num_left_parens += count_matched_chars(&line, &'('); - num_right_parens += count_matched_chars(&line, &')'); + let mut line = String::new(); + while reader.read_line(&mut line)? > 0 { + // Add the line to the module string + module += &line; - if num_left_parens == num_right_parens { - // we're done parsing the module! - break; - } - fn count_matched_chars(s: &str, c: &char) -> usize { - s.chars().filter(|ch| *ch == *c).count() - } + // count the number of left/right parens (to know when finished parsing module) + num_left_parens += count_matched_chars(&line, &'('); + num_right_parens += count_matched_chars(&line, &')'); + + if num_left_parens == num_right_parens { + // we're done parsing the module! + break; } + line.clear(); } + fn count_matched_chars(s: &str, c: &char) -> usize { + s.chars().filter(|ch| *ch == *c).count() + } Ok(module) } @@ -318,7 +414,7 @@ fn get_wasm_module(reader: &mut BufReader) -> Result, + assertions: Vec, } impl WastTestCase { fn print(&self) { @@ -326,11 +422,21 @@ impl WastTestCase { debug!("{}", self.whamm_script); for assertion in &self.assertions { - debug!("{assertion}"); + if assertion.passes_uninstrumented { + debug!("PASS un-instrumented: '{}'", assertion.str); + } else { + debug!("FAIL un-instrumented: '{}'", assertion.str); + } } } } +#[derive(Clone)] +struct Assertion { + str: String, + passes_uninstrumented: bool, +} + /// Creates a vector of test cases from the passed buffer. /// Convention: `whamm!` scripts are in comments beginning with "WHAMM --> " /// Convention: All test cases under a `whamm!` script should be run on the same instrumented module. @@ -339,6 +445,7 @@ fn get_test_cases(reader: BufReader) -> Vec { let mut first = true; let mut matched = false; + let mut passes_uninstr = false; let mut curr_test = WastTestCase::default(); for line in reader.lines().map_while(Result::ok) { if let Some(whamm) = line.strip_prefix(WHAMM_PREFIX_PATTERN) { @@ -354,7 +461,12 @@ fn get_test_cases(reader: BufReader) -> Vec { panic!("Only one module per wast file!!") } else if line.starts_with(ASSERT_PREFIX_PATTERN) { // this is an assertion within the current test case - curr_test.assertions.push(line); + curr_test.assertions.push(Assertion { + str: line, + passes_uninstrumented: passes_uninstr, + }); + } else if line.starts_with(PASSES_UNINSTR_PATTERN) { + passes_uninstr = true; } } if matched { @@ -369,15 +481,35 @@ fn get_test_cases(reader: BufReader) -> Vec { // ---- UTILITIES ---- // =================== -fn new_wast_name(wast_path: &Path, idx: usize, idx2: Option) -> String { +fn new_wast_path( + wast_path: &Path, + idx: usize, + idx2: Option, + target_parent_dir: &str, +) -> String { + // figure out name let file_name = wast_path.file_name().unwrap().to_str().unwrap().to_string(); let file_ext = wast_path.extension().unwrap().to_str().unwrap(); let file_name_stripped = file_name.strip_suffix(file_ext).unwrap(); - if let Some(idx2) = idx2 { + let new_name = if let Some(idx2) = idx2 { format!("{file_name_stripped}whamm{idx}.assertion{idx2}.bin.wast") } else { format!("{file_name_stripped}whamm{idx}.bin.wast") - } + }; + + // Figure out path + let new_sub_path = match wast_path.strip_prefix(WAST_SUITE_DIR) { + Ok(p) => p.to_str().unwrap(), + Err(e) => panic!( + "Could not strip prefix from path '{:?}' due to error: {:?}", + wast_path, e + ), + }; + + let new_path = format!("{target_parent_dir}/{}/{new_name}", new_sub_path); + try_path(&new_path); + + new_path } /// Creates a String representing the &[u8] in hex format. diff --git a/tests/wast_suite/events/wasm_opcodes/call/call.wast b/tests/wast_suite/events/wasm_opcodes/call/call.wast deleted file mode 100644 index af1c7dd3..00000000 --- a/tests/wast_suite/events/wasm_opcodes/call/call.wast +++ /dev/null @@ -1,27 +0,0 @@ -;; Test `wasm:opcode:call` event - -(module - ;; Auxiliary definitions - (func $dummy (param i32) (result i32) (local.get 0)) - - ;; Test case functions - (func $instrument_me (result i32) - (call $dummy (i32.const 0)) - ) - (export "instrument_me" (func $instrument_me)) - (memory (;0;) 1) -) - -;; NOTE: For wizard, don't do manipulations that change arg* (that requires the frame accessor). Instead change global state for now? -;; WHAMM --> wasm:opcode:call:before { arg0 = 1; } - -;; Use something like below to assert on the values of some report variable dynamically. -;; REPORT_TRACE(ID) --> 1, 3, 5, 6, 7 - -(assert_return (invoke "instrument_me") (i32.const 1)) -;; Use something like below to assert on report variable values! -;; WITH_WHAMM --> (assert_return (invoke "get_report_var" (i32.const 1)) (i32.const 7)) -(assert_return (invoke "instrument_me") (i32.const 1)) - -;; WHAMM --> wasm:opcode:call:before { arg0 = 1; } -(assert_return (invoke "instrument_me") (i32.const 1)) diff --git a/tests/wast_suite/events/wasm_opcodes/call/import/args.wast b/tests/wast_suite/events/wasm_opcodes/call/import/args.wast new file mode 100644 index 00000000..7f175bee --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/import/args.wast @@ -0,0 +1,195 @@ +;; Test `wasm:opcode:call` event + +;; Auxiliary module to import from + +(module + (func (export "dummy_five_params") (param i32 i32 i32 i32 i32) (result i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + ) +) + +(register "test") + +;; @instrument +(module + ;; Imports + (type (;0;) (func (param i32 i32 i32 i32 i32) (result i32))) + (import "test" "dummy_five_params" (func $dummy_five_params (type 0))) + + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + global.set $var + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; ==================================== +;; ---- `CALL`: imported functions ---- +;; ==================================== + +;; -------------------------- +;; ==== ARGS, body, arg0 ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg0 == 0 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg0 == 0 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg0 == 0 / { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg0 == 2 / { count = 5; return 1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should change +(assert_return (invoke "get_count") (i32.const 0)) + +;; ---------------------------- +;; ==== ARGS, body, argLEN ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg4 == 4 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg4 == 4 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg4 == 4 / { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg4 == 2 / { count = 5; return 1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should be original +(assert_return (invoke "get_count") (i32.const 0)) + +;; ---------------------------- +;; ==== ARGS, body, argMID ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg2 == 2 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg2 == 2 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg2 == 2 / { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg2 == 3 / { count = 5; return 1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should be original +(assert_return (invoke "get_count") (i32.const 0)) + +;; ------------------------------ +;; ==== ARGS, body, argMID+1 ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg3 == 3 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg3 == 3 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg3 == 3 / { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg3 == 0 / { count = 5; return 1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should be original +(assert_return (invoke "get_count") (i32.const 0)) + +;; ------------------------------ +;; ==== ARGS, body, argMID-1 ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg1 == 1 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg1 == 1 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg1 == 1 / { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg1 == 2 / { count = 5; return 1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should be original +(assert_return (invoke "get_count") (i32.const 0)) + +;; -------------------------- +;; ==== ARGS, body, arg0 ==== +;; WHAMM --> wasm:opcode:call:before { arg0 = 1; } +(assert_return (invoke "get_global_var") (i32.const 11)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg0; } +(assert_return (invoke "get_count") (i32.const 0)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg0; } +(assert_return (invoke "get_count") (i32.const 0)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = 5; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 5)) + +;; ---------------------------- +;; ==== ARGS, body, argLEN ==== +;; WHAMM --> wasm:opcode:call:before { arg4 = 1; } +(assert_return (invoke "get_global_var") (i32.const 7)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg4; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 4)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg4; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 4)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg4; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 4)) + +;; ---------------------------- +;; ==== ARGS, body, argMID ==== +;; WHAMM --> wasm:opcode:call:before { arg2 = 1; } +(assert_return (invoke "get_global_var") (i32.const 9)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg2; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 2)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg2; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 2)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg2; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) ;; alt, so global should be return value +(assert_return (invoke "get_count") (i32.const 2)) + +;; ------------------------------ +;; ==== ARGS, body, argMID+1 ==== +;; WHAMM --> wasm:opcode:call:before { arg3 = 1; } +(assert_return (invoke "get_global_var") (i32.const 8)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg3; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 3)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg3; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 3)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg3; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) +(assert_return (invoke "get_count") (i32.const 3)) + +;; ------------------------------ +;; ==== ARGS, body, argMID-1 ==== +;; WHAMM --> wasm:opcode:call:before { arg1 = 2; } +(assert_return (invoke "get_global_var") (i32.const 11)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg1; return 1; } +(assert_return (invoke "get_global_var") (i32.const 1)) +(assert_return (invoke "get_count") (i32.const 1)) diff --git a/tests/wast_suite/events/wasm_opcodes/call/import/imms.wast.todo b/tests/wast_suite/events/wasm_opcodes/call/import/imms.wast.todo new file mode 100644 index 00000000..70f31253 --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/import/imms.wast.todo @@ -0,0 +1,53 @@ +;; Test `wasm:opcode:call` event + +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; --------------------------------- +;; ==== IMMS, predicate, `imm0` ==== +;; TODO +;; ---------------------------- +;; ==== IMMS, body, `imm0` ==== +;; TODO + +;; ==================================== +;; ---- `CALL`: imported functions ---- +;; ==================================== + +;; --------------------------------- +;; ==== IMMS, predicate, `imm0` ==== +;; TODO +;; ---------------------------- +;; ==== IMMS, body, `imm0` ==== +;; TODO diff --git a/tests/wast_suite/events/wasm_opcodes/call/import/prov-fns.wast.todo b/tests/wast_suite/events/wasm_opcodes/call/import/prov-fns.wast.todo new file mode 100644 index 00000000..59c9bec0 --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/import/prov-fns.wast.todo @@ -0,0 +1,48 @@ +;; Test `wasm:opcode:call` event + +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; -------------------------------------------- +;; ==== FUNCS, predicate, `alt_call_by_id` ==== +;; TODO +;; ---------------------------------------------- +;; ==== FUNCS, predicate, `alt_call_by_name` ==== +;; TODO +;; --------------------------------------- +;; ==== FUNCS, body, `alt_call_by_id` ==== +;; TODO +;; ----------------------------------------- +;; ==== FUNCS, body, `alt_call_by_name` ==== +;; TODO diff --git a/tests/wast_suite/events/wasm_opcodes/call/import/prov-globals.wast.todo b/tests/wast_suite/events/wasm_opcodes/call/import/prov-globals.wast.todo new file mode 100644 index 00000000..2496dbcc --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/import/prov-globals.wast.todo @@ -0,0 +1,54 @@ +;; Test `wasm:opcode:call` event + +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; ---------------------------------------------- +;; ==== GLOBALS, predicate, `target_fn_type` ==== +;; TODO +;; ------------------------------------------------- +;; ==== GLOBALS, predicate, `target_imp_module` ==== +;; TODO +;; ----------------------------------------------- +;; ==== GLOBALS, predicate, `target_imp_name` ==== +;; TODO +;; ----------------------------------------- +;; ==== GLOBALS, body, `target_fn_type` ==== +;; TODO +;; -------------------------------------------- +;; ==== GLOBALS, body, `target_imp_module` ==== +;; TODO +;; ------------------------------------------ +;; ==== GLOBALS, body, `target_imp_name` ==== +;; TODO diff --git a/tests/wast_suite/events/wasm_opcodes/call/local/args.wast b/tests/wast_suite/events/wasm_opcodes/call/local/args.wast new file mode 100644 index 00000000..f33f486f --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/local/args.wast @@ -0,0 +1,185 @@ +;; Test `wasm:opcode:call` event + +;; @instrument +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; ================================= +;; ---- `CALL`: local functions ---- +;; ================================= + +;; ------------------------------- +;; ==== ARGS, predicate, arg0 ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg0 == 0 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg0 == 0 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg0 == 0 / { count = 5; } +(assert_return (invoke "get_global_var") (i32.const 0)) ;; alt, so global should not change +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg0 == 2 / { count = 5; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should change +(assert_return (invoke "get_count") (i32.const 0)) + +;; --------------------------------- +;; ==== ARGS, predicate, argLEN ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg4 == 4 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg4 == 4 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg4 == 4 / { count = 5; } +(assert_return (invoke "get_global_var") (i32.const 0)) ;; alt, so global should not change +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg4 == 2 / { count = 5; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should change +(assert_return (invoke "get_count") (i32.const 0)) + +;; --------------------------------- +;; ==== ARGS, predicate, argMID ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg2 == 2 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg2 == 2 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg2 == 2 / { count = 5; } +(assert_return (invoke "get_global_var") (i32.const 0)) ;; alt, so global should not change +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg2 == 3 / { count = 5; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should change +(assert_return (invoke "get_count") (i32.const 0)) + +;; ----------------------------------- +;; ==== ARGS, predicate, argMID+1 ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg3 == 3 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg3 == 3 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg3 == 3 / { count = 5; } +(assert_return (invoke "get_global_var") (i32.const 0)) ;; alt, so global should not change +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg3 == 0 / { count = 5; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should change +(assert_return (invoke "get_count") (i32.const 0)) + +;; ----------------------------------- +;; ==== ARGS, predicate, argMID-1 ==== +;; WHAMM --> i32 count; wasm:opcode:call:before / arg1 == 1 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after / arg1 == 1 / { count++; } +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg1 == 1 / { count = 5; } +(assert_return (invoke "get_global_var") (i32.const 0)) ;; alt, so global should not change +(assert_return (invoke "get_count") (i32.const 5)) +;; WHAMM --> i32 count; wasm:opcode:call:alt / arg1 == 2 / { count = 5; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) ;; pred == false, so global should change +(assert_return (invoke "get_count") (i32.const 0)) + +;; -------------------------- +;; ==== ARGS, body, arg0 ==== +;; WHAMM --> wasm:opcode:call:before { arg0 = 1; } +(assert_return (invoke "get_global_var") (i32.const 11)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg0; } +(assert_return (invoke "get_count") (i32.const 0)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg0; } +(assert_return (invoke "get_count") (i32.const 0)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = 5; } +(assert_return (invoke "get_count") (i32.const 5)) + +;; ---------------------------- +;; ==== ARGS, body, argLEN ==== +;; WHAMM --> wasm:opcode:call:before { arg4 = 1; } +(assert_return (invoke "get_global_var") (i32.const 7)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg4; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 4)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg4; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 4)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg4; } +(assert_return (invoke "get_global_var") (i32.const 0)) +(assert_return (invoke "get_count") (i32.const 4)) + +;; ---------------------------- +;; ==== ARGS, body, argMID ==== +;; WHAMM --> wasm:opcode:call:before { arg2 = 1; } +(assert_return (invoke "get_global_var") (i32.const 9)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg2; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 2)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg2; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 2)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg2; } +(assert_return (invoke "get_global_var") (i32.const 0)) +(assert_return (invoke "get_count") (i32.const 2)) + +;; ------------------------------ +;; ==== ARGS, body, argMID+1 ==== +;; WHAMM --> wasm:opcode:call:before { arg3 = 1; } +(assert_return (invoke "get_global_var") (i32.const 8)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg3; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 3)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg3; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 3)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg3; } +(assert_return (invoke "get_global_var") (i32.const 0)) +(assert_return (invoke "get_count") (i32.const 3)) + +;; ------------------------------ +;; ==== ARGS, body, argMID-1 ==== +;; WHAMM --> wasm:opcode:call:before { arg1 = 2; } +(assert_return (invoke "get_global_var") (i32.const 11)) +;; WHAMM --> i32 count; wasm:opcode:call:before { count = arg1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:after { count = arg1; } +;; @passes_uninstr +(assert_return (invoke "get_global_var") (i32.const 10)) +(assert_return (invoke "get_count") (i32.const 1)) +;; WHAMM --> i32 count; wasm:opcode:call:alt { count = arg1; } +(assert_return (invoke "get_global_var") (i32.const 0)) +(assert_return (invoke "get_count") (i32.const 1)) diff --git a/tests/wast_suite/events/wasm_opcodes/call/local/imms.wast.todo b/tests/wast_suite/events/wasm_opcodes/call/local/imms.wast.todo new file mode 100644 index 00000000..70f31253 --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/local/imms.wast.todo @@ -0,0 +1,53 @@ +;; Test `wasm:opcode:call` event + +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; --------------------------------- +;; ==== IMMS, predicate, `imm0` ==== +;; TODO +;; ---------------------------- +;; ==== IMMS, body, `imm0` ==== +;; TODO + +;; ==================================== +;; ---- `CALL`: imported functions ---- +;; ==================================== + +;; --------------------------------- +;; ==== IMMS, predicate, `imm0` ==== +;; TODO +;; ---------------------------- +;; ==== IMMS, body, `imm0` ==== +;; TODO diff --git a/tests/wast_suite/events/wasm_opcodes/call/local/prov-fns.wast.todo b/tests/wast_suite/events/wasm_opcodes/call/local/prov-fns.wast.todo new file mode 100644 index 00000000..59c9bec0 --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/local/prov-fns.wast.todo @@ -0,0 +1,48 @@ +;; Test `wasm:opcode:call` event + +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; -------------------------------------------- +;; ==== FUNCS, predicate, `alt_call_by_id` ==== +;; TODO +;; ---------------------------------------------- +;; ==== FUNCS, predicate, `alt_call_by_name` ==== +;; TODO +;; --------------------------------------- +;; ==== FUNCS, body, `alt_call_by_id` ==== +;; TODO +;; ----------------------------------------- +;; ==== FUNCS, body, `alt_call_by_name` ==== +;; TODO diff --git a/tests/wast_suite/events/wasm_opcodes/call/local/prov-globals.wast.todo b/tests/wast_suite/events/wasm_opcodes/call/local/prov-globals.wast.todo new file mode 100644 index 00000000..2496dbcc --- /dev/null +++ b/tests/wast_suite/events/wasm_opcodes/call/local/prov-globals.wast.todo @@ -0,0 +1,54 @@ +;; Test `wasm:opcode:call` event + +(module + ;; Globals + (global $var (mut i32) (i32.const 0)) + + ;; Global getters + (func $get_global_var (result i32) + (global.get $var) + ) + + ;; Auxiliary definitions + (func $dummy_five_params (param i32 i32 i32 i32 i32) + local.get 0 + local.get 1 + i32.add + local.get 2 + i32.add + local.get 3 + i32.add + local.get 4 + i32.add + global.set $var + ) + + ;; Test case functions + (func $five_params + (call $dummy_five_params (i32.const 0) (i32.const 1) (i32.const 2) (i32.const 3) (i32.const 4)) + ) + + (start $five_params) + (export "five_params" (func $five_params)) + (export "get_global_var" (func $get_global_var)) + (memory (;0;) 1) +) + +;; ---------------------------------------------- +;; ==== GLOBALS, predicate, `target_fn_type` ==== +;; TODO +;; ------------------------------------------------- +;; ==== GLOBALS, predicate, `target_imp_module` ==== +;; TODO +;; ----------------------------------------------- +;; ==== GLOBALS, predicate, `target_imp_name` ==== +;; TODO +;; ----------------------------------------- +;; ==== GLOBALS, body, `target_fn_type` ==== +;; TODO +;; -------------------------------------------- +;; ==== GLOBALS, body, `target_imp_module` ==== +;; TODO +;; ------------------------------------------ +;; ==== GLOBALS, body, `target_imp_name` ==== +;; TODO