| // Copyright 2017 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. |
| |
| #ifndef V8_WASM_STREAMING_DECODER_H_ |
| #define V8_WASM_STREAMING_DECODER_H_ |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "src/base/macros.h" |
| #include "src/utils/vector.h" |
| #include "src/wasm/compilation-environment.h" |
| #include "src/wasm/wasm-constants.h" |
| #include "src/wasm/wasm-result.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| class NativeModule; |
| |
| // This class is an interface for the StreamingDecoder to start the processing |
| // of the incoming module bytes. |
| class V8_EXPORT_PRIVATE StreamingProcessor { |
| public: |
| virtual ~StreamingProcessor() = default; |
| // Process the first 8 bytes of a WebAssembly module. Returns true if the |
| // processing finished successfully and the decoding should continue. |
| virtual bool ProcessModuleHeader(Vector<const uint8_t> bytes, |
| uint32_t offset) = 0; |
| |
| // Process all sections but the code section. Returns true if the processing |
| // finished successfully and the decoding should continue. |
| virtual bool ProcessSection(SectionCode section_code, |
| Vector<const uint8_t> bytes, uint32_t offset) = 0; |
| |
| // Process the start of the code section. Returns true if the processing |
| // finished successfully and the decoding should continue. |
| virtual bool ProcessCodeSectionHeader(int num_functions, uint32_t offset, |
| std::shared_ptr<WireBytesStorage>) = 0; |
| |
| // Process a function body. Returns true if the processing finished |
| // successfully and the decoding should continue. |
| virtual bool ProcessFunctionBody(Vector<const uint8_t> bytes, |
| uint32_t offset) = 0; |
| |
| // Report the end of a chunk. |
| virtual void OnFinishedChunk() = 0; |
| // Report the end of the stream. If the stream was successful, all |
| // received bytes are passed by parameter. If there has been an error, an |
| // empty array is passed. |
| virtual void OnFinishedStream(OwnedVector<uint8_t> bytes) = 0; |
| // Report an error detected in the StreamingDecoder. |
| virtual void OnError(const WasmError&) = 0; |
| // Report the abortion of the stream. |
| virtual void OnAbort() = 0; |
| |
| // Attempt to deserialize the module. Supports embedder caching. |
| virtual bool Deserialize(Vector<const uint8_t> module_bytes, |
| Vector<const uint8_t> wire_bytes) = 0; |
| }; |
| |
| // The StreamingDecoder takes a sequence of byte arrays, each received by a call |
| // of {OnBytesReceived}, and extracts the bytes which belong to section payloads |
| // and function bodies. |
| class V8_EXPORT_PRIVATE StreamingDecoder { |
| public: |
| explicit StreamingDecoder(std::unique_ptr<StreamingProcessor> processor); |
| |
| // The buffer passed into OnBytesReceived is owned by the caller. |
| void OnBytesReceived(Vector<const uint8_t> bytes); |
| |
| void Finish(); |
| |
| void Abort(); |
| |
| // Notify the StreamingDecoder that compilation ended and the |
| // StreamingProcessor should not be called anymore. |
| void NotifyCompilationEnded() { Fail(); } |
| |
| // Caching support. |
| // Sets the callback that is called after the module is fully compiled. |
| using ModuleCompiledCallback = |
| std::function<void(const std::shared_ptr<NativeModule>&)>; |
| void SetModuleCompiledCallback(ModuleCompiledCallback callback); |
| // Passes previously compiled module bytes from the embedder's cache. |
| bool SetCompiledModuleBytes(Vector<const uint8_t> compiled_module_bytes); |
| |
| void NotifyNativeModuleCreated( |
| const std::shared_ptr<NativeModule>& native_module); |
| |
| private: |
| // TODO(ahaas): Put the whole private state of the StreamingDecoder into the |
| // cc file (PIMPL design pattern). |
| |
| // The SectionBuffer is the data object for the content of a single section. |
| // It stores all bytes of the section (including section id and section |
| // length), and the offset where the actual payload starts. |
| class SectionBuffer : public WireBytesStorage { |
| public: |
| // id: The section id. |
| // payload_length: The length of the payload. |
| // length_bytes: The section length, as it is encoded in the module bytes. |
| SectionBuffer(uint32_t module_offset, uint8_t id, size_t payload_length, |
| Vector<const uint8_t> length_bytes) |
| : // ID + length + payload |
| module_offset_(module_offset), |
| bytes_(OwnedVector<uint8_t>::New(1 + length_bytes.length() + |
| payload_length)), |
| payload_offset_(1 + length_bytes.length()) { |
| bytes_.start()[0] = id; |
| memcpy(bytes_.start() + 1, &length_bytes.first(), length_bytes.length()); |
| } |
| |
| SectionCode section_code() const { |
| return static_cast<SectionCode>(bytes_.start()[0]); |
| } |
| |
| Vector<const uint8_t> GetCode(WireBytesRef ref) const final { |
| DCHECK_LE(module_offset_, ref.offset()); |
| uint32_t offset_in_code_buffer = ref.offset() - module_offset_; |
| return bytes().SubVector(offset_in_code_buffer, |
| offset_in_code_buffer + ref.length()); |
| } |
| |
| uint32_t module_offset() const { return module_offset_; } |
| Vector<uint8_t> bytes() const { return bytes_.as_vector(); } |
| Vector<uint8_t> payload() const { return bytes() + payload_offset_; } |
| size_t length() const { return bytes_.size(); } |
| size_t payload_offset() const { return payload_offset_; } |
| |
| private: |
| const uint32_t module_offset_; |
| const OwnedVector<uint8_t> bytes_; |
| const size_t payload_offset_; |
| }; |
| |
| // The decoding of a stream of wasm module bytes is organized in states. Each |
| // state provides a buffer to store the bytes required for the current state, |
| // information on how many bytes have already been received, how many bytes |
| // are needed, and a {Next} function which starts the next state once all |
| // bytes of the current state were received. |
| // |
| // The states change according to the following state diagram: |
| // |
| // Start |
| // | |
| // | |
| // v |
| // DecodeModuleHeader |
| // | _________________________________________ |
| // | | | |
| // v v | |
| // DecodeSectionID --> DecodeSectionLength --> DecodeSectionPayload |
| // A | |
| // | | (if the section id == code) |
| // | v |
| // | DecodeNumberOfFunctions -- > DecodeFunctionLength |
| // | A | |
| // | | | |
| // | (after all functions were read) | v |
| // ------------------------------------- DecodeFunctionBody |
| // |
| class DecodingState { |
| public: |
| virtual ~DecodingState() = default; |
| |
| // Reads the bytes for the current state and returns the number of read |
| // bytes. |
| virtual size_t ReadBytes(StreamingDecoder* streaming, |
| Vector<const uint8_t> bytes); |
| |
| // Returns the next state of the streaming decoding. |
| virtual std::unique_ptr<DecodingState> Next( |
| StreamingDecoder* streaming) = 0; |
| // The buffer to store the received bytes. |
| virtual Vector<uint8_t> buffer() = 0; |
| // The number of bytes which were already received. |
| size_t offset() const { return offset_; } |
| void set_offset(size_t value) { offset_ = value; } |
| // A flag to indicate if finishing the streaming decoder is allowed without |
| // error. |
| virtual bool is_finishing_allowed() const { return false; } |
| |
| private: |
| size_t offset_ = 0; |
| }; |
| |
| // Forward declarations of the concrete states. This is needed so that they |
| // can access private members of the StreamingDecoder. |
| class DecodeVarInt32; |
| class DecodeModuleHeader; |
| class DecodeSectionID; |
| class DecodeSectionLength; |
| class DecodeSectionPayload; |
| class DecodeNumberOfFunctions; |
| class DecodeFunctionLength; |
| class DecodeFunctionBody; |
| |
| // Creates a buffer for the next section of the module. |
| SectionBuffer* CreateNewBuffer(uint32_t module_offset, uint8_t section_id, |
| size_t length, |
| Vector<const uint8_t> length_bytes); |
| |
| std::unique_ptr<DecodingState> Error(const WasmError& error) { |
| if (ok()) processor_->OnError(error); |
| Fail(); |
| return std::unique_ptr<DecodingState>(nullptr); |
| } |
| |
| std::unique_ptr<DecodingState> Error(std::string message) { |
| return Error(WasmError{module_offset_ - 1, std::move(message)}); |
| } |
| |
| void ProcessModuleHeader() { |
| if (!ok()) return; |
| if (!processor_->ProcessModuleHeader(state_->buffer(), 0)) Fail(); |
| } |
| |
| void ProcessSection(SectionBuffer* buffer) { |
| if (!ok()) return; |
| if (!processor_->ProcessSection( |
| buffer->section_code(), buffer->payload(), |
| buffer->module_offset() + |
| static_cast<uint32_t>(buffer->payload_offset()))) { |
| Fail(); |
| } |
| } |
| |
| void StartCodeSection(int num_functions, |
| std::shared_ptr<WireBytesStorage> wire_bytes_storage) { |
| if (!ok()) return; |
| // The offset passed to {ProcessCodeSectionHeader} is an error offset and |
| // not the start offset of a buffer. Therefore we need the -1 here. |
| if (!processor_->ProcessCodeSectionHeader(num_functions, |
| module_offset() - 1, |
| std::move(wire_bytes_storage))) { |
| Fail(); |
| } |
| } |
| |
| void ProcessFunctionBody(Vector<const uint8_t> bytes, |
| uint32_t module_offset) { |
| if (!ok()) return; |
| if (!processor_->ProcessFunctionBody(bytes, module_offset)) Fail(); |
| } |
| |
| void Fail() { |
| // We reset the {processor_} field to represent failure. This also ensures |
| // that we do not accidentally call further methods on the processor after |
| // failure. |
| processor_.reset(); |
| } |
| |
| bool ok() const { return processor_ != nullptr; } |
| |
| uint32_t module_offset() const { return module_offset_; } |
| |
| bool deserializing() const { return !compiled_module_bytes_.empty(); } |
| |
| std::unique_ptr<StreamingProcessor> processor_; |
| std::unique_ptr<DecodingState> state_; |
| std::vector<std::shared_ptr<SectionBuffer>> section_buffers_; |
| bool code_section_processed_ = false; |
| uint32_t module_offset_ = 0; |
| size_t total_size_ = 0; |
| |
| // Caching support. |
| ModuleCompiledCallback module_compiled_callback_ = nullptr; |
| // We need wire bytes in an array for deserializing cached modules. |
| std::vector<uint8_t> wire_bytes_for_deserializing_; |
| Vector<const uint8_t> compiled_module_bytes_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StreamingDecoder); |
| }; |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_WASM_STREAMING_DECODER_H_ |