Skip to main content

json_from_wast/
lib.rs

1//! Crate to transform [`wast::Wast`] into a [`Wast`] which can be serialize
2//! out to disk.
3//!
4//! Implementation of the `wasm-tools json-from-wast` subcommand.
5
6use 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/// Top level `*.wast` structure.
16#[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    /// This field is only present when created via `Wast::from_ast` and
24    /// contains the collection of wasm modules, and their names, that should
25    /// be emitted.
26    #[serde(skip)]
27    pub wasms: Vec<(String, Vec<u8>)>,
28}
29
30impl<'a> Wast<'a> {
31    /// Parses the [`wast::Wast`] into [`Wast`] of this crate, serializing all
32    /// modules for example to binary.
33    ///
34    /// The `source_filename` is used in the returned `Wast` object and
35    /// `source_contents` is the original file used for line numbers.
36    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    /// Whether or not DWARF debugging information is inserted into encoded
52    /// modules.
53    pub fn dwarf(&mut self, enable: bool) -> &mut Self {
54        self.dwarf = enable;
55        self
56    }
57
58    /// Converts `ast` to [`Wast`].
59    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    // (ref.null none)
287    NullRef,
288    // (ref.null nofunc)
289    NullFuncRef,
290    // (ref.null noextern)
291    NullExternRef,
292    // (ref.null noexn)
293    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    // any null reference, type doesn't matter
308    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    /// Returns the value of this constant as a 128-bit value.
359    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/// A small wrapper around `T` which implements serialization with
405/// `T::to_string` and `T::parse`.
406#[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/// A small wrapper around a float which serializes/deserializes from a string.
438#[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}