| 1 | /* Copyright 2018 Mozilla Foundation |
| 2 | * |
| 3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | * you may not use this file except in compliance with the License. |
| 5 | * You may obtain a copy of the License at |
| 6 | * |
| 7 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | * |
| 9 | * Unless required by applicable law or agreed to in writing, software |
| 10 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | * See the License for the specific language governing permissions and |
| 13 | * limitations under the License. |
| 14 | */ |
| 15 | |
| 16 | use crate::{ |
| 17 | BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, RefType, Result, |
| 18 | SectionLimited, |
| 19 | }; |
| 20 | use core::ops::Range; |
| 21 | |
| 22 | /// Represents a core WebAssembly element segment. |
| 23 | #[derive (Clone)] |
| 24 | pub struct Element<'a> { |
| 25 | /// The kind of the element segment. |
| 26 | pub kind: ElementKind<'a>, |
| 27 | /// The initial elements of the element segment. |
| 28 | pub items: ElementItems<'a>, |
| 29 | /// The range of the the element segment. |
| 30 | pub range: Range<usize>, |
| 31 | } |
| 32 | |
| 33 | /// The kind of element segment. |
| 34 | #[derive (Clone)] |
| 35 | pub enum ElementKind<'a> { |
| 36 | /// The element segment is passive. |
| 37 | Passive, |
| 38 | /// The element segment is active. |
| 39 | Active { |
| 40 | /// The index of the table being initialized. |
| 41 | table_index: Option<u32>, |
| 42 | /// The initial expression of the element segment. |
| 43 | offset_expr: ConstExpr<'a>, |
| 44 | }, |
| 45 | /// The element segment is declared. |
| 46 | Declared, |
| 47 | } |
| 48 | |
| 49 | /// Represents the items of an element segment. |
| 50 | #[derive (Clone)] |
| 51 | pub enum ElementItems<'a> { |
| 52 | /// This element contains function indices. |
| 53 | Functions(SectionLimited<'a, u32>), |
| 54 | /// This element contains constant expressions used to initialize the table. |
| 55 | Expressions(RefType, SectionLimited<'a, ConstExpr<'a>>), |
| 56 | } |
| 57 | |
| 58 | /// A reader for the element section of a WebAssembly module. |
| 59 | pub type ElementSectionReader<'a> = SectionLimited<'a, Element<'a>>; |
| 60 | |
| 61 | impl<'a> FromReader<'a> for Element<'a> { |
| 62 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
| 63 | let elem_start = reader.original_position(); |
| 64 | // The current handling of the flags is largely specified in the `bulk-memory` proposal, |
| 65 | // which at the time this commend is written has been merged to the main specification |
| 66 | // draft. |
| 67 | // |
| 68 | // Notably, this proposal allows multiple different encodings of the table index 0. `00` |
| 69 | // and `02 00` are both valid ways to specify the 0-th table. However it also makes |
| 70 | // another encoding of the 0-th memory `80 00` no longer valid. |
| 71 | // |
| 72 | // We, however maintain this support by parsing `flags` as a LEB128 integer. In that case, |
| 73 | // `80 00` encoding is parsed out as `0` and is therefore assigned a `tableidx` 0, even |
| 74 | // though the current specification draft does not allow for this. |
| 75 | // |
| 76 | // See also https://github.com/WebAssembly/spec/issues/1439 |
| 77 | let flags = reader.read_var_u32()?; |
| 78 | if (flags & !0b111) != 0 { |
| 79 | return Err(BinaryReaderError::new( |
| 80 | "invalid flags byte in element segment" , |
| 81 | reader.original_position() - 1, |
| 82 | )); |
| 83 | } |
| 84 | let kind = if flags & 0b001 != 0 { |
| 85 | if flags & 0b010 != 0 { |
| 86 | ElementKind::Declared |
| 87 | } else { |
| 88 | ElementKind::Passive |
| 89 | } |
| 90 | } else { |
| 91 | let table_index = if flags & 0b010 == 0 { |
| 92 | None |
| 93 | } else { |
| 94 | Some(reader.read_var_u32()?) |
| 95 | }; |
| 96 | let offset_expr = reader.read()?; |
| 97 | ElementKind::Active { |
| 98 | table_index, |
| 99 | offset_expr, |
| 100 | } |
| 101 | }; |
| 102 | let exprs = flags & 0b100 != 0; |
| 103 | let ty = if flags & 0b011 != 0 { |
| 104 | if exprs { |
| 105 | Some(reader.read()?) |
| 106 | } else { |
| 107 | match reader.read()? { |
| 108 | ExternalKind::Func => None, |
| 109 | _ => { |
| 110 | return Err(BinaryReaderError::new( |
| 111 | "only the function external type is supported in elem segment" , |
| 112 | reader.original_position() - 1, |
| 113 | )); |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | } else { |
| 118 | None |
| 119 | }; |
| 120 | // FIXME(#188) ideally wouldn't have to do skips here |
| 121 | let data = reader.skip(|reader| { |
| 122 | let items_count = reader.read_var_u32()?; |
| 123 | if exprs { |
| 124 | for _ in 0..items_count { |
| 125 | reader.skip_const_expr()?; |
| 126 | } |
| 127 | } else { |
| 128 | for _ in 0..items_count { |
| 129 | reader.read_var_u32()?; |
| 130 | } |
| 131 | } |
| 132 | Ok(()) |
| 133 | })?; |
| 134 | let items = if exprs { |
| 135 | ElementItems::Expressions(ty.unwrap_or(RefType::FUNCREF), SectionLimited::new(data)?) |
| 136 | } else { |
| 137 | assert!(ty.is_none()); |
| 138 | ElementItems::Functions(SectionLimited::new(data)?) |
| 139 | }; |
| 140 | |
| 141 | let elem_end = reader.original_position(); |
| 142 | let range = elem_start..elem_end; |
| 143 | |
| 144 | Ok(Element { kind, items, range }) |
| 145 | } |
| 146 | } |
| 147 | |