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 | 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 | |
40 | struct WastDirectiveToken; |
41 | |
42 | impl 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)] |
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 | 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 | |
181 | impl<'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)] |
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 | 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 | |
292 | impl<'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 | |
312 | fn 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)] |
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 | 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 | |
353 | fn 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)] |
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 | 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 | |
467 | impl<'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)] |
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 | 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 ] |
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 | 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)] |
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 | 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 | |