Line data Source code
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 <memory>
9 : #include <vector>
10 :
11 : #include "src/base/macros.h"
12 : #include "src/vector.h"
13 : #include "src/wasm/compilation-environment.h"
14 : #include "src/wasm/wasm-constants.h"
15 : #include "src/wasm/wasm-result.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 : namespace wasm {
20 : class NativeModule;
21 :
22 : // This class is an interface for the StreamingDecoder to start the processing
23 : // of the incoming module bytes.
24 1582 : class V8_EXPORT_PRIVATE StreamingProcessor {
25 : public:
26 1582 : virtual ~StreamingProcessor() = default;
27 : // Process the first 8 bytes of a WebAssembly module. Returns true if the
28 : // processing finished successfully and the decoding should continue.
29 : virtual bool ProcessModuleHeader(Vector<const uint8_t> bytes,
30 : uint32_t offset) = 0;
31 :
32 : // Process all sections but the code section. Returns true if the processing
33 : // finished successfully and the decoding should continue.
34 : virtual bool ProcessSection(SectionCode section_code,
35 : Vector<const uint8_t> bytes, uint32_t offset) = 0;
36 :
37 : // Process the start of the code section. Returns true if the processing
38 : // finished successfully and the decoding should continue.
39 : virtual bool ProcessCodeSectionHeader(int num_functions, uint32_t offset,
40 : std::shared_ptr<WireBytesStorage>) = 0;
41 :
42 : // Process a function body. Returns true if the processing finished
43 : // successfully and the decoding should continue.
44 : virtual bool ProcessFunctionBody(Vector<const uint8_t> bytes,
45 : uint32_t offset) = 0;
46 :
47 : // Report the end of a chunk.
48 : virtual void OnFinishedChunk() = 0;
49 : // Report the end of the stream. If the stream was successful, all
50 : // received bytes are passed by parameter. If there has been an error, an
51 : // empty array is passed.
52 : virtual void OnFinishedStream(OwnedVector<uint8_t> bytes) = 0;
53 : // Report an error detected in the StreamingDecoder.
54 : virtual void OnError(const WasmError&) = 0;
55 : // Report the abortion of the stream.
56 : virtual void OnAbort() = 0;
57 :
58 : // Attempt to deserialize the module. Supports embedder caching.
59 : virtual bool Deserialize(Vector<const uint8_t> module_bytes,
60 : Vector<const uint8_t> wire_bytes) = 0;
61 : };
62 :
63 : // The StreamingDecoder takes a sequence of byte arrays, each received by a call
64 : // of {OnBytesReceived}, and extracts the bytes which belong to section payloads
65 : // and function bodies.
66 4746 : class V8_EXPORT_PRIVATE StreamingDecoder {
67 : public:
68 : explicit StreamingDecoder(std::unique_ptr<StreamingProcessor> processor);
69 :
70 : // The buffer passed into OnBytesReceived is owned by the caller.
71 : void OnBytesReceived(Vector<const uint8_t> bytes);
72 :
73 : void Finish();
74 :
75 : void Abort();
76 :
77 : // Notify the StreamingDecoder that compilation ended and the
78 : // StreamingProcessor should not be called anymore.
79 : void NotifyCompilationEnded() { Fail(); }
80 :
81 : // Caching support.
82 : // Sets the callback that is called after the module is fully compiled.
83 : using ModuleCompiledCallback =
84 : std::function<void(const std::shared_ptr<NativeModule>&)>;
85 : void SetModuleCompiledCallback(ModuleCompiledCallback callback);
86 : // Passes previously compiled module bytes from the embedder's cache.
87 : bool SetCompiledModuleBytes(Vector<const uint8_t> compiled_module_bytes);
88 :
89 : void NotifyNativeModuleCreated(
90 : const std::shared_ptr<NativeModule>& native_module);
91 :
92 : private:
93 : // TODO(ahaas): Put the whole private state of the StreamingDecoder into the
94 : // cc file (PIMPL design pattern).
95 :
96 : // The SectionBuffer is the data object for the content of a single section.
97 : // It stores all bytes of the section (including section id and section
98 : // length), and the offset where the actual payload starts.
99 4062 : class SectionBuffer : public WireBytesStorage {
100 : public:
101 : // id: The section id.
102 : // payload_length: The length of the payload.
103 : // length_bytes: The section length, as it is encoded in the module bytes.
104 2031 : SectionBuffer(uint32_t module_offset, uint8_t id, size_t payload_length,
105 : Vector<const uint8_t> length_bytes)
106 : : // ID + length + payload
107 : module_offset_(module_offset),
108 2031 : bytes_(OwnedVector<uint8_t>::New(1 + length_bytes.length() +
109 : payload_length)),
110 6093 : payload_offset_(1 + length_bytes.length()) {
111 2031 : bytes_.start()[0] = id;
112 4062 : memcpy(bytes_.start() + 1, &length_bytes.first(), length_bytes.length());
113 2031 : }
114 :
115 : SectionCode section_code() const {
116 1329 : return static_cast<SectionCode>(bytes_.start()[0]);
117 : }
118 :
119 313 : Vector<const uint8_t> GetCode(WireBytesRef ref) const final {
120 : DCHECK_LE(module_offset_, ref.offset());
121 313 : uint32_t offset_in_code_buffer = ref.offset() - module_offset_;
122 : return bytes().SubVector(offset_in_code_buffer,
123 626 : offset_in_code_buffer + ref.length());
124 : }
125 :
126 : uint32_t module_offset() const { return module_offset_; }
127 : Vector<uint8_t> bytes() const { return bytes_.as_vector(); }
128 3340 : Vector<uint8_t> payload() const { return bytes() + payload_offset_; }
129 : size_t length() const { return bytes_.size(); }
130 : size_t payload_offset() const { return payload_offset_; }
131 :
132 : private:
133 : const uint32_t module_offset_;
134 : const OwnedVector<uint8_t> bytes_;
135 : const size_t payload_offset_;
136 : };
137 :
138 : // The decoding of a stream of wasm module bytes is organized in states. Each
139 : // state provides a buffer to store the bytes required for the current state,
140 : // information on how many bytes have already been received, how many bytes
141 : // are needed, and a {Next} function which starts the next state once all
142 : // bytes of the current state were received.
143 : //
144 : // The states change according to the following state diagram:
145 : //
146 : // Start
147 : // |
148 : // |
149 : // v
150 : // DecodeModuleHeader
151 : // | _________________________________________
152 : // | | |
153 : // v v |
154 : // DecodeSectionID --> DecodeSectionLength --> DecodeSectionPayload
155 : // A |
156 : // | | (if the section id == code)
157 : // | v
158 : // | DecodeNumberOfFunctions -- > DecodeFunctionLength
159 : // | A |
160 : // | | |
161 : // | (after all functions were read) | v
162 : // ------------------------------------- DecodeFunctionBody
163 : //
164 9959 : class DecodingState {
165 : public:
166 6309 : virtual ~DecodingState() = default;
167 :
168 : // Reads the bytes for the current state and returns the number of read
169 : // bytes.
170 : virtual size_t ReadBytes(StreamingDecoder* streaming,
171 : Vector<const uint8_t> bytes);
172 :
173 : // Returns the next state of the streaming decoding.
174 : virtual std::unique_ptr<DecodingState> Next(
175 : StreamingDecoder* streaming) = 0;
176 : // The buffer to store the received bytes.
177 : virtual Vector<uint8_t> buffer() = 0;
178 : // The number of bytes which were already received.
179 : size_t offset() const { return offset_; }
180 10192 : void set_offset(size_t value) { offset_ = value; }
181 : // A flag to indicate if finishing the streaming decoder is allowed without
182 : // error.
183 95 : virtual bool is_finishing_allowed() const { return false; }
184 :
185 : private:
186 : size_t offset_ = 0;
187 : };
188 :
189 : // Forward declarations of the concrete states. This is needed so that they
190 : // can access private members of the StreamingDecoder.
191 : class DecodeVarInt32;
192 : class DecodeModuleHeader;
193 : class DecodeSectionID;
194 : class DecodeSectionLength;
195 : class DecodeSectionPayload;
196 : class DecodeNumberOfFunctions;
197 : class DecodeFunctionLength;
198 : class DecodeFunctionBody;
199 :
200 : // Creates a buffer for the next section of the module.
201 : SectionBuffer* CreateNewBuffer(uint32_t module_offset, uint8_t section_id,
202 : size_t length,
203 : Vector<const uint8_t> length_bytes);
204 :
205 406 : std::unique_ptr<DecodingState> Error(const WasmError& error) {
206 406 : if (ok()) processor_->OnError(error);
207 : Fail();
208 406 : return std::unique_ptr<DecodingState>(nullptr);
209 : }
210 :
211 367 : std::unique_ptr<DecodingState> Error(std::string message) {
212 1468 : return Error(WasmError{module_offset_ - 1, std::move(message)});
213 : }
214 :
215 1501 : void ProcessModuleHeader() {
216 1501 : if (!ok()) return;
217 4503 : if (!processor_->ProcessModuleHeader(state_->buffer(), 0)) Fail();
218 : }
219 :
220 1329 : void ProcessSection(SectionBuffer* buffer) {
221 1329 : if (!ok()) return;
222 2658 : if (!processor_->ProcessSection(
223 : buffer->section_code(), buffer->payload(),
224 : buffer->module_offset() +
225 1329 : static_cast<uint32_t>(buffer->payload_offset()))) {
226 : Fail();
227 : }
228 : }
229 :
230 536 : void StartCodeSection(int num_functions,
231 : std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
232 536 : if (!ok()) return;
233 : // The offset passed to {ProcessCodeSectionHeader} is an error offset and
234 : // not the start offset of a buffer. Therefore we need the -1 here.
235 1072 : if (!processor_->ProcessCodeSectionHeader(num_functions,
236 : module_offset() - 1,
237 536 : std::move(wire_bytes_storage))) {
238 : Fail();
239 : }
240 : }
241 :
242 849 : void ProcessFunctionBody(Vector<const uint8_t> bytes,
243 : uint32_t module_offset) {
244 849 : if (!ok()) return;
245 849 : if (!processor_->ProcessFunctionBody(bytes, module_offset)) Fail();
246 : }
247 :
248 : void Fail() {
249 : // We reset the {processor_} field to represent failure. This also ensures
250 : // that we do not accidentally call further methods on the processor after
251 : // failure.
252 : processor_.reset();
253 : }
254 :
255 : bool ok() const { return processor_ != nullptr; }
256 :
257 : uint32_t module_offset() const { return module_offset_; }
258 :
259 : bool deserializing() const { return !compiled_module_bytes_.empty(); }
260 :
261 : std::unique_ptr<StreamingProcessor> processor_;
262 : std::unique_ptr<DecodingState> state_;
263 : std::vector<std::shared_ptr<SectionBuffer>> section_buffers_;
264 : bool code_section_processed_ = false;
265 : uint32_t module_offset_ = 0;
266 : size_t total_size_ = 0;
267 :
268 : // Caching support.
269 : ModuleCompiledCallback module_compiled_callback_ = nullptr;
270 : // We need wire bytes in an array for deserializing cached modules.
271 : std::vector<uint8_t> wire_bytes_for_deserializing_;
272 : Vector<const uint8_t> compiled_module_bytes_;
273 :
274 : DISALLOW_COPY_AND_ASSIGN(StreamingDecoder);
275 : };
276 :
277 : } // namespace wasm
278 : } // namespace internal
279 : } // namespace v8
280 :
281 : #endif // V8_WASM_STREAMING_DECODER_H_
|