wasmparser/readers/core/
elements.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 crate::{
17    BinaryReader, BinaryReaderError, ConstExpr, ExternalKind, FromReader, RefType, Result,
18    SectionLimited,
19};
20use core::ops::Range;
21
22/// Represents a core WebAssembly element segment.
23#[derive(Clone)]
24pub 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)]
35pub 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)]
51pub 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.
59pub type ElementSectionReader<'a> = SectionLimited<'a, Element<'a>>;
60
61impl<'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}