1use crate::core::*;
2use crate::kw;
3use crate::parser::{Parse, Parser, Peek, Result};
4use crate::token::{Id, Index, LParen, NameAnnotation, Span};
5
6/// A WebAssembly `table` directive in a module.
7#[derive(Debug)]
8pub struct Table<'a> {
9 /// Where this table was defined.
10 pub span: Span,
11 /// An optional name to refer to this table 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 table is textually defined in the module.
19 pub kind: TableKind<'a>,
20}
21
22/// Different ways to textually define a table.
23#[derive(Debug)]
24pub enum TableKind<'a> {
25 /// This table is actually an inlined import definition.
26 #[allow(missing_docs)]
27 Import {
28 import: InlineImport<'a>,
29 ty: TableType<'a>,
30 },
31
32 /// A typical memory definition which simply says the limits of the table.
33 Normal {
34 /// Table type.
35 ty: TableType<'a>,
36 /// Optional items initializer expression.
37 init_expr: Option<Expression<'a>>,
38 },
39
40 /// The elem segments of this table, starting from 0, explicitly listed.
41 Inline {
42 /// The element type of this table.
43 elem: RefType<'a>,
44 /// Whether or not this will be creating a 64-bit table.
45 is64: bool,
46 /// Whether this table is shared or not.
47 shared: bool,
48 /// The element table entries to have, and the length of this list is
49 /// the limits of the table as well.
50 payload: ElemPayload<'a>,
51 },
52}
53
54impl<'a> Parse<'a> for Table<'a> {
55 fn parse(parser: Parser<'a>) -> Result<Self> {
56 let span = parser.parse::<kw::table>()?.0;
57 let id = parser.parse()?;
58 let name = parser.parse()?;
59 let exports = parser.parse()?;
60
61 // Afterwards figure out which style this is, either:
62 //
63 // * inline: `elemtype (elem ...)`
64 // * normal: `tabletype`
65 // * import: `(import "a" "b") tabletype`
66 //
67 // Where `tabletype := shared? index_type limits reftype`
68 let mut l = parser.lookahead1();
69
70 let is_shared = l.peek::<kw::shared>()?;
71 let has_index_type = l.peek::<kw::i32>()? | l.peek::<kw::i64>()?;
72 let kind = if l.peek::<RefType>()?
73 || ((is_shared || has_index_type) && parser.peek2::<RefType>()?)
74 {
75 let shared = parser.parse::<Option<kw::shared>>()?.is_some();
76 let is64 = if parser.parse::<Option<kw::i32>>()?.is_some() {
77 false
78 } else {
79 parser.parse::<Option<kw::i64>>()?.is_some()
80 };
81 let elem = parser.parse()?;
82 let payload = parser.parens(|p| {
83 p.parse::<kw::elem>()?;
84 if p.peek::<LParen>()? {
85 ElemPayload::parse_exprs(p, elem)
86 } else {
87 ElemPayload::parse_indices(p, Some(elem))
88 }
89 })?;
90 TableKind::Inline {
91 elem,
92 is64,
93 shared,
94 payload,
95 }
96 } else if is_shared || has_index_type || l.peek::<u64>()? {
97 TableKind::Normal {
98 ty: parser.parse()?,
99 init_expr: if !parser.is_empty() {
100 Some(parser.parse::<Expression>()?)
101 } else {
102 None
103 },
104 }
105 } else if let Some(import) = parser.parse()? {
106 TableKind::Import {
107 import,
108 ty: parser.parse()?,
109 }
110 } else {
111 return Err(l.error());
112 };
113 Ok(Table {
114 span,
115 id,
116 name,
117 exports,
118 kind,
119 })
120 }
121}
122
123/// An `elem` segment in a WebAssembly module.
124#[derive(Debug)]
125pub struct Elem<'a> {
126 /// Where this `elem` was defined.
127 pub span: Span,
128 /// An optional name by which to refer to this segment.
129 pub id: Option<Id<'a>>,
130 /// An optional name for this element stored in the custom `name` section.
131 pub name: Option<NameAnnotation<'a>>,
132 /// The way this segment was defined in the module.
133 pub kind: ElemKind<'a>,
134 /// The payload of this element segment, typically a list of functions.
135 pub payload: ElemPayload<'a>,
136}
137
138/// Different ways to define an element segment in an mdoule.
139#[derive(Debug)]
140pub enum ElemKind<'a> {
141 /// A passive segment that isn't associated with a table and can be used in
142 /// various bulk-memory instructions.
143 Passive,
144
145 /// A declared element segment that is purely used to declare function
146 /// references.
147 Declared,
148
149 /// An active segment associated with a table.
150 Active {
151 /// The table this `elem` is initializing.
152 table: Option<Index<'a>>,
153 /// The offset within `table` that we'll initialize at.
154 offset: Expression<'a>,
155 },
156}
157
158/// Different ways to define the element segment payload in a module.
159#[derive(Debug)]
160pub enum ElemPayload<'a> {
161 /// This element segment has a contiguous list of function indices
162 Indices(Vec<Index<'a>>),
163
164 /// This element segment has a list of optional function indices,
165 /// represented as expressions using `ref.func` and `ref.null`.
166 Exprs {
167 /// The desired type of each expression below.
168 ty: RefType<'a>,
169 /// The expressions in this segment.
170 exprs: Vec<Expression<'a>>,
171 },
172}
173
174impl<'a> Parse<'a> for Elem<'a> {
175 fn parse(parser: Parser<'a>) -> Result<Self> {
176 let span = parser.parse::<kw::elem>()?.0;
177 let id = parser.parse()?;
178 let name = parser.parse()?;
179
180 // Element segments can start in a number of different ways:
181 //
182 // * `(elem ...`
183 // * `(elem declare ...`
184 // * `(elem (table ...`
185 // * `(elem (offset ...`
186 // * `(elem (<instr> ...` (omitted `offset`)
187 let mut table_omitted = false;
188 let kind = if parser.peek::<kw::declare>()? {
189 parser.parse::<kw::declare>()?;
190 ElemKind::Declared
191 } else if parser.peek::<u32>()?
192 || (parser.peek::<LParen>()? && !parser.peek::<RefType>()?)
193 {
194 let table = if parser.peek::<u32>()? {
195 // FIXME: this is only here to accomodate
196 // proposals/threads/imports.wast at this current moment in
197 // time, this probably should get removed when the threads
198 // proposal is rebased on the current spec.
199 table_omitted = true;
200 Some(Index::Num(parser.parse()?, span))
201 } else if parser.peek2::<kw::table>()? {
202 Some(parser.parens(|p| {
203 p.parse::<kw::table>()?;
204 p.parse()
205 })?)
206 } else {
207 table_omitted = true;
208 None
209 };
210
211 let offset = parse_expr_or_single_instr::<kw::offset>(parser)?;
212 ElemKind::Active { table, offset }
213 } else {
214 ElemKind::Passive
215 };
216
217 // Element segments can have a number of ways to specify their element
218 // lists:
219 //
220 // * `func 0 1 ...` - list of indices
221 // * `<reftype> (ref.null func) ...` - list of expressions
222 // * `0 1 ...` - list of indices, only if the table was omitted for the
223 // legacy way tables were printed.
224 let indices = if parser.peek::<kw::func>()? {
225 parser.parse::<kw::func>()?;
226 true
227 } else if parser.peek::<RefType>()? {
228 false
229 } else if table_omitted {
230 true
231 } else {
232 false // this will fall through to failing to parse a `RefType`
233 };
234 let payload = if indices {
235 ElemPayload::parse_indices(parser, None)?
236 } else {
237 let ty = parser.parse()?;
238 ElemPayload::parse_exprs(parser, ty)?
239 };
240
241 Ok(Elem {
242 span,
243 id,
244 name,
245 kind,
246 payload,
247 })
248 }
249}
250
251impl<'a> ElemPayload<'a> {
252 fn parse_indices(parser: Parser<'a>, ty: Option<RefType<'a>>) -> Result<Self> {
253 let mut ret = match ty {
254 // If there is no requested type, then it's ok to parse a list of
255 // indices.
256 None => ElemPayload::Indices(Vec::new()),
257
258 // If the requested type is a `funcref` type then a list of indices
259 // can be parsed. This is because the list-of-indices encoding in
260 // the binary format can only accomodate the `funcref` type.
261 Some(ty) if ty == RefType::func() => ElemPayload::Indices(Vec::new()),
262
263 // Otherwise silently translate this list-of-indices into a
264 // list-of-expressions because that's the only way to accomodate a
265 // non-funcref type.
266 Some(ty) => ElemPayload::Exprs {
267 ty,
268 exprs: Vec::new(),
269 },
270 };
271 while !parser.is_empty() {
272 let func = parser.parse()?;
273 match &mut ret {
274 ElemPayload::Indices(list) => list.push(func),
275 ElemPayload::Exprs { exprs, .. } => {
276 let expr = Expression::one(Instruction::RefFunc(func));
277 exprs.push(expr);
278 }
279 }
280 }
281 Ok(ret)
282 }
283
284 fn parse_exprs(parser: Parser<'a>, ty: RefType<'a>) -> Result<Self> {
285 let mut exprs = Vec::new();
286 while !parser.is_empty() {
287 let expr = parse_expr_or_single_instr::<kw::item>(parser)?;
288 exprs.push(expr);
289 }
290 Ok(ElemPayload::Exprs { exprs, ty })
291 }
292}
293
294// Parses either `(T expr)` or `(instr)`, returning the resulting expression.
295fn parse_expr_or_single_instr<'a, T>(parser: Parser<'a>) -> Result<Expression<'a>>
296where
297 T: Parse<'a> + Peek,
298{
299 if parser.peek2::<T>()? {
300 parser.parens(|parser: Parser<'a>| {
301 parser.parse::<T>()?;
302 parser.parse()
303 })
304 } else {
305 // Without `T` this is "sugar" for a single instruction (still possibly folded).
306 Ok(Expression::parse_folded_instruction(parser)?)
307 }
308}
309