| 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 | |