wast/core/
memory.rs

1use crate::core::*;
2use crate::kw;
3use crate::parser::{Lookahead1, Parse, Parser, Peek, Result};
4use crate::token::*;
5
6/// A defined WebAssembly memory instance inside of a module.
7#[derive(Debug)]
8pub struct Memory<'a> {
9    /// Where this `memory` was defined
10    pub span: Span,
11    /// An optional name to refer to this memory by.
12    pub id: Option<Id<'a>>,
13    /// An optional name for this function stored in the custom `name` section.
14    pub name: Option<NameAnnotation<'a>>,
15    /// If present, inline export annotations which indicate names this
16    /// definition should be exported under.
17    pub exports: InlineExport<'a>,
18    /// How this memory is defined in the module.
19    pub kind: MemoryKind<'a>,
20}
21
22/// Different syntactical ways a memory can be defined in a module.
23#[derive(Debug)]
24pub enum MemoryKind<'a> {
25    /// This memory is actually an inlined import definition.
26    #[allow(missing_docs)]
27    Import {
28        import: InlineImport<'a>,
29        ty: MemoryType,
30    },
31
32    /// A typical memory definition which simply says the limits of the memory
33    Normal(MemoryType),
34
35    /// The data of this memory, starting from 0, explicitly listed
36    Inline {
37        /// Whether or not this will be creating a 64-bit memory
38        is64: bool,
39        /// The inline data specified for this memory
40        data: Vec<DataVal<'a>>,
41        /// Optional page size for this inline memory.
42        page_size_log2: Option<u32>,
43    },
44}
45
46impl<'a> Parse<'a> for Memory<'a> {
47    fn parse(parser: Parser<'a>) -> Result<Self> {
48        let span = parser.parse::<kw::memory>()?.0;
49        let id = parser.parse()?;
50        let name = parser.parse()?;
51        let exports = parser.parse()?;
52
53        // Afterwards figure out which style this is, either:
54        //
55        //  *   `(import "a" "b") limits`
56        //  *   `(data ...)`
57        //  *   `limits`
58        let mut l = parser.lookahead1();
59        let kind = if let Some(import) = parser.parse()? {
60            MemoryKind::Import {
61                import,
62                ty: parser.parse()?,
63            }
64        } else if l.peek::<LParen>()?
65            || ((parser.peek::<kw::i32>()? || parser.peek::<kw::i64>()?)
66                && parser.peek2::<LParen>()?)
67        {
68            let is64 = if parser.parse::<Option<kw::i32>>()?.is_some() {
69                false
70            } else {
71                parser.parse::<Option<kw::i64>>()?.is_some()
72            };
73            let page_size_log2 = page_size(parser)?;
74            let data = parser.parens(|parser| {
75                parser.parse::<kw::data>()?;
76                let mut data = Vec::new();
77                while !parser.is_empty() {
78                    data.push(parser.parse()?);
79                }
80                Ok(data)
81            })?;
82            MemoryKind::Inline {
83                data,
84                is64,
85                page_size_log2,
86            }
87        } else if l.peek::<u32>()? || l.peek::<kw::i32>()? || l.peek::<kw::i64>()? {
88            MemoryKind::Normal(parser.parse()?)
89        } else {
90            return Err(l.error());
91        };
92        Ok(Memory {
93            span,
94            id,
95            name,
96            exports,
97            kind,
98        })
99    }
100}
101
102/// A `data` directive in a WebAssembly module.
103#[derive(Debug)]
104pub struct Data<'a> {
105    /// Where this `data` was defined
106    pub span: Span,
107
108    /// The optional name of this data segment
109    pub id: Option<Id<'a>>,
110
111    /// An optional name for this data stored in the custom `name` section.
112    pub name: Option<NameAnnotation<'a>>,
113
114    /// Whether this data segment is passive or active
115    pub kind: DataKind<'a>,
116
117    /// Bytes for this `Data` segment, viewed as the concatenation of all the
118    /// contained slices.
119    pub data: Vec<DataVal<'a>>,
120}
121
122/// Different kinds of data segments, either passive or active.
123#[derive(Debug)]
124pub enum DataKind<'a> {
125    /// A passive data segment which isn't associated with a memory and is
126    /// referenced from various instructions.
127    Passive,
128
129    /// An active data segment which is associated and loaded into a particular
130    /// memory on module instantiation.
131    Active {
132        /// The memory that this `Data` will be associated with.
133        memory: Index<'a>,
134
135        /// Initial offset to load this data segment at
136        offset: Expression<'a>,
137    },
138}
139
140impl<'a> Parse<'a> for Data<'a> {
141    fn parse(parser: Parser<'a>) -> Result<Self> {
142        let span = parser.parse::<kw::data>()?.0;
143        let id = parser.parse()?;
144        let name = parser.parse()?;
145
146        let kind = if parser.peek::<&[u8]>()? || parser.peek::<RParen>()? {
147            DataKind::Passive
148
149        // ... and otherwise we must be attached to a particular memory as well
150        // as having an initialization offset.
151        } else {
152            let memory = if parser.peek::<u32>()? {
153                // FIXME: this is only here to accomodate
154                // proposals/threads/imports.wast at this current moment in
155                // time, this probably should get removed when the threads
156                // proposal is rebased on the current spec.
157                Index::Num(parser.parse()?, span)
158            } else if parser.peek2::<kw::memory>()? {
159                parser.parens(|p| {
160                    p.parse::<kw::memory>()?;
161                    p.parse()
162                })?
163            } else {
164                Index::Num(0, span)
165            };
166            let offset = parser.parens(|parser| {
167                if parser.peek::<kw::offset>()? {
168                    parser.parse::<kw::offset>()?;
169                    parser.parse()
170                } else {
171                    // This is all that the spec allows, which is that if
172                    // `offset` isn't present then this is "sugar" for a
173                    // single-instruction expression.
174                    let insn = parser.parse()?;
175                    if parser.is_empty() {
176                        return Ok(Expression::one(insn));
177                    }
178
179                    // This is support for what is currently invalid syntax
180                    // according to the strict specification but is otherwise
181                    // present in the spec test suite:
182                    //
183                    //    (data (i32.add (i32.const 0) (i32.const 0)))
184                    //
185                    // Technically the spec says this should be:
186                    //
187                    //    (data (offset ...))
188                    //
189                    // but alas
190                    let mut expr: Expression = parser.parse()?;
191                    let mut instrs = Vec::from(expr.instrs);
192                    instrs.push(insn);
193                    expr.instrs = instrs.into();
194                    Ok(expr)
195                }
196            })?;
197            DataKind::Active { memory, offset }
198        };
199
200        let mut data = Vec::new();
201        while !parser.is_empty() {
202            data.push(parser.parse()?);
203        }
204        Ok(Data {
205            span,
206            id,
207            name,
208            kind,
209            data,
210        })
211    }
212}
213
214/// Differnet ways the value of a data segment can be defined.
215#[derive(Debug)]
216#[allow(missing_docs)]
217pub enum DataVal<'a> {
218    String(&'a [u8]),
219    Integral(Vec<u8>),
220}
221
222impl DataVal<'_> {
223    /// Returns the length, in bytes, of the memory used to represent this data
224    /// value.
225    pub fn len(&self) -> usize {
226        match self {
227            DataVal::String(s) => s.len(),
228            DataVal::Integral(s) => s.len(),
229        }
230    }
231
232    /// Pushes the value of this data value onto the provided list of bytes.
233    pub fn push_onto(&self, dst: &mut Vec<u8>) {
234        match self {
235            DataVal::String(s) => dst.extend_from_slice(s),
236            DataVal::Integral(s) => dst.extend_from_slice(s),
237        }
238    }
239}
240
241impl<'a> Parse<'a> for DataVal<'a> {
242    fn parse(parser: Parser<'a>) -> Result<Self> {
243        if !parser.peek::<LParen>()? {
244            return Ok(DataVal::String(parser.parse()?));
245        }
246
247        return parser.parens(|p| {
248            let mut result = Vec::new();
249            let mut lookahead = p.lookahead1();
250            let l = &mut lookahead;
251            let r = &mut result;
252            if consume::<kw::i8, i8, _>(p, l, r, |u, v| v.push(u as u8))?
253                || consume::<kw::i16, i16, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
254                || consume::<kw::i32, i32, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
255                || consume::<kw::i64, i64, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
256                || consume::<kw::f32, F32, _>(p, l, r, |u, v| v.extend(&u.bits.to_le_bytes()))?
257                || consume::<kw::f64, F64, _>(p, l, r, |u, v| v.extend(&u.bits.to_le_bytes()))?
258                || consume::<kw::v128, V128Const, _>(p, l, r, |u, v| v.extend(&u.to_le_bytes()))?
259            {
260                Ok(DataVal::Integral(result))
261            } else {
262                Err(lookahead.error())
263            }
264        });
265
266        fn consume<'a, T: Peek + Parse<'a>, U: Parse<'a>, F>(
267            parser: Parser<'a>,
268            lookahead: &mut Lookahead1<'a>,
269            dst: &mut Vec<u8>,
270            push: F,
271        ) -> Result<bool>
272        where
273            F: Fn(U, &mut Vec<u8>),
274        {
275            if !lookahead.peek::<T>()? {
276                return Ok(false);
277            }
278            parser.parse::<T>()?;
279            while !parser.is_empty() {
280                let val = parser.parse::<U>()?;
281                push(val, dst);
282            }
283            Ok(true)
284        }
285    }
286}