1 // Copyright 2017 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 #ifndef V8_WASM_STREAMING_DECODER_H_ 6 #define V8_WASM_STREAMING_DECODER_H_ 7 8 #include <vector> 9 #include "src/isolate.h" 10 #include "src/wasm/module-decoder.h" 11 #include "src/wasm/wasm-objects.h" 12 13 namespace v8 { 14 namespace internal { 15 namespace wasm { 16 17 // This class is an interface for the StreamingDecoder to start the processing 18 // of the incoming module bytes. 19 class V8_EXPORT_PRIVATE StreamingProcessor { 20 public: 21 virtual ~StreamingProcessor() = default; 22 // Process the first 8 bytes of a WebAssembly module. Returns true if the 23 // processing finished successfully and the decoding should continue. 24 virtual bool ProcessModuleHeader(Vector<const uint8_t> bytes, 25 uint32_t offset) = 0; 26 27 // Process all sections but the code section. Returns true if the processing 28 // finished successfully and the decoding should continue. 29 virtual bool ProcessSection(SectionCode section_code, 30 Vector<const uint8_t> bytes, uint32_t offset) = 0; 31 32 // Process the start of the code section. Returns true if the processing 33 // finished successfully and the decoding should continue. 34 virtual bool ProcessCodeSectionHeader(size_t num_functions, 35 uint32_t offset) = 0; 36 37 // Process a function body. Returns true if the processing finished 38 // successfully and the decoding should continue. 39 virtual bool ProcessFunctionBody(Vector<const uint8_t> bytes, 40 uint32_t offset) = 0; 41 42 // Report the end of a chunk. 43 virtual void OnFinishedChunk() = 0; 44 // Report the end of the stream. If the stream was successful, all 45 // received bytes are passed by parameter. If there has been an error, an 46 // empty array is passed. 47 virtual void OnFinishedStream(OwnedVector<uint8_t> bytes) = 0; 48 // Report an error detected in the StreamingDecoder. 49 virtual void OnError(DecodeResult result) = 0; 50 // Report the abortion of the stream. 51 virtual void OnAbort() = 0; 52 }; 53 54 // The StreamingDecoder takes a sequence of byte arrays, each received by a call 55 // of {OnBytesReceived}, and extracts the bytes which belong to section payloads 56 // and function bodies. 57 class V8_EXPORT_PRIVATE StreamingDecoder { 58 public: 59 explicit StreamingDecoder(std::unique_ptr<StreamingProcessor> processor); 60 61 // The buffer passed into OnBytesReceived is owned by the caller. 62 void OnBytesReceived(Vector<const uint8_t> bytes); 63 64 void Finish(); 65 66 void Abort(); 67 68 // Notify the StreamingDecoder that compilation ended and the 69 // StreamingProcessor should not be called anymore. NotifyCompilationEnded()70 void NotifyCompilationEnded() { 71 // We set {ok_} to false to turn all future calls to the StreamingDecoder 72 // into no-ops. 73 ok_ = false; 74 } 75 76 private: 77 // TODO(ahaas): Put the whole private state of the StreamingDecoder into the 78 // cc file (PIMPL design pattern). 79 80 // The SectionBuffer is the data object for the content of a single section. 81 // It stores all bytes of the section (including section id and section 82 // length), and the offset where the actual payload starts. 83 class SectionBuffer { 84 public: 85 // id: The section id. 86 // payload_length: The length of the payload. 87 // 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)88 SectionBuffer(uint32_t module_offset, uint8_t id, size_t payload_length, 89 Vector<const uint8_t> length_bytes) 90 : // ID + length + payload 91 module_offset_(module_offset), 92 length_(1 + length_bytes.length() + payload_length), 93 bytes_(new uint8_t[length_]), 94 payload_offset_(1 + length_bytes.length()) { 95 bytes_[0] = id; 96 memcpy(bytes_.get() + 1, &length_bytes.first(), length_bytes.length()); 97 } 98 section_code()99 SectionCode section_code() const { 100 return static_cast<SectionCode>(bytes_[0]); 101 } 102 module_offset()103 uint32_t module_offset() const { return module_offset_; } bytes()104 uint8_t* bytes() const { return bytes_.get(); } length()105 size_t length() const { return length_; } payload_offset()106 size_t payload_offset() const { return payload_offset_; } payload_length()107 size_t payload_length() const { return length_ - payload_offset_; } payload()108 Vector<const uint8_t> payload() const { 109 return Vector<const uint8_t>(bytes() + payload_offset(), 110 payload_length()); 111 } 112 113 private: 114 uint32_t module_offset_; 115 size_t length_; 116 std::unique_ptr<uint8_t[]> bytes_; 117 size_t payload_offset_; 118 }; 119 120 // The decoding of a stream of wasm module bytes is organized in states. Each 121 // state provides a buffer to store the bytes required for the current state, 122 // information on how many bytes have already been received, how many bytes 123 // are needed, and a {Next} function which starts the next state once all 124 // bytes of the current state were received. 125 // 126 // The states change according to the following state diagram: 127 // 128 // Start 129 // | 130 // | 131 // v 132 // DecodeModuleHeader 133 // | _________________________________________ 134 // | | | 135 // v v | 136 // DecodeSectionID --> DecodeSectionLength --> DecodeSectionPayload 137 // A | 138 // | | (if the section id == code) 139 // | v 140 // | DecodeNumberOfFunctions -- > DecodeFunctionLength 141 // | A | 142 // | | | 143 // | (after all functions were read) | v 144 // ------------------------------------- DecodeFunctionBody 145 // 146 class DecodingState { 147 public: 148 virtual ~DecodingState() = default; 149 150 // Reads the bytes for the current state and returns the number of read 151 // bytes. 152 virtual size_t ReadBytes(StreamingDecoder* streaming, 153 Vector<const uint8_t> bytes); 154 155 // Returns the next state of the streaming decoding. 156 virtual std::unique_ptr<DecodingState> Next( 157 StreamingDecoder* streaming) = 0; 158 // The number of bytes to be received. 159 virtual size_t size() const = 0; 160 // The buffer to store the received bytes. 161 virtual uint8_t* buffer() = 0; 162 // The number of bytes which were already received. offset()163 size_t offset() const { return offset_; } set_offset(size_t value)164 void set_offset(size_t value) { offset_ = value; } 165 // The number of bytes which are still needed. remaining()166 size_t remaining() const { return size() - offset(); } is_finished()167 bool is_finished() const { return offset() == size(); } 168 // A flag to indicate if finishing the streaming decoder is allowed without 169 // error. is_finishing_allowed()170 virtual bool is_finishing_allowed() const { return false; } 171 172 private: 173 size_t offset_ = 0; 174 }; 175 176 // Forward declarations of the concrete states. This is needed so that they 177 // can access private members of the StreamingDecoder. 178 class DecodeVarInt32; 179 class DecodeModuleHeader; 180 class DecodeSectionID; 181 class DecodeSectionLength; 182 class DecodeSectionPayload; 183 class DecodeNumberOfFunctions; 184 class DecodeFunctionLength; 185 class DecodeFunctionBody; 186 187 // Creates a buffer for the next section of the module. CreateNewBuffer(uint32_t module_offset,uint8_t id,size_t length,Vector<const uint8_t> length_bytes)188 SectionBuffer* CreateNewBuffer(uint32_t module_offset, uint8_t id, 189 size_t length, 190 Vector<const uint8_t> length_bytes) { 191 // Check the order of sections. Unknown sections can appear at any position. 192 if (id != kUnknownSectionCode) { 193 if (id < next_section_id_) { 194 Error("Unexpected section"); 195 return nullptr; 196 } 197 next_section_id_ = id + 1; 198 } 199 section_buffers_.emplace_back( 200 new SectionBuffer(module_offset, id, length, length_bytes)); 201 return section_buffers_.back().get(); 202 } 203 Error(DecodeResult result)204 std::unique_ptr<DecodingState> Error(DecodeResult result) { 205 if (ok_) processor_->OnError(std::move(result)); 206 ok_ = false; 207 return std::unique_ptr<DecodingState>(nullptr); 208 } 209 Error(std::string message)210 std::unique_ptr<DecodingState> Error(std::string message) { 211 DecodeResult result(nullptr); 212 result.error(module_offset_ - 1, std::move(message)); 213 return Error(std::move(result)); 214 } 215 ProcessModuleHeader()216 void ProcessModuleHeader() { 217 if (!ok_) return; 218 if (!processor_->ProcessModuleHeader( 219 Vector<const uint8_t>(state_->buffer(), 220 static_cast<int>(state_->size())), 221 0)) { 222 ok_ = false; 223 } 224 } 225 ProcessSection(SectionBuffer * buffer)226 void ProcessSection(SectionBuffer* buffer) { 227 if (!ok_) return; 228 if (!processor_->ProcessSection( 229 buffer->section_code(), buffer->payload(), 230 buffer->module_offset() + 231 static_cast<uint32_t>(buffer->payload_offset()))) { 232 ok_ = false; 233 } 234 } 235 StartCodeSection(size_t num_functions)236 void StartCodeSection(size_t num_functions) { 237 if (!ok_) return; 238 // The offset passed to {ProcessCodeSectionHeader} is an error offset and 239 // not the start offset of a buffer. Therefore we need the -1 here. 240 if (!processor_->ProcessCodeSectionHeader(num_functions, 241 module_offset() - 1)) { 242 ok_ = false; 243 } 244 } 245 ProcessFunctionBody(Vector<const uint8_t> bytes,uint32_t module_offset)246 void ProcessFunctionBody(Vector<const uint8_t> bytes, 247 uint32_t module_offset) { 248 if (!ok_) return; 249 if (!processor_->ProcessFunctionBody(bytes, module_offset)) ok_ = false; 250 } 251 ok()252 bool ok() const { return ok_; } 253 module_offset()254 uint32_t module_offset() const { return module_offset_; } 255 256 std::unique_ptr<StreamingProcessor> processor_; 257 bool ok_ = true; 258 std::unique_ptr<DecodingState> state_; 259 std::vector<std::unique_ptr<SectionBuffer>> section_buffers_; 260 uint32_t module_offset_ = 0; 261 size_t total_size_ = 0; 262 uint8_t next_section_id_ = kFirstSectionInModule; 263 264 DISALLOW_COPY_AND_ASSIGN(StreamingDecoder); 265 }; 266 267 } // namespace wasm 268 } // namespace internal 269 } // namespace v8 270 271 #endif // V8_WASM_STREAMING_DECODER_H_ 272