1+ use anyhow:: { Context as _, Result , bail, ensure} ;
12use std:: fs:: { File , OpenOptions } ;
23use std:: io:: { Read , Write } ;
34use std:: path:: Path ;
4-
5- use anyhow:: { Context as _, Result , ensure} ;
5+ use wast:: core:: WastRetCore ;
66
77/// Convert a single WAST file
88fn convert_wast_file (
99 input_wast : & mut File ,
10- input_wast_path : & PathBuf ,
10+ input_wast_path : & Path ,
1111 output_wasm : & mut File ,
1212 output_js : & mut File ,
1313) -> Result < ( ) > {
@@ -23,11 +23,16 @@ fn convert_wast_file(
2323 )
2424 } ) ?;
2525
26- // TODO: write JS preamble
26+ // Start exported test function
2727 writeln ! (
2828 output_js,
2929 r#"
30- import {{ }} from "../";
30+ export async function runWastTest(args) {{
31+ if (!args) {{ throw new Error('missing args'); }}
32+ if (!args.instance) {{ throw new Error('missing loaded wasm instance'); }}
33+ if (!args.assert) {{ throw new Error('missing assert obj'); }}
34+ const {{ instance, assert }} = args;
35+ let res;
3136 "#
3237 ) ?;
3338
@@ -49,44 +54,182 @@ fn convert_wast_file(
4954 output_wasm. flush ( ) ?;
5055 }
5156 wast:: WastDirective :: ModuleDefinition ( _) => {
52- todo ! ( "unsupported directive ModuleDefinition" )
57+ bail ! ( "unsupported directive ModuleDefinition" )
5358 }
5459 wast:: WastDirective :: ModuleInstance { .. } => {
55- todo ! ( "unsupported directive ModuleInstance" )
60+ bail ! ( "unsupported directive ModuleInstance" )
5661 }
5762 wast:: WastDirective :: AssertMalformed { .. } => {
58- todo ! ( "unsupported directive AssertMalformed" )
63+ bail ! ( "unsupported directive AssertMalformed" )
5964 }
6065 wast:: WastDirective :: AssertInvalid { .. } => {
61- todo ! ( "unsupported directive AssertInvalid" )
66+ bail ! ( "unsupported directive AssertInvalid" )
6267 }
6368 wast:: WastDirective :: Register { .. } => {
64- todo ! ( "unsupported directive Register" )
69+ bail ! ( "unsupported directive Register" )
70+ }
71+ wast:: WastDirective :: Invoke ( _) => bail ! ( "unsupported directive Invoke" ) ,
72+ wast:: WastDirective :: AssertTrap { .. } => bail ! ( "unsupported directive AssertTrap" ) ,
73+ wast:: WastDirective :: AssertReturn { exec, results, .. } => {
74+ ensure ! (
75+ results. len( ) == 1 ,
76+ "assert return with multiple results not yet supported"
77+ ) ;
78+ // TODO: we need to check asyncness for await or not
79+ let ( export_name, args) = extract_export_fn ( & exec) ?;
80+ let check_expr = match results. first ( ) {
81+ Some ( ret) => {
82+ format ! ( "assert.strictEqual(res, {});" , wast_ret_to_js_param( ret) ?)
83+ }
84+ None => "" . into ( ) ,
85+ } ;
86+ writeln ! (
87+ output_js,
88+ r#"
89+ res = await instance['{export_name}']({});
90+ {check_expr}
91+ "# ,
92+ args_to_js_params( args) ?,
93+ ) ?;
6594 }
66- wast:: WastDirective :: Invoke ( _) => todo ! ( "unsupported directive Invoke" ) ,
67- wast:: WastDirective :: AssertTrap { .. } => todo ! ( "unsupported directive AssertTrap" ) ,
68- wast:: WastDirective :: AssertReturn { .. } => todo ! ( "unsupported directive AssertReturn" ) ,
6995 wast:: WastDirective :: AssertExhaustion { .. } => {
70- todo ! ( "unsupported directive AssertExhaustion" )
96+ bail ! ( "unsupported directive AssertExhaustion" )
7197 }
7298 wast:: WastDirective :: AssertUnlinkable { .. } => {
73- todo ! ( "unsupported directive AssertUnlinkable" )
99+ bail ! ( "unsupported directive AssertUnlinkable" )
74100 }
75101 wast:: WastDirective :: AssertException { .. } => {
76- todo ! ( "unsupported directive AssertException" )
102+ bail ! ( "unsupported directive AssertException" )
77103 }
78104 wast:: WastDirective :: AssertSuspension { .. } => {
79- todo ! ( "unsupported directive AssertSuspension" )
105+ bail ! ( "unsupported directive AssertSuspension" )
80106 }
81- wast:: WastDirective :: Thread ( _) => todo ! ( "unsupported directive Thread" ) ,
82- wast:: WastDirective :: Wait { .. } => todo ! ( "unsupported directive Wait" ) ,
107+ wast:: WastDirective :: Thread ( _) => bail ! ( "unsupported directive Thread" ) ,
108+ wast:: WastDirective :: Wait { .. } => bail ! ( "unsupported directive Wait" ) ,
83109 }
84110 }
85111
112+ // Close out the function
113+ writeln ! ( output_js, "}}" , ) ?;
114+
86115 output_js. flush ( ) ?;
87116 Ok ( ( ) )
88117}
89118
119+ /// Generate a list of JS params
120+ fn args_to_js_params ( args : & [ wast:: WastArg < ' _ > ] ) -> Result < String > {
121+ args. iter ( )
122+ . map ( |arg| match arg {
123+ wast:: WastArg :: Core ( v) => core_val_to_js_param ( v) ,
124+ wast:: WastArg :: Component ( v) => cm_val_to_js_param ( v) ,
125+ _ => bail ! ( "unsupported wast arg" ) ,
126+ } )
127+ . collect :: < Result < Vec < String > > > ( )
128+ . map ( |s| s. join ( "," ) )
129+ }
130+
131+ /// Convert a Wast core value to a JS value
132+ fn core_val_to_js_param ( wast_arg : & wast:: core:: WastArgCore < ' _ > ) -> Result < String > {
133+ match wast_arg {
134+ wast:: core:: WastArgCore :: I32 ( v) => Ok ( format ! ( "{v}" ) ) ,
135+ wast:: core:: WastArgCore :: I64 ( v) => Ok ( format ! ( "{v}" ) ) ,
136+ wast:: core:: WastArgCore :: F32 ( v) => Ok ( format ! ( "{:.8}" , f32 :: from_bits( v. bits) ) ) ,
137+ wast:: core:: WastArgCore :: F64 ( v) => Ok ( format ! ( "{:.8}" , f64 :: from_bits( v. bits) ) ) ,
138+ wast:: core:: WastArgCore :: V128 ( v) => Ok ( format ! ( "{}" , i128 :: from_le_bytes( v. to_le_bytes( ) ) ) ) ,
139+ wast:: core:: WastArgCore :: RefNull ( _) => bail ! ( "refs unsupported core args" ) ,
140+ wast:: core:: WastArgCore :: RefExtern ( _) => bail ! ( "refs unsupported core args" ) ,
141+ wast:: core:: WastArgCore :: RefHost ( _) => bail ! ( "refs unsupported core args" ) ,
142+ }
143+ }
144+
145+ /// Convert a Wast return value to a JS value
146+ fn wast_ret_to_js_param ( wast_ret : & wast:: WastRet < ' _ > ) -> Result < String > {
147+ match wast_ret {
148+ wast:: WastRet :: Core ( wast_ret_core) => wast_ret_core_val_to_js_param ( wast_ret_core) ,
149+ wast:: WastRet :: Component ( wast_val) => cm_val_to_js_param ( wast_val) ,
150+ _ => bail ! ( "unsupported wast ret" ) ,
151+ }
152+ }
153+
154+ /// Convert a Wast CM value to a JS value
155+ fn wast_ret_core_val_to_js_param ( wast_ret_core : & WastRetCore < ' _ > ) -> Result < String > {
156+ match wast_ret_core {
157+ WastRetCore :: I32 ( _) => bail ! ( "WastRetCore::I32 not yet supported" ) ,
158+ WastRetCore :: I64 ( _) => bail ! ( "WastRetCore::I64 not yet supported" ) ,
159+ WastRetCore :: F32 ( _nan_pattern) => bail ! ( "WastRetCore::F32 not yet supported" ) ,
160+ WastRetCore :: F64 ( _nan_pattern) => bail ! ( "WastRetCore::F64 not yet supported" ) ,
161+ WastRetCore :: V128 ( _v128_pattern) => bail ! ( "WastRetCore::V128 not yet supported" ) ,
162+ WastRetCore :: RefNull ( _heap_type) => bail ! ( "WastRetCore::RefNull not yet supported" ) ,
163+ WastRetCore :: RefExtern ( _) => bail ! ( "WastRetCore::RefExtern not yet supported" ) ,
164+ WastRetCore :: RefHost ( _) => bail ! ( "WastRetCore::RefHost not yet supported" ) ,
165+ WastRetCore :: RefFunc ( _index) => bail ! ( "WastRetCore::RefFunc not yet supported" ) ,
166+ WastRetCore :: RefAny => bail ! ( "WastRetCore::RefAny not yet supported" ) ,
167+ WastRetCore :: RefEq => bail ! ( "WastRetCore::RefEq not yet supported" ) ,
168+ WastRetCore :: RefArray => bail ! ( "WastRetCore::RefArray not yet supported" ) ,
169+ WastRetCore :: RefStruct => bail ! ( "WastRetCore::RefStruct not yet supported" ) ,
170+ WastRetCore :: RefI31 => bail ! ( "WastRetCore::RefI31 not yet supported" ) ,
171+ WastRetCore :: RefI31Shared => bail ! ( "WastRetCore::RefI31Shared not yet supported" ) ,
172+ WastRetCore :: Either ( _wast_ret_cores) => bail ! ( "WastRetCore::Either not yet supported" ) ,
173+ }
174+ }
175+
176+ /// Convert a Wast CM value to a JS value
177+ fn cm_val_to_js_param ( wast_val : & wast:: component:: WastVal < ' _ > ) -> Result < String > {
178+ match wast_val {
179+ wast:: component:: WastVal :: Bool ( v) => Ok ( format ! ( "{v}" ) ) ,
180+ wast:: component:: WastVal :: U8 ( v) => Ok ( format ! ( "{v}" ) ) ,
181+ wast:: component:: WastVal :: S8 ( v) => Ok ( format ! ( "{v}" ) ) ,
182+ wast:: component:: WastVal :: U16 ( v) => Ok ( format ! ( "{v}" ) ) ,
183+ wast:: component:: WastVal :: S16 ( v) => Ok ( format ! ( "{v}" ) ) ,
184+ wast:: component:: WastVal :: U32 ( v) => Ok ( format ! ( "{v}" ) ) ,
185+ wast:: component:: WastVal :: S32 ( v) => Ok ( format ! ( "{v}" ) ) ,
186+ wast:: component:: WastVal :: U64 ( v) => Ok ( format ! ( "{v}" ) ) ,
187+ wast:: component:: WastVal :: S64 ( v) => Ok ( format ! ( "{v}" ) ) ,
188+ wast:: component:: WastVal :: F32 ( v) => Ok ( format ! ( "{:.8}" , f32 :: from_bits( v. bits) ) ) ,
189+ wast:: component:: WastVal :: F64 ( v) => Ok ( format ! ( "{:.8}" , f64 :: from_bits( v. bits) ) ) ,
190+ wast:: component:: WastVal :: Char ( v) => Ok ( format ! ( "'{v}'" ) ) ,
191+ wast:: component:: WastVal :: String ( s) => Ok ( format ! ( "'{s}'" ) ) ,
192+ wast:: component:: WastVal :: List ( vals) | wast:: component:: WastVal :: Tuple ( vals) => vals
193+ . iter ( )
194+ . map ( |v| cm_val_to_js_param ( v) )
195+ . collect :: < Result < Vec < String > > > ( )
196+ . map ( |parts| parts. join ( "," ) )
197+ . map ( |v| format ! ( "[{v}]" ) ) ,
198+ wast:: component:: WastVal :: Record ( items) => items
199+ . iter ( )
200+ . map ( |( k, v) | cm_val_to_js_param ( v) . map ( |v| format ! ( "{k}: {v}" ) ) )
201+ . collect :: < Result < Vec < String > > > ( )
202+ . map ( |parts| parts. join ( "," ) )
203+ . map ( |v| format ! ( "{{{v}}}" ) ) ,
204+ wast:: component:: WastVal :: Variant ( tag, wast_val) => match wast_val {
205+ Some ( v) => cm_val_to_js_param ( v) . map ( |v| format ! ( "{{ tag: '{tag}', val: {v} }}" ) ) ,
206+ None => Ok ( format ! ( "{{ tag: '{tag}', val: null }}" ) ) ,
207+ } ,
208+ wast:: component:: WastVal :: Enum ( v) => Ok ( format ! ( "{{ tag: '{v}' }}" ) ) ,
209+ wast:: component:: WastVal :: Option ( wast_val) => match wast_val {
210+ Some ( v) => cm_val_to_js_param ( v) ,
211+ None => Ok ( "null" . into ( ) ) ,
212+ } ,
213+ wast:: component:: WastVal :: Result ( wast_val) => match wast_val {
214+ Ok ( v) => match v {
215+ Some ( v) => cm_val_to_js_param ( v) ,
216+ None => Ok ( "{{ tag: 'ok', val: null }}" . into ( ) ) ,
217+ } ,
218+ Err ( e) => match e {
219+ Some ( v) => cm_val_to_js_param ( v) ,
220+ None => Ok ( "{{ tag: 'err', val: null }}" . into ( ) ) ,
221+ } ,
222+ } ,
223+ wast:: component:: WastVal :: Flags ( items) => Ok ( format ! (
224+ "{{{}}}" ,
225+ items
226+ . iter( )
227+ . map( |k| format!( "{k}: true" ) )
228+ . collect:: <Vec <String >>( )
229+ . join( "," )
230+ ) ) ,
231+ }
232+ }
90233/// Build WAST tests that can be used to test p3 host compliance
91234pub ( crate ) fn run ( wast_path : & Path ) -> Result < ( ) > {
92235 let wast_path = wast_path. canonicalize ( ) . with_context ( || {
@@ -106,7 +249,6 @@ pub(crate) fn run(wast_path: &Path) -> Result<()> {
106249 output_wasm_path. add_extension ( "wasm" ) ;
107250 let mut output_wasm = OpenOptions :: new ( )
108251 . write ( true )
109- . create_new ( true )
110252 . truncate ( true )
111253 . open ( & output_wasm_path)
112254 . with_context ( || {
@@ -120,7 +262,6 @@ pub(crate) fn run(wast_path: &Path) -> Result<()> {
120262 output_js_path. add_extension ( "js" ) ;
121263 let mut output_js = OpenOptions :: new ( )
122264 . write ( true )
123- . create_new ( true )
124265 . truncate ( true )
125266 . open ( output_js_path)
126267 . with_context ( || format ! ( "failed to open output JS file @ [{}]" , wast_path. display( ) ) ) ?;
@@ -129,3 +270,22 @@ pub(crate) fn run(wast_path: &Path) -> Result<()> {
129270
130271 Ok ( ( ) )
131272}
273+
274+ /// Extract the export function from an Exec, along with it's results
275+ fn extract_export_fn < ' a > (
276+ exec : & ' a wast:: WastExecute ,
277+ ) -> Result < ( & ' a str , & ' a [ wast:: WastArg < ' a > ] ) > {
278+ match exec {
279+ wast:: WastExecute :: Invoke ( wast:: WastInvoke {
280+ module, name, args, ..
281+ } ) => {
282+ ensure ! (
283+ module. is_none( ) ,
284+ "wast invocations with modules not yet supported"
285+ ) ;
286+ Ok ( ( * name, args) )
287+ }
288+ wast:: WastExecute :: Wat ( _) => bail ! ( "unsupported wast execute type WastExecute::Wat" ) ,
289+ wast:: WastExecute :: Get { .. } => bail ! ( "unsupported wast execute type WastExecute::Get" ) ,
290+ }
291+ }
0 commit comments