70222a9d03
Follow-up to https://chromium-review.googlesource.com/c/v8/v8/+/941442. "background" refers to a priority and is inappropriate to refer to worker threads as many tasks posted to worker threads by v8 are in fact high priority. Also took advantage of this rename to make NumberOfWorkerThreads() return an int instead of size_t. While it is never negative, int is simpler and Google C++ style guide states to avoid unsigned integers in such cases (ref. "On Unsigned Integers" @ https://google.github.io/styleguide/cppguide.html#Integer_Types). The Chromium embedder for that call provided an int which was converted to size_t for this override and most often casted back down to int on the v8 side, adding churn, and readability overhead. R=ahaas@chromium.org Bug: v8:7310 Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng Change-Id: Ib5280df73d2846b111d985be65a10b049995ea6a Reviewed-on: https://chromium-review.googlesource.com/941944 Commit-Queue: Gabriel Charette <gab@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Cr-Commit-Position: refs/heads/master@{#51662}
1015 lines
36 KiB
C++
1015 lines
36 KiB
C++
// 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/api.h"
|
|
#include "src/objects-inl.h"
|
|
#include "src/v8.h"
|
|
#include "src/vector.h"
|
|
|
|
#include "src/wasm/compilation-manager.h"
|
|
#include "src/wasm/module-decoder.h"
|
|
#include "src/wasm/streaming-decoder.h"
|
|
#include "src/wasm/wasm-engine.h"
|
|
#include "src/wasm/wasm-module-builder.h"
|
|
#include "src/wasm/wasm-module.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
#include "test/common/wasm/test-signatures.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
class MockPlatform final : public TestPlatform {
|
|
public:
|
|
MockPlatform() : task_runner_(std::make_shared<MockTaskRunner>()) {
|
|
// Now that it's completely constructed, make this the current platform.
|
|
i::V8::SetPlatformForTesting(this);
|
|
}
|
|
|
|
std::shared_ptr<TaskRunner> GetForegroundTaskRunner(
|
|
v8::Isolate* isolate) override {
|
|
return task_runner_;
|
|
}
|
|
|
|
std::shared_ptr<TaskRunner> GetWorkerThreadsTaskRunner(
|
|
v8::Isolate* isolate) override {
|
|
return task_runner_;
|
|
}
|
|
|
|
void CallOnForegroundThread(v8::Isolate* isolate, Task* task) override {
|
|
task_runner_->PostTask(std::unique_ptr<Task>(task));
|
|
}
|
|
|
|
void CallOnWorkerThread(v8::Task* task) override {
|
|
task_runner_->PostTask(std::unique_ptr<Task>(task));
|
|
}
|
|
|
|
bool IdleTasksEnabled(v8::Isolate* isolate) override { return false; }
|
|
|
|
void ExecuteTasks() { task_runner_->ExecuteTasks(); }
|
|
|
|
private:
|
|
class MockTaskRunner final : public TaskRunner {
|
|
public:
|
|
void PostTask(std::unique_ptr<v8::Task> task) override {
|
|
tasks_.push_back(std::move(task));
|
|
}
|
|
|
|
void PostDelayedTask(std::unique_ptr<Task> task,
|
|
double delay_in_seconds) override {
|
|
UNREACHABLE();
|
|
};
|
|
|
|
void PostIdleTask(std::unique_ptr<IdleTask> task) override {
|
|
UNREACHABLE();
|
|
}
|
|
|
|
bool IdleTasksEnabled() override { return false; };
|
|
|
|
void ExecuteTasks() {
|
|
while (!tasks_.empty()) {
|
|
std::unique_ptr<Task> task = std::move(tasks_.back());
|
|
tasks_.pop_back();
|
|
task->Run();
|
|
}
|
|
}
|
|
|
|
private:
|
|
// We do not execute tasks concurrently, so we only need one list of tasks.
|
|
std::vector<std::unique_ptr<v8::Task>> tasks_;
|
|
};
|
|
|
|
std::shared_ptr<MockTaskRunner> task_runner_;
|
|
};
|
|
|
|
namespace {
|
|
|
|
class StreamTester {
|
|
public:
|
|
StreamTester() : zone_(&allocator_, "StreamTester") {
|
|
v8::Isolate* isolate = CcTest::isolate();
|
|
i::Isolate* i_isolate = CcTest::i_isolate();
|
|
|
|
// Create the promise for the streaming compilation.
|
|
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
|
v8::Local<Promise::Resolver> resolver;
|
|
CHECK(Promise::Resolver::New(context).ToLocal(&resolver));
|
|
CHECK(!i_isolate->has_scheduled_exception());
|
|
promise_ = resolver->GetPromise();
|
|
|
|
i::Handle<i::JSPromise> i_promise = v8::Utils::OpenHandle(*promise_);
|
|
|
|
stream_ = i_isolate->wasm_engine()
|
|
->compilation_manager()
|
|
->StartStreamingCompilation(
|
|
i_isolate, v8::Utils::OpenHandle(*context), i_promise);
|
|
}
|
|
|
|
std::shared_ptr<StreamingDecoder> stream() { return stream_; }
|
|
|
|
// Run all compiler tasks, both foreground and background tasks.
|
|
void RunCompilerTasks() {
|
|
static_cast<MockPlatform*>(i::V8::GetCurrentPlatform())->ExecuteTasks();
|
|
}
|
|
|
|
bool IsPromiseFulfilled() {
|
|
return promise_->State() == v8::Promise::kFulfilled;
|
|
}
|
|
|
|
bool IsPromiseRejected() {
|
|
return promise_->State() == v8::Promise::kRejected;
|
|
}
|
|
|
|
bool IsPromisePending() { return promise_->State() == v8::Promise::kPending; }
|
|
|
|
void OnBytesReceived(const uint8_t* start, size_t length) {
|
|
stream_->OnBytesReceived(Vector<const uint8_t>(start, length));
|
|
}
|
|
|
|
void FinishStream() { stream_->Finish(); }
|
|
|
|
Zone* zone() { return &zone_; }
|
|
|
|
private:
|
|
AccountingAllocator allocator_;
|
|
Zone zone_;
|
|
v8::Local<v8::Promise> promise_;
|
|
std::shared_ptr<StreamingDecoder> stream_;
|
|
};
|
|
} // namespace
|
|
|
|
#define STREAM_TEST(name) \
|
|
void RunStream_##name(); \
|
|
TEST(name) { \
|
|
MockPlatform platform; \
|
|
CcTest::InitializeVM(); \
|
|
v8::HandleScope handle_scope(CcTest::isolate()); \
|
|
i::HandleScope internal_scope(CcTest::i_isolate()); \
|
|
RunStream_##name(); \
|
|
} \
|
|
void RunStream_##name()
|
|
|
|
// Create a valid module with 3 functions.
|
|
ZoneBuffer GetValidModuleBytes(Zone* zone) {
|
|
ZoneBuffer buffer(zone);
|
|
TestSignatures sigs;
|
|
WasmModuleBuilder builder(zone);
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 0, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 1, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 2, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
builder.WriteTo(buffer);
|
|
return buffer;
|
|
}
|
|
|
|
// Test that all bytes arrive before doing any compilation. FinishStream is
|
|
// called immediately.
|
|
STREAM_TEST(TestAllBytesArriveImmediatelyStreamFinishesFirst) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetValidModuleBytes(tester.zone());
|
|
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
tester.FinishStream();
|
|
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
// Test that all bytes arrive before doing any compilation. FinishStream is
|
|
// called after the compilation is done.
|
|
STREAM_TEST(TestAllBytesArriveAOTCompilerFinishesFirst) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetValidModuleBytes(tester.zone());
|
|
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
size_t GetFunctionOffset(i::Isolate* isolate, const uint8_t* buffer,
|
|
size_t size, size_t index) {
|
|
ModuleResult result = SyncDecodeWasmModule(isolate, buffer, buffer + size,
|
|
false, ModuleOrigin::kWasmOrigin);
|
|
CHECK(result.ok());
|
|
std::unique_ptr<WasmModule> module = std::move(result.val);
|
|
const WasmFunction* func = &module->functions[1];
|
|
return func->code.offset();
|
|
}
|
|
|
|
// Test that some functions come in the beginning, some come after some
|
|
// functions already got compiled.
|
|
STREAM_TEST(TestCutAfterOneFunctionStreamFinishesFirst) {
|
|
i::Isolate* isolate = CcTest::i_isolate();
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetValidModuleBytes(tester.zone());
|
|
|
|
size_t offset = GetFunctionOffset(isolate, buffer.begin(), buffer.size(), 1);
|
|
tester.OnBytesReceived(buffer.begin(), offset);
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromisePending());
|
|
tester.OnBytesReceived(buffer.begin() + offset, buffer.size() - offset);
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
// Test that some functions come in the beginning, some come after some
|
|
// functions already got compiled. Call FinishStream after the compilation is
|
|
// done.
|
|
STREAM_TEST(TestCutAfterOneFunctionCompilerFinishesFirst) {
|
|
i::Isolate* isolate = CcTest::i_isolate();
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetValidModuleBytes(tester.zone());
|
|
|
|
size_t offset = GetFunctionOffset(isolate, buffer.begin(), buffer.size(), 1);
|
|
tester.OnBytesReceived(buffer.begin(), offset);
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromisePending());
|
|
tester.OnBytesReceived(buffer.begin() + offset, buffer.size() - offset);
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
// Create a module with an invalid global section.
|
|
ZoneBuffer GetModuleWithInvalidSection(Zone* zone) {
|
|
ZoneBuffer buffer(zone);
|
|
TestSignatures sigs;
|
|
WasmModuleBuilder builder(zone);
|
|
// Add an invalid global to the module. The decoder will fail there.
|
|
builder.AddGlobal(kWasmStmt, false, true,
|
|
WasmInitExpr(WasmInitExpr::kGlobalIndex, 12));
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 0, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 1, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 2, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
builder.WriteTo(buffer);
|
|
return buffer;
|
|
}
|
|
|
|
// Test an error in a section, found by the ModuleDecoder.
|
|
STREAM_TEST(TestErrorInSectionStreamFinishesFirst) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetModuleWithInvalidSection(tester.zone());
|
|
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
tester.FinishStream();
|
|
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
STREAM_TEST(TestErrorInSectionCompilerFinishesFirst) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetModuleWithInvalidSection(tester.zone());
|
|
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
STREAM_TEST(TestErrorInSectionWithCuts) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetModuleWithInvalidSection(tester.zone());
|
|
|
|
const uint8_t* current = buffer.begin();
|
|
size_t remaining = buffer.end() - buffer.begin();
|
|
while (current < buffer.end()) {
|
|
size_t size = std::min(remaining, size_t{10});
|
|
tester.OnBytesReceived(current, size);
|
|
tester.RunCompilerTasks();
|
|
current += 10;
|
|
remaining -= size;
|
|
}
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
ZoneBuffer GetModuleWithInvalidSectionSize(Zone* zone) {
|
|
// We get a valid module and overwrite the size of the first section with an
|
|
// invalid value.
|
|
ZoneBuffer buffer = GetValidModuleBytes(zone);
|
|
// 9 == 4 (wasm magic) + 4 (version) + 1 (section code)
|
|
uint8_t* section_size_address = const_cast<uint8_t*>(buffer.begin()) + 9;
|
|
// 0x808080800F is an invalid module size in leb encoding.
|
|
section_size_address[0] = 0x80;
|
|
section_size_address[1] = 0x80;
|
|
section_size_address[2] = 0x80;
|
|
section_size_address[3] = 0x80;
|
|
section_size_address[4] = 0x0F;
|
|
return buffer;
|
|
}
|
|
|
|
STREAM_TEST(TestErrorInSectionSizeStreamFinishesFirst) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetModuleWithInvalidSectionSize(tester.zone());
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
STREAM_TEST(TestErrorInSectionSizeCompilerFinishesFirst) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetModuleWithInvalidSectionSize(tester.zone());
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
STREAM_TEST(TestErrorInSectionSizeWithCuts) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer = GetModuleWithInvalidSectionSize(tester.zone());
|
|
const uint8_t* current = buffer.begin();
|
|
size_t remaining = buffer.end() - buffer.begin();
|
|
while (current < buffer.end()) {
|
|
size_t size = std::min(remaining, size_t{10});
|
|
tester.OnBytesReceived(current, size);
|
|
tester.RunCompilerTasks();
|
|
current += 10;
|
|
remaining -= size;
|
|
}
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
// Test an error in the code section, found by the ModuleDecoder. The error is a
|
|
// functions count in the code section which differs from the functions count in
|
|
// the function section.
|
|
STREAM_TEST(TestErrorInCodeSectionDetectedByModuleDecoder) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // body size
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 2), // section size
|
|
U32V_1(2), // !!! invalid function count !!!
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.FinishStream();
|
|
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
// Test an error in the code section, found by the StreamingDecoder. The error
|
|
// is an invalid function body size, so that there are not enough bytes in the
|
|
// code section for the function body.
|
|
STREAM_TEST(TestErrorInCodeSectionDetectedByStreamingDecoder) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(26), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 3), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.FinishStream();
|
|
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
// Test an error in the code section, found by the Compiler. The error is an
|
|
// invalid return type.
|
|
STREAM_TEST(TestErrorInCodeSectionDetectedByCompiler) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
uint8_t invalid_code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprI64Const, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 2 +
|
|
arraysize(invalid_code)), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.RunCompilerTasks();
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.OnBytesReceived(invalid_code, arraysize(invalid_code));
|
|
tester.RunCompilerTasks();
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
|
|
// Test Abort before any bytes arrive.
|
|
STREAM_TEST(TestAbortImmediately) {
|
|
StreamTester tester;
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort within a section.
|
|
STREAM_TEST(TestAbortWithinSection1) {
|
|
StreamTester tester;
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1) // type count
|
|
// Type section is not yet complete.
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort within a section.
|
|
STREAM_TEST(TestAbortWithinSection2) {
|
|
StreamTester tester;
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
// Function section is not yet complete.
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort just before the code section.
|
|
STREAM_TEST(TestAbortAfterSection) {
|
|
StreamTester tester;
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort after the function count in the code section. The compiler tasks
|
|
// execute before the abort.
|
|
STREAM_TEST(TestAbortAfterFunctionsCount1) {
|
|
StreamTester tester;
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(20), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort after the function count in the code section. The compiler tasks
|
|
// do not execute before the abort.
|
|
STREAM_TEST(TestAbortAfterFunctionsCount2) {
|
|
StreamTester tester;
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(20), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort after some functions got compiled. The compiler tasks execute
|
|
// before the abort.
|
|
STREAM_TEST(TestAbortAfterFunctionGotCompiled1) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(20), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort after some functions got compiled. The compiler tasks execute
|
|
// before the abort.
|
|
STREAM_TEST(TestAbortAfterFunctionGotCompiled2) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(20), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort after all functions got compiled.
|
|
STREAM_TEST(TestAbortAfterCodeSection1) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // body size
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 3), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
// Test Abort after all functions got compiled.
|
|
STREAM_TEST(TestAbortAfterCodeSection2) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // body size
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 3), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
STREAM_TEST(TestAbortAfterCompilationError1) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
uint8_t invalid_code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprI64Const, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 2 +
|
|
arraysize(invalid_code)), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(invalid_code, arraysize(invalid_code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
STREAM_TEST(TestAbortAfterCompilationError2) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
uint8_t invalid_code[] = {
|
|
U32V_1(4), // !!! invalid body size !!!
|
|
U32V_1(0), // locals count
|
|
kExprI64Const, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 2 +
|
|
arraysize(invalid_code)), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(invalid_code, arraysize(invalid_code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.stream()->Abort();
|
|
tester.RunCompilerTasks();
|
|
}
|
|
|
|
STREAM_TEST(TestOnlyModuleHeader) {
|
|
StreamTester tester;
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
STREAM_TEST(TestModuleWithZeroFunctions) {
|
|
StreamTester tester;
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1), // section size
|
|
U32V_1(0), // type count
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1), // section size
|
|
U32V_1(0), // functions count
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1), // section size
|
|
U32V_1(0), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
STREAM_TEST(TestModuleWithMultipleFunctions) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // body size
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 3), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
STREAM_TEST(TestModuleWithDataSection) {
|
|
StreamTester tester;
|
|
|
|
uint8_t code[] = {
|
|
U32V_1(4), // body size
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, 0, kExprEnd // body
|
|
};
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 3), // section size
|
|
U32V_1(3), // functions count
|
|
0, // signature index
|
|
0, // signature index
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(1 + arraysize(code) * 3), // section size
|
|
U32V_1(3), // functions count
|
|
};
|
|
|
|
const uint8_t data_section[] = {
|
|
kDataSectionCode, // section code
|
|
U32V_1(1), // section size
|
|
U32V_1(0), // data segment count
|
|
};
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.OnBytesReceived(code, arraysize(code));
|
|
tester.RunCompilerTasks();
|
|
tester.OnBytesReceived(data_section, arraysize(data_section));
|
|
tester.RunCompilerTasks();
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
// Test that all bytes arrive before doing any compilation. FinishStream is
|
|
// called immediately.
|
|
STREAM_TEST(TestModuleWithImportedFunction) {
|
|
StreamTester tester;
|
|
ZoneBuffer buffer(tester.zone());
|
|
TestSignatures sigs;
|
|
WasmModuleBuilder builder(tester.zone());
|
|
builder.AddImport(ArrayVector("Test"), sigs.i_iii());
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.i_iii());
|
|
uint8_t code[] = {kExprGetLocal, 0, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
builder.WriteTo(buffer);
|
|
|
|
tester.OnBytesReceived(buffer.begin(), buffer.end() - buffer.begin());
|
|
tester.FinishStream();
|
|
|
|
tester.RunCompilerTasks();
|
|
|
|
CHECK(tester.IsPromiseFulfilled());
|
|
}
|
|
|
|
STREAM_TEST(TestModuleWithErrorAfterDataSection) {
|
|
StreamTester tester;
|
|
|
|
const uint8_t bytes[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_x_x), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_x_x(kLocalI32, kLocalI32), // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(1 + 1), // section size
|
|
U32V_1(1), // functions count
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(6), // section size
|
|
U32V_1(1), // functions count
|
|
U32V_1(4), // body size
|
|
U32V_1(0), // locals count
|
|
kExprGetLocal, // some code
|
|
0, // some code
|
|
kExprEnd, // some code
|
|
kDataSectionCode, // section code
|
|
U32V_1(1), // section size
|
|
U32V_1(0), // data segment count
|
|
kUnknownSectionCode, // section code
|
|
U32V_1(1), // invalid section size
|
|
};
|
|
|
|
tester.OnBytesReceived(bytes, arraysize(bytes));
|
|
tester.FinishStream();
|
|
tester.RunCompilerTasks();
|
|
CHECK(tester.IsPromiseRejected());
|
|
}
|
|
#undef STREAM_TEST
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|