Coverage Report

Created: 2025-06-23 13:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/cargo-vendor-dir/wast-231.0.0/src/wast.rs
Line
Count
Source
1
#[cfg(feature = "component-model")]
2
use crate::component::WastVal;
3
use crate::core::{WastArgCore, WastRetCore};
4
use crate::kw;
5
use crate::parser::{self, Cursor, Parse, ParseBuffer, Parser, Peek, Result};
6
use crate::token::{Id, Span};
7
use 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)]
15
pub struct Wast<'a> {
16
    #[allow(missing_docs)]
17
    pub directives: Vec<WastDirective<'a>>,
18
}
19
20
impl<'a> Parse<'a> for Wast<'a> {
21
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
22
0
        let mut directives = Vec::new();
23
0
24
0
        parser.with_standard_annotations_registered(|parser| {
25
0
            // If it looks like a directive token is in the stream then we parse a
26
0
            // bunch of directives, otherwise assume this is an inline module.
27
0
            if parser.peek2::<WastDirectiveToken>()? {
28
0
                while !parser.is_empty() {
29
0
                    directives.push(parser.parens(|p| p.parse())?);
30
                }
31
            } else {
32
0
                let module = parser.parse::<Wat>()?;
33
0
                directives.push(WastDirective::Module(QuoteWat::Wat(module)));
34
            }
35
0
            Ok(Wast { directives })
36
0
        })
37
0
    }
38
}
39
40
struct WastDirectiveToken;
41
42
impl Peek for WastDirectiveToken {
43
0
    fn peek(cursor: Cursor<'_>) -> Result<bool> {
44
0
        let kw = match cursor.keyword()? {
45
0
            Some((kw, _)) => kw,
46
0
            None => return Ok(false),
47
        };
48
0
        Ok(kw.starts_with("assert_")
49
0
            || kw == "module"
50
0
            || kw == "component"
51
0
            || kw == "register"
52
0
            || kw == "invoke")
53
0
    }
54
55
0
    fn display() -> &'static str {
56
0
        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)]
67
pub 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
154
impl WastDirective<'_> {
155
    /// Returns the location in the source that this directive was defined at
156
0
    pub fn span(&self) -> Span {
157
0
        match self {
158
0
            WastDirective::Module(QuoteWat::Wat(w))
159
0
            | WastDirective::ModuleDefinition(QuoteWat::Wat(w)) => w.span(),
160
0
            WastDirective::Module(QuoteWat::QuoteModule(span, _))
161
0
            | WastDirective::ModuleDefinition(QuoteWat::QuoteModule(span, _)) => *span,
162
0
            WastDirective::Module(QuoteWat::QuoteComponent(span, _))
163
0
            | WastDirective::ModuleDefinition(QuoteWat::QuoteComponent(span, _)) => *span,
164
0
            WastDirective::ModuleInstance { span, .. }
165
0
            | WastDirective::AssertMalformed { span, .. }
166
0
            | WastDirective::Register { span, .. }
167
0
            | WastDirective::AssertTrap { span, .. }
168
0
            | WastDirective::AssertReturn { span, .. }
169
0
            | WastDirective::AssertExhaustion { span, .. }
170
0
            | WastDirective::AssertUnlinkable { span, .. }
171
0
            | WastDirective::AssertInvalid { span, .. }
172
0
            | WastDirective::AssertException { span, .. }
173
0
            | WastDirective::AssertSuspension { span, .. }
174
0
            | WastDirective::Wait { span, .. } => *span,
175
0
            WastDirective::Invoke(i) => i.span,
176
0
            WastDirective::Thread(t) => t.span,
177
        }
178
0
    }
179
}
180
181
impl<'a> Parse<'a> for WastDirective<'a> {
182
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
183
0
        let mut l = parser.lookahead1();
184
0
        if l.peek::<kw::module>()? || l.peek::<kw::component>()? {
185
0
            parse_wast_module(parser)
186
0
        } else if l.peek::<kw::assert_malformed>()? {
187
0
            let span = parser.parse::<kw::assert_malformed>()?.0;
188
            Ok(WastDirective::AssertMalformed {
189
0
                span,
190
0
                module: parser.parens(|p| p.parse())?,
191
0
                message: parser.parse()?,
192
            })
193
0
        } else if l.peek::<kw::assert_invalid>()? {
194
0
            let span = parser.parse::<kw::assert_invalid>()?.0;
195
            Ok(WastDirective::AssertInvalid {
196
0
                span,
197
0
                module: parser.parens(|p| p.parse())?,
198
0
                message: parser.parse()?,
199
            })
200
0
        } else if l.peek::<kw::register>()? {
201
0
            let span = parser.parse::<kw::register>()?.0;
202
            Ok(WastDirective::Register {
203
0
                span,
204
0
                name: parser.parse()?,
205
0
                module: parser.parse()?,
206
            })
207
0
        } else if l.peek::<kw::invoke>()? {
208
0
            Ok(WastDirective::Invoke(parser.parse()?))
209
0
        } else if l.peek::<kw::assert_trap>()? {
210
0
            let span = parser.parse::<kw::assert_trap>()?.0;
211
            Ok(WastDirective::AssertTrap {
212
0
                span,
213
0
                exec: parser.parens(|p| p.parse())?,
214
0
                message: parser.parse()?,
215
            })
216
0
        } else if l.peek::<kw::assert_return>()? {
217
0
            let span = parser.parse::<kw::assert_return>()?.0;
218
0
            let exec = parser.parens(|p| p.parse())?;
219
0
            let mut results = Vec::new();
220
0
            while !parser.is_empty() {
221
0
                results.push(parser.parens(|p| p.parse())?);
222
            }
223
0
            Ok(WastDirective::AssertReturn {
224
0
                span,
225
0
                exec,
226
0
                results,
227
0
            })
228
0
        } else if l.peek::<kw::assert_exhaustion>()? {
229
0
            let span = parser.parse::<kw::assert_exhaustion>()?.0;
230
            Ok(WastDirective::AssertExhaustion {
231
0
                span,
232
0
                call: parser.parens(|p| p.parse())?,
233
0
                message: parser.parse()?,
234
            })
235
0
        } else if l.peek::<kw::assert_unlinkable>()? {
236
0
            let span = parser.parse::<kw::assert_unlinkable>()?.0;
237
            Ok(WastDirective::AssertUnlinkable {
238
0
                span,
239
0
                module: parser.parens(parse_wat)?,
240
0
                message: parser.parse()?,
241
            })
242
0
        } else if l.peek::<kw::assert_exception>()? {
243
0
            let span = parser.parse::<kw::assert_exception>()?.0;
244
            Ok(WastDirective::AssertException {
245
0
                span,
246
0
                exec: parser.parens(|p| p.parse())?,
247
            })
248
0
        } else if l.peek::<kw::assert_suspension>()? {
249
0
            let span = parser.parse::<kw::assert_suspension>()?.0;
250
            Ok(WastDirective::AssertSuspension {
251
0
                span,
252
0
                exec: parser.parens(|p| p.parse())?,
253
0
                message: parser.parse()?,
254
            })
255
0
        } else if l.peek::<kw::thread>()? {
256
0
            Ok(WastDirective::Thread(parser.parse()?))
257
0
        } else if l.peek::<kw::wait>()? {
258
0
            let span = parser.parse::<kw::wait>()?.0;
259
            Ok(WastDirective::Wait {
260
0
                span,
261
0
                thread: parser.parse()?,
262
            })
263
        } else {
264
0
            Err(l.error())
265
        }
266
0
    }
267
}
268
269
#[allow(missing_docs)]
270
#[derive(Debug)]
271
pub 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
281
impl<'a> WastExecute<'a> {
282
    /// Returns the first span for this execute statement.
283
0
    pub fn span(&self) -> Span {
284
0
        match self {
285
0
            WastExecute::Invoke(i) => i.span,
286
0
            WastExecute::Wat(i) => i.span(),
287
0
            WastExecute::Get { span, .. } => *span,
288
        }
289
0
    }
290
}
291
292
impl<'a> Parse<'a> for WastExecute<'a> {
293
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
294
0
        let mut l = parser.lookahead1();
295
0
        if l.peek::<kw::invoke>()? {
296
0
            Ok(WastExecute::Invoke(parser.parse()?))
297
0
        } else if l.peek::<kw::module>()? || l.peek::<kw::component>()? {
298
0
            Ok(WastExecute::Wat(parse_wat(parser)?))
299
0
        } else if l.peek::<kw::get>()? {
300
0
            let span = parser.parse::<kw::get>()?.0;
301
            Ok(WastExecute::Get {
302
0
                span,
303
0
                module: parser.parse()?,
304
0
                global: parser.parse()?,
305
            })
306
        } else {
307
0
            Err(l.error())
308
        }
309
0
    }
310
}
311
312
0
fn parse_wat(parser: Parser) -> Result<Wat> {
313
0
    // Note that this doesn't use `Parse for Wat` since the `parser` provided
314
0
    // has already peeled back the first layer of parentheses while `Parse for
315
0
    // Wat` expects to be the top layer which means it also tries to peel off
316
0
    // the parens. Instead we can skip the sugar that `Wat` has for simply a
317
0
    // list of fields (no `(module ...)` container) and just parse the `Module`
318
0
    // itself.
319
0
    if parser.peek::<kw::component>()? {
320
0
        Ok(Wat::Component(parser.parse()?))
321
    } else {
322
0
        Ok(Wat::Module(parser.parse()?))
323
    }
324
0
}
325
326
#[allow(missing_docs)]
327
#[derive(Debug)]
328
pub 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
335
impl<'a> Parse<'a> for WastInvoke<'a> {
336
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
337
0
        let span = parser.parse::<kw::invoke>()?.0;
338
0
        let module = parser.parse()?;
339
0
        let name = parser.parse()?;
340
0
        let mut args = Vec::new();
341
0
        while !parser.is_empty() {
342
0
            args.push(parser.parens(|p| p.parse())?);
343
        }
344
0
        Ok(WastInvoke {
345
0
            span,
346
0
            module,
347
0
            name,
348
0
            args,
349
0
        })
350
0
    }
351
}
352
353
0
fn parse_wast_module<'a>(parser: Parser<'a>) -> Result<WastDirective<'a>> {
354
0
    if parser.peek2::<kw::quote>()? {
355
0
        QuoteWat::parse(parser).map(WastDirective::Module)
356
0
    } else if parser.peek2::<kw::definition>()? {
357
0
        fn parse_module(span: Span, parser: Parser<'_>) -> Result<Wat<'_>> {
358
0
            Ok(Wat::Module(
359
0
                crate::core::Module::parse_without_module_keyword(span, parser)?,
360
            ))
361
0
        }
362
0
        fn parse_component(_span: Span, parser: Parser<'_>) -> Result<Wat<'_>> {
363
0
            #[cfg(feature = "component-model")]
364
0
            return Ok(Wat::Component(
365
0
                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
0
        }
370
0
        let (span, ctor) = if parser.peek::<kw::component>()? {
371
            (
372
0
                parser.parse::<kw::component>()?.0,
373
0
                parse_component as fn(_, _) -> _,
374
            )
375
        } else {
376
            (
377
0
                parser.parse::<kw::module>()?.0,
378
0
                parse_module as fn(_, _) -> _,
379
            )
380
        };
381
0
        parser.parse::<kw::definition>()?;
382
0
        Ok(WastDirective::ModuleDefinition(QuoteWat::Wat(ctor(
383
0
            span, parser,
384
0
        )?)))
385
0
    } else if parser.peek2::<kw::instance>()? {
386
0
        let span = if parser.peek::<kw::component>()? {
387
0
            parser.parse::<kw::component>()?.0
388
        } else {
389
0
            parser.parse::<kw::module>()?.0
390
        };
391
0
        parser.parse::<kw::instance>()?;
392
        Ok(WastDirective::ModuleInstance {
393
0
            span,
394
0
            instance: parser.parse()?,
395
0
            module: parser.parse()?,
396
        })
397
    } else {
398
0
        QuoteWat::parse(parser).map(WastDirective::Module)
399
    }
400
0
}
401
402
#[allow(missing_docs)]
403
#[derive(Debug)]
404
pub enum QuoteWat<'a> {
405
    Wat(Wat<'a>),
406
    QuoteModule(Span, Vec<(Span, &'a [u8])>),
407
    QuoteComponent(Span, Vec<(Span, &'a [u8])>),
408
}
409
410
impl<'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
0
    pub fn encode(&mut self) -> Result<Vec<u8>, Error> {
414
0
        match self.to_test()? {
415
0
            QuoteWatTest::Binary(bytes) => Ok(bytes),
416
0
            QuoteWatTest::Text(text) => {
417
0
                let text = std::str::from_utf8(&text).map_err(|_| {
418
0
                    let span = self.span();
419
0
                    Error::new(span, "malformed UTF-8 encoding".to_string())
420
0
                })?;
421
0
                let buf = ParseBuffer::new(&text)?;
422
0
                let mut wat = parser::parse::<Wat<'_>>(&buf)?;
423
0
                wat.encode()
424
            }
425
        }
426
0
    }
427
428
    /// Converts this to either a `QuoteWatTest::Binary` or
429
    /// `QuoteWatTest::Text` depending on what it is internally.
430
0
    pub fn to_test(&mut self) -> Result<QuoteWatTest, Error> {
431
0
        let (source, prefix) = match self {
432
0
            QuoteWat::Wat(m) => return m.encode().map(QuoteWatTest::Binary),
433
0
            QuoteWat::QuoteModule(_, source) => (source, None),
434
0
            QuoteWat::QuoteComponent(_, source) => (source, Some("(component")),
435
        };
436
0
        let mut ret = Vec::new();
437
0
        for (_, src) in source {
438
0
            ret.extend_from_slice(src);
439
0
            ret.push(b' ');
440
0
        }
441
0
        if let Some(prefix) = prefix {
442
0
            ret.splice(0..0, prefix.as_bytes().iter().copied());
443
0
            ret.push(b')');
444
0
        }
445
0
        Ok(QuoteWatTest::Text(ret))
446
0
    }
447
448
    /// Returns the identifier, if registered, for this module.
449
0
    pub fn name(&self) -> Option<Id<'a>> {
450
0
        match self {
451
0
            QuoteWat::Wat(Wat::Module(m)) => m.id,
452
0
            QuoteWat::Wat(Wat::Component(m)) => m.id,
453
0
            QuoteWat::QuoteModule(..) | QuoteWat::QuoteComponent(..) => None,
454
        }
455
0
    }
456
457
    /// Returns the defining span of this module.
458
0
    pub fn span(&self) -> Span {
459
0
        match self {
460
0
            QuoteWat::Wat(w) => w.span(),
461
0
            QuoteWat::QuoteModule(span, _) => *span,
462
0
            QuoteWat::QuoteComponent(span, _) => *span,
463
        }
464
0
    }
465
}
466
467
impl<'a> Parse<'a> for QuoteWat<'a> {
468
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
469
0
        if parser.peek2::<kw::quote>()? {
470
0
            let ctor = if parser.peek::<kw::component>()? {
471
0
                parser.parse::<kw::component>()?;
472
0
                QuoteWat::QuoteComponent
473
            } else {
474
0
                parser.parse::<kw::module>()?;
475
0
                QuoteWat::QuoteModule
476
            };
477
0
            let span = parser.parse::<kw::quote>()?.0;
478
0
            let mut src = Vec::new();
479
0
            while !parser.is_empty() {
480
0
                let span = parser.cur_span();
481
0
                let string = parser.parse()?;
482
0
                src.push((span, string));
483
            }
484
0
            Ok(ctor(span, src))
485
        } else {
486
0
            Ok(QuoteWat::Wat(parse_wat(parser)?))
487
        }
488
0
    }
489
}
490
491
/// Returned from [`QuoteWat::to_test`].
492
#[allow(missing_docs)]
493
#[derive(Debug)]
494
pub enum QuoteWatTest {
495
    Binary(Vec<u8>),
496
    Text(Vec<u8>),
497
}
498
499
#[derive(Debug)]
500
#[allow(missing_docs)]
501
#[non_exhaustive]
502
pub enum WastArg<'a> {
503
    Core(WastArgCore<'a>),
504
    #[cfg(feature = "component-model")]
505
    Component(WastVal<'a>),
506
}
507
508
impl<'a> Parse<'a> for WastArg<'a> {
509
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
510
0
        #[cfg(feature = "component-model")]
511
0
        if parser.peek::<WastArgCore<'_>>()? {
512
0
            Ok(WastArg::Core(parser.parse()?))
513
        } else {
514
0
            Ok(WastArg::Component(parser.parse()?))
515
        }
516
517
        #[cfg(not(feature = "component-model"))]
518
        Ok(WastArg::Core(parser.parse()?))
519
0
    }
520
}
521
522
#[derive(Debug)]
523
#[allow(missing_docs)]
524
#[non_exhaustive]
525
pub enum WastRet<'a> {
526
    Core(WastRetCore<'a>),
527
    #[cfg(feature = "component-model")]
528
    Component(WastVal<'a>),
529
}
530
531
impl<'a> Parse<'a> for WastRet<'a> {
532
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
533
0
        #[cfg(feature = "component-model")]
534
0
        if parser.peek::<WastRetCore<'_>>()? {
535
0
            Ok(WastRet::Core(parser.parse()?))
536
        } else {
537
0
            Ok(WastRet::Component(parser.parse()?))
538
        }
539
540
        #[cfg(not(feature = "component-model"))]
541
        Ok(WastRet::Core(parser.parse()?))
542
0
    }
543
}
544
545
#[derive(Debug)]
546
#[allow(missing_docs)]
547
pub 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
554
impl<'a> Parse<'a> for WastThread<'a> {
555
0
    fn parse(parser: Parser<'a>) -> Result<Self> {
556
0
        parser.depth_check()?;
557
0
        let span = parser.parse::<kw::thread>()?.0;
558
0
        let name = parser.parse()?;
559
560
0
        let shared_module = if parser.peek2::<kw::shared>()? {
561
0
            let name = parser.parens(|p| {
562
0
                p.parse::<kw::shared>()?;
563
0
                p.parens(|p| {
564
0
                    p.parse::<kw::module>()?;
565
0
                    p.parse()
566
0
                })
567
0
            })?;
568
0
            Some(name)
569
        } else {
570
0
            None
571
        };
572
0
        let mut directives = Vec::new();
573
0
        while !parser.is_empty() {
574
0
            directives.push(parser.parens(|p| p.parse())?);
575
        }
576
0
        Ok(WastThread {
577
0
            span,
578
0
            name,
579
0
            shared_module,
580
0
            directives,
581
0
        })
582
0
    }
583
}