2019-08-04 16:44:27 -06:00
#![ deny(
bad_style ,
dead_code ,
unused_imports ,
unused_variables ,
unused_unsafe ,
unreachable_patterns
) ]
2019-01-12 23:48:21 -05:00
2020-04-03 16:27:49 -07:00
#[ cfg(not(any(
feature = " backend-llvm " ,
feature = " backend-cranelift " ,
feature = " backend-singlepass "
) ) ) ]
compile_error! ( " No compiler backend detected: please specify at least one compiler backend! " ) ;
2019-07-25 22:31:19 -05:00
#[ cfg(test) ]
mod tests {
2019-08-04 12:42:20 -06:00
// TODO fix spec failures
// TODO fix panics and remove panic handlers
2019-08-04 16:44:27 -06:00
// TODO do something with messages _message, message: _, msg: _
2019-08-04 16:13:47 -06:00
// TODO consider git submodule for spectests? & separate dir for simd/extra tests
// TODO cleanup refactor
2019-08-04 13:18:57 -06:00
// TODO Files could be run with multiple threads
// TODO Allow running WAST &str directly (E.g. for use outside of spectests)
2019-08-04 12:42:20 -06:00
2019-11-19 23:38:50 -06:00
use std ::collections ::HashSet ;
2020-04-03 16:27:49 -07:00
use std ::env ;
2019-09-23 13:43:01 -07:00
use std ::sync ::{ Arc , Mutex } ;
2019-08-10 15:56:25 -06:00
2019-08-02 16:00:35 -06:00
struct SpecFailure {
file : String ,
line : u64 ,
kind : String ,
message : String ,
}
struct TestReport {
failures : Vec < SpecFailure > ,
passed : u32 ,
failed : u32 ,
2019-08-04 16:13:47 -06:00
allowed_failure : u32 ,
2019-08-02 16:00:35 -06:00
}
impl TestReport {
2019-08-04 16:44:27 -06:00
pub fn count_passed ( & mut self ) {
2019-08-02 16:00:35 -06:00
self . passed + = 1 ;
}
2019-08-04 16:44:27 -06:00
pub fn has_failures ( & self ) -> bool {
2019-08-02 16:00:35 -06:00
self . failed > 0
}
2019-08-04 16:44:27 -06:00
pub fn add_failure (
2019-08-04 16:13:47 -06:00
& mut self ,
failure : SpecFailure ,
2019-11-19 23:38:50 -06:00
_testkey : & str ,
2020-02-21 03:23:44 +08:00
excludes : & mut Vec < Option < Exclude > > ,
2019-11-19 23:38:50 -06:00
line : u64 ,
2019-08-04 16:13:47 -06:00
) {
2020-02-21 03:23:44 +08:00
// Ensure that each exclude is only used once.
if let Some ( _ ) = excludes
. iter_mut ( )
. find ( | e | {
if let Some ( ref e ) = e {
e . line_matches ( line ) & & e . exclude_kind = = ExcludeKind ::Fail
} else {
false
}
} )
. take ( )
. and_then ( | x | x . take ( ) )
2019-11-19 23:38:50 -06:00
{
2019-08-05 20:57:07 -06:00
self . allowed_failure + = 1 ;
return ;
}
2019-08-02 16:00:35 -06:00
self . failed + = 1 ;
self . failures . push ( failure ) ;
}
}
2020-04-03 16:27:49 -07:00
fn get_available_compilers ( ) -> & 'static [ & 'static str ] {
& [
#[ cfg(feature = " backend-cranelift " ) ]
" clif " ,
#[ cfg(feature = " backend-llvm " ) ]
" llvm " ,
#[ cfg(feature = " backend-singlepass " ) ]
" singlepass " ,
]
2019-08-04 11:25:46 -06:00
}
2020-04-03 16:27:49 -07:00
fn get_compilers_to_test ( ) -> Vec < & 'static str > {
let mut out = vec! [ ] ;
2020-04-08 17:01:32 -07:00
if let Ok ( v ) = env ::var ( " WASMER_TEST_CRANELIFT " ) {
2020-04-03 16:27:49 -07:00
if v = = " 1 " {
out . push ( " clif " ) ;
}
}
2020-04-08 17:01:32 -07:00
if let Ok ( v ) = env ::var ( " WASMER_TEST_LLVM " ) {
2020-04-03 16:27:49 -07:00
if v = = " 1 " {
out . push ( " llvm " ) ;
}
}
2020-04-08 17:01:32 -07:00
if let Ok ( v ) = env ::var ( " WASMER_TEST_SINGLEPASS " ) {
2020-04-03 16:27:49 -07:00
if v = = " 1 " {
out . push ( " singlepass " ) ;
}
}
2019-08-04 11:25:46 -06:00
2020-04-03 16:27:49 -07:00
out
2019-08-04 11:25:46 -06:00
}
2019-08-05 20:57:07 -06:00
#[ cfg(unix) ]
2019-11-19 23:38:50 -06:00
fn get_target_family ( ) -> & 'static str {
2019-08-05 20:57:07 -06:00
" unix "
}
#[ cfg(windows) ]
2019-11-19 23:38:50 -06:00
fn get_target_family ( ) -> & 'static str {
2019-08-05 20:57:07 -06:00
" windows "
}
2020-04-06 12:35:08 -07:00
#[ cfg(target_os = " android " ) ]
fn get_target_os ( ) -> & 'static str {
" android "
}
2020-03-02 15:41:41 +01:00
#[ cfg(target_os = " freebsd " ) ]
fn get_target_os ( ) -> & 'static str {
" freebsd "
}
2020-02-21 03:47:01 +08:00
#[ cfg(target_os = " linux " ) ]
fn get_target_os ( ) -> & 'static str {
" linux "
}
#[ cfg(target_os = " macos " ) ]
fn get_target_os ( ) -> & 'static str {
" macos "
}
#[ cfg(target_os = " windows " ) ]
fn get_target_os ( ) -> & 'static str {
" windows "
}
2019-11-19 23:38:50 -06:00
fn get_target_arch ( ) -> & 'static str {
if cfg! ( target_arch = " x86_64 " ) {
" x86_64 "
} else if cfg! ( target_arch = " aarch64 " ) {
" aarch64 "
} else if cfg! ( target_arch = " x86 " ) {
" x86 "
} else if cfg! ( target_arch = " mips " ) {
" mips "
} else if cfg! ( target_arch = " powerpc " ) {
" powerpc "
} else if cfg! ( target_arch = " powerpc64 " ) {
" powerpc64 "
} else if cfg! ( target_arch = " arm " ) {
" arm "
} else {
panic! ( " unknown target arch " )
}
}
// clif:skip:data.wast:172:unix:x86
#[ allow(dead_code) ]
2020-02-21 03:23:44 +08:00
#[ derive(Clone) ]
2019-11-19 23:38:50 -06:00
struct Exclude {
backend : Option < String > ,
exclude_kind : ExcludeKind ,
file : String ,
line : Option < u64 > ,
target_family : Option < String > ,
target_arch : Option < String > ,
}
impl Exclude {
fn line_matches ( & self , value : u64 ) -> bool {
self . line . is_none ( ) | | self . line . unwrap ( ) = = value
}
fn line_exact_match ( & self , value : u64 ) -> bool {
self . line . is_some ( ) & & self . line . unwrap ( ) = = value
}
fn matches_backend ( & self , value : & str ) -> bool {
self . backend . is_none ( ) | | self . backend . as_ref ( ) . unwrap ( ) = = value
}
fn matches_target_family ( & self , value : & str ) -> bool {
self . target_family . is_none ( ) | | self . target_family . as_ref ( ) . unwrap ( ) = = value
}
fn matches_target_arch ( & self , value : & str ) -> bool {
self . target_arch . is_none ( ) | | self . target_arch . as_ref ( ) . unwrap ( ) = = value
}
fn from (
backend : & str ,
exclude_kind : & str ,
file : & str ,
line : & str ,
target_family : & str ,
target_arch : & str ,
) -> Exclude {
let backend : Option < String > = match backend {
" * " = > None ,
" clif " = > Some ( " clif " . to_string ( ) ) ,
" singlepass " = > Some ( " singlepass " . to_string ( ) ) ,
" llvm " = > Some ( " llvm " . to_string ( ) ) ,
_ = > panic! ( " backend {:?} not recognized " , backend ) ,
} ;
let exclude_kind = match exclude_kind {
" skip " = > ExcludeKind ::Skip ,
" fail " = > ExcludeKind ::Fail ,
_ = > panic! ( " exclude kind {:?} not recognized " , exclude_kind ) ,
} ;
let line = match line {
" * " = > None ,
_ = > Some (
line . parse ::< u64 > ( )
. expect ( & format! ( " expected * or int: {:?} " , line ) ) ,
) ,
} ;
let target_family = match target_family {
" * " = > None ,
_ = > Some ( target_family . to_string ( ) ) ,
} ;
let target_arch = match target_arch {
" * " = > None ,
_ = > Some ( target_arch . to_string ( ) ) ,
} ;
Exclude {
backend ,
exclude_kind ,
file : file . to_string ( ) ,
line ,
target_family ,
target_arch ,
}
}
}
2019-09-23 13:43:01 -07:00
fn with_instance < F , R > (
maybe_instance : Option < Arc < Mutex < Instance > > > ,
named_modules : & HashMap < String , Arc < Mutex < Instance > > > ,
module : & Option < String > ,
f : F ,
) -> Option < R >
where
R : Sized ,
F : FnOnce ( & Instance ) -> R ,
{
let ref ins = module
. as_ref ( )
. and_then ( | name | named_modules . get ( name ) . cloned ( ) )
. or ( maybe_instance ) ? ;
let guard = ins . lock ( ) . unwrap ( ) ;
Some ( f ( guard . borrow ( ) ) )
}
2019-08-04 12:42:20 -06:00
use glob ::glob ;
2019-08-04 11:25:46 -06:00
use std ::collections ::HashMap ;
2019-08-04 16:44:27 -06:00
use std ::fs ;
use std ::panic ::AssertUnwindSafe ;
2019-07-25 22:31:19 -05:00
use std ::path ::PathBuf ;
2020-04-03 16:27:49 -07:00
use std ::str ::FromStr ;
2019-07-25 22:31:19 -05:00
use wabt ::script ::{ Action , Command , CommandKind , ScriptParser , Value } ;
2019-11-21 13:36:44 -08:00
use wasmer_runtime ::{
2020-04-03 16:27:49 -07:00
compile_with_config_with , compiler_for_backend ,
2019-11-21 13:38:22 -08:00
error ::CompileError ,
func , imports ,
2019-08-04 10:19:50 -06:00
types ::{ ElementType , MemoryDescriptor , TableDescriptor } ,
units ::Pages ,
2020-04-03 16:27:49 -07:00
Backend , CompilerConfig , Ctx , Export , Features , Global , ImportObject , Instance ,
LikeNamespace , Memory , Table ,
2019-08-04 10:19:50 -06:00
} ;
2019-07-25 22:31:19 -05:00
2020-03-10 02:45:31 +08:00
fn format_panic ( e : & dyn std ::any ::Any ) -> String {
if let Some ( s ) = e . downcast_ref ::< & str > ( ) {
format! ( " {} " , s )
} else if let Some ( s ) = e . downcast_ref ::< String > ( ) {
format! ( " {} " , s )
} else {
" (unknown) " . into ( )
}
}
2019-08-04 11:25:46 -06:00
fn parse_and_run (
path : & PathBuf ,
2019-11-19 23:38:50 -06:00
file_excludes : & HashSet < String > ,
excludes : & HashMap < String , Vec < Exclude > > ,
2020-04-03 16:27:49 -07:00
backend : & 'static str ,
2019-08-04 11:25:46 -06:00
) -> Result < TestReport , String > {
2019-08-02 16:00:35 -06:00
let mut test_report = TestReport {
failures : vec ! [ ] ,
passed : 0 ,
failed : 0 ,
2019-08-04 16:13:47 -06:00
allowed_failure : 0 ,
2019-08-02 16:00:35 -06:00
} ;
2019-07-25 22:31:19 -05:00
let filename = path . file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) ;
let source = fs ::read ( & path ) . unwrap ( ) ;
2019-08-04 12:42:20 -06:00
2019-11-19 23:38:50 -06:00
// Entire file is excluded by line * and skip
if file_excludes . contains ( filename ) {
2019-08-04 12:42:20 -06:00
return Ok ( test_report ) ;
}
2019-07-25 22:31:19 -05:00
let mut features = wabt ::Features ::new ( ) ;
features . enable_simd ( ) ;
2019-08-08 19:15:20 -07:00
features . enable_threads ( ) ;
2019-11-15 21:54:34 -08:00
features . enable_sign_extension ( ) ;
2019-12-12 20:32:08 -08:00
features . enable_sat_float_to_int ( ) ;
2019-07-25 22:31:19 -05:00
let mut parser : ScriptParser =
ScriptParser ::from_source_and_name_with_features ( & source , filename , features )
. expect ( & format! ( " Failed to parse script {} " , & filename ) ) ;
2019-08-02 11:36:38 -06:00
use std ::panic ;
2019-09-23 13:43:01 -07:00
let mut instance : Option < Arc < Mutex < Instance > > > = None ;
2019-08-04 14:20:09 -06:00
2019-09-23 13:43:01 -07:00
let mut named_modules : HashMap < String , Arc < Mutex < Instance > > > = HashMap ::new ( ) ;
2019-08-03 17:31:43 -06:00
2019-09-23 13:43:01 -07:00
let mut registered_modules : HashMap < String , Arc < Mutex < Instance > > > = HashMap ::new ( ) ;
2020-02-21 03:23:44 +08:00
let mut excludes : Vec < _ > = excludes
. get ( filename )
. map ( | file | file . iter ( ) . map ( | x | Some ( x . clone ( ) ) ) . collect ( ) )
. unwrap_or ( vec! [ ] ) ;
let excludes = & mut excludes ;
2020-04-03 16:27:49 -07:00
let backend_enum = Backend ::from_str ( if backend = = " clif " {
" cranelift "
} else {
backend
} )
. unwrap ( ) ;
2019-08-04 14:44:55 -06:00
2019-07-25 22:31:19 -05:00
while let Some ( Command { kind , line } ) =
parser . next ( ) . map_err ( | e | format! ( " Parse err: {:?} " , e ) ) ?
{
2019-08-04 11:25:46 -06:00
let test_key = format! ( " {} : {} : {} " , backend , filename , line ) ;
// Use this line to debug which test is running
2019-08-10 15:33:08 -06:00
println! ( " Running test: {} " , test_key ) ;
2019-08-04 11:25:46 -06:00
2019-11-19 23:38:50 -06:00
// Skip tests that match this line
if excludes
. iter ( )
2020-02-21 03:23:44 +08:00
. filter_map ( | x | x . as_ref ( ) )
2019-11-19 23:38:50 -06:00
. any ( | e | e . line_exact_match ( line ) & & e . exclude_kind = = ExcludeKind ::Skip )
2019-08-04 11:25:46 -06:00
{
continue ;
}
2019-07-25 22:31:19 -05:00
match kind {
CommandKind ::Module { module , name } = > {
2019-08-03 17:31:43 -06:00
// println!("Module");
2019-08-04 15:36:17 -06:00
let result = panic ::catch_unwind ( AssertUnwindSafe ( | | {
let spectest_import_object =
get_spectest_import_object ( & registered_modules ) ;
2019-08-04 14:44:55 -06:00
let config = CompilerConfig {
2019-08-08 19:15:20 -07:00
features : Features {
simd : true ,
threads : true ,
} ,
2020-03-16 23:40:02 +08:00
nan_canonicalization : true ,
2020-03-24 15:46:58 -07:00
enable_verification : true ,
2019-08-04 14:44:55 -06:00
.. Default ::default ( )
} ;
2020-04-03 16:27:49 -07:00
let compiler = compiler_for_backend ( backend_enum ) . unwrap ( ) ;
let module =
compile_with_config_with ( & module . into_vec ( ) , config , & * compiler )
. expect ( " WASM can't be compiled " ) ;
2019-08-02 11:36:38 -06:00
let i = module
2019-08-04 10:19:50 -06:00
. instantiate ( & spectest_import_object )
2019-08-02 11:36:38 -06:00
. expect ( " WASM can't be instantiated " ) ;
i
2019-08-04 15:36:17 -06:00
} ) ) ;
2019-08-02 11:36:38 -06:00
match result {
Err ( e ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " Module " ) ,
2020-03-10 12:28:54 +08:00
message : format ! ( " caught panic {} " , format_panic ( & e ) ) ,
2019-08-04 16:13:47 -06:00
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-02 11:36:38 -06:00
instance = None ;
}
Ok ( i ) = > {
2019-09-23 13:43:01 -07:00
let i = Arc ::new ( Mutex ::new ( i ) ) ;
2019-08-04 14:20:09 -06:00
if name . is_some ( ) {
2019-09-23 13:43:01 -07:00
named_modules . insert ( name . unwrap ( ) , Arc ::clone ( & i ) ) ;
2019-08-04 14:20:09 -06:00
}
2019-08-02 11:36:38 -06:00
instance = Some ( i ) ;
}
}
2019-07-25 22:31:19 -05:00
}
CommandKind ::AssertReturn { action , expected } = > {
2019-08-02 16:00:35 -06:00
match action {
Action ::Invoke {
module ,
field ,
args ,
} = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result = with_instance (
instance . clone ( ) ,
& named_modules ,
& module ,
| instance | {
2019-11-21 13:36:44 -08:00
let params : Vec < wasmer_runtime ::types ::Value > =
2019-09-23 13:43:01 -07:00
args . iter ( ) . cloned ( ) . map ( convert_value ) . collect ( ) ;
instance . call ( & field , & params [ .. ] )
2019-08-04 14:20:09 -06:00
} ,
2019-09-23 13:43:01 -07:00
) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturn " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available: {:?} " , & module ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-02 16:00:35 -06:00
} else {
2019-09-23 13:43:01 -07:00
let call_result = maybe_call_result . unwrap ( ) ;
2019-08-03 17:31:43 -06:00
match call_result {
Err ( e ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturn " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " Call failed {:?} " , e ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
Ok ( values ) = > {
for ( i , v ) in values . iter ( ) . enumerate ( ) {
let expected_value =
2019-08-13 22:14:10 -06:00
convert_wabt_value ( * expected . get ( i ) . unwrap ( ) ) ;
let v = convert_wasmer_value ( v . clone ( ) ) ;
if v ! = expected_value {
2019-08-04 16:44:27 -06:00
test_report . add_failure ( SpecFailure {
2019-08-03 17:31:43 -06:00
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturn " ) ,
2019-08-03 17:31:43 -06:00
message : format ! (
" result {:?} ({:?}) does not match expected {:?} ({:?}) " ,
v , to_hex ( v . clone ( ) ) , expected_value , to_hex ( expected_value . clone ( ) )
) ,
2019-11-19 23:38:50 -06:00
} , & test_key , excludes , line ) ;
2019-08-03 17:31:43 -06:00
} else {
2019-08-04 16:44:27 -06:00
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
}
}
}
}
2019-08-02 16:00:35 -06:00
}
}
2019-08-04 13:37:39 -06:00
Action ::Get { module , field } = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result = with_instance (
instance . clone ( ) ,
& named_modules ,
& module ,
| instance | {
instance
. get_export ( & field )
. expect ( & format! ( " missing global {:?} " , & field ) )
2019-08-04 14:20:09 -06:00
} ,
2019-09-23 13:43:01 -07:00
) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturn Get " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available {:?} " , & module ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 13:37:39 -06:00
} else {
2019-09-23 13:43:01 -07:00
let export : Export = maybe_call_result . unwrap ( ) ;
2019-08-04 14:20:09 -06:00
match export {
Export ::Global ( g ) = > {
let value = g . get ( ) ;
let expected_value =
convert_value ( * expected . get ( 0 ) . unwrap ( ) ) ;
if value = = expected_value {
2019-08-04 16:44:27 -06:00
test_report . count_passed ( ) ;
2019-08-04 14:20:09 -06:00
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturn Get " ) ,
2019-08-04 16:13:47 -06:00
message : format ! (
" Expected Global {:?} got: {:?} " ,
expected_value , value
) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 13:37:39 -06:00
}
}
2019-08-04 14:20:09 -06:00
_ = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturn Get " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " Expected Global " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 14:20:09 -06:00
}
2019-08-04 13:37:39 -06:00
}
}
}
2019-08-02 16:00:35 -06:00
}
// println!("in assert return");
2019-07-25 22:31:19 -05:00
}
2019-08-03 17:31:43 -06:00
CommandKind ::AssertReturnCanonicalNan { action } = > match action {
Action ::Invoke {
module ,
field ,
args ,
} = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result =
with_instance ( instance . clone ( ) , & named_modules , & module , | instance | {
2019-11-21 13:36:44 -08:00
let params : Vec < wasmer_runtime ::types ::Value > =
2019-09-23 13:43:01 -07:00
args . iter ( ) . cloned ( ) . map ( convert_value ) . collect ( ) ;
instance . call ( & field , & params [ .. ] )
} ) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturnCanonicalNan " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available {:?} " , & module ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
} else {
2019-09-23 13:43:01 -07:00
let call_result = maybe_call_result . unwrap ( ) ;
2019-08-03 17:31:43 -06:00
match call_result {
Err ( e ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturnCanonicalNan " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " Call failed {:?} " , e ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
Ok ( values ) = > {
2019-08-04 16:44:27 -06:00
for v in values . iter ( ) {
2019-08-03 17:31:43 -06:00
if is_canonical_nan ( v . clone ( ) ) {
2019-08-04 16:44:27 -06:00
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
kind : format ! (
" {:?} " ,
" AssertReturnCanonicalNan "
) ,
message : format ! (
2019-10-17 12:20:34 -07:00
" value is not canonical nan {:?} ({:?}) " ,
v ,
value_to_hex ( v . clone ( ) ) ,
2019-08-04 16:13:47 -06:00
) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
}
}
}
}
}
_ = > panic! ( " unexpected action in assert return canonical nan " ) ,
} ,
CommandKind ::AssertReturnArithmeticNan { action } = > match action {
Action ::Invoke {
module ,
field ,
args ,
} = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result =
with_instance ( instance . clone ( ) , & named_modules , & module , | instance | {
2019-11-21 13:36:44 -08:00
let params : Vec < wasmer_runtime ::types ::Value > =
2019-09-23 13:43:01 -07:00
args . iter ( ) . cloned ( ) . map ( convert_value ) . collect ( ) ;
instance . call ( & field , & params [ .. ] )
} ) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturnArithmeticNan " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
} else {
2019-09-23 13:43:01 -07:00
let call_result = maybe_call_result . unwrap ( ) ;
2019-08-03 17:31:43 -06:00
match call_result {
Err ( e ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertReturnArithmeticNan " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " Call failed {:?} " , e ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
Ok ( values ) = > {
2019-08-04 16:44:27 -06:00
for v in values . iter ( ) {
2019-08-03 17:31:43 -06:00
if is_arithmetic_nan ( v . clone ( ) ) {
2019-08-04 16:44:27 -06:00
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
kind : format ! (
" {:?} " ,
" AssertReturnArithmeticNan "
) ,
message : format ! (
2019-10-17 12:20:34 -07:00
" value is not arithmetic nan {:?} ({:?}) " ,
v ,
value_to_hex ( v . clone ( ) ) ,
2019-08-04 16:13:47 -06:00
) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
}
}
}
}
}
_ = > panic! ( " unexpected action in assert return arithmetic nan " ) ,
} ,
2019-08-04 16:44:27 -06:00
CommandKind ::AssertTrap { action , message : _ } = > match action {
2019-08-03 17:31:43 -06:00
Action ::Invoke {
module ,
field ,
args ,
} = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result =
with_instance ( instance . clone ( ) , & named_modules , & module , | instance | {
2020-01-16 02:59:27 +08:00
#[ cfg(unix) ]
use wasmer_runtime ::{
pop_code_version , push_code_version , CodeVersion ,
} ;
// Manually push code version before calling WebAssembly function, as a hack.
//
// This should eventually be fixed by doing push/pop code version in the function invocation
// logic itself.
#[ cfg(unix) ]
let cv_pushed = if let Some ( msm ) =
instance . module . runnable_module . get_module_state_map ( )
{
push_code_version ( CodeVersion {
baseline : true ,
msm : msm ,
base : instance
. module
. runnable_module
. get_code ( )
. unwrap ( )
. as_ptr ( )
as usize ,
backend : backend . into ( ) ,
runnable_module : instance . module . runnable_module . clone ( ) ,
} ) ;
true
} else {
false
} ;
2019-11-21 13:36:44 -08:00
let params : Vec < wasmer_runtime ::types ::Value > =
2019-09-23 13:43:01 -07:00
args . iter ( ) . cloned ( ) . map ( convert_value ) . collect ( ) ;
2020-01-16 02:59:27 +08:00
let ret = instance . call ( & field , & params [ .. ] ) ;
#[ cfg(unix) ]
{
if cv_pushed {
pop_code_version ( ) . unwrap ( ) ;
}
}
ret
2019-09-23 13:43:01 -07:00
} ) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertTrap " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
} else {
2019-09-23 13:43:01 -07:00
let call_result = maybe_call_result . unwrap ( ) ;
2019-11-21 13:36:44 -08:00
use wasmer_runtime ::error ::{ CallError , RuntimeError } ;
2019-08-03 17:31:43 -06:00
match call_result {
2020-02-06 23:49:50 +08:00
Err ( e ) = > match e {
CallError ::Resolve ( _ ) = > {
test_report . add_failure (
SpecFailure {
file : filename . to_string ( ) ,
line ,
kind : format ! ( " {} " , " AssertTrap " ) ,
message : format ! ( " expected trap, got {:?} " , e ) ,
} ,
& test_key ,
excludes ,
line ,
) ;
}
CallError ::Runtime ( RuntimeError ( e ) ) = > {
use wasmer_runtime ::ExceptionCode ;
2020-02-11 03:03:05 +08:00
if let Some ( _ ) = e . downcast_ref ::< ExceptionCode > ( ) {
2020-02-06 23:49:50 +08:00
test_report . count_passed ( ) ;
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertTrap " ) ,
2020-02-06 23:49:50 +08:00
message : format ! (
2020-02-11 03:03:05 +08:00
" expected trap, got RuntimeError "
2020-02-06 23:49:50 +08:00
) ,
2019-08-04 16:13:47 -06:00
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
}
2020-02-06 23:49:50 +08:00
} ,
2019-08-03 17:31:43 -06:00
Ok ( values ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertTrap " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " expected trap, got {:?} " , values ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:31:43 -06:00
}
}
}
}
_ = > println! ( " unexpected action " ) ,
} ,
2019-08-04 16:44:27 -06:00
CommandKind ::AssertInvalid { module , message : _ } = > {
2019-08-03 17:31:43 -06:00
// println!("AssertInvalid");
2019-08-02 16:00:35 -06:00
let result = panic ::catch_unwind ( | | {
2019-08-04 14:44:55 -06:00
let config = CompilerConfig {
2019-08-08 19:15:20 -07:00
features : Features {
simd : true ,
threads : true ,
} ,
2020-03-16 23:40:02 +08:00
nan_canonicalization : true ,
2020-03-24 15:46:58 -07:00
enable_verification : true ,
2019-08-04 14:44:55 -06:00
.. Default ::default ( )
} ;
2020-04-03 16:27:49 -07:00
let compiler = compiler_for_backend ( backend_enum ) . unwrap ( ) ;
compile_with_config_with ( & module . into_vec ( ) , config , & * compiler )
2019-08-02 16:00:35 -06:00
} ) ;
match result {
Ok ( module ) = > {
2019-08-04 16:44:27 -06:00
if let Err ( CompileError ::InternalError { msg : _ } ) = module {
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
// println!("expected: {:?}", message);
// println!("actual: {:?}", msg);
2019-08-04 16:44:27 -06:00
} else if let Err ( CompileError ::ValidationError { msg : _ } ) = module {
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
// println!("validation expected: {:?}", message);
// println!("validation actual: {:?}", msg);
2019-08-02 16:00:35 -06:00
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertInvalid " ) ,
2019-08-04 16:13:47 -06:00
message : " Should be invalid " . to_string ( ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-02 16:00:35 -06:00
}
}
Err ( p ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertInvalid " ) ,
2020-03-10 12:28:54 +08:00
message : format ! ( " caught panic {} " , format_panic ( & p ) ) ,
2019-08-04 16:13:47 -06:00
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-02 16:00:35 -06:00
}
}
}
2019-08-04 16:44:27 -06:00
CommandKind ::AssertMalformed { module , message : _ } = > {
2019-08-03 17:31:43 -06:00
// println!("AssertMalformed");
2019-08-02 11:36:38 -06:00
let result = panic ::catch_unwind ( | | {
2019-08-04 14:44:55 -06:00
let config = CompilerConfig {
2019-08-08 19:15:20 -07:00
features : Features {
simd : true ,
threads : true ,
} ,
2020-03-16 23:40:02 +08:00
nan_canonicalization : true ,
2020-03-24 15:46:58 -07:00
enable_verification : true ,
2019-08-04 14:44:55 -06:00
.. Default ::default ( )
} ;
2020-04-03 16:27:49 -07:00
let compiler = compiler_for_backend ( backend_enum ) . unwrap ( ) ;
compile_with_config_with ( & module . into_vec ( ) , config , & * compiler )
2019-08-02 11:36:38 -06:00
} ) ;
match result {
Ok ( module ) = > {
2019-08-04 16:44:27 -06:00
if let Err ( CompileError ::InternalError { msg : _ } ) = module {
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
// println!("expected: {:?}", message);
// println!("actual: {:?}", msg);
2019-08-04 16:44:27 -06:00
} else if let Err ( CompileError ::ValidationError { msg : _ } ) = module {
test_report . count_passed ( ) ;
2019-08-03 17:31:43 -06:00
// println!("validation expected: {:?}", message);
// println!("validation actual: {:?}", msg);
2019-08-02 11:36:38 -06:00
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertMalformed " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " should be malformed " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-02 11:36:38 -06:00
}
}
Err ( p ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertMalformed " ) ,
2020-03-10 12:28:54 +08:00
message : format ! ( " caught panic {} " , format_panic ( & p ) ) ,
2019-08-04 16:13:47 -06:00
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-02 11:36:38 -06:00
}
2019-07-25 22:31:19 -05:00
}
}
2019-10-04 12:37:31 -07:00
CommandKind ::AssertUninstantiable { module , message : _ } = > {
let spectest_import_object = get_spectest_import_object ( & registered_modules ) ;
let config = CompilerConfig {
features : Features {
simd : true ,
threads : true ,
} ,
2020-03-16 23:40:02 +08:00
nan_canonicalization : true ,
2020-03-24 15:46:58 -07:00
enable_verification : true ,
2019-10-04 12:37:31 -07:00
.. Default ::default ( )
} ;
2020-04-03 16:27:49 -07:00
let compiler = compiler_for_backend ( backend_enum ) . unwrap ( ) ;
let module = compile_with_config_with ( & module . into_vec ( ) , config , & * compiler )
2019-11-21 13:38:22 -08:00
. expect ( " WASM can't be compiled " ) ;
2019-10-04 12:37:31 -07:00
let result = panic ::catch_unwind ( AssertUnwindSafe ( | | {
module
. instantiate ( & spectest_import_object )
. expect ( " WASM can't be instantiated " ) ;
} ) ) ;
match result {
Err ( _ ) = > test_report . count_passed ( ) ,
Ok ( _ ) = > {
test_report . add_failure (
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
kind : format ! ( " {} " , " AssertUninstantiable " ) ,
message : format ! (
" instantiate successful, expected uninstantiable "
) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-10-04 12:37:31 -07:00
) ;
}
} ;
}
2019-08-04 16:44:27 -06:00
CommandKind ::AssertExhaustion { action , message : _ } = > {
2019-08-04 13:18:57 -06:00
match action {
Action ::Invoke {
module ,
field ,
args ,
} = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result = with_instance (
instance . clone ( ) ,
& named_modules ,
& module ,
| instance | {
2019-11-21 13:36:44 -08:00
let params : Vec < wasmer_runtime ::types ::Value > =
2019-09-23 13:43:01 -07:00
args . iter ( ) . cloned ( ) . map ( convert_value ) . collect ( ) ;
instance . call ( & field , & params [ .. ] )
2019-08-04 14:20:09 -06:00
} ,
2019-09-23 13:43:01 -07:00
) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertExhaustion " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 13:18:57 -06:00
} else {
2019-09-23 13:43:01 -07:00
let call_result = maybe_call_result . unwrap ( ) ;
2019-08-04 13:18:57 -06:00
match call_result {
2019-08-04 16:44:27 -06:00
Err ( _e ) = > {
// TODO is specific error required?
test_report . count_passed ( ) ;
2019-08-04 13:18:57 -06:00
}
Ok ( values ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertExhaustion " ) ,
2019-08-04 16:13:47 -06:00
message : format ! (
" Expected call failure, got {:?} " ,
values
) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 13:18:57 -06:00
}
}
}
}
2019-08-04 13:37:39 -06:00
_ = > println! ( " unexpected action in assert exhaustion " ) ,
2019-08-04 13:18:57 -06:00
}
2019-08-03 17:57:34 -06:00
}
2019-08-04 16:44:27 -06:00
CommandKind ::AssertUnlinkable { module , message : _ } = > {
2019-08-08 21:03:00 -06:00
let result = panic ::catch_unwind ( AssertUnwindSafe ( | | {
let spectest_import_object =
get_spectest_import_object ( & registered_modules ) ;
2019-08-04 14:44:55 -06:00
let config = CompilerConfig {
2019-08-08 19:15:20 -07:00
features : Features {
simd : true ,
threads : true ,
} ,
2020-03-16 23:40:02 +08:00
nan_canonicalization : true ,
2020-03-24 15:46:58 -07:00
enable_verification : true ,
2019-08-04 14:44:55 -06:00
.. Default ::default ( )
} ;
2020-04-03 16:27:49 -07:00
let compiler = compiler_for_backend ( backend_enum ) . unwrap ( ) ;
let module =
compile_with_config_with ( & module . into_vec ( ) , config , & * compiler )
. expect ( " WASM can't be compiled " ) ;
2019-08-08 21:03:00 -06:00
module . instantiate ( & spectest_import_object )
} ) ) ;
2019-08-03 17:57:34 -06:00
match result {
Err ( e ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
2019-08-03 17:57:34 -06:00
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertUnlinkable " ) ,
2020-03-10 12:28:54 +08:00
message : format ! ( " caught panic {} " , format_panic ( & e ) ) ,
2019-08-04 16:13:47 -06:00
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
}
Ok ( result ) = > match result {
Ok ( _ ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertUnlinkable " ) ,
2019-08-04 16:13:47 -06:00
message : format ! (
" instantiate successful, expected unlinkable "
) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:57:34 -06:00
}
Err ( e ) = > match e {
2019-11-21 13:36:44 -08:00
wasmer_runtime ::error ::Error ::LinkError ( _ ) = > {
2019-08-04 16:44:27 -06:00
test_report . count_passed ( ) ;
2019-08-03 17:57:34 -06:00
}
_ = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " AssertUnlinkable " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " expected link error, got {:?} " , e ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-03 17:57:34 -06:00
}
} ,
} ,
}
}
2019-08-04 10:19:50 -06:00
CommandKind ::Register { name , as_name } = > {
2019-09-23 13:43:01 -07:00
let instance : Option < Arc < Mutex < Instance > > > = match name {
2019-08-04 15:36:17 -06:00
Some ( ref name ) = > {
let i = named_modules . get ( name ) ;
match i {
2019-09-23 13:43:01 -07:00
Some ( ins ) = > Some ( Arc ::clone ( ins ) ) ,
2019-08-04 15:36:17 -06:00
None = > None ,
}
}
None = > match instance {
2019-09-23 13:43:01 -07:00
Some ( ref i ) = > Some ( Arc ::clone ( i ) ) ,
2019-08-04 15:36:17 -06:00
None = > None ,
} ,
} ;
2019-08-10 15:56:25 -06:00
if let Some ( ins ) = instance {
registered_modules . insert ( as_name , ins ) ;
} else {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " Register " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 15:36:17 -06:00
}
2019-08-04 10:19:50 -06:00
}
CommandKind ::PerformAction ( ref action ) = > match action {
Action ::Invoke {
module ,
field ,
args ,
} = > {
2019-09-23 13:43:01 -07:00
let maybe_call_result =
with_instance ( instance . clone ( ) , & named_modules , & module , | instance | {
2019-11-21 13:36:44 -08:00
let params : Vec < wasmer_runtime ::types ::Value > =
2019-09-23 13:43:01 -07:00
args . iter ( ) . cloned ( ) . map ( convert_value ) . collect ( ) ;
instance . call ( & field , & params [ .. ] )
} ) ;
if maybe_call_result . is_none ( ) {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line : line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " PerformAction " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " No instance available " ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 10:19:50 -06:00
} else {
2019-09-23 13:43:01 -07:00
let call_result = maybe_call_result . unwrap ( ) ;
2019-08-04 14:20:09 -06:00
match call_result {
Err ( e ) = > {
2019-08-04 16:44:27 -06:00
test_report . add_failure (
2019-08-04 16:13:47 -06:00
SpecFailure {
file : filename . to_string ( ) ,
line ,
2019-08-05 17:51:02 -06:00
kind : format ! ( " {} " , " PerformAction " ) ,
2019-08-04 16:13:47 -06:00
message : format ! ( " Call failed {:?} " , e ) ,
} ,
& test_key ,
excludes ,
2019-11-19 23:38:50 -06:00
line ,
2019-08-04 16:13:47 -06:00
) ;
2019-08-04 14:20:09 -06:00
}
2019-08-04 16:44:27 -06:00
Ok ( _values ) = > {
test_report . count_passed ( ) ;
2019-08-04 10:19:50 -06:00
}
}
}
}
2019-08-04 16:44:27 -06:00
Action ::Get { module , field } = > println! (
" Action Get not implemented {:?} {:?} {:?} {:?} " ,
module , field , filename , line
) ,
2019-08-04 10:19:50 -06:00
} ,
2019-07-25 22:31:19 -05:00
}
}
2020-02-21 03:23:44 +08:00
// Check for unused excludes.
for excl in excludes {
if let Some ( ref excl ) = * excl {
if excl . exclude_kind = = ExcludeKind ::Fail {
test_report . failed + = 1 ;
test_report . failures . push ( SpecFailure {
file : filename . to_string ( ) ,
line : excl . line . unwrap_or ( 0 ) ,
kind : format ! ( " {} " , " Exclude " ) ,
message : format ! ( " Excluded failure did not occur " ) ,
} ) ;
}
}
}
2019-08-02 16:00:35 -06:00
Ok ( test_report )
2019-07-25 22:31:19 -05:00
}
2019-11-21 13:36:44 -08:00
fn is_canonical_nan ( val : wasmer_runtime ::types ::Value ) -> bool {
2019-08-03 17:31:43 -06:00
match val {
2019-11-21 13:36:44 -08:00
wasmer_runtime ::types ::Value ::F32 ( x ) = > x . is_canonical_nan ( ) ,
wasmer_runtime ::types ::Value ::F64 ( x ) = > x . is_canonical_nan ( ) ,
2019-08-03 17:31:43 -06:00
_ = > panic! ( " value is not a float {:?} " , val ) ,
}
}
2019-11-21 13:36:44 -08:00
fn is_arithmetic_nan ( val : wasmer_runtime ::types ::Value ) -> bool {
2019-08-03 17:31:43 -06:00
match val {
2019-11-21 13:36:44 -08:00
wasmer_runtime ::types ::Value ::F32 ( x ) = > x . is_quiet_nan ( ) ,
wasmer_runtime ::types ::Value ::F64 ( x ) = > x . is_quiet_nan ( ) ,
2019-08-03 17:31:43 -06:00
_ = > panic! ( " value is not a float {:?} " , val ) ,
}
2019-10-17 12:20:34 -07:00
}
2019-11-21 13:36:44 -08:00
fn value_to_hex ( val : wasmer_runtime ::types ::Value ) -> String {
2019-10-17 12:20:34 -07:00
match val {
2019-11-21 13:36:44 -08:00
wasmer_runtime ::types ::Value ::I32 ( x ) = > format! ( " {:#x} " , x ) ,
wasmer_runtime ::types ::Value ::I64 ( x ) = > format! ( " {:#x} " , x ) ,
wasmer_runtime ::types ::Value ::F32 ( x ) = > format! ( " {:#x} " , x . to_bits ( ) ) ,
wasmer_runtime ::types ::Value ::F64 ( x ) = > format! ( " {:#x} " , x . to_bits ( ) ) ,
wasmer_runtime ::types ::Value ::V128 ( x ) = > format! ( " {:#x} " , x ) ,
2019-10-17 12:20:34 -07:00
}
2019-08-03 17:31:43 -06:00
}
2019-08-13 22:14:10 -06:00
#[ derive(Debug, Clone, Eq, PartialEq) ]
pub enum SpectestValue {
I32 ( i32 ) ,
I64 ( i64 ) ,
F32 ( u32 ) ,
F64 ( u64 ) ,
V128 ( u128 ) ,
}
2019-11-21 13:36:44 -08:00
fn convert_wasmer_value ( other : wasmer_runtime ::types ::Value ) -> SpectestValue {
2019-08-13 22:14:10 -06:00
match other {
2019-11-21 13:36:44 -08:00
wasmer_runtime ::types ::Value ::I32 ( v ) = > SpectestValue ::I32 ( v ) ,
wasmer_runtime ::types ::Value ::I64 ( v ) = > SpectestValue ::I64 ( v ) ,
wasmer_runtime ::types ::Value ::F32 ( v ) = > SpectestValue ::F32 ( v . to_bits ( ) ) ,
wasmer_runtime ::types ::Value ::F64 ( v ) = > SpectestValue ::F64 ( v . to_bits ( ) ) ,
wasmer_runtime ::types ::Value ::V128 ( v ) = > SpectestValue ::V128 ( v ) ,
2019-08-13 22:14:10 -06:00
}
}
fn convert_wabt_value ( other : Value < f32 , f64 > ) -> SpectestValue {
match other {
Value ::I32 ( v ) = > SpectestValue ::I32 ( v ) ,
Value ::I64 ( v ) = > SpectestValue ::I64 ( v ) ,
Value ::F32 ( v ) = > SpectestValue ::F32 ( v . to_bits ( ) ) ,
Value ::F64 ( v ) = > SpectestValue ::F64 ( v . to_bits ( ) ) ,
Value ::V128 ( v ) = > SpectestValue ::V128 ( v ) ,
}
}
2019-11-21 13:36:44 -08:00
fn convert_value ( other : Value < f32 , f64 > ) -> wasmer_runtime ::types ::Value {
2019-08-03 17:31:43 -06:00
match other {
2019-11-21 13:36:44 -08:00
Value ::I32 ( v ) = > wasmer_runtime ::types ::Value ::I32 ( v ) ,
Value ::I64 ( v ) = > wasmer_runtime ::types ::Value ::I64 ( v ) ,
Value ::F32 ( v ) = > wasmer_runtime ::types ::Value ::F32 ( v ) ,
Value ::F64 ( v ) = > wasmer_runtime ::types ::Value ::F64 ( v ) ,
Value ::V128 ( v ) = > wasmer_runtime ::types ::Value ::V128 ( v ) ,
2019-08-03 17:31:43 -06:00
}
}
2019-08-13 22:14:10 -06:00
fn to_hex ( v : SpectestValue ) -> String {
2019-08-03 17:31:43 -06:00
match v {
2019-08-13 22:14:10 -06:00
SpectestValue ::I32 ( v ) = > format! ( " {:#x} " , v ) ,
SpectestValue ::I64 ( v ) = > format! ( " {:#x} " , v ) ,
SpectestValue ::F32 ( v ) = > format! ( " {:#x} " , v ) ,
SpectestValue ::F64 ( v ) = > format! ( " {:#x} " , v ) ,
SpectestValue ::V128 ( v ) = > format! ( " {:#x} " , v ) ,
2019-08-03 17:31:43 -06:00
}
}
2019-08-08 21:03:00 -06:00
fn print ( _ctx : & mut Ctx ) {
println! ( " " ) ;
}
2019-08-04 16:44:27 -06:00
fn print_i32 ( _ctx : & mut Ctx , val : i32 ) {
2019-08-04 10:19:50 -06:00
println! ( " {} " , val ) ;
}
2019-08-05 19:25:12 -06:00
fn print_f32 ( _ctx : & mut Ctx , val : f32 ) {
println! ( " {} " , val ) ;
}
fn print_f64 ( _ctx : & mut Ctx , val : f64 ) {
println! ( " {} " , val ) ;
}
fn print_i32_f32 ( _ctx : & mut Ctx , val : i32 , val2 : f32 ) {
println! ( " {} {} " , val , val2 ) ;
}
fn print_f64_f64 ( _ctx : & mut Ctx , val : f64 , val2 : f64 ) {
println! ( " {} {} " , val , val2 ) ;
}
2019-08-10 15:56:25 -06:00
fn get_spectest_import_object (
2019-09-23 13:43:01 -07:00
registered_modules : & HashMap < String , Arc < Mutex < Instance > > > ,
2019-08-10 15:56:25 -06:00
) -> ImportObject {
2019-09-20 18:59:36 +02:00
let memory_desc = MemoryDescriptor ::new ( Pages ( 1 ) , Some ( Pages ( 2 ) ) , false ) . unwrap ( ) ;
2019-09-20 18:54:05 +02:00
let memory = Memory ::new ( memory_desc ) . unwrap ( ) ;
2019-08-04 10:19:50 -06:00
2019-11-21 13:36:44 -08:00
let global_i32 = Global ::new ( wasmer_runtime ::types ::Value ::I32 ( 666 ) ) ;
let global_f32 = Global ::new ( wasmer_runtime ::types ::Value ::F32 ( 666.0 ) ) ;
let global_f64 = Global ::new ( wasmer_runtime ::types ::Value ::F64 ( 666.0 ) ) ;
2019-08-04 10:19:50 -06:00
let table = Table ::new ( TableDescriptor {
element : ElementType ::Anyfunc ,
minimum : 10 ,
maximum : Some ( 20 ) ,
} )
. unwrap ( ) ;
2019-08-04 15:36:17 -06:00
let mut import_object = imports! {
2019-08-04 10:19:50 -06:00
" spectest " = > {
2019-08-08 21:03:00 -06:00
" print " = > func! ( print ) ,
2019-08-04 10:19:50 -06:00
" print_i32 " = > func! ( print_i32 ) ,
2019-08-05 19:25:12 -06:00
" print_f32 " = > func! ( print_f32 ) ,
" print_f64 " = > func! ( print_f64 ) ,
" print_i32_f32 " = > func! ( print_i32_f32 ) ,
" print_f64_f64 " = > func! ( print_f64_f64 ) ,
2019-08-04 10:19:50 -06:00
" table " = > table ,
2019-08-04 16:44:27 -06:00
" memory " = > memory ,
2019-08-04 10:19:50 -06:00
" global_i32 " = > global_i32 ,
" global_f32 " = > global_f32 ,
" global_f64 " = > global_f64 ,
} ,
2019-08-04 15:36:17 -06:00
} ;
2019-08-10 15:56:25 -06:00
for ( name , instance ) in registered_modules . iter ( ) {
2019-09-23 13:43:01 -07:00
import_object . register ( name . clone ( ) , Arc ::clone ( instance ) ) ;
2019-08-04 10:19:50 -06:00
}
2019-08-04 15:36:17 -06:00
import_object
2019-08-04 10:19:50 -06:00
}
2019-08-04 11:25:46 -06:00
#[ derive(Debug, Copy, Clone, PartialEq, Eq) ]
2019-11-19 23:38:50 -06:00
enum ExcludeKind {
2019-08-04 11:25:46 -06:00
Skip ,
Fail ,
}
2019-08-04 16:44:27 -06:00
use core ::borrow ::Borrow ;
2019-08-04 11:25:46 -06:00
use std ::fs ::File ;
2019-08-04 16:44:27 -06:00
use std ::io ::{ BufRead , BufReader } ;
2019-08-04 11:25:46 -06:00
/// Reads the excludes.txt file into a hash map
2020-04-03 16:27:49 -07:00
fn read_excludes ( current_backend : & str ) -> ( HashMap < String , Vec < Exclude > > , HashSet < String > ) {
2019-08-04 11:25:46 -06:00
let mut excludes_path = PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) ;
excludes_path . push ( " tests " ) ;
2020-04-03 14:24:40 -07:00
excludes_path . push ( " spectests " ) ;
2019-08-04 11:25:46 -06:00
excludes_path . push ( " excludes.txt " ) ;
2020-04-03 16:27:49 -07:00
let input = File ::open ( excludes_path ) . unwrap ( ) ;
2019-08-04 11:25:46 -06:00
let buffered = BufReader ::new ( input ) ;
let mut result = HashMap ::new ( ) ;
2019-11-19 23:38:50 -06:00
let mut file_excludes = HashSet ::new ( ) ;
2020-02-21 03:47:01 +08:00
let current_target_os = get_target_os ( ) ;
2019-11-19 23:38:50 -06:00
let current_target_family = get_target_family ( ) ;
let current_target_arch = get_target_arch ( ) ;
2019-08-04 11:25:46 -06:00
for line in buffered . lines ( ) {
let mut line = line . unwrap ( ) ;
if line . trim ( ) . is_empty ( ) | | line . starts_with ( " # " ) {
// ignore line
} else {
2019-08-04 12:42:20 -06:00
if line . contains ( " # " ) {
// Allow end of line comment
2019-08-04 11:25:46 -06:00
let l : Vec < & str > = line . split ( '#' ) . collect ( ) ;
line = l . get ( 0 ) . unwrap ( ) . to_string ( ) ;
}
//println!("exclude line {}", line);
// <backend>:<exclude-kind>:<test-file-name>:<test-file-line>
let split : Vec < & str > = line . trim ( ) . split ( ':' ) . collect ( ) ;
2019-08-05 20:57:07 -06:00
2019-11-19 23:38:50 -06:00
let file = * split . get ( 2 ) . unwrap ( ) ;
let exclude = match split . len ( ) {
0 ..= 3 = > panic! ( " expected at least 4 exclude conditions " ) ,
4 = > Exclude ::from (
* split . get ( 0 ) . unwrap ( ) ,
* split . get ( 1 ) . unwrap ( ) ,
* split . get ( 2 ) . unwrap ( ) ,
* split . get ( 3 ) . unwrap ( ) ,
" * " ,
" * " ,
) ,
5 = > Exclude ::from (
* split . get ( 0 ) . unwrap ( ) ,
* split . get ( 1 ) . unwrap ( ) ,
* split . get ( 2 ) . unwrap ( ) ,
* split . get ( 3 ) . unwrap ( ) ,
* split . get ( 4 ) . unwrap ( ) ,
" * " ,
) ,
6 = > Exclude ::from (
* split . get ( 0 ) . unwrap ( ) ,
* split . get ( 1 ) . unwrap ( ) ,
* split . get ( 2 ) . unwrap ( ) ,
* split . get ( 3 ) . unwrap ( ) ,
* split . get ( 4 ) . unwrap ( ) ,
* split . get ( 5 ) . unwrap ( ) ,
) ,
_ = > panic! ( " too many exclude conditions {} " , split . len ( ) ) ,
2019-08-05 20:57:07 -06:00
} ;
2019-11-19 23:38:50 -06:00
if exclude . matches_backend ( current_backend )
2020-02-21 03:47:01 +08:00
& & ( exclude . matches_target_family ( current_target_os )
| | exclude . matches_target_family ( current_target_family ) )
2019-11-19 23:38:50 -06:00
& & exclude . matches_target_arch ( current_target_arch )
{
// Skip the whole file for line * and skip
if exclude . line . is_none ( ) & & exclude . exclude_kind = = ExcludeKind ::Skip {
file_excludes . insert ( file . to_string ( ) ) ;
}
if ! result . contains_key ( file ) {
result . insert ( file . to_string ( ) , vec! [ ] ) ;
}
result . get_mut ( file ) . unwrap ( ) . push ( exclude ) ;
}
2019-08-04 11:25:46 -06:00
}
}
2019-11-19 23:38:50 -06:00
( result , file_excludes )
2019-08-04 11:25:46 -06:00
}
2019-07-25 22:31:19 -05:00
#[ test ]
fn test_run_spectests ( ) {
2019-08-04 12:42:20 -06:00
let mut glob_path = PathBuf ::from ( env! ( " CARGO_MANIFEST_DIR " ) ) ;
2020-04-03 14:24:40 -07:00
glob_path . push ( " tests " ) ;
2019-08-04 12:42:20 -06:00
glob_path . push ( " spectests " ) ;
glob_path . push ( " *.wast " ) ;
2020-04-03 16:27:49 -07:00
let available_compilers = get_available_compilers ( )
. iter ( )
. cloned ( )
. collect ::< HashSet < & 'static str > > ( ) ;
let desired_compilers = get_compilers_to_test ( ) ;
// default to testing all enabled compilers
let compilers_to_test = if desired_compilers . is_empty ( ) {
available_compilers . iter ( ) . cloned ( ) . collect ::< Vec < _ > > ( )
} else {
desired_compilers
. iter ( )
. cloned ( )
. filter ( | c | available_compilers . contains ( c ) )
. collect ::< Vec < _ > > ( )
} ;
// if we've asked to run specific compilers, make sure they're all actually enabled
if ! desired_compilers . is_empty ( ) & & desired_compilers . len ( ) ! = compilers_to_test . len ( ) {
panic! ( " Asked to run spectests with ` {:?} ` compilers but ` {:?} ` compilers are enabled (found {} of them: {:?} ) " , desired_compilers , get_compilers_to_test ( ) , compilers_to_test . len ( ) , compilers_to_test ) ;
}
2019-08-04 12:42:20 -06:00
let glob_str = glob_path . to_str ( ) . unwrap ( ) ;
2020-04-03 16:27:49 -07:00
for backend in compilers_to_test {
println! ( " Testing ` {} ` backend " , backend ) ;
let ( excludes , file_excludes ) = read_excludes ( backend ) ;
let mut test_reports = vec! [ ] ;
let mut success = true ;
for entry in glob ( glob_str ) . expect ( " Failed to read glob pattern " ) {
match entry {
Ok ( wast_path ) = > {
let result = parse_and_run ( & wast_path , & file_excludes , & excludes , backend ) ;
match result {
Ok ( test_report ) = > {
if test_report . has_failures ( ) {
success = false
}
test_reports . push ( test_report ) ;
}
Err ( e ) = > {
success = false ;
println! ( " Unexpected test run error: {:?} " , e )
2019-08-04 12:42:20 -06:00
}
}
2019-08-02 16:00:35 -06:00
}
2020-04-03 16:27:49 -07:00
Err ( e ) = > panic! ( " glob err: {:?} " , e ) ,
2019-08-02 16:00:35 -06:00
}
2019-07-25 22:31:19 -05:00
}
2020-04-03 16:27:49 -07:00
// Print summary
let mut failures = vec! [ ] ;
let mut total_passed = 0 ;
let mut total_failed = 0 ;
let mut total_allowed_failures = 0 ;
for mut test_report in test_reports . into_iter ( ) {
total_passed + = test_report . passed ;
total_failed + = test_report . failed ;
total_allowed_failures + = test_report . allowed_failure ;
failures . append ( & mut test_report . failures ) ;
}
2019-08-02 16:00:35 -06:00
2020-04-03 16:27:49 -07:00
println! ( " " ) ;
println! ( " {} backend results: " , backend ) ;
println! ( " Failures: " ) ;
for failure in failures . iter ( ) {
// To print excludes for all failures:
println! (
" {}:fail:{}:{} # {} - {} " ,
backend , failure . file , failure . line , failure . kind , failure . message
) ;
}
println! ( " " ) ;
println! ( " " ) ;
println! ( " Spec tests summary report: " ) ;
2019-08-02 16:00:35 -06:00
println! (
2020-04-03 16:27:49 -07:00
" total: {} " ,
total_passed + total_failed + total_allowed_failures
2019-08-02 16:00:35 -06:00
) ;
2020-04-03 16:27:49 -07:00
println! ( " passed: {} " , total_passed ) ;
println! ( " failed: {} " , total_failed ) ;
println! ( " allowed failures: {} " , total_allowed_failures ) ;
println! ( " " ) ;
println! ( " Tests {} . " , if success { " passed " } else { " failed " } ) ;
println! ( " " ) ;
assert! ( success , " tests passed " )
2019-08-02 16:00:35 -06:00
}
2019-07-25 22:31:19 -05:00
}
2019-08-03 17:31:43 -06:00
/// Bit pattern of an f32 value:
/// 1-bit sign + 8-bit mantissa + 23-bit exponent = 32 bits
///
/// Bit pattern of an f64 value:
/// 1-bit sign + 11-bit mantissa + 52-bit exponent = 64 bits
///
/// NOTE: On some old platforms (PA-RISC, some MIPS) quiet NaNs (qNaN) have
/// their mantissa MSB unset and set for signaling NaNs (sNaN).
///
/// Links:
/// * https://en.wikipedia.org/wiki/Floating-point_arithmetic
/// * https://github.com/WebAssembly/spec/issues/286
/// * https://en.wikipedia.org/wiki/NaN
///
pub trait NaNCheck {
fn is_quiet_nan ( & self ) -> bool ;
fn is_canonical_nan ( & self ) -> bool ;
}
impl NaNCheck for f32 {
/// The MSB of the mantissa must be set for a NaN to be a quiet NaN.
fn is_quiet_nan ( & self ) -> bool {
2019-08-05 15:38:03 -07:00
let mantissa_msb = 0b1 < < 22 ;
self . is_nan ( ) & & ( self . to_bits ( ) & mantissa_msb ) ! = 0
2019-08-03 17:31:43 -06:00
}
2019-08-05 15:38:03 -07:00
/// For a NaN to be canonical, the MSB of the mantissa must be set and
/// all other mantissa bits must be unset.
2019-08-03 17:31:43 -06:00
fn is_canonical_nan ( & self ) -> bool {
2019-08-05 15:38:03 -07:00
return self . to_bits ( ) = = 0xFFC0_0000 | | self . to_bits ( ) = = 0x7FC0_0000 ;
2019-08-03 17:31:43 -06:00
}
}
impl NaNCheck for f64 {
/// The MSB of the mantissa must be set for a NaN to be a quiet NaN.
fn is_quiet_nan ( & self ) -> bool {
2019-08-05 15:38:03 -07:00
let mantissa_msb = 0b1 < < 51 ;
self . is_nan ( ) & & ( self . to_bits ( ) & mantissa_msb ) ! = 0
2019-08-03 17:31:43 -06:00
}
2019-08-05 15:38:03 -07:00
/// For a NaN to be canonical, the MSB of the mantissa must be set and
/// all other mantissa bits must be unset.
2019-08-03 17:31:43 -06:00
fn is_canonical_nan ( & self ) -> bool {
2019-08-05 15:38:03 -07:00
self . to_bits ( ) = = 0x7FF8_0000_0000_0000 | | self . to_bits ( ) = = 0xFFF8_0000_0000_0000
2019-08-03 17:31:43 -06:00
}
}
2019-07-25 22:31:19 -05:00
}