0427d9ff2a
Requesting reservation of a wasm section for experimentation with storing source code meta information, such as source code comments, and also extra inform on presentation of the AST such an `if-block` pattern being presented as a `when` operation. The wasm design already defines unrecognized sections to be ignored, and this reserved section is ignored. This section is only intended to hold source code meta information and to have no effect on code execution. With wasm going live (behind a flag) on v8, I would also like to be able to give people something to play with in terms of the deployed binary code being a useful source code. It's all experimental, but I understand the entire binary format that V8 is currently using is basically a throwaway, and that the working strategy is to get something running and then revisit format decisions. I would like a fixed reserved section number to avoid potential clashes with other projects - although I am not aware of any other calls for addition sections beyond the need for debug info. If a fixed number is not acceptable, then could this patch alternatively ignore all unrecognized sections and perhaps add the section size to them all - something which is already noted todo in the design document? BUG= Review URL: https://codereview.chromium.org/1565693002 Cr-Commit-Position: refs/heads/master@{#33165}
958 lines
28 KiB
C++
958 lines
28 KiB
C++
// Copyright 2015 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/wasm/module-decoder.h"
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
class WasmModuleVerifyTest : public TestWithZone {
|
|
public:
|
|
ModuleResult DecodeModule(const byte* module_start, const byte* module_end) {
|
|
return DecodeWasmModule(nullptr, zone(), module_start, module_end, false,
|
|
false);
|
|
}
|
|
};
|
|
|
|
|
|
#define EXPECT_VERIFIES(data) \
|
|
do { \
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data)); \
|
|
EXPECT_TRUE(result.ok()); \
|
|
if (result.val) delete result.val; \
|
|
} while (false)
|
|
|
|
|
|
#define EXPECT_FAILURE(data) \
|
|
do { \
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data)); \
|
|
EXPECT_FALSE(result.ok()); \
|
|
if (result.val) delete result.val; \
|
|
} while (false)
|
|
|
|
|
|
struct LocalTypePair {
|
|
uint8_t code;
|
|
LocalType type;
|
|
} kLocalTypes[] = {{kLocalI32, kAstI32},
|
|
{kLocalI64, kAstI64},
|
|
{kLocalF32, kAstF32},
|
|
{kLocalF64, kAstF64}};
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, DecodeEmpty) {
|
|
static const byte data[1]{kDeclEnd};
|
|
{
|
|
ModuleResult result = DecodeModule(data, data);
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
{
|
|
ModuleResult result = DecodeModule(data, data + 1);
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneGlobal) {
|
|
const byte data[] = {
|
|
kDeclGlobals,
|
|
1,
|
|
0,
|
|
0,
|
|
0,
|
|
0, // name offset
|
|
kMemI32, // memory type
|
|
0, // exported
|
|
};
|
|
|
|
{
|
|
// Should decode to exactly one global.
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(1, result.val->globals->size());
|
|
EXPECT_EQ(0, result.val->functions->size());
|
|
EXPECT_EQ(0, result.val->data_segments->size());
|
|
|
|
WasmGlobal* global = &result.val->globals->back();
|
|
|
|
EXPECT_EQ(0, global->name_offset);
|
|
EXPECT_EQ(MachineType::Int32(), global->type);
|
|
EXPECT_EQ(0, global->offset);
|
|
EXPECT_FALSE(global->exported);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
for (size_t size = 1; size < arraysize(data); size++) {
|
|
// Should fall off end of module bytes.
|
|
ModuleResult result = DecodeModule(data, data + size);
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, ZeroGlobals) {
|
|
const byte data[] = {
|
|
kDeclGlobals, 0, // declare 0 globals
|
|
};
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
static void AppendUint32v(std::vector<byte>& buffer, uint32_t val) {
|
|
while (true) {
|
|
uint32_t next = val >> 7;
|
|
uint32_t out = val & 0x7f;
|
|
if (next) {
|
|
buffer.push_back(static_cast<byte>(0x80 | out));
|
|
val = next;
|
|
} else {
|
|
buffer.push_back(static_cast<byte>(out));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, NGlobals) {
|
|
const byte data[] = {
|
|
0, 0, 0, 0, // name offset
|
|
kMemI32, // memory type
|
|
0, // exported
|
|
};
|
|
for (uint32_t i = 0; i < 1000000; i = i * 7 + 1) {
|
|
std::vector<byte> buffer;
|
|
buffer.push_back(kDeclGlobals);
|
|
AppendUint32v(buffer, i);
|
|
for (uint32_t j = 0; j < i; j++) {
|
|
buffer.insert(buffer.end(), data, data + arraysize(data));
|
|
}
|
|
|
|
ModuleResult result = DecodeModule(&buffer[0], &buffer[0] + buffer.size());
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidNameOffset) {
|
|
const byte data[] = {
|
|
kDeclGlobals,
|
|
1, // declare one global
|
|
0,
|
|
3,
|
|
0,
|
|
0, // name offset
|
|
kMemI32, // memory type
|
|
0, // exported
|
|
};
|
|
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, GlobalWithInvalidMemoryType) {
|
|
const byte data[] = {
|
|
kDeclGlobals,
|
|
1, // declare one global
|
|
0,
|
|
0,
|
|
0,
|
|
0, // name offset
|
|
33, // memory type
|
|
0, // exported
|
|
};
|
|
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, TwoGlobals) {
|
|
const byte data[] = {
|
|
kDeclGlobals,
|
|
2,
|
|
0,
|
|
0,
|
|
0,
|
|
0, // #0: name offset
|
|
kMemF32, // memory type
|
|
0, // exported
|
|
0,
|
|
0,
|
|
0,
|
|
0, // #1: name offset
|
|
kMemF64, // memory type
|
|
1, // exported
|
|
};
|
|
|
|
{
|
|
// Should decode to exactly two globals.
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(2, result.val->globals->size());
|
|
EXPECT_EQ(0, result.val->functions->size());
|
|
EXPECT_EQ(0, result.val->data_segments->size());
|
|
|
|
WasmGlobal* g0 = &result.val->globals->at(0);
|
|
WasmGlobal* g1 = &result.val->globals->at(1);
|
|
|
|
EXPECT_EQ(0, g0->name_offset);
|
|
EXPECT_EQ(MachineType::Float32(), g0->type);
|
|
EXPECT_EQ(0, g0->offset);
|
|
EXPECT_FALSE(g0->exported);
|
|
|
|
EXPECT_EQ(0, g1->name_offset);
|
|
EXPECT_EQ(MachineType::Float64(), g1->type);
|
|
EXPECT_EQ(0, g1->offset);
|
|
EXPECT_TRUE(g1->exported);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
for (size_t size = 1; size < arraysize(data); size++) {
|
|
// Should fall off end of module bytes.
|
|
ModuleResult result = DecodeModule(data, data + size);
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneSignature) {
|
|
static const byte data[] = {
|
|
kDeclSignatures, 1, 0, kLocalVoid // void -> void
|
|
};
|
|
EXPECT_VERIFIES(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, MultipleSignatures) {
|
|
static const byte data[] = {
|
|
kDeclSignatures,
|
|
3,
|
|
0,
|
|
kLocalVoid, // void -> void
|
|
1,
|
|
kLocalI32,
|
|
kLocalF32, // f32 -> i32
|
|
2,
|
|
kLocalI32,
|
|
kLocalF64,
|
|
kLocalF64, // (f64,f64) -> i32
|
|
};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(3, result.val->signatures->size());
|
|
if (result.val->signatures->size() == 3) {
|
|
EXPECT_EQ(0, result.val->signatures->at(0)->return_count());
|
|
EXPECT_EQ(1, result.val->signatures->at(1)->return_count());
|
|
EXPECT_EQ(1, result.val->signatures->at(2)->return_count());
|
|
|
|
EXPECT_EQ(0, result.val->signatures->at(0)->parameter_count());
|
|
EXPECT_EQ(1, result.val->signatures->at(1)->parameter_count());
|
|
EXPECT_EQ(2, result.val->signatures->at(2)->parameter_count());
|
|
}
|
|
if (result.val) delete result.val;
|
|
|
|
for (size_t size = 1; size < arraysize(data); size++) {
|
|
ModuleResult result = DecodeModule(data, data + size);
|
|
// Should fall off the end of module bytes.
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, FunctionWithoutSig) {
|
|
static const byte data[] = {
|
|
kDeclFunctions, 1,
|
|
// func#0 ------------------------------------------------------
|
|
0, 0, // signature index
|
|
0, 0, 0, 0, // name offset
|
|
0, 0, 0, 0, // code start offset
|
|
0, 0, 0, 0, // code end offset
|
|
1, 2, // local int32 count
|
|
3, 4, // local int64 count
|
|
5, 6, // local float32 count
|
|
7, 8, // local float64 count
|
|
0, // exported
|
|
1 // external
|
|
};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneEmptyVoidVoidFunction) {
|
|
const int kCodeStartOffset = 23;
|
|
const int kCodeEndOffset = kCodeStartOffset + 1;
|
|
|
|
static const byte data[] = {
|
|
kDeclSignatures, 1,
|
|
// sig#0 -------------------------------------------------------
|
|
0, 0, // void -> void
|
|
// func#0 ------------------------------------------------------
|
|
kDeclFunctions, 1,
|
|
kDeclFunctionLocals | kDeclFunctionExport | kDeclFunctionName, 0,
|
|
0, // signature index
|
|
9, 0, 0, 0, // name offset
|
|
11, 2, // local int32 count
|
|
13, 4, // local int64 count
|
|
15, 6, // local float32 count
|
|
17, 8, // local float64 count
|
|
1, 0, // size
|
|
kExprNop,
|
|
};
|
|
|
|
{
|
|
// Should decode to exactly one function.
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(0, result.val->globals->size());
|
|
EXPECT_EQ(1, result.val->signatures->size());
|
|
EXPECT_EQ(1, result.val->functions->size());
|
|
EXPECT_EQ(0, result.val->data_segments->size());
|
|
EXPECT_EQ(0, result.val->function_table->size());
|
|
|
|
WasmFunction* function = &result.val->functions->back();
|
|
|
|
EXPECT_EQ(9, function->name_offset);
|
|
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
|
|
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
|
|
|
|
EXPECT_EQ(523, function->local_int32_count);
|
|
EXPECT_EQ(1037, function->local_int64_count);
|
|
EXPECT_EQ(1551, function->local_float32_count);
|
|
EXPECT_EQ(2065, function->local_float64_count);
|
|
|
|
EXPECT_TRUE(function->exported);
|
|
EXPECT_FALSE(function->external);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
for (size_t size = 5; size < arraysize(data); size++) {
|
|
// Should fall off end of module bytes.
|
|
ModuleResult result = DecodeModule(data, data + size);
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneFunctionImported) {
|
|
static const byte data[] = {
|
|
kDeclSignatures, 1,
|
|
// sig#0 -------------------------------------------------------
|
|
0, 0, // void -> void
|
|
kDeclFunctions, 1,
|
|
// func#0 ------------------------------------------------------
|
|
kDeclFunctionImport, // no name, no locals, imported
|
|
0, 0, // signature index
|
|
};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(1, result.val->functions->size());
|
|
WasmFunction* function = &result.val->functions->back();
|
|
|
|
EXPECT_EQ(0, function->name_offset);
|
|
EXPECT_EQ(0, function->code_start_offset);
|
|
EXPECT_EQ(0, function->code_end_offset);
|
|
|
|
EXPECT_EQ(0, function->local_int32_count);
|
|
EXPECT_EQ(0, function->local_int64_count);
|
|
EXPECT_EQ(0, function->local_float32_count);
|
|
EXPECT_EQ(0, function->local_float64_count);
|
|
|
|
EXPECT_FALSE(function->exported);
|
|
EXPECT_TRUE(function->external);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneFunctionWithNopBody) {
|
|
static const byte kCodeStartOffset = 11;
|
|
static const byte kCodeEndOffset = kCodeStartOffset + 1;
|
|
|
|
static const byte data[] = {
|
|
kDeclSignatures, 1,
|
|
// sig#0 -------------------------------------------------------
|
|
0, 0, // void -> void
|
|
kDeclFunctions, 1,
|
|
// func#0 ------------------------------------------------------
|
|
0, // no name, no locals
|
|
0, 0, // signature index
|
|
1, 0, // body size
|
|
kExprNop // body
|
|
};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(1, result.val->functions->size());
|
|
WasmFunction* function = &result.val->functions->back();
|
|
|
|
EXPECT_EQ(0, function->name_offset);
|
|
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
|
|
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
|
|
|
|
EXPECT_EQ(0, function->local_int32_count);
|
|
EXPECT_EQ(0, function->local_int64_count);
|
|
EXPECT_EQ(0, function->local_float32_count);
|
|
EXPECT_EQ(0, function->local_float64_count);
|
|
|
|
EXPECT_FALSE(function->exported);
|
|
EXPECT_FALSE(function->external);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneFunctionWithNopBody_WithLocals) {
|
|
static const byte kCodeStartOffset = 19;
|
|
static const byte kCodeEndOffset = kCodeStartOffset + 1;
|
|
|
|
static const byte data[] = {
|
|
kDeclSignatures, 1,
|
|
// sig#0 -------------------------------------------------------
|
|
0, 0, // void -> void
|
|
kDeclFunctions, 1,
|
|
// func#0 ------------------------------------------------------
|
|
kDeclFunctionLocals, 0, 0, // signature index
|
|
1, 2, // local int32 count
|
|
3, 4, // local int64 count
|
|
5, 6, // local float32 count
|
|
7, 8, // local float64 count
|
|
1, 0, // body size
|
|
kExprNop // body
|
|
};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(1, result.val->functions->size());
|
|
WasmFunction* function = &result.val->functions->back();
|
|
|
|
EXPECT_EQ(0, function->name_offset);
|
|
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
|
|
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
|
|
|
|
EXPECT_EQ(513, function->local_int32_count);
|
|
EXPECT_EQ(1027, function->local_int64_count);
|
|
EXPECT_EQ(1541, function->local_float32_count);
|
|
EXPECT_EQ(2055, function->local_float64_count);
|
|
|
|
EXPECT_FALSE(function->exported);
|
|
EXPECT_FALSE(function->external);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneGlobalOneFunctionWithNopBodyOneDataSegment) {
|
|
static const byte kCodeStartOffset = 2 + kDeclGlobalSize + 4 + 2 + 17;
|
|
static const byte kCodeEndOffset = kCodeStartOffset + 3;
|
|
|
|
static const byte data[] = {
|
|
// global#0 --------------------------------------------------
|
|
kDeclGlobals, 1, 0, 0, 0, 0, // name offset
|
|
kMemU8, // memory type
|
|
0, // exported
|
|
// sig#0 -----------------------------------------------------
|
|
kDeclSignatures, 1, 0, 0, // void -> void
|
|
// func#0 ----------------------------------------------------
|
|
kDeclFunctions, 1, kDeclFunctionLocals | kDeclFunctionName, 0,
|
|
0, // signature index
|
|
9, 0, 0, 0, // name offset
|
|
1, 2, // local int32 count
|
|
3, 4, // local int64 count
|
|
5, 6, // local float32 count
|
|
7, 8, // local float64 count
|
|
3, 0, // body size
|
|
kExprNop, // func#0 body
|
|
kExprNop, // func#0 body
|
|
kExprNop, // func#0 body
|
|
// segment#0 -------------------------------------------------
|
|
kDeclDataSegments, 1, 0xae, 0xb3, 0x08, 0, // dest addr
|
|
15, 0, 0, 0, // source offset
|
|
5, 0, 0, 0, // source size
|
|
1, // init
|
|
// rest ------------------------------------------------------
|
|
kDeclEnd,
|
|
};
|
|
|
|
{
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(1, result.val->globals->size());
|
|
EXPECT_EQ(1, result.val->functions->size());
|
|
EXPECT_EQ(1, result.val->data_segments->size());
|
|
|
|
WasmGlobal* global = &result.val->globals->back();
|
|
|
|
EXPECT_EQ(0, global->name_offset);
|
|
EXPECT_EQ(MachineType::Uint8(), global->type);
|
|
EXPECT_EQ(0, global->offset);
|
|
EXPECT_FALSE(global->exported);
|
|
|
|
WasmFunction* function = &result.val->functions->back();
|
|
|
|
EXPECT_EQ(9, function->name_offset);
|
|
EXPECT_EQ(kCodeStartOffset, function->code_start_offset);
|
|
EXPECT_EQ(kCodeEndOffset, function->code_end_offset);
|
|
|
|
EXPECT_FALSE(function->exported);
|
|
EXPECT_FALSE(function->external);
|
|
|
|
WasmDataSegment* segment = &result.val->data_segments->back();
|
|
|
|
EXPECT_EQ(0x8b3ae, segment->dest_addr);
|
|
EXPECT_EQ(15, segment->source_offset);
|
|
EXPECT_EQ(5, segment->source_size);
|
|
EXPECT_TRUE(segment->init);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneDataSegment) {
|
|
const byte data[] = {
|
|
kDeclDataSegments,
|
|
1,
|
|
0xaa,
|
|
0xbb,
|
|
0x09,
|
|
0, // dest addr
|
|
11,
|
|
0,
|
|
0,
|
|
0, // source offset
|
|
3,
|
|
0,
|
|
0,
|
|
0, // source size
|
|
1, // init
|
|
};
|
|
|
|
{
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(0, result.val->globals->size());
|
|
EXPECT_EQ(0, result.val->functions->size());
|
|
EXPECT_EQ(1, result.val->data_segments->size());
|
|
|
|
WasmDataSegment* segment = &result.val->data_segments->back();
|
|
|
|
EXPECT_EQ(0x9bbaa, segment->dest_addr);
|
|
EXPECT_EQ(11, segment->source_offset);
|
|
EXPECT_EQ(3, segment->source_size);
|
|
EXPECT_TRUE(segment->init);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
for (size_t size = 1; size < arraysize(data); size++) {
|
|
// Should fall off end of module bytes.
|
|
ModuleResult result = DecodeModule(data, data + size);
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, TwoDataSegments) {
|
|
const byte data[] = {
|
|
kDeclDataSegments,
|
|
2,
|
|
0xee,
|
|
0xff,
|
|
0x07,
|
|
0, // dest addr
|
|
9,
|
|
0,
|
|
0,
|
|
0, // #0: source offset
|
|
4,
|
|
0,
|
|
0,
|
|
0, // source size
|
|
0, // init
|
|
0xcc,
|
|
0xdd,
|
|
0x06,
|
|
0, // #1: dest addr
|
|
6,
|
|
0,
|
|
0,
|
|
0, // source offset
|
|
10,
|
|
0,
|
|
0,
|
|
0, // source size
|
|
1, // init
|
|
};
|
|
|
|
{
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(0, result.val->globals->size());
|
|
EXPECT_EQ(0, result.val->functions->size());
|
|
EXPECT_EQ(2, result.val->data_segments->size());
|
|
|
|
WasmDataSegment* s0 = &result.val->data_segments->at(0);
|
|
WasmDataSegment* s1 = &result.val->data_segments->at(1);
|
|
|
|
EXPECT_EQ(0x7ffee, s0->dest_addr);
|
|
EXPECT_EQ(9, s0->source_offset);
|
|
EXPECT_EQ(4, s0->source_size);
|
|
EXPECT_FALSE(s0->init);
|
|
|
|
EXPECT_EQ(0x6ddcc, s1->dest_addr);
|
|
EXPECT_EQ(6, s1->source_offset);
|
|
EXPECT_EQ(10, s1->source_size);
|
|
EXPECT_TRUE(s1->init);
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
for (size_t size = 1; size < arraysize(data); size++) {
|
|
// Should fall off end of module bytes.
|
|
ModuleResult result = DecodeModule(data, data + size);
|
|
EXPECT_FALSE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
}
|
|
|
|
|
|
// To make below tests for indirect calls much shorter.
|
|
#define FUNCTION(sig_index, external) \
|
|
kDeclFunctionImport, static_cast<byte>(sig_index), \
|
|
static_cast<byte>(sig_index >> 8)
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, OneIndirectFunction) {
|
|
static const byte data[] = {
|
|
// sig#0 -------------------------------------------------------
|
|
kDeclSignatures, 1, 0, 0, // void -> void
|
|
// func#0 ------------------------------------------------------
|
|
kDeclFunctions, 1, FUNCTION(0, 0),
|
|
// indirect table ----------------------------------------------
|
|
kDeclFunctionTable, 1, 0, 0};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.ok()) {
|
|
EXPECT_EQ(1, result.val->signatures->size());
|
|
EXPECT_EQ(1, result.val->functions->size());
|
|
EXPECT_EQ(1, result.val->function_table->size());
|
|
EXPECT_EQ(0, result.val->function_table->at(0));
|
|
}
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, MultipleIndirectFunctions) {
|
|
static const byte data[] = {
|
|
// sig#0 -------------------------------------------------------
|
|
kDeclSignatures, 2, 0, 0, // void -> void
|
|
0, kLocalI32, // void -> i32
|
|
// func#0 ------------------------------------------------------
|
|
kDeclFunctions, 4, FUNCTION(0, 1), FUNCTION(1, 1), FUNCTION(0, 1),
|
|
FUNCTION(1, 1),
|
|
// indirect table ----------------------------------------------
|
|
kDeclFunctionTable, 8, 0, 0, 1, 0, 2, 0, 3, 0, 0, 0, 1, 0, 2, 0, 3, 0,
|
|
};
|
|
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.ok()) {
|
|
EXPECT_EQ(2, result.val->signatures->size());
|
|
EXPECT_EQ(4, result.val->functions->size());
|
|
EXPECT_EQ(8, result.val->function_table->size());
|
|
for (int i = 0; i < 8; i++) {
|
|
EXPECT_EQ(i & 3, result.val->function_table->at(i));
|
|
}
|
|
}
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, IndirectFunctionNoFunctions) {
|
|
static const byte data[] = {
|
|
// sig#0 -------------------------------------------------------
|
|
kDeclSignatures, 1, 0, 0, // void -> void
|
|
// indirect table ----------------------------------------------
|
|
kDeclFunctionTable, 1, 0, 0,
|
|
};
|
|
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, IndirectFunctionInvalidIndex) {
|
|
static const byte data[] = {
|
|
// sig#0 -------------------------------------------------------
|
|
kDeclSignatures, 1, 0, 0, // void -> void
|
|
// functions ---------------------------------------------------
|
|
kDeclFunctions, 1, FUNCTION(0, 1),
|
|
// indirect table ----------------------------------------------
|
|
kDeclFunctionTable, 1, 1, 0,
|
|
};
|
|
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
class WasmSignatureDecodeTest : public TestWithZone {};
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Ok_v_v) {
|
|
static const byte data[] = {0, 0};
|
|
Zone zone;
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(&zone, data, data + arraysize(data));
|
|
|
|
EXPECT_TRUE(sig != nullptr);
|
|
EXPECT_EQ(0, sig->parameter_count());
|
|
EXPECT_EQ(0, sig->return_count());
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Ok_t_v) {
|
|
for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
|
|
LocalTypePair ret_type = kLocalTypes[i];
|
|
const byte data[] = {0, ret_type.code};
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
|
|
|
|
EXPECT_TRUE(sig != nullptr);
|
|
EXPECT_EQ(0, sig->parameter_count());
|
|
EXPECT_EQ(1, sig->return_count());
|
|
EXPECT_EQ(ret_type.type, sig->GetReturn());
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Ok_v_t) {
|
|
for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
|
|
LocalTypePair param_type = kLocalTypes[i];
|
|
const byte data[] = {1, 0, param_type.code};
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
|
|
|
|
EXPECT_TRUE(sig != nullptr);
|
|
EXPECT_EQ(1, sig->parameter_count());
|
|
EXPECT_EQ(0, sig->return_count());
|
|
EXPECT_EQ(param_type.type, sig->GetParam(0));
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Ok_t_t) {
|
|
for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
|
|
LocalTypePair ret_type = kLocalTypes[i];
|
|
for (size_t j = 0; j < arraysize(kLocalTypes); j++) {
|
|
LocalTypePair param_type = kLocalTypes[j];
|
|
const byte data[] = {1, // param count
|
|
ret_type.code, // ret
|
|
param_type.code}; // param
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
|
|
|
|
EXPECT_TRUE(sig != nullptr);
|
|
EXPECT_EQ(1, sig->parameter_count());
|
|
EXPECT_EQ(1, sig->return_count());
|
|
EXPECT_EQ(param_type.type, sig->GetParam(0));
|
|
EXPECT_EQ(ret_type.type, sig->GetReturn());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Ok_i_tt) {
|
|
for (size_t i = 0; i < arraysize(kLocalTypes); i++) {
|
|
LocalTypePair p0_type = kLocalTypes[i];
|
|
for (size_t j = 0; j < arraysize(kLocalTypes); j++) {
|
|
LocalTypePair p1_type = kLocalTypes[j];
|
|
const byte data[] = {2, // param count
|
|
kLocalI32, // ret
|
|
p0_type.code, // p0
|
|
p1_type.code}; // p1
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
|
|
|
|
EXPECT_TRUE(sig != nullptr);
|
|
EXPECT_EQ(2, sig->parameter_count());
|
|
EXPECT_EQ(1, sig->return_count());
|
|
EXPECT_EQ(p0_type.type, sig->GetParam(0));
|
|
EXPECT_EQ(p1_type.type, sig->GetParam(1));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Fail_off_end) {
|
|
byte data[256];
|
|
for (int p = 0; p <= 255; p = p + 1 + p * 3) {
|
|
for (int i = 0; i <= p; i++) data[i] = kLocalI32;
|
|
data[0] = static_cast<byte>(p);
|
|
|
|
for (int i = 0; i < p + 1; i++) {
|
|
// Should fall off the end for all signatures.
|
|
FunctionSig* sig = DecodeWasmSignatureForTesting(zone(), data, data + i);
|
|
EXPECT_EQ(nullptr, sig);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Fail_invalid_type) {
|
|
byte kInvalidType = 76;
|
|
for (int i = 1; i < 3; i++) {
|
|
byte data[] = {2, kLocalI32, kLocalI32, kLocalI32};
|
|
data[i] = kInvalidType;
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
|
|
EXPECT_EQ(nullptr, sig);
|
|
}
|
|
}
|
|
|
|
|
|
TEST_F(WasmSignatureDecodeTest, Fail_invalid_param_type) {
|
|
static const int kParamCount = 3;
|
|
for (int i = 0; i < kParamCount; i++) {
|
|
byte data[] = {kParamCount, kLocalI32, kLocalI32, kLocalI32, kLocalI32};
|
|
data[i + 2] = kLocalVoid;
|
|
FunctionSig* sig =
|
|
DecodeWasmSignatureForTesting(zone(), data, data + arraysize(data));
|
|
EXPECT_EQ(nullptr, sig);
|
|
}
|
|
}
|
|
|
|
|
|
class WasmFunctionVerifyTest : public TestWithZone {};
|
|
|
|
|
|
TEST_F(WasmFunctionVerifyTest, Ok_v_v_empty) {
|
|
byte data[] = {
|
|
0, kLocalVoid, // signature
|
|
3, 0, // local int32 count
|
|
4, 0, // local int64 count
|
|
5, 0, // local float32 count
|
|
6, 0, // local float64 count
|
|
kExprNop // body
|
|
};
|
|
|
|
FunctionResult result = DecodeWasmFunction(nullptr, zone(), nullptr, data,
|
|
data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
|
|
if (result.val && result.ok()) {
|
|
WasmFunction* function = result.val;
|
|
EXPECT_EQ(0, function->sig->parameter_count());
|
|
EXPECT_EQ(0, function->sig->return_count());
|
|
EXPECT_EQ(0, function->name_offset);
|
|
EXPECT_EQ(arraysize(data) - 1, function->code_start_offset);
|
|
EXPECT_EQ(arraysize(data), function->code_end_offset);
|
|
EXPECT_EQ(3, function->local_int32_count);
|
|
EXPECT_EQ(4, function->local_int64_count);
|
|
EXPECT_EQ(5, function->local_float32_count);
|
|
EXPECT_EQ(6, function->local_float64_count);
|
|
EXPECT_FALSE(function->external);
|
|
EXPECT_FALSE(function->exported);
|
|
}
|
|
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionNoLen) {
|
|
const byte data[] = {
|
|
kDeclWLL, // section without length.
|
|
};
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionEmpty) {
|
|
const byte data[] = {
|
|
kDeclWLL, 0, // empty section
|
|
};
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionOne) {
|
|
const byte data[] = {
|
|
kDeclWLL,
|
|
1, // LEB128 1
|
|
0, // one byte section
|
|
};
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionTen) {
|
|
const byte data[] = {
|
|
kDeclWLL,
|
|
10, // LEB128 10
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 10 byte section
|
|
};
|
|
ModuleResult result = DecodeModule(data, data + arraysize(data));
|
|
EXPECT_TRUE(result.ok());
|
|
if (result.val) delete result.val;
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionOverflow) {
|
|
const byte data[] = {
|
|
kDeclWLL,
|
|
11, // LEB128 11
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 10 byte section
|
|
};
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionUnderflow) {
|
|
const byte data[] = {
|
|
kDeclWLL,
|
|
0xff, 0xff, 0xff, 0xff, 0x0f, // LEB128 0xffffffff
|
|
1, 2, 3, 4, // 4 byte section
|
|
};
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
|
|
TEST_F(WasmModuleVerifyTest, WLLSectionLoop) {
|
|
// Would infinite loop decoding if wrapping and allowed.
|
|
const byte data[] = {
|
|
kDeclWLL,
|
|
0xfa, 0xff, 0xff, 0xff, 0x0f, // LEB128 0xfffffffa
|
|
1, 2, 3, 4, // 4 byte section
|
|
};
|
|
EXPECT_FAILURE(data);
|
|
}
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|