Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,14 @@
# Metadata information for this directory.
#
# For more information on DIR_METADATA files, see:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
#
# For the schema of this file, see Metadata message:
# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
monorail {
component: "Blink>JavaScript>WebAssembly"
}
buganizer_public: {
component_id: 1456332
}

1
deps/v8/test/unittests/wasm/OWNERS vendored Normal file
View File

@ -0,0 +1 @@
file:../../../src/wasm/OWNERS

View File

@ -0,0 +1,673 @@
// Copyright 2016 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/base/overflowing-math.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/decoder.h"
#include "test/common/wasm/wasm-macro-gen.h"
namespace v8 {
namespace internal {
namespace wasm {
class DecoderTest : public TestWithZone {
public:
DecoderTest() : decoder(nullptr, nullptr) {}
Decoder decoder;
};
#define CHECK_UINT32V_INLINE(expected, expected_length, ...) \
do { \
const uint8_t data[] = {__VA_ARGS__}; \
decoder.Reset(data, data + sizeof(data)); \
auto [value, length] = \
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start()); \
EXPECT_EQ(static_cast<uint32_t>(expected), value); \
EXPECT_EQ(static_cast<unsigned>(expected_length), length); \
EXPECT_EQ(data, decoder.pc()); \
EXPECT_TRUE(decoder.ok()); \
EXPECT_EQ(static_cast<uint32_t>(expected), decoder.consume_u32v()); \
EXPECT_EQ(data + expected_length, decoder.pc()); \
} while (false)
#define CHECK_INT32V_INLINE(expected, expected_length, ...) \
do { \
const uint8_t data[] = {__VA_ARGS__}; \
decoder.Reset(data, data + sizeof(data)); \
auto [value, length] = \
decoder.read_i32v<Decoder::FullValidationTag>(decoder.start()); \
EXPECT_EQ(expected, value); \
EXPECT_EQ(static_cast<unsigned>(expected_length), length); \
EXPECT_EQ(data, decoder.pc()); \
EXPECT_TRUE(decoder.ok()); \
EXPECT_EQ(expected, decoder.consume_i32v()); \
EXPECT_EQ(data + expected_length, decoder.pc()); \
} while (false)
#define CHECK_UINT64V_INLINE(expected, expected_length, ...) \
do { \
const uint8_t data[] = {__VA_ARGS__}; \
decoder.Reset(data, data + sizeof(data)); \
auto [value, length] = \
decoder.read_u64v<Decoder::FullValidationTag>(decoder.start()); \
EXPECT_EQ(static_cast<uint64_t>(expected), value); \
EXPECT_EQ(static_cast<unsigned>(expected_length), length); \
} while (false)
#define CHECK_INT64V_INLINE(expected, expected_length, ...) \
do { \
const uint8_t data[] = {__VA_ARGS__}; \
decoder.Reset(data, data + sizeof(data)); \
auto [value, length] = \
decoder.read_i64v<Decoder::FullValidationTag>(decoder.start()); \
EXPECT_EQ(expected, value); \
EXPECT_EQ(static_cast<unsigned>(expected_length), length); \
} while (false)
TEST_F(DecoderTest, ReadU32v_OneByte) {
CHECK_UINT32V_INLINE(0, 1, 0);
CHECK_UINT32V_INLINE(5, 1, 5);
CHECK_UINT32V_INLINE(7, 1, 7);
CHECK_UINT32V_INLINE(9, 1, 9);
CHECK_UINT32V_INLINE(37, 1, 37);
CHECK_UINT32V_INLINE(69, 1, 69);
CHECK_UINT32V_INLINE(110, 1, 110);
CHECK_UINT32V_INLINE(125, 1, 125);
CHECK_UINT32V_INLINE(126, 1, 126);
CHECK_UINT32V_INLINE(127, 1, 127);
}
TEST_F(DecoderTest, ReadU32v_TwoByte) {
CHECK_UINT32V_INLINE(0, 1, 0, 0);
CHECK_UINT32V_INLINE(10, 1, 10, 0);
CHECK_UINT32V_INLINE(27, 1, 27, 0);
CHECK_UINT32V_INLINE(100, 1, 100, 0);
CHECK_UINT32V_INLINE(444, 2, U32V_2(444));
CHECK_UINT32V_INLINE(544, 2, U32V_2(544));
CHECK_UINT32V_INLINE(1311, 2, U32V_2(1311));
CHECK_UINT32V_INLINE(2333, 2, U32V_2(2333));
for (uint32_t i = 0; i < 1 << 14; i = i * 13 + 1) {
CHECK_UINT32V_INLINE(i, 2, U32V_2(i));
}
const uint32_t max = (1 << 14) - 1;
CHECK_UINT32V_INLINE(max, 2, U32V_2(max));
}
TEST_F(DecoderTest, ReadU32v_ThreeByte) {
CHECK_UINT32V_INLINE(0, 1, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(10, 1, 10, 0, 0, 0);
CHECK_UINT32V_INLINE(27, 1, 27, 0, 0, 0);
CHECK_UINT32V_INLINE(100, 1, 100, 0, 0, 0);
CHECK_UINT32V_INLINE(11, 3, U32V_3(11));
CHECK_UINT32V_INLINE(101, 3, U32V_3(101));
CHECK_UINT32V_INLINE(446, 3, U32V_3(446));
CHECK_UINT32V_INLINE(546, 3, U32V_3(546));
CHECK_UINT32V_INLINE(1319, 3, U32V_3(1319));
CHECK_UINT32V_INLINE(2338, 3, U32V_3(2338));
CHECK_UINT32V_INLINE(8191, 3, U32V_3(8191));
CHECK_UINT32V_INLINE(9999, 3, U32V_3(9999));
CHECK_UINT32V_INLINE(14444, 3, U32V_3(14444));
CHECK_UINT32V_INLINE(314444, 3, U32V_3(314444));
CHECK_UINT32V_INLINE(614444, 3, U32V_3(614444));
const uint32_t max = (1 << 21) - 1;
for (uint32_t i = 0; i <= max; i = i * 13 + 3) {
CHECK_UINT32V_INLINE(i, 3, U32V_3(i), 0);
}
CHECK_UINT32V_INLINE(max, 3, U32V_3(max));
}
TEST_F(DecoderTest, ReadU32v_FourByte) {
CHECK_UINT32V_INLINE(0, 1, 0, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(10, 1, 10, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(27, 1, 27, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(100, 1, 100, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(13, 4, U32V_4(13));
CHECK_UINT32V_INLINE(107, 4, U32V_4(107));
CHECK_UINT32V_INLINE(449, 4, U32V_4(449));
CHECK_UINT32V_INLINE(541, 4, U32V_4(541));
CHECK_UINT32V_INLINE(1317, 4, U32V_4(1317));
CHECK_UINT32V_INLINE(2334, 4, U32V_4(2334));
CHECK_UINT32V_INLINE(8191, 4, U32V_4(8191));
CHECK_UINT32V_INLINE(9994, 4, U32V_4(9994));
CHECK_UINT32V_INLINE(14442, 4, U32V_4(14442));
CHECK_UINT32V_INLINE(314442, 4, U32V_4(314442));
CHECK_UINT32V_INLINE(614442, 4, U32V_4(614442));
CHECK_UINT32V_INLINE(1614442, 4, U32V_4(1614442));
CHECK_UINT32V_INLINE(5614442, 4, U32V_4(5614442));
CHECK_UINT32V_INLINE(19614442, 4, U32V_4(19614442));
const uint32_t max = (1 << 28) - 1;
for (uint32_t i = 0; i <= max; i = i * 13 + 5) {
CHECK_UINT32V_INLINE(i, 4, U32V_4(i), 0);
}
CHECK_UINT32V_INLINE(max, 4, U32V_4(max));
}
TEST_F(DecoderTest, ReadU32v_FiveByte) {
CHECK_UINT32V_INLINE(0, 1, 0, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(10, 1, 10, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(27, 1, 27, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(100, 1, 100, 0, 0, 0, 0);
CHECK_UINT32V_INLINE(13, 5, U32V_5(13));
CHECK_UINT32V_INLINE(107, 5, U32V_5(107));
CHECK_UINT32V_INLINE(449, 5, U32V_5(449));
CHECK_UINT32V_INLINE(541, 5, U32V_5(541));
CHECK_UINT32V_INLINE(1317, 5, U32V_5(1317));
CHECK_UINT32V_INLINE(2334, 5, U32V_5(2334));
CHECK_UINT32V_INLINE(8191, 5, U32V_5(8191));
CHECK_UINT32V_INLINE(9994, 5, U32V_5(9994));
CHECK_UINT32V_INLINE(24442, 5, U32V_5(24442));
CHECK_UINT32V_INLINE(414442, 5, U32V_5(414442));
CHECK_UINT32V_INLINE(714442, 5, U32V_5(714442));
CHECK_UINT32V_INLINE(1614442, 5, U32V_5(1614442));
CHECK_UINT32V_INLINE(6614442, 5, U32V_5(6614442));
CHECK_UINT32V_INLINE(89614442, 5, U32V_5(89614442));
CHECK_UINT32V_INLINE(2219614442u, 5, U32V_5(2219614442u));
CHECK_UINT32V_INLINE(3219614442u, 5, U32V_5(3219614442u));
CHECK_UINT32V_INLINE(4019614442u, 5, U32V_5(4019614442u));
const uint32_t max = 0xFFFFFFFFu;
for (uint32_t i = 1; i < 32; i++) {
uint32_t val = 0x983489AAu << i;
CHECK_UINT32V_INLINE(val, 5, U32V_5(val), 0);
}
CHECK_UINT32V_INLINE(max, 5, U32V_5(max));
}
TEST_F(DecoderTest, ReadU32v_various) {
for (int i = 0; i < 10; i++) {
uint32_t x = 0xCCCCCCCCu * i;
for (int width = 0; width < 32; width++) {
uint32_t val = x >> width;
CHECK_UINT32V_INLINE(val & MASK_7, 1, U32V_1(val));
CHECK_UINT32V_INLINE(val & MASK_14, 2, U32V_2(val));
CHECK_UINT32V_INLINE(val & MASK_21, 3, U32V_3(val));
CHECK_UINT32V_INLINE(val & MASK_28, 4, U32V_4(val));
CHECK_UINT32V_INLINE(val, 5, U32V_5(val));
}
}
}
TEST_F(DecoderTest, ReadI32v_OneByte) {
CHECK_INT32V_INLINE(0, 1, 0);
CHECK_INT32V_INLINE(4, 1, 4);
CHECK_INT32V_INLINE(6, 1, 6);
CHECK_INT32V_INLINE(9, 1, 9);
CHECK_INT32V_INLINE(33, 1, 33);
CHECK_INT32V_INLINE(61, 1, 61);
CHECK_INT32V_INLINE(63, 1, 63);
CHECK_INT32V_INLINE(-1, 1, 127);
CHECK_INT32V_INLINE(-2, 1, 126);
CHECK_INT32V_INLINE(-11, 1, 117);
CHECK_INT32V_INLINE(-62, 1, 66);
CHECK_INT32V_INLINE(-63, 1, 65);
CHECK_INT32V_INLINE(-64, 1, 64);
}
TEST_F(DecoderTest, ReadI32v_TwoByte) {
CHECK_INT32V_INLINE(0, 2, U32V_2(0));
CHECK_INT32V_INLINE(9, 2, U32V_2(9));
CHECK_INT32V_INLINE(61, 2, U32V_2(61));
CHECK_INT32V_INLINE(63, 2, U32V_2(63));
CHECK_INT32V_INLINE(-1, 2, U32V_2(-1));
CHECK_INT32V_INLINE(-2, 2, U32V_2(-2));
CHECK_INT32V_INLINE(-63, 2, U32V_2(-63));
CHECK_INT32V_INLINE(-64, 2, U32V_2(-64));
CHECK_INT32V_INLINE(-200, 2, U32V_2(-200));
CHECK_INT32V_INLINE(-1002, 2, U32V_2(-1002));
CHECK_INT32V_INLINE(-2004, 2, U32V_2(-2004));
CHECK_INT32V_INLINE(-4077, 2, U32V_2(-4077));
CHECK_INT32V_INLINE(207, 2, U32V_2(207));
CHECK_INT32V_INLINE(1009, 2, U32V_2(1009));
CHECK_INT32V_INLINE(2003, 2, U32V_2(2003));
CHECK_INT32V_INLINE(4072, 2, U32V_2(4072));
const int32_t min = 0 - (1 << 13);
for (int i = min; i < min + 10; i++) {
CHECK_INT32V_INLINE(i, 2, U32V_2(i));
}
const int32_t max = (1 << 13) - 1;
for (int i = max; i > max - 10; i--) {
CHECK_INT32V_INLINE(i, 2, U32V_2(i));
}
}
TEST_F(DecoderTest, ReadI32v_ThreeByte) {
CHECK_INT32V_INLINE(0, 3, U32V_3(0));
CHECK_INT32V_INLINE(9, 3, U32V_3(9));
CHECK_INT32V_INLINE(61, 3, U32V_3(61));
CHECK_INT32V_INLINE(63, 3, U32V_3(63));
CHECK_INT32V_INLINE(-1, 3, U32V_3(-1));
CHECK_INT32V_INLINE(-2, 3, U32V_3(-2));
CHECK_INT32V_INLINE(-63, 3, U32V_3(-63));
CHECK_INT32V_INLINE(-64, 3, U32V_3(-64));
CHECK_INT32V_INLINE(-207, 3, U32V_3(-207));
CHECK_INT32V_INLINE(-1012, 3, U32V_3(-1012));
CHECK_INT32V_INLINE(-4067, 3, U32V_3(-4067));
CHECK_INT32V_INLINE(-14067, 3, U32V_3(-14067));
CHECK_INT32V_INLINE(-234061, 3, U32V_3(-234061));
CHECK_INT32V_INLINE(237, 3, U32V_3(237));
CHECK_INT32V_INLINE(1309, 3, U32V_3(1309));
CHECK_INT32V_INLINE(4372, 3, U32V_3(4372));
CHECK_INT32V_INLINE(64372, 3, U32V_3(64372));
CHECK_INT32V_INLINE(374372, 3, U32V_3(374372));
const int32_t min = 0 - (1 << 20);
for (int i = min; i < min + 10; i++) {
CHECK_INT32V_INLINE(i, 3, U32V_3(i));
}
const int32_t max = (1 << 20) - 1;
for (int i = max; i > max - 10; i--) {
CHECK_INT32V_INLINE(i, 3, U32V_3(i));
}
}
TEST_F(DecoderTest, ReadI32v_FourByte) {
CHECK_INT32V_INLINE(0, 4, U32V_4(0));
CHECK_INT32V_INLINE(9, 4, U32V_4(9));
CHECK_INT32V_INLINE(61, 4, U32V_4(61));
CHECK_INT32V_INLINE(63, 4, U32V_4(63));
CHECK_INT32V_INLINE(-1, 4, U32V_4(-1));
CHECK_INT32V_INLINE(-2, 4, U32V_4(-2));
CHECK_INT32V_INLINE(-63, 4, U32V_4(-63));
CHECK_INT32V_INLINE(-64, 4, U32V_4(-64));
CHECK_INT32V_INLINE(-267, 4, U32V_4(-267));
CHECK_INT32V_INLINE(-1612, 4, U32V_4(-1612));
CHECK_INT32V_INLINE(-4667, 4, U32V_4(-4667));
CHECK_INT32V_INLINE(-16067, 4, U32V_4(-16067));
CHECK_INT32V_INLINE(-264061, 4, U32V_4(-264061));
CHECK_INT32V_INLINE(-1264061, 4, U32V_4(-1264061));
CHECK_INT32V_INLINE(-6264061, 4, U32V_4(-6264061));
CHECK_INT32V_INLINE(-8264061, 4, U32V_4(-8264061));
CHECK_INT32V_INLINE(277, 4, U32V_4(277));
CHECK_INT32V_INLINE(1709, 4, U32V_4(1709));
CHECK_INT32V_INLINE(4772, 4, U32V_4(4772));
CHECK_INT32V_INLINE(67372, 4, U32V_4(67372));
CHECK_INT32V_INLINE(374372, 4, U32V_4(374372));
CHECK_INT32V_INLINE(2374372, 4, U32V_4(2374372));
CHECK_INT32V_INLINE(7374372, 4, U32V_4(7374372));
CHECK_INT32V_INLINE(9374372, 4, U32V_4(9374372));
const int32_t min = 0 - (1 << 27);
for (int i = min; i < min + 10; i++) {
CHECK_INT32V_INLINE(i, 4, U32V_4(i));
}
const int32_t max = (1 << 27) - 1;
for (int i = max; i > max - 10; i--) {
CHECK_INT32V_INLINE(i, 4, U32V_4(i));
}
}
TEST_F(DecoderTest, ReadI32v_FiveByte) {
CHECK_INT32V_INLINE(0, 5, U32V_5(0));
CHECK_INT32V_INLINE(16, 5, U32V_5(16));
CHECK_INT32V_INLINE(94, 5, U32V_5(94));
CHECK_INT32V_INLINE(127, 5, U32V_5(127));
CHECK_INT32V_INLINE(-1, 5, U32V_5(-1));
CHECK_INT32V_INLINE(-2, 5, U32V_5(-2));
CHECK_INT32V_INLINE(-63, 5, U32V_5(-63));
CHECK_INT32V_INLINE(-64, 5, U32V_5(-64));
CHECK_INT32V_INLINE(-257, 5, U32V_5(-257));
CHECK_INT32V_INLINE(-1512, 5, U32V_5(-1512));
CHECK_INT32V_INLINE(-4567, 5, U32V_5(-4567));
CHECK_INT32V_INLINE(-15067, 5, U32V_5(-15067));
CHECK_INT32V_INLINE(-254061, 5, U32V_5(-254061));
CHECK_INT32V_INLINE(-1364061, 5, U32V_5(-1364061));
CHECK_INT32V_INLINE(-6364061, 5, U32V_5(-6364061));
CHECK_INT32V_INLINE(-8364061, 5, U32V_5(-8364061));
CHECK_INT32V_INLINE(-28364061, 5, U32V_5(-28364061));
CHECK_INT32V_INLINE(-228364061, 5, U32V_5(-228364061));
CHECK_INT32V_INLINE(227, 5, U32V_5(227));
CHECK_INT32V_INLINE(1209, 5, U32V_5(1209));
CHECK_INT32V_INLINE(4272, 5, U32V_5(4272));
CHECK_INT32V_INLINE(62372, 5, U32V_5(62372));
CHECK_INT32V_INLINE(324372, 5, U32V_5(324372));
CHECK_INT32V_INLINE(2274372, 5, U32V_5(2274372));
CHECK_INT32V_INLINE(7274372, 5, U32V_5(7274372));
CHECK_INT32V_INLINE(9274372, 5, U32V_5(9274372));
CHECK_INT32V_INLINE(42374372, 5, U32V_5(42374372));
CHECK_INT32V_INLINE(429374372, 5, U32V_5(429374372));
const int32_t min = kMinInt;
for (int i = min; i < min + 10; i++) {
CHECK_INT32V_INLINE(i, 5, U32V_5(i));
}
const int32_t max = kMaxInt;
for (int i = max; i > max - 10; i--) {
CHECK_INT32V_INLINE(i, 5, U32V_5(i));
}
}
TEST_F(DecoderTest, ReadU32v_off_end1) {
static const uint8_t data[] = {U32V_1(11)};
decoder.Reset(data, data);
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
TEST_F(DecoderTest, ReadU32v_off_end2) {
static const uint8_t data[] = {U32V_2(1111)};
for (size_t i = 0; i < sizeof(data); i++) {
decoder.Reset(data, data + i);
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
}
TEST_F(DecoderTest, ReadU32v_off_end3) {
static const uint8_t data[] = {U32V_3(111111)};
for (size_t i = 0; i < sizeof(data); i++) {
decoder.Reset(data, data + i);
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
}
TEST_F(DecoderTest, ReadU32v_off_end4) {
static const uint8_t data[] = {U32V_4(11111111)};
for (size_t i = 0; i < sizeof(data); i++) {
decoder.Reset(data, data + i);
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
}
TEST_F(DecoderTest, ReadU32v_off_end5) {
static const uint8_t data[] = {U32V_5(111111111)};
for (size_t i = 0; i < sizeof(data); i++) {
decoder.Reset(data, data + i);
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
}
TEST_F(DecoderTest, ReadU32v_extra_bits) {
uint8_t data[] = {0x80, 0x80, 0x80, 0x80, 0x00};
for (int i = 1; i < 16; i++) {
data[4] = static_cast<uint8_t>(i << 4);
decoder.Reset(data, data + sizeof(data));
decoder.read_u32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
}
TEST_F(DecoderTest, ReadI32v_extra_bits_negative) {
// OK for negative signed values to have extra ones.
uint8_t data[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x7F};
decoder.Reset(data, data + sizeof(data));
auto [result, length] =
decoder.read_i32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_EQ(5u, length);
EXPECT_TRUE(decoder.ok());
}
TEST_F(DecoderTest, ReadI32v_extra_bits_positive) {
// Not OK for positive signed values to have extra ones.
uint8_t data[] = {0x80, 0x80, 0x80, 0x80, 0x77};
decoder.Reset(data, data + sizeof(data));
decoder.read_i32v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
TEST_F(DecoderTest, ReadU32v_Bits) {
// A more exhaustive test.
const int kMaxSize = 5;
const uint32_t kVals[] = {
0xAABBCCDD, 0x11223344, 0x33445566, 0xFFEEDDCC, 0xF0F0F0F0, 0x0F0F0F0F,
0xEEEEEEEE, 0xAAAAAAAA, 0x12345678, 0x9ABCDEF0, 0x80309488, 0x729ED997,
0xC4A0CF81, 0x16C6EB85, 0x4206DB8E, 0xF3B089D5, 0xAA2E223E, 0xF99E29C8,
0x4A4357D8, 0x1890B1C1, 0x8D80A085, 0xACB6AE4C, 0x1B827E10, 0xEB5C7BD9,
0xBB1BC146, 0xDF57A33l};
uint8_t data[kMaxSize];
// foreach value in above array
for (size_t v = 0; v < arraysize(kVals); v++) {
// foreach length 1...32
for (int i = 1; i <= 32; i++) {
uint32_t val = kVals[v];
if (i < 32)
val &= base::SubWithWraparound(base::ShlWithWraparound(1, i), 1);
unsigned length = 1 + i / 7;
for (unsigned j = 0; j < kMaxSize; j++) {
data[j] = static_cast<uint8_t>((val >> (7 * j)) & MASK_7);
}
for (unsigned j = 0; j < length - 1; j++) {
data[j] |= 0x80;
}
// foreach buffer size 0...5
for (unsigned limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
auto [result, rlen] =
decoder.read_u32v<Decoder::FullValidationTag>(data);
if (limit < length) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(length, rlen);
}
}
}
}
}
TEST_F(DecoderTest, ReadU64v_OneByte) {
CHECK_UINT64V_INLINE(0, 1, 0);
CHECK_UINT64V_INLINE(6, 1, 6);
CHECK_UINT64V_INLINE(8, 1, 8);
CHECK_UINT64V_INLINE(12, 1, 12);
CHECK_UINT64V_INLINE(33, 1, 33);
CHECK_UINT64V_INLINE(59, 1, 59);
CHECK_UINT64V_INLINE(110, 1, 110);
CHECK_UINT64V_INLINE(125, 1, 125);
CHECK_UINT64V_INLINE(126, 1, 126);
CHECK_UINT64V_INLINE(127, 1, 127);
}
TEST_F(DecoderTest, ReadI64v_OneByte) {
CHECK_INT64V_INLINE(0, 1, 0);
CHECK_INT64V_INLINE(4, 1, 4);
CHECK_INT64V_INLINE(6, 1, 6);
CHECK_INT64V_INLINE(9, 1, 9);
CHECK_INT64V_INLINE(33, 1, 33);
CHECK_INT64V_INLINE(61, 1, 61);
CHECK_INT64V_INLINE(63, 1, 63);
CHECK_INT64V_INLINE(-1, 1, 127);
CHECK_INT64V_INLINE(-2, 1, 126);
CHECK_INT64V_INLINE(-11, 1, 117);
CHECK_INT64V_INLINE(-62, 1, 66);
CHECK_INT64V_INLINE(-63, 1, 65);
CHECK_INT64V_INLINE(-64, 1, 64);
}
TEST_F(DecoderTest, ReadU64v_PowerOf2) {
const int kMaxSize = 10;
uint8_t data[kMaxSize];
for (unsigned i = 0; i < 64; i++) {
const uint64_t val = 1ull << i;
unsigned index = i / 7;
data[index] = 1 << (i % 7);
memset(data, 0x80, index);
for (unsigned limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
auto [result, length] =
decoder.read_u64v<Decoder::FullValidationTag>(data);
if (limit <= index) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(index + 1, length);
}
}
}
}
TEST_F(DecoderTest, ReadU64v_Bits) {
const int kMaxSize = 10;
const uint64_t kVals[] = {
0xAABBCCDD11223344ull, 0x33445566FFEEDDCCull, 0xF0F0F0F0F0F0F0F0ull,
0x0F0F0F0F0F0F0F0Full, 0xEEEEEEEEEEEEEEEEull, 0xAAAAAAAAAAAAAAAAull,
0x123456789ABCDEF0ull, 0x80309488729ED997ull, 0xC4A0CF8116C6EB85ull,
0x4206DB8EF3B089D5ull, 0xAA2E223EF99E29C8ull, 0x4A4357D81890B1C1ull,
0x8D80A085ACB6AE4Cull, 0x1B827E10EB5C7BD9ull, 0xBB1BC146DF57A338ull};
uint8_t data[kMaxSize];
// foreach value in above array
for (size_t v = 0; v < arraysize(kVals); v++) {
// foreach length 1...64
for (int i = 1; i <= 64; i++) {
uint64_t val = kVals[v];
if (i < 64) val &= ((1ull << i) - 1);
unsigned length = 1 + i / 7;
for (unsigned j = 0; j < kMaxSize; j++) {
data[j] = static_cast<uint8_t>((val >> (7 * j)) & MASK_7);
}
for (unsigned j = 0; j < length - 1; j++) {
data[j] |= 0x80;
}
// foreach buffer size 0...10
for (unsigned limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
auto [result, rlen] =
decoder.read_u64v<Decoder::FullValidationTag>(data);
if (limit < length) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(length, rlen);
}
}
}
}
}
TEST_F(DecoderTest, ReadI64v_Bits) {
const int kMaxSize = 10;
// Exhaustive signedness test.
const uint64_t kVals[] = {
0xAABBCCDD11223344ull, 0x33445566FFEEDDCCull, 0xF0F0F0F0F0F0F0F0ull,
0x0F0F0F0F0F0F0F0Full, 0xEEEEEEEEEEEEEEEEull, 0xAAAAAAAAAAAAAAAAull,
0x123456789ABCDEF0ull, 0x80309488729ED997ull, 0xC4A0CF8116C6EB85ull,
0x4206DB8EF3B089D5ull, 0xAA2E223EF99E29C8ull, 0x4A4357D81890B1C1ull,
0x8D80A085ACB6AE4Cull, 0x1B827E10EB5C7BD9ull, 0xBB1BC146DF57A338ull};
uint8_t data[kMaxSize];
// foreach value in above array
for (size_t v = 0; v < arraysize(kVals); v++) {
// foreach length 1...64
for (int i = 1; i <= 64; i++) {
const int64_t val =
base::bit_cast<int64_t>(kVals[v] << (64 - i)) >> (64 - i);
unsigned length = 1 + i / 7;
for (unsigned j = 0; j < kMaxSize; j++) {
data[j] = static_cast<uint8_t>((val >> (7 * j)) & MASK_7);
}
for (unsigned j = 0; j < length - 1; j++) {
data[j] |= 0x80;
}
// foreach buffer size 0...10
for (unsigned limit = 0; limit <= kMaxSize; limit++) {
decoder.Reset(data, data + limit);
auto [result, rlen] =
decoder.read_i64v<Decoder::FullValidationTag>(data);
if (limit < length) {
EXPECT_FALSE(decoder.ok());
} else {
EXPECT_TRUE(decoder.ok());
EXPECT_EQ(val, result);
EXPECT_EQ(length, rlen);
}
}
}
}
}
TEST_F(DecoderTest, ReadU64v_extra_bits) {
uint8_t data[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00};
for (int i = 1; i < 128; i++) {
data[9] = static_cast<uint8_t>(i << 1);
decoder.Reset(data, data + sizeof(data));
decoder.read_u64v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
}
TEST_F(DecoderTest, ReadI64v_extra_bits_negative) {
// OK for negative signed values to have extra ones.
uint8_t data[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F};
decoder.Reset(data, data + sizeof(data));
auto [result, length] =
decoder.read_i64v<Decoder::FullValidationTag>(decoder.start());
EXPECT_EQ(10u, length);
EXPECT_TRUE(decoder.ok());
}
TEST_F(DecoderTest, ReadI64v_extra_bits_positive) {
// Not OK for positive signed values to have extra ones.
uint8_t data[] = {0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x77};
decoder.Reset(data, data + sizeof(data));
decoder.read_i64v<Decoder::FullValidationTag>(decoder.start());
EXPECT_FALSE(decoder.ok());
}
TEST_F(DecoderTest, FailOnNullData) {
decoder.Reset(nullptr, nullptr);
decoder.checkAvailable(1);
EXPECT_FALSE(decoder.ok());
EXPECT_FALSE(decoder.toResult(nullptr).ok());
}
#undef CHECK_UINT32V_INLINE
#undef CHECK_INT32V_INLINE
#undef CHECK_UINT64V_INLINE
#undef CHECK_INT64V_INLINE
} // namespace wasm
} // namespace internal
} // namespace v8

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,193 @@
// Copyright 2016 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/objects-inl.h"
#include "src/wasm/decoder.h"
#include "src/wasm/leb-helper.h"
namespace v8 {
namespace internal {
namespace wasm {
class LEBHelperTest : public TestWithZone {};
TEST_F(LEBHelperTest, sizeof_u32v) {
EXPECT_EQ(1u, LEBHelper::sizeof_u32v(0));
EXPECT_EQ(1u, LEBHelper::sizeof_u32v(1));
EXPECT_EQ(1u, LEBHelper::sizeof_u32v(3));
for (uint32_t i = 4; i < 128; i++) {
EXPECT_EQ(1u, LEBHelper::sizeof_u32v(i));
}
for (uint32_t i = (1u << 7); i < (1u << 9); i++) {
EXPECT_EQ(2u, LEBHelper::sizeof_u32v(i));
}
for (uint32_t i = (1u << 14); i < (1u << 16); i += 33) {
EXPECT_EQ(3u, LEBHelper::sizeof_u32v(i));
}
for (uint32_t i = (1u << 21); i < (1u << 24); i += 33999) {
EXPECT_EQ(4u, LEBHelper::sizeof_u32v(i));
}
for (uint32_t i = (1u << 28); i < (1u << 31); i += 33997779u) {
EXPECT_EQ(5u, LEBHelper::sizeof_u32v(i));
}
EXPECT_EQ(5u, LEBHelper::sizeof_u32v(0xFFFFFFFF));
}
TEST_F(LEBHelperTest, sizeof_i32v) {
EXPECT_EQ(1u, LEBHelper::sizeof_i32v(0));
EXPECT_EQ(1u, LEBHelper::sizeof_i32v(1));
EXPECT_EQ(1u, LEBHelper::sizeof_i32v(3));
for (int32_t i = 0; i < (1 << 6); i++) {
EXPECT_EQ(1u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = (1 << 6); i < (1 << 8); i++) {
EXPECT_EQ(2u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = (1 << 13); i < (1 << 15); i += 31) {
EXPECT_EQ(3u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = (1 << 20); i < (1 << 22); i += 31991) {
EXPECT_EQ(4u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = (1 << 27); i < (1 << 29); i += 3199893) {
EXPECT_EQ(5u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = -(1 << 6); i <= 0; i++) {
EXPECT_EQ(1u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = -(1 << 13); i < -(1 << 6); i++) {
EXPECT_EQ(2u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = -(1 << 20); i < -(1 << 18); i += 11) {
EXPECT_EQ(3u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = -(1 << 27); i < -(1 << 25); i += 11999) {
EXPECT_EQ(4u, LEBHelper::sizeof_i32v(i));
}
for (int32_t i = -(1 << 30); i < -(1 << 28); i += 1199999) {
EXPECT_EQ(5u, LEBHelper::sizeof_i32v(i));
}
}
#define DECLARE_ENCODE_DECODE_CHECKER(ctype, name) \
static void CheckEncodeDecode_##name(ctype val) { \
static const int kSize = 16; \
static uint8_t buffer[kSize]; \
uint8_t* ptr = buffer; \
LEBHelper::write_##name(&ptr, val); \
EXPECT_EQ(LEBHelper::sizeof_##name(val), \
static_cast<size_t>(ptr - buffer)); \
Decoder decoder(buffer, buffer + kSize); \
auto [result, length] = \
decoder.read_##name<Decoder::NoValidationTag>(buffer); \
EXPECT_EQ(val, result); \
EXPECT_EQ(LEBHelper::sizeof_##name(val), static_cast<size_t>(length)); \
}
DECLARE_ENCODE_DECODE_CHECKER(int32_t, i32v)
DECLARE_ENCODE_DECODE_CHECKER(uint32_t, u32v)
DECLARE_ENCODE_DECODE_CHECKER(int64_t, i64v)
DECLARE_ENCODE_DECODE_CHECKER(uint64_t, u64v)
#undef DECLARE_ENCODE_DECODE_CHECKER
TEST_F(LEBHelperTest, WriteAndDecode_u32v) {
CheckEncodeDecode_u32v(0);
CheckEncodeDecode_u32v(1);
CheckEncodeDecode_u32v(5);
CheckEncodeDecode_u32v(99);
CheckEncodeDecode_u32v(298);
CheckEncodeDecode_u32v(87348723);
CheckEncodeDecode_u32v(77777);
for (uint32_t val = 0x3A; val != 0; val = val << 1) {
CheckEncodeDecode_u32v(val);
}
}
TEST_F(LEBHelperTest, WriteAndDecode_i32v) {
CheckEncodeDecode_i32v(0);
CheckEncodeDecode_i32v(1);
CheckEncodeDecode_i32v(5);
CheckEncodeDecode_i32v(99);
CheckEncodeDecode_i32v(298);
CheckEncodeDecode_i32v(87348723);
CheckEncodeDecode_i32v(77777);
CheckEncodeDecode_i32v(-2);
CheckEncodeDecode_i32v(-4);
CheckEncodeDecode_i32v(-59);
CheckEncodeDecode_i32v(-288);
CheckEncodeDecode_i32v(-12608);
CheckEncodeDecode_i32v(-87328723);
CheckEncodeDecode_i32v(-77377);
for (uint32_t val = 0x3A; val != 0; val = val << 1) {
CheckEncodeDecode_i32v(base::bit_cast<int32_t>(val));
}
for (uint32_t val = 0xFFFFFF3B; val != 0; val = val << 1) {
CheckEncodeDecode_i32v(base::bit_cast<int32_t>(val));
}
}
TEST_F(LEBHelperTest, WriteAndDecode_u64v) {
CheckEncodeDecode_u64v(0);
CheckEncodeDecode_u64v(1);
CheckEncodeDecode_u64v(5);
CheckEncodeDecode_u64v(99);
CheckEncodeDecode_u64v(298);
CheckEncodeDecode_u64v(87348723);
CheckEncodeDecode_u64v(77777);
for (uint64_t val = 0x3A; val != 0; val = val << 1) {
CheckEncodeDecode_u64v(val);
}
}
TEST_F(LEBHelperTest, WriteAndDecode_i64v) {
CheckEncodeDecode_i64v(0);
CheckEncodeDecode_i64v(1);
CheckEncodeDecode_i64v(5);
CheckEncodeDecode_i64v(99);
CheckEncodeDecode_i64v(298);
CheckEncodeDecode_i64v(87348723);
CheckEncodeDecode_i64v(77777);
CheckEncodeDecode_i64v(-2);
CheckEncodeDecode_i64v(-4);
CheckEncodeDecode_i64v(-59);
CheckEncodeDecode_i64v(-288);
CheckEncodeDecode_i64v(-87648723);
CheckEncodeDecode_i64v(-77377);
for (uint64_t val = 0x3A; val != 0; val = val << 1) {
CheckEncodeDecode_i64v(base::bit_cast<int64_t>(val));
}
for (uint64_t val = 0xFFFFFFFFFFFFFF3B; val != 0; val = val << 1) {
CheckEncodeDecode_i64v(base::bit_cast<int64_t>(val));
}
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,77 @@
// Copyright 2021 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/baseline/liftoff-assembler-defs.h"
#if V8_TARGET_ARCH_IA32
#include "src/execution/ia32/frame-constants-ia32.h"
#elif V8_TARGET_ARCH_X64
#include "src/execution/x64/frame-constants-x64.h"
#elif V8_TARGET_ARCH_MIPS64
#include "src/execution/mips64/frame-constants-mips64.h"
#elif V8_TARGET_ARCH_LOONG64
#include "src/execution/loong64/frame-constants-loong64.h"
#elif V8_TARGET_ARCH_ARM
#include "src/execution/arm/frame-constants-arm.h"
#elif V8_TARGET_ARCH_ARM64
#include "src/execution/arm64/frame-constants-arm64.h"
#elif V8_TARGET_ARCH_S390X
#include "src/execution/s390/frame-constants-s390.h"
#elif V8_TARGET_ARCH_PPC64
#include "src/execution/ppc/frame-constants-ppc.h"
#elif V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64
#include "src/execution/riscv/frame-constants-riscv.h"
#endif
#include "src/wasm/baseline/liftoff-register.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
namespace wasm {
// The registers used by Liftoff and the registers spilled by the
// WasmDebugBreak builtin should match.
static_assert(kLiftoffAssemblerGpCacheRegs ==
WasmDebugBreakFrameConstants::kPushedGpRegs);
static_assert(kLiftoffAssemblerFpCacheRegs ==
WasmDebugBreakFrameConstants::kPushedFpRegs);
class WasmRegisterTest : public ::testing::Test {};
TEST_F(WasmRegisterTest, SpreadSetBitsToAdjacentFpRegs) {
LiftoffRegList input(
// GP reg selection criteria: an even and an odd register belonging to
// separate adjacent pairs, and contained in kLiftoffAssemblerGpCacheRegs
// for the given platform.
#if V8_TARGET_ARCH_S390X || V8_TARGET_ARCH_PPC64 || V8_TARGET_ARCH_LOONG64
LiftoffRegister::from_code(kGpReg, 4),
LiftoffRegister::from_code(kGpReg, 7),
#elif V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64
LiftoffRegister::from_code(kGpReg, 10),
LiftoffRegister::from_code(kGpReg, 13),
#else
LiftoffRegister::from_code(kGpReg, 1),
LiftoffRegister::from_code(kGpReg, 2),
#endif
LiftoffRegister::from_code(kFpReg, 1),
LiftoffRegister::from_code(kFpReg, 4));
// GP regs are left alone, FP regs are spread to adjacent pairs starting
// at an even index: 1 → (0, 1) and 4 → (4, 5).
#if V8_TARGET_ARCH_RISCV32 || V8_TARGET_ARCH_RISCV64 || V8_TARGET_ARCH_IA32
// RISCV and ia32 don't have code 0 in kLiftoffAssemblerFpCacheRegs
LiftoffRegList expected =
input | LiftoffRegList(LiftoffRegister::from_code(kFpReg, 5));
#else
LiftoffRegList expected =
input | LiftoffRegList(LiftoffRegister::from_code(kFpReg, 0),
LiftoffRegister::from_code(kFpReg, 5));
#endif
LiftoffRegList actual = input.SpreadSetBitsToAdjacentFpRegs();
EXPECT_EQ(expected, actual);
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,229 @@
// Copyright 2016 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/init/v8.h"
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
#include "src/utils/bit-vector.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-module.h"
#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"
namespace v8 {
namespace internal {
namespace wasm {
#define WASM_SET_ZERO(i) WASM_LOCAL_SET(i, WASM_ZERO)
class WasmLoopAssignmentAnalyzerTest : public TestWithZone {
public:
WasmLoopAssignmentAnalyzerTest() : num_locals(0) {}
TestSignatures sigs;
uint32_t num_locals;
BitVector* Analyze(const uint8_t* start, const uint8_t* end,
bool* loop_is_innermost = nullptr) {
return AnalyzeLoopAssignmentForTesting(zone(), num_locals, start, end,
loop_is_innermost);
}
};
TEST_F(WasmLoopAssignmentAnalyzerTest, Empty0) {
uint8_t code[] = {0};
BitVector* assigned = Analyze(code, code);
EXPECT_EQ(assigned, nullptr);
}
TEST_F(WasmLoopAssignmentAnalyzerTest, Empty1) {
uint8_t code[] = {kExprLoop, kVoidCode, 0};
for (int i = 0; i < 5; i++) {
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
EXPECT_FALSE(assigned->Contains(j));
}
num_locals++;
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, One) {
num_locals = 5;
for (int i = 0; i < 5; i++) {
uint8_t code[] = {WASM_LOOP(WASM_SET_ZERO(i))};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
EXPECT_EQ(j == i, assigned->Contains(j));
}
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, TeeOne) {
num_locals = 5;
for (int i = 0; i < 5; i++) {
uint8_t code[] = {WASM_LOOP(WASM_LOCAL_TEE(i, WASM_ZERO))};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
EXPECT_EQ(j == i, assigned->Contains(j));
}
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, OneBeyond) {
num_locals = 5;
for (int i = 0; i < 5; i++) {
uint8_t code[] = {WASM_LOOP(WASM_SET_ZERO(i)), WASM_SET_ZERO(1)};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
EXPECT_EQ(j == i, assigned->Contains(j));
}
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, Two) {
num_locals = 5;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
uint8_t code[] = {WASM_LOOP(WASM_SET_ZERO(i), WASM_SET_ZERO(j))};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int k = 0; k < assigned->length(); k++) {
bool expected = k == i || k == j;
EXPECT_EQ(expected, assigned->Contains(k));
}
}
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, NestedIf) {
num_locals = 5;
for (int i = 0; i < 5; i++) {
uint8_t code[] = {WASM_LOOP(
WASM_IF_ELSE(WASM_SET_ZERO(0), WASM_SET_ZERO(i), WASM_SET_ZERO(1)))};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
bool expected = i == j || j == 0 || j == 1;
EXPECT_EQ(expected, assigned->Contains(j));
}
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, BigLocal) {
num_locals = 65000;
for (int i = 13; i < 65000; i = static_cast<int>(i * 1.5)) {
uint8_t code[] = {WASM_LOOP(WASM_I32V_1(11), kExprLocalSet, U32V_3(i))};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
bool expected = i == j;
EXPECT_EQ(expected, assigned->Contains(j));
}
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, Break) {
num_locals = 3;
uint8_t code[] = {
WASM_LOOP(WASM_IF(WASM_LOCAL_GET(0), WASM_BRV(1, WASM_SET_ZERO(1)))),
WASM_SET_ZERO(0)};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
bool expected = j == 1;
EXPECT_EQ(expected, assigned->Contains(j));
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, Loop1) {
num_locals = 5;
uint8_t code[] = {
WASM_LOOP(WASM_IF(
WASM_LOCAL_GET(0),
WASM_BRV(0, WASM_LOCAL_SET(3, WASM_I32_SUB(WASM_LOCAL_GET(0),
WASM_I32V_1(1)))))),
WASM_LOCAL_GET(0)};
BitVector* assigned = Analyze(code, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
bool expected = j == 3;
EXPECT_EQ(expected, assigned->Contains(j));
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, Loop2) {
num_locals = 6;
const uint8_t kIter = 0;
const uint8_t kSum = 3;
uint8_t code[] = {WASM_BLOCK(
WASM_WHILE(
WASM_LOCAL_GET(kIter),
WASM_BLOCK(
WASM_LOCAL_SET(
kSum, WASM_F32_ADD(WASM_LOCAL_GET(kSum),
WASM_LOAD_MEM(MachineType::Float32(),
WASM_LOCAL_GET(kIter)))),
WASM_LOCAL_SET(
kIter, WASM_I32_SUB(WASM_LOCAL_GET(kIter), WASM_I32V_1(4))))),
WASM_STORE_MEM(MachineType::Float32(), WASM_ZERO, WASM_LOCAL_GET(kSum)),
WASM_LOCAL_GET(kIter))};
BitVector* assigned = Analyze(code + 2, code + arraysize(code));
for (int j = 0; j < assigned->length(); j++) {
bool expected = j == kIter || j == kSum;
EXPECT_EQ(expected, assigned->Contains(j));
}
}
TEST_F(WasmLoopAssignmentAnalyzerTest, NestedLoop) {
num_locals = 5;
uint8_t code[] = {WASM_LOOP(WASM_LOOP(WASM_LOCAL_SET(0, 1)))};
bool outer_is_innermost = false;
BitVector* outer_assigned =
Analyze(code, code + arraysize(code), &outer_is_innermost);
for (int j = 0; j < outer_assigned->length(); j++) {
bool expected = j == 0;
EXPECT_EQ(expected, outer_assigned->Contains(j));
}
EXPECT_FALSE(outer_is_innermost);
bool inner_is_innermost = false;
BitVector* inner_assigned =
Analyze(code + 2, code + arraysize(code), &inner_is_innermost);
for (int j = 0; j < inner_assigned->length(); j++) {
bool expected = j == 0;
EXPECT_EQ(expected, inner_assigned->Contains(j));
}
EXPECT_TRUE(inner_is_innermost);
}
TEST_F(WasmLoopAssignmentAnalyzerTest, Malformed) {
uint8_t code[] = {kExprLoop, kVoidCode, kExprF32Neg, kExprBrTable, 0x0E, 'h',
'e', 'l', 'l', 'o', ',', ' ',
'w', 'o', 'r', 'l', 'd', '!'};
BitVector* assigned = Analyze(code, code + arraysize(code));
EXPECT_EQ(assigned, nullptr);
}
TEST_F(WasmLoopAssignmentAnalyzerTest, InvalidOpcode) {
uint8_t code[] = {WASM_LOOP(0xFF)};
BitVector* assigned = Analyze(code, code + arraysize(code));
EXPECT_EQ(assigned, nullptr);
}
TEST_F(WasmLoopAssignmentAnalyzerTest, regress_642867) {
static const uint8_t code[] = {
WASM_LOOP(WASM_ZERO, kExprLocalSet, 0xFA, 0xFF, 0xFF, 0xFF,
0x0F)}; // local index LEB128 0xFFFFFFFA
// Just make sure that the analysis does not crash.
Analyze(code, code + arraysize(code));
}
#undef WASM_SET_ZERO
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,279 @@
// Copyright 2021 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 <optional>
#include "include/v8config.h"
// TODO(clemensb): Extend this to other OSes.
#if V8_OS_POSIX && !V8_OS_FUCHSIA
#include <signal.h>
#endif // V8_OS_POSIX && !V8_OS_FUCHSIA
#include "src/base/macros.h"
#include "src/flags/flags.h"
#include "src/wasm/code-space-access.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-opcodes.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
namespace v8::internal::wasm {
class MemoryProtectionTest : public TestWithNativeContext {
public:
void SetUp() override {
v8_flags.wasm_lazy_compilation = false;
// The key is initially write-protected.
CHECK_IMPLIES(WasmCodeManager::HasMemoryProtectionKeySupport(),
!WasmCodeManager::MemoryProtectionKeyWritable());
}
void CompileModule() {
CHECK_NULL(native_module_);
native_module_ = CompileNativeModule();
code_ = native_module_->GetCode(0);
}
NativeModule* native_module() const { return native_module_.get(); }
WasmCode* code() const { return code_; }
bool code_is_protected() {
return V8_HAS_PTHREAD_JIT_WRITE_PROTECT ||
V8_HAS_BECORE_JIT_WRITE_PROTECT || uses_pku();
}
void WriteToCode() { code_->instructions()[0] = 0; }
void AssertCodeEventuallyProtected() {
if (!code_is_protected()) {
// Without protection, writing to code should always work.
WriteToCode();
return;
}
ASSERT_DEATH_IF_SUPPORTED(
{
WriteToCode();
base::OS::Sleep(base::TimeDelta::FromMilliseconds(10));
},
"");
}
bool uses_pku() {
// M1 always uses MAP_JIT.
if (V8_HAS_PTHREAD_JIT_WRITE_PROTECT || V8_HAS_BECORE_JIT_WRITE_PROTECT) {
return false;
}
return WasmCodeManager::HasMemoryProtectionKeySupport();
}
private:
std::shared_ptr<NativeModule> CompileNativeModule() {
// Define the bytes for a module with a single empty function.
static const uint8_t module_bytes[] = {
WASM_MODULE_HEADER, SECTION(Type, ENTRY_COUNT(1), SIG_ENTRY_v_v),
SECTION(Function, ENTRY_COUNT(1), SIG_INDEX(0)),
SECTION(Code, ENTRY_COUNT(1), ADD_COUNT(0 /* locals */, kExprEnd))};
base::OwnedVector<const uint8_t> bytes = base::OwnedCopyOf(module_bytes);
WasmDetectedFeatures detected_features;
ModuleResult result =
DecodeWasmModule(WasmEnabledFeatures::All(), bytes.as_vector(), false,
kWasmOrigin, &detected_features);
CHECK(result.ok());
ErrorThrower thrower(isolate(), "");
constexpr int kNoCompilationId = 0;
constexpr ProfileInformation* kNoProfileInformation = nullptr;
std::shared_ptr<NativeModule> native_module = CompileToNativeModule(
isolate(), WasmEnabledFeatures::All(), detected_features,
CompileTimeImports{}, &thrower, std::move(result).value(),
std::move(bytes), kNoCompilationId,
v8::metrics::Recorder::ContextId::Empty(), kNoProfileInformation);
CHECK(!thrower.error());
CHECK_NOT_NULL(native_module);
return native_module;
}
std::shared_ptr<NativeModule> native_module_;
WasmCodeRefScope code_refs_;
WasmCode* code_;
};
TEST_F(MemoryProtectionTest, CodeNotWritableAfterCompilation) {
CompileModule();
AssertCodeEventuallyProtected();
}
TEST_F(MemoryProtectionTest, CodeWritableWithinScope) {
CompileModule();
CodeSpaceWriteScope write_scope;
WriteToCode();
}
TEST_F(MemoryProtectionTest, CodeNotWritableAfterScope) {
CompileModule();
{
CodeSpaceWriteScope write_scope;
WriteToCode();
}
AssertCodeEventuallyProtected();
}
#if V8_OS_POSIX && !V8_OS_FUCHSIA
class ParameterizedMemoryProtectionTestWithSignalHandling
: public MemoryProtectionTest,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {
public:
class SignalHandlerScope {
public:
SignalHandlerScope() {
CHECK_NULL(current_handler_scope_);
current_handler_scope_ = this;
struct sigaction sa;
sa.sa_sigaction = &HandleSignal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
CHECK_EQ(0, sigaction(SIGPROF, &sa, &old_signal_handler_));
}
~SignalHandlerScope() {
CHECK_EQ(current_handler_scope_, this);
current_handler_scope_ = nullptr;
sigaction(SIGPROF, &old_signal_handler_, nullptr);
}
void SetAddressToWriteToOnSignal(uint8_t* address) {
CHECK_NULL(code_address_);
CHECK_NOT_NULL(address);
code_address_ = address;
}
int num_handled_signals() const { return handled_signals_; }
private:
static void HandleSignal(int signal, siginfo_t*, void*) {
// We execute on POSIX only, so we just directly use {printf} and friends.
if (signal == SIGPROF) {
printf("Handled SIGPROF.\n");
} else {
printf("Handled unknown signal: %d.\n", signal);
}
CHECK_NOT_NULL(current_handler_scope_);
current_handler_scope_->handled_signals_ += 1;
if (uint8_t* write_address = current_handler_scope_->code_address_) {
// Print to the error output such that we can check against this message
// in the ASSERT_DEATH_IF_SUPPORTED below.
fprintf(stderr, "Writing to code.\n");
// This write will crash if code is protected.
*write_address = 0;
fprintf(stderr, "Successfully wrote to code.\n");
}
}
struct sigaction old_signal_handler_;
int handled_signals_ = 0;
uint8_t* code_address_ = nullptr;
// These are accessed from the signal handler.
static SignalHandlerScope* current_handler_scope_;
};
};
// static
ParameterizedMemoryProtectionTestWithSignalHandling::SignalHandlerScope*
ParameterizedMemoryProtectionTestWithSignalHandling::SignalHandlerScope::
current_handler_scope_ = nullptr;
std::string PrintMemoryProtectionAndSignalHandlingTestParam(
::testing::TestParamInfo<std::tuple<bool, bool>> info) {
const bool write_in_signal_handler = std::get<0>(info.param);
const bool open_write_scope = std::get<1>(info.param);
return std::string(write_in_signal_handler ? "Write" : "NoWrite") + "_" +
(open_write_scope ? "WithScope" : "NoScope");
}
INSTANTIATE_TEST_SUITE_P(MemoryProtection,
ParameterizedMemoryProtectionTestWithSignalHandling,
::testing::Combine(::testing::Bool(),
::testing::Bool()),
PrintMemoryProtectionAndSignalHandlingTestParam);
TEST_P(ParameterizedMemoryProtectionTestWithSignalHandling, TestSignalHandler) {
// We must run in the "threadsafe" mode in order to make the spawned process
// for the death test(s) re-execute the whole unit test up to the point of the
// death test. Otherwise we would not really test the signal handling setup
// that we use in the wild.
// (see https://google.github.io/googletest/reference/assertions.html)
CHECK_EQ("threadsafe", GTEST_FLAG_GET(death_test_style));
const bool write_in_signal_handler = std::get<0>(GetParam());
const bool open_write_scope = std::get<1>(GetParam());
CompileModule();
SignalHandlerScope signal_handler_scope;
CHECK_EQ(0, signal_handler_scope.num_handled_signals());
pthread_kill(pthread_self(), SIGPROF);
CHECK_EQ(1, signal_handler_scope.num_handled_signals());
uint8_t* code_start_ptr = &code()->instructions()[0];
uint8_t code_start = *code_start_ptr;
CHECK_NE(0, code_start);
if (write_in_signal_handler) {
signal_handler_scope.SetAddressToWriteToOnSignal(code_start_ptr);
}
// If the signal handler writes to protected code we expect a crash.
// An exception is M1, where an open scope still has an effect in the signal
// handler.
bool expect_crash = write_in_signal_handler && code_is_protected() &&
((!V8_HAS_PTHREAD_JIT_WRITE_PROTECT &&
!V8_HAS_BECORE_JIT_WRITE_PROTECT) ||
!open_write_scope);
if (expect_crash) {
// Avoid {ASSERT_DEATH_IF_SUPPORTED}, because it only accepts a regex as
// second parameter, and not a matcher as {ASSERT_DEATH}.
#if GTEST_HAS_DEATH_TEST
ASSERT_DEATH(
{
std::optional<CodeSpaceWriteScope> write_scope;
if (open_write_scope) write_scope.emplace();
pthread_kill(pthread_self(), SIGPROF);
base::OS::Sleep(base::TimeDelta::FromMilliseconds(10));
},
// Check that the subprocess tried to write, but did not succeed.
::testing::AnyOf(
// non-sanitizer builds:
::testing::EndsWith("Writing to code.\n"),
// ASan:
::testing::HasSubstr("Writing to code.\n"
"AddressSanitizer:DEADLYSIGNAL"),
// MSan:
::testing::HasSubstr("Writing to code.\n"
"MemorySanitizer:DEADLYSIGNAL"),
// UBSan:
::testing::HasSubstr("Writing to code.\n"
"UndefinedBehaviorSanitizer:DEADLYSIGNAL")));
#endif // GTEST_HAS_DEATH_TEST
} else {
std::optional<CodeSpaceWriteScope> write_scope;
if (open_write_scope) write_scope.emplace();
// The signal handler does not write or code is not protected, hence this
// should succeed.
pthread_kill(pthread_self(), SIGPROF);
CHECK_EQ(2, signal_handler_scope.num_handled_signals());
CHECK_EQ(write_in_signal_handler ? 0 : code_start, *code_start_ptr);
}
}
#endif // V8_OS_POSIX && !V8_OS_FUCHSIA
} // namespace v8::internal::wasm

View File

@ -0,0 +1,81 @@
// Copyright 2020 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/objects/objects-inl.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-features.h"
#include "src/wasm/wasm-limits.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/unittests/test-utils.h"
namespace v8::internal::wasm {
class Memory64DecodingTest : public TestWithIsolateAndZone {
public:
std::shared_ptr<const WasmModule> DecodeModule(
std::initializer_list<uint8_t> module_body_bytes) {
// Add the wasm magic and version number automatically.
std::vector<uint8_t> module_bytes{WASM_MODULE_HEADER};
module_bytes.insert(module_bytes.end(), module_body_bytes);
bool kValidateFunctions = true;
WasmDetectedFeatures detected_features;
ModuleResult result =
DecodeWasmModule(WasmEnabledFeatures{}, base::VectorOf(module_bytes),
kValidateFunctions, kWasmOrigin, &detected_features);
CHECK_EQ(WasmDetectedFeatures{{WasmDetectedFeature::memory64}},
detected_features);
EXPECT_TRUE(result.ok()) << result.error().message();
return result.ok() ? std::move(result).value() : nullptr;
}
};
TEST_F(Memory64DecodingTest, MemoryLimitLEB64) {
// 2 bytes LEB (32-bit range), no maximum.
auto module = DecodeModule(
{SECTION(Memory, ENTRY_COUNT(1), kMemory64NoMaximum, U32V_2(5))});
ASSERT_NE(nullptr, module);
ASSERT_EQ(1u, module->memories.size());
const WasmMemory* memory = &module->memories[0];
EXPECT_EQ(5u, memory->initial_pages);
EXPECT_FALSE(memory->has_maximum_pages);
EXPECT_TRUE(memory->is_memory64());
// 2 bytes LEB (32-bit range), with maximum.
module = DecodeModule({SECTION(Memory, ENTRY_COUNT(1), kMemory64WithMaximum,
U32V_2(7), U32V_2(47))});
ASSERT_NE(nullptr, module);
ASSERT_EQ(1u, module->memories.size());
memory = &module->memories[0];
EXPECT_EQ(7u, memory->initial_pages);
EXPECT_TRUE(memory->has_maximum_pages);
EXPECT_EQ(47u, memory->maximum_pages);
EXPECT_TRUE(memory->is_memory64());
// 10 bytes LEB, 32-bit range, no maximum.
module = DecodeModule(
{SECTION(Memory, ENTRY_COUNT(1), kMemory64NoMaximum, U64V_10(2))});
ASSERT_NE(nullptr, module);
ASSERT_EQ(1u, module->memories.size());
memory = &module->memories[0];
EXPECT_EQ(2u, memory->initial_pages);
EXPECT_FALSE(memory->has_maximum_pages);
EXPECT_TRUE(memory->is_memory64());
// 10 bytes LEB, 32-bit range, with maximum.
module = DecodeModule({SECTION(Memory, ENTRY_COUNT(1), kMemory64WithMaximum,
U64V_10(2), U64V_10(6))});
ASSERT_NE(nullptr, module);
ASSERT_EQ(1u, module->memories.size());
memory = &module->memories[0];
EXPECT_EQ(2u, memory->initial_pages);
EXPECT_TRUE(memory->has_maximum_pages);
EXPECT_EQ(6u, memory->maximum_pages);
EXPECT_TRUE(memory->is_memory64());
// TODO(clemensb): Test numbers outside the 32-bit range once that's
// supported.
}
} // namespace v8::internal::wasm

View File

@ -0,0 +1,177 @@
// Copyright 2024 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/module-decoder.h"
#include "test/common/wasm/flag-utils.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock-support.h"
using testing::HasSubstr;
namespace v8::internal::wasm {
namespace module_decoder_unittest {
#define EXPECT_NOT_OK(result, msg) \
do { \
EXPECT_FALSE(result.ok()); \
if (!result.ok()) { \
EXPECT_THAT(result.error().message(), HasSubstr(msg)); \
} \
} while (false)
#define WASM_INIT_EXPR_I32V_1(val) WASM_I32V_1(val), kExprEnd
#define WASM_INIT_EXPR_I64V_5(val) WASM_I64V_5(val), kExprEnd
#define WASM_INIT_EXPR_FUNC_REF_NULL WASM_REF_NULL(kFuncRefCode), kExprEnd
class Table64DecodingTest : public TestWithIsolateAndZone {
public:
// Table64 is part of the Memory64 proposal, enabled via WASM_FEATURE_SCOPE
// for individual tests.
WasmEnabledFeatures enabled_features_;
ModuleResult DecodeModule(std::initializer_list<uint8_t> module_body_bytes) {
// Add the wasm magic and version number automatically.
std::vector<uint8_t> module_bytes{WASM_MODULE_HEADER};
module_bytes.insert(module_bytes.end(), module_body_bytes);
bool kValidateFunctions = true;
WasmDetectedFeatures detected_features;
ModuleResult result =
DecodeWasmModule(enabled_features_, base::VectorOf(module_bytes),
kValidateFunctions, kWasmOrigin, &detected_features);
CHECK_EQ(WasmDetectedFeatures{{WasmDetectedFeature::memory64}},
detected_features);
return result;
}
};
TEST_F(Table64DecodingTest, TableLimitLEB64) {
// 2 bytes LEB (32-bit range), no maximum.
ModuleResult module = DecodeModule({SECTION(
Table, ENTRY_COUNT(1), kFuncRefCode, kMemory64NoMaximum, U32V_2(5))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
const WasmTable* table = &module.value()->tables[0];
EXPECT_EQ(5u, table->initial_size);
EXPECT_FALSE(table->has_maximum_size);
EXPECT_TRUE(table->is_table64());
// 3 bytes LEB (32-bit range), with maximum.
module =
DecodeModule({SECTION(Table, ENTRY_COUNT(1), kExternRefCode,
kMemory64WithMaximum, U32V_3(12), U32V_3(123))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(12u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_EQ(123u, table->maximum_size);
EXPECT_TRUE(table->is_table64());
// 5 bytes LEB (32-bit range), no maximum.
module = DecodeModule({SECTION(Table, ENTRY_COUNT(1), kExternRefCode,
kMemory64NoMaximum, U64V_5(7))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(7u, table->initial_size);
EXPECT_FALSE(table->has_maximum_size);
EXPECT_TRUE(table->is_table64());
// 10 bytes LEB (32-bit range), with maximum.
module =
DecodeModule({SECTION(Table, ENTRY_COUNT(1), kFuncRefCode,
kMemory64WithMaximum, U64V_10(4), U64V_10(1234))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(4u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_EQ(1234u, table->maximum_size);
EXPECT_TRUE(table->is_table64());
// 5 bytes LEB maximum, outside 32-bit range (2^32).
module = DecodeModule(
{SECTION(Table, ENTRY_COUNT(1), kFuncRefCode, kMemory64WithMaximum,
U64V_1(0), U64V_5(uint64_t{1} << 32))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(0u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_EQ(uint64_t{1} << 32, table->maximum_size);
EXPECT_TRUE(table->is_table64());
// 10 bytes LEB maximum, maximum 64-bit value.
module = DecodeModule(
{SECTION(Table, ENTRY_COUNT(1), kFuncRefCode, kMemory64WithMaximum,
U64V_1(0), U64V_10(kMaxUInt64))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(0u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_EQ(kMaxUInt64, table->maximum_size);
EXPECT_TRUE(table->is_table64());
}
TEST_F(Table64DecodingTest, InvalidTableLimits) {
const uint8_t kInvalidLimits = 0x15;
ModuleResult module = DecodeModule({SECTION(
Table, ENTRY_COUNT(1), kFuncRefCode, kInvalidLimits, U32V_2(5))});
EXPECT_NOT_OK(module, "invalid table limits flags");
}
TEST_F(Table64DecodingTest, ImportedTable64) {
// 10 bytes LEB (32-bit range), no maximum.
ModuleResult module = DecodeModule(
{SECTION(Import, ENTRY_COUNT(1), ADD_COUNT('m'), ADD_COUNT('t'),
kExternalTable, kFuncRefCode, kMemory64NoMaximum, U64V_10(5))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
const WasmTable* table = &module.value()->tables[0];
EXPECT_EQ(5u, table->initial_size);
EXPECT_FALSE(table->has_maximum_size);
EXPECT_TRUE(table->is_table64());
// 5 bytes LEB (32-bit range), with maximum.
module = DecodeModule({SECTION(
Import, ENTRY_COUNT(1), ADD_COUNT('m'), ADD_COUNT('t'), kExternalTable,
kFuncRefCode, kMemory64WithMaximum, U64V_5(123), U64V_5(225))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(123u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_TRUE(table->is_table64());
EXPECT_EQ(225u, table->maximum_size);
// 5 bytes LEB maximum, outside 32-bit range.
module = DecodeModule(
{SECTION(Import, ENTRY_COUNT(1), ADD_COUNT('m'), ADD_COUNT('t'),
kExternalTable, kFuncRefCode, kMemory64WithMaximum, U64V_5(0),
U64V_5(uint64_t{1} << 32))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(0u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_TRUE(table->is_table64());
EXPECT_EQ(uint64_t{1} << 32, table->maximum_size);
// 10 bytes LEB maximum, maximum u64.
module = DecodeModule({SECTION(
Import, ENTRY_COUNT(1), ADD_COUNT('m'), ADD_COUNT('t'), kExternalTable,
kFuncRefCode, kMemory64WithMaximum, U64V_5(0), U64V_10(kMaxUInt64))});
EXPECT_TRUE(module.ok()) << module.error().message();
ASSERT_EQ(1u, module.value()->tables.size());
table = &module.value()->tables[0];
EXPECT_EQ(0u, table->initial_size);
EXPECT_TRUE(table->has_maximum_size);
EXPECT_TRUE(table->is_table64());
EXPECT_EQ(kMaxUInt64, table->maximum_size);
}
} // namespace module_decoder_unittest
} // namespace v8::internal::wasm

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
// Copyright 2020 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/signature-hashing.h"
#include "test/unittests/test-utils.h"
namespace v8::internal::wasm::signature_hashing_unittest {
#if V8_ENABLE_SANDBOX
class WasmSignatureHashingTest : public TestWithPlatform {
public:
uint64_t H(std::initializer_list<ValueType> params,
std::initializer_list<ValueType> returns) {
const FunctionSig* sig = FunctionSig::Build(&zone_, returns, params);
return SignatureHasher::Hash(sig);
}
private:
AccountingAllocator allocator_;
Zone zone_{&allocator_, "WasmSignatureHashingTestZone"};
};
TEST_F(WasmSignatureHashingTest, SignatureHashing) {
ValueType i = kWasmI32;
ValueType l = kWasmI64;
ValueType d = kWasmF64;
ValueType s = kWasmS128;
ValueType r = kWasmExternRef;
USE(l);
std::vector<uint64_t> distinct_hashes{
// Some simple signatures.
H({}, {}), // --
H({i}, {}), // --
H({r}, {}), // --
H({}, {i}), // --
H({}, {r}), // --
// These two have the same number of parameters, but need different
// numbers of stack slots for them. Assume that no more than 8
// untagged params can be passed in registers; the 9th must be on the
// stack.
H({d, d, d, d, d, d, d, d, d}, {}), // --
H({d, d, d, d, d, d, d, d, s}, {}), // --
#if V8_TARGET_ARCH_32_BIT
// Same, but only relevant for 32-bit platforms.
H({i, i, i, i, i, i, i, i, i}, {}), // --
H({i, i, i, i, i, i, i, i, l}, {}),
#endif // V8_TARGET_ARCH_32_BIT
// Same, but for returns. We only use 2 return registers.
H({}, {d, d, d, d}), // --
H({}, {d, d, s, d}), // --
// These two have the same number of stack parameters, but some are
// tagged.
H({i, i, i, i, i, i, i, i, i, i}, {}), // --
H({i, i, i, i, i, i, i, i, i, r}, {}), // --
};
for (size_t j = 0; j < distinct_hashes.size(); j++) {
for (size_t k = j + 1; k < distinct_hashes.size(); k++) {
uint64_t hash_j = distinct_hashes[j];
uint64_t hash_k = distinct_hashes[k];
if (hash_j == hash_k) {
PrintF("Hash collision for signatures %zu and %zu\n", j, k);
}
EXPECT_NE(hash_j, hash_k);
}
}
}
#endif // V8_ENABLE_SANDBOX
} // namespace v8::internal::wasm::signature_hashing_unittest

View File

@ -0,0 +1,738 @@
// Copyright 2020 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/simd-shuffle.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock-support.h"
using ::testing::ElementsAre;
namespace v8 {
namespace internal {
namespace wasm {
// Helper to make calls to private wasm shuffle functions.
class SimdShuffleTest : public ::testing::Test {
public:
template <int Size, typename = std::enable_if_t<Size == kSimd128Size ||
Size == kSimd256Size>>
using Shuffle = std::array<uint8_t, Size>;
template <int Size, typename = std::enable_if_t<Size == kSimd128Size ||
Size == kSimd256Size>>
struct TestShuffle {
Shuffle<Size> non_canonical;
Shuffle<Size> canonical;
bool needs_swap;
bool is_swizzle;
};
// Call testing members in wasm.
static void CanonicalizeShuffle(bool inputs_equal,
Shuffle<kSimd128Size>* shuffle,
bool* needs_swap, bool* is_swizzle) {
SimdShuffle::CanonicalizeShuffle(inputs_equal, &(*shuffle)[0], needs_swap,
is_swizzle);
}
static bool TryMatchIdentity(const Shuffle<kSimd128Size>& shuffle) {
return SimdShuffle::TryMatchIdentity(&shuffle[0]);
}
template <int LANES>
static bool TryMatchSplat(const Shuffle<kSimd128Size>& shuffle, int* index) {
return SimdShuffle::TryMatchSplat<LANES>(&shuffle[0], index);
}
static bool TryMatch64x1Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle64x1) {
return SimdShuffle::TryMatch64x1Shuffle(&shuffle[0], shuffle64x1);
}
static bool TryMatch64x2Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle64x2) {
return SimdShuffle::TryMatch64x2Shuffle(&shuffle[0], shuffle64x2);
}
static bool TryMatch32x1Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle32x1) {
return SimdShuffle::TryMatch32x1Shuffle(&shuffle[0], shuffle32x1);
}
static bool TryMatch32x2Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle32x2) {
return SimdShuffle::TryMatch32x2Shuffle(&shuffle[0], shuffle32x2);
}
static bool TryMatch32x4Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle32x4) {
return SimdShuffle::TryMatch32x4Shuffle(&shuffle[0], shuffle32x4);
}
static bool TryMatch32x8Shuffle(const Shuffle<kSimd256Size>& shuffle,
uint8_t* shuffle32x8) {
return SimdShuffle::TryMatch32x8Shuffle(&shuffle[0], shuffle32x8);
}
static bool TryMatch32x4Reverse(const uint8_t* shuffle32x4) {
return SimdShuffle::TryMatch32x4Reverse(shuffle32x4);
}
static bool TryMatch32x4OneLaneSwizzle(const uint8_t* shuffle32x4,
uint8_t* from, uint8_t* to) {
return SimdShuffle::TryMatch32x4OneLaneSwizzle(shuffle32x4, from, to);
}
static bool TryMatch16x1Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle16x1) {
return SimdShuffle::TryMatch16x1Shuffle(&shuffle[0], shuffle16x1);
}
static bool TryMatch16x2Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle16x2) {
return SimdShuffle::TryMatch16x2Shuffle(&shuffle[0], shuffle16x2);
}
static bool TryMatch16x4Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle16x4) {
return SimdShuffle::TryMatch16x4Shuffle(&shuffle[0], shuffle16x4);
}
static bool TryMatch16x8Shuffle(const Shuffle<kSimd128Size>& shuffle,
uint8_t* shuffle16x8) {
return SimdShuffle::TryMatch16x8Shuffle(&shuffle[0], shuffle16x8);
}
static bool TryMatchConcat(const Shuffle<kSimd128Size>& shuffle,
uint8_t* offset) {
return SimdShuffle::TryMatchConcat(&shuffle[0], offset);
}
static bool TryMatchBlend(const Shuffle<kSimd128Size>& shuffle) {
return SimdShuffle::TryMatchBlend(&shuffle[0]);
}
#ifdef V8_TARGET_ARCH_X64
static bool TryMatchVpshufd(const uint8_t* shuffle32x8, uint8_t* control) {
return SimdShuffle::TryMatchVpshufd(shuffle32x8, control);
}
static bool TryMatchShufps256(const uint8_t* shuffle32x8, uint8_t* control) {
return SimdShuffle::TryMatchShufps256(shuffle32x8, control);
}
#endif // V8_TARGET_ARCH_X64
};
template <int Size, typename = std::enable_if_t<Size == kSimd128Size ||
Size == kSimd256Size>>
bool operator==(const SimdShuffleTest::Shuffle<Size>& a,
const SimdShuffleTest::Shuffle<Size>& b) {
for (int i = 0; i < Size; ++i) {
if (a[i] != b[i]) return false;
}
return true;
}
TEST_F(SimdShuffleTest, CanonicalizeShuffle) {
const bool kInputsEqual = true;
const bool kNeedsSwap = true;
const bool kIsSwizzle = true;
bool needs_swap;
bool is_swizzle;
// Test canonicalization driven by input shuffle.
TestShuffle<kSimd128Size> test_shuffles[] = {
// Identity is canonical.
{{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
!kNeedsSwap,
kIsSwizzle},
// Non-canonical identity requires a swap.
{{{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}},
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
kNeedsSwap,
kIsSwizzle},
// General shuffle, canonical is unchanged.
{{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
!kNeedsSwap,
!kIsSwizzle},
// Non-canonical shuffle requires a swap.
{{{16, 0, 17, 1, 18, 2, 19, 3, 20, 4, 21, 5, 22, 6, 23, 7}},
{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
kNeedsSwap,
!kIsSwizzle},
};
for (size_t i = 0; i < arraysize(test_shuffles); ++i) {
Shuffle<kSimd128Size> shuffle = test_shuffles[i].non_canonical;
CanonicalizeShuffle(!kInputsEqual, &shuffle, &needs_swap, &is_swizzle);
EXPECT_EQ(shuffle, test_shuffles[i].canonical);
EXPECT_EQ(needs_swap, test_shuffles[i].needs_swap);
EXPECT_EQ(is_swizzle, test_shuffles[i].is_swizzle);
}
// Test canonicalization when inputs are equal (explicit swizzle).
TestShuffle<kSimd128Size> test_swizzles[] = {
// Identity is canonical.
{{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
!kNeedsSwap,
kIsSwizzle},
// Non-canonical identity requires a swap.
{{{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}},
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
!kNeedsSwap,
kIsSwizzle},
// Canonicalized to swizzle.
{{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}},
{{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}},
!kNeedsSwap,
kIsSwizzle},
// Canonicalized to swizzle.
{{{16, 0, 17, 1, 18, 2, 19, 3, 20, 4, 21, 5, 22, 6, 23, 7}},
{{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}},
!kNeedsSwap,
kIsSwizzle},
};
for (size_t i = 0; i < arraysize(test_swizzles); ++i) {
Shuffle<kSimd128Size> shuffle = test_swizzles[i].non_canonical;
CanonicalizeShuffle(kInputsEqual, &shuffle, &needs_swap, &is_swizzle);
EXPECT_EQ(shuffle, test_swizzles[i].canonical);
EXPECT_EQ(needs_swap, test_swizzles[i].needs_swap);
EXPECT_EQ(is_swizzle, test_swizzles[i].is_swizzle);
}
}
TEST_F(SimdShuffleTest, TryMatchIdentity) {
// Match shuffle that returns first source operand.
EXPECT_TRUE(TryMatchIdentity(
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}));
// The non-canonicalized identity shuffle doesn't match.
EXPECT_FALSE(TryMatchIdentity(
{{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}}));
// Even one lane out of place is not an identity shuffle.
EXPECT_FALSE(TryMatchIdentity(
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 31}}));
}
TEST_F(SimdShuffleTest, TryMatchSplat) {
int index;
// All lanes from the same 32 bit source lane.
EXPECT_TRUE(TryMatchSplat<4>(
{{4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}}, &index));
EXPECT_EQ(1, index);
// It shouldn't match for other vector shapes.
EXPECT_FALSE(TryMatchSplat<8>(
{{4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}}, &index));
EXPECT_FALSE(TryMatchSplat<16>(
{{4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7}}, &index));
// All lanes from the same 16 bit source lane.
EXPECT_TRUE(TryMatchSplat<8>(
{{16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17}},
&index));
EXPECT_EQ(8, index);
// It shouldn't match for other vector shapes.
EXPECT_FALSE(TryMatchSplat<4>(
{{16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17}},
&index));
EXPECT_FALSE(TryMatchSplat<16>(
{{16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17, 16, 17}},
&index));
// All lanes from the same 8 bit source lane.
EXPECT_TRUE(TryMatchSplat<16>(
{{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}, &index));
EXPECT_EQ(7, index);
// It shouldn't match for other vector shapes.
EXPECT_FALSE(TryMatchSplat<4>(
{{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}, &index));
EXPECT_FALSE(TryMatchSplat<8>(
{{7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}}, &index));
}
TEST_F(SimdShuffleTest, TryMatchConcat) {
uint8_t offset;
// Ascending indices, jump at end to same input (concatenating swizzle).
EXPECT_TRUE(TryMatchConcat(
{{3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2}}, &offset));
EXPECT_EQ(3, offset);
// Ascending indices, jump at end to other input (concatenating shuffle).
EXPECT_TRUE(TryMatchConcat(
{{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}}, &offset));
EXPECT_EQ(4, offset);
// Shuffles that should not match:
// Ascending indices, but jump isn't at end/beginning.
EXPECT_FALSE(TryMatchConcat(
{{3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6}}, &offset));
// Ascending indices, but multiple jumps.
EXPECT_FALSE(TryMatchConcat(
{{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}}, &offset));
}
TEST_F(SimdShuffleTest, TryMatch32x1Shuffle) {
uint8_t shuffle32x1;
EXPECT_TRUE(TryMatch32x1Shuffle({{12, 13, 14, 15}}, &shuffle32x1));
EXPECT_EQ(3, shuffle32x1);
EXPECT_TRUE(TryMatch32x1Shuffle({{16, 17, 18, 19}}, &shuffle32x1));
EXPECT_EQ(4, shuffle32x1);
EXPECT_FALSE(TryMatch32x1Shuffle({{3, 4, 5, 6}}, &shuffle32x1));
EXPECT_FALSE(TryMatch32x1Shuffle({{19, 18, 17, 16}}, &shuffle32x1));
}
TEST_F(SimdShuffleTest, TryMatch32x2Shuffle) {
uint8_t shuffle32x2[2];
EXPECT_TRUE(
TryMatch32x2Shuffle({{12, 13, 14, 15, 8, 9, 10, 11}}, shuffle32x2));
EXPECT_EQ(3, shuffle32x2[0]);
EXPECT_EQ(2, shuffle32x2[1]);
EXPECT_TRUE(TryMatch32x2Shuffle({{4, 5, 6, 7, 16, 17, 18, 19}}, shuffle32x2));
EXPECT_EQ(1, shuffle32x2[0]);
EXPECT_EQ(4, shuffle32x2[1]);
EXPECT_FALSE(
TryMatch32x2Shuffle({{3, 4, 5, 6, 16, 17, 18, 19}}, shuffle32x2));
EXPECT_FALSE(
TryMatch32x2Shuffle({{4, 5, 6, 7, 19, 18, 17, 16}}, shuffle32x2));
}
TEST_F(SimdShuffleTest, TryMatch32x4Shuffle) {
uint8_t shuffle32x4[4];
// Match if each group of 4 bytes is from the same 32 bit lane.
EXPECT_TRUE(TryMatch32x4Shuffle(
{{12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19}},
shuffle32x4));
EXPECT_EQ(3, shuffle32x4[0]);
EXPECT_EQ(2, shuffle32x4[1]);
EXPECT_EQ(1, shuffle32x4[2]);
EXPECT_EQ(4, shuffle32x4[3]);
// Bytes must be in order in the 32 bit lane.
EXPECT_FALSE(TryMatch32x4Shuffle(
{{12, 13, 14, 14, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19}},
shuffle32x4));
// Each group must start with the first byte in the 32 bit lane.
EXPECT_FALSE(TryMatch32x4Shuffle(
{{13, 14, 15, 12, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19}},
shuffle32x4));
}
TEST_F(SimdShuffleTest, TryMatch32x8Shuffle) {
uint8_t shuffle32x8[8];
// Match if each group of 4 bytes is from the same 32 bit lane.
EXPECT_TRUE(TryMatch32x8Shuffle(
{{12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3}},
shuffle32x8));
EXPECT_EQ(3, shuffle32x8[0]);
EXPECT_EQ(2, shuffle32x8[1]);
EXPECT_EQ(1, shuffle32x8[2]);
EXPECT_EQ(4, shuffle32x8[3]);
EXPECT_EQ(5, shuffle32x8[4]);
EXPECT_EQ(6, shuffle32x8[5]);
EXPECT_EQ(7, shuffle32x8[6]);
EXPECT_EQ(0, shuffle32x8[7]);
// Bytes must be in order in the 32 bit lane.
EXPECT_FALSE(TryMatch32x8Shuffle(
{{12, 13, 14, 14, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3}},
shuffle32x8));
// Each group must start with the first byte in the 32 bit lane.
EXPECT_FALSE(TryMatch32x8Shuffle(
{{13, 14, 15, 12, 8, 9, 10, 11, 4, 5, 6, 7, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3}},
shuffle32x8));
}
TEST_F(SimdShuffleTest, TryMatch32x4Reverse) {
Shuffle<kSimd128Size> low_rev = {12, 13, 14, 15, 8, 9, 10, 11,
4, 5, 6, 7, 0, 1, 2, 3};
std::array<uint8_t, 4> shuffle32x4;
// low
EXPECT_TRUE(TryMatch32x4Shuffle(low_rev, shuffle32x4.data()));
EXPECT_EQ(3, shuffle32x4[0]);
EXPECT_EQ(2, shuffle32x4[1]);
EXPECT_EQ(1, shuffle32x4[2]);
EXPECT_EQ(0, shuffle32x4[3]);
EXPECT_TRUE(TryMatch32x4Reverse(shuffle32x4.data()));
EXPECT_EQ(SimdShuffle::TryMatchCanonical(low_rev),
SimdShuffle::CanonicalShuffle::kS32x4Reverse);
// high
Shuffle<kSimd128Size> high_rev = {28, 29, 30, 31, 24, 25, 26, 27,
20, 21, 22, 23, 16, 17, 18, 19};
EXPECT_TRUE(TryMatch32x4Shuffle(high_rev, shuffle32x4.data()));
EXPECT_EQ(7, shuffle32x4[0]);
EXPECT_EQ(6, shuffle32x4[1]);
EXPECT_EQ(5, shuffle32x4[2]);
EXPECT_EQ(4, shuffle32x4[3]);
bool needs_swap = false;
bool is_swizzle = false;
CanonicalizeShuffle(false, &high_rev, &needs_swap, &is_swizzle);
EXPECT_TRUE(needs_swap);
EXPECT_TRUE(is_swizzle);
EXPECT_TRUE(TryMatch32x4Shuffle(high_rev, shuffle32x4.data()));
EXPECT_TRUE(TryMatch32x4Reverse(shuffle32x4.data()));
EXPECT_EQ(SimdShuffle::TryMatchCanonical(high_rev),
SimdShuffle::CanonicalShuffle::kS32x4Reverse);
}
TEST_F(SimdShuffleTest, TryMatch32x4OneLaneSwizzle) {
uint8_t shuffle32x4[4];
uint8_t from = 0;
uint8_t to = 0;
// low
EXPECT_TRUE(TryMatch32x4Shuffle(
{{12, 13, 14, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
shuffle32x4));
EXPECT_EQ(3, shuffle32x4[0]);
EXPECT_EQ(1, shuffle32x4[1]);
EXPECT_EQ(2, shuffle32x4[2]);
EXPECT_EQ(3, shuffle32x4[3]);
EXPECT_TRUE(TryMatch32x4OneLaneSwizzle(shuffle32x4, &from, &to));
EXPECT_EQ(from, 3);
EXPECT_EQ(to, 0);
// high
Shuffle<kSimd128Size> high_one = {16, 17, 18, 19, 20, 21, 22, 23,
20, 21, 22, 23, 28, 29, 30, 31};
EXPECT_TRUE(TryMatch32x4Shuffle(high_one, shuffle32x4));
EXPECT_EQ(4, shuffle32x4[0]);
EXPECT_EQ(5, shuffle32x4[1]);
EXPECT_EQ(5, shuffle32x4[2]);
EXPECT_EQ(7, shuffle32x4[3]);
bool needs_swap = false;
bool is_swizzle = false;
CanonicalizeShuffle(false, &high_one, &needs_swap, &is_swizzle);
EXPECT_TRUE(needs_swap);
EXPECT_TRUE(is_swizzle);
EXPECT_TRUE(TryMatch32x4Shuffle(high_one, shuffle32x4));
EXPECT_TRUE(TryMatch32x4OneLaneSwizzle(shuffle32x4, &from, &to));
EXPECT_EQ(from, 1);
EXPECT_EQ(to, 2);
}
TEST_F(SimdShuffleTest, TryMatch16x1Shuffle) {
uint8_t shuffle16x1;
// Match if each group of 2 bytes is from the same 16 bit lane.
EXPECT_TRUE(TryMatch16x1Shuffle({{12, 13}}, &shuffle16x1));
EXPECT_EQ(6, shuffle16x1);
EXPECT_TRUE(TryMatch16x1Shuffle({{26, 27}}, &shuffle16x1));
EXPECT_EQ(13, shuffle16x1);
// Bytes must be in order in the 16 bit lane.
EXPECT_FALSE(TryMatch16x1Shuffle({{1, 2}}, &shuffle16x1));
// Each group must start with the first byte in the 16 bit lane.
EXPECT_FALSE(TryMatch16x1Shuffle({{25, 26}}, &shuffle16x1));
}
TEST_F(SimdShuffleTest, TryMatch16x2Shuffle) {
uint8_t shuffle16x2[2];
// Match if each group of 2 bytes is from the same 16 bit lane.
EXPECT_TRUE(TryMatch16x2Shuffle({{12, 13, 30, 31}}, shuffle16x2));
EXPECT_EQ(6, shuffle16x2[0]);
EXPECT_EQ(15, shuffle16x2[1]);
EXPECT_TRUE(TryMatch16x2Shuffle({{8, 9, 26, 27}}, shuffle16x2));
EXPECT_EQ(4, shuffle16x2[0]);
EXPECT_EQ(13, shuffle16x2[1]);
EXPECT_TRUE(TryMatch16x2Shuffle({{4, 5, 22, 23}}, shuffle16x2));
EXPECT_EQ(2, shuffle16x2[0]);
EXPECT_EQ(11, shuffle16x2[1]);
EXPECT_TRUE(TryMatch16x2Shuffle({{16, 17, 2, 3}}, shuffle16x2));
EXPECT_EQ(8, shuffle16x2[0]);
EXPECT_EQ(1, shuffle16x2[1]);
// Bytes must be in order in the 16 bit lane.
EXPECT_FALSE(TryMatch16x2Shuffle({{12, 13, 11, 11}}, shuffle16x2));
// Each group must start with the first byte in the 16 bit lane.
EXPECT_FALSE(TryMatch16x2Shuffle({{1, 0, 3, 2}}, shuffle16x2));
}
TEST_F(SimdShuffleTest, TryMatch16x4Shuffle) {
uint8_t shuffle16x4[4];
// Match if each group of 2 bytes is from the same 16 bit lane.
EXPECT_TRUE(
TryMatch16x4Shuffle({{12, 13, 30, 31, 8, 9, 26, 27}}, shuffle16x4));
EXPECT_EQ(6, shuffle16x4[0]);
EXPECT_EQ(15, shuffle16x4[1]);
EXPECT_EQ(4, shuffle16x4[2]);
EXPECT_EQ(13, shuffle16x4[3]);
EXPECT_TRUE(TryMatch16x4Shuffle({{4, 5, 22, 23, 16, 17, 2, 3}}, shuffle16x4));
EXPECT_EQ(2, shuffle16x4[0]);
EXPECT_EQ(11, shuffle16x4[1]);
EXPECT_EQ(8, shuffle16x4[2]);
EXPECT_EQ(1, shuffle16x4[3]);
// Bytes must be in order in the 16 bit lane.
EXPECT_FALSE(
TryMatch16x4Shuffle({{12, 13, 30, 30, 8, 9, 26, 27}}, shuffle16x4));
// Each group must start with the first byte in the 16 bit lane.
EXPECT_FALSE(
TryMatch16x4Shuffle({{12, 13, 31, 30, 8, 9, 26, 27}}, shuffle16x4));
}
TEST_F(SimdShuffleTest, TryMatch16x8Shuffle) {
uint8_t shuffle16x8[8];
// Match if each group of 2 bytes is from the same 16 bit lane.
EXPECT_TRUE(TryMatch16x8Shuffle(
{{12, 13, 30, 31, 8, 9, 26, 27, 4, 5, 22, 23, 16, 17, 2, 3}},
shuffle16x8));
EXPECT_EQ(6, shuffle16x8[0]);
EXPECT_EQ(15, shuffle16x8[1]);
EXPECT_EQ(4, shuffle16x8[2]);
EXPECT_EQ(13, shuffle16x8[3]);
EXPECT_EQ(2, shuffle16x8[4]);
EXPECT_EQ(11, shuffle16x8[5]);
EXPECT_EQ(8, shuffle16x8[6]);
EXPECT_EQ(1, shuffle16x8[7]);
// Bytes must be in order in the 16 bit lane.
EXPECT_FALSE(TryMatch16x8Shuffle(
{{12, 13, 30, 30, 8, 9, 26, 27, 4, 5, 22, 23, 16, 17, 2, 3}},
shuffle16x8));
// Each group must start with the first byte in the 16 bit lane.
EXPECT_FALSE(TryMatch16x8Shuffle(
{{12, 13, 31, 30, 8, 9, 26, 27, 4, 5, 22, 23, 16, 17, 2, 3}},
shuffle16x8));
}
TEST_F(SimdShuffleTest, TryMatchBlend) {
// Match if each byte remains in place.
EXPECT_TRUE(TryMatchBlend(
{{0, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}}));
// Identity is a blend.
EXPECT_TRUE(
TryMatchBlend({{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}));
// Even one lane out of place is not a blend.
EXPECT_FALSE(TryMatchBlend(
{{1, 17, 2, 19, 4, 21, 6, 23, 8, 25, 10, 27, 12, 29, 14, 31}}));
}
TEST_F(SimdShuffleTest, PairwiseReduce) {
uint8_t shuffle64x2[2];
EXPECT_TRUE(TryMatch64x2Shuffle(
{{8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}}, shuffle64x2));
EXPECT_TRUE(SimdShuffle::TryMatch64x2Reduce(shuffle64x2));
constexpr uint8_t pairwise_32x4[] = {4, 5, 6, 7, 0, 1, 2, 3,
12, 13, 14, 15, 0, 1, 2, 3};
constexpr uint8_t pairwise_32x2[] = {8, 9, 10, 11, 0, 1, 2, 3,
0, 1, 2, 3, 0, 1, 2, 3};
EXPECT_TRUE(
SimdShuffle::TryMatch32x4PairwiseReduce(pairwise_32x4, pairwise_32x2));
}
TEST_F(SimdShuffleTest, UpperToLowerReduce) {
constexpr uint8_t upper_to_lower_32x4[] = {8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 0, 1, 2, 3};
constexpr uint8_t upper_to_lower_32x2[] = {4, 5, 6, 7, 0, 1, 2, 3,
0, 1, 2, 3, 0, 1, 2, 3};
EXPECT_TRUE(SimdShuffle::TryMatch32x4UpperToLowerReduce(upper_to_lower_32x4,
upper_to_lower_32x2));
constexpr uint8_t upper_to_lower_16x8[] = {8, 9, 10, 11, 12, 13, 14, 15, 0,
1, 0, 1, 0, 1, 0, 1, 0};
constexpr uint8_t upper_to_lower_16x4[] = {4, 5, 6, 7, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1};
constexpr uint8_t upper_to_lower_16x2[] = {2, 3, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1};
EXPECT_TRUE(SimdShuffle::TryMatch16x8UpperToLowerReduce(
upper_to_lower_16x8, upper_to_lower_16x4, upper_to_lower_16x2));
constexpr uint8_t upper_to_lower_8x16[] = {8, 9, 10, 11, 12, 13, 14, 15, 0,
1, 0, 1, 0, 1, 0, 1, 0};
constexpr uint8_t upper_to_lower_8x8[] = {4, 5, 6, 7, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1};
constexpr uint8_t upper_to_lower_8x4[] = {2, 3, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1};
constexpr uint8_t upper_to_lower_8x2[] = {1, 0, 0, 1, 0, 1, 0, 1,
0, 1, 0, 1, 0, 1, 0, 1};
EXPECT_TRUE(SimdShuffle::TryMatch8x16UpperToLowerReduce(
upper_to_lower_8x16, upper_to_lower_8x8, upper_to_lower_8x4,
upper_to_lower_8x2));
}
TEST_F(SimdShuffleTest, Shuffle64x1) {
uint8_t shuffle64x1;
EXPECT_TRUE(
TryMatch64x1Shuffle({{24, 25, 26, 27, 28, 29, 30, 31}}, &shuffle64x1));
EXPECT_EQ(3, shuffle64x1);
EXPECT_TRUE(
TryMatch64x1Shuffle({{8, 9, 10, 11, 12, 13, 14, 15}}, &shuffle64x1));
EXPECT_EQ(1, shuffle64x1);
EXPECT_FALSE(TryMatch64x1Shuffle({{1, 2, 3, 4, 5, 6, 7, 8}}, &shuffle64x1));
}
TEST_F(SimdShuffleTest, Shuffle64x2) {
constexpr uint8_t identity_64x2[] = {0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15};
std::array<uint8_t, 8> shuffle64x2;
EXPECT_TRUE(
SimdShuffle::TryMatch64x2Shuffle(identity_64x2, shuffle64x2.data()));
EXPECT_EQ(shuffle64x2[0], 0);
EXPECT_EQ(shuffle64x2[1], 1);
constexpr uint8_t shuffle_1_3[] = {8, 9, 10, 11, 12, 13, 14, 15,
24, 25, 26, 27, 28, 29, 30, 31};
EXPECT_TRUE(
SimdShuffle::TryMatch64x2Shuffle(shuffle_1_3, shuffle64x2.data()));
EXPECT_EQ(shuffle64x2[0], 1);
EXPECT_EQ(shuffle64x2[1], 3);
constexpr uint8_t rev_64x2[] = {8, 9, 10, 11, 12, 13, 14, 15,
0, 1, 2, 3, 4, 5, 6, 7};
EXPECT_TRUE(SimdShuffle::TryMatch64x2Shuffle(rev_64x2, shuffle64x2.data()));
EXPECT_EQ(shuffle64x2[0], 1);
EXPECT_EQ(shuffle64x2[1], 0);
constexpr uint8_t dup0_64x2[] = {0, 1, 2, 3, 4, 5, 6, 7,
0, 1, 2, 3, 4, 5, 6, 7};
EXPECT_TRUE(SimdShuffle::TryMatch64x2Shuffle(dup0_64x2, shuffle64x2.data()));
EXPECT_EQ(shuffle64x2[0], 0);
EXPECT_EQ(shuffle64x2[1], 0);
constexpr uint8_t dup1_64x2[] = {8, 9, 10, 11, 12, 13, 14, 15,
8, 9, 10, 11, 12, 13, 14, 15};
EXPECT_TRUE(SimdShuffle::TryMatch64x2Shuffle(dup1_64x2, shuffle64x2.data()));
EXPECT_EQ(shuffle64x2[0], 1);
EXPECT_EQ(shuffle64x2[1], 1);
}
using CanonicalShuffle = SimdShuffle::CanonicalShuffle;
using ShuffleMap = std::unordered_map<CanonicalShuffle,
const std::array<uint8_t, kSimd128Size>>;
ShuffleMap test_shuffles = {
{CanonicalShuffle::kIdentity,
{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}},
{CanonicalShuffle::kUnknown,
{{0, 1, 2, 3, 16, 17, 18, 19, 16, 17, 18, 19, 20, 21, 22, 23}}},
{CanonicalShuffle::kS64x2ReverseBytes,
{{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}}},
{CanonicalShuffle::kS64x2Reverse,
{{8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7}}},
{CanonicalShuffle::kS64x2Even,
{{0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23}}},
{CanonicalShuffle::kS64x2Odd,
{{8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31}}},
{CanonicalShuffle::kS32x4ReverseBytes,
{{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12}}},
{CanonicalShuffle::kS32x4Reverse,
{{12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3}}},
{CanonicalShuffle::kS32x4InterleaveLowHalves,
{{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23}}},
{CanonicalShuffle::kS32x4InterleaveHighHalves,
{{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31}}},
{CanonicalShuffle::kS32x4Even,
{{0, 1, 2, 3, 8, 9, 10, 11, 16, 17, 18, 19, 24, 25, 26, 27}}},
{CanonicalShuffle::kS32x4Odd,
{{4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31}}},
{CanonicalShuffle::kS32x4TransposeEven,
{{0, 1, 2, 3, 16, 17, 18, 19, 8, 9, 10, 11, 24, 25, 26, 27}}},
{CanonicalShuffle::kS32x4TransposeOdd,
{{4, 5, 6, 7, 20, 21, 22, 23, 12, 13, 14, 15, 28, 29, 30, 31}}},
{CanonicalShuffle::kS16x8ReverseBytes,
{{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14}}},
{CanonicalShuffle::kS16x8InterleaveLowHalves,
{{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23}}},
{CanonicalShuffle::kS16x8InterleaveHighHalves,
{{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31}}},
{CanonicalShuffle::kS16x8Even,
{{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29}}},
{CanonicalShuffle::kS16x8Odd,
{{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31}}},
{CanonicalShuffle::kS16x8TransposeEven,
{{0, 1, 16, 17, 4, 5, 20, 21, 8, 9, 24, 25, 12, 13, 28, 29}}},
{CanonicalShuffle::kS16x8TransposeOdd,
{{2, 3, 18, 19, 6, 7, 22, 23, 10, 11, 26, 27, 14, 15, 30, 31}}},
{CanonicalShuffle::kS8x16InterleaveLowHalves,
{{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23}}},
{CanonicalShuffle::kS8x16InterleaveHighHalves,
{{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31}}},
{CanonicalShuffle::kS8x16Even,
{{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30}}},
{CanonicalShuffle::kS8x16Odd,
{{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}}},
{CanonicalShuffle::kS8x16TransposeEven,
{{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30}}},
{CanonicalShuffle::kS8x16TransposeOdd,
{{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31}}},
{CanonicalShuffle::kS32x2Reverse,
{{4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}}},
{CanonicalShuffle::kS16x4Reverse,
{{6, 7, 4, 5, 2, 3, 0, 1, 14, 15, 12, 13, 10, 11, 8, 9}}},
{CanonicalShuffle::kS16x2Reverse,
{{2, 3, 0, 1, 6, 7, 4, 5, 10, 11, 8, 9, 14, 15, 12, 13}}},
};
TEST_F(SimdShuffleTest, CanonicalMatchers) {
for (auto& pair : test_shuffles) {
EXPECT_EQ(pair.first, SimdShuffle::TryMatchCanonical(pair.second));
}
}
TEST(SimdShufflePackTest, PackShuffle4) {
uint8_t arr[4]{0b0001, 0b0010, 0b0100, 0b1000};
EXPECT_EQ(0b00001001, SimdShuffle::PackShuffle4(arr));
}
TEST(SimdShufflePackTest, PackBlend8) {
uint8_t arr[8]{0, 2, 4, 6, 8, 10, 12, 14};
EXPECT_EQ(0b11110000, SimdShuffle::PackBlend8(arr));
}
TEST(SimdShufflePackTest, PackBlend4) {
uint8_t arr[4]{0, 2, 4, 6};
EXPECT_EQ(0b11110000, SimdShuffle::PackBlend4(arr));
}
TEST(SimdShufflePackTest, Pack4Lanes) {
uint8_t arr[4]{0x01, 0x08, 0xa0, 0x7c};
EXPECT_EQ(0x7ca00801, SimdShuffle::Pack4Lanes(arr));
}
TEST(SimdShufflePackTest, Pack16Lanes) {
uint8_t arr[16]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
uint32_t imms[4]{0};
SimdShuffle::Pack16Lanes(imms, arr);
EXPECT_THAT(imms,
ElementsAre(0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c));
}
#ifdef V8_TARGET_ARCH_X64
TEST_F(SimdShuffleTest, TryMatchVpshufd) {
uint8_t shuffle32x8[8];
EXPECT_TRUE(TryMatch32x8Shuffle(
{{12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3,
28, 29, 30, 31, 24, 25, 26, 27, 20, 21, 22, 23, 16, 17, 18, 19}},
shuffle32x8));
EXPECT_EQ(shuffle32x8[0], 3);
EXPECT_EQ(shuffle32x8[1], 2);
EXPECT_EQ(shuffle32x8[2], 1);
EXPECT_EQ(shuffle32x8[3], 0);
EXPECT_EQ(shuffle32x8[4], 7);
EXPECT_EQ(shuffle32x8[5], 6);
EXPECT_EQ(shuffle32x8[6], 5);
EXPECT_EQ(shuffle32x8[7], 4);
uint8_t control;
EXPECT_TRUE(TryMatchVpshufd(shuffle32x8, &control));
EXPECT_EQ(control, 0b00'01'10'11);
}
TEST_F(SimdShuffleTest, TryMatchShufps256) {
uint8_t shuffle32x8[8];
EXPECT_TRUE(TryMatch32x8Shuffle(
{{12, 13, 14, 15, 8, 9, 10, 11, 36, 37, 38, 39, 32, 33, 34, 35,
28, 29, 30, 31, 24, 25, 26, 27, 52, 53, 54, 55, 48, 49, 50, 51}},
shuffle32x8));
EXPECT_EQ(shuffle32x8[0], 3);
EXPECT_EQ(shuffle32x8[1], 2);
EXPECT_EQ(shuffle32x8[2], 9);
EXPECT_EQ(shuffle32x8[3], 8);
EXPECT_EQ(shuffle32x8[4], 7);
EXPECT_EQ(shuffle32x8[5], 6);
EXPECT_EQ(shuffle32x8[6], 13);
EXPECT_EQ(shuffle32x8[7], 12);
uint8_t control;
EXPECT_TRUE(TryMatchShufps256(shuffle32x8, &control));
EXPECT_EQ(control, 0b00'01'10'11);
}
#endif // V8_TARGET_ARCH_X64
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,664 @@
// 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/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 {
struct MockStreamingResult {
size_t num_sections = 0;
size_t num_functions = 0;
bool error;
base::OwnedVector<const uint8_t> received_bytes;
bool ok() const { return !error; }
MockStreamingResult() = default;
};
class NoTracer {
public:
void Bytes(const uint8_t* start, uint32_t count) {}
void Description(const char* desc) {}
};
class MockStreamingProcessor : public StreamingProcessor {
public:
explicit MockStreamingProcessor(MockStreamingResult* result)
: result_(result) {}
bool ProcessModuleHeader(base::Vector<const uint8_t> bytes) override {
Decoder decoder(bytes.begin(), bytes.end());
uint32_t magic_word = decoder.consume_u32("wasm magic", ITracer::NoTrace);
if (decoder.failed() || magic_word != kWasmMagic) {
result_->error = WasmError(0, "expected wasm magic");
return false;
}
uint32_t magic_version =
decoder.consume_u32("wasm version", ITracer::NoTrace);
if (decoder.failed() || magic_version != kWasmVersion) {
result_->error = WasmError(4, "expected wasm version");
return false;
}
return true;
}
// Process all sections but the code section.
bool ProcessSection(SectionCode section_code,
base::Vector<const uint8_t> bytes,
uint32_t offset) override {
++result_->num_sections;
return true;
}
bool ProcessCodeSectionHeader(int num_functions, uint32_t offset,
std::shared_ptr<WireBytesStorage>,
int code_section_start,
int code_section_length) override {
return true;
}
// Process a function body.
bool ProcessFunctionBody(base::Vector<const uint8_t> bytes,
uint32_t offset) override {
++result_->num_functions;
return true;
}
void OnFinishedChunk() override {}
// Finish the processing of the stream.
void OnFinishedStream(base::OwnedVector<const uint8_t> bytes,
bool after_error) override {
result_->received_bytes = std::move(bytes);
result_->error = after_error;
}
void OnAbort() override {}
bool Deserialize(base::Vector<const uint8_t> module_bytes,
base::Vector<const uint8_t> wire_bytes) override {
return false;
}
private:
MockStreamingResult* const result_;
};
class WasmStreamingDecoderTest : public ::testing::Test {
public:
void ExpectVerifies(base::Vector<const uint8_t> data,
size_t expected_sections, size_t expected_functions) {
for (int split = 0; split <= data.length(); ++split) {
MockStreamingResult result;
auto stream = StreamingDecoder::CreateAsyncStreamingDecoder(
std::make_unique<MockStreamingProcessor>(&result));
stream->OnBytesReceived(data.SubVector(0, split));
stream->OnBytesReceived(data.SubVector(split, data.length()));
stream->Finish();
EXPECT_TRUE(result.ok());
EXPECT_EQ(expected_sections, result.num_sections);
EXPECT_EQ(expected_functions, result.num_functions);
EXPECT_EQ(data, result.received_bytes.as_vector());
}
}
void ExpectFailure(base::Vector<const uint8_t> data) {
for (int split = 0; split <= data.length(); ++split) {
MockStreamingResult result;
auto stream = StreamingDecoder::CreateAsyncStreamingDecoder(
std::make_unique<MockStreamingProcessor>(&result));
stream->OnBytesReceived(data.SubVector(0, split));
stream->OnBytesReceived(data.SubVector(split, data.length()));
stream->Finish();
EXPECT_FALSE(result.ok());
EXPECT_TRUE(result.error);
}
}
};
TEST_F(WasmStreamingDecoderTest, EmptyStream) {
MockStreamingResult result;
auto stream = StreamingDecoder::CreateAsyncStreamingDecoder(
std::make_unique<MockStreamingProcessor>(&result));
stream->Finish();
EXPECT_FALSE(result.ok());
}
TEST_F(WasmStreamingDecoderTest, IncompleteModuleHeader) {
const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)};
{
MockStreamingResult result;
auto stream = StreamingDecoder::CreateAsyncStreamingDecoder(
std::make_unique<MockStreamingProcessor>(&result));
stream->OnBytesReceived(base::VectorOf(data, 1));
stream->Finish();
EXPECT_FALSE(result.ok());
}
for (uint32_t length = 1; length < sizeof(data); ++length) {
ExpectFailure(base::VectorOf(data, length));
}
}
TEST_F(WasmStreamingDecoderTest, MagicAndVersion) {
const uint8_t data[] = {U32_LE(kWasmMagic), U32_LE(kWasmVersion)};
ExpectVerifies(base::ArrayVector(data), 0, 0);
}
TEST_F(WasmStreamingDecoderTest, BadMagic) {
for (uint32_t x = 1; x; x <<= 1) {
const uint8_t data[] = {U32_LE(kWasmMagic ^ x), U32_LE(kWasmVersion)};
ExpectFailure(base::ArrayVector(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(base::ArrayVector(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(base::ArrayVector(data), 1, 0);
}
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(base::ArrayVector(data), 1, 0);
}
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(base::ArrayVector(data), 1, 0);
}
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(base::ArrayVector(data), 1, 0);
}
TEST_F(WasmStreamingDecoderTest, OneEmptySection) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x0 // Section Length
};
ExpectVerifies(base::ArrayVector(data), 1, 0);
}
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(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, OneSectionNotEnoughPayload2) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x6, // Section Length
0x0 // Payload
};
ExpectFailure(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, OneSectionInvalidLength) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
0x1, // Section ID
0x80, // Section Length (invalid LEB)
0x80, // --
0x80, // --
0x80, // --
0x80, // --
};
ExpectFailure(base::ArrayVector(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(base::ArrayVector(data), 2, 0);
}
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(base::ArrayVector(data), 2, 0);
}
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(base::ArrayVector(data), 2, 0);
}
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(base::ArrayVector(data), 2, 0);
}
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(base::ArrayVector(data), 0, 1);
}
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(base::ArrayVector(data), 0, 1);
}
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 -- ERROR
};
ExpectFailure(base::ArrayVector(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(base::ArrayVector(data), 0, 2);
}
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(base::ArrayVector(data), 0, 2);
}
TEST_F(WasmStreamingDecoderTest, CodeSectionLengthZero) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x0, // Section Length
};
ExpectFailure(base::ArrayVector(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(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, CodeSectionLengthTooHighZeroFunctions) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0xD, // Section Length
0x0, // Number of Functions
};
ExpectFailure(base::ArrayVector(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 <0>
0x7, // Function Length <1>
0x0, // Function <2>
0x0, // 2 <3>
0x0, // 3 <3>
0x0, // 4 <4>
0x0, // 5 <5>
0x0, // 6 <6>
0x0, // 7 <7>
0x1, // Function Length <8> -- ERROR
0x0, // Function
};
ExpectFailure(base::ArrayVector(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 <0>
0x80, // -- <1> -- ERROR
0x00, // --
0x7, // Function Length
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(base::ArrayVector(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 <0>
0x80, // -- <1>
0x00, // -- <2>
0x87, // Function Length <3>
0x80, // -- <4>
0x00, // -- <5> -- ERROR
0x0, // Function
0x0, // 2
0x0, // 3
0x0, // 4
0x0, // 5
0x0, // 6
0x0, // 7
0x1, // Function Length
0x0, // Function
};
ExpectFailure(base::ArrayVector(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(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, NumberOfFunctionsTooLow) {
const uint8_t data[] = {
U32_LE(kWasmMagic), // --
U32_LE(kWasmVersion), // --
kCodeSectionCode, // Section ID
0x8, // Section Length
0x2, // Number of Functions
0x1, // Function Length
0x0, // Function
0x2, // Function Length
0x0, // Function byte#0
0x0, // Function byte#1 -- ERROR
0x1, // Function Length
0x0 // Function
};
ExpectFailure(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, TwoCodeSections) {
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
kCodeSectionCode, // Section ID -- ERROR
0x3, // Section Length
0x1, // Number of Functions
0x1, // Function Length
0x0, // Function
};
ExpectFailure(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, UnknownSection) {
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
kUnknownSectionCode, // Section ID
0x3, // Section Length
0x1, // Name Length
0x1, // Name
0x0, // Content
};
ExpectVerifies(base::ArrayVector(data), 1, 1);
}
TEST_F(WasmStreamingDecoderTest, UnknownSectionSandwich) {
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
kUnknownSectionCode, // Section ID
0x3, // Section Length
0x1, // Name Length
0x1, // Name
0x0, // Content
kCodeSectionCode, // Section ID -- ERROR
0x3, // Section Length
0x1, // Number of Functions
0x1, // Function Length
0x0, // Function
};
ExpectFailure(base::ArrayVector(data));
}
TEST_F(WasmStreamingDecoderTest, InvalidSectionCode) {
uint8_t kInvalidSectionCode = 61;
const uint8_t data[] = {WASM_MODULE_HEADER, SECTION(Invalid)};
ExpectFailure(base::ArrayVector(data));
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,43 @@
// Copyright 2022 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/string-builder.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8::internal::wasm {
namespace string_builder_unittest {
TEST(StringBuilder, Simple) {
StringBuilder sb;
sb << "foo"
<< "bar" << -42 << "\n";
EXPECT_STREQ(std::string(sb.start(), sb.length()).c_str(), "foobar-42\n");
}
TEST(StringBuilder, DontLeak) {
// Should be bigger than StringBuilder::kStackSize = 256.
constexpr size_t kMoreThanStackBufferSize = 300;
StringBuilder sb;
const char* on_stack = sb.start();
sb.allocate(kMoreThanStackBufferSize);
const char* on_heap = sb.start();
// If this fails, then kMoreThanStackBufferSize was too small.
ASSERT_NE(on_stack, on_heap);
// Still don't leak on further growth.
sb.allocate(kMoreThanStackBufferSize * 4);
}
TEST(StringBuilder, SuperLongStrings) {
// Should be bigger than StringBuilder::kChunkSize = 1024 * 1024.
constexpr size_t kMoreThanChunkSize = 2 * 1024 * 1024;
StringBuilder sb;
char* s = sb.allocate(kMoreThanChunkSize);
for (size_t i = 0; i < kMoreThanChunkSize; i++) {
s[i] = 'a';
}
}
} // namespace string_builder_unittest
} // namespace v8::internal::wasm

View File

@ -0,0 +1,81 @@
// Copyright 2022 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/struct-types.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8::internal::wasm {
namespace struct_types_unittest {
class StructTypesTest : public TestWithZone {};
TEST_F(StructTypesTest, Empty) {
StructType::Builder builder(this->zone(), 0, false);
StructType* type = builder.Build();
EXPECT_EQ(0u, type->total_fields_size());
StructType::Builder desc_builder(this->zone(), 0, true);
StructType* desc_type = desc_builder.Build();
EXPECT_EQ(uint32_t{kTaggedSize}, desc_type->total_fields_size());
}
TEST_F(StructTypesTest, OneField) {
StructType::Builder builder(this->zone(), 1, false);
builder.AddField(kWasmI32, true);
StructType* type = builder.Build();
uint32_t expected = std::max(kUInt32Size, kTaggedSize);
EXPECT_EQ(expected, type->total_fields_size());
EXPECT_EQ(0u, type->field_offset(0));
StructType::Builder desc_builder(this->zone(), 1, true);
desc_builder.AddField(kWasmI32, true);
StructType* desc_type = desc_builder.Build();
EXPECT_EQ(uint32_t{kTaggedSize + std::max(kUInt32Size, kTaggedSize)},
desc_type->total_fields_size());
EXPECT_EQ(uint32_t{kTaggedSize}, desc_type->field_offset(0));
}
TEST_F(StructTypesTest, Packing) {
StructType::Builder builder(this->zone(), 5, false);
builder.AddField(kWasmI64, true);
builder.AddField(kWasmI8, true);
builder.AddField(kWasmI32, true);
builder.AddField(kWasmI16, true);
builder.AddField(kWasmI8, true);
StructType* type = builder.Build();
EXPECT_EQ(16u, type->total_fields_size());
EXPECT_EQ(0u, type->field_offset(0));
EXPECT_EQ(8u, type->field_offset(1));
EXPECT_EQ(12u, type->field_offset(2));
EXPECT_EQ(10u, type->field_offset(3));
EXPECT_EQ(9u, type->field_offset(4));
}
TEST_F(StructTypesTest, CopyingOffsets) {
StructType::Builder builder(this->zone(), 5, false);
builder.AddField(kWasmI64, true);
builder.AddField(kWasmI8, true);
builder.AddField(kWasmI32, true);
builder.AddField(kWasmI16, true);
builder.AddField(kWasmI8, true);
StructType* type = builder.Build();
StructType::Builder copy_builder(this->zone(), type->field_count(), false);
for (uint32_t i = 0; i < type->field_count(); i++) {
copy_builder.AddField(type->field(i), type->mutability(i),
type->field_offset(i));
}
copy_builder.set_total_fields_size(type->total_fields_size());
StructType* copy = copy_builder.Build();
for (uint32_t i = 0; i < type->field_count(); i++) {
EXPECT_EQ(type->field_offset(i), copy->field_offset(i));
}
EXPECT_EQ(type->total_fields_size(), copy->total_fields_size());
}
} // namespace struct_types_unittest
} // namespace v8::internal::wasm

View File

@ -0,0 +1,872 @@
// Copyright 2020 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/canonical-types.h"
#include "src/wasm/wasm-subtyping.h"
#include "test/common/flag-utils.h"
#include "test/common/wasm/flag-utils.h"
#include "test/unittests/test-utils.h"
namespace v8::internal::wasm::subtyping_unittest {
class WasmSubtypingTest : public TestWithPlatform {};
using FieldInit = std::pair<ValueType, bool>;
using Idx = ModuleTypeIndex;
constexpr bool kShared = true;
constexpr ValueType refS(uint32_t index, bool shared = kNotShared) {
return ValueType::Ref(Idx{index}, shared, RefTypeKind::kStruct);
}
constexpr ValueType refA(uint32_t index, bool shared = kNotShared) {
return ValueType::Ref(Idx{index}, shared, RefTypeKind::kArray);
}
constexpr ValueType refF(uint32_t index, bool shared = kNotShared) {
return ValueType::Ref(Idx{index}, shared, RefTypeKind::kFunction);
}
constexpr ValueType refC(uint32_t index, bool shared = kNotShared) {
return ValueType::Ref(Idx{index}, shared, RefTypeKind::kCont);
}
constexpr ValueType refNullS(uint32_t index, bool shared = kNotShared) {
return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kStruct);
}
constexpr ValueType refNullA(uint32_t index, bool shared = kNotShared) {
return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kArray);
}
constexpr ValueType refNullF(uint32_t index, bool shared = kNotShared) {
return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kFunction);
}
constexpr ValueType refNullC(uint32_t index, bool shared = kNotShared) {
return ValueType::RefNull(Idx{index}, shared, RefTypeKind::kCont);
}
FieldInit mut(ValueType type) { return FieldInit(type, true); }
FieldInit immut(ValueType type) { return FieldInit(type, false); }
void DefineStruct(WasmModule* module, std::initializer_list<FieldInit> fields,
ModuleTypeIndex supertype = kNoSuperType,
bool is_final = false, bool is_shared = false,
bool in_singleton_rec_group = true) {
StructType::Builder builder(&module->signature_zone,
static_cast<uint32_t>(fields.size()), false);
for (FieldInit field : fields) {
builder.AddField(field.first, field.second);
}
module->AddStructTypeForTesting(builder.Build(), supertype, is_final,
is_shared);
if (in_singleton_rec_group) {
GetTypeCanonicalizer()->AddRecursiveSingletonGroup(module);
}
}
void DefineArray(WasmModule* module, FieldInit element_type,
ModuleTypeIndex supertype = kNoSuperType,
bool is_final = false, bool is_shared = false,
bool in_singleton_rec_group = true) {
module->AddArrayTypeForTesting(module->signature_zone.New<ArrayType>(
element_type.first, element_type.second),
supertype, is_final, is_shared);
if (in_singleton_rec_group) {
GetTypeCanonicalizer()->AddRecursiveSingletonGroup(module);
}
}
void DefineSignature(WasmModule* module,
std::initializer_list<ValueType> params,
std::initializer_list<ValueType> returns,
ModuleTypeIndex supertype = kNoSuperType,
bool is_final = false, bool is_shared = false,
bool in_singleton_rec_group = true) {
module->AddSignatureForTesting(
FunctionSig::Build(&module->signature_zone, returns, params), supertype,
is_final, is_shared);
if (in_singleton_rec_group) {
GetTypeCanonicalizer()->AddRecursiveGroup(module, 1);
}
}
void DefineCont(WasmModule* module, ModuleTypeIndex cont,
ModuleTypeIndex supertype = kNoSuperType, bool is_final = false,
bool is_shared = false) {
module->AddContTypeForTesting(module->signature_zone.New<ContType>(cont),
supertype, is_final, is_shared);
}
TEST_F(WasmSubtypingTest, Subtyping) {
v8::internal::AccountingAllocator allocator;
WasmModule module1_;
WasmModule module2_;
WasmModule* module1 = &module1_;
WasmModule* module2 = &module2_;
// Set up two identical modules.
for (WasmModule* module : {module1, module2}) {
// Three mutually recursive types.
/* 0 */ DefineStruct(module, {mut(refA(2)), immut(refNullA(2))},
kNoSuperType, false, false, false);
/* 1 */ DefineStruct(module, {mut(refA(2)), immut(refA(2))}, Idx{0}, false,
false, false);
/* 2 */ DefineArray(module, immut(refS(0)), kNoSuperType, false, false,
false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 3);
/* 3 */ DefineArray(module, immut(refS(1)), Idx{2});
/* 4 */ DefineStruct(
module, {mut(refA(2)), immut(refA(3)), immut(kWasmF64)}, Idx{1});
/* 5 */ DefineStruct(module, {mut(refNullA(2)), immut(refA(2))});
/* 6 */ DefineArray(module, mut(kWasmI32));
/* 7 */ DefineArray(module, immut(kWasmI32));
/* 8 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(8))});
/* 9 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(8))}, Idx{8});
/* 10 */ DefineSignature(module, {}, {});
/* 11 */ DefineSignature(module, {kWasmI32}, {kWasmI32});
/* 12 */ DefineSignature(module, {kWasmI32, kWasmI32}, {kWasmI32});
/* 13 */ DefineSignature(module, {refS(1)}, {kWasmI32});
/* 14 */ DefineSignature(module, {refS(0)}, {kWasmI32}, Idx{13});
/* 15 */ DefineSignature(module, {refS(0)}, {refS(0)});
/* 16 */ DefineSignature(module, {refS(0)}, {refS(4)}, Idx{15});
/* 17 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))});
// Rec. group.
/* 18 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}, Idx{17},
false, false, false);
/* 19 */ DefineArray(module, {mut(refNullF(21))}, kNoSuperType, false,
false, false);
/* 20 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false, false, false);
/* 21 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, Idx{20}, false,
false, false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
// Identical rec. group.
/* 22 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}, Idx{17},
false, false, false);
/* 23 */ DefineArray(module, {mut(refNullF(25))}, kNoSuperType, false,
false, false);
/* 24 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false, false, false);
/* 25 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, Idx{24}, false,
false, false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
// Nonidentical rec. group: the last function extends a type outside the
// recursive group.
/* 26 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(17))}, Idx{17},
false, false, false);
/* 27 */ DefineArray(module, {mut(refNullF(29))}, kNoSuperType, false,
false, false);
/* 28 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false, false, false);
/* 29 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, Idx{20}, false,
false, false);
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
/* 30 */ DefineStruct(module, {mut(kWasmI32), immut(refNullS(18))},
Idx{18});
/* 31 */ DefineStruct(
module, {mut(refA(2)), immut(refNullA(2)), immut(kWasmS128)}, Idx{1});
// Final types
/* 32 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true);
/* 33 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, Idx{32},
true);
/* 34 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, true);
/* 35 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false);
// Shared types.
/* 36 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType);
/* 37 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, Idx{36});
/* 38 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false, true);
/* 39 */ DefineStruct(module, {mut(kWasmI32), mut(kWasmI64)}, Idx{38},
false, true);
/* 40 */ DefineStruct(module, {mut(kWasmI32)}, kNoSuperType, false, true);
/* 41 */ DefineSignature(module, {kWasmI32}, {kWasmI32}, kNoSuperType,
false, true, true);
// Continuation types (switching group)
/* 42 */ DefineSignature(module, {kWasmI32}, {refNullC(45)}, kNoSuperType,
false, false, false);
/* 43 */ DefineSignature(module, {refNullC(44)}, {kWasmI32}, kNoSuperType,
false, false, false);
/* 44 */ DefineCont(module, ModuleTypeIndex{42});
/* 45 */ DefineCont(module, ModuleTypeIndex{43});
GetTypeCanonicalizer()->AddRecursiveGroup(module, 4);
// Continuation types, functions outside the group
/* 46 */ DefineCont(module, ModuleTypeIndex{42});
/* 47 */ DefineCont(module, ModuleTypeIndex{43}, Idx{45});
GetTypeCanonicalizer()->AddRecursiveGroup(module, 2);
}
constexpr ValueType numeric_types[] = {kWasmI32, kWasmI64, kWasmF32, kWasmF64,
kWasmS128};
constexpr ValueType ref_types[] = {
kWasmFuncRef, kWasmEqRef, kWasmStructRef,
kWasmArrayRef, kWasmI31Ref, kWasmAnyRef,
kWasmExternRef, kWasmNullExternRef, kWasmNullRef,
kWasmNullFuncRef, kWasmStringRef, kWasmStringViewIter,
kWasmExnRef, kWasmNullExnRef, kWasmRefNullExternString,
kWasmContRef, kWasmNullContRef,
refNullS(0), // struct
refS(0), // struct
refNullA(2), // array
refA(2), // array
refNullF(11), // function
refF(11), // function
refNullC(44), // continuation
refC(44) // continuation
};
// Some macros to help managing types and modules.
#define SUBTYPE(type1, type2) \
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module))
#define SUBTYPE_IFF(type1, type2, condition) \
EXPECT_EQ(IsSubtypeOf(type1, type2, module1, module), condition)
#define NOT_SUBTYPE(type1, type2) \
EXPECT_FALSE(IsSubtypeOf(type1, type2, module1, module))
// Use only with indexed types.
#define VALID_SUBTYPE(type1, type2) \
EXPECT_TRUE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
module1, module)); \
EXPECT_TRUE(IsSubtypeOf(type1, type2, module1, module));
#define NOT_VALID_SUBTYPE(type1, type2) \
EXPECT_FALSE(ValidSubtypeDefinition(type1.ref_index(), type2.ref_index(), \
module1, module));
#define IDENTICAL(kind, index1, index2) \
EXPECT_TRUE(EquivalentTypes(refNull##kind(index1), refNull##kind(index2), \
module1, module));
#define DISTINCT(kind, index1, index2) \
EXPECT_FALSE(EquivalentTypes(refNull##kind(index1), refNull##kind(index2), \
module1, module));
#define DISTINCT_SHARED(kind, index1, shared1, index2, shared2) \
EXPECT_FALSE(EquivalentTypes(refNull##kind(index1, shared1), \
refNull##kind(index2, shared2), module1, \
module));
// For union and intersection, we have a version that also checks the module,
// and one that does not.
#define UNION(type1, type2, type_result) \
EXPECT_EQ(Union(type1, type2, module1, module).type, type_result)
#define UNION_M(type1, type2, type_result, module_result) \
EXPECT_EQ(Union(type1, type2, module1, module), \
TypeInModule(type_result, module_result))
#define INTERSECTION(type1, type2, type_result) \
EXPECT_EQ(Intersection(type1, type2, module1, module).type, type_result)
#define INTERSECTION_M(type1, type2, type_result, module_result) \
EXPECT_EQ(Intersection(type1, type2, module1, module), \
TypeInModule(type_result, module_result))
for (WasmModule* module : {module1, module2}) {
// Type judgements across modules should work the same as within one module.
// Value types are unrelated, except if they are equal.
for (ValueType subtype : numeric_types) {
for (ValueType supertype : numeric_types) {
SUBTYPE_IFF(subtype, supertype, subtype == supertype);
}
}
// Value types are unrelated with reference types.
for (ValueType value_type : numeric_types) {
for (ValueType ref_type : ref_types) {
NOT_SUBTYPE(value_type, ref_type);
NOT_SUBTYPE(ref_type, value_type);
}
}
for (ValueType ref_type : ref_types) {
const bool is_extern = ref_type == kWasmExternRef ||
ref_type == kWasmNullExternRef ||
ref_type == kWasmRefNullExternString;
const bool is_any_func = ref_type == kWasmFuncRef ||
ref_type == kWasmNullFuncRef ||
ref_type == refNullF(11) || ref_type == refF(11);
const bool is_string_view = ref_type == kWasmStringViewIter ||
ref_type == kWasmStringViewWtf8 ||
ref_type == kWasmStringViewWtf16;
const bool is_any_cont = ref_type == kWasmContRef ||
ref_type == kWasmNullContRef ||
ref_type == refNullC(44) || ref_type == refC(44);
const bool is_exn =
ref_type == kWasmExnRef || ref_type == kWasmNullExnRef;
SCOPED_TRACE("ref_type: " + ref_type.name());
// Concrete reference types, i31ref, structref and arrayref are subtypes
// of eqref, externref/funcref/anyref/exnref/functions are not.
SUBTYPE_IFF(ref_type, kWasmEqRef,
ref_type != kWasmAnyRef && !is_any_func && !is_extern &&
!is_string_view && ref_type != kWasmStringRef &&
!is_exn && !is_any_cont);
// Struct types are subtypes of structref.
SUBTYPE_IFF(ref_type, kWasmStructRef,
ref_type == kWasmStructRef || ref_type == kWasmNullRef ||
ref_type == refS(0) || ref_type == refNullS(0));
// Array types are subtypes of arrayref.
SUBTYPE_IFF(ref_type, kWasmArrayRef,
ref_type == kWasmArrayRef || ref_type == refA(2) ||
ref_type == kWasmNullRef || ref_type == refNullA(2));
// Functions are subtypes of funcref.
SUBTYPE_IFF(ref_type, kWasmFuncRef, is_any_func);
// Each reference type is a subtype of itself.
SUBTYPE(ref_type, ref_type);
// Each non-func, non-extern, non-string-view, non-string-iter reference
// type is a subtype of anyref.
SUBTYPE_IFF(ref_type, kWasmAnyRef,
!is_any_func && !is_extern && !is_string_view && !is_exn &&
!is_any_cont);
// Only anyref is a subtype of anyref.
SUBTYPE_IFF(kWasmAnyRef, ref_type, ref_type == kWasmAnyRef);
// Only externref and nullexternref are subtypes of externref.
SUBTYPE_IFF(ref_type, kWasmExternRef, is_extern);
// Only nullexternref is a subtype of nullexternref.
SUBTYPE_IFF(ref_type, kWasmNullExternRef, ref_type == kWasmNullExternRef);
// Each nullable non-func, non-extern reference type is a supertype of
// nullref.
SUBTYPE_IFF(kWasmNullRef, ref_type,
ref_type.is_nullable() && !is_any_func && !is_extern &&
!is_exn && !is_any_cont);
// Only nullref is a subtype of nullref.
SUBTYPE_IFF(ref_type, kWasmNullRef, ref_type == kWasmNullRef);
// Only nullable funcs are supertypes of nofunc.
SUBTYPE_IFF(kWasmNullFuncRef, ref_type,
ref_type.is_nullable() && is_any_func);
// Only nullfuncref is a subtype of nullfuncref.
SUBTYPE_IFF(ref_type, kWasmNullFuncRef, ref_type == kWasmNullFuncRef);
// Make sure symmetric relations are symmetric.
for (ValueType ref_type2 : ref_types) {
if (ref_type == ref_type2) {
EXPECT_TRUE(EquivalentTypes(ref_type, ref_type2, module, module1));
EXPECT_TRUE(EquivalentTypes(ref_type2, ref_type, module1, module));
} else {
EXPECT_FALSE(EquivalentTypes(ref_type, ref_type2, module, module1));
EXPECT_FALSE(EquivalentTypes(ref_type2, ref_type, module1, module));
}
}
}
// The rest of ref. types are unrelated.
for (ValueType type_1 :
{kWasmFuncRef, kWasmI31Ref, kWasmArrayRef, kWasmExnRef}) {
for (ValueType type_2 :
{kWasmFuncRef, kWasmI31Ref, kWasmArrayRef, kWasmExnRef}) {
SUBTYPE_IFF(type_1, type_2, type_1 == type_2);
}
}
// Unrelated refs are unrelated.
NOT_VALID_SUBTYPE(refS(0), refA(2));
NOT_VALID_SUBTYPE(refNullA(3), refNullS(1));
// ref is a subtype of ref null for the same struct/array.
VALID_SUBTYPE(refS(0), refNullS(0));
VALID_SUBTYPE(refA(2), refNullA(2));
// ref null is not a subtype of ref for the same struct/array.
NOT_SUBTYPE(refNullS(0), refS(0));
NOT_SUBTYPE(refNullA(2), refA(2));
// ref is a subtype of ref null if the same is true for the underlying
// structs/arrays.
VALID_SUBTYPE(refA(3), refNullA(2));
// Prefix subtyping for structs.
VALID_SUBTYPE(refNullS(4), refNullS(0));
// Mutable fields are invariant.
NOT_VALID_SUBTYPE(refS(0), refS(5));
// Immutable fields are covariant.
VALID_SUBTYPE(refS(1), refS(0));
// Prefix subtyping + immutable field covariance for structs.
VALID_SUBTYPE(refNullS(4), refNullS(1));
// No subtyping between mutable/immutable fields.
NOT_VALID_SUBTYPE(refA(7), refA(6));
NOT_VALID_SUBTYPE(refA(6), refA(7));
// Recursive types.
VALID_SUBTYPE(refS(9), refS(8));
// Function subtyping;
// Unrelated function types are unrelated.
NOT_VALID_SUBTYPE(refF(10), refF(11));
// Function type with different parameter counts are unrelated.
NOT_VALID_SUBTYPE(refF(12), refF(11));
// Parameter contravariance holds.
VALID_SUBTYPE(refF(14), refF(13));
// Return type covariance holds.
VALID_SUBTYPE(refF(16), refF(15));
// Identical types are subtype-related.
VALID_SUBTYPE(refF(10), refF(10));
VALID_SUBTYPE(refF(11), refF(11));
// Continuation subtyping:
VALID_SUBTYPE(refC(44), refC(44));
NOT_VALID_SUBTYPE(refC(44), refC(45));
VALID_SUBTYPE(refC(45), refC(45));
NOT_VALID_SUBTYPE(refC(45), refC(44));
INTERSECTION(refF(11), refC(44), kWasmBottom); // Just checking ...
INTERSECTION(refNullC(44), refNullC(45), kWasmNullContRef);
INTERSECTION(refNullC(44), kWasmContRef, refNullC(44));
INTERSECTION(refC(44), kWasmContRef, refC(44));
INTERSECTION(refC(44), refC(45), kWasmBottom);
INTERSECTION(refNullC(44), kWasmNullContRef, kWasmNullContRef);
INTERSECTION(kWasmContRef, kWasmNullContRef, kWasmNullContRef);
UNION(kWasmNullContRef, kWasmContRef, kWasmContRef);
UNION(refNullC(44), kWasmContRef, kWasmContRef);
UNION(refC(44), kWasmContRef, kWasmContRef);
UNION(refNullC(44), refNullC(45), kWasmContRef);
UNION(refC(44), refC(45), kWasmContRef.AsNonNull());
UNION(refNullC(44), kWasmNullContRef, refNullC(44));
UNION(kWasmContRef, kWasmNullContRef, kWasmContRef);
// Canonicalization tests.
// Groups should only be canonicalized to identical groups.
IDENTICAL(S, 18, 22);
IDENTICAL(A, 19, 23);
IDENTICAL(F, 20, 24);
IDENTICAL(F, 21, 25);
DISTINCT(S, 18, 26);
DISTINCT(A, 19, 27);
DISTINCT(F, 20, 28);
DISTINCT(F, 21, 29);
// A type should not be canonicalized to an identical one with a different
// group structure.
DISTINCT(S, 18, 17);
// A subtype should also be subtype of an equivalent type.
VALID_SUBTYPE(refS(30), refS(18));
VALID_SUBTYPE(refS(30), refS(22));
NOT_SUBTYPE(refS(30), refS(26));
// Final types
// A type is not a valid subtype of a final type.
NOT_VALID_SUBTYPE(refS(33), refS(32));
IDENTICAL(S, 32, 34);
// A final and a non-final type are distinct.
DISTINCT(S, 32, 35);
/* Shared types */
// A shared type can be a subtype of a shared type.
VALID_SUBTYPE(refS(39, kShared), refS(38, kShared));
// A shared type is not a valid subtype of a non-shared type and vice versa.
NOT_VALID_SUBTYPE(refS(39, kShared), refS(36));
NOT_VALID_SUBTYPE(refS(37), refS(38, kShared));
// Two shared types are identical. A shared and non-shared type are
// distinct.
IDENTICAL(S, 38, 40);
DISTINCT_SHARED(S, 36, kNotShared, 38, kShared);
// Abstract types.
auto Gen = ValueType::Generic;
using G = GenericKind;
ValueType kRefAny = kWasmAnyRef.AsNonNull();
ValueType kRefAnyShared = Gen(G::kAny, kNonNullable, kShared);
ValueType kRefEq = kWasmEqRef.AsNonNull();
ValueType kRefEqShared = Gen(G::kEq, kNonNullable, kShared);
ValueType kRefI31Shared = Gen(G::kI31, kNonNullable, kShared);
ValueType kRefStructShared = Gen(G::kStruct, kNonNullable, kShared);
ValueType kRefArrayShared = Gen(G::kArray, kNonNullable, kShared);
ValueType kRefNoneShared = Gen(G::kNone, kNonNullable, kShared);
ValueType kRefFunc = kWasmFuncRef.AsNonNull();
ValueType kRefFuncShared = Gen(G::kFunc, kNonNullable, kShared);
ValueType kRefNoFuncShared = Gen(G::kNoFunc, kNonNullable, kShared);
ValueType kRefNoExternShared = Gen(G::kNoExtern, kNonNullable, kShared);
ValueType kRefNullAnyShared = Gen(G::kAny, kNullable, kShared);
ValueType kRefNullFuncShared = Gen(G::kFunc, kNullable, kShared);
ValueType kRefNullEqShared = Gen(G::kEq, kNullable, kShared);
ValueType kRefNullExternShared = Gen(G::kExtern, kNullable, kShared);
ValueType kRefNullNoneShared = Gen(G::kNone, kNullable, kShared);
ValueType kRefNullNoFuncShared = Gen(G::kNoFunc, kNullable, kShared);
ValueType kRefNullI31Shared = Gen(G::kI31, kNullable, kShared);
SUBTYPE(kRefEqShared, kRefAnyShared);
NOT_SUBTYPE(kRefEqShared, kRefAny);
NOT_SUBTYPE(kRefEq, kRefAnyShared);
NOT_SUBTYPE(kRefFuncShared, kRefAnyShared);
SUBTYPE(kRefNullNoneShared, kRefNullI31Shared);
SUBTYPE(kRefNullNoFuncShared, kRefNullFuncShared);
SUBTYPE(refS(40, kShared), kRefNullEqShared);
SUBTYPE(kRefNullNoneShared, refNullS(40, kShared));
NOT_SUBTYPE(refS(40, kShared), kWasmEqRef);
NOT_SUBTYPE(refS(40, kShared), kRefNullExternShared);
SUBTYPE(refF(41, kShared), kRefNullFuncShared);
SUBTYPE(kRefNullNoFuncShared, refNullF(41, kShared));
NOT_SUBTYPE(kRefNullNoFuncShared, refF(41, kShared));
NOT_SUBTYPE(refF(41, kShared), kRefNullAnyShared);
NOT_SUBTYPE(refF(41, kShared), kWasmFuncRef);
NOT_SUBTYPE(refS(0), kRefStructShared);
NOT_SUBTYPE(refA(2), kRefArrayShared);
NOT_SUBTYPE(refF(10), kRefFuncShared);
// Unions and intersections.
// Distinct numeric types are unrelated.
for (ValueType type1 : numeric_types) {
for (ValueType type2 : numeric_types) {
UNION(type1, type2, (type1 == type2 ? type1 : kWasmTop));
INTERSECTION(type1, type2, (type1 == type2 ? type1 : kWasmBottom));
}
}
// Numeric and reference types are unrelated.
for (ValueType type1 : numeric_types) {
for (ValueType type2 : ref_types) {
UNION(type1, type2, kWasmTop);
INTERSECTION(type1, type2, kWasmBottom);
}
}
// Reference type vs. itself and anyref.
for (ValueType type : ref_types) {
SCOPED_TRACE(type.name());
if (type == kWasmStringViewIter || type == kWasmStringViewWtf8 ||
type == kWasmStringViewWtf16) {
// String views aren't subtypes of any nor supertypes of null.
INTERSECTION(type, kWasmAnyRef, kWasmBottom);
INTERSECTION(type, kWasmNullRef, kWasmBottom);
} else if (type == kWasmFuncRef || type == kWasmNullFuncRef ||
type == refF(11) || type == refNullF(11) ||
type == kWasmExternRef || type == kWasmNullExternRef ||
type == kWasmRefNullExternString || type == kWasmContRef ||
type == kWasmNullContRef || type == refNullC(44) ||
type == refC(44)) {
// func, cont and extern types don't share the same type hierarchy as
// anyref.
INTERSECTION(type, kWasmAnyRef, kWasmBottom);
} else {
bool is_exn = type == kWasmExnRef || type == kWasmNullExnRef;
UNION(kWasmAnyRef, type, is_exn ? kWasmTop : kWasmAnyRef);
INTERSECTION(kWasmAnyRef, type, is_exn ? kWasmBottom : type);
UNION(kWasmAnyRef.AsNonNull(), type,
is_exn ? kWasmTop
: type.is_nullable() ? kWasmAnyRef
: kWasmAnyRef.AsNonNull());
INTERSECTION(kWasmAnyRef.AsNonNull(), type,
is_exn ? kWasmBottom
: type != kWasmNullRef ? type.AsNonNull()
: kWasmBottom);
}
}
// Abstract types vs abstract types.
UNION(kWasmEqRef, kWasmStructRef, kWasmEqRef);
UNION(kWasmEqRef, kWasmI31Ref, kWasmEqRef);
UNION(kWasmEqRef, kWasmArrayRef, kWasmEqRef);
UNION(kWasmEqRef, kWasmNullRef, kWasmEqRef);
UNION(kWasmStructRef, kWasmI31Ref, kWasmEqRef);
UNION(kWasmStructRef, kWasmArrayRef, kWasmEqRef);
UNION(kWasmStructRef, kWasmNullRef, kWasmStructRef);
UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef.AsNonNull(),
kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref, kWasmNullRef, kWasmI31Ref);
UNION(kWasmArrayRef, kWasmNullRef, kWasmArrayRef);
UNION(kWasmStructRef.AsNonNull(), kWasmI31Ref.AsNonNull(),
kWasmEqRef.AsNonNull());
UNION(kWasmI31Ref.AsNonNull(), kWasmArrayRef, kWasmEqRef);
UNION(kWasmAnyRef, kWasmNullRef, kWasmAnyRef);
UNION(kWasmExternRef, kWasmNullExternRef, kWasmExternRef);
UNION(kWasmRefNullExternString, kWasmNullExternRef,
kWasmRefNullExternString);
UNION(kWasmRefNullExternString.AsNonNull(), kWasmNullExternRef,
kWasmRefNullExternString);
UNION(kWasmRefNullExternString, kWasmExternRef, kWasmExternRef);
UNION(kWasmRefNullExternString, kWasmAnyRef, kWasmTop);
UNION(kWasmRefNullExternString, kWasmFuncRef, kWasmTop);
// Imported strings and stringref represent the same values. Still, they are
// in different type hierarchies and therefore incompatible (e.g. due to
// different null representation).
// (There is no interoperability between stringref and imported strings as
// they are competing proposals.)
UNION(kWasmRefNullExternString, kWasmStringRef, kWasmTop);
UNION(kWasmRefNullExternString.AsNonNull(), kWasmStringRef.AsNonNull(),
kWasmTop);
UNION(kWasmFuncRef, kWasmNullFuncRef, kWasmFuncRef);
UNION(kWasmFuncRef, kWasmStructRef, kWasmTop);
UNION(kWasmFuncRef, kWasmArrayRef, kWasmTop);
UNION(kWasmFuncRef, kWasmAnyRef, kWasmTop);
UNION(kWasmFuncRef, kWasmEqRef, kWasmTop);
UNION(kWasmStringRef, kWasmAnyRef, kWasmAnyRef);
UNION(kWasmStringRef, kWasmStructRef, kWasmAnyRef);
UNION(kWasmStringRef, kWasmArrayRef, kWasmAnyRef);
UNION(kWasmStringRef, kWasmFuncRef, kWasmTop);
UNION(kWasmStringViewIter, kWasmStringRef, kWasmTop);
UNION(kWasmStringViewWtf8, kWasmStringRef, kWasmTop);
UNION(kWasmStringViewWtf16, kWasmStringRef, kWasmTop);
UNION(kWasmStringViewIter, kWasmAnyRef, kWasmTop);
UNION(kWasmStringViewWtf8, kWasmAnyRef, kWasmTop);
UNION(kWasmStringViewWtf16, kWasmAnyRef, kWasmTop);
UNION(kWasmNullFuncRef, kWasmEqRef, kWasmTop);
INTERSECTION(kWasmExternRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmStructRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmI31Ref.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmExternRef, kWasmFuncRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmStructRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmNullExternRef, kWasmExternRef, kWasmNullExternRef);
INTERSECTION(kWasmNullExternRef, kWasmExternRef.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmRefNullExternString, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmRefNullExternString, kWasmAnyRef, kWasmBottom);
INTERSECTION(kWasmRefNullExternString, kWasmFuncRef.AsNonNull(),
kWasmBottom);
INTERSECTION(kWasmRefNullExternString, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmRefNullExternString, kWasmNullExternRef,
kWasmNullExternRef);
INTERSECTION(kWasmRefNullExternString.AsNonNull(), kWasmNullExternRef,
kWasmBottom);
INTERSECTION(kWasmRefNullExternString, kWasmExternRef,
kWasmRefNullExternString);
INTERSECTION(kWasmRefNullExternString, kWasmExternRef.AsNonNull(),
kWasmRefNullExternString.AsNonNull());
INTERSECTION(kWasmFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmStructRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmI31Ref.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmFuncRef, kWasmNullExternRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmEqRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmStructRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmI31Ref, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmArrayRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmFuncRef, kWasmNullFuncRef);
INTERSECTION(kWasmNullFuncRef, kWasmFuncRef.AsNonNull(), kWasmBottom);
INTERSECTION(kWasmNullFuncRef, kWasmNullExternRef, kWasmBottom);
INTERSECTION(kWasmEqRef, kWasmStructRef, kWasmStructRef);
INTERSECTION(kWasmEqRef, kWasmI31Ref, kWasmI31Ref);
INTERSECTION(kWasmEqRef, kWasmArrayRef, kWasmArrayRef);
INTERSECTION(kWasmEqRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmEqRef, kWasmFuncRef, kWasmBottom);
INTERSECTION(kWasmStructRef, kWasmI31Ref, kWasmNullRef);
INTERSECTION(kWasmStructRef, kWasmArrayRef, kWasmNullRef);
INTERSECTION(kWasmStructRef, kWasmNullRef, kWasmNullRef);
INTERSECTION(kWasmI31Ref, kWasmArrayRef, kWasmNullRef);
INTERSECTION(kWasmI31Ref.AsNonNull(), kWasmNullRef, kWasmBottom);
INTERSECTION(kWasmArrayRef.AsNonNull(), kWasmNullRef, kWasmBottom);
ValueType struct_type = refS(0);
ValueType array_type = refA(2);
ValueType function_type = refF(11);
// Abstract vs indexed types.
UNION(kWasmFuncRef, function_type, kWasmFuncRef);
UNION(kWasmFuncRef, struct_type, kWasmTop);
UNION(kWasmFuncRef, array_type, kWasmTop);
INTERSECTION(kWasmFuncRef, struct_type, kWasmBottom);
INTERSECTION(kWasmFuncRef, array_type, kWasmBottom);
INTERSECTION_M(kWasmFuncRef, function_type, function_type, module);
UNION(kWasmExnRef, struct_type, kWasmTop);
UNION(kWasmExnRef, array_type, kWasmTop);
UNION(kWasmExnRef, function_type, kWasmTop);
INTERSECTION(kWasmExnRef, struct_type, kWasmBottom);
INTERSECTION(kWasmExnRef, array_type, kWasmBottom);
INTERSECTION(kWasmExnRef, function_type, kWasmBottom);
UNION(kWasmNullFuncRef, function_type, function_type.AsNullable());
UNION(kWasmNullFuncRef, struct_type, kWasmTop);
UNION(kWasmNullFuncRef, array_type, kWasmTop);
INTERSECTION(kWasmNullFuncRef, struct_type, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, struct_type.AsNullable(), kWasmBottom);
INTERSECTION(kWasmNullFuncRef, array_type, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, array_type.AsNullable(), kWasmBottom);
INTERSECTION(kWasmNullFuncRef, function_type, kWasmBottom);
INTERSECTION(kWasmNullFuncRef, function_type.AsNullable(),
kWasmNullFuncRef);
UNION(kWasmEqRef, struct_type, kWasmEqRef);
UNION(kWasmEqRef, array_type, kWasmEqRef);
INTERSECTION(kWasmEqRef, struct_type, struct_type);
INTERSECTION(kWasmEqRef, array_type, array_type);
INTERSECTION(kWasmEqRef, function_type, kWasmBottom);
UNION(kWasmStructRef, struct_type, kWasmStructRef);
UNION(kWasmStructRef, array_type, kWasmEqRef);
UNION(kWasmStructRef, function_type, kWasmTop);
INTERSECTION_M(kWasmStructRef, struct_type, struct_type, module);
INTERSECTION(kWasmStructRef, array_type, kWasmBottom);
INTERSECTION(kWasmStructRef, function_type, kWasmBottom);
UNION(kWasmI31Ref, struct_type, kWasmEqRef);
UNION(kWasmI31Ref, array_type, kWasmEqRef);
INTERSECTION(kWasmI31Ref, struct_type, kWasmBottom);
INTERSECTION(kWasmI31Ref, array_type, kWasmBottom);
INTERSECTION(kWasmI31Ref, function_type, kWasmBottom);
UNION(kWasmArrayRef, struct_type, kWasmEqRef);
UNION(kWasmArrayRef, array_type, kWasmArrayRef);
UNION(kWasmArrayRef, function_type, kWasmTop);
INTERSECTION(kWasmArrayRef, struct_type, kWasmBottom);
INTERSECTION_M(kWasmArrayRef, array_type, array_type, module);
INTERSECTION(kWasmArrayRef, function_type, kWasmBottom);
UNION_M(kWasmNullRef, struct_type, struct_type.AsNullable(), module);
UNION_M(kWasmNullRef, array_type, array_type.AsNullable(), module);
UNION(kWasmNullRef, function_type, kWasmTop);
INTERSECTION(kWasmNullRef, struct_type, kWasmBottom);
INTERSECTION(kWasmNullRef, array_type, kWasmBottom);
INTERSECTION(kWasmNullRef, function_type, kWasmBottom);
INTERSECTION(kWasmNullRef, struct_type.AsNullable(), kWasmNullRef);
INTERSECTION(kWasmNullRef, array_type.AsNullable(), kWasmNullRef);
INTERSECTION(kWasmNullRef, function_type.AsNullable(), kWasmBottom);
UNION(struct_type, kWasmStringRef, kWasmAnyRef);
UNION(array_type, kWasmStringRef, kWasmAnyRef);
UNION(function_type, kWasmStringRef, kWasmTop);
UNION(struct_type, kWasmRefNullExternString, kWasmTop);
UNION(array_type, kWasmRefNullExternString, kWasmTop);
UNION(function_type, kWasmRefNullExternString, kWasmTop);
// Indexed types of different kinds.
UNION(struct_type, array_type, kRefEq);
INTERSECTION(struct_type, array_type, kWasmBottom);
INTERSECTION(struct_type, function_type, kWasmBottom);
INTERSECTION(array_type, function_type, kWasmBottom);
// Nullable vs. non-nullable.
UNION(struct_type, struct_type.AsNullable(), struct_type.AsNullable());
INTERSECTION(struct_type, struct_type.AsNullable(), struct_type);
UNION(kWasmStructRef, kWasmStructRef, kWasmStructRef);
INTERSECTION(kWasmStructRef, kWasmStructRef, kWasmStructRef);
// Concrete types of the same kind.
// Subtyping relation.
UNION_M(refNullS(4), refS(1), refNullS(1), module1);
INTERSECTION_M(refNullS(4), refS(1), refS(4), module1);
INTERSECTION_M(refNullS(1), refNullS(4), refNullS(4), module);
// Common ancestor.
UNION_M(refS(4), refS(31), refS(1), module1);
INTERSECTION(refS(4), refS(31), kWasmBottom);
// No common ancestor.
UNION(refA(6), refNullA(2), kWasmArrayRef);
INTERSECTION(refA(6), refNullA(2), kWasmBottom);
UNION(refS(0), refS(17), kWasmStructRef.AsNonNull());
INTERSECTION(refS(0), refS(17), kWasmBottom);
UNION(refF(10), refNullF(11), kWasmFuncRef);
INTERSECTION(refF(10), refNullF(11), kWasmBottom);
// Shared types
ValueType struct_shared = refS(40, kShared);
ValueType function_shared = refF(41, kShared);
UNION(struct_shared, struct_shared.AsNullable(),
struct_shared.AsNullable());
UNION(struct_shared, struct_type, kWasmTop);
UNION(struct_shared, function_shared, kWasmTop);
UNION(struct_shared, kRefI31Shared, kRefEqShared);
UNION(struct_shared, kRefAnyShared, kRefAnyShared);
UNION(struct_shared, kRefNoneShared, struct_shared);
UNION(struct_shared, kRefAny, kWasmTop);
INTERSECTION(struct_shared, struct_shared.AsNullable(), struct_shared);
INTERSECTION(struct_shared, struct_type, kWasmBottom);
INTERSECTION(struct_shared, function_shared, kWasmBottom);
INTERSECTION(struct_shared.AsNullable(), kRefNullI31Shared,
kRefNullNoneShared);
INTERSECTION(struct_shared, kRefAnyShared, struct_shared);
INTERSECTION(struct_shared.AsNullable(), kRefNullNoneShared,
kRefNullNoneShared);
INTERSECTION(struct_shared, kRefAny, kWasmBottom);
UNION(function_shared, kRefFuncShared, kRefFuncShared);
UNION(function_shared, kRefFunc, kWasmTop);
UNION(function_shared, kRefEqShared, kWasmTop);
UNION(function_shared, kRefNoFuncShared, function_shared);
UNION(function_shared, kRefNoExternShared, kWasmTop);
INTERSECTION(function_shared, kRefFuncShared, function_shared);
INTERSECTION(function_shared, kRefFunc, kWasmBottom);
INTERSECTION(function_shared, kRefEqShared, kWasmBottom);
INTERSECTION(function_shared.AsNullable(), kRefNullNoFuncShared,
kRefNullNoFuncShared);
INTERSECTION(function_shared, kRefNoExternShared, kWasmBottom);
}
// Generic test covering all kinds of always applicable rules (like
// commutativity).
const WasmModule* module = module2;
std::vector<ValueType> test_types;
test_types.reserve(arraysize(numeric_types) + arraysize(ref_types));
test_types.insert(test_types.end(), std::begin(numeric_types),
std::end(numeric_types));
test_types.insert(test_types.end(), std::begin(ref_types),
std::end(ref_types));
test_types.push_back(kWasmBottom);
test_types.push_back(kWasmTop);
for (const ValueType type_a : test_types) {
SCOPED_TRACE("a = " + type_a.name());
TypeInModule a(type_a, module1);
// Neutral elements: kWasmTop wrt. intersection, kWasmBottom wrt. union.
INTERSECTION(type_a, kWasmTop, type_a);
UNION(type_a, kWasmBottom, type_a);
// Absorbing element: kWasmTop wrt. union, kWasmBottom wrt. intersection.
UNION(type_a, kWasmTop, kWasmTop);
INTERSECTION(type_a, kWasmBottom, kWasmBottom);
UNION(type_a, type_a, type_a); // idempotency
INTERSECTION(type_a, type_a, type_a); // idempotency
for (const ValueType type_b : test_types) {
SCOPED_TRACE("b = " + type_b.name());
TypeInModule b(type_b, module2);
// There may not be any "cycles" in the type hierarchy.
if (IsSubtypeOf(a.type, b.type, module1) && a.type != b.type) {
EXPECT_FALSE(IsSubtypeOf(b.type, a.type, module1));
}
// The union of two types is always a super type of both types.
TypeInModule union_ab = Union(a, b);
EXPECT_TRUE(IsSubtypeOf(a.type, union_ab.type, module1));
EXPECT_TRUE(IsSubtypeOf(b.type, union_ab.type, module1));
// Test commutativity.
EXPECT_EQ(Union(a, b).type, Union(b, a).type);
EXPECT_EQ(Intersection(a, b).type, Intersection(b, a).type);
// If the union of a and b is b, then a is a subtype of b, so the
// intersection has to be a.
EXPECT_EQ(Union(a, b).type == b.type, Intersection(a, b).type == a.type);
for (const ValueType type_c : test_types) {
SCOPED_TRACE("c = " + type_c.name());
TypeInModule c(type_c, module1);
// Test associativity.
EXPECT_EQ(Union(a, Union(b, c)).type, Union(Union(a, b), c).type);
EXPECT_EQ(Intersection(a, Intersection(b, c)).type,
Intersection(Intersection(a, b), c).type);
// Test transitivity.
if (IsSubtypeOf(a.type, b.type, module1) &&
IsSubtypeOf(b.type, c.type, module1)) {
EXPECT_TRUE(IsSubtypeOf(a.type, c.type, module1));
}
// The Union(a, b) is the most specific supertype of a and b.
// Therefore there may not be any type c that is a supertype of a and b
// but not a supertype of c.
if (IsSubtypeOf(a.type, c.type, module1) &&
IsSubtypeOf(b.type, c.type, module1)) {
EXPECT_TRUE(IsSubtypeOf(union_ab.type, c.type, module1));
}
}
}
}
#undef SUBTYPE
#undef NOT_SUBTYPE
#undef SUBTYPE_IFF
#undef VALID_SUBTYPE
#undef NOT_VALID_SUBTYPE
#undef IDENTICAL
#undef DISTINCT
#undef UNION
#undef UNION_M
#undef INTERSECTION
#undef INTERSECTION_M
}
} // namespace v8::internal::wasm::subtyping_unittest

View File

@ -0,0 +1,728 @@
// Copyright 2018 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 "include/v8config.h"
#if V8_OS_LINUX || V8_OS_FREEBSD
#include <signal.h>
#include <ucontext.h>
#elif V8_OS_DARWIN
#include <signal.h>
#include <sys/ucontext.h>
#elif V8_OS_WIN
#include <windows.h>
#endif
#include "testing/gtest/include/gtest/gtest.h"
#if V8_OS_POSIX
#include "include/v8-wasm-trap-handler-posix.h"
#elif V8_OS_WIN
#include "include/v8-wasm-trap-handler-win.h"
#endif
#include "src/base/page-allocator.h"
#include "src/base/vector.h"
#include "src/codegen/assembler-inl.h"
#include "src/codegen/macro-assembler-inl.h"
#include "src/execution/simulator.h"
#include "src/objects/backing-store.h"
#include "src/trap-handler/trap-handler.h"
#include "src/utils/allocation.h"
#include "src/wasm/wasm-engine.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#if V8_TRAP_HANDLER_SUPPORTED
#if V8_HOST_ARCH_ARM64 && (!V8_OS_LINUX && !V8_OS_DARWIN && !V8_OS_WIN)
#error Unsupported platform
#endif
namespace v8 {
namespace internal {
namespace wasm {
namespace {
#if V8_HOST_ARCH_X64
constexpr Register scratch = r10;
#endif
bool g_test_handler_executed = false;
#if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD
struct sigaction g_old_segv_action;
struct sigaction g_old_other_action; // FPE or TRAP, depending on x64 or arm64.
struct sigaction g_old_bus_action; // We get SIGBUS on Mac sometimes.
#elif V8_OS_WIN
void* g_registered_handler = nullptr;
#endif
// Flag to indicate if the test handler should call the trap handler as a first
// chance handler.
bool g_use_as_first_chance_handler = false;
} // namespace
#define __ masm.
enum TrapHandlerStyle : int {
// The test uses the default trap handler of V8.
kDefault = 0,
// The test installs the trap handler callback in its own test handler.
kCallback = 1
};
std::string PrintTrapHandlerTestParam(
::testing::TestParamInfo<TrapHandlerStyle> info) {
switch (info.param) {
case kDefault:
return "DefaultTrapHandler";
case kCallback:
return "Callback";
}
UNREACHABLE();
}
class TrapHandlerTest : public TestWithIsolate,
public ::testing::WithParamInterface<TrapHandlerStyle> {
protected:
void SetUp() override {
InstallFallbackHandler();
SetupTrapHandler(GetParam());
backing_store_ = BackingStore::AllocateWasmMemory(
i_isolate(), 1, 1, WasmMemoryFlag::kWasmMemory32,
SharedFlag::kNotShared);
CHECK(backing_store_);
EXPECT_TRUE(backing_store_->has_guard_regions());
// The allocated backing store ends with a guard page.
crash_address_ = reinterpret_cast<Address>(backing_store_->buffer_start()) +
backing_store_->byte_length() + 32;
// Allocate a buffer for the generated code.
buffer_ = AllocateAssemblerBuffer(AssemblerBase::kDefaultBufferSize,
GetRandomMmapAddr());
}
void InstallFallbackHandler() {
#if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD
// Set up a signal handler to recover from the expected crash.
struct sigaction action;
action.sa_sigaction = SignalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
// SIGSEGV happens for wasm oob memory accesses on Linux.
EXPECT_EQ(0, sigaction(SIGSEGV, &action, &g_old_segv_action));
// SIGBUS happens for wasm oob memory accesses on macOS.
EXPECT_EQ(0, sigaction(SIGBUS, &action, &g_old_bus_action));
#if V8_HOST_ARCH_X64
// SIGFPE to simulate crashes which are not handled by the trap handler.
EXPECT_EQ(0, sigaction(SIGFPE, &action, &g_old_other_action));
#elif V8_HOST_ARCH_ARM64
// SIGTRAP to simulate crashes which are not handled by the trap handler.
EXPECT_EQ(0, sigaction(SIGTRAP, &action, &g_old_other_action));
#elif V8_HOST_ARCH_LOONG64
// SIGTRAP to simulate crashes which are not handled by the trap handler.
EXPECT_EQ(0, sigaction(SIGTRAP, &action, &g_old_other_action));
#elif V8_HOST_ARCH_RISCV64
// SIGTRAP to simulate crashes which are not handled by the trap handler.
EXPECT_EQ(0, sigaction(SIGTRAP, &action, &g_old_other_action));
#else
#error Unsupported platform
#endif
#elif V8_OS_WIN
g_registered_handler =
AddVectoredExceptionHandler(/*first=*/0, TestHandler);
#endif
}
void TearDown() override {
// We should always have left wasm code.
EXPECT_TRUE(!GetThreadInWasmFlag());
buffer_.reset();
recovery_buffer_.reset();
backing_store_.reset();
// Clean up the trap handler
trap_handler::RemoveTrapHandler();
if (!g_test_handler_executed) {
#if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD
// The test handler cleans up the signal handler setup in the test. If the
// test handler was not called, we have to do the cleanup ourselves.
EXPECT_EQ(0, sigaction(SIGSEGV, &g_old_segv_action, nullptr));
EXPECT_EQ(0, sigaction(SIGBUS, &g_old_bus_action, nullptr));
#if V8_HOST_ARCH_X64
EXPECT_EQ(0, sigaction(SIGFPE, &g_old_other_action, nullptr));
#elif V8_HOST_ARCH_ARM64
EXPECT_EQ(0, sigaction(SIGTRAP, &g_old_other_action, nullptr));
#elif V8_HOST_ARCH_LOONG64
EXPECT_EQ(0, sigaction(SIGTRAP, &g_old_other_action, nullptr));
#elif V8_HOST_ARCH_RISCV64
EXPECT_EQ(0, sigaction(SIGTRAP, &g_old_other_action, nullptr));
#else
#error Unsupported platform
#endif
#elif V8_OS_WIN
RemoveVectoredExceptionHandler(g_registered_handler);
g_registered_handler = nullptr;
#endif
}
}
static void RecoveryHandler() { return; }
#if V8_OS_LINUX || V8_OS_DARWIN || V8_OS_FREEBSD
static void SignalHandler(int signal, siginfo_t* info, void* context) {
if (g_use_as_first_chance_handler) {
if (v8::TryHandleWebAssemblyTrapPosix(signal, info, context)) {
return;
}
}
// Reset the signal handler, to avoid that this signal handler is called
// repeatedly.
sigaction(SIGSEGV, &g_old_segv_action, nullptr);
#if V8_HOST_ARCH_X64
sigaction(SIGFPE, &g_old_other_action, nullptr);
#elif V8_HOST_ARCH_ARM64
sigaction(SIGTRAP, &g_old_other_action, nullptr);
#elif V8_HOST_ARCH_LOONG64
sigaction(SIGTRAP, &g_old_other_action, nullptr);
#elif V8_HOST_ARCH_RISCV64
sigaction(SIGTRAP, &g_old_other_action, nullptr);
#else
#error Unsupported platform
#endif
sigaction(SIGBUS, &g_old_bus_action, nullptr);
g_test_handler_executed = true;
// Set the $rip to the recovery code.
ucontext_t* uc = reinterpret_cast<ucontext_t*>(context);
uintptr_t recovery_handler = reinterpret_cast<uintptr_t>(&RecoveryHandler);
#if V8_OS_DARWIN && V8_HOST_ARCH_ARM64
uc->uc_mcontext->__ss.__pc = recovery_handler;
#elif V8_OS_DARWIN && V8_HOST_ARCH_X64
uc->uc_mcontext->__ss.__rip = recovery_handler;
#elif V8_OS_LINUX && V8_HOST_ARCH_ARM64
uc->uc_mcontext.pc = recovery_handler;
#elif V8_OS_LINUX && V8_HOST_ARCH_LOONG64
uc->uc_mcontext.__pc = recovery_handler;
#elif V8_OS_LINUX && V8_HOST_ARCH_RISCV64
uc->uc_mcontext.__gregs[REG_PC] = recovery_handler;
#elif V8_OS_LINUX && V8_HOST_ARCH_X64
uc->uc_mcontext.gregs[REG_RIP] = recovery_handler;
#elif V8_OS_FREEBSD
uc->uc_mcontext.mc_rip = recovery_handler;
#else
#error Unsupported platform
#endif
}
#endif
#if V8_OS_WIN
static LONG WINAPI TestHandler(EXCEPTION_POINTERS* exception) {
if (g_use_as_first_chance_handler) {
if (v8::TryHandleWebAssemblyTrapWindows(exception)) {
return EXCEPTION_CONTINUE_EXECUTION;
}
}
RemoveVectoredExceptionHandler(g_registered_handler);
g_registered_handler = nullptr;
g_test_handler_executed = true;
uintptr_t recovery_handler = reinterpret_cast<uintptr_t>(&RecoveryHandler);
#if V8_HOST_ARCH_X64
exception->ContextRecord->Rip = recovery_handler;
#elif V8_HOST_ARCH_ARM64
exception->ContextRecord->Pc = recovery_handler;
#else
#error Unsupported architecture
#endif // V8_HOST_ARCH_X64
return EXCEPTION_CONTINUE_EXECUTION;
}
#endif
void SetupTrapHandler(TrapHandlerStyle style) {
bool use_default_handler = style == kDefault;
g_use_as_first_chance_handler = !use_default_handler;
CHECK(v8::V8::EnableWebAssemblyTrapHandler(use_default_handler));
}
public:
void GenerateSetThreadInWasmFlagCode(MacroAssembler* masm) {
#if V8_HOST_ARCH_X64
masm->Move(scratch,
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_,
RelocInfo::NO_INFO);
masm->movl(MemOperand(scratch, 0), Immediate(1));
#elif V8_HOST_ARCH_ARM64
UseScratchRegisterScope temps(masm);
Register addr = temps.AcquireX();
masm->Mov(addr,
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_);
Register one = temps.AcquireX();
masm->Mov(one, 1);
masm->Str(one, MemOperand(addr));
#elif V8_HOST_ARCH_LOONG64
UseScratchRegisterScope temps(masm);
Register addr = temps.Acquire();
masm->li(
addr,
static_cast<int64_t>(
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_));
Register one = temps.Acquire();
masm->li(one, 1);
masm->St_d(one, MemOperand(addr, 0));
#elif V8_HOST_ARCH_RISCV64
UseScratchRegisterScope temps(masm);
Register addr = temps.Acquire();
masm->li(
addr,
static_cast<int64_t>(
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_));
Register one = temps.Acquire();
masm->li(one, 1);
masm->StoreWord(one, MemOperand(addr, 0));
#else
#error Unsupported platform
#endif
}
void GenerateResetThreadInWasmFlagCode(MacroAssembler* masm) {
#if V8_HOST_ARCH_X64
masm->Move(scratch,
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_,
RelocInfo::NO_INFO);
masm->movl(MemOperand(scratch, 0), Immediate(0));
#elif V8_HOST_ARCH_ARM64
UseScratchRegisterScope temps(masm);
Register addr = temps.AcquireX();
masm->Mov(addr,
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_);
masm->Str(xzr, MemOperand(addr));
#elif V8_HOST_ARCH_LOONG64
UseScratchRegisterScope temps(masm);
Register addr = temps.Acquire();
masm->li(
addr,
static_cast<int64_t>(
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_));
masm->St_d(zero_reg, MemOperand(addr, 0));
#elif V8_HOST_ARCH_RISCV64
UseScratchRegisterScope temps(masm);
Register addr = temps.Acquire();
masm->li(
addr,
static_cast<int64_t>(
i_isolate()->thread_local_top()->thread_in_wasm_flag_address_));
masm->StoreWord(zero_reg, MemOperand(addr, 0));
#else
#error Unsupported platform
#endif
}
bool GetThreadInWasmFlag() {
return *reinterpret_cast<int*>(
trap_handler::GetThreadInWasmThreadLocalAddress());
}
// Execute the code in buffer.
void ExecuteBuffer() {
buffer_->MakeExecutable();
GeneratedCode<void>::FromAddress(
i_isolate(), reinterpret_cast<Address>(buffer_->start()))
.Call();
EXPECT_FALSE(g_test_handler_executed);
}
// Execute the code in buffer. We expect a crash which we recover from in the
// test handler.
void ExecuteExpectCrash(TestingAssemblerBuffer* buffer,
bool check_wasm_flag = true) {
EXPECT_FALSE(g_test_handler_executed);
buffer->MakeExecutable();
GeneratedCode<void>::FromAddress(i_isolate(),
reinterpret_cast<Address>(buffer->start()))
.Call();
EXPECT_TRUE(g_test_handler_executed);
g_test_handler_executed = false;
if (check_wasm_flag) {
EXPECT_FALSE(GetThreadInWasmFlag());
}
}
bool test_handler_executed() { return g_test_handler_executed; }
// The backing store used for testing the trap handler.
std::unique_ptr<BackingStore> backing_store_;
// Address within the guard region of the wasm memory. Accessing this memory
// address causes a signal or exception.
Address crash_address_;
// Buffer for generated code.
std::unique_ptr<TestingAssemblerBuffer> buffer_;
// Buffer for the code for the landing pad of the test handler.
std::unique_ptr<TestingAssemblerBuffer> recovery_buffer_;
};
// TODO(almuthanna): These tests were skipped because they cause a crash when
// they are ran on Fuchsia. This issue should be solved later on
// Ticket: https://crbug.com/1028617
#if !defined(V8_TARGET_OS_FUCHSIA)
namespace {
void (*landing_pad)() = nullptr;
DISABLE_CFI_ICALL void LandingPadTrampoline() { landing_pad(); }
} // namespace
TEST_P(TrapHandlerTest, TestTrapHandlerRecovery) {
// Test that the wasm trap handler can recover a memory access violation in
// wasm code (we fake the wasm code and the access violation).
MacroAssembler masm(i_isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer_->CreateView());
#if V8_HOST_ARCH_X64
GenerateSetThreadInWasmFlagCode(&masm);
__ Move(scratch, crash_address_, RelocInfo::NO_INFO);
uint32_t crash_offset = __ pc_offset();
__ testl(MemOperand(scratch, 0), Immediate(1));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_ARM64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.AcquireX();
__ Mov(scratch, crash_address_);
uint32_t crash_offset = __ pc_offset();
__ Ldr(scratch, MemOperand(scratch));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_LOONG64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ Ld_d(scratch, MemOperand(scratch, 0));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_RISCV64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ LoadWord(scratch, MemOperand(scratch, 0));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#else
#error Unsupported platform
#endif
__ Ret();
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
trap_handler::ProtectedInstructionData protected_instruction{crash_offset};
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction);
landing_pad =
reinterpret_cast<void (*)()>(buffer_->start() + recovery_offset);
trap_handler::SetLandingPad(
reinterpret_cast<uintptr_t>(&LandingPadTrampoline));
ExecuteBuffer();
trap_handler::SetLandingPad(0);
}
TEST_P(TrapHandlerTest, TestReleaseHandlerData) {
// Test that after we release handler data in the trap handler, it cannot
// recover from the specific memory access violation anymore.
MacroAssembler masm(i_isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer_->CreateView());
#if V8_HOST_ARCH_X64
GenerateSetThreadInWasmFlagCode(&masm);
__ Move(scratch, crash_address_, RelocInfo::NO_INFO);
uint32_t crash_offset = __ pc_offset();
__ testl(MemOperand(scratch, 0), Immediate(1));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_ARM64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.AcquireX();
__ Mov(scratch, crash_address_);
uint32_t crash_offset = __ pc_offset();
__ Ldr(scratch, MemOperand(scratch));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_LOONG64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ Ld_d(scratch, MemOperand(scratch, 0));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_RISCV64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ LoadWord(scratch, MemOperand(scratch, 0));
uint32_t recovery_offset = __ pc_offset();
GenerateResetThreadInWasmFlagCode(&masm);
#else
#error Unsupported platform
#endif
__ Ret();
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
trap_handler::ProtectedInstructionData protected_instruction{crash_offset};
int handler_id = trap_handler::RegisterHandlerData(
reinterpret_cast<Address>(desc.buffer), desc.instr_size, 1,
&protected_instruction);
landing_pad =
reinterpret_cast<void (*)()>(buffer_->start() + recovery_offset);
trap_handler::SetLandingPad(
reinterpret_cast<uintptr_t>(&LandingPadTrampoline));
ExecuteBuffer();
// Deregister from the trap handler. The trap handler should not do the
// recovery now.
trap_handler::ReleaseHandlerData(handler_id);
ExecuteExpectCrash(buffer_.get());
trap_handler::SetLandingPad(0);
}
TEST_P(TrapHandlerTest, TestNoThreadInWasmFlag) {
// That that if the thread_in_wasm flag is not set, the trap handler does not
// get active.
MacroAssembler masm(i_isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer_->CreateView());
#if V8_HOST_ARCH_X64
__ Move(scratch, crash_address_, RelocInfo::NO_INFO);
uint32_t crash_offset = __ pc_offset();
__ testl(MemOperand(scratch, 0), Immediate(1));
#elif V8_HOST_ARCH_ARM64
UseScratchRegisterScope temps(&masm);
Register scratch = temps.AcquireX();
__ Mov(scratch, crash_address_);
uint32_t crash_offset = __ pc_offset();
__ Ldr(scratch, MemOperand(scratch));
#elif V8_HOST_ARCH_LOONG64
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ Ld_d(scratch, MemOperand(scratch, 0));
#elif V8_HOST_ARCH_RISCV64
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ LoadWord(scratch, MemOperand(scratch, 0));
#else
#error Unsupported platform
#endif
__ Ret();
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
trap_handler::ProtectedInstructionData protected_instruction{crash_offset};
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction);
ExecuteExpectCrash(buffer_.get());
}
TEST_P(TrapHandlerTest, TestCrashInWasmNoProtectedInstruction) {
// Test that if the crash in wasm happened at an instruction which is not
// protected, then the trap handler does not handle it.
MacroAssembler masm(i_isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer_->CreateView());
#if V8_HOST_ARCH_X64
GenerateSetThreadInWasmFlagCode(&masm);
uint32_t no_crash_offset = __ pc_offset();
__ Move(scratch, crash_address_, RelocInfo::NO_INFO);
__ testl(MemOperand(scratch, 0), Immediate(1));
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_ARM64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.AcquireX();
uint32_t no_crash_offset = __ pc_offset();
__ Mov(scratch, crash_address_);
__ Ldr(scratch, MemOperand(scratch));
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_LOONG64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
uint32_t no_crash_offset = __ pc_offset();
__ li(scratch, static_cast<int64_t>(crash_address_));
__ Ld_d(scratch, MemOperand(scratch, 0));
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_RISCV64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
uint32_t no_crash_offset = __ pc_offset();
__ li(scratch, static_cast<int64_t>(crash_address_));
__ LoadWord(scratch, MemOperand(scratch, 0));
GenerateResetThreadInWasmFlagCode(&masm);
#else
#error Unsupported platform
#endif
__ Ret();
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
trap_handler::ProtectedInstructionData protected_instruction{no_crash_offset};
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction);
ExecuteExpectCrash(buffer_.get());
}
TEST_P(TrapHandlerTest, TestCrashInWasmWrongCrashType) {
// Test that if the crash reason is not a memory access violation, then the
// wasm trap handler does not handle it.
MacroAssembler masm(i_isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer_->CreateView());
#if V8_HOST_ARCH_X64
GenerateSetThreadInWasmFlagCode(&masm);
__ xorq(scratch, scratch);
uint32_t crash_offset = __ pc_offset();
__ divq(scratch);
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_ARM64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
uint32_t crash_offset = __ pc_offset();
__ Trap();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_LOONG64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
uint32_t crash_offset = __ pc_offset();
__ Trap();
GenerateResetThreadInWasmFlagCode(&masm);
#elif V8_HOST_ARCH_RISCV64
GenerateSetThreadInWasmFlagCode(&masm);
UseScratchRegisterScope temps(&masm);
uint32_t crash_offset = __ pc_offset();
__ Trap();
GenerateResetThreadInWasmFlagCode(&masm);
#else
#error Unsupported platform
#endif
__ Ret();
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
trap_handler::ProtectedInstructionData protected_instruction{crash_offset};
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction);
#if V8_OS_POSIX
// On Posix, the V8 default trap handler does not register for SIGFPE,
// therefore the thread-in-wasm flag is never reset in this test. We
// therefore do not check the value of this flag.
bool check_wasm_flag = GetParam() != kDefault;
#elif V8_OS_WIN
// On Windows, the trap handler returns immediately if not an exception of
// interest.
bool check_wasm_flag = false;
#else
bool check_wasm_flag = true;
#endif
ExecuteExpectCrash(buffer_.get(), check_wasm_flag);
if (!check_wasm_flag) {
// Reset the thread-in-wasm flag because it was probably not reset in the
// trap handler.
*trap_handler::GetThreadInWasmThreadLocalAddress() = 0;
}
}
#endif
class CodeRunner : public v8::base::Thread {
public:
CodeRunner(TrapHandlerTest* test, TestingAssemblerBuffer* buffer)
: Thread(Options("CodeRunner")), test_(test), buffer_(buffer) {}
void Run() override { test_->ExecuteExpectCrash(buffer_); }
private:
TrapHandlerTest* test_;
TestingAssemblerBuffer* buffer_;
};
// TODO(almuthanna): This test was skipped because it causes a crash when it is
// ran on Fuchsia. This issue should be solved later on
// Ticket: https://crbug.com/1028617
#if !defined(V8_TARGET_OS_FUCHSIA)
TEST_P(TrapHandlerTest, TestCrashInOtherThread) {
// Test setup:
// The current thread enters wasm land (sets the thread_in_wasm flag)
// A second thread crashes at a protected instruction without having the flag
// set.
MacroAssembler masm(i_isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer_->CreateView());
#if V8_HOST_ARCH_X64
__ Move(scratch, crash_address_, RelocInfo::NO_INFO);
uint32_t crash_offset = __ pc_offset();
__ testl(MemOperand(scratch, 0), Immediate(1));
#elif V8_HOST_ARCH_ARM64
UseScratchRegisterScope temps(&masm);
Register scratch = temps.AcquireX();
__ Mov(scratch, crash_address_);
uint32_t crash_offset = __ pc_offset();
__ Ldr(scratch, MemOperand(scratch));
#elif V8_HOST_ARCH_LOONG64
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ Ld_d(scratch, MemOperand(scratch, 0));
#elif V8_HOST_ARCH_RISCV64
UseScratchRegisterScope temps(&masm);
Register scratch = temps.Acquire();
__ li(scratch, static_cast<int64_t>(crash_address_));
uint32_t crash_offset = __ pc_offset();
__ LoadWord(scratch, MemOperand(scratch, 0));
#else
#error Unsupported platform
#endif
__ Ret();
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
trap_handler::ProtectedInstructionData protected_instruction{crash_offset};
trap_handler::RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction);
CodeRunner runner(this, buffer_.get());
EXPECT_FALSE(GetThreadInWasmFlag());
// Set the thread-in-wasm flag manually in this thread.
*trap_handler::GetThreadInWasmThreadLocalAddress() = 1;
EXPECT_TRUE(runner.Start());
runner.Join();
EXPECT_TRUE(GetThreadInWasmFlag());
// Reset the thread-in-wasm flag.
*trap_handler::GetThreadInWasmThreadLocalAddress() = 0;
}
#endif
#if !V8_OS_FUCHSIA
INSTANTIATE_TEST_SUITE_P(Traps, TrapHandlerTest,
::testing::Values(kDefault, kCallback),
PrintTrapHandlerTestParam);
#endif // !V8_OS_FUCHSIA
#undef __
} // namespace wasm
} // namespace internal
} // namespace v8
#endif

View File

@ -0,0 +1,70 @@
// 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 "include/v8-initialization.h"
#include "src/trap-handler/trap-handler.h"
#include "testing/gtest/include/gtest/gtest.h"
#if V8_OS_POSIX
#include <setjmp.h>
#include <signal.h>
#endif
namespace {
#if V8_TRAP_HANDLER_SUPPORTED
void CrashOnPurpose() { *reinterpret_cast<volatile int*>(42); }
// When using V8::RegisterDefaultSignalHandler, we save the old one to fall back
// on if V8 doesn't handle the signal. This allows tools like ASan to register a
// handler early on during the process startup and still generate stack traces
// on failures.
class SignalHandlerFallbackTest : public ::testing::Test {
protected:
void SetUp() override {
struct sigaction action;
action.sa_sigaction = SignalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &action, &old_segv_action_);
sigaction(SIGBUS, &action, &old_bus_action_);
}
void TearDown() override {
// be a good citizen and restore the old signal handler.
sigaction(SIGSEGV, &old_segv_action_, nullptr);
sigaction(SIGBUS, &old_bus_action_, nullptr);
}
static sigjmp_buf continuation_;
private:
static void SignalHandler(int signal, siginfo_t* info, void*) {
siglongjmp(continuation_, 1);
}
struct sigaction old_segv_action_;
struct sigaction old_bus_action_; // We get SIGBUS on Mac sometimes.
};
sigjmp_buf SignalHandlerFallbackTest::continuation_;
TEST_F(SignalHandlerFallbackTest, DoTest) {
const int save_sigs = 1;
if (!sigsetjmp(continuation_, save_sigs)) {
constexpr bool kUseDefaultTrapHandler = true;
EXPECT_TRUE(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultTrapHandler));
CrashOnPurpose();
FAIL();
} else {
// Our signal handler ran.
v8::internal::trap_handler::RemoveTrapHandler();
SUCCEED();
return;
}
FAIL();
}
#endif
} // namespace

View File

@ -0,0 +1,185 @@
// Copyright 2021 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/trap-handler/trap-handler-simulator.h"
#include <cstdint>
#include "include/v8-initialization.h"
#include "src/codegen/macro-assembler-inl.h"
#include "src/execution/simulator.h"
#include "src/trap-handler/trap-handler.h"
#include "test/common/assembler-tester.h"
#include "test/unittests/test-utils.h"
#ifdef V8_TRAP_HANDLER_VIA_SIMULATOR
namespace v8 {
namespace internal {
namespace trap_handler {
constexpr uintptr_t kFakePc = 11;
class SimulatorTrapHandlerTest : public TestWithIsolate {
public:
~SimulatorTrapHandlerTest() {
if (inaccessible_memory_) {
auto* page_allocator = GetArrayBufferPageAllocator();
CHECK(page_allocator->FreePages(inaccessible_memory_,
page_allocator->AllocatePageSize()));
}
}
void SetThreadInWasm() {
EXPECT_EQ(0, *thread_in_wasm);
*thread_in_wasm = 1;
}
void ResetThreadInWasm() {
EXPECT_EQ(1, *thread_in_wasm);
*thread_in_wasm = 0;
}
uintptr_t InaccessibleMemoryPtr() {
if (!inaccessible_memory_) {
auto* page_allocator = GetArrayBufferPageAllocator();
size_t page_size = page_allocator->AllocatePageSize();
inaccessible_memory_ =
reinterpret_cast<uint8_t*>(page_allocator->AllocatePages(
nullptr, /* size */ page_size, /* align */ page_size,
PageAllocator::kNoAccess));
CHECK_NOT_NULL(inaccessible_memory_);
}
return reinterpret_cast<uintptr_t>(inaccessible_memory_);
}
int* thread_in_wasm = trap_handler::GetThreadInWasmThreadLocalAddress();
private:
uint8_t* inaccessible_memory_ = nullptr;
};
TEST_F(SimulatorTrapHandlerTest, ProbeMemorySuccess) {
int x = 47;
EXPECT_EQ(0u, ProbeMemory(reinterpret_cast<uintptr_t>(&x), kFakePc));
}
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryFailNullptr) {
constexpr uintptr_t kNullAddress = 0;
EXPECT_DEATH_IF_SUPPORTED(ProbeMemory(kNullAddress, kFakePc), "");
}
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryFailInaccessible) {
EXPECT_DEATH_IF_SUPPORTED(ProbeMemory(InaccessibleMemoryPtr(), kFakePc), "");
}
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryFailWhileInWasm) {
// Test that we still crash if the trap handler is set up and the "thread in
// wasm" flag is set, but the PC is not registered as a protected instruction.
constexpr bool kUseDefaultHandler = true;
CHECK(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultHandler));
SetThreadInWasm();
EXPECT_DEATH_IF_SUPPORTED(ProbeMemory(InaccessibleMemoryPtr(), kFakePc), "");
}
namespace {
uintptr_t v8_landing_pad() {
EmbeddedData embedded_data = EmbeddedData::FromBlob();
return embedded_data.InstructionStartOf(Builtin::kWasmTrapHandlerLandingPad);
}
} // namespace
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryWithTrapHandled) {
constexpr bool kUseDefaultHandler = true;
CHECK(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultHandler));
ProtectedInstructionData fake_protected_instruction{kFakePc};
int handler_data_index =
RegisterHandlerData(0, 128, 1, &fake_protected_instruction);
SetThreadInWasm();
EXPECT_EQ(v8_landing_pad(), ProbeMemory(InaccessibleMemoryPtr(), kFakePc));
// Reset everything.
ResetThreadInWasm();
ReleaseHandlerData(handler_data_index);
RemoveTrapHandler();
}
TEST_F(SimulatorTrapHandlerTest, ProbeMemoryWithLandingPad) {
EXPECT_EQ(0u, GetRecoveredTrapCount());
// Test that the trap handler can recover a memory access violation in
// wasm code (we fake the wasm code and the access violation).
std::unique_ptr<TestingAssemblerBuffer> buffer = AllocateAssemblerBuffer();
MacroAssembler masm(isolate(), AssemblerOptions{}, CodeObjectRequired::kNo,
buffer->CreateView());
#ifdef V8_TARGET_ARCH_ARM64
constexpr Register scratch = x0;
// Generate an illegal memory access.
masm.Mov(scratch, InaccessibleMemoryPtr());
uint32_t crash_offset = masm.pc_offset();
masm.Str(scratch, MemOperand(scratch, 0)); // load from inaccessible memory.
uint32_t recovery_offset = masm.pc_offset();
// Return.
masm.Ret();
#elif V8_TARGET_ARCH_LOONG64
constexpr Register scratch = a0;
// Generate an illegal memory access.
masm.li(scratch, static_cast<int64_t>(InaccessibleMemoryPtr()));
uint32_t crash_offset = masm.pc_offset();
masm.St_d(scratch, MemOperand(scratch, 0)); // load from inaccessible memory.
uint32_t recovery_offset = masm.pc_offset();
// Return.
masm.Ret();
#elif V8_TARGET_ARCH_RISCV64
constexpr Register scratch = a0;
// Generate an illegal memory access.
masm.li(scratch, static_cast<int64_t>(InaccessibleMemoryPtr()));
uint32_t crash_offset = masm.pc_offset();
masm.StoreWord(scratch,
MemOperand(scratch, 0)); // load from inaccessible memory.
uint32_t recovery_offset = masm.pc_offset();
// Return.
masm.Ret();
#else
#error Unsupported platform
#endif
CodeDesc desc;
masm.GetCode(static_cast<LocalIsolate*>(nullptr), &desc);
constexpr bool kUseDefaultHandler = true;
CHECK(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultHandler));
ProtectedInstructionData protected_instruction{crash_offset};
int handler_data_index =
RegisterHandlerData(reinterpret_cast<Address>(desc.buffer),
desc.instr_size, 1, &protected_instruction);
// Now execute the code.
buffer->MakeExecutable();
GeneratedCode<void> code = GeneratedCode<void>::FromAddress(
i_isolate(), reinterpret_cast<Address>(desc.buffer));
trap_handler::SetLandingPad(reinterpret_cast<uintptr_t>(buffer->start()) +
recovery_offset);
SetThreadInWasm();
code.Call();
ResetThreadInWasm();
ReleaseHandlerData(handler_data_index);
RemoveTrapHandler();
trap_handler::SetLandingPad(0);
EXPECT_EQ(1u, GetRecoveredTrapCount());
}
} // namespace trap_handler
} // namespace internal
} // namespace v8
#endif // V8_TRAP_HANDLER_VIA_SIMULATOR

View File

@ -0,0 +1,95 @@
// Copyright 2018 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 <windows.h>
#include "include/v8-initialization.h"
#include "include/v8-platform.h"
#include "src/base/page-allocator.h"
#include "src/trap-handler/trap-handler.h"
#include "src/utils/allocation.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
#if V8_TRAP_HANDLER_SUPPORTED
bool g_handler_got_executed = false;
// The start address of the virtual memory we use to cause an exception.
i::Address g_start_address;
// When using V8::EnableWebAssemblyTrapHandler, we save the old one to fall back
// on if V8 doesn't handle the exception. This allows tools like ASan to
// register a handler early on during the process startup and still generate
// stack traces on failures.
class ExceptionHandlerFallbackTest : public v8::TestWithPlatform {
protected:
void SetUp() override {
// Register this handler as the last handler.
registered_handler_ = AddVectoredExceptionHandler(/*first=*/0, TestHandler);
CHECK_NOT_NULL(registered_handler_);
v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
// We only need a single page.
size_t size = page_allocator->AllocatePageSize();
void* hint = page_allocator->GetRandomMmapAddr();
i::VirtualMemory mem(page_allocator, size, hint, size);
g_start_address = mem.address();
// Set the permissions of the memory to no-access.
CHECK(mem.SetPermissions(g_start_address, size,
v8::PageAllocator::kNoAccess));
mem_ = std::move(mem);
}
void WriteToTestMemory(int value) {
*reinterpret_cast<volatile int*>(g_start_address) = value;
}
int ReadFromTestMemory() {
return *reinterpret_cast<volatile int*>(g_start_address);
}
void TearDown() override {
// be a good citizen and remove the exception handler.
ULONG result = RemoveVectoredExceptionHandler(registered_handler_);
EXPECT_TRUE(result);
}
private:
static LONG WINAPI TestHandler(EXCEPTION_POINTERS* exception) {
g_handler_got_executed = true;
v8::PageAllocator* page_allocator = i::GetPlatformPageAllocator();
// Make the allocated memory accessible so that from now on memory accesses
// do not cause an exception anymore.
EXPECT_TRUE(i::SetPermissions(page_allocator, g_start_address,
page_allocator->AllocatePageSize(),
v8::PageAllocator::kReadWrite));
// The memory access should work now, we can continue execution.
return EXCEPTION_CONTINUE_EXECUTION;
}
i::VirtualMemory mem_;
void* registered_handler_;
};
TEST_F(ExceptionHandlerFallbackTest, DoTest) {
constexpr bool kUseDefaultTrapHandler = true;
EXPECT_TRUE(v8::V8::EnableWebAssemblyTrapHandler(kUseDefaultTrapHandler));
// In the original test setup the test memory is protected against any kind of
// access. Therefore the access here causes an access violation exception,
// which should be caught by the exception handler we install above. In the
// exception handler we change the permission of the test memory to make it
// accessible, and then return from the exception handler to execute the
// memory access again. This time we expect the memory access to work.
constexpr int test_value = 42;
WriteToTestMemory(test_value);
EXPECT_EQ(test_value, ReadFromTestMemory());
EXPECT_TRUE(g_handler_got_executed);
v8::internal::trap_handler::RemoveTrapHandler();
}
#endif
} // namespace

View File

@ -0,0 +1,286 @@
// Copyright 2024 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 "include/v8-json.h"
#include "src/utils/ostreams.h"
#include "src/wasm/canonical-types.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/wasm-module-builder.h"
#include "test/unittests/fuzztest.h"
#include "test/unittests/test-utils.h"
namespace v8::internal::wasm {
// Introduce a separate representation for recursion groups to be used by this
// fuzz test.
namespace test {
// Provide operator<<(std::ostream&, T> for types with T.Print(std::ostream*).
template <typename T>
requires requires(const T& t, std::ostream& os) { t.Print(os); }
std::ostream& operator<<(std::ostream& os, const T& t) {
t.Print(os);
return os;
}
struct FieldType {
ValueType value_type;
bool mutability;
void Print(std::ostream& os) const {
os << (mutability ? "mut " : "") << value_type;
}
};
struct StructType {
std::vector<FieldType> field_types;
void BuildType(Zone* zone, WasmModuleBuilder* builder) const {
// TODO(381687256): Populate final and supertype.
constexpr bool kNotFinal = false;
constexpr ModuleTypeIndex kNoSupertype = ModuleTypeIndex::Invalid();
// If this fails, we need to constrain vector sizes in the fuzzer domains.
DCHECK_GE(kMaxUInt32, field_types.size());
uint32_t field_count = static_cast<uint32_t>(field_types.size());
// Offsets are not used and never accessed, hence we can pass nullptr.
constexpr uint32_t* kNoOffsets = nullptr;
ValueType* reps = zone->AllocateArray<ValueType>(field_count);
bool* mutabilities = zone->AllocateArray<bool>(field_count);
for (uint32_t i = 0; i < field_count; ++i) {
reps[i] = field_types[i].value_type;
mutabilities[i] = field_types[i].mutability;
}
bool is_descriptor = false; // TODO(403372470): Add support.
builder->AddStructType(
zone->New<wasm::StructType>(field_count, kNoOffsets, reps, mutabilities,
is_descriptor),
kNotFinal, kNoSupertype);
}
void Print(std::ostream& os) const {
os << "struct(" << PrintCollection(field_types).WithoutBrackets() << ")";
}
};
struct ArrayType {
FieldType field_type;
void BuildType(Zone* zone, WasmModuleBuilder* builder) const {
// TODO(381687256): Populate final and supertype.
constexpr bool kNotFinal = false;
constexpr ModuleTypeIndex kNoSupertype = ModuleTypeIndex::Invalid();
builder->AddArrayType(zone->New<wasm::ArrayType>(field_type.value_type,
field_type.mutability),
kNotFinal, kNoSupertype);
}
void Print(std::ostream& os) const { os << "array(" << field_type << ")"; }
};
struct FunctionType {
std::vector<ValueType> params;
std::vector<ValueType> returns;
void BuildType(Zone* zone, WasmModuleBuilder* builder) const {
// TODO(381687256): Populate final and supertype.
constexpr bool kNotFinal = false;
constexpr ModuleTypeIndex kNoSupertype = ModuleTypeIndex::Invalid();
FunctionSig::Builder sig_builder(zone, returns.size(), params.size());
for (ValueType param : params) sig_builder.AddParam(param);
for (ValueType ret : returns) sig_builder.AddReturn(ret);
FunctionSig* sig = sig_builder.Get();
builder->ForceAddSignature(sig, kNotFinal, kNoSupertype);
}
void Print(std::ostream& os) const {
os << "func params (" << PrintCollection(params).WithoutBrackets()
<< ") returns (" << PrintCollection(returns).WithoutBrackets() << ")";
}
};
using Type = std::variant<StructType, ArrayType, FunctionType>;
std::ostream& operator<<(std::ostream& os, const Type& type) {
// Call operator<< on the contained type.
std::visit([&os](auto& t) { os << t; }, type);
return os;
}
struct RecursionGroup {
std::vector<Type> types;
// If {single_type} is false, this type will be outside any recursion group.
// This is only allowed if {types.size() == 1}.
bool single_type = false;
void BuildTypes(Zone* zone, WasmModuleBuilder* builder) const {
auto build_type = [zone, builder](const auto& t) {
t.BuildType(zone, builder);
};
if (single_type) {
DCHECK_EQ(1, types.size());
std::visit(build_type, types[0]);
} else {
builder->StartRecursiveTypeGroup();
for (const Type& type : types) {
std::visit(build_type, type);
}
builder->EndRecursiveTypeGroup();
}
}
void Print(std::ostream& os) const {
// Note: {single_type} is not included here because it makes no difference
// for canonicalization.
os << "recgroup(" << PrintCollection(types).WithoutBrackets() << ")";
}
};
// A module with a number of types.
struct Module {
std::vector<RecursionGroup> rec_groups;
void BuildTypes(Zone* zone, WasmModuleBuilder* builder) const {
for (const RecursionGroup& rec_group : rec_groups) {
rec_group.BuildTypes(zone, builder);
}
}
};
} // namespace test
class TypeCanonicalizerTest
: public fuzztest::PerFuzzTestFixtureAdapter<TestWithPlatform> {
public:
TypeCanonicalizerTest() : zone_(&allocator_, "TypeCanonicalizerTest") {}
~TypeCanonicalizerTest() override = default;
void TestCanonicalization(const std::vector<test::Module>&);
private:
void Reset() {
wasm::GetTypeCanonicalizer()->EmptyStorageForTesting();
zone_.Reset();
}
v8::internal::AccountingAllocator allocator_;
Zone zone_;
const WasmEnabledFeatures enabled_features_ =
WasmEnabledFeatures::FromFlags();
};
// FuzzTest domain construction.
static fuzztest::Domain<test::Module> ArbitraryModule() {
ValueType kI8 = kWasmI8;
ValueType kI16 = kWasmI16;
ValueType kI32 = kWasmI32;
ValueType kI64 = kWasmI64;
ValueType kF32 = kWasmF32;
ValueType kF64 = kWasmF64;
auto storage_type_domain = fuzztest::ElementOf(
{kI8, kI16, kI32, kI64, kF32, kF64
/* TODO(381687256: Add kS128 on SIMD-enabled hosts */});
auto value_type_domain = fuzztest::ElementOf(
{kI32, kI64, kF32, kF64
/* TODO(381687256: Add kS128 on SIMD-enabled hosts */});
auto field_type_domain = fuzztest::StructOf<test::FieldType>(
storage_type_domain, fuzztest::Arbitrary<bool>());
auto struct_type_domain = fuzztest::StructOf<test::StructType>(
fuzztest::VectorOf(field_type_domain));
auto array_type_domain =
fuzztest::StructOf<test::ArrayType>(field_type_domain);
auto function_type_domain = fuzztest::StructOf<test::FunctionType>(
fuzztest::VectorOf(value_type_domain),
fuzztest::VectorOf(value_type_domain));
auto type_domain = fuzztest::VariantOf<test::Type>(
struct_type_domain, array_type_domain, function_type_domain);
auto recgroup_domain = fuzztest::OneOf(
// A single type declared outside any recursion group.
fuzztest::StructOf<test::RecursionGroup>(
fuzztest::VectorOf(type_domain).WithSize(1), fuzztest::Just(true)),
// An actual recursion group of arbitrary size.
fuzztest::StructOf<test::RecursionGroup>(fuzztest::VectorOf(type_domain),
fuzztest::Just(false)));
auto module_domain =
fuzztest::StructOf<test::Module>(fuzztest::VectorOf(recgroup_domain));
return module_domain;
}
// Fuzz tests.
void TypeCanonicalizerTest::TestCanonicalization(
const std::vector<test::Module>& test_modules) {
// For each test, reset the type canonicalizer such that individual inputs are
// independent of each other.
Reset();
// Keep a map of all recgroups in all modules to check that canonicalization
// works as expected. The key is a text representation of the respective type
// or recursion group; we expect same text to mean identical group.
std::map<std::string, CanonicalTypeIndex> canonical_types;
for (const test::Module& test_module : test_modules) {
WasmModuleBuilder builder(&zone_);
test_module.BuildTypes(&zone_, &builder);
ZoneBuffer buffer{&zone_};
builder.WriteTo(&buffer);
WasmDetectedFeatures detected_features;
bool kValidateModule = true;
ModuleResult result =
DecodeWasmModule(enabled_features_, base::VectorOf(buffer),
kValidateModule, kWasmOrigin, &detected_features);
// If this fails due to too many types, we need to constrain vector sizes in
// the fuzzer domains.
ASSERT_TRUE(result.ok());
std::shared_ptr<WasmModule> module = std::move(result).value();
size_t total_types = 0;
for (const test::RecursionGroup& rec_group : test_module.rec_groups) {
total_types += rec_group.types.size();
}
ASSERT_EQ(module->types.size(), total_types);
size_t num_previous_types = 0;
for (const test::RecursionGroup& rec_group : test_module.rec_groups) {
// The total number of types must be within kV8MaxWasmTypes.
ASSERT_GE(kMaxUInt32, num_previous_types);
uint32_t first_type_id = static_cast<uint32_t>(num_previous_types);
num_previous_types += rec_group.types.size();
// Skip empty recursion groups; they do not get a canonical ID assigned,
// so we cannot check anything for them (except that they do not confuse
// canonicalization of surrounding types or groups).
if (rec_group.types.empty()) continue;
DCHECK(!rec_group.types.empty());
CanonicalTypeIndex first_canonical_id =
module->canonical_type_id(ModuleTypeIndex{first_type_id});
for (uint32_t i = 1; i < rec_group.types.size(); ++i) {
// Canonical IDs are consecutive within the recursion group.
ASSERT_EQ(
CanonicalTypeIndex{first_canonical_id.index + i},
module->canonical_type_id(ModuleTypeIndex{first_type_id + i}));
}
std::string recgroup_str = (std::ostringstream{} << rec_group).str();
auto [it, added] = canonical_types.insert(
std::make_pair(recgroup_str, first_canonical_id));
// Check that the entry holds first_canonical_id; either it was added
// here, or it existed and we check against the existing entry.
ASSERT_EQ(it->second, first_canonical_id)
<< "New recgroup:\n"
<< recgroup_str << "\nOld recgroup:\n"
<< it->first;
}
}
}
V8_FUZZ_TEST_F(TypeCanonicalizerTest, TestCanonicalization)
.WithDomains(fuzztest::VectorOf(ArbitraryModule()));
} // namespace v8::internal::wasm

View File

@ -0,0 +1,156 @@
// 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 "testing/gmock/include/gmock/gmock.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/jump-table-assembler.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace wasm_heap_unittest {
class DisjointAllocationPoolTest : public ::testing::Test {
public:
void CheckPool(const DisjointAllocationPool& mem,
std::initializer_list<base::AddressRegion> expected_regions);
void CheckRange(base::AddressRegion region1, base::AddressRegion region2);
DisjointAllocationPool Make(
std::initializer_list<base::AddressRegion> regions);
};
void DisjointAllocationPoolTest::CheckPool(
const DisjointAllocationPool& mem,
std::initializer_list<base::AddressRegion> expected_regions) {
const auto& regions = mem.regions();
EXPECT_EQ(regions.size(), expected_regions.size());
auto iter = expected_regions.begin();
for (auto it = regions.begin(), e = regions.end(); it != e; ++it, ++iter) {
EXPECT_EQ(*it, *iter);
}
}
void DisjointAllocationPoolTest::CheckRange(base::AddressRegion region1,
base::AddressRegion region2) {
EXPECT_EQ(region1, region2);
}
DisjointAllocationPool DisjointAllocationPoolTest::Make(
std::initializer_list<base::AddressRegion> regions) {
DisjointAllocationPool ret;
for (auto& region : regions) {
ret.Merge(region);
}
return ret;
}
TEST_F(DisjointAllocationPoolTest, ConstructEmpty) {
DisjointAllocationPool a;
EXPECT_TRUE(a.IsEmpty());
CheckPool(a, {});
a.Merge({1, 4});
CheckPool(a, {{1, 4}});
}
TEST_F(DisjointAllocationPoolTest, ConstructWithRange) {
DisjointAllocationPool a({1, 4});
EXPECT_FALSE(a.IsEmpty());
CheckPool(a, {{1, 4}});
}
TEST_F(DisjointAllocationPoolTest, SimpleExtract) {
DisjointAllocationPool a = Make({{1, 4}});
base::AddressRegion b = a.Allocate(2);
CheckPool(a, {{3, 2}});
CheckRange(b, {1, 2});
a.Merge(b);
CheckPool(a, {{1, 4}});
EXPECT_EQ(a.regions().size(), uint32_t{1});
EXPECT_EQ(a.regions().begin()->begin(), uint32_t{1});
EXPECT_EQ(a.regions().begin()->end(), uint32_t{5});
}
TEST_F(DisjointAllocationPoolTest, ExtractAll) {
DisjointAllocationPool a({1, 4});
base::AddressRegion b = a.Allocate(4);
CheckRange(b, {1, 4});
EXPECT_TRUE(a.IsEmpty());
a.Merge(b);
CheckPool(a, {{1, 4}});
}
TEST_F(DisjointAllocationPoolTest, FailToExtract) {
DisjointAllocationPool a = Make({{1, 4}});
base::AddressRegion b = a.Allocate(5);
CheckPool(a, {{1, 4}});
EXPECT_TRUE(b.is_empty());
}
TEST_F(DisjointAllocationPoolTest, FailToExtractExact) {
DisjointAllocationPool a = Make({{1, 4}, {10, 4}});
base::AddressRegion b = a.Allocate(5);
CheckPool(a, {{1, 4}, {10, 4}});
EXPECT_TRUE(b.is_empty());
}
TEST_F(DisjointAllocationPoolTest, ExtractExact) {
DisjointAllocationPool a = Make({{1, 4}, {10, 5}});
base::AddressRegion b = a.Allocate(5);
CheckPool(a, {{1, 4}});
CheckRange(b, {10, 5});
}
TEST_F(DisjointAllocationPoolTest, Merging) {
DisjointAllocationPool a = Make({{10, 5}, {20, 5}});
a.Merge({15, 5});
CheckPool(a, {{10, 15}});
}
TEST_F(DisjointAllocationPoolTest, MergingFirst) {
DisjointAllocationPool a = Make({{10, 5}, {20, 5}});
a.Merge({5, 5});
CheckPool(a, {{5, 10}, {20, 5}});
}
TEST_F(DisjointAllocationPoolTest, MergingAbove) {
DisjointAllocationPool a = Make({{10, 5}, {25, 5}});
a.Merge({20, 5});
CheckPool(a, {{10, 5}, {20, 10}});
}
TEST_F(DisjointAllocationPoolTest, MergingMore) {
DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
a.Merge({15, 5});
a.Merge({25, 5});
CheckPool(a, {{10, 25}});
}
TEST_F(DisjointAllocationPoolTest, MergingSkip) {
DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
a.Merge({25, 5});
CheckPool(a, {{10, 5}, {20, 15}});
}
TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrc) {
DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
a.Merge({25, 5});
a.Merge({35, 5});
CheckPool(a, {{10, 5}, {20, 20}});
}
TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrcWithGap) {
DisjointAllocationPool a = Make({{10, 5}, {20, 5}, {30, 5}});
a.Merge({25, 5});
a.Merge({36, 4});
CheckPool(a, {{10, 5}, {20, 15}, {36, 4}});
}
} // namespace wasm_heap_unittest
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,106 @@
// Copyright 2024 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/wasm-code-pointer-table-inl.h"
#include "test/unittests/test-utils.h"
namespace v8::internal::wasm {
namespace {
template <typename FunctionType>
class BackgroundThread final : public v8::base::Thread {
public:
explicit BackgroundThread(FunctionType function)
: v8::base::Thread(base::Thread::Options("BackgroundThread")),
function_(function),
should_stop_(false) {}
void Stop() {
should_stop_.store(true);
Join();
}
void Run() override {
while (!should_stop_.load()) {
function_();
}
}
private:
FunctionType function_;
std::atomic<bool> should_stop_;
};
template <typename FunctionType>
BackgroundThread(FunctionType) -> BackgroundThread<FunctionType>;
} // anonymous namespace
class WasmCodePointerTableTest : public TestWithPlatform {
public:
WasmCodePointerTableTest()
: code_pointer_table_(GetProcessWideWasmCodePointerTable()) {}
protected:
void SetUp() override {}
void TearDown() override {
for (auto handle : handles_) {
code_pointer_table_->FreeEntry(handle);
}
handles_.clear();
}
void CreateHoleySegments() {
std::vector<WasmCodePointer> to_free_handles;
for (size_t i = 0; i < 3 * WasmCodePointerTable::kEntriesPerSegment + 1337;
i++) {
handles_.push_back(code_pointer_table_->AllocateUninitializedEntry());
}
for (size_t i = 0; i < 3 * WasmCodePointerTable::kEntriesPerSegment; i++) {
to_free_handles.push_back(
code_pointer_table_->AllocateUninitializedEntry());
}
for (size_t i = 0; i < 3 * WasmCodePointerTable::kEntriesPerSegment + 1337;
i++) {
handles_.push_back(code_pointer_table_->AllocateUninitializedEntry());
}
for (size_t i = 0; i < 3 * WasmCodePointerTable::kEntriesPerSegment; i++) {
to_free_handles.push_back(
code_pointer_table_->AllocateUninitializedEntry());
}
for (size_t i = 0; i < 3 * WasmCodePointerTable::kEntriesPerSegment + 1337;
i++) {
handles_.push_back(code_pointer_table_->AllocateUninitializedEntry());
}
for (auto to_free_handle : to_free_handles) {
code_pointer_table_->FreeEntry(to_free_handle);
}
}
WasmCodePointerTable* code_pointer_table_;
std::vector<WasmCodePointer> handles_;
};
TEST_F(WasmCodePointerTableTest, ConcurrentSweep) {
BackgroundThread sweep_thread1(
[this]() { code_pointer_table_->SweepSegments(); });
BackgroundThread sweep_thread2(
[this]() { code_pointer_table_->SweepSegments(); });
CreateHoleySegments();
sweep_thread1.StartSynchronously();
sweep_thread2.StartSynchronously();
CreateHoleySegments();
sweep_thread1.Stop();
sweep_thread2.Stop();
}
} // namespace v8::internal::wasm

View File

@ -0,0 +1,95 @@
// Copyright 2024 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 TEST_UNITTESTS_WASM_WASM_COMPILE_MODULE_H_
#define TEST_UNITTESTS_WASM_WASM_COMPILE_MODULE_H_
#include "include/libplatform/libplatform.h"
#include "src/base/vector.h"
#include "src/execution/isolate.h"
#include "src/handles/handles-inl.h"
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-objects.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest-support.h"
namespace v8::internal::wasm {
class WasmCompileHelper : public AllStatic {
public:
static void SyncCompile(Isolate* isolate,
base::OwnedVector<const uint8_t> bytes) {
ErrorThrower thrower(isolate, "WasmCompileHelper::SyncCompile");
GetWasmEngine()->SyncCompile(isolate, WasmEnabledFeatures::All(),
CompileTimeImports{}, &thrower,
std::move(bytes));
ASSERT_FALSE(thrower.error()) << thrower.error_msg();
}
static void AsyncCompile(Isolate* isolate,
base::OwnedVector<const uint8_t> bytes) {
std::shared_ptr<TestResolver> resolver = std::make_shared<TestResolver>();
GetWasmEngine()->AsyncCompile(
isolate, WasmEnabledFeatures::All(), CompileTimeImports{}, resolver,
std::move(bytes), "WasmCompileHelper::AsyncCompile");
while (resolver->pending()) {
v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
reinterpret_cast<v8::Isolate*>(isolate));
}
}
static void StreamingCompile(Isolate* isolate,
base::Vector<const uint8_t> bytes) {
std::shared_ptr<TestResolver> resolver = std::make_shared<TestResolver>();
std::shared_ptr<StreamingDecoder> streaming_decoder =
GetWasmEngine()->StartStreamingCompilation(
isolate, WasmEnabledFeatures::All(), CompileTimeImports{},
direct_handle(isolate->context()->native_context(), isolate),
"StreamingCompile", resolver);
base::RandomNumberGenerator* rng = isolate->random_number_generator();
for (auto remaining_bytes = bytes; !remaining_bytes.empty();) {
// Split randomly; with 10% probability do not split.
ASSERT_GE(size_t{kMaxInt / 2}, remaining_bytes.size());
size_t split_point =
remaining_bytes.size() == 1 || rng->NextInt(10) == 0
? remaining_bytes.size()
: 1 + rng->NextInt(static_cast<int>(remaining_bytes.size() - 1));
streaming_decoder->OnBytesReceived(
remaining_bytes.SubVector(0, split_point));
remaining_bytes += split_point;
}
streaming_decoder->Finish(true);
while (resolver->pending()) {
v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
reinterpret_cast<v8::Isolate*>(isolate));
}
}
private:
struct TestResolver : public CompilationResultResolver {
public:
void OnCompilationSucceeded(
i::DirectHandle<i::WasmModuleObject> module) override {
ASSERT_FALSE(module.is_null());
ASSERT_EQ(true, pending_.exchange(false, std::memory_order_relaxed));
}
void OnCompilationFailed(i::DirectHandle<i::JSAny> error_reason) override {
Print(*error_reason);
FAIL();
}
bool pending() const { return pending_.load(std::memory_order_relaxed); }
private:
std::atomic<bool> pending_{true};
};
};
} // namespace v8::internal::wasm
#endif // TEST_UNITTESTS_WASM_WASM_COMPILE_MODULE_H_

View File

@ -0,0 +1,110 @@
// Copyright 2018 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/compiler/wasm-compiler.h"
#include "src/codegen/machine-type.h"
#include "src/codegen/signature.h"
#include "src/compiler/linkage.h"
#include "src/wasm/value-type.h"
#include "src/wasm/wasm-linkage.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmCallDescriptorTest : public TestWithZone {};
TEST_F(WasmCallDescriptorTest, TestExternRefIsGrouped) {
constexpr size_t kMaxCount = 30;
ValueType params[kMaxCount];
for (size_t i = 0; i < kMaxCount; i += 2) {
params[i] = kWasmExternRef;
EXPECT_TRUE(i + 1 < kMaxCount);
params[i + 1] = kWasmI32;
}
for (size_t count = 1; count <= kMaxCount; ++count) {
FunctionSig sig(/*return_count=*/0, count, params);
compiler::CallDescriptor* desc =
compiler::GetWasmCallDescriptor(zone(), &sig);
// The WasmInstance is the implicit first parameter.
EXPECT_EQ(count + 1, desc->ParameterCount());
bool has_untagged_stack_param = false;
bool has_tagged_register_param = false;
int max_tagged_stack_location = std::numeric_limits<int>::min();
int min_untagged_stack_location = std::numeric_limits<int>::max();
for (size_t i = 1; i < desc->ParameterCount(); ++i) {
// InputLocation i + 1, because target is the first input.
LinkageLocation location = desc->GetInputLocation(i + 1);
if (desc->GetParameterType(i).IsTagged()) {
if (location.IsRegister()) {
has_tagged_register_param = true;
} else {
EXPECT_TRUE(location.IsCallerFrameSlot());
max_tagged_stack_location =
std::max(max_tagged_stack_location, location.AsCallerFrameSlot());
}
} else { // !isTagged()
if (location.IsCallerFrameSlot()) {
has_untagged_stack_param = true;
min_untagged_stack_location = std::min(min_untagged_stack_location,
location.AsCallerFrameSlot());
} else {
EXPECT_TRUE(location.IsRegister());
}
}
}
// There should never be a tagged parameter in a register and an untagged
// parameter on the stack at the same time.
EXPECT_EQ(false, has_tagged_register_param && has_untagged_stack_param);
EXPECT_TRUE(max_tagged_stack_location < min_untagged_stack_location);
}
}
TEST_F(WasmCallDescriptorTest, Regress_1174500) {
// Our test signature should have just enough params and returns to force
// 1 param and 1 return to be allocated as stack slots. Use FP registers to
// avoid interference with implicit parameters, like the Wasm Instance.
constexpr int kParamRegisters = arraysize(kFpParamRegisters);
constexpr int kParams = kParamRegisters + 1;
constexpr int kReturnRegisters = arraysize(kFpReturnRegisters);
constexpr int kReturns = kReturnRegisters + 1;
ValueType types[kReturns + kParams];
// One S128 return slot which shouldn't be padded unless the arguments area
// of the frame requires it.
for (int i = 0; i < kReturnRegisters; ++i) types[i] = kWasmF32;
types[kReturnRegisters] = kWasmS128;
// One F32 parameter slot to misalign the parameter area.
for (int i = 0; i < kParamRegisters; ++i) types[kReturns + i] = kWasmF32;
types[kReturns + kParamRegisters] = kWasmF32;
FunctionSig sig(kReturns, kParams, types);
compiler::CallDescriptor* desc =
compiler::GetWasmCallDescriptor(zone(), &sig);
// Get the location of our stack parameter slot. Skip the implicit Wasm
// instance parameter.
LinkageLocation last_param = desc->GetInputLocation(kParams + 1);
EXPECT_TRUE(last_param.IsCallerFrameSlot());
EXPECT_EQ(MachineType::Float32(), last_param.GetType());
EXPECT_EQ(-1, last_param.GetLocation());
// The stack return slot should be right above our last parameter, and any
// argument padding slots. The return slot itself should not be padded.
const int padding = ShouldPadArguments(1);
const int first_return_slot = -1 - (padding + 1);
LinkageLocation return_location = desc->GetReturnLocation(kReturns - 1);
EXPECT_TRUE(return_location.IsCallerFrameSlot());
EXPECT_EQ(MachineType::Simd128(), return_location.GetType());
EXPECT_EQ(first_return_slot, return_location.GetLocation());
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,145 @@
// This Wasm module has a name section which is invalid in that it
// contains each sub-section twice.
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
// The only purpose of this table section is to trigger lazy decoding
// of the name section.
0x04, // section kind: Table
0x04, // section length 4
0x01, 0x70, 0x00, // table count 1: funcref no maximum
0x00, // initial size 0
0x00, // section kind: Unknown
0xb3, 0x01, // section length 179
0x04, // section name length: 4
0x6e, 0x61, 0x6d, 0x65, // section name: name
0x01, // name type: function
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x02, // name type: local
0x0b, // payload length: 11
0x02, // outer count 2
0x00, 0x01, // outer index 0 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x01, 0x01, // outer index 1 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x03, // name type: label
0x0b, // payload length: 11
0x02, // outer count 2
0x00, 0x01, // outer index 0 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x01, 0x01, // outer index 1 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x04, // name type: type
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x05, // name type: table
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x06, // name type: memory
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x07, // name type: global
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x08, // name type: element segment
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x09, // name type: data segment
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x0a, // name type: field
0x0b, // payload length: 11
0x02, // outer count 2
0x00, 0x01, // outer index 0 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x01, 0x01, // outer index 1 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x0b, // name type: tag
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x01, // name type: function
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x02, // name type: local
0x0b, // payload length: 11
0x02, // outer count 2
0x00, 0x01, // outer index 0 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x01, 0x01, // outer index 1 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x03, // name type: label
0x0b, // payload length: 11
0x02, // outer count 2
0x00, 0x01, // outer index 0 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x01, 0x01, // outer index 1 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x04, // name type: type
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x05, // name type: table
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x06, // name type: memory
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x07, // name type: global
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x08, // name type: element segment
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x09, // name type: data segment
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"
0x0a, // name type: field
0x0b, // payload length: 11
0x02, // outer count 2
0x00, 0x01, // outer index 0 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x01, 0x01, // outer index 1 inner count 1
0x00, 0x01, 0x78, // inner index 0 name length 1 "x"
0x0b, // name type: tag
0x04, // payload length: 4
0x01, // names count 1
0x00, 0x01, 0x78, // index 0 name length 1 "x"

View File

@ -0,0 +1,36 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x09, // section length 9
0x02, // types count 2
0x60, // kind: func
0x00, // param count 0
0x00, // return count 0
0x60, // kind: func
0x01, 0x7f, // param count 1: i32
0x01, 0x7f, // return count 1: i32
0x03, // section kind: Function
0x02, // section length 2
0x01, 0x01, // functions count 1: 0 $func0 (param i32) (result i32)
0x0d, // section kind: Tag
0x03, // section length 3
0x01, 0x00, 0x00, // tag count 1:
0x0a, // section kind: Code
0x16, // section length 22
0x01, // functions count 1
// function #0 $func0
0x14, // body size 20
0x00, // 0 entries in locals list
0x02, 0x40, // block $label0
0x1f, 0x7f, 0x01, 0x00, 0x00, 0x00, // try_table (result i32) catch $tag0 $label0
0x41, 0x00, // i32.const 0
0x0c, 0x00, // br $label0
0x0b, // end $label0
0x0c, 0x01, // br 1
0x0b, // end
0x41, 0x00, // i32.const 0
0x0b, // end

View File

@ -0,0 +1,16 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
(tag $tag0)
(func $func0 (param $var0 i32) (result i32)
block $label0
try_table $label1 (result i32) catch $tag0 $label0
i32.const 0
br $label1
end $label1
br 1
end $label0
i32.const 0
)
)
;;)---";

View File

@ -0,0 +1,144 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x4b, // section length 75
0x10, // types count 16
0x4e, 0x00, // empty rec.group
0x4e, 0x00, // empty rec.group
0x50, 0x00, 0x5f, 0x00, // type #0 $type0 subtype, supertype count 0, kind: struct, field count 0
0x5f, 0x01, 0x7f, 0x00, // type #1 $type1 kind: struct, field count 1: i32 immutable
0x5f, 0x02, // type #2 $type2 kind: struct, field count 2
0x7f, 0x01, // i32 mutable
0x7e, 0x01, // i64 mutable
0x5f, 0x02, // type #3 $type3 kind: struct, field count 2
0x78, 0x00, // i8 immutable
0x77, 0x01, // i16 mutable
0x5e, 0x7e, 0x00, // type #4 $type4 kind: array i64 immutable
0x5e, 0x7e, 0x01, // type #5 $type5 kind: array i64 mutable
0x5e, 0x78, 0x00, // type #6 $type6 kind: array i8 immutable
0x5f, 0x01, 0x64, 0x00, 0x00, // type #7 $type7 kind: struct, field count 1: (ref $type0) immutable
0x4e, // rec. group definition
0x02, // recursive group size 2
0x5f, 0x01, 0x64, 0x09, 0x00, // type #8 $type8 kind: struct, field count 1: (ref $type9) immutable
0x5f, 0x01, 0x64, 0x08, 0x00, // type #9 $type9 kind: struct, field count 1: (ref $type8) immutable
0x50, 0x01, 0x00, // type #10 $type10 subtype, supertype count 1: supertype 0
0x5f, 0x01, 0x7f, 0x00, // kind: struct, field count 1: i32 immutable
0x60, // type #11 $type11 kind: func
0x02, // param count 2
0x64, 0x01, 0x6d, // (ref $type1) eqref
0x00, // return count 0
0x4f, 0x01, 0x00, // type #12 $type12 final subtype, supertype count 1: supertype 0
0x5f, 0x01, 0x7f, 0x01, // kind: struct, field count 1: i32 mutable
0x4e, 0x00, // empty rec.group
0x4e, 0x00, // empty rec.group
0x02, // section kind: Import
0x30, // section length 48
0x02, // imports count 2
// import #0
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x0f, // field name length: 15
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: imported_global
0x03, 0x64, 0x07, 0x00, // kind: global (ref $type7) immutable
// import #1
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x0e, // field name length: 14
0x61, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f,
0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: another_global
0x03, 0x64, 0x08, 0x00, // kind: global (ref $type8) immutable
0x03, // section kind: Function
0x02, // section length 2
0x01, 0x0b, // functions count 1: 0 $func0 (param (ref $type1) eqref)
0x06, // section kind: Global
0x0b, // section length 11
0x02, // globals count 2
0x6e, 0x00, // global #2: anyref immutable
0xd0, 0x71, 0x0b, // ref.null none
0x6d, 0x01, // global #3: eqref mutable
0xd0, 0x71, 0x0b, // ref.null none
0x0a, // section kind: Code
0x9d, 0x01, // section length 157
0x01, // functions count 1
// function #0 $func0
0x9a, 0x01, // body size 154
0x00, // 0 entries in locals list
0xfb, 0x01, 0x01, // struct.new_default $type1
0xfb, 0x02, 0x01, 0x00, // struct.get $type1 $field0
0x1a, // drop
0xfb, 0x01, 0x02, // struct.new_default $type2
0x41, 0x00, // i32.const 0
0xfb, 0x05, 0x02, 0x00, // struct.set $type2 $field0
0xfb, 0x01, 0x03, // struct.new_default $type3
0xfb, 0x03, 0x03, 0x00, // struct.get_s $type3 $field0
0x1a, // drop
0xfb, 0x01, 0x03, // struct.new_default $type3
0xfb, 0x04, 0x03, 0x01, // struct.get_u $type3 $field1
0x1a, // drop
0xfb, 0x08, 0x04, 0x00, // array.new_fixed $type4 0
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfb, 0x07, 0x04, // array.new_default $type4
0xfb, 0x0f, // array.len
0x1a, // drop
0x42, 0x00, // i64.const 0
0x41, 0x00, // i32.const 0
0xfb, 0x06, 0x04, // array.new $type4
0x41, 0x00, // i32.const 0
0xfb, 0x0b, 0x04, // array.get $type4
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfb, 0x07, 0x05, // array.new_default $type5
0x41, 0x00, // i32.const 0
0x42, 0x00, // i64.const 0
0xfb, 0x0e, 0x05, // array.set $type5
0x41, 0x00, // i32.const 0
0xfb, 0x07, 0x06, // array.new_default $type6
0x41, 0x00, // i32.const 0
0xfb, 0x0c, 0x06, // array.get_s $type6
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfb, 0x07, 0x06, // array.new_default $type6
0x41, 0x00, // i32.const 0
0xfb, 0x0d, 0x06, // array.get_u $type6
0x1a, // drop
0x20, 0x01, // local.get $var1
0x20, 0x01, // local.get $var1
0xd3, // ref.eq
0x1a, // drop
0x20, 0x01, // local.get $var1
0xfb, 0x14, 0x00, // ref.test $type0
0x1a, // drop
0x20, 0x00, // local.get $var0
0xfb, 0x16, 0x00, // ref.cast $type0
0x1a, // drop
0x20, 0x00, // local.get $var0
0xfb, 0x15, 0x00, // ref.test null $type0
0x1a, // drop
0x20, 0x00, // local.get $var0
0xfb, 0x17, 0x00, // ref.cast null $type0
0x1a, // drop
0x02, 0x64, 0x01, // block (result (ref $type1)) $label0
0x20, 0x00, // local.get $var0
0xd6, 0x00, // br_on_non_null $label0
0x20, 0x00, // local.get $var0
0xfb, 0x18, 0x01, 0x00, 0x00, 0x01,
// br_on_cast $label0 (ref null $type0) (ref $type1)
0x1a, // drop
0x20, 0x00, // local.get $var0
0xfb, 0x19, 0x02, 0x00, 0x00, 0x01,
// br_on_cast_fail $label0 (ref $type0)
// (ref null $type1)
0x1a, // drop
0x20, 0x00, // local.get $var0
0x0b, // end $label0
0x1a, // drop
0x0b, // end

View File

@ -0,0 +1,108 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
;; Empty recgroups are useless but supported.
(rec)
(rec)
;; Structs.
(type $type0 (struct))
(type $type1 (struct (field $field0 i32)))
(type $type2 (struct (field $field0 (mut i32)) (field $field1 (mut i64))))
(type $type3 (struct (field $field0 i8) (field $field1 (mut i16))))
;; Arrays.
(type $type4 (array (field i64)))
(type $type5 (array (field (mut i64))))
(type $type6 (array (field i8)))
;; References to other types, mutual recursion.
(type $type7 (struct (field $field0 (ref $type0))))
(rec
(type $type8 (struct (field $field0 (ref $type9))))
(type $type9 (struct (field $field0 (ref $type8))))
)
;; Subtyping constraints.
(type $type10 (sub $type0 (struct (field $field0 i32))))
(type $type12 (sub final $type0 (struct (field $field0 (mut i32)))))
;; Empty recgroups are useless but supported.
(rec)
(rec)
;; Globals using reference types.
(global $env.imported_global (;0;) (import "env" "imported_global") (ref $type7))
(global $env.another_global (;1;) (import "env" "another_global") (ref $type8))
(global $global2 anyref (ref.null none))
(global $global3 (mut eqref) (ref.null none))
;; Function with GC instructions and taking GC types as parameters.
(func $func0 (param $var0 (ref $type1)) (param $var1 eqref)
;; Structs.
struct.new_default $type1
struct.get $type1 $field0
drop
struct.new_default $type2
i32.const 0
struct.set $type2 $field0
struct.new_default $type3
struct.get_s $type3 $field0
drop
struct.new_default $type3
struct.get_u $type3 $field1
drop
;; Arrays.
array.new_fixed $type4 0
drop
i32.const 0
array.new_default $type4
array.len
drop
i64.const 0
i32.const 0
array.new $type4
i32.const 0
array.get $type4
drop
i32.const 0
array.new_default $type5
i32.const 0
i64.const 0
array.set $type5
i32.const 0
array.new_default $type6
i32.const 0
array.get_s $type6
drop
i32.const 0
array.new_default $type6
i32.const 0
array.get_u $type6
drop
;; References.
local.get $var1
local.get $var1
ref.eq
drop
local.get $var1
ref.test $type0
drop
local.get $var0
ref.cast $type0
drop
local.get $var0
ref.test null $type0
drop
local.get $var0
ref.cast null $type0
drop
;; Branches.
block $label0 (result (ref $type1))
local.get $var0
br_on_non_null $label0
local.get $var0
br_on_cast $label0 (ref null $type0) (ref $type1)
drop
local.get $var0
br_on_cast_fail $label0 (ref $type0) (ref null $type1)
drop
local.get $var0
end $label0
drop
)
)
;;)---";

View File

@ -0,0 +1,568 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x1d, // section length 29
0x07, // types count 7
0x60, // type #0 $type0 kind: func
0x00, // param count 0
0x00, // return count 0
0x60, // type #1 $type1 kind: func
0x01, 0x7f, // param count 1: i32
0x00, // return count 0
0x60, // type #2 $type2 kind: func
0x00, // param count 0
0x01, 0x7f, // return count 1: i32
0x60, // type #3 $type3 kind: func
0x00, // param count 0
0x01, 0x7e, // return count 1: i64
0x60, // type #4 $type4 kind: func
0x00, // param count 0
0x01, 0x7d, // return count 1: f32
0x60, // type #5 $type5 kind: func
0x00, // param count 0
0x01, 0x7c, // return count 1: f64
0x60, // type #6 $type6 kind: func
0x01, 0x7e, // param count 1: i64
0x01, 0x7c, // return count 1: f64
0x02, // section kind: Import
0x30, // section length 48
0x02, // imports count 2
// import #0
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x0f, // field name length: 15
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: imported_global
0x03, 0x7f, 0x00, // kind: global i32 immutable
// import #1
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x11, // field name length: 17
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, // field name: imported_function
0x00, 0x00, // kind: function
0x03, // section kind: Function
0x09, // section length 9
0x08, // functions count 8
0x01, // 1 $func1 (param i32)
0x02, // 2 $func2 (result i32)
0x03, // 3 $func3 (result i64)
0x04, // 4 $func4 (result f32)
0x05, // 5 $func5 (result f64)
0x00, // 6 $func6
0x00, // 7 $func7
0x00, // 8 $exported_function
0x04, // section kind: Table
0x04, // section length 4
0x01, 0x70, 0x00, // table count 1: funcref no maximum
0x04, // initial size 4
0x05, // section kind: Memory
0x04, // section length 4
0x01, 0x01, // memory count 1: with maximum
0x00, // initial size 0
0x01, // maximum size 1
0x06, // section kind: Global
0x10, // section length 16
0x03, // globals count 3
0x7f, 0x01, // global #1: i32 mutable
0x41, 0x00, 0x0b, // i32.const 0
0x7f, 0x00, // global #2: i32 immutable
0x23, 0x00, 0x0b, // global.get $env.imported_global
0x7e, 0x00, // global #3: i64 immutable
0x42, 0x00, 0x0b, // i64.const 0
0x07, // section kind: Export
0x27, // section length 39
0x02, // exports count 2
// export # 0
0x0f, // field name length: 15
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: exported_global
0x03, 0x03, // kind: global index: 3
// export # 1
0x11, // field name length: 17
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, // field name: exported_function
0x00, 0x08, // kind: function index: 8
0x09, // section kind: Element
0x0a, // section length 10
0x01, 0x00, // segment count 1: flag: active, offset:
0x41, 0x00, 0x0b, // i32.const 0
0x04, // number of elements 4
0x00, // index: 0
0x01, // index: 1
0x01, // index: 1
0x03, // index: 3
0x0a, // section kind: Code
0xdc, 0x07, // section length 988
0x08, // functions count 8
// function #1 $func1
0x14, // body size 20
0x01, // 1 entries in locals list
0x01, 0x7e, // 1 local of type i64
0x20, 0x00, // local.get $var0
0x22, 0x00, // local.tee $var0
0x21, 0x00, // local.set $var0
0x20, 0x01, // local.get $var1
0x22, 0x01, // local.tee $var1
0x21, 0x01, // local.set $var1
0x23, 0x01, // global.get $global1
0x24, 0x01, // global.set $global1
0x0b, // end
// function #2 $func2
0x8c, 0x01, // body size 140
0x00, // 0 entries in locals list
0x41, 0x00, // i32.const 0
0x45, // i32.eqz
0x41, 0x01, // i32.const 1
0x46, // i32.eq
0x41, 0x7f, // i32.const -1
0x47, // i32.ne
0x41, 0xff, 0xff, 0xff, 0xff, 0x07, // i32.const 2147483647
0x48, // i32.lt_s
0x41, 0x80, 0x80, 0x80, 0x80, 0x78, // i32.const -2147483648
0x49, // i32.lt_u
0x41, 0x00, // i32.const 0
0x4a, // i32.gt_s
0x41, 0x00, // i32.const 0
0x4b, // i32.gt_u
0x41, 0x00, // i32.const 0
0x4c, // i32.le_s
0x41, 0x00, // i32.const 0
0x4d, // i32.le_u
0x41, 0x00, // i32.const 0
0x4e, // i32.ge_s
0x41, 0x00, // i32.const 0
0x4f, // i32.ge_u
0x67, // i32.clz
0x68, // i32.ctz
0x69, // i32.popcnt
0x41, 0x00, // i32.const 0
0x6a, // i32.add
0x41, 0x00, // i32.const 0
0x6b, // i32.sub
0x41, 0x00, // i32.const 0
0x6c, // i32.mul
0x41, 0x00, // i32.const 0
0x6d, // i32.div_s
0x41, 0x00, // i32.const 0
0x6e, // i32.div_u
0x41, 0x00, // i32.const 0
0x6f, // i32.rem_s
0x41, 0x00, // i32.const 0
0x70, // i32.rem_u
0x41, 0x00, // i32.const 0
0x71, // i32.and
0x41, 0x00, // i32.const 0
0x72, // i32.or
0x41, 0x00, // i32.const 0
0x73, // i32.xor
0x41, 0x00, // i32.const 0
0x74, // i32.shl
0x41, 0x00, // i32.const 0
0x75, // i32.shr_s
0x41, 0x00, // i32.const 0
0x76, // i32.shr_u
0x41, 0x00, // i32.const 0
0x77, // i32.rotl
0x41, 0x00, // i32.const 0
0x78, // i32.rotr
0x1a, // drop
0x42, 0x00, // i64.const 0
0xa7, // i32.wrap_i64
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xa8, // i32.trunc_f32_s
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xa9, // i32.trunc_f32_u
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xaa, // i32.trunc_f64_s
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xab, // i32.trunc_f64_u
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xbc, // i32.reinterpret_f32
0xc0, // i32.extend8_s
0xc1, // i32.extend16_s
0x0b, // end
// function #3 $func3
0xc0, 0x01, // body size 192
0x00, // 0 entries in locals list
0x42, 0x00, // i64.const 0
0x50, // i64.eqz
0x1a, // drop
0x42, 0x01, // i64.const 1
0x42, 0x7f, // i64.const -1
0x51, // i64.eq
0x1a, // drop
0x42, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x7f, // i64.const -9223372036854775808
0x42, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, // i64.const 9223372036854775807
0x52, // i64.ne
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x53, // i64.lt_s
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x54, // i64.lt_u
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x55, // i64.gt_s
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x56, // i64.gt_u
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x57, // i64.le_s
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x58, // i64.le_u
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x59, // i64.ge_s
0x1a, // drop
0x42, 0x00, // i64.const 0
0x42, 0x00, // i64.const 0
0x5a, // i64.ge_u
0x1a, // drop
0x42, 0x00, // i64.const 0
0x79, // i64.clz
0x7a, // i64.ctz
0x7b, // i64.popcnt
0x42, 0x00, // i64.const 0
0x7c, // i64.add
0x42, 0x00, // i64.const 0
0x7d, // i64.sub
0x42, 0x00, // i64.const 0
0x7e, // i64.mul
0x42, 0x00, // i64.const 0
0x7f, // i64.div_s
0x42, 0x00, // i64.const 0
0x80, // i64.div_u
0x42, 0x00, // i64.const 0
0x81, // i64.rem_s
0x42, 0x00, // i64.const 0
0x82, // i64.rem_u
0x42, 0x00, // i64.const 0
0x83, // i64.and
0x42, 0x00, // i64.const 0
0x84, // i64.or
0x42, 0x00, // i64.const 0
0x85, // i64.xor
0x42, 0x00, // i64.const 0
0x86, // i64.shl
0x42, 0x00, // i64.const 0
0x87, // i64.shr_s
0x42, 0x00, // i64.const 0
0x88, // i64.shr_u
0x42, 0x00, // i64.const 0
0x89, // i64.rotl
0x42, 0x00, // i64.const 0
0x8a, // i64.rotr
0x1a, // drop
0x41, 0x00, // i32.const 0
0xac, // i64.extend_i32_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0xad, // i64.extend_i32_u
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xae, // i64.trunc_f32_s
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xaf, // i64.trunc_f32_u
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xb0, // i64.trunc_f64_s
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xb1, // i64.trunc_f64_u
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xbd, // i64.reinterpret_f64
0xc2, // i64.extend8_s
0xc3, // i64.extend16_s
0xc4, // i64.extend32_s
0x0b, // end
// function #4 $func4
0x9f, 0x01, // body size 159
0x00, // 0 entries in locals list
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x43, 0x00, 0x00, 0x00, 0x80, // f32.const -0.0
0x5b, // f32.eq
0x1a, // drop
0x43, 0x00, 0x00, 0x80, 0x3f, // f32.const 1
0x43, 0x00, 0x00, 0x80, 0xbf, // f32.const -1
0x5c, // f32.ne
0x1a, // drop
0x43, 0x00, 0x00, 0x80, 0x7f, // f32.const inf
0x43, 0x00, 0x00, 0x80, 0xff, // f32.const -inf
0x5d, // f32.lt
0x1a, // drop
0x43, 0x00, 0x00, 0xc0, 0x7f, // f32.const nan
0x43, 0x00, 0x00, 0xc0, 0xff, // f32.const -nan
0x5e, // f32.gt
0x1a, // drop
0x43, 0x01, 0x00, 0x80, 0x7f, // f32.const +nan:0x1
0x43, 0xff, 0xff, 0x8f, 0x7f, // f32.const +nan:0xfffff
0x5f, // f32.le
0x1a, // drop
0x43, 0xcd, 0xcc, 0xcc, 0x3d, // f32.const 0.100000001
0x43, 0x3c, 0xb4, 0x96, 0x49, // f32.const 1234567.5
0x60, // f32.ge
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x8b, // f32.abs
0x8c, // f32.neg
0x8d, // f32.ceil
0x8e, // f32.floor
0x8f, // f32.trunc
0x90, // f32.nearest
0x91, // f32.sqrt
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x92, // f32.add
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x93, // f32.sub
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x94, // f32.mul
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x95, // f32.div
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x96, // f32.min
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x97, // f32.max
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x98, // f32.copysign
0x1a, // drop
0x41, 0x00, // i32.const 0
0xb2, // f32.convert_i32_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0xb3, // f32.convert_i32_u
0x1a, // drop
0x42, 0x00, // i64.const 0
0xb4, // f32.convert_i64_s
0x1a, // drop
0x42, 0x00, // i64.const 0
0xb5, // f32.convert_i64_u
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xb6, // f32.demote_f64
0x1a, // drop
0x41, 0x00, // i32.const 0
0xbe, // f32.reinterpret_i32
0x0b, // end
// function #5 $func5
0xeb, 0x01, // body size 235
0x00, // 0 entries in locals list
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // f64.const -0.0
0x61, // f64.eq
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, // f64.const 1
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xbf, // f64.const -1
0x62, // f64.ne
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, // f64.const inf
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, // f64.const -inf
0x63, // f64.lt
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x7f, // f64.const nan
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, // f64.const -nan
0x64, // f64.gt
0x1a, // drop
0x44, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x7f, // f64.const +nan:0x1
0x44, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, // f64.const +nan:0xfffffffffffff
0x65, // f64.le
0x1a, // drop
0x44, 0x9a, 0x99, 0x99, 0x99, 0x99, 0x99, 0xb9, 0x3f, // f64.const 0.1
0x44, 0x00, 0x00, 0x00, 0x80, 0x87, 0xd6, 0x32, 0x41, // f64.const 1234567.5
0x66, // f64.ge
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0x99, // f64.abs
0x9a, // f64.neg
0x9b, // f64.ceil
0x9c, // f64.floor
0x9d, // f64.trunc
0x9e, // f64.nearest
0x9f, // f64.sqrt
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa0, // f64.add
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa1, // f64.sub
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa2, // f64.mul
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa3, // f64.div
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa4, // f64.min
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa5, // f64.max
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xa6, // f64.copysign
0x1a, // drop
0x41, 0x00, // i32.const 0
0xb7, // f64.convert_i32_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0xb8, // f64.convert_i32_u
0x1a, // drop
0x42, 0x00, // i64.const 0
0xb9, // f64.convert_i64_s
0x1a, // drop
0x42, 0x00, // i64.const 0
0xba, // f64.convert_i64_u
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xbb, // f64.promote_f32
0x1a, // drop
0x42, 0x00, // i64.const 0
0xbf, // f64.reinterpret_i64
0x0b, // end
// function #6 $func6
0x3c, // body size 60
0x00, // 0 entries in locals list
0x10, 0x07, // call $func7
0x42, 0x00, // i64.const 0
0x41, 0x00, // i32.const 0
0x11, 0x06, 0x00, // call_indirect (param i64) (result f64)
0x0f, // return
0x02, 0x40, // block $label0
0x03, 0x40, // loop $label1
0x02, 0x7e, // block (result i64)
0x03, 0x7e, // loop (result i64) $label2
0x0c, 0x03, // br $label0
0x41, 0x00, // i32.const 0
0x0d, 0x02, // br_if $label1
0x41, 0x00, // i32.const 0
0x0e, 0x03, 0x03, 0x02, 0x02, 0x00, // br_table $label0 $label1 $label1 $label2
0x42, 0x00, // i64.const 0
0x0b, // end $label2
0x0b, // end
0x1a, // drop
0x0b, // end $label1
0x0b, // end $label0
0x42, 0x00, // i64.const 0
0x42, 0x01, // i64.const 1
0x41, 0x00, // i32.const 0
0x1b, // select
0x1a, // drop
0x41, 0x00, // i32.const 0
0x04, 0x7e, // if (result i64)
0x42, 0x00, // i64.const 0
0x05, // else
0x42, 0x01, // i64.const 1
0x0b, // end
0x1a, // drop
0x0b, // end
// function #7 $func7
0xa4, 0x01, // body size 164
0x00, // 0 entries in locals list
0x41, 0x00, // i32.const 0
0x28, 0x02, 0x00, // i32.load
0x1a, // drop
0x41, 0x00, // i32.const 0
0x29, 0x03, 0x03, // i64.load offset=3
0x1a, // drop
0x41, 0x00, // i32.const 0
0x2a, 0x01, 0x00, // f32.load align=2
0x1a, // drop
0x41, 0x00, // i32.const 0
0x2b, 0x02, 0x03, // f64.load offset=3 align=4
0x1a, // drop
0x41, 0x00, // i32.const 0
0x2c, 0x00, 0x00, // i32.load8_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0x2d, 0x00, 0x00, // i32.load8_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0x2e, 0x01, 0x00, // i32.load16_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0x2f, 0x01, 0x00, // i32.load16_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0x30, 0x00, 0x00, // i64.load8_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0x31, 0x00, 0x00, // i64.load8_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0x32, 0x01, 0x00, // i64.load16_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0x33, 0x01, 0x00, // i64.load16_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0x34, 0x02, 0x00, // i64.load32_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0x35, 0x02, 0x00, // i64.load32_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0x41, 0x00, // i32.const 0
0x36, 0x02, 0x00, // i32.store
0x41, 0x00, // i32.const 0
0x42, 0x00, // i64.const 0
0x37, 0x03, 0x00, // i64.store
0x41, 0x00, // i32.const 0
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0x38, 0x02, 0x00, // f32.store
0x41, 0x00, // i32.const 0
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0x39, 0x03, 0x00, // f64.store
0x41, 0x00, // i32.const 0
0x41, 0x00, // i32.const 0
0x3a, 0x00, 0x00, // i32.store8
0x41, 0x00, // i32.const 0
0x41, 0x00, // i32.const 0
0x3b, 0x01, 0x00, // i32.store16
0x41, 0x00, // i32.const 0
0x42, 0x00, // i64.const 0
0x3c, 0x00, 0x00, // i64.store8
0x41, 0x00, // i32.const 0
0x42, 0x00, // i64.const 0
0x3d, 0x01, 0x00, // i64.store16
0x41, 0x00, // i32.const 0
0x42, 0x00, // i64.const 0
0x3e, 0x02, 0x00, // i64.store32
0x3f, 0x00, // memory.size
0x40, 0x00, // memory.grow
0x1a, // drop
0x0b, // end
// function #8 $exported_function
0x04, // body size 4
0x00, // 0 entries in locals list
0x01, // nop
0x00, // unreachable
0x0b, // end
0x0b, // section kind: Data
0x0b, // section length 11
0x01, 0x00, // data segments count 1: flag: active no index
0x23, 0x00, 0x0b, // global.get $env.imported_global
0x05, // source size 5
0x66, 0x6f, 0x6f, 0x0a, 0x00, // segment data

View File

@ -0,0 +1,488 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
;; Imports.
(global $env.imported_global (;0;) (import "env" "imported_global") i32)
(func $env.imported_function (;0;) (import "env" "imported_function"))
;; Table and memory sections.
(table $table0 4 funcref)
(memory $memory0 0 1)
;; Non-imported globals: mutable, non-mutable, exported.
(global $global1 (mut i32) (i32.const 0))
(global $global2 i32 (global.get $env.imported_global))
(global $exported_global (;3;) (export "exported_global") i64 (i64.const 0))
;; Element section for table initialization.
(elem $elem0 (i32.const 0) (ref func) (ref.func $env.imported_function) (ref.func $func1) (ref.func $func1) (ref.func $func3))
;; Instructions on globals, locals, parameters.
(func $func1 (param $var0 i32)
(local $var1 i64)
local.get $var0
local.tee $var0
local.set $var0
local.get $var1
local.tee $var1
local.set $var1
global.get $global1
global.set $global1
)
;; i32 operations.
(func $func2 (result i32)
;; Comparisons and constant literals.
i32.const 0
i32.eqz
i32.const 1
i32.eq
i32.const -1
i32.ne
i32.const 2147483647
i32.lt_s
i32.const -2147483648
i32.lt_u
i32.const 0
i32.gt_s
i32.const 0
i32.gt_u
i32.const 0
i32.le_s
i32.const 0
i32.le_u
i32.const 0
i32.ge_s
i32.const 0
i32.ge_u
;; Bitcounting.
i32.clz
i32.ctz
i32.popcnt
;; Arithmetic and logic.
i32.const 0
i32.add
i32.const 0
i32.sub
i32.const 0
i32.mul
i32.const 0
i32.div_s
i32.const 0
i32.div_u
i32.const 0
i32.rem_s
i32.const 0
i32.rem_u
i32.const 0
i32.and
i32.const 0
i32.or
i32.const 0
i32.xor
i32.const 0
i32.shl
i32.const 0
i32.shr_s
i32.const 0
i32.shr_u
i32.const 0
i32.rotl
i32.const 0
i32.rotr
drop
;; Conversions.
i64.const 0
i32.wrap_i64
drop
f32.const 0.0
i32.trunc_f32_s
drop
f32.const 0.0
i32.trunc_f32_u
drop
f64.const 0.0
i32.trunc_f64_s
drop
f64.const 0.0
i32.trunc_f64_u
drop
f32.const 0.0
i32.reinterpret_f32
i32.extend8_s
i32.extend16_s
)
;; i64 operations.
(func $func3 (result i64)
;; Comparisons and constant literals.
i64.const 0
i64.eqz
drop
i64.const 1
i64.const -1
i64.eq
drop
i64.const -9223372036854775808
i64.const 9223372036854775807
i64.ne
drop
i64.const 0
i64.const 0
i64.lt_s
drop
i64.const 0
i64.const 0
i64.lt_u
drop
i64.const 0
i64.const 0
i64.gt_s
drop
i64.const 0
i64.const 0
i64.gt_u
drop
i64.const 0
i64.const 0
i64.le_s
drop
i64.const 0
i64.const 0
i64.le_u
drop
i64.const 0
i64.const 0
i64.ge_s
drop
i64.const 0
i64.const 0
i64.ge_u
drop
;; Bitcounting.
i64.const 0
i64.clz
i64.ctz
i64.popcnt
;; Arithmetic and logic.
i64.const 0
i64.add
i64.const 0
i64.sub
i64.const 0
i64.mul
i64.const 0
i64.div_s
i64.const 0
i64.div_u
i64.const 0
i64.rem_s
i64.const 0
i64.rem_u
i64.const 0
i64.and
i64.const 0
i64.or
i64.const 0
i64.xor
i64.const 0
i64.shl
i64.const 0
i64.shr_s
i64.const 0
i64.shr_u
i64.const 0
i64.rotl
i64.const 0
i64.rotr
drop
;; Conversions.
i32.const 0
i64.extend_i32_s
drop
i32.const 0
i64.extend_i32_u
drop
f32.const 0.0
i64.trunc_f32_s
drop
f32.const 0.0
i64.trunc_f32_u
drop
f64.const 0.0
i64.trunc_f64_s
drop
f64.const 0.0
i64.trunc_f64_u
drop
f64.const 0.0
i64.reinterpret_f64
i64.extend8_s
i64.extend16_s
i64.extend32_s
)
;; f32 operations.
(func $func4 (result f32)
;; Comparisons and constant literals.
f32.const 0.0
f32.const -0.0
f32.eq
drop
f32.const 1
f32.const -1
f32.ne
drop
f32.const inf
f32.const -inf
f32.lt
drop
f32.const nan
f32.const -nan
f32.gt
drop
;; Non-canonical NaN encodings.
f32.const +nan:0x1
f32.const +nan:0xfffff
f32.le
drop
;; TODO(dlehmann): Change to `0.1`, once `ImmediatesPrinter` is improved to
;; print floats as shortest round-trippable decimal representation.
f32.const 0.100000001
f32.const 1234567.5
f32.ge
drop
;; Arithmetic.
f32.const 0.0
f32.abs
f32.neg
f32.ceil
f32.floor
f32.trunc
f32.nearest
f32.sqrt
f32.const 0.0
f32.add
f32.const 0.0
f32.sub
f32.const 0.0
f32.mul
f32.const 0.0
f32.div
f32.const 0.0
f32.min
f32.const 0.0
f32.max
f32.const 0.0
f32.copysign
drop
;; Conversions.
i32.const 0
f32.convert_i32_s
drop
i32.const 0
f32.convert_i32_u
drop
i64.const 0
f32.convert_i64_s
drop
i64.const 0
f32.convert_i64_u
drop
f64.const 0.0
f32.demote_f64
drop
i32.const 0
f32.reinterpret_i32
)
;; f64 operations.
(func $func5 (result f64)
;; Comparisons and constant literals.
f64.const 0.0
f64.const -0.0
f64.eq
drop
f64.const 1
f64.const -1
f64.ne
drop
f64.const inf
f64.const -inf
f64.lt
drop
f64.const nan
f64.const -nan
f64.gt
drop
;; Non-canonical NaN encodings.
f64.const +nan:0x1
f64.const +nan:0xfffffffffffff
f64.le
drop
f64.const 0.1
f64.const 1234567.5
f64.ge
drop
;; Arithmetic.
f64.const 0.0
f64.abs
f64.neg
f64.ceil
f64.floor
f64.trunc
f64.nearest
f64.sqrt
f64.const 0.0
f64.add
f64.const 0.0
f64.sub
f64.const 0.0
f64.mul
f64.const 0.0
f64.div
f64.const 0.0
f64.min
f64.const 0.0
f64.max
f64.const 0.0
f64.copysign
drop
;; Conversions.
i32.const 0
f64.convert_i32_s
drop
i32.const 0
f64.convert_i32_u
drop
i64.const 0
f64.convert_i64_s
drop
i64.const 0
f64.convert_i64_u
drop
f32.const 0.0
f64.promote_f32
drop
i64.const 0
f64.reinterpret_i64
)
;; Control-flow.
(func $func6
;; Calls and return.
call $func7
i64.const 0
i32.const 0
call_indirect (param i64) (result f64)
return
;; Blocks and loops, with and without block type.
block $label0
loop $label1
block (result i64)
loop $label2 (result i64)
;; Branches
br $label0
i32.const 0
br_if $label1
i32.const 0
br_table $label0 $label1 $label1 $label2
i64.const 0
end $label2
end
drop
end $label1
end $label0
;; Select and if.
i64.const 0
i64.const 1
i32.const 0
select
drop
i32.const 0
if (result i64)
i64.const 0
else
i64.const 1
end
drop
)
;; Memory operations.
(func $func7
;; Loads.
i32.const 0
i32.load
drop
i32.const 0
;; Non-default memargs.
i64.load offset=3
drop
i32.const 0
f32.load align=2
drop
i32.const 0
f64.load offset=3 align=4
drop
i32.const 0
i32.load8_s
drop
i32.const 0
i32.load8_u
drop
i32.const 0
i32.load16_s
drop
i32.const 0
i32.load16_u
drop
i32.const 0
i64.load8_s
drop
i32.const 0
i64.load8_u
drop
i32.const 0
i64.load16_s
drop
i32.const 0
i64.load16_u
drop
i32.const 0
i64.load32_s
drop
i32.const 0
i64.load32_u
drop
;; Stores.
i32.const 0
i32.const 0
i32.store
i32.const 0
i64.const 0
i64.store
i32.const 0
f32.const 0.0
f32.store
i32.const 0
f64.const 0.0
f64.store
i32.const 0
i32.const 0
i32.store8
i32.const 0
i32.const 0
i32.store16
i32.const 0
i64.const 0
i64.store8
i32.const 0
i64.const 0
i64.store16
i32.const 0
i64.const 0
i64.store32
;; Other memory instructions.
memory.size
memory.grow
drop
)
;; Other instructions. (Also an exported function.)
(func $exported_function (;8;) (export "exported_function")
nop
unreachable
)
;; Data and element sections.
(data (global.get $env.imported_global) "foo\0a\00")
)
;;)---";

View File

@ -0,0 +1,201 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x0a, // section length 10
0x02, // types count 2
0x60, // kind: func
0x00, // param count 0
0x00, // return count 0
0x60, // kind: func
0x03, // param count 3
0x7f, 0x7f, 0x7e, // i32 i32 i64
0x00, // return count 0
0x02, // section kind: Import
0x30, // section length 48
0x02, // imports count 2
// import #0
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x0f, // field name length: 15
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: imported_global
0x03, 0x7f, 0x00, // kind: global i32 immutable
// import #1
0x03, // module name length: 3
0x65, 0x6e, 0x76, // module name: env
0x11, // field name length: 17
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, // field name: imported_function
0x00, 0x00, // kind: function
0x03, // section kind: Function
0x03, // section length 3
0x02, // functions count 2
0x01, // 1 $function_with_name (param i32 i32 i64)
0x00, // 2 $exported_function_with_name
0x04, // section kind: Table
0x04, // section length 4
0x01, 0x70, 0x00, // table count 1: funcref no maximum
0x00, // initial size 0
0x05, // section kind: Memory
0x03, // section length 3
0x01, 0x00, // memory count 1: no maximum
0x00, // initial size 0
0x06, // section kind: Global
0x0b, // section length 11
0x02, // globals count 2
0x7f, 0x00, // global #1: i32 immutable
0x41, 0x00, 0x0b, // i32.const 0
0x7f, 0x00, // global #2: i32 immutable
0x41, 0x00, 0x0b, // i32.const 0
0x07, // section kind: Export
0x27, // section length 39
0x02, // exports count 2
// export # 0
0x0f, // field name length: 15
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
// field name: exported_global
0x03, 0x02, // kind: global index: 2
// export # 1
0x11, // field name length: 17
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, // field name: exported_function
0x00, 0x02, // kind: function index: 2
0x09, // section kind: Element
0x05, // section length 5
0x01, 0x01, 0x00, // segment count 1: flag: passive, element type: function
0x01, 0x01, // number of elements 1: index: 1
0x0a, // section kind: Code
0x1d, // section length 29
0x02, // functions count 2
// function #1 $function_with_name
0x0b, // body size 11
0x00, // 0 entries in locals list
0x20, 0x00, // local.get $param_with_name_1
0x1a, // drop
0x20, 0x01, // local.get $param_with_name_2
0x1a, // drop
0x20, 0x02, // local.get $param_with_name_3
0x1a, // drop
0x0b, // end
// function #2 $exported_function_with_name
0x0f, // body size 15
0x02, // 2 entries in locals list
0x02, 0x7f, // 2 locals of type i32
0x01, 0x7e, // 1 local of type i64
0x20, 0x00, // local.get $local_with_name_1
0x1a, // drop
0x20, 0x01, // local.get $local_with_name_2
0x1a, // drop
0x20, 0x02, // local.get $local_with_name_3
0x1a, // drop
0x0b, // end
0x0b, // section kind: Data
0x0b, // section length 11
0x01, 0x00, // data segments count 1: flag: active no index
0x41, 0x00, 0x0b, // i32.const 0
0x05, // source size 5
0x66, 0x6f, 0x6f, 0x0a, 0x00, // segment data
0x00, // section kind: Unknown
0xd8, 0x02, // section length 344
0x04, // section name length: 4
0x6e, 0x61, 0x6d, 0x65, // section name: name
0x01, // name type: function
0x4f, // payload length: 79
0x03, // names count 3
0x00, 0x1b, // index 0 name length: 27
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e,
0x61, 0x6d, 0x65, // name: imported_function_with_name
0x01, 0x12, // index 1 name length: 18
0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61,
0x6d, 0x65, // name: function_with_name
0x02, 0x1b, // index 2 name length: 27
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e,
0x61, 0x6d, 0x65, // name: exported_function_with_name
0x02, // name type: local
0x79, // payload length: 121
0x03, // outer count 3
0x00, 0x00, // outer index 0 inner count 0
0x01, 0x03, // outer index 1 inner count 3
0x00, 0x11, // inner index 0 name length: 17
0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x31, // name: param_with_name_1
0x01, 0x11, // inner index 1 name length: 17
0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x32, // name: param_with_name_2
0x02, 0x11, // inner index 2 name length: 17
0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x33, // name: param_with_name_3
0x02, 0x03, // outer index 2 inner count 3
0x00, 0x11, // inner index 0 name length: 17
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x31, // name: local_with_name_1
0x01, 0x11, // inner index 1 name length: 17
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x32, // name: local_with_name_2
0x02, 0x11, // inner index 2 name length: 17
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
0x33, // name: local_with_name_3
0x05, // name type: table
0x12, // payload length: 18
0x01, // names count 1
0x00, 0x0f, // index 0 name length: 15
0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x77, 0x69,
0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: table_with_name
0x06, // name type: memory
0x13, // payload length: 19
0x01, // names count 1
0x00, 0x10, // index 0 name length: 16
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x77,
0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: memory_with_name
0x07, // name type: global
0x49, // payload length: 73
0x03, // names count 3
0x00, 0x19, // index 0 name length: 25
0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f,
0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d,
0x65, // name: imported_global_with_name
0x01, 0x10, // index 1 name length: 16
0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x77,
0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: global_with_name
0x02, 0x19, // index 2 name length: 25
0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x5f,
0x77, 0x69, 0x74, 0x68, 0x5f, 0x6e, 0x61, 0x6d,
0x65, // name: exported_global_with_name
0x08, // name type: element segment
0x11, // payload length: 17
0x01, // names count 1
0x00, 0x0e, // index 0 name length: 14
0x65, 0x6c, 0x65, 0x6d, 0x5f, 0x77, 0x69, 0x74,
0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
// name: elem_with_name

View File

@ -0,0 +1,42 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
;; TODO(jkummerow): This type name is missing from the disassembler ouput.
;; (type $type_with_name (;0;) (func (param f32)))
(global $imported_global_with_name (;0;) (import "env" "imported_global") i32)
(func $imported_function_with_name (;0;) (import "env" "imported_function"))
(table $table_with_name (;0;) 0 funcref)
(memory $memory_with_name (;0;) 0)
(global $global_with_name (;1;) i32 (i32.const 0))
(global $exported_global_with_name (;2;) (export "exported_global") i32 (i32.const 0))
(elem $elem_with_name (;0;) (ref func) (ref.func $function_with_name))
(func $function_with_name (;1;) (param $param_with_name_1 (;0;) i32) (param $param_with_name_2 (;1;) i32) (param $param_with_name_3 (;2;) i64)
local.get $param_with_name_1
drop
local.get $param_with_name_2
drop
local.get $param_with_name_3
drop
)
(func $exported_function_with_name (;2;) (export "exported_function")
;; Local variables.
(local $local_with_name_1 i32)
(local $local_with_name_2 i32)
(local $local_with_name_3 i64)
local.get $local_with_name_1
drop
local.get $local_with_name_2
drop
local.get $local_with_name_3
drop
)
;; TODO(jkummerow): Functions with a named type are printed with their type
;; inline instead of as follows.
;; (func $another_function (;3;) (type $type_with_name)
;; )
;; For compatibility with the legacy DevTools behavior, we don't print data
;; segment names. If we change that, uncomment the following line.
;; (data $data_with_name (;0;) (i32.const 0) "foo\0a\00")
(data (i32.const 0) "foo\0a\00")
)
;;)---";

View File

@ -0,0 +1,507 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x08, // section length 8
0x02, // types count 2
0x60, // kind: func
0x00, // param count 0
0x01, 0x7b, // return count 1: v128
0x60, // kind: func
0x00, // param count 0
0x00, // return count 0
0x03, // section kind: Function
0x03, // section length 3
0x02, // functions count 2
0x00, // 0 $func0 (result v128)
0x01, // 1 $func1
0x05, // section kind: Memory
0x03, // section length 3
0x01, 0x00, // memory count 1: no maximum
0x00, // initial size 0
0x0a, // section kind: Code
0xed, 0x08, // section length 1133
0x02, // functions count 2
// function #0 $func0
0x14, // body size 20
0x00, // 0 entries in locals list
0xfd, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000
0x0b, // end
// function #1 $func1
0xd5, 0x08, // body size 1109
0x00, // 0 entries in locals list
0x41, 0x00, // i32.const 0
0xfd, 0x00, 0x04, 0x00, // v128.load
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x01, 0x03, 0x03, // v128.load8x8_s offset=3
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x02, 0x01, 0x00, // v128.load8x8_u align=2
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x03, 0x02, 0x03, // v128.load16x4_s offset=3 align=4
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x04, 0x03, 0x00, // v128.load16x4_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x05, 0x03, 0x00, // v128.load32x2_s
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x06, 0x03, 0x00, // v128.load32x2_u
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x07, 0x00, 0x00, // v128.load8_splat
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x08, 0x01, 0x00, // v128.load16_splat
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x09, 0x02, 0x00, // v128.load32_splat
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x0a, 0x03, 0x00, // v128.load64_splat
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x5c, 0x02, 0x00, // v128.load32_zero
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x5d, 0x03, 0x00, // v128.load64_zero
0x1a, // drop
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x0b, 0x04, 0x00, // v128.store
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x54, 0x00, 0x00, 0x00, // v128.load8_lane 0
0x1a, // drop
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x55, 0x01, 0x00, 0x01, // v128.load16_lane 1
0x1a, // drop
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x56, 0x02, 0x00, 0x03, // v128.load32_lane 3
0x1a, // drop
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x57, 0x03, 0x00, 0x00, // v128.load64_lane 0
0x1a, // drop
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x59, 0x01, 0x00, 0x00, // v128.store16_lane 0
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x5a, 0x02, 0x00, 0x01, // v128.store32_lane 1
0x41, 0x00, // i32.const 0
0x10, 0x00, // call $func0
0xfd, 0x5b, 0x03, 0x00, 0x00, // v128.store64_lane 0
0x10, 0x00, // call $func0
0x10, 0x00, // call $func0
0xfd, 0x0d, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, // i8x16.shuffle 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0x10, 0x00, // call $func0
0xfd, 0x0e, // i8x16.swizzle
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x0f, // i8x16.splat
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x10, // i16x8.splat
0x1a, // drop
0x41, 0x00, // i32.const 0
0xfd, 0x11, // i32x4.splat
0x1a, // drop
0x42, 0x00, // i64.const 0
0xfd, 0x12, // i64x2.splat
0x1a, // drop
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xfd, 0x13, // f32x4.splat
0x1a, // drop
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xfd, 0x14, // f64x2.splat
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x15, 0x00, // i8x16.extract_lane_s 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x16, 0x00, // i8x16.extract_lane_u 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x18, 0x00, // i16x8.extract_lane_s 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x19, 0x00, // i16x8.extract_lane_u 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x1b, 0x00, // i32x4.extract_lane 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x1d, 0x00, // i64x2.extract_lane 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x1f, 0x00, // f32x4.extract_lane 0
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x21, 0x00, // f64x2.extract_lane 0
0x1a, // drop
0x10, 0x00, // call $func0
0x41, 0x00, // i32.const 0
0xfd, 0x17, 0x00, // i8x16.replace_lane 0
0x41, 0x00, // i32.const 0
0xfd, 0x1a, 0x00, // i16x8.replace_lane 0
0x41, 0x00, // i32.const 0
0xfd, 0x1c, 0x00, // i32x4.replace_lane 0
0x42, 0x00, // i64.const 0
0xfd, 0x1e, 0x00, // i64x2.replace_lane 0
0x43, 0x00, 0x00, 0x00, 0x00, // f32.const 0.0
0xfd, 0x20, 0x00, // f32x4.replace_lane 0
0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // f64.const 0.0
0xfd, 0x22, 0x00, // f64x2.replace_lane 0
0x10, 0x00, // call $func0
0xfd, 0x23, // i8x16.eq
0x10, 0x00, // call $func0
0xfd, 0x24, // i8x16.ne
0x10, 0x00, // call $func0
0xfd, 0x25, // i8x16.lt_s
0x10, 0x00, // call $func0
0xfd, 0x26, // i8x16.lt_u
0x10, 0x00, // call $func0
0xfd, 0x27, // i8x16.gt_s
0x10, 0x00, // call $func0
0xfd, 0x28, // i8x16.gt_u
0x10, 0x00, // call $func0
0xfd, 0x29, // i8x16.le_s
0x10, 0x00, // call $func0
0xfd, 0x2a, // i8x16.le_u
0x10, 0x00, // call $func0
0xfd, 0x2b, // i8x16.ge_s
0x10, 0x00, // call $func0
0xfd, 0x2c, // i8x16.ge_u
0x10, 0x00, // call $func0
0xfd, 0x2d, // i16x8.eq
0x10, 0x00, // call $func0
0xfd, 0x2e, // i16x8.ne
0x10, 0x00, // call $func0
0xfd, 0x2f, // i16x8.lt_s
0x10, 0x00, // call $func0
0xfd, 0x30, // i16x8.lt_u
0x10, 0x00, // call $func0
0xfd, 0x31, // i16x8.gt_s
0x10, 0x00, // call $func0
0xfd, 0x32, // i16x8.gt_u
0x10, 0x00, // call $func0
0xfd, 0x33, // i16x8.le_s
0x10, 0x00, // call $func0
0xfd, 0x34, // i16x8.le_u
0x10, 0x00, // call $func0
0xfd, 0x35, // i16x8.ge_s
0x10, 0x00, // call $func0
0xfd, 0x36, // i16x8.ge_u
0x10, 0x00, // call $func0
0xfd, 0x37, // i32x4.eq
0x10, 0x00, // call $func0
0xfd, 0x38, // i32x4.ne
0x10, 0x00, // call $func0
0xfd, 0x39, // i32x4.lt_s
0x10, 0x00, // call $func0
0xfd, 0x3a, // i32x4.lt_u
0x10, 0x00, // call $func0
0xfd, 0x3b, // i32x4.gt_s
0x10, 0x00, // call $func0
0xfd, 0x3c, // i32x4.gt_u
0x10, 0x00, // call $func0
0xfd, 0x3d, // i32x4.le_s
0x10, 0x00, // call $func0
0xfd, 0x3e, // i32x4.le_u
0x10, 0x00, // call $func0
0xfd, 0x3f, // i32x4.ge_s
0x10, 0x00, // call $func0
0xfd, 0x40, // i32x4.ge_u
0x10, 0x00, // call $func0
0xfd, 0xd6, 0x01, // i64x2.eq
0x10, 0x00, // call $func0
0xfd, 0xd7, 0x01, // i64x2.ne
0x10, 0x00, // call $func0
0xfd, 0xd8, 0x01, // i64x2.lt_s
0x10, 0x00, // call $func0
0xfd, 0xd9, 0x01, // i64x2.gt_s
0x10, 0x00, // call $func0
0xfd, 0xda, 0x01, // i64x2.le_s
0x10, 0x00, // call $func0
0xfd, 0xdb, 0x01, // i64x2.ge_s
0x10, 0x00, // call $func0
0xfd, 0x41, // f32x4.eq
0x10, 0x00, // call $func0
0xfd, 0x42, // f32x4.ne
0x10, 0x00, // call $func0
0xfd, 0x43, // f32x4.lt
0x10, 0x00, // call $func0
0xfd, 0x44, // f32x4.gt
0x10, 0x00, // call $func0
0xfd, 0x45, // f32x4.le
0x10, 0x00, // call $func0
0xfd, 0x46, // f32x4.ge
0x10, 0x00, // call $func0
0xfd, 0x47, // f64x2.eq
0x10, 0x00, // call $func0
0xfd, 0x48, // f64x2.ne
0x10, 0x00, // call $func0
0xfd, 0x49, // f64x2.lt
0x10, 0x00, // call $func0
0xfd, 0x4a, // f64x2.gt
0x10, 0x00, // call $func0
0xfd, 0x4b, // f64x2.le
0x10, 0x00, // call $func0
0xfd, 0x4c, // f64x2.ge
0xfd, 0x4d, // v128.not
0x10, 0x00, // call $func0
0xfd, 0x4e, // v128.and
0x10, 0x00, // call $func0
0xfd, 0x4f, // v128.andnot
0x10, 0x00, // call $func0
0xfd, 0x50, // v128.or
0x10, 0x00, // call $func0
0xfd, 0x51, // v128.xor
0x10, 0x00, // call $func0
0x10, 0x00, // call $func0
0xfd, 0x52, // v128.bitselect
0xfd, 0x53, // v128.any_true
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x5e, // f32x4.demote_f64x2_zero
0xfd, 0x5f, // f64x2.promote_low_f32x4
0xfd, 0x60, // i8x16.abs
0xfd, 0x61, // i8x16.neg
0xfd, 0x62, // i8x16.popcnt
0xfd, 0x63, // i8x16.all_true
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x64, // i8x16.bitmask
0x1a, // drop
0x10, 0x00, // call $func0
0x10, 0x00, // call $func0
0xfd, 0x65, // i8x16.narrow_i16x8_s
0x10, 0x00, // call $func0
0xfd, 0x66, // i8x16.narrow_i16x8_u
0x41, 0x00, // i32.const 0
0xfd, 0x6b, // i8x16.shl
0x41, 0x00, // i32.const 0
0xfd, 0x6c, // i8x16.shr_s
0x41, 0x00, // i32.const 0
0xfd, 0x6d, // i8x16.shr_u
0x10, 0x00, // call $func0
0xfd, 0x6e, // i8x16.add
0x10, 0x00, // call $func0
0xfd, 0x6f, // i8x16.add_sat_s
0x10, 0x00, // call $func0
0xfd, 0x70, // i8x16.add_sat_u
0x10, 0x00, // call $func0
0xfd, 0x71, // i8x16.sub
0x10, 0x00, // call $func0
0xfd, 0x72, // i8x16.sub_sat_s
0x10, 0x00, // call $func0
0xfd, 0x73, // i8x16.sub_sat_u
0x10, 0x00, // call $func0
0xfd, 0x76, // i8x16.min_s
0x10, 0x00, // call $func0
0xfd, 0x77, // i8x16.min_u
0x10, 0x00, // call $func0
0xfd, 0x78, // i8x16.max_s
0x10, 0x00, // call $func0
0xfd, 0x79, // i8x16.max_u
0x10, 0x00, // call $func0
0xfd, 0x7b, // i8x16.avgr_u
0xfd, 0x80, 0x01, // i16x8.abs
0xfd, 0x81, 0x01, // i16x8.neg
0x10, 0x00, // call $func0
0xfd, 0x82, 0x01, // i16x8.q15mulr_sat_s
0xfd, 0x83, 0x01, // i16x8.all_true
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0x84, 0x01, // i16x8.bitmask
0x1a, // drop
0x10, 0x00, // call $func0
0x10, 0x00, // call $func0
0xfd, 0x85, 0x01, // i16x8.narrow_i32x4_s
0x10, 0x00, // call $func0
0xfd, 0x86, 0x01, // i16x8.narrow_i32x4_u
0xfd, 0x87, 0x01, // i16x8.extend_low_i8x16_s
0xfd, 0x88, 0x01, // i16x8.extend_high_i8x16_s
0xfd, 0x89, 0x01, // i16x8.extend_low_i8x16_u
0xfd, 0x8a, 0x01, // i16x8.extend_high_i8x16_u
0x41, 0x00, // i32.const 0
0xfd, 0x8b, 0x01, // i16x8.shl
0x41, 0x00, // i32.const 0
0xfd, 0x8c, 0x01, // i16x8.shr_s
0x41, 0x00, // i32.const 0
0xfd, 0x8d, 0x01, // i16x8.shr_u
0x10, 0x00, // call $func0
0xfd, 0x8e, 0x01, // i16x8.add
0x10, 0x00, // call $func0
0xfd, 0x8f, 0x01, // i16x8.add_sat_s
0x10, 0x00, // call $func0
0xfd, 0x90, 0x01, // i16x8.add_sat_u
0x10, 0x00, // call $func0
0xfd, 0x91, 0x01, // i16x8.sub
0x10, 0x00, // call $func0
0xfd, 0x92, 0x01, // i16x8.sub_sat_s
0x10, 0x00, // call $func0
0xfd, 0x93, 0x01, // i16x8.sub_sat_u
0x10, 0x00, // call $func0
0xfd, 0x95, 0x01, // i16x8.mul
0x10, 0x00, // call $func0
0xfd, 0x96, 0x01, // i16x8.min_s
0x10, 0x00, // call $func0
0xfd, 0x97, 0x01, // i16x8.min_u
0x10, 0x00, // call $func0
0xfd, 0x98, 0x01, // i16x8.max_s
0x10, 0x00, // call $func0
0xfd, 0x99, 0x01, // i16x8.max_u
0x10, 0x00, // call $func0
0xfd, 0x9b, 0x01, // i16x8.avgr_u
0xfd, 0xa0, 0x01, // i32x4.abs
0xfd, 0xa1, 0x01, // i32x4.neg
0xfd, 0xa3, 0x01, // i32x4.all_true
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0xa4, 0x01, // i32x4.bitmask
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0xa7, 0x01, // i32x4.extend_low_i16x8_s
0xfd, 0xa8, 0x01, // i32x4.extend_high_i16x8_s
0xfd, 0xa9, 0x01, // i32x4.extend_low_i16x8_u
0xfd, 0xaa, 0x01, // i32x4.extend_high_i16x8_u
0x41, 0x00, // i32.const 0
0xfd, 0xab, 0x01, // i32x4.shl
0x41, 0x00, // i32.const 0
0xfd, 0xac, 0x01, // i32x4.shr_s
0x41, 0x00, // i32.const 0
0xfd, 0xad, 0x01, // i32x4.shr_u
0x10, 0x00, // call $func0
0xfd, 0xae, 0x01, // i32x4.add
0x10, 0x00, // call $func0
0xfd, 0xb1, 0x01, // i32x4.sub
0x10, 0x00, // call $func0
0xfd, 0xb5, 0x01, // i32x4.mul
0x10, 0x00, // call $func0
0xfd, 0xb6, 0x01, // i32x4.min_s
0x10, 0x00, // call $func0
0xfd, 0xb7, 0x01, // i32x4.min_u
0x10, 0x00, // call $func0
0xfd, 0xb8, 0x01, // i32x4.max_s
0x10, 0x00, // call $func0
0xfd, 0xb9, 0x01, // i32x4.max_u
0x10, 0x00, // call $func0
0xfd, 0xba, 0x01, // i32x4.dot_i16x8_s
0xfd, 0xc0, 0x01, // i64x2.abs
0xfd, 0xc1, 0x01, // i64x2.neg
0xfd, 0xc3, 0x01, // i64x2.all_true
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0xc4, 0x01, // i64x2.bitmask
0x1a, // drop
0x10, 0x00, // call $func0
0xfd, 0xc7, 0x01, // i64x2.extend_low_i32x4_s
0xfd, 0xc8, 0x01, // i64x2.extend_high_i32x4_s
0xfd, 0xc9, 0x01, // i64x2.extend_low_i32x4_u
0xfd, 0xca, 0x01, // i64x2.extend_high_i32x4_u
0x41, 0x00, // i32.const 0
0xfd, 0xcb, 0x01, // i64x2.shl
0x41, 0x00, // i32.const 0
0xfd, 0xcc, 0x01, // i64x2.shr_s
0x41, 0x00, // i32.const 0
0xfd, 0xcd, 0x01, // i64x2.shr_u
0x10, 0x00, // call $func0
0xfd, 0xce, 0x01, // i64x2.add
0x10, 0x00, // call $func0
0xfd, 0xd1, 0x01, // i64x2.sub
0x10, 0x00, // call $func0
0xfd, 0xd5, 0x01, // i64x2.mul
0xfd, 0x67, // f32x4.ceil
0xfd, 0x68, // f32x4.floor
0xfd, 0x69, // f32x4.trunc
0xfd, 0x6a, // f32x4.nearest
0xfd, 0xe0, 0x01, // f32x4.abs
0xfd, 0xe1, 0x01, // f32x4.neg
0xfd, 0xe3, 0x01, // f32x4.sqrt
0x10, 0x00, // call $func0
0xfd, 0xe4, 0x01, // f32x4.add
0x10, 0x00, // call $func0
0xfd, 0xe5, 0x01, // f32x4.sub
0x10, 0x00, // call $func0
0xfd, 0xe6, 0x01, // f32x4.mul
0x10, 0x00, // call $func0
0xfd, 0xe7, 0x01, // f32x4.div
0x10, 0x00, // call $func0
0xfd, 0xe8, 0x01, // f32x4.min
0x10, 0x00, // call $func0
0xfd, 0xe9, 0x01, // f32x4.max
0x10, 0x00, // call $func0
0xfd, 0xea, 0x01, // f32x4.pmin
0x10, 0x00, // call $func0
0xfd, 0xeb, 0x01, // f32x4.pmax
0xfd, 0x74, // f64x2.ceil
0xfd, 0x75, // f64x2.floor
0xfd, 0x7a, // f64x2.trunc
0xfd, 0x94, 0x01, // f64x2.nearest
0xfd, 0xec, 0x01, // f64x2.abs
0xfd, 0xed, 0x01, // f64x2.neg
0xfd, 0xef, 0x01, // f64x2.sqrt
0x10, 0x00, // call $func0
0xfd, 0xf0, 0x01, // f64x2.add
0x10, 0x00, // call $func0
0xfd, 0xf1, 0x01, // f64x2.sub
0x10, 0x00, // call $func0
0xfd, 0xf2, 0x01, // f64x2.mul
0x10, 0x00, // call $func0
0xfd, 0xf3, 0x01, // f64x2.div
0x10, 0x00, // call $func0
0xfd, 0xf4, 0x01, // f64x2.min
0x10, 0x00, // call $func0
0xfd, 0xf5, 0x01, // f64x2.max
0x10, 0x00, // call $func0
0xfd, 0xf6, 0x01, // f64x2.pmin
0x10, 0x00, // call $func0
0xfd, 0xf7, 0x01, // f64x2.pmax
0xfd, 0x7c, // i16x8.extadd_pairwise_i8x16_s
0xfd, 0x7d, // i16x8.extadd_pairwise_i8x16_u
0xfd, 0x7e, // i32x4.extadd_pairwise_i16x8_s
0xfd, 0x7f, // i32x4.extadd_pairwise_i16x8_u
0x10, 0x00, // call $func0
0xfd, 0x9c, 0x01, // i16x8.extmul_low_i8x16_s
0x10, 0x00, // call $func0
0xfd, 0x9d, 0x01, // i16x8.extmul_high_i8x16_s
0x10, 0x00, // call $func0
0xfd, 0x9e, 0x01, // i16x8.extmul_low_i8x16_u
0x10, 0x00, // call $func0
0xfd, 0x9f, 0x01, // i16x8.extmul_high_i8x16_u
0x10, 0x00, // call $func0
0xfd, 0xbc, 0x01, // i32x4.extmul_low_i16x8_s
0x10, 0x00, // call $func0
0xfd, 0xbd, 0x01, // i32x4.extmul_high_i16x8_s
0x10, 0x00, // call $func0
0xfd, 0xbe, 0x01, // i32x4.extmul_low_i16x8_u
0x10, 0x00, // call $func0
0xfd, 0xbf, 0x01, // i32x4.extmul_high_i16x8_u
0x10, 0x00, // call $func0
0xfd, 0xdc, 0x01, // i64x2.extmul_low_i32x4_s
0x10, 0x00, // call $func0
0xfd, 0xdd, 0x01, // i64x2.extmul_high_i32x4_s
0x10, 0x00, // call $func0
0xfd, 0xde, 0x01, // i64x2.extmul_low_i32x4_u
0x10, 0x00, // call $func0
0xfd, 0xdf, 0x01, // i64x2.extmul_high_i32x4_u
0xfd, 0xf8, 0x01, // i32x4.trunc_sat_f32x4_s
0xfd, 0xf9, 0x01, // i32x4.trunc_sat_f32x4_u
0xfd, 0xfa, 0x01, // f32x4.convert_i32x4_s
0xfd, 0xfb, 0x01, // f32x4.convert_i32x4_u
0xfd, 0xfc, 0x01, // i32x4.trunc_sat_f64x2_s_zero
0xfd, 0xfd, 0x01, // i32x4.trunc_sat_f64x2_u_zero
0xfd, 0xfe, 0x01, // f64x2.convert_low_i32x4_s
0xfd, 0xff, 0x01, // f64x2.convert_low_i32x4_u
0x1a, // drop
0x0b, // end

View File

@ -0,0 +1,506 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
(memory $memory0 0)
;; Function with SIMD type and constant.
(func $func0 (result v128)
;; We always print v128 constants as hexadecimal 4*i32.
v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000
)
(func $func1
;; SIMD load and stores.
i32.const 0
v128.load
drop
i32.const 0
;; Non-default memargs.
v128.load8x8_s offset=3
drop
i32.const 0
v128.load8x8_u align=2
drop
i32.const 0
v128.load16x4_s offset=3 align=4
drop
i32.const 0
v128.load16x4_u
drop
i32.const 0
v128.load32x2_s
drop
i32.const 0
v128.load32x2_u
drop
i32.const 0
v128.load8_splat
drop
i32.const 0
v128.load16_splat
drop
i32.const 0
v128.load32_splat
drop
i32.const 0
v128.load64_splat
drop
i32.const 0
v128.load32_zero
drop
i32.const 0
v128.load64_zero
drop
i32.const 0
;; Call function instead of repeating large immediate(s) all the time.
call $func0
v128.store
i32.const 0
call $func0
v128.load8_lane 0
drop
i32.const 0
call $func0
v128.load16_lane 1
drop
i32.const 0
call $func0
v128.load32_lane 3
drop
i32.const 0
call $func0
v128.load64_lane 0
drop
i32.const 0
call $func0
v128.store16_lane 0
i32.const 0
call $func0
v128.store32_lane 1
i32.const 0
call $func0
v128.store64_lane 0
;; Other SIMD instructions.
call $func0
call $func0
i8x16.shuffle 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
call $func0
i8x16.swizzle
drop
;; splat
i32.const 0
i8x16.splat
drop
i32.const 0
i16x8.splat
drop
i32.const 0
i32x4.splat
drop
i64.const 0
i64x2.splat
drop
f32.const 0.0
f32x4.splat
drop
f64.const 0.0
f64x2.splat
drop
;; extract_lane and replace_lane
call $func0
i8x16.extract_lane_s 0
drop
call $func0
i8x16.extract_lane_u 0
drop
call $func0
i16x8.extract_lane_s 0
drop
call $func0
i16x8.extract_lane_u 0
drop
call $func0
i32x4.extract_lane 0
drop
call $func0
i64x2.extract_lane 0
drop
call $func0
f32x4.extract_lane 0
drop
call $func0
f64x2.extract_lane 0
drop
call $func0
i32.const 0
i8x16.replace_lane 0
i32.const 0
i16x8.replace_lane 0
i32.const 0
i32x4.replace_lane 0
i64.const 0
i64x2.replace_lane 0
f32.const 0.0
f32x4.replace_lane 0
f64.const 0.0
f64x2.replace_lane 0
;; Comparisons: i8
call $func0
i8x16.eq
call $func0
i8x16.ne
call $func0
i8x16.lt_s
call $func0
i8x16.lt_u
call $func0
i8x16.gt_s
call $func0
i8x16.gt_u
call $func0
i8x16.le_s
call $func0
i8x16.le_u
call $func0
i8x16.ge_s
call $func0
i8x16.ge_u
;; Comparisons: i16
call $func0
i16x8.eq
call $func0
i16x8.ne
call $func0
i16x8.lt_s
call $func0
i16x8.lt_u
call $func0
i16x8.gt_s
call $func0
i16x8.gt_u
call $func0
i16x8.le_s
call $func0
i16x8.le_u
call $func0
i16x8.ge_s
call $func0
i16x8.ge_u
;; Comparisons: i32
call $func0
i32x4.eq
call $func0
i32x4.ne
call $func0
i32x4.lt_s
call $func0
i32x4.lt_u
call $func0
i32x4.gt_s
call $func0
i32x4.gt_u
call $func0
i32x4.le_s
call $func0
i32x4.le_u
call $func0
i32x4.ge_s
call $func0
i32x4.ge_u
;; Comparisons: i64
call $func0
i64x2.eq
call $func0
i64x2.ne
call $func0
i64x2.lt_s
call $func0
i64x2.gt_s
call $func0
i64x2.le_s
call $func0
i64x2.ge_s
;; Comparisons: f32
call $func0
f32x4.eq
call $func0
f32x4.ne
call $func0
f32x4.lt
call $func0
f32x4.gt
call $func0
f32x4.le
call $func0
f32x4.ge
;; Comparisons: f64
call $func0
f64x2.eq
call $func0
f64x2.ne
call $func0
f64x2.lt
call $func0
f64x2.gt
call $func0
f64x2.le
call $func0
f64x2.ge
;; Bitwise operations.
v128.not
call $func0
v128.and
call $func0
v128.andnot
call $func0
v128.or
call $func0
v128.xor
call $func0
call $func0
v128.bitselect
v128.any_true
drop
;; Floating-point demotion and promotions.
call $func0
f32x4.demote_f64x2_zero
f64x2.promote_low_f32x4
;; i8 operations.
i8x16.abs
i8x16.neg
i8x16.popcnt
i8x16.all_true
drop
call $func0
i8x16.bitmask
drop
call $func0
call $func0
i8x16.narrow_i16x8_s
call $func0
i8x16.narrow_i16x8_u
i32.const 0
i8x16.shl
i32.const 0
i8x16.shr_s
i32.const 0
i8x16.shr_u
call $func0
i8x16.add
call $func0
i8x16.add_sat_s
call $func0
i8x16.add_sat_u
call $func0
i8x16.sub
call $func0
i8x16.sub_sat_s
call $func0
i8x16.sub_sat_u
call $func0
i8x16.min_s
call $func0
i8x16.min_u
call $func0
i8x16.max_s
call $func0
i8x16.max_u
call $func0
i8x16.avgr_u
;; i16 operations.
i16x8.abs
i16x8.neg
call $func0
i16x8.q15mulr_sat_s
i16x8.all_true
drop
call $func0
i16x8.bitmask
drop
call $func0
call $func0
i16x8.narrow_i32x4_s
call $func0
i16x8.narrow_i32x4_u
i16x8.extend_low_i8x16_s
i16x8.extend_high_i8x16_s
i16x8.extend_low_i8x16_u
i16x8.extend_high_i8x16_u
i32.const 0
i16x8.shl
i32.const 0
i16x8.shr_s
i32.const 0
i16x8.shr_u
call $func0
i16x8.add
call $func0
i16x8.add_sat_s
call $func0
i16x8.add_sat_u
call $func0
i16x8.sub
call $func0
i16x8.sub_sat_s
call $func0
i16x8.sub_sat_u
call $func0
i16x8.mul
call $func0
i16x8.min_s
call $func0
i16x8.min_u
call $func0
i16x8.max_s
call $func0
i16x8.max_u
call $func0
i16x8.avgr_u
;; i32 operations.
i32x4.abs
i32x4.neg
i32x4.all_true
drop
call $func0
i32x4.bitmask
drop
call $func0
i32x4.extend_low_i16x8_s
i32x4.extend_high_i16x8_s
i32x4.extend_low_i16x8_u
i32x4.extend_high_i16x8_u
i32.const 0
i32x4.shl
i32.const 0
i32x4.shr_s
i32.const 0
i32x4.shr_u
call $func0
i32x4.add
call $func0
i32x4.sub
call $func0
i32x4.mul
call $func0
i32x4.min_s
call $func0
i32x4.min_u
call $func0
i32x4.max_s
call $func0
i32x4.max_u
call $func0
i32x4.dot_i16x8_s
;; i64 operations.
i64x2.abs
i64x2.neg
i64x2.all_true
drop
call $func0
i64x2.bitmask
drop
call $func0
i64x2.extend_low_i32x4_s
i64x2.extend_high_i32x4_s
i64x2.extend_low_i32x4_u
i64x2.extend_high_i32x4_u
i32.const 0
i64x2.shl
i32.const 0
i64x2.shr_s
i32.const 0
i64x2.shr_u
call $func0
i64x2.add
call $func0
i64x2.sub
call $func0
i64x2.mul
;; f32 operations.
f32x4.ceil
f32x4.floor
f32x4.trunc
f32x4.nearest
f32x4.abs
f32x4.neg
f32x4.sqrt
call $func0
f32x4.add
call $func0
f32x4.sub
call $func0
f32x4.mul
call $func0
f32x4.div
call $func0
f32x4.min
call $func0
f32x4.max
call $func0
f32x4.pmin
call $func0
f32x4.pmax
;; f64 operations.
f64x2.ceil
f64x2.floor
f64x2.trunc
f64x2.nearest
f64x2.abs
f64x2.neg
f64x2.sqrt
call $func0
f64x2.add
call $func0
f64x2.sub
call $func0
f64x2.mul
call $func0
f64x2.div
call $func0
f64x2.min
call $func0
f64x2.max
call $func0
f64x2.pmin
call $func0
f64x2.pmax
;; Extended integer arithmetic.
i16x8.extadd_pairwise_i8x16_s
i16x8.extadd_pairwise_i8x16_u
i32x4.extadd_pairwise_i16x8_s
i32x4.extadd_pairwise_i16x8_u
call $func0
i16x8.extmul_low_i8x16_s
call $func0
i16x8.extmul_high_i8x16_s
call $func0
i16x8.extmul_low_i8x16_u
call $func0
i16x8.extmul_high_i8x16_u
call $func0
i32x4.extmul_low_i16x8_s
call $func0
i32x4.extmul_high_i16x8_s
call $func0
i32x4.extmul_low_i16x8_u
call $func0
i32x4.extmul_high_i16x8_u
call $func0
i64x2.extmul_low_i32x4_s
call $func0
i64x2.extmul_high_i32x4_s
call $func0
i64x2.extmul_low_i32x4_u
call $func0
i64x2.extmul_high_i32x4_u
;; Conversions.
i32x4.trunc_sat_f32x4_s
i32x4.trunc_sat_f32x4_u
f32x4.convert_i32x4_s
f32x4.convert_i32x4_u
i32x4.trunc_sat_f64x2_s_zero
i32x4.trunc_sat_f64x2_u_zero
f64x2.convert_low_i32x4_s
f64x2.convert_low_i32x4_u
drop
)
)
;;)---";

View File

@ -0,0 +1,29 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x06, // section length 6
0x01, 0x4e, // types count 1: rec. group definition
0x01, 0x60, // recursive group size 1: kind: func
0x00, // param count 0
0x00, // return count 0
0x03, // section kind: Function
0x02, // section length 2
0x01, 0x00, // functions count 1: 0 $func0
0x0e, // section kind: StringRef
0x06, // section length 6
0x00, // deferred string literal count 0
0x01, 0x03, // string literal count 1: string literal length: 3
0x66, 0x6f, 0x6f, // string literal: foo
0x0a, // section kind: Code
0x09, // section length 9
0x01, // functions count 1
// function #0 $func0
0x07, // body size 7
0x00, // 0 entries in locals list
0xfb, 0x82, 0x01, 0x00, // string.const "foo" (;0;)
0x1a, // drop
0x0b, // end

View File

@ -0,0 +1,13 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
;; Comment lines are ignored and not expected in the disassembler output.
(module
(rec
(type $type0 (func))
)
(string "foo" (;0;))
(func $func0
string.const "foo" (;0;)
drop
)
)
;;)---";

View File

@ -0,0 +1,31 @@
0x00, 0x61, 0x73, 0x6d, // wasm magic
0x01, 0x00, 0x00, 0x00, // wasm version
0x01, // section kind: Type
0x04, // section length 4
0x01, 0x60, // types count 1: kind: func
0x00, // param count 0
0x00, // return count 0
0x03, // section kind: Function
0x02, // section length 2
0x01, 0x00, // functions count 1: 0 $doubleEnd
0x07, // section kind: Export
0x0d, // section length 13
0x01, // exports count 1: export # 0
0x09, // field name length: 9
0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x45, 0x6e,
0x64, // field name: doubleEnd
0x00, 0x00, // kind: function index: 0
0x0a, // section kind: Code
0x07, // section length 7
0x01, // functions count 1
// function #0 $doubleEnd
0x05, // body size 5
0x00, // 0 entries in locals list
0x01, // nop
0x0b, // end
0x0b, // end
0x0b, // end

View File

@ -0,0 +1,9 @@
;; expected = R"---(;; This is a polyglot C++/WAT file.
(module
(func $doubleEnd (;0;) (export "doubleEnd")
nop
)
;; Unexpected end byte
;; Unexpected end byte
)
;;)---";

View File

@ -0,0 +1,167 @@
// Copyright 2022 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 <regex>
#include <string>
#include "src/base/vector.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/string-builder-multiline.h"
#include "src/wasm/wasm-disassembler-impl.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmDisassemblerTest : public ::v8::TestWithPlatform {};
// Code that is shared for all tests, the only difference is the input module
// and expected disassembler output.
void CheckDisassemblerOutput(base::Vector<const uint8_t> module_bytes,
std::string expected_output) {
AccountingAllocator allocator;
std::unique_ptr<OffsetsProvider> offsets = AllocateOffsetsProvider();
ModuleResult module_result =
DecodeWasmModuleForDisassembler(module_bytes, offsets.get());
ASSERT_TRUE(module_result.ok())
<< "Decoding error: " << module_result.error().message() << " at offset "
<< module_result.error().offset();
WasmModule* module = module_result.value().get();
ModuleWireBytes wire_bytes(module_bytes);
NamesProvider names(module, module_bytes);
MultiLineStringBuilder output_sb;
constexpr bool kNoOffsets = false;
ModuleDisassembler md(output_sb, module, &names, wire_bytes, &allocator,
std::move(offsets));
constexpr size_t max_mb = 100; // Even 1 would be enough.
md.PrintModule({0, 2}, max_mb);
std::ostringstream output;
output_sb.WriteTo(output, kNoOffsets);
// Remove comment lines from expected output since they cannot be recovered
// by a disassembler.
// They were also used as part of the C++/WAT polyglot trick described below.
std::regex comment_regex(" *;;[^\\n]*\\n?");
expected_output = std::regex_replace(expected_output, comment_regex, "");
std::string output_str = std::regex_replace(output.str(), comment_regex, "");
EXPECT_EQ(expected_output, output_str);
}
TEST_F(WasmDisassemblerTest, Mvp) {
// If you want to extend this test (and the other tests below):
// 1. Modify the included .wat.inc file(s), e.g., add more instructions.
// 2. Convert the Wasm text file to a Wasm binary with `wat2wasm`.
// 3. Convert the Wasm binary to an array init expression with
// `wami --full-hexdump` and paste it into the included file below.
// One liner example (Linux):
// wat2wasm wasm-disassembler-unittest-mvp.wat.inc --output=-
// | wami --full-hexdump
// | head -n-1 | tail -n+2 > wasm-disassembler-unittest-mvp.wasm.inc
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-mvp.wasm.inc"
};
// Little trick: polyglot C++/WebAssembly text file.
// We want to include the expected disassembler text output as a string into
// this test (instead of reading it from the file at runtime, which would make
// it dependent on the current working directory).
// At the same time, we want the included file itself to be valid WAT, such
// that it can be processed e.g. by wat2wasm to build the module bytes above.
// For that to work, we abuse that ;; starts a line comment in WAT, but at
// the same time, ;; in C++ are just two empty statements, which are no
// harm when including the file here either.
std::string expected;
#include "wasm-disassembler-unittest-mvp.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, Names) {
// You can create a binary with a custom name section from the text format via
// `wat2wasm --debug-names`.
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-names.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-names.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, InvalidNameSection) {
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-bad-name-section.wasm.inc"
};
std::string expected(
"(module\n"
" (table $x (;0;) 0 funcref)\n"
")\n");
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, Simd) {
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-simd.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-simd.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, Gc) {
// Since WABT's `wat2wasm` didn't support some GC features yet, I used
// Binaryen's `wasm-as --enable-gc --hybrid` here to produce the binary.
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-gc.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-gc.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, TooManyends) {
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-too-many-ends.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-too-many-ends.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, Stringref) {
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-stringref.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-stringref.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
TEST_F(WasmDisassemblerTest, Exnref) {
constexpr uint8_t module_bytes[] = {
#include "wasm-disassembler-unittest-exnref.wasm.inc"
};
std::string expected;
#include "wasm-disassembler-unittest-exnref.wat.inc"
CheckDisassemblerOutput(base::ArrayVector(module_bytes), expected);
}
// TODO(dlehmann): Add tests for the following Wasm features and extensions:
// - custom name section for Wasm GC constructs (struct and array type names,
// struct fields).
// - exception-related instructions (try, catch, catch_all, delegate) and named
// exception tags.
// - atomic instructions (threads proposal, 0xfe prefix).
// - some "numeric" instructions (0xfc prefix).
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,274 @@
// Copyright 2019 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 <string>
#include "src/debug/wasm/gdb-server/packet.h"
#include "src/debug/wasm/gdb-server/session.h"
#include "src/debug/wasm/gdb-server/transport.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace gdb_server {
using ::testing::_;
using ::testing::Return;
using ::testing::SetArrayArgument;
using ::testing::StrEq;
class WasmGdbRemoteTest : public ::testing::Test {};
TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddChars) {
Packet packet;
// Read empty packet
bool end_of_packet = packet.EndOfPacket();
EXPECT_TRUE(end_of_packet);
// Add raw chars
packet.AddRawChar('4');
packet.AddRawChar('2');
std::string str;
packet.GetString(&str);
EXPECT_EQ("42", str);
}
TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddBlock) {
static const uint8_t block[] = {0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09};
static const size_t kLen = sizeof(block) / sizeof(uint8_t);
Packet packet;
packet.AddBlock(block, kLen);
uint8_t buffer[kLen];
bool ok = packet.GetBlock(buffer, kLen);
EXPECT_TRUE(ok);
EXPECT_EQ(0, memcmp(block, buffer, kLen));
packet.Rewind();
std::string str;
ok = packet.GetString(&str);
EXPECT_TRUE(ok);
EXPECT_EQ("010203040506070809", str);
}
TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddString) {
Packet packet;
packet.AddHexString("foobar");
std::string str;
bool ok = packet.GetString(&str);
EXPECT_TRUE(ok);
EXPECT_EQ("666f6f626172", str);
packet.Clear();
packet.AddHexString("GDB");
ok = packet.GetString(&str);
EXPECT_TRUE(ok);
EXPECT_EQ("474442", str);
}
TEST_F(WasmGdbRemoteTest, GdbRemotePacketAddNumbers) {
Packet packet;
static const uint64_t u64_val = 0xdeadbeef89abcdef;
static const uint8_t u8_val = 0x42;
packet.AddNumberSep(u64_val, ';');
packet.AddWord8(u8_val);
std::string str;
packet.GetString(&str);
EXPECT_EQ("deadbeef89abcdef;42", str);
packet.Rewind();
uint64_t val = 0;
char sep = '\0';
bool ok = packet.GetNumberSep(&val, &sep);
EXPECT_TRUE(ok);
EXPECT_EQ(u64_val, val);
uint8_t b = 0;
ok = packet.GetWord8(&b);
EXPECT_TRUE(ok);
EXPECT_EQ(u8_val, b);
}
TEST_F(WasmGdbRemoteTest, GdbRemotePacketSequenceNumber) {
Packet packet_with_sequence_num;
packet_with_sequence_num.AddWord8(42);
packet_with_sequence_num.AddRawChar(':');
packet_with_sequence_num.AddHexString("foobar");
int32_t sequence_num = 0;
packet_with_sequence_num.ParseSequence();
bool ok = packet_with_sequence_num.GetSequence(&sequence_num);
EXPECT_TRUE(ok);
EXPECT_EQ(42, sequence_num);
Packet packet_without_sequence_num;
packet_without_sequence_num.AddHexString("foobar");
packet_without_sequence_num.ParseSequence();
ok = packet_without_sequence_num.GetSequence(&sequence_num);
EXPECT_FALSE(ok);
}
TEST_F(WasmGdbRemoteTest, GdbRemotePacketRunLengthEncoded) {
Packet packet1;
packet1.AddRawChar('0');
packet1.AddRawChar('*');
packet1.AddRawChar(' ');
std::string str1;
bool ok = packet1.GetHexString(&str1);
EXPECT_TRUE(ok);
EXPECT_EQ("0000", std::string(packet1.GetPayload()));
Packet packet2;
packet2.AddRawChar('1');
packet2.AddRawChar('2');
packet2.AddRawChar('3');
packet2.AddRawChar('*');
packet2.AddRawChar(' ');
packet2.AddRawChar('a');
packet2.AddRawChar('b');
std::string str2;
ok = packet2.GetHexString(&str2);
EXPECT_TRUE(ok);
EXPECT_EQ("123333ab", std::string(packet2.GetPayload()));
}
TEST_F(WasmGdbRemoteTest, GdbRemoteUtilStringSplit) {
std::vector<std::string> parts1 = StringSplit({}, ",");
EXPECT_EQ(size_t(0), parts1.size());
auto parts2 = StringSplit("a", nullptr);
EXPECT_EQ(size_t(1), parts2.size());
EXPECT_EQ("a", parts2[0]);
auto parts3 = StringSplit(";a;bc;def;", ",");
EXPECT_EQ(size_t(1), parts3.size());
EXPECT_EQ(";a;bc;def;", parts3[0]);
auto parts4 = StringSplit(";a;bc;def;", ";");
EXPECT_EQ(size_t(3), parts4.size());
EXPECT_EQ("a", parts4[0]);
EXPECT_EQ("bc", parts4[1]);
EXPECT_EQ("def", parts4[2]);
}
class MockTransport : public TransportBase {
public:
MOCK_METHOD(bool, AcceptConnection, (), (override));
MOCK_METHOD(bool, Read, (char*, int32_t), (override));
MOCK_METHOD(bool, Write, (const char*, int32_t), (override));
MOCK_METHOD(bool, IsDataAvailable, (), (const, override));
MOCK_METHOD(void, Disconnect, (), (override));
MOCK_METHOD(void, Close, (), (override));
MOCK_METHOD(void, WaitForDebugStubEvent, (), (override));
MOCK_METHOD(bool, SignalThreadEvent, (), (override));
};
TEST_F(WasmGdbRemoteTest, GdbRemoteSessionSendPacket) {
const char* ack_buffer = "+";
MockTransport mock_transport;
EXPECT_CALL(mock_transport, Write(StrEq("$474442#39"), 10))
.WillOnce(Return(true));
EXPECT_CALL(mock_transport, Read(_, _))
.Times(1)
.WillOnce(
DoAll(SetArrayArgument<0>(ack_buffer, ack_buffer + 1), Return(true)));
Session session(&mock_transport);
Packet packet;
packet.AddHexString("GDB");
bool ok = session.SendPacket(&packet);
EXPECT_TRUE(ok);
}
TEST_F(WasmGdbRemoteTest, GdbRemoteSessionSendPacketDisconnectOnNoAck) {
MockTransport mock_transport;
EXPECT_CALL(mock_transport, Write(StrEq("$474442#39"), 10))
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_transport, Read(_, _)).Times(1).WillOnce(Return(false));
EXPECT_CALL(mock_transport, Disconnect()).Times(1);
Session session(&mock_transport);
Packet packet;
packet.AddHexString("GDB");
bool ok = session.SendPacket(&packet);
EXPECT_FALSE(ok);
}
TEST_F(WasmGdbRemoteTest, GdbRemoteSessionGetPacketCheckChecksum) {
const char* buffer_bad = "$47#00";
const char* buffer_ok = "$47#6b";
MockTransport mock_transport;
EXPECT_CALL(mock_transport, Read(_, _))
.WillOnce(
DoAll(SetArrayArgument<0>(buffer_bad, buffer_bad + 1), Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 1, buffer_bad + 2),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 2, buffer_bad + 3),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 3, buffer_bad + 4),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 4, buffer_bad + 5),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_bad + 5, buffer_bad + 6),
Return(true)))
.WillOnce(
DoAll(SetArrayArgument<0>(buffer_ok, buffer_ok + 1), Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 1, buffer_ok + 2),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 2, buffer_ok + 3),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 3, buffer_ok + 4),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 4, buffer_ok + 5),
Return(true)))
.WillOnce(DoAll(SetArrayArgument<0>(buffer_ok + 5, buffer_ok + 6),
Return(true)));
EXPECT_CALL(mock_transport, Write(StrEq("-"), 1)) // Signal bad packet
.Times(1)
.WillOnce(Return(true));
EXPECT_CALL(mock_transport, Write(StrEq("+"), 1)) // Signal ack
.Times(1)
.WillOnce(Return(true));
Session session(&mock_transport);
Packet packet;
bool ok = session.GetPacket(&packet);
EXPECT_TRUE(ok);
char ch;
ok = packet.GetBlock(&ch, 1);
EXPECT_TRUE(ok);
EXPECT_EQ('G', ch);
}
TEST_F(WasmGdbRemoteTest, GdbRemoteSessionGetPacketDisconnectOnReadFailure) {
MockTransport mock_transport;
EXPECT_CALL(mock_transport, Read(_, _)).Times(1).WillOnce(Return(false));
EXPECT_CALL(mock_transport, Disconnect()).Times(1);
Session session(&mock_transport);
Packet packet;
bool ok = session.GetPacket(&packet);
EXPECT_FALSE(ok);
}
} // namespace gdb_server
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,317 @@
// 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 "test/common/wasm/wasm-macro-gen.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmMacroGenTest : public TestWithZone {};
#define EXPECT_SIZE(size, ...) \
do { \
uint8_t code[] = {__VA_ARGS__}; \
USE(code); \
EXPECT_EQ(static_cast<size_t>(size), sizeof(code)); \
} while (false)
TEST_F(WasmMacroGenTest, Constants) {
EXPECT_SIZE(2, WASM_ONE);
EXPECT_SIZE(2, WASM_ZERO);
EXPECT_SIZE(2, WASM_I32V_1(-22));
EXPECT_SIZE(2, WASM_I32V_1(54));
EXPECT_SIZE(2, WASM_I32V_1(1));
EXPECT_SIZE(3, WASM_I32V_2(200));
EXPECT_SIZE(4, WASM_I32V_3(10000));
EXPECT_SIZE(5, WASM_I32V_4(-9828934));
EXPECT_SIZE(6, WASM_I32V_5(-1119828934));
EXPECT_SIZE(2, WASM_I64V_1(1));
EXPECT_SIZE(3, WASM_I64V_2(300));
EXPECT_SIZE(4, WASM_I64V_3(10000));
EXPECT_SIZE(5, WASM_I64V_4(-9828934));
EXPECT_SIZE(6, WASM_I64V_5(-1119828934));
EXPECT_SIZE(10, WASM_I64V_9(0x123456789ABCDEF0ULL));
EXPECT_SIZE(5, WASM_F32(1.0f));
EXPECT_SIZE(5, WASM_F32(10000.0f));
EXPECT_SIZE(5, WASM_F32(-9828934.0f));
EXPECT_SIZE(9, WASM_F64(1.5));
EXPECT_SIZE(9, WASM_F64(10200.0));
EXPECT_SIZE(9, WASM_F64(-9818934.0));
}
TEST_F(WasmMacroGenTest, Statements) {
EXPECT_SIZE(1, WASM_NOP);
EXPECT_SIZE(1, WASM_END);
EXPECT_SIZE(4, WASM_LOCAL_SET(0, WASM_ZERO));
EXPECT_SIZE(4, WASM_GLOBAL_SET(0, WASM_ZERO));
EXPECT_SIZE(7, WASM_STORE_MEM(MachineType::Int32(), WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(6, WASM_IF(WASM_ZERO, WASM_NOP));
EXPECT_SIZE(8, WASM_IF_ELSE(WASM_ZERO, WASM_NOP, WASM_NOP));
EXPECT_SIZE(5, WASM_SELECT(WASM_ZERO, WASM_NOP, WASM_NOP));
EXPECT_SIZE(2, WASM_BR(0));
EXPECT_SIZE(4, WASM_BR_IF(0, WASM_ZERO));
EXPECT_SIZE(4, WASM_BLOCK(WASM_NOP));
EXPECT_SIZE(5, WASM_BLOCK(WASM_NOP, WASM_NOP));
EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_NOP));
EXPECT_SIZE(5, WASM_INFINITE_LOOP);
EXPECT_SIZE(4, WASM_LOOP(WASM_NOP));
EXPECT_SIZE(5, WASM_LOOP(WASM_NOP, WASM_NOP));
EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_NOP));
EXPECT_SIZE(5, WASM_LOOP(WASM_BR(0)));
EXPECT_SIZE(7, WASM_LOOP(WASM_BR_IF(0, WASM_ZERO)));
EXPECT_SIZE(1, WASM_RETURN0);
EXPECT_SIZE(3, WASM_RETURN(WASM_ZERO));
EXPECT_SIZE(1, WASM_UNREACHABLE);
}
TEST_F(WasmMacroGenTest, MacroStatements) {
EXPECT_SIZE(11, WASM_WHILE(WASM_ZERO, WASM_NOP));
EXPECT_SIZE(7, WASM_INC_LOCAL(0));
EXPECT_SIZE(7, WASM_INC_LOCAL_BY(0, 3));
EXPECT_SIZE(2, WASM_CONTINUE(0));
}
TEST_F(WasmMacroGenTest, BrTable) {
EXPECT_SIZE(5, WASM_BR_TABLE(WASM_ZERO, 1, BR_TARGET(0)));
EXPECT_SIZE(6, WASM_BR_TABLE(WASM_ZERO, 2, BR_TARGET(0), BR_TARGET(0)));
}
TEST_F(WasmMacroGenTest, Expressions) {
EXPECT_SIZE(2, WASM_LOCAL_GET(0));
EXPECT_SIZE(2, WASM_LOCAL_GET(1));
EXPECT_SIZE(2, WASM_LOCAL_GET(12));
EXPECT_SIZE(2, WASM_GLOBAL_GET(0));
EXPECT_SIZE(2, WASM_GLOBAL_GET(1));
EXPECT_SIZE(2, WASM_GLOBAL_GET(12));
EXPECT_SIZE(5, WASM_LOAD_MEM(MachineType::Int32(), WASM_ZERO));
EXPECT_SIZE(5, WASM_LOAD_MEM(MachineType::Float64(), WASM_ZERO));
EXPECT_SIZE(5, WASM_LOAD_MEM(MachineType::Float32(), WASM_ZERO));
EXPECT_SIZE(3, WASM_NOT(WASM_ZERO));
EXPECT_SIZE(4, WASM_BRV(1, WASM_ZERO));
EXPECT_SIZE(6, WASM_BRV_IF(1, WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_BLOCK(WASM_ZERO));
EXPECT_SIZE(6, WASM_BLOCK(WASM_NOP, WASM_ZERO));
EXPECT_SIZE(7, WASM_BLOCK(WASM_NOP, WASM_NOP, WASM_ZERO));
EXPECT_SIZE(5, WASM_LOOP(WASM_ZERO));
EXPECT_SIZE(6, WASM_LOOP(WASM_NOP, WASM_ZERO));
EXPECT_SIZE(7, WASM_LOOP(WASM_NOP, WASM_NOP, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, CallFunction) {
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(0));
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(1));
EXPECT_SIZE(2, WASM_CALL_FUNCTION0(11));
EXPECT_SIZE(4, WASM_CALL_FUNCTION(0, WASM_ZERO));
EXPECT_SIZE(6, WASM_CALL_FUNCTION(1, WASM_ZERO, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, CallIndirect) {
EXPECT_SIZE(5, WASM_CALL_INDIRECT(0, WASM_ZERO));
EXPECT_SIZE(5, WASM_CALL_INDIRECT(1, WASM_ZERO));
EXPECT_SIZE(5, WASM_CALL_INDIRECT(11, WASM_ZERO));
EXPECT_SIZE(7, WASM_CALL_INDIRECT(0, WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(9, WASM_CALL_INDIRECT(1, WASM_ZERO, WASM_ZERO, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, Int32Ops) {
EXPECT_SIZE(5, WASM_I32_ADD(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_SUB(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_MUL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_DIVS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_DIVU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_REMS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_REMU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_AND(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_IOR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_XOR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_SHL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_SHR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_SAR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_ROR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_ROL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_EQ(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_LTS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_LES(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_LTU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_LEU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_GTS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_GES(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_GTU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I32_GEU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_CLZ(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_CTZ(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_POPCNT(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_EQZ(WASM_ZERO));
}
TEST_F(WasmMacroGenTest, Int64Ops) {
EXPECT_SIZE(5, WASM_I64_ADD(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_SUB(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_MUL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_DIVS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_DIVU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_REMS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_REMU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_AND(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_IOR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_XOR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_SHL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_SHR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_SAR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_ROR(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_ROL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_EQ(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_LTS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_LES(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_LTU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_LEU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_GTS(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_GES(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_GTU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_I64_GEU(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_CLZ(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_CTZ(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_POPCNT(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_EQZ(WASM_ZERO));
}
TEST_F(WasmMacroGenTest, Float32Ops) {
EXPECT_SIZE(5, WASM_F32_ADD(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_SUB(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_MUL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_DIV(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_MIN(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_MAX(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_COPYSIGN(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_ABS(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_NEG(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_CEIL(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_FLOOR(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_TRUNC(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_NEARESTINT(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_SQRT(WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_EQ(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_LT(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_LE(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_GT(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F32_GE(WASM_ZERO, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, Float64Ops) {
EXPECT_SIZE(5, WASM_F64_ADD(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_SUB(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_MUL(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_DIV(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_MIN(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_MAX(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_COPYSIGN(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_ABS(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_NEG(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_CEIL(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_FLOOR(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_TRUNC(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_NEARESTINT(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_SQRT(WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_EQ(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_LT(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_LE(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_GT(WASM_ZERO, WASM_ZERO));
EXPECT_SIZE(5, WASM_F64_GE(WASM_ZERO, WASM_ZERO));
}
TEST_F(WasmMacroGenTest, Conversions) {
EXPECT_SIZE(3, WASM_I32_SCONVERT_F32(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_SCONVERT_F64(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_UCONVERT_F32(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_UCONVERT_F64(WASM_ZERO));
EXPECT_SIZE(3, WASM_I32_CONVERT_I64(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_SCONVERT_F32(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_SCONVERT_F64(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_UCONVERT_F32(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_UCONVERT_F64(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_SCONVERT_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_I64_UCONVERT_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_SCONVERT_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_UCONVERT_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_SCONVERT_I64(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_UCONVERT_I64(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_CONVERT_F64(WASM_ZERO));
EXPECT_SIZE(3, WASM_F32_REINTERPRET_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_SCONVERT_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_UCONVERT_I32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_SCONVERT_I64(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_UCONVERT_I64(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_CONVERT_F32(WASM_ZERO));
EXPECT_SIZE(3, WASM_F64_REINTERPRET_I64(WASM_ZERO));
}
static const MachineType kMemTypes[] = {
MachineType::Int8(), MachineType::Uint8(), MachineType::Int16(),
MachineType::Uint16(), MachineType::Int32(), MachineType::Uint32(),
MachineType::Int64(), MachineType::Uint64(), MachineType::Float32(),
MachineType::Float64()};
TEST_F(WasmMacroGenTest, LoadsAndStores) {
for (size_t i = 0; i < arraysize(kMemTypes); i++) {
EXPECT_SIZE(5, WASM_LOAD_MEM(kMemTypes[i], WASM_ZERO));
}
for (size_t i = 0; i < arraysize(kMemTypes); i++) {
EXPECT_SIZE(7, WASM_STORE_MEM(kMemTypes[i], WASM_ZERO, WASM_LOCAL_GET(0)));
}
}
TEST_F(WasmMacroGenTest, LoadsAndStoresWithOffset) {
for (size_t i = 0; i < arraysize(kMemTypes); i++) {
EXPECT_SIZE(5, WASM_LOAD_MEM_OFFSET(kMemTypes[i], 11, WASM_ZERO));
}
for (size_t i = 0; i < arraysize(kMemTypes); i++) {
EXPECT_SIZE(7, WASM_STORE_MEM_OFFSET(kMemTypes[i], 13, WASM_ZERO,
WASM_LOCAL_GET(0)));
}
}
#undef EXPECT_SIZE
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,37 @@
// 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/init/v8.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/function-body-decoder.h"
#include "src/wasm/wasm-module-builder.h"
#include "test/common/wasm/test-signatures.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmModuleBuilderTest : public TestWithZone {
protected:
void AddLocal(WasmFunctionBuilder* f, ValueType type) {
uint16_t index = f->AddLocal(type);
f->EmitGetLocal(index);
}
};
TEST_F(WasmModuleBuilderTest, Regression_647329) {
// Test crashed with asan.
ZoneBuffer buffer(zone());
const size_t kSize = ZoneBuffer::kInitialSize * 3 + 4096 + 100;
uint8_t data[kSize] = {0};
buffer.write(data, kSize);
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,224 @@
// Copyright 2019 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/wasm-module-sourcemap.h"
#include <memory>
#include "src/api/api.h"
#include "test/common/wasm/flag-utils.h"
#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/unittests/test-utils.h"
#include "testing/gmock-support.h"
namespace v8 {
namespace internal {
namespace wasm {
class WasmModuleSourceMapTest : public TestWithIsolateAndZone {};
TEST_F(WasmModuleSourceMapTest, InvalidSourceMap) {
auto i_isolate = isolate();
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
// Incomplete source map without "sources" entry.
char incomplete_src_map[] =
"{\"version\":3,\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto incomplete_src_map_str =
v8::String::NewFromUtf8(v8_isolate, incomplete_src_map).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> incomplete_src_map_ptr(
new WasmModuleSourceMap(v8_isolate, incomplete_src_map_str));
EXPECT_FALSE(incomplete_src_map_ptr->IsValid());
// Miswrite key "mappings" as "mapping".
char wrong_key[] =
"{\"version\":3,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mapping\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto wrong_key_str =
v8::String::NewFromUtf8(v8_isolate, wrong_key).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> wrong_key_ptr(
new WasmModuleSourceMap(v8_isolate, wrong_key_str));
EXPECT_FALSE(wrong_key_ptr->IsValid());
// Wrong version number.
char wrong_ver[] =
"{\"version\":2,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto wrong_ver_str =
v8::String::NewFromUtf8(v8_isolate, wrong_ver).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> wrong_ver_ptr(
new WasmModuleSourceMap(v8_isolate, wrong_ver_str));
EXPECT_FALSE(wrong_ver_ptr->IsValid());
// Wrong type of "version" entry.
char ver_as_arr[] =
"{\"version\":[3],\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto ver_as_arr_str =
v8::String::NewFromUtf8(v8_isolate, ver_as_arr).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> ver_as_arr_ptr(
new WasmModuleSourceMap(v8_isolate, ver_as_arr_str));
EXPECT_FALSE(ver_as_arr_ptr->IsValid());
// Wrong type of "sources" entry.
char sources_as_str[] =
"{\"version\":3,\"sources\":\"./"
"test.h,main.cpp\",\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto sources_as_str_str =
v8::String::NewFromUtf8(v8_isolate, sources_as_str).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> sources_as_str_ptr(
new WasmModuleSourceMap(v8_isolate, sources_as_str_str));
EXPECT_FALSE(sources_as_str_ptr->IsValid());
// Invalid "mappings" entry.
char wrong_mappings[] =
"{\"version\":3,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"&BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto wrong_mappings_str =
v8::String::NewFromUtf8(v8_isolate, wrong_mappings).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> wrong_mappings_ptr(
new WasmModuleSourceMap(v8_isolate, wrong_mappings_str));
EXPECT_FALSE(wrong_mappings_ptr->IsValid());
}
TEST_F(WasmModuleSourceMapTest, HasSource) {
char src_map[] =
"{\"version\":3,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto i_isolate = isolate();
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
auto src_map_str =
v8::String::NewFromUtf8(v8_isolate, src_map).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> src_map_ptr(
new WasmModuleSourceMap(v8_isolate, src_map_str));
EXPECT_TRUE(src_map_ptr->IsValid());
EXPECT_FALSE(src_map_ptr->HasSource(0x387, 0x3AF));
EXPECT_FALSE(src_map_ptr->HasSource(0x3B0, 0x3B5));
EXPECT_FALSE(src_map_ptr->HasSource(0x3B6, 0x3BC));
EXPECT_FALSE(src_map_ptr->HasSource(0x3BD, 0x3C7));
EXPECT_FALSE(src_map_ptr->HasSource(0x3C8, 0x3DA));
EXPECT_TRUE(src_map_ptr->HasSource(0x3DB, 0x414));
EXPECT_TRUE(src_map_ptr->HasSource(0x415, 0x44E));
EXPECT_TRUE(src_map_ptr->HasSource(0x450, 0x4DC));
EXPECT_TRUE(src_map_ptr->HasSource(0x4DE, 0x5F1));
EXPECT_FALSE(src_map_ptr->HasSource(0x5F3, 0x437A));
EXPECT_FALSE(src_map_ptr->HasSource(0x437C, 0x5507));
EXPECT_FALSE(src_map_ptr->HasSource(0x5508, 0x5557));
EXPECT_FALSE(src_map_ptr->HasSource(0x5559, 0x5609));
EXPECT_FALSE(src_map_ptr->HasSource(0x560A, 0x563D));
EXPECT_FALSE(src_map_ptr->HasSource(0x563E, 0x564A));
EXPECT_FALSE(src_map_ptr->HasSource(0x564B, 0x5656));
EXPECT_FALSE(src_map_ptr->HasSource(0x5658, 0x5713));
EXPECT_FALSE(src_map_ptr->HasSource(0x5715, 0x59B0));
EXPECT_FALSE(src_map_ptr->HasSource(0x59B1, 0x59BC));
EXPECT_FALSE(src_map_ptr->HasSource(0x59BD, 0x59C6));
EXPECT_FALSE(src_map_ptr->HasSource(0x59C7, 0x59D8));
EXPECT_FALSE(src_map_ptr->HasSource(0x59D9, 0x59E7));
EXPECT_FALSE(src_map_ptr->HasSource(0x59E9, 0x5B50));
EXPECT_FALSE(src_map_ptr->HasSource(0x5B52, 0x5C53));
EXPECT_FALSE(src_map_ptr->HasSource(0x5C54, 0x5C57));
EXPECT_FALSE(src_map_ptr->HasSource(0x5C59, 0x5EBD));
EXPECT_FALSE(src_map_ptr->HasSource(0x5EBF, 0x6030));
EXPECT_FALSE(src_map_ptr->HasSource(0x6031, 0x608D));
EXPECT_FALSE(src_map_ptr->HasSource(0x608E, 0x609E));
EXPECT_FALSE(src_map_ptr->HasSource(0x609F, 0x60B3));
EXPECT_FALSE(src_map_ptr->HasSource(0x60B4, 0x60BD));
}
TEST_F(WasmModuleSourceMapTest, HasValidEntry) {
char src_map[] =
"{\"version\":3,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto i_isolate = isolate();
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
auto src_map_str =
v8::String::NewFromUtf8(v8_isolate, src_map).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> src_map_ptr(
new WasmModuleSourceMap(v8_isolate, src_map_str));
EXPECT_TRUE(src_map_ptr->IsValid());
EXPECT_FALSE(src_map_ptr->HasValidEntry(0x450, 0x467));
EXPECT_FALSE(src_map_ptr->HasValidEntry(0x450, 0x450));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x450, 0x47A));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x450, 0x4A9));
EXPECT_FALSE(src_map_ptr->HasValidEntry(0x4DE, 0x4F5));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x541));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x57D));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x5B7));
EXPECT_FALSE(src_map_ptr->HasValidEntry(0x4DE, 0x4DE));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x500));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x521));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x560));
EXPECT_TRUE(src_map_ptr->HasValidEntry(0x4DE, 0x597));
}
TEST_F(WasmModuleSourceMapTest, GetFilename) {
char src_map[] =
"{\"version\":3,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto i_isolate = isolate();
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
auto src_map_str =
v8::String::NewFromUtf8(v8_isolate, src_map).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> src_map_ptr(
new WasmModuleSourceMap(v8_isolate, src_map_str));
EXPECT_TRUE(src_map_ptr->IsValid());
EXPECT_STREQ("./test.h", src_map_ptr->GetFilename(0x47A).c_str());
EXPECT_STREQ("./test.h", src_map_ptr->GetFilename(0x4A9).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x500).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x521).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x541).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x560).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x57D).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x597).c_str());
EXPECT_STREQ("main.cpp", src_map_ptr->GetFilename(0x5B7).c_str());
}
TEST_F(WasmModuleSourceMapTest, SourceLine) {
char src_map[] =
"{\"version\":3,\"sources\":[\"./"
"test.h\",\"main.cpp\"],\"names\":[],\"mappings\":\"6/"
"BAGA,0DAIA,2DAIA,IAEA,+BACA,wCADA,mBAGA,4CCXA,6BACA,IACA,4BACA,gBADA,"
"mBAIA,4BACA,QADA,mBAIA,4BACA,gBADA,mBAVA,mBAcA\"}";
auto i_isolate = isolate();
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(i_isolate);
auto src_map_str =
v8::String::NewFromUtf8(v8_isolate, src_map).ToLocalChecked();
std::unique_ptr<WasmModuleSourceMap> src_map_ptr(
new WasmModuleSourceMap(v8_isolate, src_map_str));
EXPECT_TRUE(src_map_ptr->IsValid());
EXPECT_EQ(13ul, src_map_ptr->GetSourceLine(0x47A));
EXPECT_EQ(14ul, src_map_ptr->GetSourceLine(0x4A9));
EXPECT_EQ(5ul, src_map_ptr->GetSourceLine(0x500));
EXPECT_EQ(7ul, src_map_ptr->GetSourceLine(0x521));
EXPECT_EQ(8ul, src_map_ptr->GetSourceLine(0x541));
EXPECT_EQ(11ul, src_map_ptr->GetSourceLine(0x560));
EXPECT_EQ(12ul, src_map_ptr->GetSourceLine(0x57D));
EXPECT_EQ(15ul, src_map_ptr->GetSourceLine(0x597));
EXPECT_EQ(16ul, src_map_ptr->GetSourceLine(0x5B7));
}
} // namespace wasm
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,121 @@
// Copyright 2024 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 <map>
#include "include/v8-isolate.h"
#include "src/wasm/wasm-module-builder.h"
#include "test/common/wasm/test-signatures.h"
#include "test/unittests/test-utils.h"
#include "test/unittests/wasm/wasm-compile-module.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace v8::internal::wasm {
// Execute each test with sync, async, and streaming compilation.
enum CompileType { kSync, kAsync, kStreaming };
class WasmUseCounterTest
: public WithZoneMixin<WithInternalIsolateMixin<WithContextMixin<
WithIsolateScopeMixin<WithIsolateMixin<WithDefaultPlatformMixin<
::testing::TestWithParam<CompileType>>>>>>> {
public:
using UC = v8::Isolate::UseCounterFeature;
using UCMap = std::map<UC, int>;
WasmUseCounterTest() {
isolate()->SetUseCounterCallback([](v8::Isolate* isolate, UC feature) {
GetUseCounterMap()[feature] += 1;
});
}
void AddFunction(std::initializer_list<const uint8_t> body) {
WasmFunctionBuilder* f = builder_.AddFunction(sigs_.v_i());
f->EmitCode(body);
builder_.WriteTo(&buffer_);
}
void Compile() {
base::OwnedVector<const uint8_t> bytes = base::OwnedCopyOf(buffer_);
switch (GetParam()) {
case kSync:
return WasmCompileHelper::SyncCompile(isolate(), std::move(bytes));
case kAsync:
return WasmCompileHelper::AsyncCompile(isolate(), std::move(bytes));
case kStreaming:
return WasmCompileHelper::StreamingCompile(isolate(),
bytes.as_vector());
}
}
void CheckUseCounters(
std::initializer_list<std::pair<const UC, int>> use_counters) {
EXPECT_THAT(GetUseCounterMap(),
testing::UnorderedElementsAreArray(use_counters));
}
WasmModuleBuilder& builder() { return builder_; }
private:
static UCMap& GetUseCounterMap() {
static UCMap global_use_counter_map;
return global_use_counter_map;
}
ZoneBuffer buffer_{zone()};
HandleScope scope_{isolate()};
WasmModuleBuilder builder_{zone()};
TestSignatures sigs_;
};
std::string PrintCompileType(
::testing::TestParamInfo<CompileType> compile_type) {
switch (compile_type.param) {
case kSync:
return "Sync";
case kAsync:
return "Async";
case kStreaming:
return "Streaming";
}
}
INSTANTIATE_TEST_SUITE_P(CompileTypes, WasmUseCounterTest,
::testing::Values(CompileType::kSync,
CompileType::kAsync,
CompileType::kStreaming),
PrintCompileType);
TEST_P(WasmUseCounterTest, SimpleModule) {
AddFunction({kExprEnd});
Compile();
CheckUseCounters({{UC::kWasmModuleCompilation, 1}});
}
TEST_P(WasmUseCounterTest, Memory64) {
builder().AddMemory64(1, 1);
AddFunction({kExprEnd});
Compile();
CheckUseCounters({{UC::kWasmModuleCompilation, 1}, {UC::kWasmMemory64, 1}});
}
TEST_P(WasmUseCounterTest, Memory64_Twice) {
builder().AddMemory64(1, 1);
AddFunction({kExprEnd});
Compile();
Compile();
CheckUseCounters({{UC::kWasmModuleCompilation, 2}, {UC::kWasmMemory64, 2}});
}
TEST_P(WasmUseCounterTest, Memory64AndRefTypes) {
builder().AddMemory64(1, 1);
AddFunction({kExprRefNull, kFuncRefCode, kExprDrop, kExprEnd});
Compile();
CheckUseCounters({{UC::kWasmModuleCompilation, 1},
{UC::kWasmMemory64, 1},
{UC::kWasmRefTypes, 1}});
}
} // namespace v8::internal::wasm