Commit 3b64603d authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Reset StreamingProcessor on error

After the first decoder error, the streaming processor should not be
called again. To enforce this, reset the {processor_} field. This also
makes the {ok_} field redundant.
Note that this refactoring is also necessary for a future CL which
makes the {StreamingProcessor} keep the {AsyncCompileJob} alive. By
resetting the processor, we also remove that link.

R=ahaas@chromium.org

Bug: v8:7921
Change-Id: I42f5ed26a8f26c3dc8db5676557a0d82021e132e
Reviewed-on: https://chromium-review.googlesource.com/c/1329179
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57435}
parent f7be7ae0
...@@ -136,7 +136,9 @@ class Vector { ...@@ -136,7 +136,9 @@ class Vector {
} }
// Implicit conversion from Vector<T> to Vector<const T>. // Implicit conversion from Vector<T> to Vector<const T>.
inline operator Vector<const T>() { return Vector<const T>::cast(*this); } inline operator Vector<const T>() const {
return Vector<const T>::cast(*this);
}
// Factory method for creating empty vectors. // Factory method for creating empty vectors.
static Vector<T> empty() { return Vector<T>(nullptr, 0); } static Vector<T> empty() { return Vector<T>(nullptr, 0); }
...@@ -147,7 +149,7 @@ class Vector { ...@@ -147,7 +149,7 @@ class Vector {
input.length() * sizeof(S) / sizeof(T)); input.length() * sizeof(S) / sizeof(T));
} }
bool operator==(const Vector<T>& other) const { bool operator==(const Vector<const T> other) const {
if (length_ != other.length_) return false; if (length_ != other.length_) return false;
if (start_ == other.start_) return true; if (start_ == other.start_) return true;
for (size_t i = 0; i < length_; ++i) { for (size_t i = 0; i < length_; ++i) {
......
...@@ -62,6 +62,9 @@ size_t StreamingDecoder::DecodingState::ReadBytes(StreamingDecoder* streaming, ...@@ -62,6 +62,9 @@ size_t StreamingDecoder::DecodingState::ReadBytes(StreamingDecoder* streaming,
} }
void StreamingDecoder::Finish() { void StreamingDecoder::Finish() {
TRACE_STREAMING("Finish\n");
if (!ok()) return;
if (deserializing()) { if (deserializing()) {
Vector<const uint8_t> wire_bytes(wire_bytes_for_deserializing_.data(), Vector<const uint8_t> wire_bytes(wire_bytes_for_deserializing_.data(),
wire_bytes_for_deserializing_.size()); wire_bytes_for_deserializing_.size());
...@@ -75,11 +78,6 @@ void StreamingDecoder::Finish() { ...@@ -75,11 +78,6 @@ void StreamingDecoder::Finish() {
// The decoder has received all wire bytes; fall through and finish. // The decoder has received all wire bytes; fall through and finish.
} }
TRACE_STREAMING("Finish\n");
if (!ok()) {
return;
}
if (!state_->is_finishing_allowed()) { if (!state_->is_finishing_allowed()) {
// The byte stream ended too early, we report an error. // The byte stream ended too early, we report an error.
Error("unexpected end of stream"); Error("unexpected end of stream");
...@@ -105,10 +103,9 @@ void StreamingDecoder::Finish() { ...@@ -105,10 +103,9 @@ void StreamingDecoder::Finish() {
void StreamingDecoder::Abort() { void StreamingDecoder::Abort() {
TRACE_STREAMING("Abort\n"); TRACE_STREAMING("Abort\n");
if (ok()) { if (!ok()) return; // Failed already.
ok_ = false; processor_->OnAbort();
processor_->OnAbort(); Fail();
}
} }
void StreamingDecoder::SetModuleCompiledCallback( void StreamingDecoder::SetModuleCompiledCallback(
......
...@@ -80,11 +80,7 @@ class V8_EXPORT_PRIVATE StreamingDecoder { ...@@ -80,11 +80,7 @@ class V8_EXPORT_PRIVATE StreamingDecoder {
// Notify the StreamingDecoder that compilation ended and the // Notify the StreamingDecoder that compilation ended and the
// StreamingProcessor should not be called anymore. // StreamingProcessor should not be called anymore.
void NotifyCompilationEnded() { void NotifyCompilationEnded() { Fail(); }
// We set {ok_} to false to turn all future calls to the StreamingDecoder
// into no-ops.
ok_ = false;
}
// Caching support. // Caching support.
// Sets the callback that is called after the module is fully compiled. // Sets the callback that is called after the module is fully compiled.
...@@ -212,8 +208,8 @@ class V8_EXPORT_PRIVATE StreamingDecoder { ...@@ -212,8 +208,8 @@ class V8_EXPORT_PRIVATE StreamingDecoder {
Vector<const uint8_t> length_bytes); Vector<const uint8_t> length_bytes);
std::unique_ptr<DecodingState> Error(VoidResult result) { std::unique_ptr<DecodingState> Error(VoidResult result) {
if (ok_) processor_->OnError(std::move(result)); if (ok()) processor_->OnError(std::move(result));
ok_ = false; Fail();
return std::unique_ptr<DecodingState>(nullptr); return std::unique_ptr<DecodingState>(nullptr);
} }
...@@ -222,48 +218,52 @@ class V8_EXPORT_PRIVATE StreamingDecoder { ...@@ -222,48 +218,52 @@ class V8_EXPORT_PRIVATE StreamingDecoder {
} }
void ProcessModuleHeader() { void ProcessModuleHeader() {
if (!ok_) return; if (!ok()) return;
if (!processor_->ProcessModuleHeader(state_->buffer(), 0)) { if (!processor_->ProcessModuleHeader(state_->buffer(), 0)) Fail();
ok_ = false;
}
} }
void ProcessSection(SectionBuffer* buffer) { void ProcessSection(SectionBuffer* buffer) {
if (!ok_) return; if (!ok()) return;
if (!processor_->ProcessSection( if (!processor_->ProcessSection(
buffer->section_code(), buffer->payload(), buffer->section_code(), buffer->payload(),
buffer->module_offset() + buffer->module_offset() +
static_cast<uint32_t>(buffer->payload_offset()))) { static_cast<uint32_t>(buffer->payload_offset()))) {
ok_ = false; Fail();
} }
} }
void StartCodeSection(size_t num_functions, void StartCodeSection(size_t num_functions,
std::shared_ptr<WireBytesStorage> wire_bytes_storage) { std::shared_ptr<WireBytesStorage> wire_bytes_storage) {
if (!ok_) return; if (!ok()) return;
// The offset passed to {ProcessCodeSectionHeader} is an error offset and // The offset passed to {ProcessCodeSectionHeader} is an error offset and
// not the start offset of a buffer. Therefore we need the -1 here. // not the start offset of a buffer. Therefore we need the -1 here.
if (!processor_->ProcessCodeSectionHeader(num_functions, if (!processor_->ProcessCodeSectionHeader(num_functions,
module_offset() - 1, module_offset() - 1,
std::move(wire_bytes_storage))) { std::move(wire_bytes_storage))) {
ok_ = false; Fail();
} }
} }
void ProcessFunctionBody(Vector<const uint8_t> bytes, void ProcessFunctionBody(Vector<const uint8_t> bytes,
uint32_t module_offset) { uint32_t module_offset) {
if (!ok_) return; if (!ok()) return;
if (!processor_->ProcessFunctionBody(bytes, module_offset)) ok_ = false; 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 ok_; } bool ok() const { return processor_ != nullptr; }
uint32_t module_offset() const { return module_offset_; } uint32_t module_offset() const { return module_offset_; }
bool deserializing() const { return !compiled_module_bytes_.is_empty(); } bool deserializing() const { return !compiled_module_bytes_.is_empty(); }
std::unique_ptr<StreamingProcessor> processor_; std::unique_ptr<StreamingProcessor> processor_;
bool ok_ = true;
std::unique_ptr<DecodingState> state_; std::unique_ptr<DecodingState> state_;
std::vector<std::shared_ptr<SectionBuffer>> section_buffers_; std::vector<std::shared_ptr<SectionBuffer>> section_buffers_;
uint32_t module_offset_ = 0; uint32_t module_offset_ = 0;
......
...@@ -17,20 +17,32 @@ namespace v8 { ...@@ -17,20 +17,32 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
struct MockStreamingResult {
size_t num_sections = 0;
size_t num_functions = 0;
bool ok = true;
OwnedVector<uint8_t> received_bytes;
MockStreamingResult() = default;
};
class MockStreamingProcessor : public StreamingProcessor { class MockStreamingProcessor : public StreamingProcessor {
public: public:
explicit MockStreamingProcessor(MockStreamingResult* result)
: result_(result) {}
bool ProcessModuleHeader(Vector<const uint8_t> bytes, bool ProcessModuleHeader(Vector<const uint8_t> bytes,
uint32_t offset) override { uint32_t offset) override {
// TODO(ahaas): Share code with the module-decoder. // TODO(ahaas): Share code with the module-decoder.
Decoder decoder(bytes.begin(), bytes.end()); Decoder decoder(bytes.begin(), bytes.end());
uint32_t magic_word = decoder.consume_u32("wasm magic"); uint32_t magic_word = decoder.consume_u32("wasm magic");
if (decoder.failed() || magic_word != kWasmMagic) { if (decoder.failed() || magic_word != kWasmMagic) {
ok_ = false; result_->ok = false;
return false; return false;
} }
uint32_t magic_version = decoder.consume_u32("wasm version"); uint32_t magic_version = decoder.consume_u32("wasm version");
if (decoder.failed() || magic_version != kWasmVersion) { if (decoder.failed() || magic_version != kWasmVersion) {
ok_ = false; result_->ok = false;
return false; return false;
} }
return true; return true;
...@@ -38,7 +50,7 @@ class MockStreamingProcessor : public StreamingProcessor { ...@@ -38,7 +50,7 @@ class MockStreamingProcessor : public StreamingProcessor {
// Process all sections but the code section. // Process all sections but the code section.
bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes, bool ProcessSection(SectionCode section_code, Vector<const uint8_t> bytes,
uint32_t offset) override { uint32_t offset) override {
++num_sections_; ++result_->num_sections;
return true; return true;
} }
...@@ -50,7 +62,7 @@ class MockStreamingProcessor : public StreamingProcessor { ...@@ -50,7 +62,7 @@ class MockStreamingProcessor : public StreamingProcessor {
// Process a function body. // Process a function body.
bool ProcessFunctionBody(Vector<const uint8_t> bytes, bool ProcessFunctionBody(Vector<const uint8_t> bytes,
uint32_t offset) override { uint32_t offset) override {
++num_functions_; ++result_->num_functions;
return true; return true;
} }
...@@ -58,11 +70,11 @@ class MockStreamingProcessor : public StreamingProcessor { ...@@ -58,11 +70,11 @@ class MockStreamingProcessor : public StreamingProcessor {
// Finish the processing of the stream. // Finish the processing of the stream.
void OnFinishedStream(OwnedVector<uint8_t> bytes) override { void OnFinishedStream(OwnedVector<uint8_t> bytes) override {
received_bytes_ = std::move(bytes); result_->received_bytes = std::move(bytes);
} }
// Report an error detected in the StreamingDecoder. // Report an error detected in the StreamingDecoder.
void OnError(DecodeResult result) override { ok_ = false; } void OnError(DecodeResult result) override { result_->ok = false; }
void OnAbort() override {} void OnAbort() override {}
...@@ -71,18 +83,8 @@ class MockStreamingProcessor : public StreamingProcessor { ...@@ -71,18 +83,8 @@ class MockStreamingProcessor : public StreamingProcessor {
return false; return false;
}; };
size_t num_sections() const { return num_sections_; }
size_t num_functions() const { return num_functions_; }
bool ok() const { return ok_; }
Vector<const uint8_t> received_bytes() const {
return received_bytes_.as_vector();
}
private: private:
size_t num_sections_ = 0; MockStreamingResult* const result_;
size_t num_functions_ = 0;
bool ok_ = true;
OwnedVector<uint8_t> received_bytes_;
}; };
class WasmStreamingDecoderTest : public ::testing::Test { class WasmStreamingDecoderTest : public ::testing::Test {
...@@ -90,50 +92,49 @@ class WasmStreamingDecoderTest : public ::testing::Test { ...@@ -90,50 +92,49 @@ class WasmStreamingDecoderTest : public ::testing::Test {
void ExpectVerifies(Vector<const uint8_t> data, size_t expected_sections, void ExpectVerifies(Vector<const uint8_t> data, size_t expected_sections,
size_t expected_functions) { size_t expected_functions) {
for (int split = 0; split <= data.length(); ++split) { for (int split = 0; split <= data.length(); ++split) {
// Use a unique_ptr so that the StreamingDecoder can own the processor. MockStreamingResult result;
std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); StreamingDecoder stream(
MockStreamingProcessor* processor = p.get(); base::make_unique<MockStreamingProcessor>(&result));
StreamingDecoder stream(std::move(p));
stream.OnBytesReceived(data.SubVector(0, split)); stream.OnBytesReceived(data.SubVector(0, split));
stream.OnBytesReceived(data.SubVector(split, data.length())); stream.OnBytesReceived(data.SubVector(split, data.length()));
stream.Finish(); stream.Finish();
EXPECT_TRUE(processor->ok()); EXPECT_TRUE(result.ok);
EXPECT_EQ(expected_sections, processor->num_sections()); EXPECT_EQ(expected_sections, result.num_sections);
EXPECT_EQ(expected_functions, processor->num_functions()); EXPECT_EQ(expected_functions, result.num_functions);
EXPECT_EQ(data, processor->received_bytes()); EXPECT_EQ(data, result.received_bytes.as_vector());
} }
} }
void ExpectFailure(Vector<const uint8_t> data) { void ExpectFailure(Vector<const uint8_t> data) {
for (int split = 0; split <= data.length(); ++split) { for (int split = 0; split <= data.length(); ++split) {
std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); MockStreamingResult result;
MockStreamingProcessor* processor = p.get(); StreamingDecoder stream(
StreamingDecoder stream(std::move(p)); base::make_unique<MockStreamingProcessor>(&result));
stream.OnBytesReceived(data.SubVector(0, split)); stream.OnBytesReceived(data.SubVector(0, split));
stream.OnBytesReceived(data.SubVector(split, data.length())); stream.OnBytesReceived(data.SubVector(split, data.length()));
stream.Finish(); stream.Finish();
EXPECT_FALSE(processor->ok()); EXPECT_FALSE(result.ok);
} }
} }
MockStreamingResult result;
}; };
TEST_F(WasmStreamingDecoderTest, EmptyStream) { TEST_F(WasmStreamingDecoderTest, EmptyStream) {
std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); MockStreamingResult result;
MockStreamingProcessor* processor = p.get(); StreamingDecoder stream(base::make_unique<MockStreamingProcessor>(&result));
StreamingDecoder stream(std::move(p));
stream.Finish(); stream.Finish();
EXPECT_FALSE(processor->ok()); EXPECT_FALSE(result.ok);
} }
TEST_F(WasmStreamingDecoderTest, IncompleteModuleHeader) { TEST_F(WasmStreamingDecoderTest, IncompleteModuleHeader) {
const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)}; const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)};
{ {
std::unique_ptr<MockStreamingProcessor> p(new MockStreamingProcessor()); MockStreamingResult result;
MockStreamingProcessor* processor = p.get(); StreamingDecoder stream(base::make_unique<MockStreamingProcessor>(&result));
StreamingDecoder stream(std::move(p));
stream.OnBytesReceived(Vector<const uint8_t>(data, 1)); stream.OnBytesReceived(Vector<const uint8_t>(data, 1));
stream.Finish(); stream.Finish();
EXPECT_FALSE(processor->ok()); EXPECT_FALSE(result.ok);
} }
for (int length = 1; length < static_cast<int>(arraysize(data)); ++length) { for (int length = 1; length < static_cast<int>(arraysize(data)); ++length) {
ExpectFailure(Vector<const uint8_t>(data, length)); ExpectFailure(Vector<const uint8_t>(data, length));
......
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