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 std::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 type of the elements.
30    pub ty: RefType,
31    /// The range of the the element segment.
32    pub range: Range<usize>,
33}
34
35/// The kind of element segment.
36#[derive(Clone)]
37pub enum ElementKind<'a> {
38    /// The element segment is passive.
39    Passive,
40    /// The element segment is active.
41    Active {
42        /// The index of the table being initialized.
43        table_index: u32,
44        /// The initial expression of the element segment.
45        offset_expr: ConstExpr<'a>,
46    },
47    /// The element segment is declared.
48    Declared,
49}
50
51/// Represents the items of an element segment.
52#[derive(Clone)]
53pub enum ElementItems<'a> {
54    /// This element contains function indices.
55    Functions(SectionLimited<'a, u32>),
56    /// This element contains constant expressions used to initialize the table.
57    Expressions(SectionLimited<'a, ConstExpr<'a>>),
58}
59
60/// A reader for the element section of a WebAssembly module.
61pub type ElementSectionReader<'a> = SectionLimited<'a, Element<'a>>;
62
63impl<'a> FromReader<'a> for Element<'a> {
64    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
65        let elem_start = reader.original_position();
66        // The current handling of the flags is largely specified in the `bulk-memory` proposal,
67        // which at the time this commend is written has been merged to the main specification
68        // draft.
69        //
70        // Notably, this proposal allows multiple different encodings of the table index 0. `00`
71        // and `02 00` are both valid ways to specify the 0-th table. However it also makes
72        // another encoding of the 0-th memory `80 00` no longer valid.
73        //
74        // We, however maintain this support by parsing `flags` as a LEB128 integer. In that case,
75        // `80 00` encoding is parsed out as `0` and is therefore assigned a `tableidx` 0, even
76        // though the current specification draft does not allow for this.
77        //
78        // See also https://github.com/WebAssembly/spec/issues/1439
79        let flags = reader.read_var_u32()?;
80        if (flags & !0b111) != 0 {
81            return Err(BinaryReaderError::new(
82                "invalid flags byte in element segment",
83                reader.original_position() - 1,
84            ));
85        }
86        let kind = if flags & 0b001 != 0 {
87            if flags & 0b010 != 0 {
88                ElementKind::Declared
89            } else {
90                ElementKind::Passive
91            }
92        } else {
93            let table_index = if flags & 0b010 == 0 {
94                0
95            } else {
96                reader.read_var_u32()?
97            };
98            let offset_expr = reader.read()?;
99            ElementKind::Active {
100                table_index,
101                offset_expr,
102            }
103        };
104        let exprs = flags & 0b100 != 0;
105        let ty = if flags & 0b011 != 0 {
106            if exprs {
107                reader.read()?
108            } else {
109                match reader.read()? {
110                    ExternalKind::Func => RefType::FUNCREF,
111                    _ => {
112                        return Err(BinaryReaderError::new(
113                            "only the function external type is supported in elem segment",
114                            reader.original_position() - 1,
115                        ));
116                    }
117                }
118            }
119        } else {
120            RefType::FUNCREF
121        };
122        // FIXME(#188) ideally wouldn't have to do skips here
123        let data = reader.skip(|reader| {
124            let items_count = reader.read_var_u32()?;
125            if exprs {
126                for _ in 0..items_count {
127                    reader.skip_const_expr()?;
128                }
129            } else {
130                for _ in 0..items_count {
131                    reader.read_var_u32()?;
132                }
133            }
134            Ok(())
135        })?;
136        let items = if exprs {
137            ElementItems::Expressions(SectionLimited::new(
138                data.remaining_buffer(),
139                data.original_position(),
140            )?)
141        } else {
142            ElementItems::Functions(SectionLimited::new(
143                data.remaining_buffer(),
144                data.original_position(),
145            )?)
146        };
147
148        let elem_end = reader.original_position();
149        let range = elem_start..elem_end;
150
151        Ok(Element {
152            kind,
153            items,
154            ty,
155            range,
156        })
157    }
158}