Skip to main content

wasmparser/readers/core/
imports.rs

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
16use core::mem;
17
18use crate::{
19    BinaryReader, BinaryReaderError, ExternalKind, FromReader, GlobalType, MemoryType, Result,
20    SectionLimited, SectionLimitedIntoIterWithOffsets, TableType, TagType,
21};
22
23/// Represents a reference to a type definition in a WebAssembly module.
24#[derive(Debug, Clone, Copy, Eq, PartialEq)]
25pub enum TypeRef {
26    /// The type is a function.
27    Func(u32),
28    /// The type is a table.
29    Table(TableType),
30    /// The type is a memory.
31    Memory(MemoryType),
32    /// The type is a global.
33    Global(GlobalType),
34    /// The type is a tag.
35    ///
36    /// This variant is only used for the exception handling proposal.
37    ///
38    /// The value is an index in the types index space.
39    Tag(TagType),
40    /// The type is a function.
41    FuncExact(u32),
42}
43
44/// Represents a group of imports in a WebAssembly module.
45#[derive(Debug, Clone)]
46pub enum Imports<'a> {
47    /// The group contains a single import.
48    Single(usize, Import<'a>),
49    /// The group contains many imports that share the same module name, but have different types.
50    Compact1 {
51        /// The module being imported from.
52        module: &'a str,
53        /// The imported items.
54        items: SectionLimited<'a, ImportItemCompact<'a>>,
55    },
56    /// The group contains many imports that share the same module name and type.
57    Compact2 {
58        /// The module each item will be imported from.
59        module: &'a str,
60        /// The type of the imported items.
61        ty: TypeRef,
62        /// The imported item names.
63        names: SectionLimited<'a, &'a str>,
64    },
65}
66
67/// Represents an import in a WebAssembly module.
68#[derive(Debug, Copy, Clone, Eq, PartialEq)]
69pub struct Import<'a> {
70    /// The module being imported from.
71    pub module: &'a str,
72    /// The name of the imported item.
73    pub name: &'a str,
74    /// The type of the imported item.
75    pub ty: TypeRef,
76}
77
78/// A single compact import item.
79#[derive(Debug, Copy, Clone, Eq, PartialEq)]
80pub struct ImportItemCompact<'a> {
81    /// The name of the imported item.
82    pub name: &'a str,
83    /// The type of the imported item.
84    pub ty: TypeRef,
85}
86
87/// A reader for the import section of a WebAssembly module.
88pub type ImportSectionReader<'a> = SectionLimited<'a, Imports<'a>>;
89
90impl<'a> FromReader<'a> for Imports<'a> {
91    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
92        let start = reader.original_position();
93        let module = reader.read_string()?;
94        let single_item_name = reader.read_string()?;
95        let discriminator = reader.peek_bytes(1)?[0];
96        match (single_item_name, discriminator) {
97            ("", 0x7F) => {
98                if !reader.compact_imports() {
99                    bail!(
100                        reader.original_position(),
101                        "invalid leading byte 0x7F with compact imports \
102                         proposal disabled"
103                    );
104                }
105                // Compact encoding 1: one module name, many item names / types
106                reader.read_bytes(1)?;
107                // FIXME(#188) shouldn't need to skip here
108                let items = reader.skip(|reader| {
109                    let count = reader.read_var_u32()?;
110                    for _ in 0..count {
111                        reader.skip_string()?;
112                        reader.read::<TypeRef>()?;
113                    }
114                    Ok(())
115                })?;
116                Ok(Imports::Compact1 {
117                    module,
118                    items: SectionLimited::new(items)?,
119                })
120            }
121            ("", 0x7E) => {
122                if !reader.compact_imports() {
123                    bail!(
124                        reader.original_position(),
125                        "invalid leading byte 0x7E with compact imports \
126                         proposal disabled"
127                    );
128                }
129                // Compact encoding 2: one module name / type, many item names
130                reader.read_bytes(1)?;
131                let ty: TypeRef = reader.read()?;
132                // FIXME(#188) shouldn't need to skip here
133                let names = reader.skip(|reader| {
134                    let count = reader.read_var_u32()?;
135                    for _ in 0..count {
136                        reader.skip_string()?;
137                    }
138                    Ok(())
139                })?;
140                Ok(Imports::Compact2 {
141                    module,
142                    ty,
143                    names: SectionLimited::new(names)?,
144                })
145            }
146            _ => Ok(Imports::Single(
147                start,
148                Import {
149                    module: module,
150                    name: single_item_name,
151                    ty: reader.read()?,
152                },
153            )),
154        }
155    }
156}
157
158impl<'a> FromReader<'a> for Import<'a> {
159    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
160        Ok(Import {
161            module: reader.read()?,
162            name: reader.read()?,
163            ty: reader.read()?,
164        })
165    }
166}
167
168impl<'a> FromReader<'a> for ImportItemCompact<'a> {
169    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
170        Ok(ImportItemCompact {
171            name: reader.read()?,
172            ty: reader.read()?,
173        })
174    }
175}
176
177impl<'a> FromReader<'a> for TypeRef {
178    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
179        Ok(match reader.read()? {
180            ExternalKind::Func => TypeRef::Func(reader.read_var_u32()?),
181            ExternalKind::FuncExact => TypeRef::FuncExact(reader.read_var_u32()?),
182            ExternalKind::Table => TypeRef::Table(reader.read()?),
183            ExternalKind::Memory => TypeRef::Memory(reader.read()?),
184            ExternalKind::Global => TypeRef::Global(reader.read()?),
185            ExternalKind::Tag => TypeRef::Tag(reader.read()?),
186        })
187    }
188}
189
190// Iterator implementations to streamline usage of the Imports type in its
191// various possible encodings
192
193impl<'a> SectionLimited<'a, Imports<'a>> {
194    /// Converts the section into an iterator over individual [`Import`]s, flattening any groups
195    /// of compact imports.
196    pub fn into_imports(self) -> impl Iterator<Item = Result<Import<'a>>> {
197        self.into_imports_with_offsets()
198            .map(|res| res.map(|(_, import)| import))
199    }
200
201    /// Converts the section into an iterator over individual [`Import`]s and their offsets,
202    /// flattening any groups of compact imports.
203    pub fn into_imports_with_offsets(self) -> impl Iterator<Item = Result<(usize, Import<'a>)>> {
204        self.into_iter().flat_map(|res| match res {
205            Ok(imports) => imports.into_iter(),
206            Err(e) => ImportsIter {
207                state: ImportsIterState::Error(e),
208            },
209        })
210    }
211}
212
213impl<'a> IntoIterator for Imports<'a> {
214    type Item = Result<(usize, Import<'a>)>;
215    type IntoIter = ImportsIter<'a>;
216
217    fn into_iter(self) -> Self::IntoIter {
218        ImportsIter {
219            state: match self {
220                Imports::Single(start, import) => ImportsIterState::Single(start, import),
221                Imports::Compact1 { module, items } => ImportsIterState::Compact1 {
222                    module: module,
223                    iter: items.into_iter_with_offsets(),
224                },
225                Imports::Compact2 {
226                    module,
227                    ty,
228                    names: items,
229                } => ImportsIterState::Compact2 {
230                    module: module,
231                    ty: ty,
232                    iter: items.into_iter_with_offsets(),
233                },
234            },
235        }
236    }
237}
238
239/// An iterator over the [`Import`]s in a single [`Imports`] object.
240pub struct ImportsIter<'a> {
241    state: ImportsIterState<'a>,
242}
243
244enum ImportsIterState<'a> {
245    Done,
246    Error(BinaryReaderError),
247    Single(usize, Import<'a>),
248    Compact1 {
249        module: &'a str,
250        iter: SectionLimitedIntoIterWithOffsets<'a, ImportItemCompact<'a>>,
251    },
252    Compact2 {
253        module: &'a str,
254        ty: TypeRef,
255        iter: SectionLimitedIntoIterWithOffsets<'a, &'a str>,
256    },
257}
258
259impl<'a> Iterator for ImportsIter<'a> {
260    type Item = Result<(usize, Import<'a>)>;
261
262    fn next(&mut self) -> Option<Self::Item> {
263        match &mut self.state {
264            ImportsIterState::Done => None,
265            ImportsIterState::Error(_) => {
266                let ImportsIterState::Error(e) =
267                    mem::replace(&mut self.state, ImportsIterState::Done)
268                else {
269                    unreachable!()
270                };
271                Some(Err(e))
272            }
273
274            ImportsIterState::Single(offset, i) => {
275                let ret = Some(Ok((*offset, *i)));
276                self.state = ImportsIterState::Done;
277                ret
278            }
279            ImportsIterState::Compact1 { module, iter } => {
280                let item = iter.next()?;
281                Some(item.map(|(offset, item)| {
282                    (
283                        offset,
284                        Import {
285                            module,
286                            name: item.name,
287                            ty: item.ty,
288                        },
289                    )
290                }))
291            }
292            ImportsIterState::Compact2 { module, ty, iter } => {
293                let item = iter.next()?;
294                Some(item.map(|(offset, name)| {
295                    (
296                        offset,
297                        Import {
298                            module,
299                            name,
300                            ty: *ty,
301                        },
302                    )
303                }))
304            }
305        }
306    }
307}