// Copyright 2015 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #if !V8_ENABLE_WEBASSEMBLY #error This header should only be included if WebAssembly is enabled. #endif // !V8_ENABLE_WEBASSEMBLY #ifndef V8_WASM_MODULE_DECODER_H_ #define V8_WASM_MODULE_DECODER_H_ #include <memory> #include "src/common/globals.h" #include "src/logging/metrics.h" #include "src/wasm/function-body-decoder.h" #include "src/wasm/wasm-constants.h" #include "src/wasm/wasm-features.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-result.h" namespace v8 { namespace internal { class Counters; namespace wasm { struct CompilationEnv; inline bool IsValidSectionCode(uint8_t byte) { return kTypeSectionCode <= byte && byte <= kLastKnownModuleSection; } const char* SectionName(SectionCode code); using ModuleResult = Result<std::shared_ptr<WasmModule>>; using FunctionResult = Result<std::unique_ptr<WasmFunction>>; using FunctionOffsets = std::vector<std::pair<int, int>>; using FunctionOffsetsResult = Result<FunctionOffsets>; struct AsmJsOffsetEntry { int byte_offset; int source_position_call; int source_position_number_conversion; }; struct AsmJsOffsetFunctionEntries { int start_offset; int end_offset; std::vector<AsmJsOffsetEntry> entries; }; struct AsmJsOffsets { std::vector<AsmJsOffsetFunctionEntries> functions; }; using AsmJsOffsetsResult = Result<AsmJsOffsets>; // The class names "NameAssoc", "NameMap", and "IndirectNameMap" match // the terms used by the spec: // https://webassembly.github.io/spec/core/bikeshed/index.html#name-section%E2%91%A0 class NameAssoc { public: NameAssoc(int index, WireBytesRef name) : index_(index), name_(name) {} int index() const { return index_; } WireBytesRef name() const { return name_; } struct IndexLess { bool operator()(const NameAssoc& a, const NameAssoc& b) const { return a.index() < b.index(); } }; private: int index_; WireBytesRef name_; }; class NameMap { public: // For performance reasons, {NameMap} should not be copied. MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(NameMap); explicit NameMap(std::vector<NameAssoc> names) : names_(std::move(names)) { DCHECK( std::is_sorted(names_.begin(), names_.end(), NameAssoc::IndexLess{})); } WireBytesRef GetName(int index) { auto it = std::lower_bound(names_.begin(), names_.end(), NameAssoc{index, {}}, NameAssoc::IndexLess{}); if (it == names_.end()) return {}; if (it->index() != index) return {}; return it->name(); } private: std::vector<NameAssoc> names_; }; class IndirectNameMapEntry : public NameMap { public: // For performance reasons, {IndirectNameMapEntry} should not be copied. MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(IndirectNameMapEntry); IndirectNameMapEntry(int index, std::vector<NameAssoc> names) : NameMap(std::move(names)), index_(index) {} int index() const { return index_; } struct IndexLess { bool operator()(const IndirectNameMapEntry& a, const IndirectNameMapEntry& b) const { return a.index() < b.index(); } }; private: int index_; }; class IndirectNameMap { public: // For performance reasons, {IndirectNameMap} should not be copied. MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(IndirectNameMap); explicit IndirectNameMap(std::vector<IndirectNameMapEntry> functions) : functions_(std::move(functions)) { DCHECK(std::is_sorted(functions_.begin(), functions_.end(), IndirectNameMapEntry::IndexLess{})); } WireBytesRef GetName(int function_index, int local_index) { auto it = std::lower_bound(functions_.begin(), functions_.end(), IndirectNameMapEntry{function_index, {}}, IndirectNameMapEntry::IndexLess{}); if (it == functions_.end()) return {}; if (it->index() != function_index) return {}; return it->GetName(local_index); } private: std::vector<IndirectNameMapEntry> functions_; }; enum class DecodingMethod { kSync, kAsync, kSyncStream, kAsyncStream, kDeserialize }; // Decodes the bytes of a wasm module between {module_start} and {module_end}. V8_EXPORT_PRIVATE ModuleResult DecodeWasmModule( const WasmFeatures& enabled, const byte* module_start, const byte* module_end, bool verify_functions, ModuleOrigin origin, Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, v8::metrics::Recorder::ContextId context_id, DecodingMethod decoding_method, AccountingAllocator* allocator); // Exposed for testing. Decodes a single function signature, allocating it // in the given zone. Returns {nullptr} upon failure. V8_EXPORT_PRIVATE const FunctionSig* DecodeWasmSignatureForTesting( const WasmFeatures& enabled, Zone* zone, const byte* start, const byte* end); // Decodes the bytes of a wasm function between // {function_start} and {function_end}. V8_EXPORT_PRIVATE FunctionResult DecodeWasmFunctionForTesting( const WasmFeatures& enabled, Zone* zone, const ModuleWireBytes& wire_bytes, const WasmModule* module, const byte* function_start, const byte* function_end, Counters* counters); V8_EXPORT_PRIVATE ConstantExpression DecodeWasmInitExprForTesting(const WasmFeatures& enabled, const byte* start, const byte* end, ValueType expected); struct CustomSectionOffset { WireBytesRef section; WireBytesRef name; WireBytesRef payload; }; V8_EXPORT_PRIVATE std::vector<CustomSectionOffset> DecodeCustomSections( const byte* start, const byte* end); // Extracts the mapping from wasm byte offset to asm.js source position per // function. AsmJsOffsetsResult DecodeAsmJsOffsets( base::Vector<const uint8_t> encoded_offsets); // Decode the function names from the name section. Returns the result as an // unordered map. Only names with valid utf8 encoding are stored and conflicts // are resolved by choosing the last name read. void DecodeFunctionNames(const byte* module_start, const byte* module_end, std::unordered_map<uint32_t, WireBytesRef>* names); // Decode the requested subsection of the name section. // The result will be empty if no name section is present. On encountering an // error in the name section, returns all information decoded up to the first // error. NameMap DecodeNameMap(base::Vector<const uint8_t> module_bytes, uint8_t name_section_kind); IndirectNameMap DecodeIndirectNameMap(base::Vector<const uint8_t> module_bytes, uint8_t name_section_kind); class ModuleDecoderImpl; class ModuleDecoder { public: explicit ModuleDecoder(const WasmFeatures& enabled); ~ModuleDecoder(); void StartDecoding(Counters* counters, std::shared_ptr<metrics::Recorder> metrics_recorder, v8::metrics::Recorder::ContextId context_id, AccountingAllocator* allocator, ModuleOrigin origin = ModuleOrigin::kWasmOrigin); void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint32_t offset); void DecodeSection(SectionCode section_code, base::Vector<const uint8_t> bytes, uint32_t offset, bool verify_functions = true); void StartCodeSection(); bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset); void DecodeFunctionBody(uint32_t index, uint32_t size, uint32_t offset, bool verify_functions = true); ModuleResult FinishDecoding(bool verify_functions = true); void set_code_section(uint32_t offset, uint32_t size); const std::shared_ptr<WasmModule>& shared_module() const; WasmModule* module() const { return shared_module().get(); } bool ok(); // Translates the unknown section that decoder is pointing to to an extended // SectionCode if the unknown section is known to decoder. // The decoder is expected to point after the section length and just before // the identifier string of the unknown section. // The return value is the number of bytes that were consumed. static size_t IdentifyUnknownSection(ModuleDecoder* decoder, base::Vector<const uint8_t> bytes, uint32_t offset, SectionCode* result); private: const WasmFeatures enabled_features_; std::unique_ptr<ModuleDecoderImpl> impl_; }; } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_MODULE_DECODER_H_