File: | out/../deps/v8/src/wasm/wasm-module.h |
Warning: | line 443, column 35 Forming reference to null pointer |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 | #include "src/wasm/module-decoder.h" | ||||
6 | |||||
7 | #include "src/base/functional.h" | ||||
8 | #include "src/base/platform/platform.h" | ||||
9 | #include "src/base/platform/wrappers.h" | ||||
10 | #include "src/flags/flags.h" | ||||
11 | #include "src/init/v8.h" | ||||
12 | #include "src/logging/counters.h" | ||||
13 | #include "src/logging/metrics.h" | ||||
14 | #include "src/objects/objects-inl.h" | ||||
15 | #include "src/utils/ostreams.h" | ||||
16 | #include "src/wasm/canonical-types.h" | ||||
17 | #include "src/wasm/decoder.h" | ||||
18 | #include "src/wasm/function-body-decoder-impl.h" | ||||
19 | #include "src/wasm/init-expr-interface.h" | ||||
20 | #include "src/wasm/struct-types.h" | ||||
21 | #include "src/wasm/wasm-constants.h" | ||||
22 | #include "src/wasm/wasm-engine.h" | ||||
23 | #include "src/wasm/wasm-limits.h" | ||||
24 | #include "src/wasm/wasm-opcodes-inl.h" | ||||
25 | |||||
26 | namespace v8 { | ||||
27 | namespace internal { | ||||
28 | namespace wasm { | ||||
29 | |||||
30 | #define TRACE(...) \ | ||||
31 | do { \ | ||||
32 | if (FLAG_trace_wasm_decoder) PrintF(__VA_ARGS__); \ | ||||
33 | } while (false) | ||||
34 | |||||
35 | namespace { | ||||
36 | |||||
37 | constexpr char kNameString[] = "name"; | ||||
38 | constexpr char kSourceMappingURLString[] = "sourceMappingURL"; | ||||
39 | constexpr char kCompilationHintsString[] = "compilationHints"; | ||||
40 | constexpr char kBranchHintsString[] = "metadata.code.branch_hint"; | ||||
41 | constexpr char kDebugInfoString[] = ".debug_info"; | ||||
42 | constexpr char kExternalDebugInfoString[] = "external_debug_info"; | ||||
43 | |||||
44 | const char* ExternalKindName(ImportExportKindCode kind) { | ||||
45 | switch (kind) { | ||||
46 | case kExternalFunction: | ||||
47 | return "function"; | ||||
48 | case kExternalTable: | ||||
49 | return "table"; | ||||
50 | case kExternalMemory: | ||||
51 | return "memory"; | ||||
52 | case kExternalGlobal: | ||||
53 | return "global"; | ||||
54 | case kExternalTag: | ||||
55 | return "tag"; | ||||
56 | } | ||||
57 | return "unknown"; | ||||
58 | } | ||||
59 | |||||
60 | } // namespace | ||||
61 | |||||
62 | const char* SectionName(SectionCode code) { | ||||
63 | switch (code) { | ||||
64 | case kUnknownSectionCode: | ||||
65 | return "Unknown"; | ||||
66 | case kTypeSectionCode: | ||||
67 | return "Type"; | ||||
68 | case kImportSectionCode: | ||||
69 | return "Import"; | ||||
70 | case kFunctionSectionCode: | ||||
71 | return "Function"; | ||||
72 | case kTableSectionCode: | ||||
73 | return "Table"; | ||||
74 | case kMemorySectionCode: | ||||
75 | return "Memory"; | ||||
76 | case kGlobalSectionCode: | ||||
77 | return "Global"; | ||||
78 | case kExportSectionCode: | ||||
79 | return "Export"; | ||||
80 | case kStartSectionCode: | ||||
81 | return "Start"; | ||||
82 | case kCodeSectionCode: | ||||
83 | return "Code"; | ||||
84 | case kElementSectionCode: | ||||
85 | return "Element"; | ||||
86 | case kDataSectionCode: | ||||
87 | return "Data"; | ||||
88 | case kTagSectionCode: | ||||
89 | return "Tag"; | ||||
90 | case kDataCountSectionCode: | ||||
91 | return "DataCount"; | ||||
92 | case kNameSectionCode: | ||||
93 | return kNameString; | ||||
94 | case kSourceMappingURLSectionCode: | ||||
95 | return kSourceMappingURLString; | ||||
96 | case kDebugInfoSectionCode: | ||||
97 | return kDebugInfoString; | ||||
98 | case kExternalDebugInfoSectionCode: | ||||
99 | return kExternalDebugInfoString; | ||||
100 | case kCompilationHintsSectionCode: | ||||
101 | return kCompilationHintsString; | ||||
102 | case kBranchHintsSectionCode: | ||||
103 | return kBranchHintsString; | ||||
104 | default: | ||||
105 | return "<unknown>"; | ||||
106 | } | ||||
107 | } | ||||
108 | |||||
109 | namespace { | ||||
110 | |||||
111 | bool validate_utf8(Decoder* decoder, WireBytesRef string) { | ||||
112 | return unibrow::Utf8::ValidateEncoding( | ||||
113 | decoder->start() + decoder->GetBufferRelativeOffset(string.offset()), | ||||
114 | string.length()); | ||||
115 | } | ||||
116 | |||||
117 | // Reads a length-prefixed string, checking that it is within bounds. Returns | ||||
118 | // the offset of the string, and the length as an out parameter. | ||||
119 | WireBytesRef consume_string(Decoder* decoder, bool validate_utf8, | ||||
120 | const char* name) { | ||||
121 | uint32_t length = decoder->consume_u32v("string length"); | ||||
122 | uint32_t offset = decoder->pc_offset(); | ||||
123 | const byte* string_start = decoder->pc(); | ||||
124 | // Consume bytes before validation to guarantee that the string is not oob. | ||||
125 | if (length > 0) { | ||||
126 | decoder->consume_bytes(length, name); | ||||
127 | if (decoder->ok() && validate_utf8 && | ||||
128 | !unibrow::Utf8::ValidateEncoding(string_start, length)) { | ||||
129 | decoder->errorf(string_start, "%s: no valid UTF-8 string", name); | ||||
130 | } | ||||
131 | } | ||||
132 | return {offset, decoder->failed() ? 0 : length}; | ||||
133 | } | ||||
134 | |||||
135 | namespace { | ||||
136 | SectionCode IdentifyUnknownSectionInternal(Decoder* decoder) { | ||||
137 | WireBytesRef string = consume_string(decoder, true, "section name"); | ||||
138 | if (decoder->failed()) { | ||||
139 | return kUnknownSectionCode; | ||||
140 | } | ||||
141 | const byte* section_name_start = | ||||
142 | decoder->start() + decoder->GetBufferRelativeOffset(string.offset()); | ||||
143 | |||||
144 | TRACE(" +%d section name : \"%.*s\"\n", | ||||
145 | static_cast<int>(section_name_start - decoder->start()), | ||||
146 | string.length() < 20 ? string.length() : 20, section_name_start); | ||||
147 | |||||
148 | using SpecialSectionPair = std::pair<base::Vector<const char>, SectionCode>; | ||||
149 | static constexpr SpecialSectionPair kSpecialSections[]{ | ||||
150 | {base::StaticCharVector(kNameString), kNameSectionCode}, | ||||
151 | {base::StaticCharVector(kSourceMappingURLString), | ||||
152 | kSourceMappingURLSectionCode}, | ||||
153 | {base::StaticCharVector(kCompilationHintsString), | ||||
154 | kCompilationHintsSectionCode}, | ||||
155 | {base::StaticCharVector(kBranchHintsString), kBranchHintsSectionCode}, | ||||
156 | {base::StaticCharVector(kDebugInfoString), kDebugInfoSectionCode}, | ||||
157 | {base::StaticCharVector(kExternalDebugInfoString), | ||||
158 | kExternalDebugInfoSectionCode}}; | ||||
159 | |||||
160 | auto name_vec = base::Vector<const char>::cast( | ||||
161 | base::VectorOf(section_name_start, string.length())); | ||||
162 | for (auto& special_section : kSpecialSections) { | ||||
163 | if (name_vec == special_section.first) return special_section.second; | ||||
164 | } | ||||
165 | |||||
166 | return kUnknownSectionCode; | ||||
167 | } | ||||
168 | } // namespace | ||||
169 | |||||
170 | // An iterator over the sections in a wasm binary module. | ||||
171 | // Automatically skips all unknown sections. | ||||
172 | class WasmSectionIterator { | ||||
173 | public: | ||||
174 | explicit WasmSectionIterator(Decoder* decoder) | ||||
175 | : decoder_(decoder), | ||||
176 | section_code_(kUnknownSectionCode), | ||||
177 | section_start_(decoder->pc()), | ||||
178 | section_end_(decoder->pc()) { | ||||
179 | next(); | ||||
180 | } | ||||
181 | |||||
182 | bool more() const { return decoder_->ok() && decoder_->more(); } | ||||
183 | |||||
184 | SectionCode section_code() const { return section_code_; } | ||||
185 | |||||
186 | const byte* section_start() const { return section_start_; } | ||||
187 | |||||
188 | uint32_t section_length() const { | ||||
189 | return static_cast<uint32_t>(section_end_ - section_start_); | ||||
190 | } | ||||
191 | |||||
192 | base::Vector<const uint8_t> payload() const { | ||||
193 | return {payload_start_, payload_length()}; | ||||
194 | } | ||||
195 | |||||
196 | const byte* payload_start() const { return payload_start_; } | ||||
197 | |||||
198 | uint32_t payload_length() const { | ||||
199 | return static_cast<uint32_t>(section_end_ - payload_start_); | ||||
200 | } | ||||
201 | |||||
202 | const byte* section_end() const { return section_end_; } | ||||
203 | |||||
204 | // Advances to the next section, checking that decoding the current section | ||||
205 | // stopped at {section_end_}. | ||||
206 | void advance(bool move_to_section_end = false) { | ||||
207 | if (move_to_section_end && decoder_->pc() < section_end_) { | ||||
208 | decoder_->consume_bytes( | ||||
209 | static_cast<uint32_t>(section_end_ - decoder_->pc())); | ||||
210 | } | ||||
211 | if (decoder_->pc() != section_end_) { | ||||
212 | const char* msg = decoder_->pc() < section_end_ ? "shorter" : "longer"; | ||||
213 | decoder_->errorf(decoder_->pc(), | ||||
214 | "section was %s than expected size " | ||||
215 | "(%u bytes expected, %zu decoded)", | ||||
216 | msg, section_length(), | ||||
217 | static_cast<size_t>(decoder_->pc() - section_start_)); | ||||
218 | } | ||||
219 | next(); | ||||
220 | } | ||||
221 | |||||
222 | private: | ||||
223 | Decoder* decoder_; | ||||
224 | SectionCode section_code_; | ||||
225 | const byte* section_start_; | ||||
226 | const byte* payload_start_; | ||||
227 | const byte* section_end_; | ||||
228 | |||||
229 | // Reads the section code/name at the current position and sets up | ||||
230 | // the embedder fields. | ||||
231 | void next() { | ||||
232 | if (!decoder_->more()) { | ||||
233 | section_code_ = kUnknownSectionCode; | ||||
234 | return; | ||||
235 | } | ||||
236 | section_start_ = decoder_->pc(); | ||||
237 | uint8_t section_code = decoder_->consume_u8("section code"); | ||||
238 | // Read and check the section size. | ||||
239 | uint32_t section_length = decoder_->consume_u32v("section length"); | ||||
240 | |||||
241 | payload_start_ = decoder_->pc(); | ||||
242 | if (decoder_->checkAvailable(section_length)) { | ||||
243 | // Get the limit of the section within the module. | ||||
244 | section_end_ = payload_start_ + section_length; | ||||
245 | } else { | ||||
246 | // The section would extend beyond the end of the module. | ||||
247 | section_end_ = payload_start_; | ||||
248 | } | ||||
249 | |||||
250 | if (section_code == kUnknownSectionCode) { | ||||
251 | // Check for the known "name", "sourceMappingURL", or "compilationHints" | ||||
252 | // section. | ||||
253 | // To identify the unknown section we set the end of the decoder bytes to | ||||
254 | // the end of the custom section, so that we do not read the section name | ||||
255 | // beyond the end of the section. | ||||
256 | const byte* module_end = decoder_->end(); | ||||
257 | decoder_->set_end(section_end_); | ||||
258 | section_code = IdentifyUnknownSectionInternal(decoder_); | ||||
259 | if (decoder_->ok()) decoder_->set_end(module_end); | ||||
260 | // As a side effect, the above function will forward the decoder to after | ||||
261 | // the identifier string. | ||||
262 | payload_start_ = decoder_->pc(); | ||||
263 | } else if (!IsValidSectionCode(section_code)) { | ||||
264 | decoder_->errorf(decoder_->pc(), "unknown section code #0x%02x", | ||||
265 | section_code); | ||||
266 | section_code = kUnknownSectionCode; | ||||
267 | } | ||||
268 | section_code_ = decoder_->failed() ? kUnknownSectionCode | ||||
269 | : static_cast<SectionCode>(section_code); | ||||
270 | |||||
271 | if (section_code_ == kUnknownSectionCode && section_end_ > decoder_->pc()) { | ||||
272 | // skip to the end of the unknown section. | ||||
273 | uint32_t remaining = static_cast<uint32_t>(section_end_ - decoder_->pc()); | ||||
274 | decoder_->consume_bytes(remaining, "section payload"); | ||||
275 | } | ||||
276 | } | ||||
277 | }; | ||||
278 | |||||
279 | } // namespace | ||||
280 | |||||
281 | // The main logic for decoding the bytes of a module. | ||||
282 | class ModuleDecoderImpl : public Decoder { | ||||
283 | public: | ||||
284 | explicit ModuleDecoderImpl(const WasmFeatures& enabled, ModuleOrigin origin) | ||||
285 | : Decoder(nullptr, nullptr), | ||||
286 | enabled_features_(enabled), | ||||
287 | origin_(origin) {} | ||||
288 | |||||
289 | ModuleDecoderImpl(const WasmFeatures& enabled, const byte* module_start, | ||||
290 | const byte* module_end, ModuleOrigin origin) | ||||
291 | : Decoder(module_start, module_end), | ||||
292 | enabled_features_(enabled), | ||||
293 | module_start_(module_start), | ||||
294 | module_end_(module_end), | ||||
295 | origin_(origin) { | ||||
296 | if (end_ < start_) { | ||||
297 | error(start_, "end is less than start"); | ||||
298 | end_ = start_; | ||||
299 | } | ||||
300 | } | ||||
301 | |||||
302 | void onFirstError() override { | ||||
303 | pc_ = end_; // On error, terminate section decoding loop. | ||||
304 | } | ||||
305 | |||||
306 | void DumpModule(const base::Vector<const byte> module_bytes) { | ||||
307 | std::string path; | ||||
308 | if (FLAG_dump_wasm_module_path) { | ||||
309 | path = FLAG_dump_wasm_module_path; | ||||
310 | if (path.size() && | ||||
311 | !base::OS::isDirectorySeparator(path[path.size() - 1])) { | ||||
312 | path += base::OS::DirectorySeparator(); | ||||
313 | } | ||||
314 | } | ||||
315 | // File are named `HASH.{ok,failed}.wasm`. | ||||
316 | size_t hash = base::hash_range(module_bytes.begin(), module_bytes.end()); | ||||
317 | base::EmbeddedVector<char, 32> buf; | ||||
318 | SNPrintF(buf, "%016zx.%s.wasm", hash, ok() ? "ok" : "failed"); | ||||
319 | path += buf.begin(); | ||||
320 | size_t rv = 0; | ||||
321 | if (FILE* file = base::OS::FOpen(path.c_str(), "wb")) { | ||||
322 | rv = fwrite(module_bytes.begin(), module_bytes.length(), 1, file); | ||||
323 | base::Fclose(file); | ||||
324 | } | ||||
325 | if (rv != 1) { | ||||
326 | OFStream os(stderrstderr); | ||||
327 | os << "Error while dumping wasm file to " << path << std::endl; | ||||
328 | } | ||||
329 | } | ||||
330 | |||||
331 | void StartDecoding(Counters* counters, AccountingAllocator* allocator) { | ||||
332 | CHECK_NULL(module_)do { if ((__builtin_expect(!!(!((module_) == nullptr)), 0))) { V8_Fatal("Check failed: %s.", "(module_) == nullptr"); } } while (false); | ||||
333 | SetCounters(counters); | ||||
334 | module_.reset( | ||||
335 | new WasmModule(std::make_unique<Zone>(allocator, "signatures"))); | ||||
336 | module_->initial_pages = 0; | ||||
337 | module_->maximum_pages = 0; | ||||
338 | module_->mem_export = false; | ||||
339 | module_->origin = origin_; | ||||
340 | } | ||||
341 | |||||
342 | void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint8_t offset) { | ||||
343 | if (failed()) return; | ||||
344 | Reset(bytes, offset); | ||||
345 | |||||
346 | const byte* pos = pc_; | ||||
347 | uint32_t magic_word = consume_u32("wasm magic"); | ||||
348 | #define BYTES(x) (x & 0xFF), (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF | ||||
349 | if (magic_word != kWasmMagic) { | ||||
350 | errorf(pos, | ||||
351 | "expected magic word %02x %02x %02x %02x, " | ||||
352 | "found %02x %02x %02x %02x", | ||||
353 | BYTES(kWasmMagic), BYTES(magic_word)); | ||||
354 | } | ||||
355 | |||||
356 | pos = pc_; | ||||
357 | { | ||||
358 | uint32_t magic_version = consume_u32("wasm version"); | ||||
359 | if (magic_version != kWasmVersion) { | ||||
360 | errorf(pos, | ||||
361 | "expected version %02x %02x %02x %02x, " | ||||
362 | "found %02x %02x %02x %02x", | ||||
363 | BYTES(kWasmVersion), BYTES(magic_version)); | ||||
364 | } | ||||
365 | } | ||||
366 | #undef BYTES | ||||
367 | } | ||||
368 | |||||
369 | bool CheckSectionOrder(SectionCode section_code, | ||||
370 | SectionCode prev_section_code, | ||||
371 | SectionCode next_section_code) { | ||||
372 | if (next_ordered_section_ > next_section_code) { | ||||
373 | errorf(pc(), "The %s section must appear before the %s section", | ||||
374 | SectionName(section_code), SectionName(next_section_code)); | ||||
375 | return false; | ||||
376 | } | ||||
377 | if (next_ordered_section_ <= prev_section_code) { | ||||
378 | next_ordered_section_ = prev_section_code + 1; | ||||
379 | } | ||||
380 | return true; | ||||
381 | } | ||||
382 | |||||
383 | bool CheckUnorderedSection(SectionCode section_code) { | ||||
384 | if (has_seen_unordered_section(section_code)) { | ||||
385 | errorf(pc(), "Multiple %s sections not allowed", | ||||
386 | SectionName(section_code)); | ||||
387 | return false; | ||||
388 | } | ||||
389 | set_seen_unordered_section(section_code); | ||||
390 | return true; | ||||
391 | } | ||||
392 | |||||
393 | void DecodeSection(SectionCode section_code, | ||||
394 | base::Vector<const uint8_t> bytes, uint32_t offset, | ||||
395 | bool verify_functions = true) { | ||||
396 | if (failed()) return; | ||||
397 | Reset(bytes, offset); | ||||
398 | TRACE("Section: %s\n", SectionName(section_code)); | ||||
399 | TRACE("Decode Section %p - %p\n", bytes.begin(), bytes.end()); | ||||
400 | |||||
401 | // Check if the section is out-of-order. | ||||
402 | if (section_code < next_ordered_section_ && | ||||
403 | section_code < kFirstUnorderedSection) { | ||||
404 | errorf(pc(), "unexpected section <%s>", SectionName(section_code)); | ||||
405 | return; | ||||
406 | } | ||||
407 | |||||
408 | switch (section_code) { | ||||
409 | case kUnknownSectionCode: | ||||
410 | break; | ||||
411 | case kDataCountSectionCode: | ||||
412 | if (!CheckUnorderedSection(section_code)) return; | ||||
413 | // If wasm-gc is enabled, we allow the data cound section anywhere in | ||||
414 | // the module. | ||||
415 | if (!enabled_features_.has_gc() && | ||||
416 | !CheckSectionOrder(section_code, kElementSectionCode, | ||||
417 | kCodeSectionCode)) { | ||||
418 | return; | ||||
419 | } | ||||
420 | break; | ||||
421 | case kTagSectionCode: | ||||
422 | if (!CheckUnorderedSection(section_code)) return; | ||||
423 | if (!CheckSectionOrder(section_code, kMemorySectionCode, | ||||
424 | kGlobalSectionCode)) { | ||||
425 | return; | ||||
426 | } | ||||
427 | break; | ||||
428 | case kNameSectionCode: | ||||
429 | // TODO(titzer): report out of place name section as a warning. | ||||
430 | // Be lenient with placement of name section. All except first | ||||
431 | // occurrence are ignored. | ||||
432 | case kSourceMappingURLSectionCode: | ||||
433 | // sourceMappingURL is a custom section and currently can occur anywhere | ||||
434 | // in the module. In case of multiple sourceMappingURL sections, all | ||||
435 | // except the first occurrence are ignored. | ||||
436 | case kDebugInfoSectionCode: | ||||
437 | // .debug_info is a custom section containing core DWARF information | ||||
438 | // if produced by compiler. Its presence likely means that Wasm was | ||||
439 | // built in a debug mode. | ||||
440 | case kExternalDebugInfoSectionCode: | ||||
441 | // external_debug_info is a custom section containing a reference to an | ||||
442 | // external symbol file. | ||||
443 | case kCompilationHintsSectionCode: | ||||
444 | // TODO(frgossen): report out of place compilation hints section as a | ||||
445 | // warning. | ||||
446 | // Be lenient with placement of compilation hints section. All except | ||||
447 | // first occurrence after function section and before code section are | ||||
448 | // ignored. | ||||
449 | break; | ||||
450 | case kBranchHintsSectionCode: | ||||
451 | // TODO(yuri): report out of place branch hints section as a | ||||
452 | // warning. | ||||
453 | // Be lenient with placement of compilation hints section. All except | ||||
454 | // first occurrence after function section and before code section are | ||||
455 | // ignored. | ||||
456 | break; | ||||
457 | default: | ||||
458 | next_ordered_section_ = section_code + 1; | ||||
459 | break; | ||||
460 | } | ||||
461 | |||||
462 | switch (section_code) { | ||||
463 | case kUnknownSectionCode: | ||||
464 | break; | ||||
465 | case kTypeSectionCode: | ||||
466 | DecodeTypeSection(); | ||||
467 | break; | ||||
468 | case kImportSectionCode: | ||||
469 | DecodeImportSection(); | ||||
470 | break; | ||||
471 | case kFunctionSectionCode: | ||||
472 | DecodeFunctionSection(); | ||||
473 | break; | ||||
474 | case kTableSectionCode: | ||||
475 | DecodeTableSection(); | ||||
476 | break; | ||||
477 | case kMemorySectionCode: | ||||
478 | DecodeMemorySection(); | ||||
479 | break; | ||||
480 | case kGlobalSectionCode: | ||||
481 | DecodeGlobalSection(); | ||||
482 | break; | ||||
483 | case kExportSectionCode: | ||||
484 | DecodeExportSection(); | ||||
485 | break; | ||||
486 | case kStartSectionCode: | ||||
487 | DecodeStartSection(); | ||||
488 | break; | ||||
489 | case kCodeSectionCode: | ||||
490 | DecodeCodeSection(verify_functions); | ||||
491 | break; | ||||
492 | case kElementSectionCode: | ||||
493 | DecodeElementSection(); | ||||
494 | break; | ||||
495 | case kDataSectionCode: | ||||
496 | DecodeDataSection(); | ||||
497 | break; | ||||
498 | case kNameSectionCode: | ||||
499 | DecodeNameSection(); | ||||
500 | break; | ||||
501 | case kSourceMappingURLSectionCode: | ||||
502 | DecodeSourceMappingURLSection(); | ||||
503 | break; | ||||
504 | case kDebugInfoSectionCode: | ||||
505 | // If there is an explicit source map, prefer it over DWARF info. | ||||
506 | if (module_->debug_symbols.type == WasmDebugSymbols::Type::None) { | ||||
507 | module_->debug_symbols = {WasmDebugSymbols::Type::EmbeddedDWARF, {}}; | ||||
508 | } | ||||
509 | consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info"); | ||||
510 | break; | ||||
511 | case kExternalDebugInfoSectionCode: | ||||
512 | DecodeExternalDebugInfoSection(); | ||||
513 | break; | ||||
514 | case kCompilationHintsSectionCode: | ||||
515 | if (enabled_features_.has_compilation_hints()) { | ||||
516 | DecodeCompilationHintsSection(); | ||||
517 | } else { | ||||
518 | // Ignore this section when feature was disabled. It is an optional | ||||
519 | // custom section anyways. | ||||
520 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
521 | } | ||||
522 | break; | ||||
523 | case kBranchHintsSectionCode: | ||||
524 | if (enabled_features_.has_branch_hinting()) { | ||||
525 | DecodeBranchHintsSection(); | ||||
526 | } else { | ||||
527 | // Ignore this section when feature was disabled. It is an optional | ||||
528 | // custom section anyways. | ||||
529 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
530 | } | ||||
531 | break; | ||||
532 | case kDataCountSectionCode: | ||||
533 | DecodeDataCountSection(); | ||||
534 | break; | ||||
535 | case kTagSectionCode: | ||||
536 | if (enabled_features_.has_eh()) { | ||||
537 | DecodeTagSection(); | ||||
538 | } else { | ||||
539 | errorf(pc(), | ||||
540 | "unexpected section <%s> (enable with --experimental-wasm-eh)", | ||||
541 | SectionName(section_code)); | ||||
542 | } | ||||
543 | break; | ||||
544 | default: | ||||
545 | errorf(pc(), "unexpected section <%s>", SectionName(section_code)); | ||||
546 | return; | ||||
547 | } | ||||
548 | |||||
549 | if (pc() != bytes.end()) { | ||||
550 | const char* msg = pc() < bytes.end() ? "shorter" : "longer"; | ||||
551 | errorf(pc(), | ||||
552 | "section was %s than expected size " | ||||
553 | "(%zu bytes expected, %zu decoded)", | ||||
554 | msg, bytes.size(), static_cast<size_t>(pc() - bytes.begin())); | ||||
555 | } | ||||
556 | } | ||||
557 | |||||
558 | TypeDefinition consume_base_type_definition() { | ||||
559 | DCHECK(enabled_features_.has_gc())((void) 0); | ||||
560 | uint8_t kind = consume_u8("type kind"); | ||||
561 | switch (kind) { | ||||
562 | case kWasmFunctionTypeCode: { | ||||
563 | const FunctionSig* sig = consume_sig(module_->signature_zone.get()); | ||||
564 | return {sig, kNoSuperType}; | ||||
565 | } | ||||
566 | case kWasmStructTypeCode: { | ||||
567 | const StructType* type = consume_struct(module_->signature_zone.get()); | ||||
568 | return {type, kNoSuperType}; | ||||
569 | } | ||||
570 | case kWasmArrayTypeCode: { | ||||
571 | const ArrayType* type = consume_array(module_->signature_zone.get()); | ||||
572 | return {type, kNoSuperType}; | ||||
573 | } | ||||
574 | case kWasmFunctionNominalCode: | ||||
575 | case kWasmArrayNominalCode: | ||||
576 | case kWasmStructNominalCode: | ||||
577 | errorf(pc() - 1, | ||||
578 | "mixing nominal and isorecursive types is not allowed"); | ||||
579 | return {}; | ||||
580 | default: | ||||
581 | errorf(pc() - 1, "unknown type form: %d", kind); | ||||
582 | return {}; | ||||
583 | } | ||||
584 | } | ||||
585 | |||||
586 | bool check_supertype(uint32_t supertype) { | ||||
587 | if (V8_UNLIKELY(supertype >= module_->types.size())(__builtin_expect(!!(supertype >= module_->types.size() ), 0))) { | ||||
588 | errorf(pc(), "type %zu: forward-declared supertype %d", | ||||
589 | module_->types.size(), supertype); | ||||
590 | return false; | ||||
591 | } | ||||
592 | return true; | ||||
593 | } | ||||
594 | |||||
595 | TypeDefinition consume_nominal_type_definition() { | ||||
596 | DCHECK(enabled_features_.has_gc())((void) 0); | ||||
597 | size_t num_types = module_->types.size(); | ||||
598 | uint8_t kind = consume_u8("type kind"); | ||||
599 | switch (kind) { | ||||
600 | case kWasmFunctionNominalCode: { | ||||
601 | const FunctionSig* sig = consume_sig(module_->signature_zone.get()); | ||||
602 | uint32_t super_index = kNoSuperType; | ||||
603 | HeapType super_type = consume_super_type(); | ||||
604 | if (super_type.is_index()) { | ||||
605 | super_index = super_type.representation(); | ||||
606 | } else if (V8_UNLIKELY(super_type != HeapType::kFunc)(__builtin_expect(!!(super_type != HeapType::kFunc), 0))) { | ||||
607 | errorf(pc() - 1, "type %zu: invalid supertype %d", num_types, | ||||
608 | super_type.code()); | ||||
609 | return {}; | ||||
610 | } | ||||
611 | return {sig, super_index}; | ||||
612 | } | ||||
613 | case kWasmStructNominalCode: { | ||||
614 | const StructType* type = consume_struct(module_->signature_zone.get()); | ||||
615 | uint32_t super_index = kNoSuperType; | ||||
616 | HeapType super_type = consume_super_type(); | ||||
617 | if (super_type.is_index()) { | ||||
618 | super_index = super_type.representation(); | ||||
619 | } else if (V8_UNLIKELY(super_type != HeapType::kData)(__builtin_expect(!!(super_type != HeapType::kData), 0))) { | ||||
620 | errorf(pc() - 1, "type %zu: invalid supertype %d", num_types, | ||||
621 | super_type.code()); | ||||
622 | return {}; | ||||
623 | } | ||||
624 | return {type, super_index}; | ||||
625 | } | ||||
626 | case kWasmArrayNominalCode: { | ||||
627 | const ArrayType* type = consume_array(module_->signature_zone.get()); | ||||
628 | uint32_t super_index = kNoSuperType; | ||||
629 | HeapType super_type = consume_super_type(); | ||||
630 | if (super_type.is_index()) { | ||||
631 | super_index = super_type.representation(); | ||||
632 | } else if (V8_UNLIKELY(super_type != HeapType::kData)(__builtin_expect(!!(super_type != HeapType::kData), 0))) { | ||||
633 | errorf(pc() - 1, "type %zu: invalid supertype %d", num_types, | ||||
634 | super_type.code()); | ||||
635 | return {}; | ||||
636 | } | ||||
637 | return {type, super_index}; | ||||
638 | } | ||||
639 | case kWasmFunctionTypeCode: | ||||
640 | case kWasmArrayTypeCode: | ||||
641 | case kWasmStructTypeCode: | ||||
642 | case kWasmSubtypeCode: | ||||
643 | case kWasmRecursiveTypeGroupCode: | ||||
644 | errorf(pc() - 1, | ||||
645 | "mixing nominal and isorecursive types is not allowed"); | ||||
646 | return {}; | ||||
647 | default: | ||||
648 | errorf(pc() - 1, "unknown type form: %d", kind); | ||||
649 | return {}; | ||||
650 | } | ||||
651 | } | ||||
652 | |||||
653 | TypeDefinition consume_subtype_definition() { | ||||
654 | DCHECK(enabled_features_.has_gc())((void) 0); | ||||
655 | uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind"); | ||||
656 | if (kind == kWasmSubtypeCode) { | ||||
657 | consume_bytes(1, "subtype definition"); | ||||
658 | constexpr uint32_t kMaximumSupertypes = 1; | ||||
659 | uint32_t supertype_count = | ||||
660 | consume_count("supertype count", kMaximumSupertypes); | ||||
661 | uint32_t supertype = | ||||
662 | supertype_count == 1 ? consume_u32v("supertype") : kNoSuperType; | ||||
663 | if (!check_supertype(supertype)) return {}; | ||||
664 | TypeDefinition type = consume_base_type_definition(); | ||||
665 | type.supertype = supertype; | ||||
666 | return type; | ||||
667 | } else { | ||||
668 | return consume_base_type_definition(); | ||||
669 | } | ||||
670 | } | ||||
671 | |||||
672 | void DecodeTypeSection() { | ||||
673 | TypeCanonicalizer* type_canon = GetTypeCanonicalizer(); | ||||
674 | uint32_t types_count = consume_count("types count", kV8MaxWasmTypes); | ||||
675 | |||||
676 | // Non wasm-gc type section decoding. | ||||
677 | if (!enabled_features_.has_gc()) { | ||||
678 | module_->types.reserve(types_count); | ||||
679 | for (uint32_t i = 0; i < types_count; ++i) { | ||||
680 | TRACE("DecodeSignature[%d] module+%d\n", i, | ||||
681 | static_cast<int>(pc_ - start_)); | ||||
682 | expect_u8("signature definition", kWasmFunctionTypeCode); | ||||
683 | const FunctionSig* sig = consume_sig(module_->signature_zone.get()); | ||||
684 | if (!ok()) break; | ||||
685 | module_->add_signature(sig, kNoSuperType); | ||||
686 | if (FLAG_wasm_type_canonicalization) { | ||||
687 | type_canon->AddRecursiveGroup(module_.get(), 1); | ||||
688 | } | ||||
689 | } | ||||
690 | return; | ||||
691 | } | ||||
692 | |||||
693 | if (types_count > 0) { | ||||
694 | uint8_t first_type_opcode = this->read_u8<Decoder::kFullValidation>(pc()); | ||||
695 | if (first_type_opcode == kWasmFunctionNominalCode || | ||||
696 | first_type_opcode == kWasmStructNominalCode || | ||||
697 | first_type_opcode == kWasmArrayNominalCode) { | ||||
698 | // wasm-gc nominal type section decoding. | ||||
699 | // In a nominal module, all types belong in the same recursive group. We | ||||
700 | // use the type vector's capacity to mark the end of the current | ||||
701 | // recursive group. | ||||
702 | module_->types.reserve(types_count); | ||||
703 | for (uint32_t i = 0; ok() && i
| ||||
704 | TRACE("DecodeType[%d] module+%d\n", i, | ||||
705 | static_cast<int>(pc_ - start_)); | ||||
706 | TypeDefinition type = consume_nominal_type_definition(); | ||||
707 | if (ok()) module_->add_type(type); | ||||
708 | } | ||||
709 | if (ok() && FLAG_wasm_type_canonicalization) { | ||||
710 | type_canon->AddRecursiveGroup(module_.get(), types_count); | ||||
711 | } | ||||
712 | } else { | ||||
713 | // wasm-gc isorecursive type section decoding. | ||||
714 | for (uint32_t i = 0; ok() && i < types_count; ++i) { | ||||
715 | TRACE("DecodeType[%d] module+%d\n", i, | ||||
716 | static_cast<int>(pc_ - start_)); | ||||
717 | uint8_t kind = read_u8<Decoder::kFullValidation>(pc(), "type kind"); | ||||
718 | if (kind == kWasmRecursiveTypeGroupCode) { | ||||
719 | consume_bytes(1, "rec. group definition"); | ||||
720 | uint32_t group_size = | ||||
721 | consume_count("recursive group size", kV8MaxWasmTypes); | ||||
722 | if (module_->types.size() + group_size > kV8MaxWasmTypes) { | ||||
723 | errorf(pc(), "Type definition count exeeds maximum %zu", | ||||
724 | kV8MaxWasmTypes); | ||||
725 | return; | ||||
726 | } | ||||
727 | // Reserve space for the current recursive group, so we are | ||||
728 | // allowed to reference its elements. | ||||
729 | module_->types.reserve(module_->types.size() + group_size); | ||||
730 | for (uint32_t i = 0; i < group_size; i++) { | ||||
731 | TypeDefinition type = consume_subtype_definition(); | ||||
732 | if (ok()) module_->add_type(type); | ||||
733 | } | ||||
734 | if (ok() && FLAG_wasm_type_canonicalization) { | ||||
735 | type_canon->AddRecursiveGroup(module_.get(), group_size); | ||||
736 | } | ||||
737 | } else { | ||||
738 | TypeDefinition type = consume_subtype_definition(); | ||||
739 | if (ok()) { | ||||
740 | module_->add_type(type); | ||||
741 | if (FLAG_wasm_type_canonicalization) { | ||||
742 | type_canon->AddRecursiveGroup(module_.get(), 1); | ||||
743 | } | ||||
744 | } | ||||
745 | } | ||||
746 | } | ||||
747 | } | ||||
748 | } | ||||
749 | |||||
750 | // Check validity of explicitly defined supertypes. | ||||
751 | const WasmModule* module = module_.get(); | ||||
752 | for (uint32_t i = 0; ok() && i < types_count; ++i) { | ||||
753 | uint32_t explicit_super = module_->supertype(i); | ||||
754 | if (explicit_super == kNoSuperType) continue; | ||||
755 | DCHECK_LT(explicit_super, types_count)((void) 0); // {consume_super_type} checks. | ||||
756 | int depth = GetSubtypingDepth(module, i); | ||||
757 | if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) { | ||||
758 | errorf("type %d: subtyping depth is greater than allowed", i); | ||||
759 | continue; | ||||
760 | } | ||||
761 | // TODO(7748): Replace this with a DCHECK once we reject inheritance | ||||
762 | // cycles for nominal modules. | ||||
763 | if (depth == -1) { | ||||
764 | errorf("type %d: cyclic inheritance", i); | ||||
765 | continue; | ||||
766 | } | ||||
767 | if (!ValidSubtypeDefinition(i, explicit_super, module, module)) { | ||||
768 | errorf("type %d has invalid explicit supertype %d", i, explicit_super); | ||||
769 | continue; | ||||
770 | } | ||||
771 | } | ||||
772 | module_->signature_map.Freeze(); | ||||
773 | } | ||||
774 | |||||
775 | void DecodeImportSection() { | ||||
776 | uint32_t import_table_count = | ||||
777 | consume_count("imports count", kV8MaxWasmImports); | ||||
778 | module_->import_table.reserve(import_table_count); | ||||
779 | for (uint32_t i = 0; ok() && i < import_table_count; ++i) { | ||||
780 | TRACE("DecodeImportTable[%d] module+%d\n", i, | ||||
781 | static_cast<int>(pc_ - start_)); | ||||
782 | |||||
783 | module_->import_table.push_back({ | ||||
784 | {0, 0}, // module_name | ||||
785 | {0, 0}, // field_name | ||||
786 | kExternalFunction, // kind | ||||
787 | 0 // index | ||||
788 | }); | ||||
789 | WasmImport* import = &module_->import_table.back(); | ||||
790 | const byte* pos = pc_; | ||||
791 | import->module_name = consume_string(this, true, "module name"); | ||||
792 | import->field_name = consume_string(this, true, "field name"); | ||||
793 | import->kind = | ||||
794 | static_cast<ImportExportKindCode>(consume_u8("import kind")); | ||||
795 | switch (import->kind) { | ||||
796 | case kExternalFunction: { | ||||
797 | // ===== Imported function =========================================== | ||||
798 | import->index = static_cast<uint32_t>(module_->functions.size()); | ||||
799 | module_->num_imported_functions++; | ||||
800 | module_->functions.push_back({nullptr, // sig | ||||
801 | import->index, // func_index | ||||
802 | 0, // sig_index | ||||
803 | {0, 0}, // code | ||||
804 | 0, // feedback slots | ||||
805 | true, // imported | ||||
806 | false, // exported | ||||
807 | false}); // declared | ||||
808 | WasmFunction* function = &module_->functions.back(); | ||||
809 | function->sig_index = | ||||
810 | consume_sig_index(module_.get(), &function->sig); | ||||
811 | break; | ||||
812 | } | ||||
813 | case kExternalTable: { | ||||
814 | // ===== Imported table ============================================== | ||||
815 | import->index = static_cast<uint32_t>(module_->tables.size()); | ||||
816 | module_->num_imported_tables++; | ||||
817 | module_->tables.emplace_back(); | ||||
818 | WasmTable* table = &module_->tables.back(); | ||||
819 | table->imported = true; | ||||
820 | const byte* type_position = pc(); | ||||
821 | ValueType type = consume_reference_type(); | ||||
822 | if (!WasmTable::IsValidTableType(type, module_.get())) { | ||||
823 | errorf(type_position, "Invalid table type %s", type.name().c_str()); | ||||
824 | break; | ||||
825 | } | ||||
826 | table->type = type; | ||||
827 | uint8_t flags = validate_table_flags("element count"); | ||||
828 | consume_resizable_limits( | ||||
829 | "element count", "elements", std::numeric_limits<uint32_t>::max(), | ||||
830 | &table->initial_size, &table->has_maximum_size, | ||||
831 | std::numeric_limits<uint32_t>::max(), &table->maximum_size, | ||||
832 | flags); | ||||
833 | break; | ||||
834 | } | ||||
835 | case kExternalMemory: { | ||||
836 | // ===== Imported memory ============================================= | ||||
837 | if (!AddMemory(module_.get())) break; | ||||
838 | uint8_t flags = validate_memory_flags(&module_->has_shared_memory, | ||||
839 | &module_->is_memory64); | ||||
840 | consume_resizable_limits( | ||||
841 | "memory", "pages", kSpecMaxMemoryPages, &module_->initial_pages, | ||||
842 | &module_->has_maximum_pages, kSpecMaxMemoryPages, | ||||
843 | &module_->maximum_pages, flags); | ||||
844 | break; | ||||
845 | } | ||||
846 | case kExternalGlobal: { | ||||
847 | // ===== Imported global ============================================= | ||||
848 | import->index = static_cast<uint32_t>(module_->globals.size()); | ||||
849 | module_->globals.push_back({kWasmVoid, false, {}, {0}, true, false}); | ||||
850 | WasmGlobal* global = &module_->globals.back(); | ||||
851 | global->type = consume_value_type(); | ||||
852 | global->mutability = consume_mutability(); | ||||
853 | if (global->mutability) { | ||||
854 | module_->num_imported_mutable_globals++; | ||||
855 | } | ||||
856 | break; | ||||
857 | } | ||||
858 | case kExternalTag: { | ||||
859 | // ===== Imported tag ================================================ | ||||
860 | if (!enabled_features_.has_eh()) { | ||||
861 | errorf(pos, "unknown import kind 0x%02x", import->kind); | ||||
862 | break; | ||||
863 | } | ||||
864 | import->index = static_cast<uint32_t>(module_->tags.size()); | ||||
865 | const WasmTagSig* tag_sig = nullptr; | ||||
866 | consume_exception_attribute(); // Attribute ignored for now. | ||||
867 | consume_tag_sig_index(module_.get(), &tag_sig); | ||||
868 | module_->tags.emplace_back(tag_sig); | ||||
869 | break; | ||||
870 | } | ||||
871 | default: | ||||
872 | errorf(pos, "unknown import kind 0x%02x", import->kind); | ||||
873 | break; | ||||
874 | } | ||||
875 | } | ||||
876 | } | ||||
877 | |||||
878 | void DecodeFunctionSection() { | ||||
879 | uint32_t functions_count = | ||||
880 | consume_count("functions count", kV8MaxWasmFunctions); | ||||
881 | auto counter = | ||||
882 | SELECT_WASM_COUNTER(GetCounters(), origin_, wasm_functions_per, module)((origin_) == kWasmOrigin ? (GetCounters())->wasm_functions_per_wasm_module () : (GetCounters())->wasm_functions_per_asm_module()); | ||||
883 | counter->AddSample(static_cast<int>(functions_count)); | ||||
884 | DCHECK_EQ(module_->functions.size(), module_->num_imported_functions)((void) 0); | ||||
885 | uint32_t total_function_count = | ||||
886 | module_->num_imported_functions + functions_count; | ||||
887 | module_->functions.reserve(total_function_count); | ||||
888 | module_->num_declared_functions = functions_count; | ||||
889 | for (uint32_t i = 0; i < functions_count; ++i) { | ||||
890 | uint32_t func_index = static_cast<uint32_t>(module_->functions.size()); | ||||
891 | module_->functions.push_back({nullptr, // sig | ||||
892 | func_index, // func_index | ||||
893 | 0, // sig_index | ||||
894 | {0, 0}, // code | ||||
895 | 0, // feedback slots | ||||
896 | false, // imported | ||||
897 | false, // exported | ||||
898 | false}); // declared | ||||
899 | WasmFunction* function = &module_->functions.back(); | ||||
900 | function->sig_index = consume_sig_index(module_.get(), &function->sig); | ||||
901 | if (!ok()) return; | ||||
902 | } | ||||
903 | DCHECK_EQ(module_->functions.size(), total_function_count)((void) 0); | ||||
904 | } | ||||
905 | |||||
906 | void DecodeTableSection() { | ||||
907 | uint32_t table_count = consume_count("table count", kV8MaxWasmTables); | ||||
908 | |||||
909 | for (uint32_t i = 0; ok() && i < table_count; i++) { | ||||
910 | module_->tables.emplace_back(); | ||||
911 | WasmTable* table = &module_->tables.back(); | ||||
912 | const byte* type_position = pc(); | ||||
913 | ValueType table_type = consume_reference_type(); | ||||
914 | if (!WasmTable::IsValidTableType(table_type, module_.get())) { | ||||
915 | error(type_position, | ||||
916 | "Currently, only externref and function references are allowed " | ||||
917 | "as table types"); | ||||
918 | continue; | ||||
919 | } | ||||
920 | table->type = table_type; | ||||
921 | uint8_t flags = validate_table_flags("table elements"); | ||||
922 | consume_resizable_limits( | ||||
923 | "table elements", "elements", std::numeric_limits<uint32_t>::max(), | ||||
924 | &table->initial_size, &table->has_maximum_size, | ||||
925 | std::numeric_limits<uint32_t>::max(), &table->maximum_size, flags); | ||||
926 | if (!table_type.is_defaultable()) { | ||||
927 | table->initial_value = consume_init_expr(module_.get(), table_type); | ||||
928 | } | ||||
929 | } | ||||
930 | } | ||||
931 | |||||
932 | void DecodeMemorySection() { | ||||
933 | uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories); | ||||
934 | |||||
935 | for (uint32_t i = 0; ok() && i < memory_count; i++) { | ||||
936 | if (!AddMemory(module_.get())) break; | ||||
937 | uint8_t flags = validate_memory_flags(&module_->has_shared_memory, | ||||
938 | &module_->is_memory64); | ||||
939 | consume_resizable_limits("memory", "pages", kSpecMaxMemoryPages, | ||||
940 | &module_->initial_pages, | ||||
941 | &module_->has_maximum_pages, kSpecMaxMemoryPages, | ||||
942 | &module_->maximum_pages, flags); | ||||
943 | } | ||||
944 | } | ||||
945 | |||||
946 | void DecodeGlobalSection() { | ||||
947 | uint32_t globals_count = consume_count("globals count", kV8MaxWasmGlobals); | ||||
948 | uint32_t imported_globals = static_cast<uint32_t>(module_->globals.size()); | ||||
949 | // It is important to not resize the globals vector from the beginning, | ||||
950 | // because we use its current size when decoding the initializer. | ||||
951 | module_->globals.reserve(imported_globals + globals_count); | ||||
952 | for (uint32_t i = 0; ok() && i < globals_count; ++i) { | ||||
953 | TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); | ||||
954 | ValueType type = consume_value_type(); | ||||
955 | bool mutability = consume_mutability(); | ||||
956 | if (failed()) break; | ||||
957 | ConstantExpression init = consume_init_expr(module_.get(), type); | ||||
958 | module_->globals.push_back({type, mutability, init, {0}, false, false}); | ||||
959 | } | ||||
960 | if (ok()) CalculateGlobalOffsets(module_.get()); | ||||
961 | } | ||||
962 | |||||
963 | void DecodeExportSection() { | ||||
964 | uint32_t export_table_count = | ||||
965 | consume_count("exports count", kV8MaxWasmExports); | ||||
966 | module_->export_table.reserve(export_table_count); | ||||
967 | for (uint32_t i = 0; ok() && i < export_table_count; ++i) { | ||||
968 | TRACE("DecodeExportTable[%d] module+%d\n", i, | ||||
969 | static_cast<int>(pc_ - start_)); | ||||
970 | |||||
971 | module_->export_table.push_back({ | ||||
972 | {0, 0}, // name | ||||
973 | kExternalFunction, // kind | ||||
974 | 0 // index | ||||
975 | }); | ||||
976 | WasmExport* exp = &module_->export_table.back(); | ||||
977 | |||||
978 | exp->name = consume_string(this, true, "field name"); | ||||
979 | |||||
980 | const byte* pos = pc(); | ||||
981 | exp->kind = static_cast<ImportExportKindCode>(consume_u8("export kind")); | ||||
982 | switch (exp->kind) { | ||||
983 | case kExternalFunction: { | ||||
984 | WasmFunction* func = nullptr; | ||||
985 | exp->index = | ||||
986 | consume_func_index(module_.get(), &func, "export function index"); | ||||
987 | |||||
988 | if (failed()) break; | ||||
989 | DCHECK_NOT_NULL(func)((void) 0); | ||||
990 | |||||
991 | module_->num_exported_functions++; | ||||
992 | func->exported = true; | ||||
993 | // Exported functions are considered "declared". | ||||
994 | func->declared = true; | ||||
995 | break; | ||||
996 | } | ||||
997 | case kExternalTable: { | ||||
998 | WasmTable* table = nullptr; | ||||
999 | exp->index = consume_table_index(module_.get(), &table); | ||||
1000 | if (table) table->exported = true; | ||||
1001 | break; | ||||
1002 | } | ||||
1003 | case kExternalMemory: { | ||||
1004 | uint32_t index = consume_u32v("memory index"); | ||||
1005 | // TODO(titzer): This should become more regular | ||||
1006 | // once we support multiple memories. | ||||
1007 | if (!module_->has_memory || index != 0) { | ||||
1008 | error("invalid memory index != 0"); | ||||
1009 | } | ||||
1010 | module_->mem_export = true; | ||||
1011 | break; | ||||
1012 | } | ||||
1013 | case kExternalGlobal: { | ||||
1014 | WasmGlobal* global = nullptr; | ||||
1015 | exp->index = consume_global_index(module_.get(), &global); | ||||
1016 | if (global) { | ||||
1017 | global->exported = true; | ||||
1018 | } | ||||
1019 | break; | ||||
1020 | } | ||||
1021 | case kExternalTag: { | ||||
1022 | if (!enabled_features_.has_eh()) { | ||||
1023 | errorf(pos, "invalid export kind 0x%02x", exp->kind); | ||||
1024 | break; | ||||
1025 | } | ||||
1026 | WasmTag* tag = nullptr; | ||||
1027 | exp->index = consume_tag_index(module_.get(), &tag); | ||||
1028 | break; | ||||
1029 | } | ||||
1030 | default: | ||||
1031 | errorf(pos, "invalid export kind 0x%02x", exp->kind); | ||||
1032 | break; | ||||
1033 | } | ||||
1034 | } | ||||
1035 | // Check for duplicate exports (except for asm.js). | ||||
1036 | if (ok() && origin_ == kWasmOrigin && module_->export_table.size() > 1) { | ||||
1037 | std::vector<WasmExport> sorted_exports(module_->export_table); | ||||
1038 | |||||
1039 | auto cmp_less = [this](const WasmExport& a, const WasmExport& b) { | ||||
1040 | // Return true if a < b. | ||||
1041 | if (a.name.length() != b.name.length()) { | ||||
1042 | return a.name.length() < b.name.length(); | ||||
1043 | } | ||||
1044 | const byte* left = start() + GetBufferRelativeOffset(a.name.offset()); | ||||
1045 | const byte* right = start() + GetBufferRelativeOffset(b.name.offset()); | ||||
1046 | return memcmp(left, right, a.name.length()) < 0; | ||||
1047 | }; | ||||
1048 | std::stable_sort(sorted_exports.begin(), sorted_exports.end(), cmp_less); | ||||
1049 | |||||
1050 | auto it = sorted_exports.begin(); | ||||
1051 | WasmExport* last = &*it++; | ||||
1052 | for (auto end = sorted_exports.end(); it != end; last = &*it++) { | ||||
1053 | DCHECK(!cmp_less(*it, *last))((void) 0); // Vector must be sorted. | ||||
1054 | if (!cmp_less(*last, *it)) { | ||||
1055 | const byte* pc = start() + GetBufferRelativeOffset(it->name.offset()); | ||||
1056 | TruncatedUserString<> name(pc, it->name.length()); | ||||
1057 | errorf(pc, "Duplicate export name '%.*s' for %s %d and %s %d", | ||||
1058 | name.length(), name.start(), ExternalKindName(last->kind), | ||||
1059 | last->index, ExternalKindName(it->kind), it->index); | ||||
1060 | break; | ||||
1061 | } | ||||
1062 | } | ||||
1063 | } | ||||
1064 | } | ||||
1065 | |||||
1066 | void DecodeStartSection() { | ||||
1067 | WasmFunction* func; | ||||
1068 | const byte* pos = pc_; | ||||
1069 | module_->start_function_index = | ||||
1070 | consume_func_index(module_.get(), &func, "start function index"); | ||||
1071 | if (func && | ||||
1072 | (func->sig->parameter_count() > 0 || func->sig->return_count() > 0)) { | ||||
1073 | error(pos, "invalid start function: non-zero parameter or return count"); | ||||
1074 | } | ||||
1075 | } | ||||
1076 | |||||
1077 | void DecodeElementSection() { | ||||
1078 | uint32_t element_count = | ||||
1079 | consume_count("element count", FLAG_wasm_max_table_size); | ||||
1080 | |||||
1081 | for (uint32_t i = 0; i < element_count; ++i) { | ||||
1082 | WasmElemSegment segment = consume_element_segment_header(); | ||||
1083 | if (failed()) return; | ||||
1084 | DCHECK_NE(segment.type, kWasmBottom)((void) 0); | ||||
1085 | |||||
1086 | uint32_t num_elem = | ||||
1087 | consume_count("number of elements", max_table_init_entries()); | ||||
1088 | |||||
1089 | for (uint32_t j = 0; j < num_elem; j++) { | ||||
1090 | ConstantExpression entry = | ||||
1091 | segment.element_type == WasmElemSegment::kExpressionElements | ||||
1092 | ? consume_init_expr(module_.get(), segment.type) | ||||
1093 | : ConstantExpression::RefFunc( | ||||
1094 | consume_element_func_index(segment.type)); | ||||
1095 | if (failed()) return; | ||||
1096 | segment.entries.push_back(entry); | ||||
1097 | } | ||||
1098 | module_->elem_segments.push_back(std::move(segment)); | ||||
1099 | } | ||||
1100 | } | ||||
1101 | |||||
1102 | void DecodeCodeSection(bool verify_functions) { | ||||
1103 | StartCodeSection(); | ||||
1104 | uint32_t code_section_start = pc_offset(); | ||||
1105 | uint32_t functions_count = consume_u32v("functions count"); | ||||
1106 | CheckFunctionsCount(functions_count, code_section_start); | ||||
1107 | for (uint32_t i = 0; ok() && i < functions_count; ++i) { | ||||
1108 | const byte* pos = pc(); | ||||
1109 | uint32_t size = consume_u32v("body size"); | ||||
1110 | if (size > kV8MaxWasmFunctionSize) { | ||||
1111 | errorf(pos, "size %u > maximum function size %zu", size, | ||||
1112 | kV8MaxWasmFunctionSize); | ||||
1113 | return; | ||||
1114 | } | ||||
1115 | uint32_t offset = pc_offset(); | ||||
1116 | consume_bytes(size, "function body"); | ||||
1117 | if (failed()) break; | ||||
1118 | DecodeFunctionBody(i, size, offset, verify_functions); | ||||
1119 | } | ||||
1120 | DCHECK_GE(pc_offset(), code_section_start)((void) 0); | ||||
1121 | set_code_section(code_section_start, pc_offset() - code_section_start); | ||||
1122 | } | ||||
1123 | |||||
1124 | void StartCodeSection() { | ||||
1125 | if (ok()) { | ||||
1126 | // Make sure global offset were calculated before they get accessed during | ||||
1127 | // function compilation. | ||||
1128 | CalculateGlobalOffsets(module_.get()); | ||||
1129 | } | ||||
1130 | } | ||||
1131 | |||||
1132 | bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset) { | ||||
1133 | if (functions_count != module_->num_declared_functions) { | ||||
1134 | errorf(error_offset, "function body count %u mismatch (%u expected)", | ||||
1135 | functions_count, module_->num_declared_functions); | ||||
1136 | return false; | ||||
1137 | } | ||||
1138 | return true; | ||||
1139 | } | ||||
1140 | |||||
1141 | void DecodeFunctionBody(uint32_t index, uint32_t length, uint32_t offset, | ||||
1142 | bool verify_functions) { | ||||
1143 | WasmFunction* function = | ||||
1144 | &module_->functions[index + module_->num_imported_functions]; | ||||
1145 | function->code = {offset, length}; | ||||
1146 | if (verify_functions) { | ||||
1147 | ModuleWireBytes bytes(module_start_, module_end_); | ||||
1148 | VerifyFunctionBody(module_->signature_zone->allocator(), | ||||
1149 | index + module_->num_imported_functions, bytes, | ||||
1150 | module_.get(), function); | ||||
1151 | } | ||||
1152 | } | ||||
1153 | |||||
1154 | bool CheckDataSegmentsCount(uint32_t data_segments_count) { | ||||
1155 | if (has_seen_unordered_section(kDataCountSectionCode) && | ||||
1156 | data_segments_count != module_->num_declared_data_segments) { | ||||
1157 | errorf(pc(), "data segments count %u mismatch (%u expected)", | ||||
1158 | data_segments_count, module_->num_declared_data_segments); | ||||
1159 | return false; | ||||
1160 | } | ||||
1161 | return true; | ||||
1162 | } | ||||
1163 | |||||
1164 | void DecodeDataSection() { | ||||
1165 | uint32_t data_segments_count = | ||||
1166 | consume_count("data segments count", kV8MaxWasmDataSegments); | ||||
1167 | if (!CheckDataSegmentsCount(data_segments_count)) return; | ||||
1168 | |||||
1169 | module_->data_segments.reserve(data_segments_count); | ||||
1170 | for (uint32_t i = 0; ok() && i < data_segments_count; ++i) { | ||||
1171 | const byte* pos = pc(); | ||||
1172 | TRACE("DecodeDataSegment[%d] module+%d\n", i, | ||||
1173 | static_cast<int>(pc_ - start_)); | ||||
1174 | |||||
1175 | bool is_active; | ||||
1176 | uint32_t memory_index; | ||||
1177 | ConstantExpression dest_addr; | ||||
1178 | consume_data_segment_header(&is_active, &memory_index, &dest_addr); | ||||
1179 | if (failed()) break; | ||||
1180 | |||||
1181 | if (is_active) { | ||||
1182 | if (!module_->has_memory) { | ||||
1183 | error("cannot load data without memory"); | ||||
1184 | break; | ||||
1185 | } | ||||
1186 | if (memory_index != 0) { | ||||
1187 | errorf(pos, "illegal memory index %u != 0", memory_index); | ||||
1188 | break; | ||||
1189 | } | ||||
1190 | } | ||||
1191 | |||||
1192 | uint32_t source_length = consume_u32v("source size"); | ||||
1193 | uint32_t source_offset = pc_offset(); | ||||
1194 | |||||
1195 | if (is_active) { | ||||
1196 | module_->data_segments.emplace_back(std::move(dest_addr)); | ||||
1197 | } else { | ||||
1198 | module_->data_segments.emplace_back(); | ||||
1199 | } | ||||
1200 | |||||
1201 | WasmDataSegment* segment = &module_->data_segments.back(); | ||||
1202 | |||||
1203 | consume_bytes(source_length, "segment data"); | ||||
1204 | if (failed()) break; | ||||
1205 | |||||
1206 | segment->source = {source_offset, source_length}; | ||||
1207 | } | ||||
1208 | } | ||||
1209 | |||||
1210 | void DecodeNameSection() { | ||||
1211 | // TODO(titzer): find a way to report name errors as warnings. | ||||
1212 | // Ignore all but the first occurrence of name section. | ||||
1213 | if (!has_seen_unordered_section(kNameSectionCode)) { | ||||
1214 | set_seen_unordered_section(kNameSectionCode); | ||||
1215 | // Use an inner decoder so that errors don't fail the outer decoder. | ||||
1216 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
1217 | // Decode all name subsections. | ||||
1218 | // Be lenient with their order. | ||||
1219 | while (inner.ok() && inner.more()) { | ||||
1220 | uint8_t name_type = inner.consume_u8("name type"); | ||||
1221 | if (name_type & 0x80) inner.error("name type if not varuint7"); | ||||
1222 | |||||
1223 | uint32_t name_payload_len = inner.consume_u32v("name payload length"); | ||||
1224 | if (!inner.checkAvailable(name_payload_len)) break; | ||||
1225 | |||||
1226 | // Decode module name, ignore the rest. | ||||
1227 | // Function and local names will be decoded when needed. | ||||
1228 | if (name_type == NameSectionKindCode::kModuleCode) { | ||||
1229 | WireBytesRef name = consume_string(&inner, false, "module name"); | ||||
1230 | if (inner.ok() && validate_utf8(&inner, name)) { | ||||
1231 | module_->name = name; | ||||
1232 | } | ||||
1233 | } else { | ||||
1234 | inner.consume_bytes(name_payload_len, "name subsection payload"); | ||||
1235 | } | ||||
1236 | } | ||||
1237 | } | ||||
1238 | // Skip the whole names section in the outer decoder. | ||||
1239 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
1240 | } | ||||
1241 | |||||
1242 | void DecodeSourceMappingURLSection() { | ||||
1243 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
1244 | WireBytesRef url = wasm::consume_string(&inner, true, "module name"); | ||||
1245 | if (inner.ok() && | ||||
1246 | module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) { | ||||
1247 | module_->debug_symbols = {WasmDebugSymbols::Type::SourceMap, url}; | ||||
1248 | } | ||||
1249 | set_seen_unordered_section(kSourceMappingURLSectionCode); | ||||
1250 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
1251 | } | ||||
1252 | |||||
1253 | void DecodeExternalDebugInfoSection() { | ||||
1254 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
1255 | WireBytesRef url = | ||||
1256 | wasm::consume_string(&inner, true, "external symbol file"); | ||||
1257 | // If there is an explicit source map, prefer it over DWARF info. | ||||
1258 | if (inner.ok() && | ||||
1259 | module_->debug_symbols.type != WasmDebugSymbols::Type::SourceMap) { | ||||
1260 | module_->debug_symbols = {WasmDebugSymbols::Type::ExternalDWARF, url}; | ||||
1261 | set_seen_unordered_section(kExternalDebugInfoSectionCode); | ||||
1262 | } | ||||
1263 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
1264 | } | ||||
1265 | |||||
1266 | void DecodeCompilationHintsSection() { | ||||
1267 | TRACE("DecodeCompilationHints module+%d\n", static_cast<int>(pc_ - start_)); | ||||
1268 | |||||
1269 | // TODO(frgossen): Find a way to report compilation hint errors as warnings. | ||||
1270 | // All except first occurrence after function section and before code | ||||
1271 | // section are ignored. | ||||
1272 | const bool before_function_section = | ||||
1273 | next_ordered_section_ <= kFunctionSectionCode; | ||||
1274 | const bool after_code_section = next_ordered_section_ > kCodeSectionCode; | ||||
1275 | if (before_function_section || after_code_section || | ||||
1276 | has_seen_unordered_section(kCompilationHintsSectionCode)) { | ||||
1277 | return; | ||||
1278 | } | ||||
1279 | set_seen_unordered_section(kCompilationHintsSectionCode); | ||||
1280 | |||||
1281 | // TODO(frgossen) Propagate errors to outer decoder in experimental phase. | ||||
1282 | // We should use an inner decoder later and propagate its errors as | ||||
1283 | // warnings. | ||||
1284 | Decoder& decoder = *this; | ||||
1285 | // Decoder decoder(start_, pc_, end_, buffer_offset_); | ||||
1286 | |||||
1287 | // Ensure exactly one compilation hint per function. | ||||
1288 | uint32_t hint_count = decoder.consume_u32v("compilation hint count"); | ||||
1289 | if (hint_count != module_->num_declared_functions) { | ||||
1290 | decoder.errorf(decoder.pc(), "Expected %u compilation hints (%u found)", | ||||
1291 | module_->num_declared_functions, hint_count); | ||||
1292 | } | ||||
1293 | |||||
1294 | // Decode sequence of compilation hints. | ||||
1295 | if (decoder.ok()) { | ||||
1296 | module_->compilation_hints.reserve(hint_count); | ||||
1297 | } | ||||
1298 | for (uint32_t i = 0; decoder.ok() && i < hint_count; i++) { | ||||
1299 | TRACE("DecodeCompilationHints[%d] module+%d\n", i, | ||||
1300 | static_cast<int>(pc_ - start_)); | ||||
1301 | |||||
1302 | // Compilation hints are encoded in one byte each. | ||||
1303 | // +-------+----------+---------------+----------+ | ||||
1304 | // | 2 bit | 2 bit | 2 bit | 2 bit | | ||||
1305 | // | ... | Top tier | Baseline tier | Strategy | | ||||
1306 | // +-------+----------+---------------+----------+ | ||||
1307 | uint8_t hint_byte = decoder.consume_u8("compilation hint"); | ||||
1308 | if (!decoder.ok()) break; | ||||
1309 | |||||
1310 | // Validate the hint_byte. | ||||
1311 | // For the compilation strategy, all 2-bit values are valid. For the tier, | ||||
1312 | // only 0x0, 0x1, and 0x2 are allowed. | ||||
1313 | static_assert( | ||||
1314 | static_cast<int>(WasmCompilationHintTier::kDefault) == 0 && | ||||
1315 | static_cast<int>(WasmCompilationHintTier::kBaseline) == 1 && | ||||
1316 | static_cast<int>(WasmCompilationHintTier::kOptimized) == 2, | ||||
1317 | "The check below assumes that 0x03 is the only invalid 2-bit number " | ||||
1318 | "for a compilation tier"); | ||||
1319 | if (((hint_byte >> 2) & 0x03) == 0x03 || | ||||
1320 | ((hint_byte >> 4) & 0x03) == 0x03) { | ||||
1321 | decoder.errorf(decoder.pc(), | ||||
1322 | "Invalid compilation hint %#04x (invalid tier 0x03)", | ||||
1323 | hint_byte); | ||||
1324 | break; | ||||
1325 | } | ||||
1326 | |||||
1327 | // Decode compilation hint. | ||||
1328 | WasmCompilationHint hint; | ||||
1329 | hint.strategy = | ||||
1330 | static_cast<WasmCompilationHintStrategy>(hint_byte & 0x03); | ||||
1331 | hint.baseline_tier = | ||||
1332 | static_cast<WasmCompilationHintTier>((hint_byte >> 2) & 0x03); | ||||
1333 | hint.top_tier = | ||||
1334 | static_cast<WasmCompilationHintTier>((hint_byte >> 4) & 0x03); | ||||
1335 | |||||
1336 | // Ensure that the top tier never downgrades a compilation result. If | ||||
1337 | // baseline and top tier are the same compilation will be invoked only | ||||
1338 | // once. | ||||
1339 | if (hint.top_tier < hint.baseline_tier && | ||||
1340 | hint.top_tier != WasmCompilationHintTier::kDefault) { | ||||
1341 | decoder.errorf(decoder.pc(), | ||||
1342 | "Invalid compilation hint %#04x (forbidden downgrade)", | ||||
1343 | hint_byte); | ||||
1344 | } | ||||
1345 | |||||
1346 | // Happily accept compilation hint. | ||||
1347 | if (decoder.ok()) { | ||||
1348 | module_->compilation_hints.push_back(std::move(hint)); | ||||
1349 | } | ||||
1350 | } | ||||
1351 | |||||
1352 | // If section was invalid reset compilation hints. | ||||
1353 | if (decoder.failed()) { | ||||
1354 | module_->compilation_hints.clear(); | ||||
1355 | } | ||||
1356 | |||||
1357 | // @TODO(frgossen) Skip the whole compilation hints section in the outer | ||||
1358 | // decoder if inner decoder was used. | ||||
1359 | // consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
1360 | } | ||||
1361 | |||||
1362 | void DecodeBranchHintsSection() { | ||||
1363 | TRACE("DecodeBranchHints module+%d\n", static_cast<int>(pc_ - start_)); | ||||
1364 | if (!has_seen_unordered_section(kBranchHintsSectionCode)) { | ||||
1365 | set_seen_unordered_section(kBranchHintsSectionCode); | ||||
1366 | // Use an inner decoder so that errors don't fail the outer decoder. | ||||
1367 | Decoder inner(start_, pc_, end_, buffer_offset_); | ||||
1368 | BranchHintInfo branch_hints; | ||||
1369 | |||||
1370 | uint32_t func_count = inner.consume_u32v("number of functions"); | ||||
1371 | // Keep track of the previous function index to validate the ordering | ||||
1372 | int64_t last_func_idx = -1; | ||||
1373 | for (uint32_t i = 0; i < func_count; i++) { | ||||
1374 | uint32_t func_idx = inner.consume_u32v("function index"); | ||||
1375 | if (int64_t(func_idx) <= last_func_idx) { | ||||
1376 | inner.errorf("Invalid function index: %d", func_idx); | ||||
1377 | break; | ||||
1378 | } | ||||
1379 | last_func_idx = func_idx; | ||||
1380 | uint32_t num_hints = inner.consume_u32v("number of hints"); | ||||
1381 | BranchHintMap func_branch_hints; | ||||
1382 | TRACE("DecodeBranchHints[%d] module+%d\n", func_idx, | ||||
1383 | static_cast<int>(inner.pc() - inner.start())); | ||||
1384 | // Keep track of the previous branch offset to validate the ordering | ||||
1385 | int64_t last_br_off = -1; | ||||
1386 | for (uint32_t j = 0; j < num_hints; ++j) { | ||||
1387 | uint32_t br_off = inner.consume_u32v("branch instruction offset"); | ||||
1388 | if (int64_t(br_off) <= last_br_off) { | ||||
1389 | inner.errorf("Invalid branch offset: %d", br_off); | ||||
1390 | break; | ||||
1391 | } | ||||
1392 | last_br_off = br_off; | ||||
1393 | uint32_t data_size = inner.consume_u32v("data size"); | ||||
1394 | if (data_size != 1) { | ||||
1395 | inner.errorf("Invalid data size: %#x. Expected 1.", data_size); | ||||
1396 | break; | ||||
1397 | } | ||||
1398 | uint32_t br_dir = inner.consume_u8("branch direction"); | ||||
1399 | TRACE("DecodeBranchHints[%d][%d] module+%d\n", func_idx, br_off, | ||||
1400 | static_cast<int>(inner.pc() - inner.start())); | ||||
1401 | WasmBranchHint hint; | ||||
1402 | switch (br_dir) { | ||||
1403 | case 0: | ||||
1404 | hint = WasmBranchHint::kUnlikely; | ||||
1405 | break; | ||||
1406 | case 1: | ||||
1407 | hint = WasmBranchHint::kLikely; | ||||
1408 | break; | ||||
1409 | default: | ||||
1410 | hint = WasmBranchHint::kNoHint; | ||||
1411 | inner.errorf(inner.pc(), "Invalid branch hint %#x", br_dir); | ||||
1412 | break; | ||||
1413 | } | ||||
1414 | if (!inner.ok()) { | ||||
1415 | break; | ||||
1416 | } | ||||
1417 | func_branch_hints.insert(br_off, hint); | ||||
1418 | } | ||||
1419 | if (!inner.ok()) { | ||||
1420 | break; | ||||
1421 | } | ||||
1422 | branch_hints.emplace(func_idx, std::move(func_branch_hints)); | ||||
1423 | } | ||||
1424 | // Extra unexpected bytes are an error. | ||||
1425 | if (inner.more()) { | ||||
1426 | inner.errorf("Unexpected extra bytes: %d\n", | ||||
1427 | static_cast<int>(inner.pc() - inner.start())); | ||||
1428 | } | ||||
1429 | // If everything went well, accept the hints for the module. | ||||
1430 | if (inner.ok()) { | ||||
1431 | module_->branch_hints = std::move(branch_hints); | ||||
1432 | } | ||||
1433 | } | ||||
1434 | // Skip the whole branch hints section in the outer decoder. | ||||
1435 | consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); | ||||
1436 | } | ||||
1437 | |||||
1438 | void DecodeDataCountSection() { | ||||
1439 | module_->num_declared_data_segments = | ||||
1440 | consume_count("data segments count", kV8MaxWasmDataSegments); | ||||
1441 | } | ||||
1442 | |||||
1443 | void DecodeTagSection() { | ||||
1444 | uint32_t tag_count = consume_count("tag count", kV8MaxWasmTags); | ||||
1445 | for (uint32_t i = 0; ok() && i < tag_count; ++i) { | ||||
1446 | TRACE("DecodeTag[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); | ||||
1447 | const WasmTagSig* tag_sig = nullptr; | ||||
1448 | consume_exception_attribute(); // Attribute ignored for now. | ||||
1449 | consume_tag_sig_index(module_.get(), &tag_sig); | ||||
1450 | module_->tags.emplace_back(tag_sig); | ||||
1451 | } | ||||
1452 | } | ||||
1453 | |||||
1454 | bool CheckMismatchedCounts() { | ||||
1455 | // The declared vs. defined function count is normally checked when | ||||
1456 | // decoding the code section, but we have to check it here too in case the | ||||
1457 | // code section is absent. | ||||
1458 | if (module_->num_declared_functions != 0) { | ||||
1459 | DCHECK_LT(module_->num_imported_functions, module_->functions.size())((void) 0); | ||||
1460 | // We know that the code section has been decoded if the first | ||||
1461 | // non-imported function has its code set. | ||||
1462 | if (!module_->functions[module_->num_imported_functions].code.is_set()) { | ||||
1463 | errorf(pc(), "function count is %u, but code section is absent", | ||||
1464 | module_->num_declared_functions); | ||||
1465 | return false; | ||||
1466 | } | ||||
1467 | } | ||||
1468 | // Perform a similar check for the DataCount and Data sections, where data | ||||
1469 | // segments are declared but the Data section is absent. | ||||
1470 | if (!CheckDataSegmentsCount( | ||||
1471 | static_cast<uint32_t>(module_->data_segments.size()))) { | ||||
1472 | return false; | ||||
1473 | } | ||||
1474 | return true; | ||||
1475 | } | ||||
1476 | |||||
1477 | ModuleResult FinishDecoding(bool verify_functions = true) { | ||||
1478 | if (ok() && CheckMismatchedCounts()) { | ||||
1479 | // We calculate the global offsets here, because there may not be a | ||||
1480 | // global section and code section that would have triggered the | ||||
1481 | // calculation before. Even without the globals section the calculation | ||||
1482 | // is needed because globals can also be defined in the import section. | ||||
1483 | CalculateGlobalOffsets(module_.get()); | ||||
1484 | } | ||||
1485 | |||||
1486 | ModuleResult result = toResult(std::move(module_)); | ||||
1487 | if (verify_functions && result.ok() && intermediate_error_.has_error()) { | ||||
1488 | // Copy error message and location. | ||||
1489 | return ModuleResult{std::move(intermediate_error_)}; | ||||
1490 | } | ||||
1491 | return result; | ||||
1492 | } | ||||
1493 | |||||
1494 | void set_code_section(uint32_t offset, uint32_t size) { | ||||
1495 | module_->code = {offset, size}; | ||||
1496 | } | ||||
1497 | |||||
1498 | // Decodes an entire module. | ||||
1499 | ModuleResult DecodeModule(Counters* counters, AccountingAllocator* allocator, | ||||
1500 | bool verify_functions = true) { | ||||
1501 | StartDecoding(counters, allocator); | ||||
1502 | uint32_t offset = 0; | ||||
1503 | base::Vector<const byte> orig_bytes(start(), end() - start()); | ||||
1504 | DecodeModuleHeader(base::VectorOf(start(), end() - start()), offset); | ||||
1505 | if (failed()) { | ||||
1506 | return FinishDecoding(verify_functions); | ||||
1507 | } | ||||
1508 | // Size of the module header. | ||||
1509 | offset += 8; | ||||
1510 | Decoder decoder(start_ + offset, end_, offset); | ||||
1511 | |||||
1512 | WasmSectionIterator section_iter(&decoder); | ||||
1513 | |||||
1514 | while (ok()) { | ||||
1515 | // Shift the offset by the section header length | ||||
1516 | offset += section_iter.payload_start() - section_iter.section_start(); | ||||
1517 | if (section_iter.section_code() != SectionCode::kUnknownSectionCode) { | ||||
1518 | DecodeSection(section_iter.section_code(), section_iter.payload(), | ||||
1519 | offset, verify_functions); | ||||
1520 | } | ||||
1521 | // Shift the offset by the remaining section payload | ||||
1522 | offset += section_iter.payload_length(); | ||||
1523 | if (!section_iter.more()) break; | ||||
1524 | section_iter.advance(true); | ||||
1525 | } | ||||
1526 | |||||
1527 | if (FLAG_dump_wasm_module) DumpModule(orig_bytes); | ||||
1528 | |||||
1529 | if (decoder.failed()) { | ||||
1530 | return decoder.toResult<std::unique_ptr<WasmModule>>(nullptr); | ||||
1531 | } | ||||
1532 | |||||
1533 | return FinishDecoding(verify_functions); | ||||
1534 | } | ||||
1535 | |||||
1536 | // Decodes a single anonymous function starting at {start_}. | ||||
1537 | FunctionResult DecodeSingleFunction(Zone* zone, | ||||
1538 | const ModuleWireBytes& wire_bytes, | ||||
1539 | const WasmModule* module, | ||||
1540 | std::unique_ptr<WasmFunction> function) { | ||||
1541 | pc_ = start_; | ||||
1542 | expect_u8("type form", kWasmFunctionTypeCode); | ||||
1543 | if (!ok()) return FunctionResult{std::move(intermediate_error_)}; | ||||
1544 | function->sig = consume_sig(zone); | ||||
1545 | function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)}; | ||||
1546 | |||||
1547 | if (ok()) | ||||
1548 | VerifyFunctionBody(zone->allocator(), 0, wire_bytes, module, | ||||
1549 | function.get()); | ||||
1550 | |||||
1551 | if (intermediate_error_.has_error()) { | ||||
1552 | return FunctionResult{std::move(intermediate_error_)}; | ||||
1553 | } | ||||
1554 | |||||
1555 | return FunctionResult(std::move(function)); | ||||
1556 | } | ||||
1557 | |||||
1558 | // Decodes a single function signature at {start}. | ||||
1559 | const FunctionSig* DecodeFunctionSignature(Zone* zone, const byte* start) { | ||||
1560 | pc_ = start; | ||||
1561 | if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr; | ||||
1562 | const FunctionSig* result = consume_sig(zone); | ||||
1563 | return ok() ? result : nullptr; | ||||
1564 | } | ||||
1565 | |||||
1566 | ConstantExpression DecodeInitExprForTesting(ValueType expected) { | ||||
1567 | return consume_init_expr(module_.get(), expected); | ||||
1568 | } | ||||
1569 | |||||
1570 | const std::shared_ptr<WasmModule>& shared_module() const { return module_; } | ||||
1571 | |||||
1572 | Counters* GetCounters() const { | ||||
1573 | DCHECK_NOT_NULL(counters_)((void) 0); | ||||
1574 | return counters_; | ||||
1575 | } | ||||
1576 | |||||
1577 | void SetCounters(Counters* counters) { | ||||
1578 | DCHECK_NULL(counters_)((void) 0); | ||||
1579 | counters_ = counters; | ||||
1580 | } | ||||
1581 | |||||
1582 | private: | ||||
1583 | const WasmFeatures enabled_features_; | ||||
1584 | std::shared_ptr<WasmModule> module_; | ||||
1585 | const byte* module_start_ = nullptr; | ||||
1586 | const byte* module_end_ = nullptr; | ||||
1587 | Counters* counters_ = nullptr; | ||||
1588 | // The type section is the first section in a module. | ||||
1589 | uint8_t next_ordered_section_ = kFirstSectionInModule; | ||||
1590 | // We store next_ordered_section_ as uint8_t instead of SectionCode so that | ||||
1591 | // we can increment it. This static_assert should make sure that SectionCode | ||||
1592 | // does not get bigger than uint8_t accidentially. | ||||
1593 | static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) == | ||||
1594 | sizeof(SectionCode), | ||||
1595 | "type mismatch"); | ||||
1596 | uint32_t seen_unordered_sections_ = 0; | ||||
1597 | static_assert(kBitsPerByte * | ||||
1598 | sizeof(ModuleDecoderImpl::seen_unordered_sections_) > | ||||
1599 | kLastKnownModuleSection, | ||||
1600 | "not enough bits"); | ||||
1601 | WasmError intermediate_error_; | ||||
1602 | ModuleOrigin origin_; | ||||
1603 | AccountingAllocator allocator_; | ||||
1604 | Zone init_expr_zone_{&allocator_, "initializer expression zone"}; | ||||
1605 | |||||
1606 | bool has_seen_unordered_section(SectionCode section_code) { | ||||
1607 | return seen_unordered_sections_ & (1 << section_code); | ||||
1608 | } | ||||
1609 | |||||
1610 | void set_seen_unordered_section(SectionCode section_code) { | ||||
1611 | seen_unordered_sections_ |= 1 << section_code; | ||||
1612 | } | ||||
1613 | |||||
1614 | uint32_t off(const byte* ptr) { | ||||
1615 | return static_cast<uint32_t>(ptr - start_) + buffer_offset_; | ||||
1616 | } | ||||
1617 | |||||
1618 | bool AddMemory(WasmModule* module) { | ||||
1619 | if (module->has_memory) { | ||||
1620 | error("At most one memory is supported"); | ||||
1621 | return false; | ||||
1622 | } else { | ||||
1623 | module->has_memory = true; | ||||
1624 | return true; | ||||
1625 | } | ||||
1626 | } | ||||
1627 | |||||
1628 | // Calculate individual global offsets and total size of globals table. This | ||||
1629 | // function should be called after all globals have been defined, which is | ||||
1630 | // after the import section and the global section, but before the global | ||||
1631 | // offsets are accessed, e.g. by the function compilers. The moment when this | ||||
1632 | // function should be called is not well-defined, as the global section may | ||||
1633 | // not exist. Therefore this function is called multiple times. | ||||
1634 | void CalculateGlobalOffsets(WasmModule* module) { | ||||
1635 | if (module->globals.empty() || module->untagged_globals_buffer_size != 0 || | ||||
1636 | module->tagged_globals_buffer_size != 0) { | ||||
1637 | // This function has already been executed before, so we don't have to | ||||
1638 | // execute it again. | ||||
1639 | return; | ||||
1640 | } | ||||
1641 | uint32_t untagged_offset = 0; | ||||
1642 | uint32_t tagged_offset = 0; | ||||
1643 | uint32_t num_imported_mutable_globals = 0; | ||||
1644 | for (WasmGlobal& global : module->globals) { | ||||
1645 | if (global.mutability && global.imported) { | ||||
1646 | global.index = num_imported_mutable_globals++; | ||||
1647 | } else if (global.type.is_reference()) { | ||||
1648 | global.offset = tagged_offset; | ||||
1649 | // All entries in the tagged_globals_buffer have size 1. | ||||
1650 | tagged_offset++; | ||||
1651 | } else { | ||||
1652 | int size = global.type.value_kind_size(); | ||||
1653 | untagged_offset = (untagged_offset + size - 1) & ~(size - 1); // align | ||||
1654 | global.offset = untagged_offset; | ||||
1655 | untagged_offset += size; | ||||
1656 | } | ||||
1657 | } | ||||
1658 | module->untagged_globals_buffer_size = untagged_offset; | ||||
1659 | module->tagged_globals_buffer_size = tagged_offset; | ||||
1660 | } | ||||
1661 | |||||
1662 | // Verifies the body (code) of a given function. | ||||
1663 | void VerifyFunctionBody(AccountingAllocator* allocator, uint32_t func_num, | ||||
1664 | const ModuleWireBytes& wire_bytes, | ||||
1665 | const WasmModule* module, WasmFunction* function) { | ||||
1666 | WasmFunctionName func_name(function, | ||||
1667 | wire_bytes.GetNameOrNull(function, module)); | ||||
1668 | if (FLAG_trace_wasm_decoder) { | ||||
1669 | StdoutStream{} << "Verifying wasm function " << func_name << std::endl; | ||||
1670 | } | ||||
1671 | FunctionBody body = { | ||||
1672 | function->sig, function->code.offset(), | ||||
1673 | start_ + GetBufferRelativeOffset(function->code.offset()), | ||||
1674 | start_ + GetBufferRelativeOffset(function->code.end_offset())}; | ||||
1675 | |||||
1676 | WasmFeatures unused_detected_features = WasmFeatures::None(); | ||||
1677 | DecodeResult result = VerifyWasmCode(allocator, enabled_features_, module, | ||||
1678 | &unused_detected_features, body); | ||||
1679 | |||||
1680 | // If the decode failed and this is the first error, set error code and | ||||
1681 | // location. | ||||
1682 | if (result.failed() && intermediate_error_.empty()) { | ||||
1683 | // Wrap the error message from the function decoder. | ||||
1684 | std::ostringstream error_msg; | ||||
1685 | error_msg << "in function " << func_name << ": " | ||||
1686 | << result.error().message(); | ||||
1687 | intermediate_error_ = WasmError{result.error().offset(), error_msg.str()}; | ||||
1688 | } | ||||
1689 | } | ||||
1690 | |||||
1691 | uint32_t consume_sig_index(WasmModule* module, const FunctionSig** sig) { | ||||
1692 | const byte* pos = pc_; | ||||
1693 | uint32_t sig_index = consume_u32v("signature index"); | ||||
1694 | if (!module->has_signature(sig_index)) { | ||||
1695 | errorf(pos, "signature index %u out of bounds (%d signatures)", sig_index, | ||||
1696 | static_cast<int>(module->types.size())); | ||||
1697 | *sig = nullptr; | ||||
1698 | return 0; | ||||
1699 | } | ||||
1700 | *sig = module->signature(sig_index); | ||||
1701 | return sig_index; | ||||
1702 | } | ||||
1703 | |||||
1704 | uint32_t consume_tag_sig_index(WasmModule* module, const FunctionSig** sig) { | ||||
1705 | const byte* pos = pc_; | ||||
1706 | uint32_t sig_index = consume_sig_index(module, sig); | ||||
1707 | if (*sig && (*sig)->return_count() != 0) { | ||||
1708 | errorf(pos, "tag signature %u has non-void return", sig_index); | ||||
1709 | *sig = nullptr; | ||||
1710 | return 0; | ||||
1711 | } | ||||
1712 | return sig_index; | ||||
1713 | } | ||||
1714 | |||||
1715 | uint32_t consume_count(const char* name, size_t maximum) { | ||||
1716 | const byte* p = pc_; | ||||
1717 | uint32_t count = consume_u32v(name); | ||||
1718 | if (count > maximum) { | ||||
1719 | errorf(p, "%s of %u exceeds internal limit of %zu", name, count, maximum); | ||||
1720 | return static_cast<uint32_t>(maximum); | ||||
1721 | } | ||||
1722 | return count; | ||||
1723 | } | ||||
1724 | |||||
1725 | uint32_t consume_func_index(WasmModule* module, WasmFunction** func, | ||||
1726 | const char* name) { | ||||
1727 | return consume_index(name, &module->functions, func); | ||||
1728 | } | ||||
1729 | |||||
1730 | uint32_t consume_global_index(WasmModule* module, WasmGlobal** global) { | ||||
1731 | return consume_index("global index", &module->globals, global); | ||||
1732 | } | ||||
1733 | |||||
1734 | uint32_t consume_table_index(WasmModule* module, WasmTable** table) { | ||||
1735 | return consume_index("table index", &module->tables, table); | ||||
1736 | } | ||||
1737 | |||||
1738 | uint32_t consume_tag_index(WasmModule* module, WasmTag** tag) { | ||||
1739 | return consume_index("tag index", &module->tags, tag); | ||||
1740 | } | ||||
1741 | |||||
1742 | template <typename T> | ||||
1743 | uint32_t consume_index(const char* name, std::vector<T>* vector, T** ptr) { | ||||
1744 | const byte* pos = pc_; | ||||
1745 | uint32_t index = consume_u32v(name); | ||||
1746 | if (index >= vector->size()) { | ||||
1747 | errorf(pos, "%s %u out of bounds (%d entr%s)", name, index, | ||||
1748 | static_cast<int>(vector->size()), | ||||
1749 | vector->size() == 1 ? "y" : "ies"); | ||||
1750 | *ptr = nullptr; | ||||
1751 | return 0; | ||||
1752 | } | ||||
1753 | *ptr = &(*vector)[index]; | ||||
1754 | return index; | ||||
1755 | } | ||||
1756 | |||||
1757 | uint8_t validate_table_flags(const char* name) { | ||||
1758 | uint8_t flags = consume_u8("table limits flags"); | ||||
1759 | STATIC_ASSERT(kNoMaximum < kWithMaximum)static_assert(kNoMaximum < kWithMaximum, "kNoMaximum < kWithMaximum" ); | ||||
1760 | if (V8_UNLIKELY(flags > kWithMaximum)(__builtin_expect(!!(flags > kWithMaximum), 0))) { | ||||
1761 | errorf(pc() - 1, "invalid %s limits flags", name); | ||||
1762 | } | ||||
1763 | return flags; | ||||
1764 | } | ||||
1765 | |||||
1766 | uint8_t validate_memory_flags(bool* has_shared_memory, bool* is_memory64) { | ||||
1767 | uint8_t flags = consume_u8("memory limits flags"); | ||||
1768 | *has_shared_memory = false; | ||||
1769 | switch (flags) { | ||||
1770 | case kNoMaximum: | ||||
1771 | case kWithMaximum: | ||||
1772 | break; | ||||
1773 | case kSharedNoMaximum: | ||||
1774 | case kSharedWithMaximum: | ||||
1775 | if (!enabled_features_.has_threads()) { | ||||
1776 | errorf(pc() - 1, | ||||
1777 | "invalid memory limits flags 0x%x (enable via " | ||||
1778 | "--experimental-wasm-threads)", | ||||
1779 | flags); | ||||
1780 | } | ||||
1781 | *has_shared_memory = true; | ||||
1782 | // V8 does not support shared memory without a maximum. | ||||
1783 | if (flags == kSharedNoMaximum) { | ||||
1784 | errorf(pc() - 1, | ||||
1785 | "memory limits flags must have maximum defined if shared is " | ||||
1786 | "true"); | ||||
1787 | } | ||||
1788 | break; | ||||
1789 | case kMemory64NoMaximum: | ||||
1790 | case kMemory64WithMaximum: | ||||
1791 | if (!enabled_features_.has_memory64()) { | ||||
1792 | errorf(pc() - 1, | ||||
1793 | "invalid memory limits flags 0x%x (enable via " | ||||
1794 | "--experimental-wasm-memory64)", | ||||
1795 | flags); | ||||
1796 | } | ||||
1797 | *is_memory64 = true; | ||||
1798 | break; | ||||
1799 | default: | ||||
1800 | errorf(pc() - 1, "invalid memory limits flags 0x%x", flags); | ||||
1801 | break; | ||||
1802 | } | ||||
1803 | return flags; | ||||
1804 | } | ||||
1805 | |||||
1806 | void consume_resizable_limits(const char* name, const char* units, | ||||
1807 | uint32_t max_initial, uint32_t* initial, | ||||
1808 | bool* has_max, uint32_t max_maximum, | ||||
1809 | uint32_t* maximum, uint8_t flags) { | ||||
1810 | const byte* pos = pc(); | ||||
1811 | // For memory64 we need to read the numbers as LEB-encoded 64-bit unsigned | ||||
1812 | // integer. All V8 limits are still within uint32_t range though. | ||||
1813 | const bool is_memory64 = | ||||
1814 | flags == kMemory64NoMaximum || flags == kMemory64WithMaximum; | ||||
1815 | uint64_t initial_64 = is_memory64 ? consume_u64v("initial size") | ||||
1816 | : consume_u32v("initial size"); | ||||
1817 | if (initial_64 > max_initial) { | ||||
1818 | errorf(pos, | ||||
1819 | "initial %s size (%" PRIu64"l" "u" | ||||
1820 | " %s) is larger than implementation limit (%u)", | ||||
1821 | name, initial_64, units, max_initial); | ||||
1822 | } | ||||
1823 | *initial = static_cast<uint32_t>(initial_64); | ||||
1824 | if (flags & 1) { | ||||
1825 | *has_max = true; | ||||
1826 | pos = pc(); | ||||
1827 | uint64_t maximum_64 = is_memory64 ? consume_u64v("maximum size") | ||||
1828 | : consume_u32v("maximum size"); | ||||
1829 | if (maximum_64 > max_maximum) { | ||||
1830 | errorf(pos, | ||||
1831 | "maximum %s size (%" PRIu64"l" "u" | ||||
1832 | " %s) is larger than implementation limit (%u)", | ||||
1833 | name, maximum_64, units, max_maximum); | ||||
1834 | } | ||||
1835 | if (maximum_64 < *initial) { | ||||
1836 | errorf(pos, | ||||
1837 | "maximum %s size (%" PRIu64"l" "u" " %s) is less than initial (%u %s)", | ||||
1838 | name, maximum_64, units, *initial, units); | ||||
1839 | } | ||||
1840 | *maximum = static_cast<uint32_t>(maximum_64); | ||||
1841 | } else { | ||||
1842 | *has_max = false; | ||||
1843 | *maximum = max_initial; | ||||
1844 | } | ||||
1845 | } | ||||
1846 | |||||
1847 | // Consumes a byte, and emits an error if it does not equal {expected}. | ||||
1848 | bool expect_u8(const char* name, uint8_t expected) { | ||||
1849 | const byte* pos = pc(); | ||||
1850 | uint8_t value = consume_u8(name); | ||||
1851 | if (value != expected) { | ||||
1852 | errorf(pos, "expected %s 0x%02x, got 0x%02x", name, expected, value); | ||||
1853 | return false; | ||||
1854 | } | ||||
1855 | return true; | ||||
1856 | } | ||||
1857 | |||||
1858 | ConstantExpression consume_init_expr(WasmModule* module, ValueType expected) { | ||||
1859 | uint32_t length; | ||||
1860 | |||||
1861 | // The error message mimics the one generated by the {WasmFullDecoder}. | ||||
1862 | #define TYPE_CHECK(found) \ | ||||
1863 | if (V8_UNLIKELY(!IsSubtypeOf(found, expected, module_.get()))(__builtin_expect(!!(!IsSubtypeOf(found, expected, module_.get ())), 0))) { \ | ||||
1864 | errorf(pc() + 1, \ | ||||
1865 | "type error in init. expression[0] (expected %s, got %s)", \ | ||||
1866 | expected.name().c_str(), found.name().c_str()); \ | ||||
1867 | return {}; \ | ||||
1868 | } | ||||
1869 | |||||
1870 | // To avoid initializing a {WasmFullDecoder} for the most common | ||||
1871 | // expressions, we replicate their decoding and validation here. The | ||||
1872 | // manually handled cases correspond to {ConstantExpression}'s kinds. | ||||
1873 | // We need to make sure to check that the expression ends in {kExprEnd}; | ||||
1874 | // otherwise, it is just the first operand of a composite expression, and we | ||||
1875 | // fall back to the default case. | ||||
1876 | if (!more()) { | ||||
1877 | error("Beyond end of code"); | ||||
1878 | return {}; | ||||
1879 | } | ||||
1880 | switch (static_cast<WasmOpcode>(*pc())) { | ||||
1881 | case kExprI32Const: { | ||||
1882 | int32_t value = | ||||
1883 | read_i32v<kFullValidation>(pc() + 1, &length, "i32.const"); | ||||
1884 | if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {}; | ||||
1885 | if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) { | ||||
1886 | TYPE_CHECK(kWasmI32) | ||||
1887 | consume_bytes(length + 2); | ||||
1888 | return ConstantExpression::I32Const(value); | ||||
1889 | } | ||||
1890 | break; | ||||
1891 | } | ||||
1892 | case kExprRefFunc: { | ||||
1893 | uint32_t index = | ||||
1894 | read_u32v<kFullValidation>(pc() + 1, &length, "ref.func"); | ||||
1895 | if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {}; | ||||
1896 | if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) { | ||||
1897 | if (V8_UNLIKELY(index >= module_->functions.size())(__builtin_expect(!!(index >= module_->functions.size() ), 0))) { | ||||
1898 | errorf(pc() + 1, "function index %u out of bounds", index); | ||||
1899 | return {}; | ||||
1900 | } | ||||
1901 | ValueType type = | ||||
1902 | enabled_features_.has_typed_funcref() | ||||
1903 | ? ValueType::Ref(module_->functions[index].sig_index, | ||||
1904 | kNonNullable) | ||||
1905 | : kWasmFuncRef; | ||||
1906 | TYPE_CHECK(type) | ||||
1907 | module_->functions[index].declared = true; | ||||
1908 | consume_bytes(length + 2); | ||||
1909 | return ConstantExpression::RefFunc(index); | ||||
1910 | } | ||||
1911 | break; | ||||
1912 | } | ||||
1913 | case kExprRefNull: { | ||||
1914 | HeapType type = value_type_reader::read_heap_type<kFullValidation>( | ||||
1915 | this, pc() + 1, &length, module_.get(), enabled_features_); | ||||
1916 | if (V8_UNLIKELY(failed())(__builtin_expect(!!(failed()), 0))) return {}; | ||||
1917 | if (V8_LIKELY(lookahead(1 + length, kExprEnd))(__builtin_expect(!!(lookahead(1 + length, kExprEnd)), 1))) { | ||||
1918 | TYPE_CHECK(ValueType::Ref(type, kNullable)) | ||||
1919 | consume_bytes(length + 2); | ||||
1920 | return ConstantExpression::RefNull(type.representation()); | ||||
1921 | } | ||||
1922 | break; | ||||
1923 | } | ||||
1924 | default: | ||||
1925 | break; | ||||
1926 | } | ||||
1927 | #undef TYPE_CHECK | ||||
1928 | |||||
1929 | auto sig = FixedSizeSignature<ValueType>::Returns(expected); | ||||
1930 | FunctionBody body(&sig, buffer_offset_, pc_, end_); | ||||
1931 | WasmFeatures detected; | ||||
1932 | WasmFullDecoder<Decoder::kFullValidation, InitExprInterface, | ||||
1933 | kInitExpression> | ||||
1934 | decoder(&init_expr_zone_, module, enabled_features_, &detected, body, | ||||
1935 | module); | ||||
1936 | |||||
1937 | uint32_t offset = this->pc_offset(); | ||||
1938 | |||||
1939 | decoder.DecodeFunctionBody(); | ||||
1940 | |||||
1941 | this->pc_ = decoder.end(); | ||||
1942 | |||||
1943 | if (decoder.failed()) { | ||||
1944 | error(decoder.error().offset(), decoder.error().message().c_str()); | ||||
1945 | return {}; | ||||
1946 | } | ||||
1947 | |||||
1948 | if (!decoder.interface().end_found()) { | ||||
1949 | error("Initializer expression is missing 'end'"); | ||||
1950 | return {}; | ||||
1951 | } | ||||
1952 | |||||
1953 | return ConstantExpression::WireBytes( | ||||
1954 | offset, static_cast<uint32_t>(decoder.end() - decoder.start())); | ||||
1955 | } | ||||
1956 | |||||
1957 | // Read a mutability flag | ||||
1958 | bool consume_mutability() { | ||||
1959 | byte val = consume_u8("mutability"); | ||||
1960 | if (val > 1) error(pc_ - 1, "invalid mutability"); | ||||
1961 | return val != 0; | ||||
1962 | } | ||||
1963 | |||||
1964 | ValueType consume_value_type() { | ||||
1965 | uint32_t type_length; | ||||
1966 | ValueType result = value_type_reader::read_value_type<kFullValidation>( | ||||
1967 | this, this->pc(), &type_length, module_.get(), | ||||
1968 | origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None()); | ||||
1969 | consume_bytes(type_length, "value type"); | ||||
1970 | return result; | ||||
1971 | } | ||||
1972 | |||||
1973 | HeapType consume_super_type() { | ||||
1974 | return value_type_reader::consume_heap_type(this, module_.get(), | ||||
1975 | enabled_features_); | ||||
1976 | } | ||||
1977 | |||||
1978 | ValueType consume_storage_type() { | ||||
1979 | uint8_t opcode = read_u8<kFullValidation>(this->pc()); | ||||
1980 | switch (opcode) { | ||||
1981 | case kI8Code: | ||||
1982 | consume_bytes(1, "i8"); | ||||
1983 | return kWasmI8; | ||||
1984 | case kI16Code: | ||||
1985 | consume_bytes(1, "i16"); | ||||
1986 | return kWasmI16; | ||||
1987 | default: | ||||
1988 | // It is not a packed type, so it has to be a value type. | ||||
1989 | return consume_value_type(); | ||||
1990 | } | ||||
1991 | } | ||||
1992 | |||||
1993 | // Reads a reference type for tables and element segment headers. | ||||
1994 | ValueType consume_reference_type() { | ||||
1995 | const byte* position = pc(); | ||||
1996 | ValueType result = consume_value_type(); | ||||
1997 | if (!result.is_reference()) { | ||||
1998 | error(position, "expected reference type"); | ||||
1999 | } | ||||
2000 | return result; | ||||
2001 | } | ||||
2002 | |||||
2003 | const FunctionSig* consume_sig(Zone* zone) { | ||||
2004 | // Parse parameter types. | ||||
2005 | uint32_t param_count = | ||||
2006 | consume_count("param count", kV8MaxWasmFunctionParams); | ||||
2007 | if (failed()) return nullptr; | ||||
2008 | std::vector<ValueType> params; | ||||
2009 | for (uint32_t i = 0; ok() && i < param_count; ++i) { | ||||
2010 | params.push_back(consume_value_type()); | ||||
2011 | } | ||||
2012 | std::vector<ValueType> returns; | ||||
2013 | |||||
2014 | // Parse return types. | ||||
2015 | uint32_t return_count = | ||||
2016 | consume_count("return count", kV8MaxWasmFunctionReturns); | ||||
2017 | if (failed()) return nullptr; | ||||
2018 | for (uint32_t i = 0; ok() && i < return_count; ++i) { | ||||
2019 | returns.push_back(consume_value_type()); | ||||
2020 | } | ||||
2021 | if (failed()) return nullptr; | ||||
2022 | |||||
2023 | // FunctionSig stores the return types first. | ||||
2024 | ValueType* buffer = zone->NewArray<ValueType>(param_count + return_count); | ||||
2025 | uint32_t b = 0; | ||||
2026 | for (uint32_t i = 0; i < return_count; ++i) buffer[b++] = returns[i]; | ||||
2027 | for (uint32_t i = 0; i < param_count; ++i) buffer[b++] = params[i]; | ||||
2028 | |||||
2029 | return zone->New<FunctionSig>(return_count, param_count, buffer); | ||||
2030 | } | ||||
2031 | |||||
2032 | const StructType* consume_struct(Zone* zone) { | ||||
2033 | uint32_t field_count = consume_count("field count", kV8MaxWasmStructFields); | ||||
2034 | if (failed()) return nullptr; | ||||
2035 | ValueType* fields = zone->NewArray<ValueType>(field_count); | ||||
2036 | bool* mutabilities = zone->NewArray<bool>(field_count); | ||||
2037 | for (uint32_t i = 0; ok() && i < field_count; ++i) { | ||||
2038 | fields[i] = consume_storage_type(); | ||||
2039 | mutabilities[i] = consume_mutability(); | ||||
2040 | } | ||||
2041 | if (failed()) return nullptr; | ||||
2042 | uint32_t* offsets = zone->NewArray<uint32_t>(field_count); | ||||
2043 | return zone->New<StructType>(field_count, offsets, fields, mutabilities); | ||||
2044 | } | ||||
2045 | |||||
2046 | const ArrayType* consume_array(Zone* zone) { | ||||
2047 | ValueType element_type = consume_storage_type(); | ||||
2048 | bool mutability = consume_mutability(); | ||||
2049 | if (failed()) return nullptr; | ||||
2050 | return zone->New<ArrayType>(element_type, mutability); | ||||
2051 | } | ||||
2052 | |||||
2053 | // Consume the attribute field of an exception. | ||||
2054 | uint32_t consume_exception_attribute() { | ||||
2055 | const byte* pos = pc_; | ||||
2056 | uint32_t attribute = consume_u32v("exception attribute"); | ||||
2057 | if (attribute != kExceptionAttribute) { | ||||
2058 | errorf(pos, "exception attribute %u not supported", attribute); | ||||
2059 | return 0; | ||||
2060 | } | ||||
2061 | return attribute; | ||||
2062 | } | ||||
2063 | |||||
2064 | WasmElemSegment consume_element_segment_header() { | ||||
2065 | const byte* pos = pc(); | ||||
2066 | |||||
2067 | // The mask for the bit in the flag which indicates if the segment is | ||||
2068 | // active or not (0 is active). | ||||
2069 | constexpr uint8_t kNonActiveMask = 1 << 0; | ||||
2070 | // The mask for the bit in the flag which indicates: | ||||
2071 | // - for active tables, if the segment has an explicit table index field. | ||||
2072 | // - for non-active tables, whether the table is declarative (vs. passive). | ||||
2073 | constexpr uint8_t kHasTableIndexOrIsDeclarativeMask = 1 << 1; | ||||
2074 | // The mask for the bit in the flag which indicates if the functions of this | ||||
2075 | // segment are defined as function indices (0) or init. expressions (1). | ||||
2076 | constexpr uint8_t kExpressionsAsElementsMask = 1 << 2; | ||||
2077 | constexpr uint8_t kFullMask = kNonActiveMask | | ||||
2078 | kHasTableIndexOrIsDeclarativeMask | | ||||
2079 | kExpressionsAsElementsMask; | ||||
2080 | |||||
2081 | uint32_t flag = consume_u32v("flag"); | ||||
2082 | if ((flag & kFullMask) != flag) { | ||||
2083 | errorf(pos, "illegal flag value %u. Must be between 0 and 7", flag); | ||||
2084 | return {}; | ||||
2085 | } | ||||
2086 | |||||
2087 | const WasmElemSegment::Status status = | ||||
2088 | (flag & kNonActiveMask) ? (flag & kHasTableIndexOrIsDeclarativeMask) | ||||
2089 | ? WasmElemSegment::kStatusDeclarative | ||||
2090 | : WasmElemSegment::kStatusPassive | ||||
2091 | : WasmElemSegment::kStatusActive; | ||||
2092 | const bool is_active = status == WasmElemSegment::kStatusActive; | ||||
2093 | |||||
2094 | WasmElemSegment::ElementType element_type = | ||||
2095 | flag & kExpressionsAsElementsMask | ||||
2096 | ? WasmElemSegment::kExpressionElements | ||||
2097 | : WasmElemSegment::kFunctionIndexElements; | ||||
2098 | |||||
2099 | const bool has_table_index = | ||||
2100 | is_active && (flag & kHasTableIndexOrIsDeclarativeMask); | ||||
2101 | uint32_t table_index = has_table_index ? consume_u32v("table index") : 0; | ||||
2102 | if (is_active && table_index >= module_->tables.size()) { | ||||
2103 | errorf(pos, "out of bounds%s table index %u", | ||||
2104 | has_table_index ? " implicit" : "", table_index); | ||||
2105 | return {}; | ||||
2106 | } | ||||
2107 | ValueType table_type = | ||||
2108 | is_active ? module_->tables[table_index].type : kWasmBottom; | ||||
2109 | |||||
2110 | ConstantExpression offset; | ||||
2111 | if (is_active) { | ||||
2112 | offset = consume_init_expr(module_.get(), kWasmI32); | ||||
2113 | // Failed to parse offset initializer, return early. | ||||
2114 | if (failed()) return {}; | ||||
2115 | } | ||||
2116 | |||||
2117 | // Denotes an active segment without table index, type, or element kind. | ||||
2118 | const bool backwards_compatible_mode = | ||||
2119 | is_active && !(flag & kHasTableIndexOrIsDeclarativeMask); | ||||
2120 | ValueType type; | ||||
2121 | if (element_type == WasmElemSegment::kExpressionElements) { | ||||
2122 | type = | ||||
2123 | backwards_compatible_mode ? kWasmFuncRef : consume_reference_type(); | ||||
2124 | if (is_active && !IsSubtypeOf(type, table_type, this->module_.get())) { | ||||
2125 | errorf(pos, | ||||
2126 | "Element segment of type %s is not a subtype of referenced " | ||||
2127 | "table %u (of type %s)", | ||||
2128 | type.name().c_str(), table_index, table_type.name().c_str()); | ||||
2129 | return {}; | ||||
2130 | } | ||||
2131 | } else { | ||||
2132 | if (!backwards_compatible_mode) { | ||||
2133 | // We have to check that there is an element kind of type Function. All | ||||
2134 | // other element kinds are not valid yet. | ||||
2135 | uint8_t val = consume_u8("element kind"); | ||||
2136 | if (static_cast<ImportExportKindCode>(val) != kExternalFunction) { | ||||
2137 | errorf(pos, "illegal element kind 0x%x. Must be 0x%x", val, | ||||
2138 | kExternalFunction); | ||||
2139 | return {}; | ||||
2140 | } | ||||
2141 | } | ||||
2142 | if (!is_active) { | ||||
2143 | // Declarative and passive segments without explicit type are funcref. | ||||
2144 | type = kWasmFuncRef; | ||||
2145 | } else { | ||||
2146 | type = table_type; | ||||
2147 | // Active segments with function indices must reference a function | ||||
2148 | // table. TODO(7748): Add support for anyref tables when we have them. | ||||
2149 | if (!IsSubtypeOf(table_type, kWasmFuncRef, this->module_.get())) { | ||||
2150 | errorf(pos, | ||||
2151 | "An active element segment with function indices as elements " | ||||
2152 | "must reference a table of %s. Instead, table %u of type %s " | ||||
2153 | "is referenced.", | ||||
2154 | enabled_features_.has_typed_funcref() | ||||
2155 | ? "a subtype of type funcref" | ||||
2156 | : "type funcref", | ||||
2157 | table_index, table_type.name().c_str()); | ||||
2158 | return {}; | ||||
2159 | } | ||||
2160 | } | ||||
2161 | } | ||||
2162 | |||||
2163 | if (is_active) { | ||||
2164 | return {type, table_index, std::move(offset), element_type}; | ||||
2165 | } else { | ||||
2166 | return {type, status, element_type}; | ||||
2167 | } | ||||
2168 | } | ||||
2169 | |||||
2170 | void consume_data_segment_header(bool* is_active, uint32_t* index, | ||||
2171 | ConstantExpression* offset) { | ||||
2172 | const byte* pos = pc(); | ||||
2173 | uint32_t flag = consume_u32v("flag"); | ||||
2174 | |||||
2175 | // Some flag values are only valid for specific proposals. | ||||
2176 | if (flag != SegmentFlags::kActiveNoIndex && | ||||
2177 | flag != SegmentFlags::kPassive && | ||||
2178 | flag != SegmentFlags::kActiveWithIndex) { | ||||
2179 | errorf(pos, "illegal flag value %u. Must be 0, 1, or 2", flag); | ||||
2180 | return; | ||||
2181 | } | ||||
2182 | |||||
2183 | // We know now that the flag is valid. Time to read the rest. | ||||
2184 | ValueType expected_type = module_->is_memory64 ? kWasmI64 : kWasmI32; | ||||
2185 | if (flag == SegmentFlags::kActiveNoIndex) { | ||||
2186 | *is_active = true; | ||||
2187 | *index = 0; | ||||
2188 | *offset = consume_init_expr(module_.get(), expected_type); | ||||
2189 | return; | ||||
2190 | } | ||||
2191 | if (flag == SegmentFlags::kPassive) { | ||||
2192 | *is_active = false; | ||||
2193 | return; | ||||
2194 | } | ||||
2195 | if (flag == SegmentFlags::kActiveWithIndex) { | ||||
2196 | *is_active = true; | ||||
2197 | *index = consume_u32v("memory index"); | ||||
2198 | *offset = consume_init_expr(module_.get(), expected_type); | ||||
2199 | } | ||||
2200 | } | ||||
2201 | |||||
2202 | uint32_t consume_element_func_index(ValueType expected) { | ||||
2203 | WasmFunction* func = nullptr; | ||||
2204 | const byte* initial_pc = pc(); | ||||
2205 | uint32_t index = | ||||
2206 | consume_func_index(module_.get(), &func, "element function index"); | ||||
2207 | if (failed()) return index; | ||||
2208 | DCHECK_NOT_NULL(func)((void) 0); | ||||
2209 | DCHECK_EQ(index, func->func_index)((void) 0); | ||||
2210 | ValueType entry_type = ValueType::Ref(func->sig_index, kNonNullable); | ||||
2211 | if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module_.get()))(__builtin_expect(!!(!IsSubtypeOf(entry_type, expected, module_ .get())), 0))) { | ||||
2212 | errorf(initial_pc, | ||||
2213 | "Invalid type in element entry: expected %s, got %s instead.", | ||||
2214 | expected.name().c_str(), entry_type.name().c_str()); | ||||
2215 | return index; | ||||
2216 | } | ||||
2217 | func->declared = true; | ||||
2218 | return index; | ||||
2219 | } | ||||
2220 | }; | ||||
2221 | |||||
2222 | ModuleResult DecodeWasmModule( | ||||
2223 | const WasmFeatures& enabled, const byte* module_start, | ||||
2224 | const byte* module_end, bool verify_functions, ModuleOrigin origin, | ||||
2225 | Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, | ||||
2226 | v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method, | ||||
2227 | AccountingAllocator* allocator) { | ||||
2228 | size_t size = module_end - module_start; | ||||
2229 | CHECK_LE(module_start, module_end)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(module_start)>::type, typename ::v8::base::pass_value_or_ref<decltype(module_end)>::type >((module_start), (module_end)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "module_start" " " "<=" " " "module_end"); } } while (false); } while (false ); | ||||
2230 | size_t max_size = max_module_size(); | ||||
2231 | if (size > max_size) { | ||||
2232 | return ModuleResult{ | ||||
2233 | WasmError{0, "size > maximum module size (%zu): %zu", max_size, size}}; | ||||
2234 | } | ||||
2235 | // TODO(bradnelson): Improve histogram handling of size_t. | ||||
2236 | auto size_counter = | ||||
2237 | SELECT_WASM_COUNTER(counters, origin, wasm, module_size_bytes)((origin) == kWasmOrigin ? (counters)->wasm_wasm_module_size_bytes () : (counters)->wasm_asm_module_size_bytes()); | ||||
2238 | size_counter->AddSample(static_cast<int>(size)); | ||||
2239 | // Signatures are stored in zone memory, which have the same lifetime | ||||
2240 | // as the {module}. | ||||
2241 | ModuleDecoderImpl decoder(enabled, module_start, module_end, origin); | ||||
2242 | v8::metrics::WasmModuleDecoded metrics_event; | ||||
2243 | base::ElapsedTimer timer; | ||||
2244 | timer.Start(); | ||||
2245 | base::ThreadTicks thread_ticks = base::ThreadTicks::IsSupported() | ||||
2246 | ? base::ThreadTicks::Now() | ||||
2247 | : base::ThreadTicks(); | ||||
2248 | ModuleResult result = | ||||
2249 | decoder.DecodeModule(counters, allocator, verify_functions); | ||||
2250 | |||||
2251 | // Record event metrics. | ||||
2252 | metrics_event.wall_clock_duration_in_us = timer.Elapsed().InMicroseconds(); | ||||
2253 | timer.Stop(); | ||||
2254 | if (!thread_ticks.IsNull()) { | ||||
2255 | metrics_event.cpu_duration_in_us = | ||||
2256 | (base::ThreadTicks::Now() - thread_ticks).InMicroseconds(); | ||||
2257 | } | ||||
2258 | metrics_event.success = decoder.ok() && result.ok(); | ||||
2259 | metrics_event.async = decoding_method == DecodingMethod::kAsync || | ||||
2260 | decoding_method == DecodingMethod::kAsyncStream; | ||||
2261 | metrics_event.streamed = decoding_method == DecodingMethod::kSyncStream || | ||||
2262 | decoding_method == DecodingMethod::kAsyncStream; | ||||
2263 | if (result.ok()) { | ||||
2264 | metrics_event.function_count = result.value()->num_declared_functions; | ||||
2265 | } else if (auto&& module = decoder.shared_module()) { | ||||
2266 | metrics_event.function_count = module->num_declared_functions; | ||||
2267 | } | ||||
2268 | metrics_event.module_size_in_bytes = size; | ||||
2269 | metrics_recorder->DelayMainThreadEvent(metrics_event, context_id); | ||||
2270 | |||||
2271 | return result; | ||||
2272 | } | ||||
2273 | |||||
2274 | ModuleDecoder::ModuleDecoder(const WasmFeatures& enabled) | ||||
2275 | : enabled_features_(enabled) {} | ||||
2276 | |||||
2277 | ModuleDecoder::~ModuleDecoder() = default; | ||||
2278 | |||||
2279 | const std::shared_ptr<WasmModule>& ModuleDecoder::shared_module() const { | ||||
2280 | return impl_->shared_module(); | ||||
2281 | } | ||||
2282 | |||||
2283 | void ModuleDecoder::StartDecoding( | ||||
2284 | Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, | ||||
2285 | v8::metrics::Recorder::ContextId context_id, AccountingAllocator* allocator, | ||||
2286 | ModuleOrigin origin) { | ||||
2287 | DCHECK_NULL(impl_)((void) 0); | ||||
2288 | impl_.reset(new ModuleDecoderImpl(enabled_features_, origin)); | ||||
2289 | impl_->StartDecoding(counters, allocator); | ||||
2290 | } | ||||
2291 | |||||
2292 | void ModuleDecoder::DecodeModuleHeader(base::Vector<const uint8_t> bytes, | ||||
2293 | uint32_t offset) { | ||||
2294 | impl_->DecodeModuleHeader(bytes, offset); | ||||
2295 | } | ||||
2296 | |||||
2297 | void ModuleDecoder::DecodeSection(SectionCode section_code, | ||||
2298 | base::Vector<const uint8_t> bytes, | ||||
2299 | uint32_t offset, bool verify_functions) { | ||||
2300 | impl_->DecodeSection(section_code, bytes, offset, verify_functions); | ||||
| |||||
2301 | } | ||||
2302 | |||||
2303 | void ModuleDecoder::DecodeFunctionBody(uint32_t index, uint32_t length, | ||||
2304 | uint32_t offset, bool verify_functions) { | ||||
2305 | impl_->DecodeFunctionBody(index, length, offset, verify_functions); | ||||
2306 | } | ||||
2307 | |||||
2308 | void ModuleDecoder::StartCodeSection() { impl_->StartCodeSection(); } | ||||
2309 | |||||
2310 | bool ModuleDecoder::CheckFunctionsCount(uint32_t functions_count, | ||||
2311 | uint32_t error_offset) { | ||||
2312 | return impl_->CheckFunctionsCount(functions_count, error_offset); | ||||
2313 | } | ||||
2314 | |||||
2315 | ModuleResult ModuleDecoder::FinishDecoding(bool verify_functions) { | ||||
2316 | return impl_->FinishDecoding(verify_functions); | ||||
2317 | } | ||||
2318 | |||||
2319 | void ModuleDecoder::set_code_section(uint32_t offset, uint32_t size) { | ||||
2320 | return impl_->set_code_section(offset, size); | ||||
2321 | } | ||||
2322 | |||||
2323 | size_t ModuleDecoder::IdentifyUnknownSection(ModuleDecoder* decoder, | ||||
2324 | base::Vector<const uint8_t> bytes, | ||||
2325 | uint32_t offset, | ||||
2326 | SectionCode* result) { | ||||
2327 | if (!decoder->ok()) return 0; | ||||
2328 | decoder->impl_->Reset(bytes, offset); | ||||
2329 | *result = IdentifyUnknownSectionInternal(decoder->impl_.get()); | ||||
2330 | return decoder->impl_->pc() - bytes.begin(); | ||||
2331 | } | ||||
2332 | |||||
2333 | bool ModuleDecoder::ok() { return impl_->ok(); } | ||||
2334 | |||||
2335 | const FunctionSig* DecodeWasmSignatureForTesting(const WasmFeatures& enabled, | ||||
2336 | Zone* zone, const byte* start, | ||||
2337 | const byte* end) { | ||||
2338 | ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin); | ||||
2339 | return decoder.DecodeFunctionSignature(zone, start); | ||||
2340 | } | ||||
2341 | |||||
2342 | ConstantExpression DecodeWasmInitExprForTesting(const WasmFeatures& enabled, | ||||
2343 | const byte* start, | ||||
2344 | const byte* end, | ||||
2345 | ValueType expected) { | ||||
2346 | ModuleDecoderImpl decoder(enabled, start, end, kWasmOrigin); | ||||
2347 | AccountingAllocator allocator; | ||||
2348 | decoder.StartDecoding(nullptr, &allocator); | ||||
2349 | return decoder.DecodeInitExprForTesting(expected); | ||||
2350 | } | ||||
2351 | |||||
2352 | FunctionResult DecodeWasmFunctionForTesting( | ||||
2353 | const WasmFeatures& enabled, Zone* zone, const ModuleWireBytes& wire_bytes, | ||||
2354 | const WasmModule* module, const byte* function_start, | ||||
2355 | const byte* function_end, Counters* counters) { | ||||
2356 | size_t size = function_end - function_start; | ||||
2357 | CHECK_LE(function_start, function_end)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(function_start)>::type, typename ::v8::base::pass_value_or_ref<decltype(function_end)>:: type>((function_start), (function_end)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "function_start" " " "<=" " " "function_end"); } } while (false); } while ( false); | ||||
2358 | if (size > kV8MaxWasmFunctionSize) { | ||||
2359 | return FunctionResult{WasmError{0, | ||||
2360 | "size > maximum function size (%zu): %zu", | ||||
2361 | kV8MaxWasmFunctionSize, size}}; | ||||
2362 | } | ||||
2363 | ModuleDecoderImpl decoder(enabled, function_start, function_end, kWasmOrigin); | ||||
2364 | decoder.SetCounters(counters); | ||||
2365 | return decoder.DecodeSingleFunction(zone, wire_bytes, module, | ||||
2366 | std::make_unique<WasmFunction>()); | ||||
2367 | } | ||||
2368 | |||||
2369 | AsmJsOffsetsResult DecodeAsmJsOffsets( | ||||
2370 | base::Vector<const uint8_t> encoded_offsets) { | ||||
2371 | std::vector<AsmJsOffsetFunctionEntries> functions; | ||||
2372 | |||||
2373 | Decoder decoder(encoded_offsets); | ||||
2374 | uint32_t functions_count = decoder.consume_u32v("functions count"); | ||||
2375 | // Consistency check. | ||||
2376 | DCHECK_GE(encoded_offsets.size(), functions_count)((void) 0); | ||||
2377 | functions.reserve(functions_count); | ||||
2378 | |||||
2379 | for (uint32_t i = 0; i < functions_count; ++i) { | ||||
2380 | uint32_t size = decoder.consume_u32v("table size"); | ||||
2381 | if (size == 0) { | ||||
2382 | functions.emplace_back(); | ||||
2383 | continue; | ||||
2384 | } | ||||
2385 | DCHECK(decoder.checkAvailable(size))((void) 0); | ||||
2386 | const byte* table_end = decoder.pc() + size; | ||||
2387 | uint32_t locals_size = decoder.consume_u32v("locals size"); | ||||
2388 | int function_start_position = decoder.consume_u32v("function start pos"); | ||||
2389 | int function_end_position = function_start_position; | ||||
2390 | int last_byte_offset = locals_size; | ||||
2391 | int last_asm_position = function_start_position; | ||||
2392 | std::vector<AsmJsOffsetEntry> func_asm_offsets; | ||||
2393 | func_asm_offsets.reserve(size / 4); // conservative estimation | ||||
2394 | // Add an entry for the stack check, associated with position 0. | ||||
2395 | func_asm_offsets.push_back( | ||||
2396 | {0, function_start_position, function_start_position}); | ||||
2397 | while (decoder.pc() < table_end) { | ||||
2398 | DCHECK(decoder.ok())((void) 0); | ||||
2399 | last_byte_offset += decoder.consume_u32v("byte offset delta"); | ||||
2400 | int call_position = | ||||
2401 | last_asm_position + decoder.consume_i32v("call position delta"); | ||||
2402 | int to_number_position = | ||||
2403 | call_position + decoder.consume_i32v("to_number position delta"); | ||||
2404 | last_asm_position = to_number_position; | ||||
2405 | if (decoder.pc() == table_end) { | ||||
2406 | // The last entry is the function end marker. | ||||
2407 | DCHECK_EQ(call_position, to_number_position)((void) 0); | ||||
2408 | function_end_position = call_position; | ||||
2409 | } else { | ||||
2410 | func_asm_offsets.push_back( | ||||
2411 | {last_byte_offset, call_position, to_number_position}); | ||||
2412 | } | ||||
2413 | } | ||||
2414 | DCHECK_EQ(decoder.pc(), table_end)((void) 0); | ||||
2415 | functions.emplace_back(AsmJsOffsetFunctionEntries{ | ||||
2416 | function_start_position, function_end_position, | ||||
2417 | std::move(func_asm_offsets)}); | ||||
2418 | } | ||||
2419 | DCHECK(decoder.ok())((void) 0); | ||||
2420 | DCHECK(!decoder.more())((void) 0); | ||||
2421 | |||||
2422 | return decoder.toResult(AsmJsOffsets{std::move(functions)}); | ||||
2423 | } | ||||
2424 | |||||
2425 | std::vector<CustomSectionOffset> DecodeCustomSections(const byte* start, | ||||
2426 | const byte* end) { | ||||
2427 | Decoder decoder(start, end); | ||||
2428 | decoder.consume_bytes(4, "wasm magic"); | ||||
2429 | decoder.consume_bytes(4, "wasm version"); | ||||
2430 | |||||
2431 | std::vector<CustomSectionOffset> result; | ||||
2432 | |||||
2433 | while (decoder.more()) { | ||||
2434 | byte section_code = decoder.consume_u8("section code"); | ||||
2435 | uint32_t section_length = decoder.consume_u32v("section length"); | ||||
2436 | uint32_t section_start = decoder.pc_offset(); | ||||
2437 | if (section_code != 0) { | ||||
2438 | // Skip known sections. | ||||
2439 | decoder.consume_bytes(section_length, "section bytes"); | ||||
2440 | continue; | ||||
2441 | } | ||||
2442 | uint32_t name_length = decoder.consume_u32v("name length"); | ||||
2443 | uint32_t name_offset = decoder.pc_offset(); | ||||
2444 | decoder.consume_bytes(name_length, "section name"); | ||||
2445 | uint32_t payload_offset = decoder.pc_offset(); | ||||
2446 | if (section_length < (payload_offset - section_start)) { | ||||
2447 | decoder.error("invalid section length"); | ||||
2448 | break; | ||||
2449 | } | ||||
2450 | uint32_t payload_length = section_length - (payload_offset - section_start); | ||||
2451 | decoder.consume_bytes(payload_length); | ||||
2452 | if (decoder.failed()) break; | ||||
2453 | result.push_back({{section_start, section_length}, | ||||
2454 | {name_offset, name_length}, | ||||
2455 | {payload_offset, payload_length}}); | ||||
2456 | } | ||||
2457 | |||||
2458 | return result; | ||||
2459 | } | ||||
2460 | |||||
2461 | namespace { | ||||
2462 | |||||
2463 | bool FindNameSection(Decoder* decoder) { | ||||
2464 | static constexpr int kModuleHeaderSize = 8; | ||||
2465 | decoder->consume_bytes(kModuleHeaderSize, "module header"); | ||||
2466 | |||||
2467 | WasmSectionIterator section_iter(decoder); | ||||
2468 | |||||
2469 | while (decoder->ok() && section_iter.more() && | ||||
2470 | section_iter.section_code() != kNameSectionCode) { | ||||
2471 | section_iter.advance(true); | ||||
2472 | } | ||||
2473 | if (!section_iter.more()) return false; | ||||
2474 | |||||
2475 | // Reset the decoder to not read beyond the name section end. | ||||
2476 | decoder->Reset(section_iter.payload(), decoder->pc_offset()); | ||||
2477 | return true; | ||||
2478 | } | ||||
2479 | |||||
2480 | } // namespace | ||||
2481 | |||||
2482 | void DecodeFunctionNames(const byte* module_start, const byte* module_end, | ||||
2483 | std::unordered_map<uint32_t, WireBytesRef>* names) { | ||||
2484 | DCHECK_NOT_NULL(names)((void) 0); | ||||
2485 | DCHECK(names->empty())((void) 0); | ||||
2486 | |||||
2487 | Decoder decoder(module_start, module_end); | ||||
2488 | if (FindNameSection(&decoder)) { | ||||
2489 | while (decoder.ok() && decoder.more()) { | ||||
2490 | uint8_t name_type = decoder.consume_u8("name type"); | ||||
2491 | if (name_type & 0x80) break; // no varuint7 | ||||
2492 | |||||
2493 | uint32_t name_payload_len = decoder.consume_u32v("name payload length"); | ||||
2494 | if (!decoder.checkAvailable(name_payload_len)) break; | ||||
2495 | |||||
2496 | if (name_type != NameSectionKindCode::kFunctionCode) { | ||||
2497 | decoder.consume_bytes(name_payload_len, "name subsection payload"); | ||||
2498 | continue; | ||||
2499 | } | ||||
2500 | uint32_t functions_count = decoder.consume_u32v("functions count"); | ||||
2501 | |||||
2502 | for (; decoder.ok() && functions_count > 0; --functions_count) { | ||||
2503 | uint32_t function_index = decoder.consume_u32v("function index"); | ||||
2504 | WireBytesRef name = consume_string(&decoder, false, "function name"); | ||||
2505 | |||||
2506 | // Be lenient with errors in the name section: Ignore non-UTF8 names. | ||||
2507 | // You can even assign to the same function multiple times (last valid | ||||
2508 | // one wins). | ||||
2509 | if (decoder.ok() && validate_utf8(&decoder, name)) { | ||||
2510 | names->insert(std::make_pair(function_index, name)); | ||||
2511 | } | ||||
2512 | } | ||||
2513 | } | ||||
2514 | } | ||||
2515 | } | ||||
2516 | |||||
2517 | NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes, | ||||
2518 | uint8_t name_section_kind) { | ||||
2519 | Decoder decoder(module_bytes); | ||||
2520 | if (!FindNameSection(&decoder)) return NameMap{{}}; | ||||
2521 | |||||
2522 | std::vector<NameAssoc> names; | ||||
2523 | while (decoder.ok() && decoder.more()) { | ||||
2524 | uint8_t name_type = decoder.consume_u8("name type"); | ||||
2525 | if (name_type & 0x80) break; // no varuint7 | ||||
2526 | |||||
2527 | uint32_t name_payload_len = decoder.consume_u32v("name payload length"); | ||||
2528 | if (!decoder.checkAvailable(name_payload_len)) break; | ||||
2529 | |||||
2530 | if (name_type != name_section_kind) { | ||||
2531 | decoder.consume_bytes(name_payload_len, "name subsection payload"); | ||||
2532 | continue; | ||||
2533 | } | ||||
2534 | |||||
2535 | uint32_t count = decoder.consume_u32v("names count"); | ||||
2536 | for (uint32_t i = 0; i < count; i++) { | ||||
2537 | uint32_t index = decoder.consume_u32v("index"); | ||||
2538 | WireBytesRef name = consume_string(&decoder, false, "name"); | ||||
2539 | if (!decoder.ok()) break; | ||||
2540 | if (index > kMaxInt) continue; | ||||
2541 | if (!validate_utf8(&decoder, name)) continue; | ||||
2542 | names.emplace_back(static_cast<int>(index), name); | ||||
2543 | } | ||||
2544 | } | ||||
2545 | std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{}); | ||||
2546 | return NameMap{std::move(names)}; | ||||
2547 | } | ||||
2548 | |||||
2549 | IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes, | ||||
2550 | uint8_t name_section_kind) { | ||||
2551 | Decoder decoder(module_bytes); | ||||
2552 | if (!FindNameSection(&decoder)) return IndirectNameMap{{}}; | ||||
2553 | |||||
2554 | std::vector<IndirectNameMapEntry> entries; | ||||
2555 | while (decoder.ok() && decoder.more()) { | ||||
2556 | uint8_t name_type = decoder.consume_u8("name type"); | ||||
2557 | if (name_type & 0x80) break; // no varuint7 | ||||
2558 | |||||
2559 | uint32_t name_payload_len = decoder.consume_u32v("name payload length"); | ||||
2560 | if (!decoder.checkAvailable(name_payload_len)) break; | ||||
2561 | |||||
2562 | if (name_type != name_section_kind) { | ||||
2563 | decoder.consume_bytes(name_payload_len, "name subsection payload"); | ||||
2564 | continue; | ||||
2565 | } | ||||
2566 | |||||
2567 | uint32_t outer_count = decoder.consume_u32v("outer count"); | ||||
2568 | for (uint32_t i = 0; i < outer_count; ++i) { | ||||
2569 | uint32_t outer_index = decoder.consume_u32v("outer index"); | ||||
2570 | if (outer_index > kMaxInt) continue; | ||||
2571 | std::vector<NameAssoc> names; | ||||
2572 | uint32_t inner_count = decoder.consume_u32v("inner count"); | ||||
2573 | for (uint32_t k = 0; k < inner_count; ++k) { | ||||
2574 | uint32_t inner_index = decoder.consume_u32v("inner index"); | ||||
2575 | WireBytesRef name = consume_string(&decoder, false, "name"); | ||||
2576 | if (!decoder.ok()) break; | ||||
2577 | if (inner_index > kMaxInt) continue; | ||||
2578 | // Ignore non-utf8 names. | ||||
2579 | if (!validate_utf8(&decoder, name)) continue; | ||||
2580 | names.emplace_back(static_cast<int>(inner_index), name); | ||||
2581 | } | ||||
2582 | // Use stable sort to get deterministic names (the first one declared) | ||||
2583 | // even in the presence of duplicates. | ||||
2584 | std::stable_sort(names.begin(), names.end(), NameAssoc::IndexLess{}); | ||||
2585 | entries.emplace_back(static_cast<int>(outer_index), std::move(names)); | ||||
2586 | } | ||||
2587 | } | ||||
2588 | std::stable_sort(entries.begin(), entries.end(), | ||||
2589 | IndirectNameMapEntry::IndexLess{}); | ||||
2590 | return IndirectNameMap{std::move(entries)}; | ||||
2591 | } | ||||
2592 | |||||
2593 | #undef TRACE | ||||
2594 | |||||
2595 | } // namespace wasm | ||||
2596 | } // namespace internal | ||||
2597 | } // namespace v8 |
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_WEBASSEMBLY1 | ||||
6 | #error This header should only be included if WebAssembly is enabled. | ||||
7 | #endif // !V8_ENABLE_WEBASSEMBLY | ||||
8 | |||||
9 | #ifndef V8_WASM_WASM_MODULE_H_ | ||||
10 | #define V8_WASM_WASM_MODULE_H_ | ||||
11 | |||||
12 | #include <map> | ||||
13 | #include <memory> | ||||
14 | |||||
15 | #include "src/base/optional.h" | ||||
16 | #include "src/base/platform/wrappers.h" | ||||
17 | #include "src/base/vector.h" | ||||
18 | #include "src/common/globals.h" | ||||
19 | #include "src/handles/handles.h" | ||||
20 | #include "src/wasm/branch-hint-map.h" | ||||
21 | #include "src/wasm/signature-map.h" | ||||
22 | #include "src/wasm/struct-types.h" | ||||
23 | #include "src/wasm/wasm-constants.h" | ||||
24 | #include "src/wasm/wasm-init-expr.h" | ||||
25 | #include "src/wasm/wasm-limits.h" | ||||
26 | |||||
27 | namespace v8 { | ||||
28 | |||||
29 | namespace internal { | ||||
30 | |||||
31 | class WasmModuleObject; | ||||
32 | |||||
33 | namespace wasm { | ||||
34 | |||||
35 | using WasmName = base::Vector<const char>; | ||||
36 | |||||
37 | struct AsmJsOffsets; | ||||
38 | class ErrorThrower; | ||||
39 | |||||
40 | // Reference to a string in the wire bytes. | ||||
41 | class WireBytesRef { | ||||
42 | public: | ||||
43 | WireBytesRef() : WireBytesRef(0, 0) {} | ||||
44 | WireBytesRef(uint32_t offset, uint32_t length) | ||||
45 | : offset_(offset), length_(length) { | ||||
46 | DCHECK_IMPLIES(offset_ == 0, length_ == 0)((void) 0); | ||||
47 | DCHECK_LE(offset_, offset_ + length_)((void) 0); // no uint32_t overflow. | ||||
48 | } | ||||
49 | |||||
50 | uint32_t offset() const { return offset_; } | ||||
51 | uint32_t length() const { return length_; } | ||||
52 | uint32_t end_offset() const { return offset_ + length_; } | ||||
53 | bool is_empty() const { return length_ == 0; } | ||||
54 | bool is_set() const { return offset_ != 0; } | ||||
55 | |||||
56 | private: | ||||
57 | uint32_t offset_; | ||||
58 | uint32_t length_; | ||||
59 | }; | ||||
60 | |||||
61 | // Static representation of a wasm function. | ||||
62 | struct WasmFunction { | ||||
63 | const FunctionSig* sig; // signature of the function. | ||||
64 | uint32_t func_index; // index into the function table. | ||||
65 | uint32_t sig_index; // index into the signature table. | ||||
66 | WireBytesRef code; // code of this function. | ||||
67 | // Required number of slots in a feedback vector. Marked {mutable} because | ||||
68 | // this is computed late (by Liftoff compilation), when the rest of the | ||||
69 | // {WasmFunction} is typically considered {const}. | ||||
70 | mutable int feedback_slots; | ||||
71 | bool imported; | ||||
72 | bool exported; | ||||
73 | bool declared; | ||||
74 | }; | ||||
75 | |||||
76 | // A representation of a constant expression. The most common expression types | ||||
77 | // are hard-coded, while the rest are represented as a {WireBytesRef}. | ||||
78 | class ConstantExpression { | ||||
79 | public: | ||||
80 | enum Kind { | ||||
81 | kEmpty, | ||||
82 | kI32Const, | ||||
83 | kRefNull, | ||||
84 | kRefFunc, | ||||
85 | kWireBytesRef, | ||||
86 | kLastKind = kWireBytesRef | ||||
87 | }; | ||||
88 | |||||
89 | union Value { | ||||
90 | int32_t i32_value; | ||||
91 | uint32_t index_or_offset; | ||||
92 | HeapType::Representation repr; | ||||
93 | }; | ||||
94 | |||||
95 | ConstantExpression() : bit_field_(KindField::encode(kEmpty)) {} | ||||
96 | |||||
97 | static ConstantExpression I32Const(int32_t value) { | ||||
98 | return ConstantExpression(ValueField::encode(value) | | ||||
99 | KindField::encode(kI32Const)); | ||||
100 | } | ||||
101 | static ConstantExpression RefFunc(uint32_t index) { | ||||
102 | return ConstantExpression(ValueField::encode(index) | | ||||
103 | KindField::encode(kRefFunc)); | ||||
104 | } | ||||
105 | static ConstantExpression RefNull(HeapType::Representation repr) { | ||||
106 | return ConstantExpression(ValueField::encode(repr) | | ||||
107 | KindField::encode(kRefNull)); | ||||
108 | } | ||||
109 | static ConstantExpression WireBytes(uint32_t offset, uint32_t length) { | ||||
110 | return ConstantExpression(OffsetField::encode(offset) | | ||||
111 | LengthField::encode(length) | | ||||
112 | KindField::encode(kWireBytesRef)); | ||||
113 | } | ||||
114 | |||||
115 | Kind kind() const { return KindField::decode(bit_field_); } | ||||
116 | |||||
117 | bool is_set() const { return kind() != kEmpty; } | ||||
118 | |||||
119 | uint32_t index() const { | ||||
120 | DCHECK_EQ(kind(), kRefFunc)((void) 0); | ||||
121 | return ValueField::decode(bit_field_); | ||||
122 | } | ||||
123 | |||||
124 | HeapType::Representation repr() const { | ||||
125 | DCHECK_EQ(kind(), kRefNull)((void) 0); | ||||
126 | return static_cast<HeapType::Representation>( | ||||
127 | ValueField::decode(bit_field_)); | ||||
128 | } | ||||
129 | |||||
130 | int32_t i32_value() const { | ||||
131 | DCHECK_EQ(kind(), kI32Const)((void) 0); | ||||
132 | return ValueField::decode(bit_field_); | ||||
133 | } | ||||
134 | |||||
135 | WireBytesRef wire_bytes_ref() const { | ||||
136 | DCHECK_EQ(kind(), kWireBytesRef)((void) 0); | ||||
137 | return WireBytesRef(OffsetField::decode(bit_field_), | ||||
138 | LengthField::decode(bit_field_)); | ||||
139 | } | ||||
140 | |||||
141 | private: | ||||
142 | static constexpr int kValueBits = 32; | ||||
143 | static constexpr int kLengthBits = 30; | ||||
144 | static constexpr int kOffsetBits = 30; | ||||
145 | static constexpr int kKindBits = 3; | ||||
146 | |||||
147 | // There are two possible combinations of fields: offset + length + kind if | ||||
148 | // kind = kWireBytesRef, or value + kind for anything else. | ||||
149 | using ValueField = base::BitField<uint32_t, 0, kValueBits, uint64_t>; | ||||
150 | using OffsetField = base::BitField<uint32_t, 0, kOffsetBits, uint64_t>; | ||||
151 | using LengthField = OffsetField::Next<uint32_t, kLengthBits>; | ||||
152 | using KindField = LengthField::Next<Kind, kKindBits>; | ||||
153 | |||||
154 | // Make sure we reserve enough bits for a {WireBytesRef}'s length and offset. | ||||
155 | STATIC_ASSERT(kV8MaxWasmModuleSize <= LengthField::kMax + 1)static_assert(kV8MaxWasmModuleSize <= LengthField::kMax + 1 , "kV8MaxWasmModuleSize <= LengthField::kMax + 1"); | ||||
156 | STATIC_ASSERT(kV8MaxWasmModuleSize <= OffsetField::kMax + 1)static_assert(kV8MaxWasmModuleSize <= OffsetField::kMax + 1 , "kV8MaxWasmModuleSize <= OffsetField::kMax + 1"); | ||||
157 | // Make sure kind fits in kKindBits. | ||||
158 | STATIC_ASSERT(kLastKind <= KindField::kMax + 1)static_assert(kLastKind <= KindField::kMax + 1, "kLastKind <= KindField::kMax + 1" ); | ||||
159 | |||||
160 | explicit ConstantExpression(uint64_t bit_field) : bit_field_(bit_field) {} | ||||
161 | |||||
162 | uint64_t bit_field_; | ||||
163 | }; | ||||
164 | |||||
165 | // We want to keep {ConstantExpression} small to reduce memory usage during | ||||
166 | // compilation/instantiation. | ||||
167 | STATIC_ASSERT(sizeof(ConstantExpression) <= 8)static_assert(sizeof(ConstantExpression) <= 8, "sizeof(ConstantExpression) <= 8" ); | ||||
168 | |||||
169 | // Static representation of a wasm global variable. | ||||
170 | struct WasmGlobal { | ||||
171 | ValueType type; // type of the global. | ||||
172 | bool mutability; // {true} if mutable. | ||||
173 | ConstantExpression init; // the initialization expression of the global. | ||||
174 | union { | ||||
175 | // Index of imported mutable global. | ||||
176 | uint32_t index; | ||||
177 | // Offset into global memory (if not imported & mutable). Expressed in bytes | ||||
178 | // for value-typed globals, and in tagged words for reference-typed globals. | ||||
179 | uint32_t offset; | ||||
180 | }; | ||||
181 | bool imported; // true if imported. | ||||
182 | bool exported; // true if exported. | ||||
183 | }; | ||||
184 | |||||
185 | // Note: An exception tag signature only uses the params portion of a function | ||||
186 | // signature. | ||||
187 | using WasmTagSig = FunctionSig; | ||||
188 | |||||
189 | // Static representation of a wasm tag type. | ||||
190 | struct WasmTag { | ||||
191 | explicit WasmTag(const WasmTagSig* sig) : sig(sig) {} | ||||
192 | const FunctionSig* ToFunctionSig() const { return sig; } | ||||
193 | |||||
194 | const WasmTagSig* sig; // type signature of the tag. | ||||
195 | }; | ||||
196 | |||||
197 | // Static representation of a wasm data segment. | ||||
198 | struct WasmDataSegment { | ||||
199 | // Construct an active segment. | ||||
200 | explicit WasmDataSegment(ConstantExpression dest_addr) | ||||
201 | : dest_addr(dest_addr), active(true) {} | ||||
202 | |||||
203 | // Construct a passive segment, which has no dest_addr. | ||||
204 | WasmDataSegment() : active(false) {} | ||||
205 | |||||
206 | ConstantExpression dest_addr; // destination memory address of the data. | ||||
207 | WireBytesRef source; // start offset in the module bytes. | ||||
208 | bool active = true; // true if copied automatically during instantiation. | ||||
209 | }; | ||||
210 | |||||
211 | // Static representation of wasm element segment (table initializer). | ||||
212 | struct WasmElemSegment { | ||||
213 | enum Status { | ||||
214 | kStatusActive, // copied automatically during instantiation. | ||||
215 | kStatusPassive, // copied explicitly after instantiation. | ||||
216 | kStatusDeclarative // purely declarative and never copied. | ||||
217 | }; | ||||
218 | enum ElementType { kFunctionIndexElements, kExpressionElements }; | ||||
219 | |||||
220 | // Construct an active segment. | ||||
221 | WasmElemSegment(ValueType type, uint32_t table_index, | ||||
222 | ConstantExpression offset, ElementType element_type) | ||||
223 | : status(kStatusActive), | ||||
224 | type(type), | ||||
225 | table_index(table_index), | ||||
226 | offset(std::move(offset)), | ||||
227 | element_type(element_type) {} | ||||
228 | |||||
229 | // Construct a passive or declarative segment, which has no table index or | ||||
230 | // offset. | ||||
231 | WasmElemSegment(ValueType type, Status status, ElementType element_type) | ||||
232 | : status(status), type(type), table_index(0), element_type(element_type) { | ||||
233 | DCHECK_NE(status, kStatusActive)((void) 0); | ||||
234 | } | ||||
235 | |||||
236 | // Default constructor. Constucts an invalid segment. | ||||
237 | WasmElemSegment() | ||||
238 | : status(kStatusActive), | ||||
239 | type(kWasmBottom), | ||||
240 | table_index(0), | ||||
241 | element_type(kFunctionIndexElements) {} | ||||
242 | |||||
243 | WasmElemSegment(const WasmElemSegment&) = delete; | ||||
244 | WasmElemSegment(WasmElemSegment&&) V8_NOEXCEPTnoexcept = default; | ||||
245 | WasmElemSegment& operator=(const WasmElemSegment&) = delete; | ||||
246 | WasmElemSegment& operator=(WasmElemSegment&&) V8_NOEXCEPTnoexcept = default; | ||||
247 | |||||
248 | Status status; | ||||
249 | ValueType type; | ||||
250 | uint32_t table_index; | ||||
251 | ConstantExpression offset; | ||||
252 | ElementType element_type; | ||||
253 | std::vector<ConstantExpression> entries; | ||||
254 | }; | ||||
255 | |||||
256 | // Static representation of a wasm import. | ||||
257 | struct WasmImport { | ||||
258 | WireBytesRef module_name; // module name. | ||||
259 | WireBytesRef field_name; // import name. | ||||
260 | ImportExportKindCode kind; // kind of the import. | ||||
261 | uint32_t index; // index into the respective space. | ||||
262 | }; | ||||
263 | |||||
264 | // Static representation of a wasm export. | ||||
265 | struct WasmExport { | ||||
266 | WireBytesRef name; // exported name. | ||||
267 | ImportExportKindCode kind; // kind of the export. | ||||
268 | uint32_t index; // index into the respective space. | ||||
269 | }; | ||||
270 | |||||
271 | enum class WasmCompilationHintStrategy : uint8_t { | ||||
272 | kDefault = 0, | ||||
273 | kLazy = 1, | ||||
274 | kEager = 2, | ||||
275 | kLazyBaselineEagerTopTier = 3, | ||||
276 | }; | ||||
277 | |||||
278 | enum class WasmCompilationHintTier : uint8_t { | ||||
279 | kDefault = 0, | ||||
280 | kBaseline = 1, | ||||
281 | kOptimized = 2, | ||||
282 | }; | ||||
283 | |||||
284 | // Static representation of a wasm compilation hint | ||||
285 | struct WasmCompilationHint { | ||||
286 | WasmCompilationHintStrategy strategy; | ||||
287 | WasmCompilationHintTier baseline_tier; | ||||
288 | WasmCompilationHintTier top_tier; | ||||
289 | }; | ||||
290 | |||||
291 | enum ModuleOrigin : uint8_t { | ||||
292 | kWasmOrigin, | ||||
293 | kAsmJsSloppyOrigin, | ||||
294 | kAsmJsStrictOrigin | ||||
295 | }; | ||||
296 | |||||
297 | #define SELECT_WASM_COUNTER(counters, origin, prefix, suffix)((origin) == kWasmOrigin ? (counters)->prefix_wasm_suffix( ) : (counters)->prefix_asm_suffix()) \ | ||||
298 | ((origin) == kWasmOrigin ? (counters)->prefix##_wasm_##suffix() \ | ||||
299 | : (counters)->prefix##_asm_##suffix()) | ||||
300 | |||||
301 | struct ModuleWireBytes; | ||||
302 | |||||
303 | class V8_EXPORT_PRIVATE LazilyGeneratedNames { | ||||
304 | public: | ||||
305 | WireBytesRef LookupFunctionName(const ModuleWireBytes& wire_bytes, | ||||
306 | uint32_t function_index) const; | ||||
307 | |||||
308 | void AddForTesting(int function_index, WireBytesRef name); | ||||
309 | |||||
310 | private: | ||||
311 | // {function_names_} are populated lazily after decoding, and | ||||
312 | // therefore need a mutex to protect concurrent modifications | ||||
313 | // from multiple {WasmModuleObject}. | ||||
314 | mutable base::Mutex mutex_; | ||||
315 | mutable std::unique_ptr<std::unordered_map<uint32_t, WireBytesRef>> | ||||
316 | function_names_; | ||||
317 | }; | ||||
318 | |||||
319 | class V8_EXPORT_PRIVATE AsmJsOffsetInformation { | ||||
320 | public: | ||||
321 | explicit AsmJsOffsetInformation(base::Vector<const byte> encoded_offsets); | ||||
322 | |||||
323 | // Destructor defined in wasm-module.cc, where the definition of | ||||
324 | // {AsmJsOffsets} is available. | ||||
325 | ~AsmJsOffsetInformation(); | ||||
326 | |||||
327 | int GetSourcePosition(int func_index, int byte_offset, | ||||
328 | bool is_at_number_conversion); | ||||
329 | |||||
330 | std::pair<int, int> GetFunctionOffsets(int func_index); | ||||
331 | |||||
332 | private: | ||||
333 | void EnsureDecodedOffsets(); | ||||
334 | |||||
335 | // The offset information table is decoded lazily, hence needs to be | ||||
336 | // protected against concurrent accesses. | ||||
337 | // Exactly one of the two fields below will be set at a time. | ||||
338 | mutable base::Mutex mutex_; | ||||
339 | |||||
340 | // Holds the encoded offset table bytes. | ||||
341 | base::OwnedVector<const uint8_t> encoded_offsets_; | ||||
342 | |||||
343 | // Holds the decoded offset table. | ||||
344 | std::unique_ptr<AsmJsOffsets> decoded_offsets_; | ||||
345 | }; | ||||
346 | |||||
347 | // Used as the supertype for a type at the top of the type hierarchy. | ||||
348 | constexpr uint32_t kNoSuperType = std::numeric_limits<uint32_t>::max(); | ||||
349 | |||||
350 | struct TypeDefinition { | ||||
351 | enum Kind { kFunction, kStruct, kArray }; | ||||
352 | |||||
353 | TypeDefinition(const FunctionSig* sig, uint32_t supertype) | ||||
354 | : function_sig(sig), supertype(supertype), kind(kFunction) {} | ||||
355 | TypeDefinition(const StructType* type, uint32_t supertype) | ||||
356 | : struct_type(type), supertype(supertype), kind(kStruct) {} | ||||
357 | TypeDefinition(const ArrayType* type, uint32_t supertype) | ||||
358 | : array_type(type), supertype(supertype), kind(kArray) {} | ||||
359 | TypeDefinition() | ||||
360 | : function_sig(nullptr), supertype(kNoSuperType), kind(kFunction) {} | ||||
361 | |||||
362 | union { | ||||
363 | const FunctionSig* function_sig; | ||||
364 | const StructType* struct_type; | ||||
365 | const ArrayType* array_type; | ||||
366 | }; | ||||
367 | |||||
368 | bool operator==(const TypeDefinition& other) const { | ||||
369 | if (supertype != other.supertype || kind != other.kind) { | ||||
370 | return false; | ||||
371 | } | ||||
372 | switch (kind) { | ||||
373 | case kFunction: | ||||
374 | return *function_sig == *other.function_sig; | ||||
375 | case kStruct: | ||||
376 | return *struct_type == *other.struct_type; | ||||
377 | case kArray: | ||||
378 | return *array_type == *other.array_type; | ||||
379 | } | ||||
380 | } | ||||
381 | |||||
382 | bool operator!=(const TypeDefinition& other) const { | ||||
383 | return !(*this == other); | ||||
384 | } | ||||
385 | |||||
386 | uint32_t supertype; | ||||
387 | Kind kind; | ||||
388 | }; | ||||
389 | |||||
390 | struct V8_EXPORT_PRIVATE WasmDebugSymbols { | ||||
391 | enum class Type { None, SourceMap, EmbeddedDWARF, ExternalDWARF }; | ||||
392 | Type type = Type::None; | ||||
393 | WireBytesRef external_url; | ||||
394 | }; | ||||
395 | |||||
396 | struct CallSiteFeedback { | ||||
397 | int function_index; | ||||
398 | int absolute_call_frequency; | ||||
399 | }; | ||||
400 | struct FunctionTypeFeedback { | ||||
401 | std::vector<CallSiteFeedback> feedback_vector; | ||||
402 | std::map<WasmCodePosition, int> positions; | ||||
403 | int tierup_priority = 0; | ||||
404 | }; | ||||
405 | struct TypeFeedbackStorage { | ||||
406 | std::map<uint32_t, FunctionTypeFeedback> feedback_for_function; | ||||
407 | // Accesses to {feedback_for_function} are guarded by this mutex. | ||||
408 | base::Mutex mutex; | ||||
409 | }; | ||||
410 | |||||
411 | struct WasmTable; | ||||
412 | |||||
413 | // Static representation of a module. | ||||
414 | struct V8_EXPORT_PRIVATE WasmModule { | ||||
415 | std::unique_ptr<Zone> signature_zone; | ||||
416 | uint32_t initial_pages = 0; // initial size of the memory in 64k pages | ||||
417 | uint32_t maximum_pages = 0; // maximum size of the memory in 64k pages | ||||
418 | bool has_shared_memory = false; // true if memory is a SharedArrayBuffer | ||||
419 | bool has_maximum_pages = false; // true if there is a maximum memory size | ||||
420 | bool is_memory64 = false; // true if the memory is 64 bit | ||||
421 | bool has_memory = false; // true if the memory was defined or imported | ||||
422 | bool mem_export = false; // true if the memory is exported | ||||
423 | int start_function_index = -1; // start function, >= 0 if any | ||||
424 | |||||
425 | // Size of the buffer required for all globals that are not imported and | ||||
426 | // mutable. | ||||
427 | uint32_t untagged_globals_buffer_size = 0; | ||||
428 | uint32_t tagged_globals_buffer_size = 0; | ||||
429 | uint32_t num_imported_mutable_globals = 0; | ||||
430 | uint32_t num_imported_functions = 0; | ||||
431 | uint32_t num_imported_tables = 0; | ||||
432 | uint32_t num_declared_functions = 0; // excluding imported | ||||
433 | uint32_t num_exported_functions = 0; | ||||
434 | uint32_t num_declared_data_segments = 0; // From the DataCount section. | ||||
435 | // Position and size of the code section (payload only, i.e. without section | ||||
436 | // ID and length). | ||||
437 | WireBytesRef code = {0, 0}; | ||||
438 | WireBytesRef name = {0, 0}; | ||||
439 | |||||
440 | void add_type(TypeDefinition type) { | ||||
441 | types.push_back(type); | ||||
442 | uint32_t canonical_id = type.kind
| ||||
443 | ? signature_map.FindOrInsert(*type.function_sig) | ||||
| |||||
444 | : 0; | ||||
445 | canonicalized_type_ids.push_back(canonical_id); | ||||
446 | // Canonical type will be computed later. | ||||
447 | isorecursive_canonical_type_ids.push_back(kNoSuperType); | ||||
448 | } | ||||
449 | |||||
450 | bool has_type(uint32_t index) const { return index < types.size(); } | ||||
451 | |||||
452 | void add_signature(const FunctionSig* sig, uint32_t supertype) { | ||||
453 | DCHECK_NOT_NULL(sig)((void) 0); | ||||
454 | add_type(TypeDefinition(sig, supertype)); | ||||
455 | } | ||||
456 | bool has_signature(uint32_t index) const { | ||||
457 | return index < types.size() && | ||||
458 | types[index].kind == TypeDefinition::kFunction; | ||||
459 | } | ||||
460 | const FunctionSig* signature(uint32_t index) const { | ||||
461 | DCHECK(has_signature(index))((void) 0); | ||||
462 | return types[index].function_sig; | ||||
463 | } | ||||
464 | |||||
465 | void add_struct_type(const StructType* type, uint32_t supertype) { | ||||
466 | DCHECK_NOT_NULL(type)((void) 0); | ||||
467 | add_type(TypeDefinition(type, supertype)); | ||||
468 | } | ||||
469 | bool has_struct(uint32_t index) const { | ||||
470 | return index < types.size() && types[index].kind == TypeDefinition::kStruct; | ||||
471 | } | ||||
472 | const StructType* struct_type(uint32_t index) const { | ||||
473 | DCHECK(has_struct(index))((void) 0); | ||||
474 | return types[index].struct_type; | ||||
475 | } | ||||
476 | |||||
477 | void add_array_type(const ArrayType* type, uint32_t supertype) { | ||||
478 | DCHECK_NOT_NULL(type)((void) 0); | ||||
479 | add_type(TypeDefinition(type, supertype)); | ||||
480 | } | ||||
481 | bool has_array(uint32_t index) const { | ||||
482 | return index < types.size() && types[index].kind == TypeDefinition::kArray; | ||||
483 | } | ||||
484 | const ArrayType* array_type(uint32_t index) const { | ||||
485 | DCHECK(has_array(index))((void) 0); | ||||
486 | return types[index].array_type; | ||||
487 | } | ||||
488 | |||||
489 | uint32_t supertype(uint32_t index) const { | ||||
490 | DCHECK(index < types.size())((void) 0); | ||||
491 | return types[index].supertype; | ||||
492 | } | ||||
493 | bool has_supertype(uint32_t index) const { | ||||
494 | return supertype(index) != kNoSuperType; | ||||
495 | } | ||||
496 | |||||
497 | std::vector<TypeDefinition> types; // by type index | ||||
498 | // TODO(7748): Unify the following two arrays. | ||||
499 | // Maps each type index to a canonical index for purposes of call_indirect. | ||||
500 | std::vector<uint32_t> canonicalized_type_ids; | ||||
501 | // Maps each type index to its global (cross-module) canonical index as per | ||||
502 | // isorecursive type canonicalization. | ||||
503 | std::vector<uint32_t> isorecursive_canonical_type_ids; | ||||
504 | // Canonicalizing map for signature indexes. | ||||
505 | SignatureMap signature_map; | ||||
506 | std::vector<WasmFunction> functions; | ||||
507 | std::vector<WasmGlobal> globals; | ||||
508 | std::vector<WasmDataSegment> data_segments; | ||||
509 | std::vector<WasmTable> tables; | ||||
510 | std::vector<WasmImport> import_table; | ||||
511 | std::vector<WasmExport> export_table; | ||||
512 | std::vector<WasmTag> tags; | ||||
513 | std::vector<WasmElemSegment> elem_segments; | ||||
514 | std::vector<WasmCompilationHint> compilation_hints; | ||||
515 | BranchHintInfo branch_hints; | ||||
516 | mutable TypeFeedbackStorage type_feedback; | ||||
517 | |||||
518 | ModuleOrigin origin = kWasmOrigin; // origin of the module | ||||
519 | LazilyGeneratedNames lazily_generated_names; | ||||
520 | WasmDebugSymbols debug_symbols; | ||||
521 | |||||
522 | // Asm.js source position information. Only available for modules compiled | ||||
523 | // from asm.js. | ||||
524 | std::unique_ptr<AsmJsOffsetInformation> asm_js_offset_information; | ||||
525 | |||||
526 | explicit WasmModule(std::unique_ptr<Zone> signature_zone = nullptr); | ||||
527 | WasmModule(const WasmModule&) = delete; | ||||
528 | WasmModule& operator=(const WasmModule&) = delete; | ||||
529 | }; | ||||
530 | |||||
531 | // Static representation of a wasm indirect call table. | ||||
532 | struct WasmTable { | ||||
533 | MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmTable)WasmTable() = default; WasmTable(WasmTable&&) noexcept = default; WasmTable& operator=(WasmTable&&) noexcept = default; WasmTable(const WasmTable&) = delete; WasmTable & operator=(const WasmTable&) = delete; | ||||
534 | |||||
535 | // 'module' can be nullptr | ||||
536 | // TODO(9495): Update this function as more table types are supported, or | ||||
537 | // remove it completely when all reference types are allowed. | ||||
538 | static bool IsValidTableType(ValueType type, const WasmModule* module) { | ||||
539 | if (!type.is_object_reference()) return false; | ||||
540 | HeapType heap_type = type.heap_type(); | ||||
541 | return heap_type == HeapType::kFunc || heap_type == HeapType::kAny || | ||||
542 | (module != nullptr && heap_type.is_index() && | ||||
543 | module->has_signature(heap_type.ref_index())); | ||||
544 | } | ||||
545 | |||||
546 | ValueType type = kWasmVoid; // table type. | ||||
547 | uint32_t initial_size = 0; // initial table size. | ||||
548 | uint32_t maximum_size = 0; // maximum table size. | ||||
549 | bool has_maximum_size = false; // true if there is a maximum size. | ||||
550 | bool imported = false; // true if imported. | ||||
551 | bool exported = false; // true if exported. | ||||
552 | ConstantExpression initial_value; | ||||
553 | }; | ||||
554 | |||||
555 | inline bool is_asmjs_module(const WasmModule* module) { | ||||
556 | return module->origin != kWasmOrigin; | ||||
557 | } | ||||
558 | |||||
559 | size_t EstimateStoredSize(const WasmModule* module); | ||||
560 | |||||
561 | // Returns the number of possible export wrappers for a given module. | ||||
562 | V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module); | ||||
563 | |||||
564 | // Returns the wrapper index for a function in {module} with signature {sig} | ||||
565 | // or {sig_index} and origin defined by {is_import}. | ||||
566 | // Prefer to use the {sig_index} consuming version, as it is much faster. | ||||
567 | int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig, | ||||
568 | bool is_import); | ||||
569 | int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index, | ||||
570 | bool is_import); | ||||
571 | |||||
572 | // Return the byte offset of the function identified by the given index. | ||||
573 | // The offset will be relative to the start of the module bytes. | ||||
574 | // Returns -1 if the function index is invalid. | ||||
575 | int GetWasmFunctionOffset(const WasmModule* module, uint32_t func_index); | ||||
576 | |||||
577 | // Returns the function containing the given byte offset. | ||||
578 | // Returns -1 if the byte offset is not contained in any | ||||
579 | // function of this module. | ||||
580 | int GetContainingWasmFunction(const WasmModule* module, uint32_t byte_offset); | ||||
581 | |||||
582 | // Returns the function containing the given byte offset. | ||||
583 | // Will return preceding function if the byte offset is not | ||||
584 | // contained within a function. | ||||
585 | int GetNearestWasmFunction(const WasmModule* module, uint32_t byte_offset); | ||||
586 | |||||
587 | // Gets the explicitly defined subtyping depth for the given type. | ||||
588 | // Returns 0 if the type has no explicit supertype. | ||||
589 | // The result is capped to {kV8MaxRttSubtypingDepth + 1}. | ||||
590 | // Invalid cyclic hierarchies will return -1. | ||||
591 | V8_EXPORT_PRIVATE int GetSubtypingDepth(const WasmModule* module, | ||||
592 | uint32_t type_index); | ||||
593 | |||||
594 | // Interface to the storage (wire bytes) of a wasm module. | ||||
595 | // It is illegal for anyone receiving a ModuleWireBytes to store pointers based | ||||
596 | // on module_bytes, as this storage is only guaranteed to be alive as long as | ||||
597 | // this struct is alive. | ||||
598 | struct V8_EXPORT_PRIVATE ModuleWireBytes { | ||||
599 | explicit ModuleWireBytes(base::Vector<const byte> module_bytes) | ||||
600 | : module_bytes_(module_bytes) {} | ||||
601 | ModuleWireBytes(const byte* start, const byte* end) | ||||
602 | : module_bytes_(start, static_cast<int>(end - start)) { | ||||
603 | DCHECK_GE(kMaxInt, end - start)((void) 0); | ||||
604 | } | ||||
605 | |||||
606 | // Get a string stored in the module bytes representing a name. | ||||
607 | WasmName GetNameOrNull(WireBytesRef ref) const; | ||||
608 | |||||
609 | // Get a string stored in the module bytes representing a function name. | ||||
610 | WasmName GetNameOrNull(const WasmFunction* function, | ||||
611 | const WasmModule* module) const; | ||||
612 | |||||
613 | // Checks the given reference is contained within the module bytes. | ||||
614 | bool BoundsCheck(WireBytesRef ref) const { | ||||
615 | uint32_t size = static_cast<uint32_t>(module_bytes_.length()); | ||||
616 | return ref.offset() <= size && ref.length() <= size - ref.offset(); | ||||
617 | } | ||||
618 | |||||
619 | base::Vector<const byte> GetFunctionBytes( | ||||
620 | const WasmFunction* function) const { | ||||
621 | return module_bytes_.SubVector(function->code.offset(), | ||||
622 | function->code.end_offset()); | ||||
623 | } | ||||
624 | |||||
625 | base::Vector<const byte> module_bytes() const { return module_bytes_; } | ||||
626 | const byte* start() const { return module_bytes_.begin(); } | ||||
627 | const byte* end() const { return module_bytes_.end(); } | ||||
628 | size_t length() const { return module_bytes_.length(); } | ||||
629 | |||||
630 | private: | ||||
631 | base::Vector<const byte> module_bytes_; | ||||
632 | }; | ||||
633 | |||||
634 | // A helper for printing out the names of functions. | ||||
635 | struct WasmFunctionName { | ||||
636 | WasmFunctionName(const WasmFunction* function, WasmName name) | ||||
637 | : function_(function), name_(name) {} | ||||
638 | |||||
639 | const WasmFunction* function_; | ||||
640 | const WasmName name_; | ||||
641 | }; | ||||
642 | |||||
643 | std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name); | ||||
644 | |||||
645 | V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate, | ||||
646 | Handle<Context> context); | ||||
647 | |||||
648 | Handle<JSObject> GetTypeForFunction(Isolate* isolate, const FunctionSig* sig, | ||||
649 | bool for_exception = false); | ||||
650 | Handle<JSObject> GetTypeForGlobal(Isolate* isolate, bool is_mutable, | ||||
651 | ValueType type); | ||||
652 | Handle<JSObject> GetTypeForMemory(Isolate* isolate, uint32_t min_size, | ||||
653 | base::Optional<uint32_t> max_size, | ||||
654 | bool shared); | ||||
655 | Handle<JSObject> GetTypeForTable(Isolate* isolate, ValueType type, | ||||
656 | uint32_t min_size, | ||||
657 | base::Optional<uint32_t> max_size); | ||||
658 | Handle<JSArray> GetImports(Isolate* isolate, Handle<WasmModuleObject> module); | ||||
659 | Handle<JSArray> GetExports(Isolate* isolate, Handle<WasmModuleObject> module); | ||||
660 | Handle<JSArray> GetCustomSections(Isolate* isolate, | ||||
661 | Handle<WasmModuleObject> module, | ||||
662 | Handle<String> name, ErrorThrower* thrower); | ||||
663 | |||||
664 | // Get the source position from a given function index and byte offset, | ||||
665 | // for either asm.js or pure Wasm modules. | ||||
666 | int GetSourcePosition(const WasmModule*, uint32_t func_index, | ||||
667 | uint32_t byte_offset, bool is_at_number_conversion); | ||||
668 | |||||
669 | // Translate function index to the index relative to the first declared (i.e. | ||||
670 | // non-imported) function. | ||||
671 | inline int declared_function_index(const WasmModule* module, int func_index) { | ||||
672 | DCHECK_LE(module->num_imported_functions, func_index)((void) 0); | ||||
673 | int declared_idx = func_index - module->num_imported_functions; | ||||
674 | DCHECK_GT(module->num_declared_functions, declared_idx)((void) 0); | ||||
675 | return declared_idx; | ||||
676 | } | ||||
677 | |||||
678 | // TruncatedUserString makes it easy to output names up to a certain length, and | ||||
679 | // output a truncation followed by '...' if they exceed a limit. | ||||
680 | // Use like this: | ||||
681 | // TruncatedUserString<> name (pc, len); | ||||
682 | // printf("... %.*s ...", name.length(), name.start()) | ||||
683 | template <int kMaxLen = 50> | ||||
684 | class TruncatedUserString { | ||||
685 | static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)"); | ||||
686 | |||||
687 | public: | ||||
688 | template <typename T> | ||||
689 | explicit TruncatedUserString(base::Vector<T> name) | ||||
690 | : TruncatedUserString(name.begin(), name.length()) {} | ||||
691 | |||||
692 | TruncatedUserString(const byte* start, size_t len) | ||||
693 | : TruncatedUserString(reinterpret_cast<const char*>(start), len) {} | ||||
694 | |||||
695 | TruncatedUserString(const char* start, size_t len) | ||||
696 | : start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) { | ||||
697 | if (len > static_cast<size_t>(kMaxLen)) { | ||||
698 | memcpy(buffer_, start, kMaxLen - 3); | ||||
699 | memset(buffer_ + kMaxLen - 3, '.', 3); | ||||
700 | start_ = buffer_; | ||||
701 | } | ||||
702 | } | ||||
703 | |||||
704 | const char* start() const { return start_; } | ||||
705 | |||||
706 | int length() const { return length_; } | ||||
707 | |||||
708 | private: | ||||
709 | const char* start_; | ||||
710 | const int length_; | ||||
711 | char buffer_[kMaxLen]; | ||||
712 | }; | ||||
713 | |||||
714 | // Print the signature into the given {buffer}, using {delimiter} as separator | ||||
715 | // between parameter types and return types. If {buffer} is non-empty, it will | ||||
716 | // be null-terminated, even if the signature is cut off. Returns the number of | ||||
717 | // characters written, excluding the terminating null-byte. | ||||
718 | size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*, | ||||
719 | char delimiter = ':'); | ||||
720 | |||||
721 | } // namespace wasm | ||||
722 | } // namespace internal | ||||
723 | } // namespace v8 | ||||
724 | |||||
725 | #endif // V8_WASM_WASM_MODULE_H_ |