Commit 72019a04 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Streaming decoder

This CL implements a streaming decoder which takes the bytes
of a wasm module as an input, potentially split into multiple
chunks, and decodes them into segments. Each segment either
contains the payload of a whole section, or the code of a
single function. The goal is that the streaming decoder is
used for streaming compilation. That's where the interface
comes from, see
(https://cs.chromium.org/chromium/src/v8/include/v8.h?q=OnBytesReceived&sq=package:chromium&l=4060)

Error positions are not reported correctly at the moment. I
plan to do this in a separate CL.

Change-Id: I6e3df6a91945c7baec2dc4f5de2e5f47636083df
Reviewed-on: https://chromium-review.googlesource.com/471350
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarMircea Trofin <mtrofin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45250}
parent a6424c76
......@@ -1977,6 +1977,8 @@ v8_source_set("v8_base") {
"src/wasm/module-decoder.h",
"src/wasm/signature-map.cc",
"src/wasm/signature-map.h",
"src/wasm/streaming-decoder.cc",
"src/wasm/streaming-decoder.h",
"src/wasm/wasm-code-specialization.cc",
"src/wasm/wasm-code-specialization.h",
"src/wasm/wasm-debug.cc",
......
......@@ -1362,6 +1362,8 @@
'wasm/module-decoder.h',
'wasm/signature-map.cc',
'wasm/signature-map.h',
'wasm/streaming-decoder.cc',
'wasm/streaming-decoder.h',
'wasm/wasm-code-specialization.h',
'wasm/wasm-code-specialization.cc',
'wasm/wasm-debug.cc',
......
// 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/objects-inl.h"
#include "src/handles.h"
#include "src/wasm/decoder.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-result.h"
#include "src/objects/descriptor-array.h"
#include "src/objects/dictionary.h"
using namespace v8::internal;
using namespace v8::internal::wasm;
void StreamingDecoder::OnBytesReceived(Vector<const uint8_t> bytes) {
int current = 0;
while (decoder()->ok() && current < bytes.length()) {
int num_bytes = static_cast<int>(
state_->ReadBytes(this, bytes.SubVector(current, bytes.length())));
current += num_bytes;
if (state_->is_finished()) {
state_ = state_->Next(this);
}
}
total_size_ += bytes.length();
}
size_t StreamingDecoder::DecodingState::ReadBytes(StreamingDecoder* streaming,
Vector<const uint8_t> bytes) {
size_t num_bytes = std::min(static_cast<size_t>(bytes.length()), remaining());
memcpy(buffer() + offset(), &bytes.first(), num_bytes);
set_offset(offset() + num_bytes);
return num_bytes;
}
MaybeHandle<WasmModuleObject> StreamingDecoder::Finish() {
UNIMPLEMENTED();
return Handle<WasmModuleObject>::null();
}
bool StreamingDecoder::FinishForTesting() {
return decoder_.ok() && state_->is_finishing_allowed();
}
// 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 StreamingDecoder::DecodeVarInt32 : public DecodingState {
public:
explicit DecodeVarInt32(size_t max_value) : max_value_(max_value) {}
uint8_t* buffer() override { return byte_buffer_; }
size_t size() const override { return kMaxVarInt32Size; }
size_t ReadBytes(StreamingDecoder* streaming,
Vector<const uint8_t> bytes) override;
std::unique_ptr<DecodingState> Next(StreamingDecoder* streaming) override;
virtual std::unique_ptr<DecodingState> NextWithValue(
StreamingDecoder* streaming) = 0;
size_t value() const { return value_; }
size_t bytes_needed() const { return bytes_needed_; }
private:
uint8_t byte_buffer_[kMaxVarInt32Size];
// The maximum valid value decoded in this state. {Next} returns an error if
// this value is exceeded.
size_t max_value_;
size_t value_ = 0;
size_t bytes_needed_ = 0;
};
class StreamingDecoder::DecodeModuleHeader : public DecodingState {
public:
size_t size() const override { return kModuleHeaderSize; }
uint8_t* buffer() override { return byte_buffer_; }
std::unique_ptr<DecodingState> Next(StreamingDecoder* 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 StreamingDecoder::DecodeSectionID : public DecodingState {
public:
size_t size() const override { return 1; }
uint8_t* buffer() override { return &id_; }
bool is_finishing_allowed() const override { return true; }
uint8_t id() const { return id_; }
std::unique_ptr<DecodingState> Next(StreamingDecoder* streaming) override;
private:
uint8_t id_ = 0;
};
class StreamingDecoder::DecodeSectionLength : public DecodeVarInt32 {
public:
explicit DecodeSectionLength(uint8_t id)
: DecodeVarInt32(kV8MaxWasmModuleSize), section_id_(id) {}
uint8_t section_id() const { return section_id_; }
std::unique_ptr<DecodingState> NextWithValue(
StreamingDecoder* streaming) override;
private:
uint8_t section_id_;
};
class StreamingDecoder::DecodeSectionPayload : public DecodingState {
public:
explicit DecodeSectionPayload(SectionBuffer* section_buffer)
: section_buffer_(section_buffer) {}
size_t size() const override { return section_buffer_->payload_length(); }
uint8_t* buffer() override {
return section_buffer_->bytes() + section_buffer_->payload_offset();
}
std::unique_ptr<DecodingState> Next(StreamingDecoder* streaming) override;
private:
SectionBuffer* section_buffer_;
};
class StreamingDecoder::DecodeNumberOfFunctions : public DecodeVarInt32 {
public:
explicit DecodeNumberOfFunctions(SectionBuffer* section_buffer)
: DecodeVarInt32(kV8MaxWasmFunctions), section_buffer_(section_buffer) {}
SectionBuffer* section_buffer() const { return section_buffer_; }
std::unique_ptr<DecodingState> NextWithValue(
StreamingDecoder* streaming) override;
private:
SectionBuffer* section_buffer_;
};
class StreamingDecoder::DecodeFunctionLength : public DecodeVarInt32 {
public:
explicit DecodeFunctionLength(SectionBuffer* section_buffer,
size_t buffer_offset,
size_t num_remaining_functions)
: DecodeVarInt32(kV8MaxWasmFunctionSize),
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);
}
size_t num_remaining_functions() const { return num_remaining_functions_; }
size_t buffer_offset() const { return buffer_offset_; }
SectionBuffer* section_buffer() const { return section_buffer_; }
std::unique_ptr<DecodingState> NextWithValue(
StreamingDecoder* streaming) override;
private:
SectionBuffer* section_buffer_;
size_t buffer_offset_;
size_t num_remaining_functions_;
};
class StreamingDecoder::DecodeFunctionBody : public DecodingState {
public:
explicit DecodeFunctionBody(SectionBuffer* section_buffer,
size_t buffer_offset, size_t function_length,
size_t num_remaining_functions)
: section_buffer_(section_buffer),
buffer_offset_(buffer_offset),
size_(function_length),
num_remaining_functions_(num_remaining_functions) {}
size_t size() const override { return size_; }
uint8_t* buffer() override {
return section_buffer_->bytes() + buffer_offset_;
}
size_t num_remaining_functions() const { return num_remaining_functions_; }
size_t buffer_offset() const { return buffer_offset_; }
SectionBuffer* section_buffer() const { return section_buffer_; }
std::unique_ptr<DecodingState> Next(StreamingDecoder* streaming) override;
private:
SectionBuffer* section_buffer_;
size_t buffer_offset_;
size_t size_;
size_t num_remaining_functions_;
};
size_t StreamingDecoder::DecodeVarInt32::ReadBytes(
StreamingDecoder* streaming, Vector<const uint8_t> bytes) {
size_t bytes_read =
std::min(static_cast<size_t>(bytes.length()), remaining());
memcpy(buffer() + offset(), &bytes.first(), bytes_read);
streaming->decoder()->Reset(buffer(), buffer() + offset() + bytes_read);
value_ = streaming->decoder()->consume_i32v();
// The number of bytes we actually needed to read.
DCHECK_GT(streaming->decoder()->pc(), buffer());
bytes_needed_ = static_cast<size_t>(streaming->decoder()->pc() - buffer());
if (streaming->decoder()->failed()) {
if (offset() + bytes_read < size()) {
// We did not decode a full buffer, so we ignore errors. Maybe the
// decoding will succeed when we have more bytes.
streaming->decoder()->Reset(nullptr, nullptr);
}
set_offset(offset() + bytes_read);
return bytes_read;
} else {
DCHECK_GT(bytes_needed_, offset());
size_t result = bytes_needed_ - offset();
// We read all the bytes we needed.
set_offset(size());
return result;
}
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeVarInt32::Next(StreamingDecoder* streaming) {
if (streaming->decoder()->failed()) {
return std::unique_ptr<DecodingState>(nullptr);
}
if (value() > max_value_) {
streaming->decoder()->errorf(nullptr, "size > maximum function size: %zu",
value());
return std::unique_ptr<DecodingState>(nullptr);
}
return NextWithValue(streaming);
}
#define BYTES(x) (x & 0xff), (x >> 8) & 0xff, (x >> 16) & 0xff, (x >> 24) & 0xff
// Decode the module header. The error state of the decoder stores the result.
void StreamingDecoder::DecodeModuleHeader::CheckHeader(Decoder* decoder) {
// TODO(ahaas): Share code with the module-decoder.
decoder->Reset(buffer(), buffer() + size());
uint32_t magic_word = decoder->consume_u32("wasm magic");
if (magic_word != kWasmMagic) {
decoder->errorf(nullptr,
"expected magic word %02x %02x %02x %02x, "
"found %02x %02x %02x %02x",
BYTES(kWasmMagic), BYTES(magic_word));
}
uint32_t magic_version = decoder->consume_u32("wasm version");
if (magic_version != kWasmVersion) {
decoder->errorf(nullptr,
"expected version %02x %02x %02x %02x, "
"found %02x %02x %02x %02x",
BYTES(kWasmVersion), BYTES(magic_version));
}
}
#undef BYTES
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeModuleHeader::Next(StreamingDecoder* streaming) {
CheckHeader(streaming->decoder());
return std::unique_ptr<DecodingState>(new DecodeSectionID());
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeSectionID::Next(StreamingDecoder* streaming) {
return std::unique_ptr<DecodingState>(new DecodeSectionLength(id()));
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeSectionLength::NextWithValue(
StreamingDecoder* streaming) {
SectionBuffer* buf = streaming->CreateNewBuffer(
section_id(), value(),
Vector<const uint8_t>(buffer(), static_cast<int>(bytes_needed())));
if (value() == 0) {
// There is no payload, we go to the next section immediately.
return std::unique_ptr<DecodingState>(new DecodeSectionID());
} else if (section_id() == SectionCode::kCodeSectionCode) {
// We reached the code section. All functions of the code section are put
// into the same SectionBuffer.
return std::unique_ptr<DecodingState>(new DecodeNumberOfFunctions(buf));
} else {
return std::unique_ptr<DecodingState>(new DecodeSectionPayload(buf));
}
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeSectionPayload::Next(StreamingDecoder* streaming) {
return std::unique_ptr<DecodingState>(new DecodeSectionID());
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeNumberOfFunctions::NextWithValue(
StreamingDecoder* streaming) {
// Copy the bytes we read into the section buffer.
if (section_buffer_->payload_length() >= bytes_needed()) {
memcpy(section_buffer_->bytes() + section_buffer_->payload_offset(),
buffer(), bytes_needed());
} else {
streaming->decoder()->error("Invalid code section length");
return std::unique_ptr<DecodingState>(new DecodeSectionID());
}
// {value} is the number of functions.
if (value() > 0) {
return std::unique_ptr<DecodingState>(new DecodeFunctionLength(
section_buffer(), section_buffer()->payload_offset() + bytes_needed(),
value()));
} else {
return std::unique_ptr<DecodingState>(new DecodeSectionID());
}
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeFunctionLength::NextWithValue(
StreamingDecoder* streaming) {
// Copy the bytes we read into the section buffer.
if (section_buffer_->length() >= buffer_offset_ + bytes_needed()) {
memcpy(section_buffer_->bytes() + buffer_offset_, buffer(), bytes_needed());
} else {
streaming->decoder()->error("Invalid code section length");
return std::unique_ptr<DecodingState>(new DecodeSectionID());
}
// {value} is the length of the function.
if (value() == 0) {
streaming->decoder()->errorf(nullptr, "Invalid function length (0)");
return std::unique_ptr<DecodingState>(nullptr);
} else if (buffer_offset() + bytes_needed() + value() >
section_buffer()->length()) {
streaming->decoder()->errorf(nullptr, "not enough code section bytes");
return std::unique_ptr<DecodingState>(nullptr);
}
return std::unique_ptr<DecodingState>(
new DecodeFunctionBody(section_buffer(), buffer_offset() + bytes_needed(),
value(), num_remaining_functions()));
}
std::unique_ptr<StreamingDecoder::DecodingState>
StreamingDecoder::DecodeFunctionBody::Next(StreamingDecoder* streaming) {
// TODO(ahaas): Start compilation of the function here.
if (num_remaining_functions() != 0) {
return std::unique_ptr<DecodingState>(new DecodeFunctionLength(
section_buffer(), buffer_offset() + size(), num_remaining_functions()));
} else {
if (buffer_offset() + size() != section_buffer()->length()) {
streaming->decoder()->errorf(nullptr,
"not all code section bytes were used");
return std::unique_ptr<DecodingState>(nullptr);
}
return std::unique_ptr<DecodingState>(new DecodeSectionID());
}
}
StreamingDecoder::StreamingDecoder(Isolate* isolate)
: isolate_(isolate),
// A module always starts with a module header.
state_(new DecodeModuleHeader()),
decoder_(nullptr, nullptr) {
USE(isolate_);
}
// 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 <vector>
#include "src/isolate.h"
#include "src/wasm/decoder.h"
#include "src/wasm/wasm-objects.h"
namespace v8 {
namespace internal {
namespace wasm {
// 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(Isolate* isolate);
// The buffer passed into OnBytesReceived is owned by the caller.
void OnBytesReceived(Vector<const uint8_t> bytes);
// Finishes the stream and returns compiled WasmModuleObject.
MaybeHandle<WasmModuleObject> Finish();
// Finishes the streaming and returns true if no error was detected.
bool FinishForTesting();
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:
// 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(uint8_t id, size_t payload_length,
Vector<const uint8_t> length_bytes)
: // ID + length + payload
length_(1 + length_bytes.length() + payload_length),
bytes_(new uint8_t[length_]),
payload_offset_(1 + length_bytes.length()) {
bytes_[0] = id;
memcpy(bytes_.get() + 1, &length_bytes.first(), length_bytes.length());
}
uint8_t* bytes() const { return bytes_.get(); }
size_t length() const { return length_; }
size_t payload_offset() const { return payload_offset_; }
size_t payload_length() const { return length_ - payload_offset_; }
private:
size_t length_;
std::unique_ptr<uint8_t[]> bytes_;
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 number of bytes to be received.
virtual size_t size() const = 0;
// The buffer to store the received bytes.
virtual 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; }
// The number of bytes which are still needed.
size_t remaining() const { return size() - offset(); }
bool is_finished() const { return offset() == size(); }
// 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(uint8_t id, size_t length,
Vector<const uint8_t> length_bytes) {
section_buffers_.emplace_back(new SectionBuffer(id, length, length_bytes));
return section_buffers_.back().get();
}
Decoder* decoder() { return &decoder_; }
Isolate* isolate_;
std::unique_ptr<DecodingState> state_;
// The decoder is an instance variable because we use it for error handling.
Decoder decoder_;
std::vector<std::unique_ptr<SectionBuffer>> section_buffers_;
size_t total_size_ = 0;
DISALLOW_COPY_AND_ASSIGN(StreamingDecoder);
};
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_STREAMING_DECODER_H_
......@@ -149,6 +149,7 @@ v8_executable("unittests") {
"wasm/leb-helper-unittest.cc",
"wasm/loop-assignment-analysis-unittest.cc",
"wasm/module-decoder-unittest.cc",
"wasm/streaming-decoder-unittest.cc",
"wasm/wasm-macro-gen-unittest.cc",
"wasm/wasm-module-builder-unittest.cc",
"wasm/wasm-opcodes-unittest.cc",
......
......@@ -150,6 +150,7 @@
'wasm/leb-helper-unittest.cc',
'wasm/loop-assignment-analysis-unittest.cc',
'wasm/module-decoder-unittest.cc',
'wasm/streaming-decoder-unittest.cc',
'wasm/wasm-macro-gen-unittest.cc',
'wasm/wasm-module-builder-unittest.cc',
'wasm/wasm-opcodes-unittest.cc',
......
// 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 "test/unittests/test-utils.h"
#include "src/objects-inl.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h"
#include "src/objects/descriptor-array.h"
#include "src/objects/dictionary.h"
#include "test/common/wasm/wasm-macro-gen.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmStreamingDecoderTest : public ::testing::Test {
public:
void ExpectVerifies(Vector<const uint8_t> data) {
for (int split = 0; split <= data.length(); ++split) {
StreamingDecoder stream(nullptr);
stream.OnBytesReceived(data.SubVector(0, split));
stream.OnBytesReceived(data.SubVector(split, data.length()));
EXPECT_TRUE(stream.FinishForTesting());
}
}
void ExpectFailure(Vector<const uint8_t> data) {
for (int split = 0; split <= data.length(); ++split) {
StreamingDecoder stream(nullptr);
stream.OnBytesReceived(data.SubVector(0, split));
stream.OnBytesReceived(data.SubVector(split, data.length()));
EXPECT_FALSE(stream.FinishForTesting());
}
}
};
TEST_F(WasmStreamingDecoderTest, EmptyStream) {
StreamingDecoder stream(nullptr);
EXPECT_FALSE(stream.FinishForTesting());
}
TEST_F(WasmStreamingDecoderTest, IncompleteModuleHeader) {
const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)};
{
StreamingDecoder stream(nullptr);
stream.OnBytesReceived(Vector<const uint8_t>(data, 1));
EXPECT_FALSE(stream.FinishForTesting());
}
for (int length = 1; length < static_cast<int>(arraysize(data)); ++length) {
ExpectFailure(Vector<const uint8_t>(data, length));
}
}
TEST_F(WasmStreamingDecoderTest, MagicAndVersion) {
const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, BadMagic) {
for (uint32_t x = 1; x; x <<= 1) {
const uint8_t data[] = {U32_LE(kWasmMagic ^ x), U32_LE(kWasmVersion)};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
}
TEST_F(WasmStreamingDecoderTest, BadVersion) {
for (uint32_t x = 1; x; x <<= 1) {
const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion ^ x)};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
}
TEST_F(WasmStreamingDecoderTest, OneSection) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x6, // Section Length
0x0, // Payload
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0 // 6
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneSection_b) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x86, // Section Length = 6 (LEB)
0x0, // --
0x0, // Payload
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0 // 6
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneShortSection) {
// Short section means that section length + payload is less than 5 bytes,
// which is the maximum size of the length field.
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x2, // Section Length
0x0, // Payload
0x0 // 2
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneShortSection_b) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x82, // Section Length = 2 (LEB)
0x80, // --
0x0, // --
0x0, // Payload
0x0 // 2
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneEmptySection) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x0 // Section Length
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneSectionNotEnoughPayload1) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x6, // Section Length
0x0, // Payload
0x0, // 2
0x0, // 3
0x0, // 4
0x0 // 5
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneSectionNotEnoughPayload2) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x6, // Section Length
0x0 // Payload
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneSectionInvalidLength) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x80, // Section Length (0 in LEB)
0x80, // --
0x80, // --
0x80, // --
0x80, // --
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, TwoLongSections) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x6, // Section Length
0x0, // Payload
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x2, // Section ID
0x7, // Section Length
0x0, // Payload
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0 // 7
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, TwoShortSections) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x1, // Section Length
0x0, // Payload
0x2, // Section ID
0x2, // Section Length
0x0, // Payload
0x0, // 2
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, TwoSectionsShortLong) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x1, // Section Length
0x0, // Payload
0x2, // Section ID
0x7, // Section Length
0x0, // Payload
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0 // 7
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, TwoEmptySections) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x0, // Section Length
0x2, // Section ID
0x0 // Section Length
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, EmptyCodeSection) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x0, // Section Length
0xb, // Section ID
0x0 // Section Length
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneFunction) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x8, // Section Length
0x1, // Number of Functions
0x6, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, OneShortFunction) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x3, // Section Length
0x1, // Number of Functions
0x1, // Function Length
0x0, // Function
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, EmptyFunction) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x2, // Section Length
0x1, // Number of Functions
0x0, // Function Length
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, TwoFunctions) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x10, // Section Length
0x2, // Number of Functions
0x6, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, TwoFunctions_b) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0xb, // Section Length
0x2, // Number of Functions
0x1, // Function Length
0x0, // Function
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
};
ExpectVerifies(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooHigh) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0xd, // Section Length
0x2, // Number of Functions
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooLow) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x9, // Section Length
0x2, // Number of Functions
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooLowEndsInNumFunctions) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x1, // Section Length
0x82, // Number of Functions
0x80, // --
0x00, // --
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooLowEndsInFunctionLength) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x5, // Section Length
0x82, // Number of Functions
0x80, // --
0x00, // --
0x87, // Function Length
0x80, // --
0x00, // --
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, NumberOfFunctionsTooHigh) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0xb, // Section Length
0x4, // Number of Functions
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
TEST_F(WasmStreamingDecoderTest, NumberOfFunctionsTooLow) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0xe, // Section Length
0x2, // Number of Functions
0x1, // Function Length
0x0, // Function
0x2, // Function Length
0x0, // Function
0x0, // 2
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
};
ExpectFailure(Vector<const uint8_t>(data, arraysize(data)));
}
} // namespace wasm
} // namespace internal
} // namespace v8
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment