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}