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> = Vec::new();
23
24 parser.with_standard_annotations_registered(|parser: Parser<'a>| {
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: Parser<'a>| p.parse())?);
30 }
31 } else {
32 let module: Wat<'_> = 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: &str = match cursor.keyword()? {
45 Some((kw: &str, _)) => 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: &WastInvoke<'_>) => i.span,
286 WastExecute::Wat(i: &Wat<'_>) => i.span(),
287 WastExecute::Get { span: &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: Lookahead1<'_> = 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: 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: Span = parser.parse::<kw::invoke>()?.0;
338 let module: Option> = parser.parse()?;
339 let name: &str = parser.parse()?;
340 let mut args: Vec> = Vec::new();
341 while !parser.is_empty() {
342 args.push(parser.parens(|p: Parser<'a>| 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: fn(Span, Vec<(Span, &[u8])>) -> … = 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: Span = parser.parse::<kw::quote>()?.0;
478 let mut src: Vec<(Span, &[u8])> = Vec::new();
479 while !parser.is_empty() {
480 let span: Span = parser.cur_span();
481 let string: &[u8] = 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}
584