1use anyhow::Result;
7use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
8use std::borrow::Cow;
9use std::fmt;
10use std::str::FromStr;
11use wast::core::NanPattern;
12
13mod build;
14
15#[derive(Serialize, Deserialize)]
17pub struct Wast<'a> {
18 #[serde(borrow)]
19 pub source_filename: Cow<'a, str>,
20 #[serde(borrow)]
21 pub commands: Vec<Command<'a>>,
22
23 #[serde(skip)]
27 pub wasms: Vec<(String, Vec<u8>)>,
28}
29
30impl<'a> Wast<'a> {
31 pub fn from_ast(
37 source_filename: &'a str,
38 source_contents: &'a str,
39 ast: wast::Wast<'a>,
40 ) -> Result<Wast<'a>> {
41 Opts::default().convert(source_filename, source_contents, ast)
42 }
43}
44
45#[derive(Default)]
46pub struct Opts {
47 dwarf: bool,
48}
49
50impl Opts {
51 pub fn dwarf(&mut self, enable: bool) -> &mut Self {
54 self.dwarf = enable;
55 self
56 }
57
58 pub fn convert<'a>(
60 &self,
61 source_filename: &'a str,
62 source_contents: &'a str,
63 ast: wast::Wast<'a>,
64 ) -> Result<Wast<'a>> {
65 build::run(self, source_filename, source_contents, ast)
66 }
67}
68
69#[derive(Serialize, Deserialize)]
70#[serde(tag = "type", rename_all = "snake_case")]
71pub enum Command<'a> {
72 Module {
73 line: u32,
74 #[serde(borrow, skip_serializing_if = "Option::is_none")]
75 name: Option<Cow<'a, str>>,
76 #[serde(borrow, flatten)]
77 file: WasmFile<'a>,
78 },
79 ModuleDefinition {
80 line: u32,
81 #[serde(borrow, skip_serializing_if = "Option::is_none")]
82 name: Option<Cow<'a, str>>,
83 #[serde(borrow, flatten)]
84 file: WasmFile<'a>,
85 },
86 ModuleInstance {
87 line: u32,
88 #[serde(borrow, skip_serializing_if = "Option::is_none")]
89 instance: Option<Cow<'a, str>>,
90 #[serde(borrow, skip_serializing_if = "Option::is_none")]
91 module: Option<Cow<'a, str>>,
92 },
93 AssertMalformed {
94 line: u32,
95 #[serde(borrow, flatten)]
96 file: WasmFile<'a>,
97 #[serde(borrow)]
98 text: Cow<'a, str>,
99 },
100 AssertInvalid {
101 line: u32,
102 #[serde(borrow, flatten)]
103 file: WasmFile<'a>,
104 #[serde(borrow)]
105 text: Cow<'a, str>,
106 },
107 Register {
108 line: u32,
109 #[serde(borrow, skip_serializing_if = "Option::is_none")]
110 name: Option<Cow<'a, str>>,
111 #[serde(borrow, rename = "as")]
112 as_: Cow<'a, str>,
113 },
114 AssertUnlinkable {
115 line: u32,
116 #[serde(borrow, flatten)]
117 file: WasmFile<'a>,
118 #[serde(borrow)]
119 text: Cow<'a, str>,
120 },
121 AssertReturn {
122 line: u32,
123 #[serde(borrow)]
124 action: Action<'a>,
125 #[serde(borrow)]
126 expected: Vec<Const<'a>>,
127 },
128 Action {
129 line: u32,
130 #[serde(borrow)]
131 action: Action<'a>,
132 },
133 AssertTrap {
134 line: u32,
135 #[serde(borrow)]
136 action: Action<'a>,
137 #[serde(borrow)]
138 text: Cow<'a, str>,
139 },
140 AssertExhaustion {
141 line: u32,
142 #[serde(borrow)]
143 action: Action<'a>,
144 #[serde(borrow)]
145 text: Cow<'a, str>,
146 },
147 AssertException {
148 line: u32,
149 #[serde(borrow)]
150 action: Action<'a>,
151 },
152 AssertSuspension {
153 line: u32,
154 #[serde(borrow)]
155 action: Action<'a>,
156 #[serde(borrow)]
157 text: Cow<'a, str>,
158 },
159 AssertUninstantiable {
160 line: u32,
161 #[serde(borrow, flatten)]
162 file: WasmFile<'a>,
163 #[serde(borrow)]
164 text: Cow<'a, str>,
165 },
166 Thread {
167 line: u32,
168 #[serde(borrow)]
169 name: Cow<'a, str>,
170 #[serde(borrow, skip_serializing_if = "Option::is_none")]
171 shared_module: Option<Cow<'a, str>>,
172 #[serde(borrow)]
173 commands: Vec<Command<'a>>,
174 },
175 Wait {
176 line: u32,
177 #[serde(borrow)]
178 thread: Cow<'a, str>,
179 },
180}
181
182#[derive(Serialize, Deserialize)]
183pub struct WasmFile<'a> {
184 #[serde(borrow)]
185 pub filename: Cow<'a, str>,
186 pub module_type: WasmFileType,
187 #[serde(borrow, skip_serializing_if = "Option::is_none")]
188 pub binary_filename: Option<Cow<'a, str>>,
189}
190
191impl Command<'_> {
192 pub fn line(&self) -> u32 {
193 use Command::*;
194
195 match self {
196 Module { line, .. }
197 | ModuleDefinition { line, .. }
198 | ModuleInstance { line, .. }
199 | AssertMalformed { line, .. }
200 | AssertInvalid { line, .. }
201 | Register { line, .. }
202 | AssertUnlinkable { line, .. }
203 | AssertUninstantiable { line, .. }
204 | AssertReturn { line, .. }
205 | Action { line, .. }
206 | AssertTrap { line, .. }
207 | AssertExhaustion { line, .. }
208 | AssertException { line, .. }
209 | AssertSuspension { line, .. }
210 | Thread { line, .. }
211 | Wait { line, .. } => *line,
212 }
213 }
214}
215
216#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Copy)]
217#[serde(rename_all = "snake_case")]
218pub enum WasmFileType {
219 Text,
220 Binary,
221}
222
223#[derive(Serialize, Deserialize)]
224#[serde(tag = "type", rename_all = "snake_case")]
225pub enum Action<'a> {
226 Invoke {
227 #[serde(borrow, skip_serializing_if = "Option::is_none")]
228 module: Option<Cow<'a, str>>,
229 #[serde(borrow)]
230 field: Cow<'a, str>,
231 #[serde(borrow)]
232 args: Vec<Const<'a>>,
233 },
234 Get {
235 #[serde(borrow, skip_serializing_if = "Option::is_none")]
236 module: Option<Cow<'a, str>>,
237 #[serde(borrow)]
238 field: Cow<'a, str>,
239 },
240}
241
242#[derive(Serialize, Deserialize, Debug)]
243#[serde(untagged)]
244pub enum Const<'a> {
245 Core(CoreConst),
246 #[serde(borrow)]
247 Component(ComponentConst<'a>),
248}
249
250#[derive(Serialize, Deserialize, Debug)]
251#[serde(tag = "type", rename_all = "lowercase")]
252pub enum CoreConst {
253 I32 {
254 value: IntString<i32>,
255 },
256 I64 {
257 value: IntString<i64>,
258 },
259 F32 {
260 value: FloatConst<f32>,
261 },
262 F64 {
263 value: FloatConst<f64>,
264 },
265 FuncRef {
266 #[serde(skip_serializing_if = "Option::is_none")]
267 value: Option<FuncRef>,
268 },
269 ExternRef {
270 #[serde(skip_serializing_if = "Option::is_none")]
271 value: Option<ExternRef>,
272 },
273 AnyRef {
274 #[serde(skip_serializing_if = "Option::is_none")]
275 value: Option<AnyRef>,
276 },
277 V128(V128),
278 Either {
279 values: Vec<CoreConst>,
280 },
281 EqRef,
282 ArrayRef,
283 StructRef,
284 I31Ref,
285 I31RefShared,
286 NullRef,
288 NullFuncRef,
290 NullExternRef,
292 NullExnRef,
294
295 ExnRef {
296 #[serde(skip_serializing_if = "Option::is_none")]
297 value: Option<ExnRef>,
298 },
299
300 ContRef {
301 #[serde(skip_serializing_if = "Option::is_none")]
302 value: Option<ContRef>,
303 },
304
305 NullContRef,
306
307 RefNull,
309}
310
311#[derive(Serialize, Deserialize, Debug)]
312#[serde(rename_all = "lowercase")]
313pub enum ExternRef {
314 Null,
315 #[serde(untagged)]
316 Host(IntString<u32>),
317}
318
319#[derive(Serialize, Deserialize, Debug)]
320#[serde(rename_all = "lowercase")]
321pub enum AnyRef {
322 Null,
323 #[serde(untagged)]
324 Host(IntString<u32>),
325}
326
327#[derive(Serialize, Deserialize, Debug)]
328#[serde(rename_all = "lowercase")]
329pub enum FuncRef {
330 Null,
331 Index(u32),
332}
333
334#[derive(Serialize, Deserialize, Debug)]
335#[serde(rename_all = "lowercase")]
336pub enum ExnRef {
337 Null,
338}
339
340#[derive(Serialize, Deserialize, Debug)]
341#[serde(rename_all = "lowercase")]
342pub enum ContRef {
343 Null,
344}
345
346#[derive(Serialize, Deserialize, Debug)]
347#[serde(tag = "lane_type", rename_all = "lowercase")]
348pub enum V128 {
349 I8 { value: [IntString<i8>; 16] },
350 I16 { value: [IntString<i16>; 8] },
351 I32 { value: [IntString<i32>; 4] },
352 I64 { value: [IntString<i64>; 2] },
353 F32 { value: [FloatConst<f32>; 4] },
354 F64 { value: [FloatConst<f64>; 2] },
355}
356
357impl V128 {
358 pub fn to_u128(&self) -> u128 {
360 let mut bytes = [0; 16];
361 match self {
362 V128::I8 { value } => {
363 let value = value.map(|i| i.0.to_le_bytes());
364 for (i, slot) in value.iter().flatten().zip(&mut bytes) {
365 *slot = *i;
366 }
367 }
368 V128::I16 { value } => {
369 let value = value.map(|i| i.0.to_le_bytes());
370 for (i, slot) in value.iter().flatten().zip(&mut bytes) {
371 *slot = *i;
372 }
373 }
374 V128::I32 { value } => {
375 let value = value.map(|i| i.0.to_le_bytes());
376 for (i, slot) in value.iter().flatten().zip(&mut bytes) {
377 *slot = *i;
378 }
379 }
380 V128::I64 { value } => {
381 let value = value.map(|i| i.0.to_le_bytes());
382 for (i, slot) in value.iter().flatten().zip(&mut bytes) {
383 *slot = *i;
384 }
385 }
386 V128::F32 { value } => {
387 let value = value.map(|i| i.to_bits().to_le_bytes());
388 for (i, slot) in value.iter().flatten().zip(&mut bytes) {
389 *slot = *i;
390 }
391 }
392 V128::F64 { value } => {
393 let value = value.map(|i| i.to_bits().to_le_bytes());
394 for (i, slot) in value.iter().flatten().zip(&mut bytes) {
395 *slot = *i;
396 }
397 }
398 }
399
400 u128::from_le_bytes(bytes)
401 }
402}
403
404#[derive(Copy, Clone)]
407pub struct IntString<T>(pub T);
408
409impl<'de, T: FromStr> Deserialize<'de> for IntString<T>
410where
411 T::Err: fmt::Display,
412{
413 fn deserialize<D>(d: D) -> Result<IntString<T>, D::Error>
414 where
415 D: Deserializer<'de>,
416 {
417 let s: Cow<'de, str> = Cow::deserialize(d)?;
418 Ok(IntString(s.parse().map_err(D::Error::custom)?))
419 }
420}
421
422impl<T: fmt::Display> Serialize for IntString<T> {
423 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
424 where
425 S: Serializer,
426 {
427 self.0.to_string().serialize(s)
428 }
429}
430
431impl<T: fmt::Debug> fmt::Debug for IntString<T> {
432 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433 self.0.fmt(f)
434 }
435}
436
437#[derive(Copy, Clone)]
439pub enum FloatConst<F> {
440 ArithmeticNan,
441 CanonicalNan,
442 Value(F),
443}
444
445impl<F: Float> FloatConst<F> {
446 pub fn to_bits(&self) -> F::Bits {
447 match self {
448 Self::ArithmeticNan => F::ARITHMETIC_NAN,
449 Self::CanonicalNan => F::CANONICAL_NAN,
450 Self::Value(f) => f.to_bits(),
451 }
452 }
453}
454
455impl<'de, F: Float> Deserialize<'de> for FloatConst<F>
456where
457 <F::Bits as FromStr>::Err: fmt::Display,
458{
459 fn deserialize<D>(d: D) -> Result<Self, D::Error>
460 where
461 D: Deserializer<'de>,
462 {
463 let s: Cow<'de, str> = Cow::deserialize(d)?;
464 let s: &str = &s;
465 Ok(match s {
466 "nan:arithmetic" => FloatConst::ArithmeticNan,
467 "nan:canonical" => FloatConst::CanonicalNan,
468 other => {
469 let bits = other.parse().map_err(D::Error::custom)?;
470 FloatConst::Value(F::from_bits(bits))
471 }
472 })
473 }
474}
475
476impl<F: Float> Serialize for FloatConst<F> {
477 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
478 where
479 S: Serializer,
480 {
481 match self {
482 FloatConst::ArithmeticNan => s.serialize_str("nan:arithmetic"),
483 FloatConst::CanonicalNan => s.serialize_str("nan:canonical"),
484 FloatConst::Value(other) => s.serialize_str(&other.to_bits().to_string()),
485 }
486 }
487}
488
489impl<F: fmt::Debug> fmt::Debug for FloatConst<F> {
490 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
491 match self {
492 FloatConst::ArithmeticNan => f.write_str("nan:arithmetic"),
493 FloatConst::CanonicalNan => f.write_str("nan:canonical"),
494 FloatConst::Value(other) => other.fmt(f),
495 }
496 }
497}
498
499pub trait Float: Sized {
500 type Bits: fmt::Display + FromStr;
501 const ARITHMETIC_NAN: Self::Bits;
502 const CANONICAL_NAN: Self::Bits;
503 fn from_bits(bits: Self::Bits) -> Self;
504 fn to_bits(&self) -> Self::Bits;
505}
506
507impl Float for f32 {
508 type Bits = u32;
509
510 const ARITHMETIC_NAN: u32 = 0x7f80_0000;
511 const CANONICAL_NAN: u32 = 0x7fc0_0000;
512
513 fn from_bits(bits: u32) -> Self {
514 f32::from_bits(bits)
515 }
516
517 fn to_bits(&self) -> u32 {
518 f32::to_bits(*self)
519 }
520}
521
522impl Float for f64 {
523 type Bits = u64;
524
525 const ARITHMETIC_NAN: u64 = 0x7ff0_0000_0000_0000;
526 const CANONICAL_NAN: u64 = 0x7ff8_0000_0000_0000;
527
528 fn from_bits(bits: u64) -> Self {
529 f64::from_bits(bits)
530 }
531
532 fn to_bits(&self) -> u64 {
533 f64::to_bits(*self)
534 }
535}
536
537impl From<wast::token::F32> for FloatConst<f32> {
538 fn from(wast: wast::token::F32) -> FloatConst<f32> {
539 FloatConst::Value(f32::from_bits(wast.bits))
540 }
541}
542
543impl From<wast::token::F64> for FloatConst<f64> {
544 fn from(wast: wast::token::F64) -> FloatConst<f64> {
545 FloatConst::Value(f64::from_bits(wast.bits))
546 }
547}
548
549impl<T, F> From<NanPattern<T>> for FloatConst<F>
550where
551 T: Into<FloatConst<F>>,
552{
553 fn from(wast: NanPattern<T>) -> FloatConst<F> {
554 match wast {
555 NanPattern::ArithmeticNan => FloatConst::ArithmeticNan,
556 NanPattern::CanonicalNan => FloatConst::CanonicalNan,
557 NanPattern::Value(v) => v.into(),
558 }
559 }
560}
561
562#[derive(Serialize, Deserialize, Debug)]
563#[serde(tag = "type", content = "value", rename_all = "lowercase")]
564pub enum ComponentConst<'a> {
565 #[serde(borrow)]
566 String(Cow<'a, str>),
567 Char(char),
568 Bool(bool),
569 U8(IntString<u8>),
570 S8(IntString<i8>),
571 U16(IntString<u16>),
572 S16(IntString<i16>),
573 U32(IntString<u32>),
574 S32(IntString<i32>),
575 U64(IntString<u64>),
576 S64(IntString<i64>),
577 F32(IntString<u32>),
578 F64(IntString<u64>),
579 #[serde(borrow)]
580 List(Vec<ComponentConst<'a>>),
581 #[serde(borrow)]
582 Record(Vec<(Cow<'a, str>, ComponentConst<'a>)>),
583 #[serde(borrow)]
584 Tuple(Vec<ComponentConst<'a>>),
585 Variant {
586 #[serde(borrow)]
587 case: Cow<'a, str>,
588 #[serde(borrow, skip_serializing_if = "Option::is_none")]
589 payload: Option<Box<ComponentConst<'a>>>,
590 },
591 #[serde(borrow)]
592 Enum(Cow<'a, str>),
593 #[serde(borrow)]
594 Option(Option<Box<ComponentConst<'a>>>),
595 #[serde(borrow)]
596 Result(Result<Option<Box<ComponentConst<'a>>>, Option<Box<ComponentConst<'a>>>>),
597 #[serde(borrow)]
598 Flags(Vec<Cow<'a, str>>),
599}