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}