1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #if !V8_ENABLE_WEBASSEMBLY
6 #error This header should only be included if WebAssembly is enabled.
7 #endif // !V8_ENABLE_WEBASSEMBLY
8
9 #ifndef V8_WASM_MODULE_DECODER_H_
10 #define V8_WASM_MODULE_DECODER_H_
11
12 #include <memory>
13
14 #include "src/common/globals.h"
15 #include "src/logging/metrics.h"
16 #include "src/wasm/function-body-decoder.h"
17 #include "src/wasm/wasm-constants.h"
18 #include "src/wasm/wasm-features.h"
19 #include "src/wasm/wasm-module.h"
20 #include "src/wasm/wasm-result.h"
21
22 namespace v8 {
23 namespace internal {
24
25 class Counters;
26
27 namespace wasm {
28
29 struct CompilationEnv;
30
IsValidSectionCode(uint8_t byte)31 inline bool IsValidSectionCode(uint8_t byte) {
32 return kTypeSectionCode <= byte && byte <= kLastKnownModuleSection;
33 }
34
35 const char* SectionName(SectionCode code);
36
37 using ModuleResult = Result<std::shared_ptr<WasmModule>>;
38 using FunctionResult = Result<std::unique_ptr<WasmFunction>>;
39 using FunctionOffsets = std::vector<std::pair<int, int>>;
40 using FunctionOffsetsResult = Result<FunctionOffsets>;
41
42 struct AsmJsOffsetEntry {
43 int byte_offset;
44 int source_position_call;
45 int source_position_number_conversion;
46 };
47 struct AsmJsOffsetFunctionEntries {
48 int start_offset;
49 int end_offset;
50 std::vector<AsmJsOffsetEntry> entries;
51 };
52 struct AsmJsOffsets {
53 std::vector<AsmJsOffsetFunctionEntries> functions;
54 };
55 using AsmJsOffsetsResult = Result<AsmJsOffsets>;
56
57 // The class names "NameAssoc", "NameMap", and "IndirectNameMap" match
58 // the terms used by the spec:
59 // https://webassembly.github.io/spec/core/bikeshed/index.html#name-section%E2%91%A0
60 class NameAssoc {
61 public:
NameAssoc(int index,WireBytesRef name)62 NameAssoc(int index, WireBytesRef name) : index_(index), name_(name) {}
63
index()64 int index() const { return index_; }
name()65 WireBytesRef name() const { return name_; }
66
67 struct IndexLess {
operatorIndexLess68 bool operator()(const NameAssoc& a, const NameAssoc& b) const {
69 return a.index() < b.index();
70 }
71 };
72
73 private:
74 int index_;
75 WireBytesRef name_;
76 };
77
78 class NameMap {
79 public:
80 // For performance reasons, {NameMap} should not be copied.
81 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(NameMap);
82
NameMap(std::vector<NameAssoc> names)83 explicit NameMap(std::vector<NameAssoc> names) : names_(std::move(names)) {
84 DCHECK(
85 std::is_sorted(names_.begin(), names_.end(), NameAssoc::IndexLess{}));
86 }
87
GetName(int index)88 WireBytesRef GetName(int index) {
89 auto it = std::lower_bound(names_.begin(), names_.end(),
90 NameAssoc{index, {}}, NameAssoc::IndexLess{});
91 if (it == names_.end()) return {};
92 if (it->index() != index) return {};
93 return it->name();
94 }
95
96 private:
97 std::vector<NameAssoc> names_;
98 };
99
100 class IndirectNameMapEntry : public NameMap {
101 public:
102 // For performance reasons, {IndirectNameMapEntry} should not be copied.
103 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(IndirectNameMapEntry);
104
IndirectNameMapEntry(int index,std::vector<NameAssoc> names)105 IndirectNameMapEntry(int index, std::vector<NameAssoc> names)
106 : NameMap(std::move(names)), index_(index) {}
107
index()108 int index() const { return index_; }
109
110 struct IndexLess {
operatorIndexLess111 bool operator()(const IndirectNameMapEntry& a,
112 const IndirectNameMapEntry& b) const {
113 return a.index() < b.index();
114 }
115 };
116
117 private:
118 int index_;
119 };
120
121 class IndirectNameMap {
122 public:
123 // For performance reasons, {IndirectNameMap} should not be copied.
124 MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(IndirectNameMap);
125
IndirectNameMap(std::vector<IndirectNameMapEntry> functions)126 explicit IndirectNameMap(std::vector<IndirectNameMapEntry> functions)
127 : functions_(std::move(functions)) {
128 DCHECK(std::is_sorted(functions_.begin(), functions_.end(),
129 IndirectNameMapEntry::IndexLess{}));
130 }
131
GetName(int function_index,int local_index)132 WireBytesRef GetName(int function_index, int local_index) {
133 auto it = std::lower_bound(functions_.begin(), functions_.end(),
134 IndirectNameMapEntry{function_index, {}},
135 IndirectNameMapEntry::IndexLess{});
136 if (it == functions_.end()) return {};
137 if (it->index() != function_index) return {};
138 return it->GetName(local_index);
139 }
140
141 private:
142 std::vector<IndirectNameMapEntry> functions_;
143 };
144
145 enum class DecodingMethod {
146 kSync,
147 kAsync,
148 kSyncStream,
149 kAsyncStream,
150 kDeserialize
151 };
152
153 // Decodes the bytes of a wasm module between {module_start} and {module_end}.
154 V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule(
155 const WasmFeatures& enabled, const byte* module_start,
156 const byte* module_end, bool verify_functions, ModuleOrigin origin,
157 Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder,
158 v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method,
159 AccountingAllocator* allocator);
160
161 // Exposed for testing. Decodes a single function signature, allocating it
162 // in the given zone. Returns {nullptr} upon failure.
163 V8_EXPORT_PRIVATE const FunctionSig* DecodeWasmSignatureForTesting(
164 const WasmFeatures& enabled, Zone* zone, const byte* start,
165 const byte* end);
166
167 // Decodes the bytes of a wasm function between
168 // {function_start} and {function_end}.
169 V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunctionForTesting(
170 const WasmFeatures& enabled, Zone* zone, const ModuleWireBytes& wire_bytes,
171 const WasmModule* module, const byte* function_start,
172 const byte* function_end, Counters* counters);
173
174 V8_EXPORT_PRIVATE ConstantExpression
175 DecodeWasmInitExprForTesting(const WasmFeatures& enabled, const byte* start,
176 const byte* end, ValueType expected);
177
178 struct CustomSectionOffset {
179 WireBytesRef section;
180 WireBytesRef name;
181 WireBytesRef payload;
182 };
183
184 V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections(
185 const byte* start, const byte* end);
186
187 // Extracts the mapping from wasm byte offset to asm.js source position per
188 // function.
189 AsmJsOffsetsResult DecodeAsmJsOffsets(
190 base::Vector<const uint8_t> encoded_offsets);
191
192 // Decode the function names from the name section. Returns the result as an
193 // unordered map. Only names with valid utf8 encoding are stored and conflicts
194 // are resolved by choosing the last name read.
195 void DecodeFunctionNames(const byte* module_start, const byte* module_end,
196 std::unordered_map<uint32_t, WireBytesRef>* names);
197
198 // Decode the requested subsection of the name section.
199 // The result will be empty if no name section is present. On encountering an
200 // error in the name section, returns all information decoded up to the first
201 // error.
202 NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes,
203 uint8_t name_section_kind);
204 IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes,
205 uint8_t name_section_kind);
206
207 class ModuleDecoderImpl;
208
209 class ModuleDecoder {
210 public:
211 explicit ModuleDecoder(const WasmFeatures& enabled);
212 ~ModuleDecoder();
213
214 void StartDecoding(Counters* counters,
215 std::shared_ptr<metrics::Recorder> metrics_recorder,
216 v8::metrics::Recorder::ContextId context_id,
217 AccountingAllocator* allocator,
218 ModuleOrigin origin = ModuleOrigin::kWasmOrigin);
219
220 void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint32_t offset);
221
222 void DecodeSection(SectionCode section_code,
223 base::Vector<const uint8_t> bytes, uint32_t offset,
224 bool verify_functions = true);
225
226 void StartCodeSection();
227
228 bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset);
229
230 void DecodeFunctionBody(uint32_t index, uint32_t size, uint32_t offset,
231 bool verify_functions = true);
232
233 ModuleResult FinishDecoding(bool verify_functions = true);
234
235 void set_code_section(uint32_t offset, uint32_t size);
236
237 const std::shared_ptr<WasmModule>& shared_module() const;
238
module()239 WasmModule* module() const { return shared_module().get(); }
240
241 bool ok();
242
243 // Translates the unknown section that decoder is pointing to to an extended
244 // SectionCode if the unknown section is known to decoder.
245 // The decoder is expected to point after the section length and just before
246 // the identifier string of the unknown section.
247 // The return value is the number of bytes that were consumed.
248 static size_t IdentifyUnknownSection(ModuleDecoder* decoder,
249 base::Vector<const uint8_t> bytes,
250 uint32_t offset, SectionCode* result);
251
252 private:
253 const WasmFeatures enabled_features_;
254 std::unique_ptr<ModuleDecoderImpl> impl_;
255 };
256
257 } // namespace wasm
258 } // namespace internal
259 } // namespace v8
260
261 #endif // V8_WASM_MODULE_DECODER_H_
262