blob: e9b987f72ce87cedacc225f6d6a95682830c4a4b [file] [log] [blame]
// 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.
#include "src/wasm/streaming-decoder.h"
#include "src/handles/handles.h"
#include "src/objects/descriptor-array.h"
#include "src/objects/dictionary.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/decoder.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-limits.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
#define TRACE_STREAMING(...) \
do { \
if (FLAG_trace_wasm_streaming) PrintF(__VA_ARGS__); \
} while (false)
namespace v8 {
namespace internal {
namespace wasm {
class V8_EXPORT_PRIVATE AsyncStreamingDecoder : public StreamingDecoder {
public:
explicit AsyncStreamingDecoder(std::unique_ptr<StreamingProcessor> processor);
AsyncStreamingDecoder(const AsyncStreamingDecoder&) = delete;
AsyncStreamingDecoder& operator=(const AsyncStreamingDecoder&) = delete;
// The buffer passed into OnBytesReceived is owned by the caller.
void OnBytesReceived(Vector<const uint8_t> bytes) override;
void Finish() override;
void Abort() override;
// Notify the StreamingDecoder that compilation ended and the
// StreamingProcessor should not be called anymore.
void NotifyCompilationEnded() override { Fail(); }
void NotifyNativeModuleCreated(
const std::shared_ptr<NativeModule>& native_module) override;
private:
// 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>::NewForOverwrite(
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(AsyncStreamingDecoder* streaming,
Vector<const uint8_t> bytes);
// Returns the next state of the streaming decoding.
virtual std::unique_ptr<DecodingState> Next(
AsyncStreamingDecoder* 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 AsyncStreamingDecoder.
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,
int code_section_length) {
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),
code_section_length)) {
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_; }
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;
bool stream_finished_ = false;
// We need wire bytes in an array for deserializing cached modules.
std::vector<uint8_t> wire_bytes_for_deserializing_;
};
void AsyncStreamingDecoder::OnBytesReceived(Vector<const uint8_t> bytes) {
if (deserializing()) {
wire_bytes_for_deserializing_.insert(wire_bytes_for_deserializing_.end(),
bytes.begin(), bytes.end());
return;
}
TRACE_STREAMING("OnBytesReceived(%zu bytes)\n", bytes.size());
size_t current = 0;
while (ok() && current < bytes.size()) {
size_t num_bytes =
state_->ReadBytes(this, bytes.SubVector(current, bytes.size()));
current += num_bytes;
module_offset_ += num_bytes;
if (state_->offset() == state_->buffer().size()) {
state_ = state_->Next(this);
}
}
total_size_ += bytes.size();
if (ok()) {
processor_->OnFinishedChunk();
}
}
size_t AsyncStreamingDecoder::DecodingState::ReadBytes(
AsyncStreamingDecoder* streaming, Vector<const uint8_t> bytes) {
Vector<uint8_t> remaining_buf = buffer() + offset();
size_t num_bytes = std::min(bytes.size(), remaining_buf.size());
TRACE_STREAMING("ReadBytes(%zu bytes)\n", num_bytes);
memcpy(remaining_buf.begin(), &bytes.first(), num_bytes);
set_offset(offset() + num_bytes);
return num_bytes;
}
void AsyncStreamingDecoder::Finish() {
TRACE_STREAMING("Finish\n");
DCHECK(!stream_finished_);
stream_finished_ = true;
if (!ok()) return;
if (deserializing()) {
Vector<const uint8_t> wire_bytes = VectorOf(wire_bytes_for_deserializing_);
// Try to deserialize the module from wire bytes and module bytes.
if (processor_->Deserialize(compiled_module_bytes_, wire_bytes)) return;
// Deserialization failed. Restart decoding using |wire_bytes|.
compiled_module_bytes_ = {};
DCHECK(!deserializing());
OnBytesReceived(wire_bytes);
// The decoder has received all wire bytes; fall through and finish.
}
if (!state_->is_finishing_allowed()) {
// The byte stream ended too early, we report an error.
Error("unexpected end of stream");
return;
}
OwnedVector<uint8_t> bytes =
OwnedVector<uint8_t>::NewForOverwrite(total_size_);
uint8_t* cursor = bytes.start();
{
#define BYTES(x) (x & 0xFF), (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF
uint8_t module_header[]{BYTES(kWasmMagic), BYTES(kWasmVersion)};
#undef BYTES
memcpy(cursor, module_header, arraysize(module_header));
cursor += arraysize(module_header);
}
for (const auto& buffer : section_buffers_) {
DCHECK_LE(cursor - bytes.start() + buffer->length(), total_size_);
memcpy(cursor, buffer->bytes().begin(), buffer->length());
cursor += buffer->length();
}
processor_->OnFinishedStream(std::move(bytes));
}
void AsyncStreamingDecoder::Abort() {
TRACE_STREAMING("Abort\n");
if (stream_finished_) return;
stream_finished_ = true;
if (!ok()) return; // Failed already.
processor_->OnAbort();
Fail();
}
namespace {
class TopTierCompiledCallback {
public:
TopTierCompiledCallback(
std::weak_ptr<NativeModule> native_module,
AsyncStreamingDecoder::ModuleCompiledCallback callback)
: native_module_(std::move(native_module)),
callback_(std::move(callback)) {}
void operator()(CompilationEvent event) const {
if (event != CompilationEvent::kFinishedTopTierCompilation) return;
// If the native module is still alive, get back a shared ptr and call the
// callback.
if (std::shared_ptr<NativeModule> native_module = native_module_.lock()) {
callback_(native_module);
}
#ifdef DEBUG
DCHECK(!called_);
called_ = true;
#endif
}
private:
const std::weak_ptr<NativeModule> native_module_;
const AsyncStreamingDecoder::ModuleCompiledCallback callback_;
#ifdef DEBUG
mutable bool called_ = false;
#endif
};
} // namespace
void AsyncStreamingDecoder::NotifyNativeModuleCreated(
const std::shared_ptr<NativeModule>& native_module) {
if (!module_compiled_callback_) return;
auto* comp_state = native_module->compilation_state();
comp_state->AddCallback(TopTierCompiledCallback{
std::move(native_module), std::move(module_compiled_callback_)});
module_compiled_callback_ = {};
}
// An abstract class to share code among the states which decode VarInts. This
// class takes over the decoding of the VarInt and then calls the actual decode
// code with the decoded value.
class AsyncStreamingDecoder::DecodeVarInt32 : public DecodingState {
public:
explicit DecodeVarInt32(size_t max_value, const char* field_name)
: max_value_(max_value), field_name_(field_name) {}
Vector<uint8_t> buffer() override { return ArrayVector(byte_buffer_); }
size_t ReadBytes(AsyncStreamingDecoder* streaming,
Vector<const uint8_t> bytes) override;
std::unique_ptr<DecodingState> Next(
AsyncStreamingDecoder* streaming) override;
virtual std::unique_ptr<DecodingState> NextWithValue(
AsyncStreamingDecoder* streaming) = 0;
protected:
uint8_t byte_buffer_[kMaxVarInt32Size];
// The maximum valid value decoded in this state. {Next} returns an error if
// this value is exceeded.
const size_t max_value_;
const char* const field_name_;
size_t value_ = 0;
size_t bytes_consumed_ = 0;
};
class AsyncStreamingDecoder::DecodeModuleHeader : public DecodingState {
public:
Vector<uint8_t> buffer() override { return ArrayVector(byte_buffer_); }
std::unique_ptr<DecodingState> Next(
AsyncStreamingDecoder* streaming) override;
private:
// Checks if the magic bytes of the module header are correct.
void CheckHeader(Decoder* decoder);
// The size of the module header.
static constexpr size_t kModuleHeaderSize = 8;
uint8_t byte_buffer_[kModuleHeaderSize];
};
class AsyncStreamingDecoder::DecodeSectionID : public DecodingState {
public:
explicit DecodeSectionID(uint32_t module_offset)
: module_offset_(module_offset) {}
Vector<uint8_t> buffer() override { return {&id_, 1}; }
bool is_finishing_allowed() const override { return true; }
std::unique_ptr<DecodingState> Next(
AsyncStreamingDecoder* streaming) override;
private:
uint8_t id_ = 0;
// The start offset of this section in the module.
const uint32_t module_offset_;
};
class AsyncStreamingDecoder::DecodeSectionLength : public DecodeVarInt32 {
public:
explicit DecodeSectionLength(uint8_t id, uint32_t module_offset)
: DecodeVarInt32(max_module_size(), "section length"),
section_id_(id),
module_offset_(module_offset) {}
std::unique_ptr<DecodingState> NextWithValue(
AsyncStreamingDecoder* streaming) override;
private:
const uint8_t section_id_;
// The start offset of this section in the module.
const uint32_t module_offset_;
};
class AsyncStreamingDecoder::DecodeSectionPayload : public DecodingState {
public:
explicit DecodeSectionPayload(SectionBuffer* section_buffer)
: section_buffer_(section_buffer) {}
Vector<uint8_t> buffer() override { return section_buffer_->payload(); }
std::unique_ptr<DecodingState> Next(
AsyncStreamingDecoder* streaming) override;
private:
SectionBuffer* const section_buffer_;
};
class AsyncStreamingDecoder::DecodeNumberOfFunctions : public DecodeVarInt32 {
public:
explicit DecodeNumberOfFunctions(SectionBuffer* section_buffer)
: DecodeVarInt32(kV8MaxWasmFunctions, "functions count"),
section_buffer_(section_buffer) {}
std::unique_ptr<DecodingState> NextWithValue(
AsyncStreamingDecoder* streaming) override;
private:
SectionBuffer* const section_buffer_;
};
class AsyncStreamingDecoder::DecodeFunctionLength : public DecodeVarInt32 {
public:
explicit DecodeFunctionLength(SectionBuffer* section_buffer,
size_t buffer_offset,
size_t num_remaining_functions)
: DecodeVarInt32(kV8MaxWasmFunctionSize, "body size"),
section_buffer_(section_buffer),
buffer_offset_(buffer_offset),
// We are reading a new function, so one function less is remaining.
num_remaining_functions_(num_remaining_functions - 1) {
DCHECK_GT(num_remaining_functions, 0);
}
std::unique_ptr<DecodingState> NextWithValue(
AsyncStreamingDecoder* streaming) override;
private:
SectionBuffer* const section_buffer_;
const size_t buffer_offset_;
const size_t num_remaining_functions_;
};
class AsyncStreamingDecoder::DecodeFunctionBody : public DecodingState {
public:
explicit DecodeFunctionBody(SectionBuffer* section_buffer,
size_t buffer_offset, size_t function_body_length,
size_t num_remaining_functions,
uint32_t module_offset)
: section_buffer_(section_buffer),
buffer_offset_(buffer_offset),
function_body_length_(function_body_length),
num_remaining_functions_(num_remaining_functions),
module_offset_(module_offset) {}
Vector<uint8_t> buffer() override {
Vector<uint8_t> remaining_buffer =
section_buffer_->bytes() + buffer_offset_;
return remaining_buffer.SubVector(0, function_body_length_);
}
std::unique_ptr<DecodingState> Next(
AsyncStreamingDecoder* streaming) override;
private:
SectionBuffer* const section_buffer_;
const size_t buffer_offset_;
const size_t function_body_length_;
const size_t num_remaining_functions_;
const uint32_t module_offset_;
};
size_t AsyncStreamingDecoder::DecodeVarInt32::ReadBytes(
AsyncStreamingDecoder* streaming, Vector<const uint8_t> bytes) {
Vector<uint8_t> buf = buffer();
Vector<uint8_t> remaining_buf = buf + offset();
size_t new_bytes = std::min(bytes.size(), remaining_buf.size());
TRACE_STREAMING("ReadBytes of a VarInt\n");
memcpy(remaining_buf.begin(), &bytes.first(), new_bytes);
buf.Truncate(offset() + new_bytes);
Decoder decoder(buf,
streaming->module_offset() - static_cast<uint32_t>(offset()));
value_ = decoder.consume_u32v(field_name_);
if (decoder.failed()) {
if (new_bytes == remaining_buf.size()) {
// We only report an error if we read all bytes.
streaming->Error(decoder.error());
}
set_offset(offset() + new_bytes);
return new_bytes;
}
// The number of bytes we actually needed to read.
DCHECK_GT(decoder.pc(), buffer().begin());
bytes_consumed_ = static_cast<size_t>(decoder.pc() - buf.begin());
TRACE_STREAMING(" ==> %zu bytes consumed\n", bytes_consumed_);
// We read all the bytes we needed.
DCHECK_GT(bytes_consumed_, offset());
new_bytes = bytes_consumed_ - offset();
// Set the offset to the buffer size to signal that we are at the end of this
// section.
set_offset(buffer().size());
return new_bytes;
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeVarInt32::Next(AsyncStreamingDecoder* streaming) {
if (!streaming->ok()) return nullptr;
if (value_ > max_value_) {
std::ostringstream oss;
oss << "function size > maximum function size: " << value_ << " < "
<< max_value_;
return streaming->Error(oss.str());
}
return NextWithValue(streaming);
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeModuleHeader::Next(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeModuleHeader\n");
streaming->ProcessModuleHeader();
if (!streaming->ok()) return nullptr;
return std::make_unique<DecodeSectionID>(streaming->module_offset());
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeSectionID::Next(AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeSectionID: %s section\n",
SectionName(static_cast<SectionCode>(id_)));
if (id_ == SectionCode::kCodeSectionCode) {
// Explicitly check for multiple code sections as module decoder never
// sees the code section and hence cannot track this section.
if (streaming->code_section_processed_) {
// TODO(wasm): This error message (and others in this class) is different
// for non-streaming decoding. Bring them in sync and test.
return streaming->Error("code section can only appear once");
}
streaming->code_section_processed_ = true;
}
return std::make_unique<DecodeSectionLength>(id_, module_offset_);
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeSectionLength::NextWithValue(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeSectionLength(%zu)\n", value_);
SectionBuffer* buf =
streaming->CreateNewBuffer(module_offset_, section_id_, value_,
buffer().SubVector(0, bytes_consumed_));
DCHECK_NOT_NULL(buf);
if (value_ == 0) {
if (section_id_ == SectionCode::kCodeSectionCode) {
return streaming->Error("code section cannot have size 0");
}
// Process section without payload as well, to enforce section order and
// other feature checks specific to each individual section.
streaming->ProcessSection(buf);
if (!streaming->ok()) return nullptr;
// There is no payload, we go to the next section immediately.
return std::make_unique<DecodeSectionID>(streaming->module_offset_);
}
if (section_id_ == SectionCode::kCodeSectionCode) {
// We reached the code section. All functions of the code section are put
// into the same SectionBuffer.
return std::make_unique<DecodeNumberOfFunctions>(buf);
}
return std::make_unique<DecodeSectionPayload>(buf);
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeSectionPayload::Next(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeSectionPayload\n");
streaming->ProcessSection(section_buffer_);
if (!streaming->ok()) return nullptr;
return std::make_unique<DecodeSectionID>(streaming->module_offset());
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeNumberOfFunctions::NextWithValue(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeNumberOfFunctions(%zu)\n", value_);
// Copy the bytes we read into the section buffer.
Vector<uint8_t> payload_buf = section_buffer_->payload();
if (payload_buf.size() < bytes_consumed_) {
return streaming->Error("invalid code section length");
}
memcpy(payload_buf.begin(), buffer().begin(), bytes_consumed_);
// {value} is the number of functions.
if (value_ == 0) {
if (payload_buf.size() != bytes_consumed_) {
return streaming->Error("not all code section bytes were used");
}
return std::make_unique<DecodeSectionID>(streaming->module_offset());
}
DCHECK_GE(kMaxInt, payload_buf.length());
int code_section_len = static_cast<int>(payload_buf.length());
DCHECK_GE(kMaxInt, value_);
streaming->StartCodeSection(static_cast<int>(value_),
streaming->section_buffers_.back(),
code_section_len);
if (!streaming->ok()) return nullptr;
return std::make_unique<DecodeFunctionLength>(
section_buffer_, section_buffer_->payload_offset() + bytes_consumed_,
value_);
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeFunctionLength::NextWithValue(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeFunctionLength(%zu)\n", value_);
// Copy the bytes we consumed into the section buffer.
Vector<uint8_t> fun_length_buffer = section_buffer_->bytes() + buffer_offset_;
if (fun_length_buffer.size() < bytes_consumed_) {
return streaming->Error("read past code section end");
}
memcpy(fun_length_buffer.begin(), buffer().begin(), bytes_consumed_);
// {value} is the length of the function.
if (value_ == 0) return streaming->Error("invalid function length (0)");
if (buffer_offset_ + bytes_consumed_ + value_ > section_buffer_->length()) {
return streaming->Error("not enough code section bytes");
}
return std::make_unique<DecodeFunctionBody>(
section_buffer_, buffer_offset_ + bytes_consumed_, value_,
num_remaining_functions_, streaming->module_offset());
}
std::unique_ptr<AsyncStreamingDecoder::DecodingState>
AsyncStreamingDecoder::DecodeFunctionBody::Next(
AsyncStreamingDecoder* streaming) {
TRACE_STREAMING("DecodeFunctionBody\n");
streaming->ProcessFunctionBody(buffer(), module_offset_);
if (!streaming->ok()) return nullptr;
size_t end_offset = buffer_offset_ + function_body_length_;
if (num_remaining_functions_ > 0) {
return std::make_unique<DecodeFunctionLength>(section_buffer_, end_offset,
num_remaining_functions_);
}
// We just read the last function body. Continue with the next section.
if (end_offset != section_buffer_->length()) {
return streaming->Error("not all code section bytes were used");
}
return std::make_unique<DecodeSectionID>(streaming->module_offset());
}
AsyncStreamingDecoder::AsyncStreamingDecoder(
std::unique_ptr<StreamingProcessor> processor)
: processor_(std::move(processor)),
// A module always starts with a module header.
state_(new DecodeModuleHeader()) {}
AsyncStreamingDecoder::SectionBuffer* AsyncStreamingDecoder::CreateNewBuffer(
uint32_t module_offset, uint8_t section_id, size_t length,
Vector<const uint8_t> length_bytes) {
// Section buffers are allocated in the same order they appear in the module,
// they will be processed and later on concatenated in that same order.
section_buffers_.emplace_back(std::make_shared<SectionBuffer>(
module_offset, section_id, length, length_bytes));
return section_buffers_.back().get();
}
std::unique_ptr<StreamingDecoder> StreamingDecoder::CreateAsyncStreamingDecoder(
std::unique_ptr<StreamingProcessor> processor) {
return std::make_unique<AsyncStreamingDecoder>(std::move(processor));
}
} // namespace wasm
} // namespace internal
} // namespace v8
#undef TRACE_STREAMING