inf_wast/
wast.rs

1#[cfg(feature = "component-model")]
2use crate::component::WastVal;
3use crate::core::{WastArgCore, WastRetCore};
4use crate::kw;
5use crate::parser::{self, Cursor, Parse, ParseBuffer, Parser, Peek, Result};
6use crate::token::{Id, Span};
7use crate::{Error, Wat};
8
9/// A parsed representation of a `*.wast` file.
10///
11/// WAST files are not officially specified but are used in the official test
12/// suite to write official spec tests for wasm. This type represents a parsed
13/// `*.wast` file which parses a list of directives in a file.
14#[derive(Debug)]
15pub struct Wast<'a> {
16    #[allow(missing_docs)]
17    pub directives: Vec<WastDirective<'a>>,
18}
19
20impl<'a> Parse<'a> for Wast<'a> {
21    fn parse(parser: Parser<'a>) -> Result<Self> {
22        let mut directives = Vec::new();
23
24        parser.with_standard_annotations_registered(|parser| {
25            // If it looks like a directive token is in the stream then we parse a
26            // bunch of directives, otherwise assume this is an inline module.
27            if parser.peek2::<WastDirectiveToken>()? {
28                while !parser.is_empty() {
29                    directives.push(parser.parens(|p| p.parse())?);
30                }
31            } else {
32                let module = parser.parse::<Wat>()?;
33                directives.push(WastDirective::Module(QuoteWat::Wat(module)));
34            }
35            Ok(Wast { directives })
36        })
37    }
38}
39
40struct WastDirectiveToken;
41
42impl Peek for WastDirectiveToken {
43    fn peek(cursor: Cursor<'_>) -> Result<bool> {
44        let kw = match cursor.keyword()? {
45            Some((kw, _)) => kw,
46            None => return Ok(false),
47        };
48        Ok(kw.starts_with("assert_")
49            || kw == "module"
50            || kw == "component"
51            || kw == "register"
52            || kw == "invoke")
53    }
54
55    fn display() -> &'static str {
56        unimplemented!()
57    }
58}
59
60/// The different kinds of directives found in a `*.wast` file.
61///
62///
63/// Some more information about these various branches can be found at
64/// <https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#scripts>.
65#[allow(missing_docs)]
66#[derive(Debug)]
67pub enum WastDirective<'a> {
68    /// The provided module is defined, validated, and then instantiated.
69    Module(QuoteWat<'a>),
70
71    /// The provided module is defined and validated.
72    ///
73    /// This module is not instantiated automatically.
74    ModuleDefinition(QuoteWat<'a>),
75
76    /// The named module is instantiated under the instance name provided.
77    ModuleInstance {
78        span: Span,
79        instance: Option<Id<'a>>,
80        module: Option<Id<'a>>,
81    },
82
83    /// Asserts the module cannot be decoded with the given error.
84    AssertMalformed {
85        span: Span,
86        module: QuoteWat<'a>,
87        message: &'a str,
88    },
89
90    /// Asserts the module cannot be validated with the given error.
91    AssertInvalid {
92        span: Span,
93        module: QuoteWat<'a>,
94        message: &'a str,
95    },
96
97    /// Registers the `module` instance with the given `name` to be available
98    /// for importing in future module instances.
99    Register {
100        span: Span,
101        name: &'a str,
102        module: Option<Id<'a>>,
103    },
104
105    /// Invokes the specified export.
106    Invoke(WastInvoke<'a>),
107
108    /// The invocation provided should trap with the specified error.
109    AssertTrap {
110        span: Span,
111        exec: WastExecute<'a>,
112        message: &'a str,
113    },
114
115    /// The invocation provided should succeed with the specified results.
116    AssertReturn {
117        span: Span,
118        exec: WastExecute<'a>,
119        results: Vec<WastRet<'a>>,
120    },
121
122    /// The invocation provided should exhaust system resources (e.g. stack
123    /// overflow).
124    AssertExhaustion {
125        span: Span,
126        call: WastInvoke<'a>,
127        message: &'a str,
128    },
129
130    /// The provided module should fail to link when instantiation is attempted.
131    AssertUnlinkable {
132        span: Span,
133        module: Wat<'a>,
134        message: &'a str,
135    },
136
137    /// The invocation provided should throw an exception.
138    AssertException { span: Span, exec: WastExecute<'a> },
139
140    /// The invocation should fail to handle a suspension.
141    AssertSuspension {
142        span: Span,
143        exec: WastExecute<'a>,
144        message: &'a str,
145    },
146
147    /// Creates a new system thread which executes the given commands.
148    Thread(WastThread<'a>),
149
150    /// Waits for the specified thread to exit.
151    Wait { span: Span, thread: Id<'a> },
152}
153
154impl WastDirective<'_> {
155    /// Returns the location in the source that this directive was defined at
156    pub fn span(&self) -> Span {
157        match self {
158            WastDirective::Module(QuoteWat::Wat(w))
159            | WastDirective::ModuleDefinition(QuoteWat::Wat(w)) => w.span(),
160            WastDirective::Module(QuoteWat::QuoteModule(span, _))
161            | WastDirective::ModuleDefinition(QuoteWat::QuoteModule(span, _)) => *span,
162            WastDirective::Module(QuoteWat::QuoteComponent(span, _))
163            | WastDirective::ModuleDefinition(QuoteWat::QuoteComponent(span, _)) => *span,
164            WastDirective::ModuleInstance { span, .. }
165            | WastDirective::AssertMalformed { span, .. }
166            | WastDirective::Register { span, .. }
167            | WastDirective::AssertTrap { span, .. }
168            | WastDirective::AssertReturn { span, .. }
169            | WastDirective::AssertExhaustion { span, .. }
170            | WastDirective::AssertUnlinkable { span, .. }
171            | WastDirective::AssertInvalid { span, .. }
172            | WastDirective::AssertException { span, .. }
173            | WastDirective::AssertSuspension { span, .. }
174            | WastDirective::Wait { span, .. } => *span,
175            WastDirective::Invoke(i) => i.span,
176            WastDirective::Thread(t) => t.span,
177        }
178    }
179}
180
181impl<'a> Parse<'a> for WastDirective<'a> {
182    fn parse(parser: Parser<'a>) -> Result<Self> {
183        let mut l = parser.lookahead1();
184        if l.peek::<kw::module>()? || l.peek::<kw::component>()? {
185            parse_wast_module(parser)
186        } else if l.peek::<kw::assert_malformed>()? {
187            let span = parser.parse::<kw::assert_malformed>()?.0;
188            Ok(WastDirective::AssertMalformed {
189                span,
190                module: parser.parens(|p| p.parse())?,
191                message: parser.parse()?,
192            })
193        } else if l.peek::<kw::assert_invalid>()? {
194            let span = parser.parse::<kw::assert_invalid>()?.0;
195            Ok(WastDirective::AssertInvalid {
196                span,
197                module: parser.parens(|p| p.parse())?,
198                message: parser.parse()?,
199            })
200        } else if l.peek::<kw::register>()? {
201            let span = parser.parse::<kw::register>()?.0;
202            Ok(WastDirective::Register {
203                span,
204                name: parser.parse()?,
205                module: parser.parse()?,
206            })
207        } else if l.peek::<kw::invoke>()? {
208            Ok(WastDirective::Invoke(parser.parse()?))
209        } else if l.peek::<kw::assert_trap>()? {
210            let span = parser.parse::<kw::assert_trap>()?.0;
211            Ok(WastDirective::AssertTrap {
212                span,
213                exec: parser.parens(|p| p.parse())?,
214                message: parser.parse()?,
215            })
216        } else if l.peek::<kw::assert_return>()? {
217            let span = parser.parse::<kw::assert_return>()?.0;
218            let exec = parser.parens(|p| p.parse())?;
219            let mut results = Vec::new();
220            while !parser.is_empty() {
221                results.push(parser.parens(|p| p.parse())?);
222            }
223            Ok(WastDirective::AssertReturn {
224                span,
225                exec,
226                results,
227            })
228        } else if l.peek::<kw::assert_exhaustion>()? {
229            let span = parser.parse::<kw::assert_exhaustion>()?.0;
230            Ok(WastDirective::AssertExhaustion {
231                span,
232                call: parser.parens(|p| p.parse())?,
233                message: parser.parse()?,
234            })
235        } else if l.peek::<kw::assert_unlinkable>()? {
236            let span = parser.parse::<kw::assert_unlinkable>()?.0;
237            Ok(WastDirective::AssertUnlinkable {
238                span,
239                module: parser.parens(parse_wat)?,
240                message: parser.parse()?,
241            })
242        } else if l.peek::<kw::assert_exception>()? {
243            let span = parser.parse::<kw::assert_exception>()?.0;
244            Ok(WastDirective::AssertException {
245                span,
246                exec: parser.parens(|p| p.parse())?,
247            })
248        } else if l.peek::<kw::assert_suspension>()? {
249            let span = parser.parse::<kw::assert_suspension>()?.0;
250            Ok(WastDirective::AssertSuspension {
251                span,
252                exec: parser.parens(|p| p.parse())?,
253                message: parser.parse()?,
254            })
255        } else if l.peek::<kw::thread>()? {
256            Ok(WastDirective::Thread(parser.parse()?))
257        } else if l.peek::<kw::wait>()? {
258            let span = parser.parse::<kw::wait>()?.0;
259            Ok(WastDirective::Wait {
260                span,
261                thread: parser.parse()?,
262            })
263        } else {
264            Err(l.error())
265        }
266    }
267}
268
269#[allow(missing_docs)]
270#[derive(Debug)]
271pub enum WastExecute<'a> {
272    Invoke(WastInvoke<'a>),
273    Wat(Wat<'a>),
274    Get {
275        span: Span,
276        module: Option<Id<'a>>,
277        global: &'a str,
278    },
279}
280
281impl<'a> WastExecute<'a> {
282    /// Returns the first span for this execute statement.
283    pub fn span(&self) -> Span {
284        match self {
285            WastExecute::Invoke(i) => i.span,
286            WastExecute::Wat(i) => i.span(),
287            WastExecute::Get { span, .. } => *span,
288        }
289    }
290}
291
292impl<'a> Parse<'a> for WastExecute<'a> {
293    fn parse(parser: Parser<'a>) -> Result<Self> {
294        let mut l = parser.lookahead1();
295        if l.peek::<kw::invoke>()? {
296            Ok(WastExecute::Invoke(parser.parse()?))
297        } else if l.peek::<kw::module>()? || l.peek::<kw::component>()? {
298            Ok(WastExecute::Wat(parse_wat(parser)?))
299        } else if l.peek::<kw::get>()? {
300            let span = parser.parse::<kw::get>()?.0;
301            Ok(WastExecute::Get {
302                span,
303                module: parser.parse()?,
304                global: parser.parse()?,
305            })
306        } else {
307            Err(l.error())
308        }
309    }
310}
311
312fn parse_wat(parser: Parser) -> Result<Wat> {
313    // Note that this doesn't use `Parse for Wat` since the `parser` provided
314    // has already peeled back the first layer of parentheses while `Parse for
315    // Wat` expects to be the top layer which means it also tries to peel off
316    // the parens. Instead we can skip the sugar that `Wat` has for simply a
317    // list of fields (no `(module ...)` container) and just parse the `Module`
318    // itself.
319    if parser.peek::<kw::component>()? {
320        Ok(Wat::Component(parser.parse()?))
321    } else {
322        Ok(Wat::Module(parser.parse()?))
323    }
324}
325
326#[allow(missing_docs)]
327#[derive(Debug)]
328pub struct WastInvoke<'a> {
329    pub span: Span,
330    pub module: Option<Id<'a>>,
331    pub name: &'a str,
332    pub args: Vec<WastArg<'a>>,
333}
334
335impl<'a> Parse<'a> for WastInvoke<'a> {
336    fn parse(parser: Parser<'a>) -> Result<Self> {
337        let span = parser.parse::<kw::invoke>()?.0;
338        let module = parser.parse()?;
339        let name = parser.parse()?;
340        let mut args = Vec::new();
341        while !parser.is_empty() {
342            args.push(parser.parens(|p| p.parse())?);
343        }
344        Ok(WastInvoke {
345            span,
346            module,
347            name,
348            args,
349        })
350    }
351}
352
353fn parse_wast_module<'a>(parser: Parser<'a>) -> Result<WastDirective<'a>> {
354    if parser.peek2::<kw::quote>()? {
355        QuoteWat::parse(parser).map(WastDirective::Module)
356    } else if parser.peek2::<kw::definition>()? {
357        fn parse_module(span: Span, parser: Parser<'_>) -> Result<Wat<'_>> {
358            Ok(Wat::Module(
359                crate::core::Module::parse_without_module_keyword(span, parser)?,
360            ))
361        }
362        fn parse_component(_span: Span, parser: Parser<'_>) -> Result<Wat<'_>> {
363            #[cfg(feature = "component-model")]
364            return Ok(Wat::Component(
365                crate::component::Component::parse_without_component_keyword(_span, parser)?,
366            ));
367            #[cfg(not(feature = "component-model"))]
368            return Err(parser.error("component model support disabled at compile time"));
369        }
370        let (span, ctor) = if parser.peek::<kw::component>()? {
371            (
372                parser.parse::<kw::component>()?.0,
373                parse_component as fn(_, _) -> _,
374            )
375        } else {
376            (
377                parser.parse::<kw::module>()?.0,
378                parse_module as fn(_, _) -> _,
379            )
380        };
381        parser.parse::<kw::definition>()?;
382        Ok(WastDirective::ModuleDefinition(QuoteWat::Wat(ctor(
383            span, parser,
384        )?)))
385    } else if parser.peek2::<kw::instance>()? {
386        let span = if parser.peek::<kw::component>()? {
387            parser.parse::<kw::component>()?.0
388        } else {
389            parser.parse::<kw::module>()?.0
390        };
391        parser.parse::<kw::instance>()?;
392        Ok(WastDirective::ModuleInstance {
393            span,
394            instance: parser.parse()?,
395            module: parser.parse()?,
396        })
397    } else {
398        QuoteWat::parse(parser).map(WastDirective::Module)
399    }
400}
401
402#[allow(missing_docs)]
403#[derive(Debug)]
404pub enum QuoteWat<'a> {
405    Wat(Wat<'a>),
406    QuoteModule(Span, Vec<(Span, &'a [u8])>),
407    QuoteComponent(Span, Vec<(Span, &'a [u8])>),
408}
409
410impl<'a> QuoteWat<'a> {
411    /// Encodes this module to bytes, either by encoding the module directly or
412    /// parsing the contents and then encoding it.
413    pub fn encode(&mut self) -> Result<Vec<u8>, Error> {
414        match self.to_test()? {
415            QuoteWatTest::Binary(bytes) => Ok(bytes),
416            QuoteWatTest::Text(text) => {
417                let text = std::str::from_utf8(&text).map_err(|_| {
418                    let span = self.span();
419                    Error::new(span, "malformed UTF-8 encoding".to_string())
420                })?;
421                let buf = ParseBuffer::new(&text)?;
422                let mut wat = parser::parse::<Wat<'_>>(&buf)?;
423                wat.encode()
424            }
425        }
426    }
427
428    /// Converts this to either a `QuoteWatTest::Binary` or
429    /// `QuoteWatTest::Text` depending on what it is internally.
430    pub fn to_test(&mut self) -> Result<QuoteWatTest, Error> {
431        let (source, prefix) = match self {
432            QuoteWat::Wat(m) => return m.encode().map(QuoteWatTest::Binary),
433            QuoteWat::QuoteModule(_, source) => (source, None),
434            QuoteWat::QuoteComponent(_, source) => (source, Some("(component")),
435        };
436        let mut ret = Vec::new();
437        for (_, src) in source {
438            ret.extend_from_slice(src);
439            ret.push(b' ');
440        }
441        if let Some(prefix) = prefix {
442            ret.splice(0..0, prefix.as_bytes().iter().copied());
443            ret.push(b')');
444        }
445        Ok(QuoteWatTest::Text(ret))
446    }
447
448    /// Returns the identifier, if registered, for this module.
449    pub fn name(&self) -> Option<Id<'a>> {
450        match self {
451            QuoteWat::Wat(Wat::Module(m)) => m.id,
452            QuoteWat::Wat(Wat::Component(m)) => m.id,
453            QuoteWat::QuoteModule(..) | QuoteWat::QuoteComponent(..) => None,
454        }
455    }
456
457    /// Returns the defining span of this module.
458    pub fn span(&self) -> Span {
459        match self {
460            QuoteWat::Wat(w) => w.span(),
461            QuoteWat::QuoteModule(span, _) => *span,
462            QuoteWat::QuoteComponent(span, _) => *span,
463        }
464    }
465}
466
467impl<'a> Parse<'a> for QuoteWat<'a> {
468    fn parse(parser: Parser<'a>) -> Result<Self> {
469        if parser.peek2::<kw::quote>()? {
470            let ctor = if parser.peek::<kw::component>()? {
471                parser.parse::<kw::component>()?;
472                QuoteWat::QuoteComponent
473            } else {
474                parser.parse::<kw::module>()?;
475                QuoteWat::QuoteModule
476            };
477            let span = parser.parse::<kw::quote>()?.0;
478            let mut src = Vec::new();
479            while !parser.is_empty() {
480                let span = parser.cur_span();
481                let string = parser.parse()?;
482                src.push((span, string));
483            }
484            Ok(ctor(span, src))
485        } else {
486            Ok(QuoteWat::Wat(parse_wat(parser)?))
487        }
488    }
489}
490
491/// Returned from [`QuoteWat::to_test`].
492#[allow(missing_docs)]
493#[derive(Debug)]
494pub enum QuoteWatTest {
495    Binary(Vec<u8>),
496    Text(Vec<u8>),
497}
498
499#[derive(Debug)]
500#[allow(missing_docs)]
501#[non_exhaustive]
502pub enum WastArg<'a> {
503    Core(WastArgCore<'a>),
504    #[cfg(feature = "component-model")]
505    Component(WastVal<'a>),
506}
507
508impl<'a> Parse<'a> for WastArg<'a> {
509    fn parse(parser: Parser<'a>) -> Result<Self> {
510        #[cfg(feature = "component-model")]
511        if parser.peek::<WastArgCore<'_>>()? {
512            Ok(WastArg::Core(parser.parse()?))
513        } else {
514            Ok(WastArg::Component(parser.parse()?))
515        }
516
517        #[cfg(not(feature = "component-model"))]
518        Ok(WastArg::Core(parser.parse()?))
519    }
520}
521
522#[derive(Debug)]
523#[allow(missing_docs)]
524#[non_exhaustive]
525pub enum WastRet<'a> {
526    Core(WastRetCore<'a>),
527    #[cfg(feature = "component-model")]
528    Component(WastVal<'a>),
529}
530
531impl<'a> Parse<'a> for WastRet<'a> {
532    fn parse(parser: Parser<'a>) -> Result<Self> {
533        #[cfg(feature = "component-model")]
534        if parser.peek::<WastRetCore<'_>>()? {
535            Ok(WastRet::Core(parser.parse()?))
536        } else {
537            Ok(WastRet::Component(parser.parse()?))
538        }
539
540        #[cfg(not(feature = "component-model"))]
541        Ok(WastRet::Core(parser.parse()?))
542    }
543}
544
545#[derive(Debug)]
546#[allow(missing_docs)]
547pub struct WastThread<'a> {
548    pub span: Span,
549    pub name: Id<'a>,
550    pub shared_module: Option<Id<'a>>,
551    pub directives: Vec<WastDirective<'a>>,
552}
553
554impl<'a> Parse<'a> for WastThread<'a> {
555    fn parse(parser: Parser<'a>) -> Result<Self> {
556        parser.depth_check()?;
557        let span = parser.parse::<kw::thread>()?.0;
558        let name = parser.parse()?;
559
560        let shared_module = if parser.peek2::<kw::shared>()? {
561            let name = parser.parens(|p| {
562                p.parse::<kw::shared>()?;
563                p.parens(|p| {
564                    p.parse::<kw::module>()?;
565                    p.parse()
566                })
567            })?;
568            Some(name)
569        } else {
570            None
571        };
572        let mut directives = Vec::new();
573        while !parser.is_empty() {
574            directives.push(parser.parens(|p| p.parse())?);
575        }
576        Ok(WastThread {
577            span,
578            name,
579            shared_module,
580            directives,
581        })
582    }
583}