| 1 | use crate::{encode_section, ConstExpr, Encode, RefType, Section, SectionId}; |
| 2 | use alloc::borrow::Cow; |
| 3 | use alloc::vec::Vec; |
| 4 | |
| 5 | /// An encoder for the element section. |
| 6 | /// |
| 7 | /// Element sections are only supported for modules. |
| 8 | /// |
| 9 | /// # Example |
| 10 | /// |
| 11 | /// ``` |
| 12 | /// use std::borrow::Cow; |
| 13 | /// use wasm_encoder::{ |
| 14 | /// Elements, ElementSection, Module, TableSection, TableType, |
| 15 | /// RefType, ConstExpr |
| 16 | /// }; |
| 17 | /// |
| 18 | /// let mut tables = TableSection::new(); |
| 19 | /// tables.table(TableType { |
| 20 | /// element_type: RefType::FUNCREF, |
| 21 | /// minimum: 128, |
| 22 | /// maximum: None, |
| 23 | /// table64: false, |
| 24 | /// shared: false, |
| 25 | /// }); |
| 26 | /// |
| 27 | /// let mut elements = ElementSection::new(); |
| 28 | /// let table_index = 0; |
| 29 | /// let offset = ConstExpr::i32_const(42); |
| 30 | /// let functions = Elements::Functions(Cow::Borrowed(&[ |
| 31 | /// // Function indices... |
| 32 | /// ])); |
| 33 | /// elements.active(Some(table_index), &offset, functions); |
| 34 | /// |
| 35 | /// let mut module = Module::new(); |
| 36 | /// module |
| 37 | /// .section(&tables) |
| 38 | /// .section(&elements); |
| 39 | /// |
| 40 | /// let wasm_bytes = module.finish(); |
| 41 | /// ``` |
| 42 | #[derive (Clone, Default, Debug)] |
| 43 | pub struct ElementSection { |
| 44 | bytes: Vec<u8>, |
| 45 | num_added: u32, |
| 46 | } |
| 47 | |
| 48 | /// A sequence of elements in a segment in the element section. |
| 49 | #[derive (Clone, Debug)] |
| 50 | pub enum Elements<'a> { |
| 51 | /// A sequences of references to functions by their indices. |
| 52 | Functions(Cow<'a, [u32]>), |
| 53 | /// A sequence of reference expressions. |
| 54 | Expressions(RefType, Cow<'a, [ConstExpr]>), |
| 55 | } |
| 56 | |
| 57 | /// An element segment's mode. |
| 58 | #[derive (Clone, Debug)] |
| 59 | pub enum ElementMode<'a> { |
| 60 | /// A passive element segment. |
| 61 | /// |
| 62 | /// Passive segments are part of the bulk memory proposal. |
| 63 | Passive, |
| 64 | /// A declared element segment. |
| 65 | /// |
| 66 | /// Declared segments are part of the bulk memory proposal. |
| 67 | Declared, |
| 68 | /// An active element segment. |
| 69 | Active { |
| 70 | /// The table index. |
| 71 | /// |
| 72 | /// `Active` element specifying a `None` table forces the MVP encoding and refers to the |
| 73 | /// 0th table holding `funcref`s. Non-`None` tables use the encoding introduced with the |
| 74 | /// bulk memory proposal and can refer to tables with any valid reference type. |
| 75 | table: Option<u32>, |
| 76 | /// The offset within the table to place this segment. |
| 77 | offset: &'a ConstExpr, |
| 78 | }, |
| 79 | } |
| 80 | |
| 81 | /// An element segment in the element section. |
| 82 | #[derive (Clone, Debug)] |
| 83 | pub struct ElementSegment<'a> { |
| 84 | /// The element segment's mode. |
| 85 | pub mode: ElementMode<'a>, |
| 86 | /// This segment's elements. |
| 87 | pub elements: Elements<'a>, |
| 88 | } |
| 89 | |
| 90 | impl ElementSection { |
| 91 | /// Create a new element section encoder. |
| 92 | pub fn new() -> Self { |
| 93 | Self::default() |
| 94 | } |
| 95 | |
| 96 | /// The number of element segments in the section. |
| 97 | pub fn len(&self) -> u32 { |
| 98 | self.num_added |
| 99 | } |
| 100 | |
| 101 | /// Determines if the section is empty. |
| 102 | pub fn is_empty(&self) -> bool { |
| 103 | self.num_added == 0 |
| 104 | } |
| 105 | |
| 106 | /// Define an element segment. |
| 107 | pub fn segment<'a>(&mut self, segment: ElementSegment<'a>) -> &mut Self { |
| 108 | let expr_bit = match segment.elements { |
| 109 | Elements::Expressions(..) => 0b100u32, |
| 110 | Elements::Functions(_) => 0b000u32, |
| 111 | }; |
| 112 | let mut encode_type = false; |
| 113 | match &segment.mode { |
| 114 | ElementMode::Passive => { |
| 115 | (0x01 | expr_bit).encode(&mut self.bytes); |
| 116 | encode_type = true; |
| 117 | } |
| 118 | ElementMode::Active { table, offset } => { |
| 119 | match (table, &segment.elements) { |
| 120 | // If the `table` is not specified then the 0x00 encoding |
| 121 | // can be used with either function indices or expressions |
| 122 | // that have a `funcref` type. |
| 123 | (None, Elements::Functions(_) | Elements::Expressions(RefType::FUNCREF, _)) => { |
| 124 | (/* 0x00 | */expr_bit).encode(&mut self.bytes); |
| 125 | } |
| 126 | |
| 127 | // ... otherwise fall through for all other expressions here |
| 128 | // with table 0 or an explicitly specified table to the 0x02 |
| 129 | // encoding. |
| 130 | (None, Elements::Expressions(..)) | (Some(_), _) => { |
| 131 | (0x02 | expr_bit).encode(&mut self.bytes); |
| 132 | table.unwrap_or(0).encode(&mut self.bytes); |
| 133 | encode_type = true; |
| 134 | } |
| 135 | } |
| 136 | offset.encode(&mut self.bytes); |
| 137 | } |
| 138 | ElementMode::Declared => { |
| 139 | (0x03 | expr_bit).encode(&mut self.bytes); |
| 140 | encode_type = true; |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | match segment.elements { |
| 145 | Elements::Functions(fs) => { |
| 146 | if encode_type { |
| 147 | // elemkind == funcref |
| 148 | self.bytes.push(0x00); |
| 149 | } |
| 150 | fs.encode(&mut self.bytes); |
| 151 | } |
| 152 | Elements::Expressions(ty, e) => { |
| 153 | if encode_type { |
| 154 | ty.encode(&mut self.bytes); |
| 155 | } |
| 156 | e.len().encode(&mut self.bytes); |
| 157 | for expr in e.iter() { |
| 158 | expr.encode(&mut self.bytes); |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | self.num_added += 1; |
| 164 | self |
| 165 | } |
| 166 | |
| 167 | /// Define an active element segment. |
| 168 | /// |
| 169 | /// `Active` element specifying a `None` table forces the MVP encoding and refers to the 0th |
| 170 | /// table holding `funcref`s. Non-`None` tables use the encoding introduced with the bulk |
| 171 | /// memory proposal and can refer to tables with any valid reference type. |
| 172 | pub fn active( |
| 173 | &mut self, |
| 174 | table_index: Option<u32>, |
| 175 | offset: &ConstExpr, |
| 176 | elements: Elements<'_>, |
| 177 | ) -> &mut Self { |
| 178 | self.segment(ElementSegment { |
| 179 | mode: ElementMode::Active { |
| 180 | table: table_index, |
| 181 | offset, |
| 182 | }, |
| 183 | elements, |
| 184 | }) |
| 185 | } |
| 186 | |
| 187 | /// Encode a passive element segment. |
| 188 | /// |
| 189 | /// Passive segments are part of the bulk memory proposal. |
| 190 | pub fn passive<'a>(&mut self, elements: Elements<'a>) -> &mut Self { |
| 191 | self.segment(ElementSegment { |
| 192 | mode: ElementMode::Passive, |
| 193 | elements, |
| 194 | }) |
| 195 | } |
| 196 | |
| 197 | /// Encode a declared element segment. |
| 198 | /// |
| 199 | /// Declared segments are part of the bulk memory proposal. |
| 200 | pub fn declared<'a>(&mut self, elements: Elements<'a>) -> &mut Self { |
| 201 | self.segment(ElementSegment { |
| 202 | mode: ElementMode::Declared, |
| 203 | elements, |
| 204 | }) |
| 205 | } |
| 206 | |
| 207 | /// Copy a raw, already-encoded element segment into this elements section. |
| 208 | pub fn raw(&mut self, raw_bytes: &[u8]) -> &mut Self { |
| 209 | self.bytes.extend_from_slice(raw_bytes); |
| 210 | self.num_added += 1; |
| 211 | self |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | impl Encode for ElementSection { |
| 216 | fn encode(&self, sink: &mut Vec<u8>) { |
| 217 | encode_section(sink, self.num_added, &self.bytes); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | impl Section for ElementSection { |
| 222 | fn id(&self) -> u8 { |
| 223 | SectionId::Element.into() |
| 224 | } |
| 225 | } |
| 226 | |