| #include "lldb/DataFormatters/FormatterBytecode.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "llvm/Testing/Support/Error.h" | |
| #include "gtest/gtest.h" | |
| using namespace lldb_private; | |
| using namespace lldb; | |
| using namespace FormatterBytecode; | |
| using llvm::FailedWithMessage; | |
| using llvm::StringRef; | |
| namespace { | |
| class FormatterBytecodeTest : public ::testing::Test {}; | |
| } // namespace | |
| static bool Interpret(std::vector<uint8_t> code, DataStack &data) { | |
| auto buf = | |
| StringRef(reinterpret_cast<const char *>(code.data()), code.size()); | |
| ControlStack control({buf}); | |
| if (auto error = Interpret(control, data, sig_summary)) { | |
| #ifndef NDEBUG | |
| llvm::errs() << llvm::toString(std::move(error)) << '\n'; | |
| #else | |
| llvm::consumeError(std::move(error)); | |
| #endif | |
| return false; | |
| } | |
| return true; | |
| } | |
| /// Like Interpret() above, but returns (instead of discarding) the Error, | |
| /// allowing tests to assert on the error message. | |
| static llvm::Error InterpretFail(std::vector<uint8_t> code) { | |
| auto buf = | |
| StringRef(reinterpret_cast<const char *>(code.data()), code.size()); | |
| ControlStack control({buf}); | |
| DataStack data; | |
| return Interpret(control, data, sig_summary); | |
| } | |
| TEST_F(FormatterBytecodeTest, StackOps) { | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 23, op_dup, op_plus}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 46u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_drop}, data)); | |
| ASSERT_EQ(data.size(), 0u); | |
| } | |
| { | |
| for (unsigned char i = 0; i < 3; ++i) { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_lit_uint, 2, | |
| op_lit_uint, i, op_pick}, | |
| data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), i); | |
| } | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_over}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_swap}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret( | |
| {op_lit_uint, 0, op_lit_uint, 1, op_lit_uint, 2, op_rot}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 2u); | |
| } | |
| } | |
| TEST_F(FormatterBytecodeTest, ControlOps) { | |
| { | |
| DataStack data; | |
| ASSERT_TRUE( | |
| Interpret({op_lit_uint, 0, op_begin, 2, op_lit_uint, 42, op_if}, data)); | |
| ASSERT_EQ(data.size(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE( | |
| Interpret({op_lit_uint, 1, op_begin, 2, op_lit_uint, 42, op_if}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 42u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_begin, 2, op_lit_uint, 42, | |
| op_begin, 2, op_lit_uint, 23, op_ifelse}, | |
| data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 23u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_begin, 2, op_lit_uint, 42, | |
| op_begin, 2, op_lit_uint, 23, op_ifelse}, | |
| data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 42u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_begin, 3, op_lit_uint, 42, | |
| op_return, op_if, op_lit_uint, 23}, | |
| data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 42u); | |
| } | |
| } | |
| TEST_F(FormatterBytecodeTest, ConversionOps) { | |
| { | |
| DataStack data(lldb::ValueObjectSP{}); | |
| ASSERT_TRUE(Interpret({op_is_null}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1u, op_as_int}, data)); | |
| ASSERT_EQ(data.Pop<int64_t>(), 1); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_int, 126, op_as_uint}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), ~1ULL); | |
| } | |
| } | |
| TEST_F(FormatterBytecodeTest, ArithOps) { | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 2, op_lit_uint, 3, op_plus}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 5u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 3, op_lit_uint, 2, op_minus}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 3, op_lit_uint, 2, op_mul}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 6u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 6, op_lit_uint, 2, op_div}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 3u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_FALSE(Interpret({op_lit_uint, 23, op_lit_uint, 0, op_div}, data)); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 2, op_shl}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 4u); | |
| } | |
| { | |
| DataStack data; | |
| unsigned char minus_one = 127; | |
| ASSERT_FALSE( | |
| Interpret({op_lit_int, minus_one, op_lit_uint, 2, op_shl}, data)); | |
| unsigned char minus_two = 126; | |
| ASSERT_TRUE( | |
| Interpret({op_lit_int, minus_two, op_lit_uint, 1, op_shr}, data)); | |
| ASSERT_EQ(data.Pop<int64_t>(), -1); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_FALSE(Interpret({op_lit_uint, 1, op_lit_uint, 65, op_shl}, data)); | |
| ASSERT_FALSE(Interpret({op_lit_uint, 1, op_lit_uint, 65, op_shr}, data)); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_and}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_and}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_or}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_or}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_or}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_xor}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_xor}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_xor}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_not}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0xffffffffffffffff); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_eq}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_eq}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_neq}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 0, op_neq}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_lt}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_lt}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_lt}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_gt}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_gt}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_gt}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_le}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_le}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_le}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| } | |
| { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({op_lit_uint, 0, op_lit_uint, 1, op_ge}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 0u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 0, op_ge}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| ASSERT_TRUE(Interpret({op_lit_uint, 1, op_lit_uint, 1, op_ge}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 1u); | |
| } | |
| } | |
| TEST_F(FormatterBytecodeTest, OutOfBounds) { | |
| // op_lit_uint's ULEB128 operand is truncated: the interpreter runs off | |
| // the end of the buffer while decoding it. | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_lit_uint}), | |
| FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " | |
| "malformed uleb128, extends past end")); | |
| // op_begin claims a block that is longer than the remaining bytecode. | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_begin, 5, op_lit_uint, 42}), | |
| FailedWithMessage( | |
| "unexpected end of data at offset 0x4 while reading [0x2, 0x7)")); | |
| // The ULEB128 byte's continuation bit is set, but there is no | |
| // terminating byte. | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_lit_uint, 0x80}), | |
| FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " | |
| "malformed uleb128, extends past end")); | |
| // Same as above, but for op_lit_int's SLEB128 operand. | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_lit_int, 0x80}), | |
| FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " | |
| "malformed sleb128, extends past end")); | |
| // The ULEB128 operand encodes a value that doesn't fit into a uint64_t: | |
| // 9 continuation bytes (63 bits) followed by a final byte contributing | |
| // more than the single remaining bit. | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_lit_uint, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, | |
| 0x80, 0x80, 0x02}), | |
| FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " | |
| "uleb128 too big for uint64")); | |
| // Same as above, but for op_lit_int's SLEB128 operand not fitting into | |
| // an int64_t. | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_lit_int, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, | |
| 0x80, 0x02}), | |
| FailedWithMessage("unable to decode LEB128 at offset 0x00000001: " | |
| "sleb128 too big for int64")); | |
| } | |
| TEST_F(FormatterBytecodeTest, EmptyBytecode) { | |
| DataStack data; | |
| ASSERT_TRUE(Interpret({}, data)); | |
| ASSERT_EQ(data.size(), 0u); | |
| } | |
| TEST_F(FormatterBytecodeTest, UnknownSelector) { | |
| EXPECT_THAT_ERROR( | |
| InterpretFail({op_lit_selector, 0xff, op_call}), | |
| FailedWithMessage( | |
| "selector not implemented (opcode=call, selector=@255)")); | |
| } | |
| TEST_F(FormatterBytecodeTest, UnknownOpcode) { | |
| EXPECT_THAT_ERROR(InterpretFail({0xaa}), | |
| FailedWithMessage("opcode not implemented(opcode=170)")); | |
| } | |
| TEST_F(FormatterBytecodeTest, CallOps) { | |
| { | |
| DataStack data; | |
| data.Push(std::string{"hello"}); | |
| ASSERT_TRUE(Interpret({op_lit_selector, sel_strlen, op_call}, data)); | |
| ASSERT_EQ(data.Pop<uint64_t>(), 5u); | |
| } | |
| { | |
| DataStack data; | |
| data.Push(std::string{"A"}); | |
| data.Push(std::string{"B"}); | |
| data.Push(std::string{"{1}{0}"}); | |
| ASSERT_TRUE(Interpret({op_lit_selector, sel_fmt, op_call}, data)); | |
| ASSERT_EQ(data.Pop<std::string>(), "BA"); | |
| } | |
| { | |
| DataStack data; | |
| data.Push(std::string{"{0}"}); | |
| ASSERT_FALSE(Interpret({op_lit_selector, sel_fmt, op_call}, data)); | |
| } | |
| } |