1 | use crate::core::*; |
2 | use crate::kw; |
3 | use crate::parser::{Parse, Parser, Peek, Result}; |
4 | use crate::token::{Id, Index, LParen, NameAnnotation, Span}; |
5 | |
6 | /// A WebAssembly `table` directive in a module. |
7 | #[derive (Debug)] |
8 | pub 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)] |
24 | pub 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 | |
54 | impl<'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)] |
125 | pub 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)] |
140 | pub 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)] |
160 | pub 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 | |
174 | impl<'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 | |
251 | impl<'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. |
295 | fn parse_expr_or_single_instr<'a, T>(parser: Parser<'a>) -> Result<Expression<'a>> |
296 | where |
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 | |