2019-06-07 21:47:58 +00:00
|
|
|
// 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 <sstream>
|
|
|
|
|
|
|
|
#include "test/unittests/test-utils.h"
|
|
|
|
|
|
|
|
#include "src/wasm/module-decoder.h"
|
|
|
|
#include "src/wasm/wasm-module-builder.h"
|
|
|
|
#include "src/wasm/wasm-module.h"
|
|
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
|
|
#include "src/wasm/wasm-text.h"
|
|
|
|
#include "test/common/wasm/test-signatures.h"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace wasm {
|
|
|
|
|
|
|
|
class WasmTextTest : public TestWithIsolateAndZone {
|
|
|
|
public:
|
|
|
|
TestSignatures sigs;
|
|
|
|
WasmFeatures enabled_features_;
|
|
|
|
|
|
|
|
void TestInstruction(const byte* func_start, size_t func_size) {
|
|
|
|
WasmModuleBuilder mb(zone());
|
|
|
|
auto* fb = mb.AddFunction(sigs.v_v());
|
|
|
|
fb->EmitCode(func_start, static_cast<uint32_t>(func_size));
|
|
|
|
fb->Emit(kExprEnd);
|
|
|
|
|
|
|
|
ZoneBuffer buffer(zone());
|
2019-07-08 09:16:39 +00:00
|
|
|
mb.WriteTo(&buffer);
|
2019-06-07 21:47:58 +00:00
|
|
|
|
|
|
|
ModuleWireBytes wire_bytes(
|
|
|
|
Vector<const byte>(buffer.begin(), buffer.size()));
|
|
|
|
|
|
|
|
ModuleResult result = DecodeWasmModule(
|
|
|
|
enabled_features_, buffer.begin(), buffer.end(), false, kWasmOrigin,
|
|
|
|
isolate()->counters(), isolate()->wasm_engine()->allocator());
|
|
|
|
EXPECT_TRUE(result.ok());
|
|
|
|
|
|
|
|
std::stringstream ss;
|
|
|
|
PrintWasmText(result.value().get(), wire_bytes, 0, ss, nullptr);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(WasmTextTest, EveryOpcodeCanBeDecoded) {
|
|
|
|
static const struct {
|
|
|
|
WasmOpcode opcode;
|
|
|
|
const char* debug_name;
|
|
|
|
} kValues[] = {
|
|
|
|
#define DECLARE_ELEMENT(name, opcode, sig) {kExpr##name, "kExpr" #name},
|
|
|
|
FOREACH_OPCODE(DECLARE_ELEMENT)};
|
|
|
|
#undef DECLARE_ELEMENT
|
|
|
|
|
|
|
|
for (const auto& value : kValues) {
|
|
|
|
// Pad with 0 for any immediate values. If they're not needed, they'll be
|
|
|
|
// interpreted as unreachable.
|
|
|
|
byte data[20] = {0};
|
|
|
|
|
|
|
|
printf("%s\n", value.debug_name);
|
|
|
|
switch (value.opcode) {
|
|
|
|
// Instructions that have a special case because they affect the control
|
|
|
|
// depth.
|
|
|
|
case kExprBlock:
|
|
|
|
case kExprLoop:
|
|
|
|
case kExprIf:
|
|
|
|
case kExprTry:
|
|
|
|
data[0] = value.opcode;
|
|
|
|
data[1] = kLocalVoid;
|
|
|
|
data[2] = kExprEnd;
|
|
|
|
break;
|
|
|
|
case kExprElse:
|
|
|
|
data[0] = kExprIf;
|
|
|
|
data[1] = value.opcode;
|
|
|
|
data[2] = kExprEnd;
|
|
|
|
break;
|
|
|
|
case kExprCatch:
|
|
|
|
data[0] = kExprTry;
|
|
|
|
data[1] = value.opcode;
|
|
|
|
data[2] = kExprEnd;
|
|
|
|
break;
|
|
|
|
case kExprEnd:
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Instructions with special requirements for immediates.
|
|
|
|
case kExprSelectWithType:
|
|
|
|
data[0] = kExprSelectWithType;
|
|
|
|
data[1] = 1;
|
|
|
|
data[2] = kLocalI32;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
if (value.opcode >= 0x100) {
|
|
|
|
data[0] = value.opcode >> 8; // Prefix byte.
|
|
|
|
byte opcode = value.opcode & 0xff; // Actual opcode.
|
|
|
|
if (opcode >= 0x80) {
|
|
|
|
// Opcode with prefix, and needs to be LEB encoded (3 bytes).
|
|
|
|
// For now, this can only be in the range [0x80, 0xff], which means
|
|
|
|
// that the third byte is always 1.
|
|
|
|
data[1] = (opcode & 0x7f) | 0x80;
|
|
|
|
data[2] = 1;
|
|
|
|
} else {
|
|
|
|
// Opcode with prefix (2 bytes).
|
|
|
|
data[1] = opcode;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Single-byte opcode.
|
|
|
|
data[0] = value.opcode;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TestInstruction(data, arraysize(data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace wasm
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|