|  | // Copyright 2018 The Fuchsia 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/developer/debug/zxdb/expr/eval_operators.h" | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "src/developer/debug/zxdb/common/err.h" | 
|  | #include "src/developer/debug/zxdb/common/test_with_loop.h" | 
|  | #include "src/developer/debug/zxdb/expr/expr_value.h" | 
|  | #include "src/developer/debug/zxdb/expr/mock_eval_context.h" | 
|  | #include "src/developer/debug/zxdb/expr/mock_expr_node.h" | 
|  | #include "src/developer/debug/zxdb/symbols/base_type.h" | 
|  | #include "src/developer/debug/zxdb/symbols/type_test_support.h" | 
|  |  | 
|  | namespace zxdb { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using debug_ipc::RegisterID; | 
|  |  | 
|  | class EvalOperators : public TestWithLoop { | 
|  | public: | 
|  | EvalOperators() : eval_context_(fxl::MakeRefCounted<MockEvalContext>()) {} | 
|  | ~EvalOperators() = default; | 
|  |  | 
|  | fxl::RefPtr<MockEvalContext>& eval_context() { return eval_context_; } | 
|  |  | 
|  | ErrOrValue SyncEvalBinaryOperator(const ExprValue& left, ExprTokenType op, | 
|  | const ExprValue& right) { | 
|  | ErrOrValue result((ExprValue())); | 
|  | EvalBinaryOperator(eval_context(), left, ExprToken(op, "", 0), right, | 
|  | [&result](ErrOrValue value) { result = value; }); | 
|  | loop().RunUntilNoTasks(); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | ErrOrValue SyncEvalUnaryOperator(ExprTokenType op, const ExprValue& right) { | 
|  | ErrOrValue result((ExprValue())); | 
|  | EvalUnaryOperator(eval_context(), ExprToken(op, "", 0), right, | 
|  | [&result](ErrOrValue value) { result = value; }); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | void DoUnaryMinusTest(T in) { | 
|  | ExprValue original(in); | 
|  |  | 
|  | ErrOrValue out = SyncEvalUnaryOperator(ExprTokenType::kMinus, original); | 
|  | ASSERT_TRUE(out.ok()) << out.err().msg(); | 
|  |  | 
|  | // This checked that the type conversions have followed C rules. This is the expected value | 
|  | // (int/unsigned unchanged, everything smaller than an int is promoted to an int, everything | 
|  | // larger remains unchanged). | 
|  | auto expected = -in; | 
|  |  | 
|  | // The type of the output should be the same as the input for unary '-'. | 
|  | // TODO(brettw) the actual type pointer should be the same. | 
|  | EXPECT_EQ(sizeof(expected), out.value().data().size()); | 
|  | if (std::is_unsigned<decltype(expected)>::value) { | 
|  | EXPECT_EQ(BaseType::kBaseTypeUnsigned, out.value().GetBaseType()); | 
|  | } else { | 
|  | EXPECT_EQ(BaseType::kBaseTypeSigned, out.value().GetBaseType()); | 
|  | } | 
|  | EXPECT_EQ(expected, out.value().GetAs<decltype(expected)>()); | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | void DoUnaryMinusTypeTest() { | 
|  | DoUnaryMinusTest<T>(0); | 
|  | DoUnaryMinusTest<T>(std::numeric_limits<T>::max()); | 
|  | DoUnaryMinusTest<T>(std::numeric_limits<T>::lowest()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | fxl::RefPtr<MockEvalContext> eval_context_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(EvalOperators, AssignmentMem) { | 
|  | auto int32_type = MakeInt32Type(); | 
|  |  | 
|  | // The casting test provides most tests for conversions so this test just | 
|  | // checks that the correct values are written and returned. | 
|  | constexpr uint64_t kAddress = 0x98723461923; | 
|  | ExprValue dest(int32_type, {0, 0, 0, 0}, ExprValueSource(kAddress)); | 
|  |  | 
|  | std::vector<uint8_t> data{0x12, 0x34, 0x56, 0x78}; | 
|  | ExprValue source(int32_type, data, ExprValueSource()); | 
|  |  | 
|  | ErrOrValue out = SyncEvalBinaryOperator(dest, ExprTokenType::kEquals, source); | 
|  |  | 
|  | // Written value returned. | 
|  | ASSERT_FALSE(out.has_error()); | 
|  | EXPECT_EQ(source, out.value()); | 
|  |  | 
|  | // Memory written to target. | 
|  | auto mem_writes = eval_context()->data_provider()->GetMemoryWrites(); | 
|  | ASSERT_EQ(1u, mem_writes.size()); | 
|  | EXPECT_EQ(kAddress, mem_writes[0].first); | 
|  | EXPECT_EQ(data, mem_writes[0].second); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, AssignmentBad) { | 
|  | auto int32_type = MakeInt32Type(); | 
|  |  | 
|  | ExprValue source(42);  // Value we'll assign from. | 
|  |  | 
|  | // Assignment to a temporary. | 
|  | ExprValue temp_value(0, fxl::RefPtr<Type>(), ExprValueSource(ExprValueSource::Type::kTemporary)); | 
|  | ErrOrValue out = SyncEvalBinaryOperator(temp_value, ExprTokenType::kEquals, source); | 
|  | ASSERT_TRUE(out.has_error()); | 
|  | EXPECT_EQ("Can't assign to a temporary.", out.err().msg()); | 
|  |  | 
|  | // Assignment to a constant. | 
|  | ExprValue const_value(0, fxl::RefPtr<Type>(), ExprValueSource(ExprValueSource::Type::kConstant)); | 
|  | out = SyncEvalBinaryOperator(const_value, ExprTokenType::kEquals, source); | 
|  | ASSERT_TRUE(out.has_error()); | 
|  | EXPECT_EQ("Can't assign to a constant.", out.err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, AssignmentFullRegister) { | 
|  | // Assign to a full regular register. | 
|  | auto int64_type = MakeUint64Type(); | 
|  | ExprValue dest(static_cast<uint64_t>(0), int64_type, ExprValueSource(RegisterID::kX64_rax)); | 
|  |  | 
|  | constexpr uint64_t kValue = 0x12345678; | 
|  | ExprValue source(kValue); | 
|  |  | 
|  | ErrOrValue out = SyncEvalBinaryOperator(dest, ExprTokenType::kEquals, source); | 
|  |  | 
|  | // Written value returned. | 
|  | ASSERT_FALSE(out.has_error()); | 
|  | EXPECT_EQ(source, out.value()); | 
|  |  | 
|  | // Register written to target. | 
|  | auto reg_writes = eval_context()->data_provider()->GetRegisterWrites(); | 
|  | ASSERT_EQ(1u, reg_writes.size()); | 
|  | EXPECT_EQ(RegisterID::kX64_rax, reg_writes[0].first); | 
|  | EXPECT_EQ(source.data(), reg_writes[0].second); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, AssignmentBitfieldRegister) { | 
|  | //                                                             |- AH -| | 
|  | // Byte: 7        6        5        4        3        2        1        0 | 
|  | //  RAX: -------- -------- -------- -------- -------- -------- -----==- -------- | 
|  | //                                                                  ^^ dest bits | 
|  |  | 
|  | // Assign to bit #1-2 (next-to-low and the next highest one) of a subregister that's itself 8 bits | 
|  | // from the low but of the rax register. | 
|  | ExprValue dest(static_cast<uint8_t>(0), fxl::RefPtr<Type>(), | 
|  | ExprValueSource(RegisterID::kX64_ah, 2, 1)); | 
|  |  | 
|  | // Existing register value has each byte numbered. Both reads and writes should be for the | 
|  | // canonical register. | 
|  | eval_context()->data_provider()->AddRegisterValue(RegisterID::kX64_rax, false, | 
|  | std::vector<uint8_t>{0, 1, 2, 3, 4, 5, 6, 7}); | 
|  |  | 
|  | constexpr uint8_t kValue = 0x3;  // Set both bits to 1. | 
|  | ExprValue source(kValue); | 
|  |  | 
|  | ErrOrValue out = SyncEvalBinaryOperator(dest, ExprTokenType::kEquals, source); | 
|  |  | 
|  | // Written value returned. | 
|  | ASSERT_FALSE(out.has_error()); | 
|  | EXPECT_EQ(source, out.value()); | 
|  |  | 
|  | // Register written to target. | 
|  | auto reg_writes = eval_context()->data_provider()->GetRegisterWrites(); | 
|  | ASSERT_EQ(1u, reg_writes.size()); | 
|  | EXPECT_EQ(RegisterID::kX64_rax, reg_writes[0].first); | 
|  | std::vector<uint8_t> expected{0, 7, 2, 3, 4, 5, 6, 7};  // Set bits 1-2 of byte 1. | 
|  | EXPECT_EQ(expected, reg_writes[0].second); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, AssignmentVectorRegister) { | 
|  | // Writing the next-to-highest 64-bit word fo the 256-bit "ymm0" register. The "192" is | 
|  | // 128 - 64 (unused high word) - 64 (word we're changing). | 
|  | auto double_type = fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeFloat, 8, "double"); | 
|  | ExprValue dest(static_cast<double>(0), fxl::RefPtr<Type>(), | 
|  | ExprValueSource(RegisterID::kX64_ymm0, 64, 128)); | 
|  |  | 
|  | // Existing 512-bit register value has each 16-bit word numbered. Both reads and writes should be | 
|  | // for the canonical register. | 
|  | // clang-format off | 
|  | std::vector<uint8_t> original{ 0, 0,  1, 0,  2, 0,  3, 0,  4, 0,  5, 0,  6, 0,  7, 0, | 
|  | 8, 0,  9, 0, 10, 0, 11, 0, 12, 0, 13, 0, 14, 0, 15, 0, | 
|  | 16, 0, 17, 0, 18, 0, 19, 0, 20, 0, 21, 0, 22, 0, 23, 0, | 
|  | 24, 0, 25, 0, 26, 0, 27, 0, 28, 0, 29, 0, 30, 0, 31, 0 }; | 
|  | eval_context()->data_provider()->AddRegisterValue(RegisterID::kX64_zmm0, false, original); | 
|  | // clang-format on | 
|  |  | 
|  | std::vector<uint8_t> new_data{0x91, 0x92, 0x93, 0x04, 0x95, 0x96, 0x97, 0x98}; | 
|  | ExprValue source(double_type, new_data); | 
|  |  | 
|  | ErrOrValue out = SyncEvalBinaryOperator(dest, ExprTokenType::kEquals, source); | 
|  |  | 
|  | // Written value returned. | 
|  | ASSERT_FALSE(out.has_error()); | 
|  | EXPECT_EQ(source, out.value()); | 
|  |  | 
|  | // Register written to target. | 
|  | auto reg_writes = eval_context()->data_provider()->GetRegisterWrites(); | 
|  | ASSERT_EQ(1u, reg_writes.size()); | 
|  | EXPECT_EQ(RegisterID::kX64_zmm0, reg_writes[0].first); | 
|  | std::vector<uint8_t> expected = original; | 
|  | for (size_t i = 0; i < new_data.size(); i++) | 
|  | expected[16 + i] = new_data[i]; | 
|  | EXPECT_EQ(expected, reg_writes[0].second); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, IntArithmetic) { | 
|  | // Simple signed arithmatic of 32-bit types. We promote all math results to 64-bit. | 
|  | ErrOrValue out = SyncEvalBinaryOperator(ExprValue(static_cast<int32_t>(12)), ExprTokenType::kPlus, | 
|  | ExprValue(static_cast<int32_t>(-1))); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(11, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Type promotion to larger size. This uses a custom 64-bit int type so we can tell it's been | 
|  | // preserved. This is "127 + (-2)" | 
|  | auto weird_64 = fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 8, "Weird64"); | 
|  | out = | 
|  | SyncEvalBinaryOperator(ExprValue(static_cast<int8_t>(0x7f)), ExprTokenType::kPlus, | 
|  | ExprValue(weird_64, {0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff})); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(125, out.value().GetAs<int64_t>()); | 
|  | EXPECT_EQ(weird_64.get(), out.value().type()); | 
|  |  | 
|  | // Promotion to unsigned when sizes match. | 
|  | auto int32_type = MakeInt32Type(); | 
|  | auto uint32_type = MakeUint32Type(); | 
|  | out = SyncEvalBinaryOperator(ExprValue(int32_type, {1, 0, 0, 0}), ExprTokenType::kPlus, | 
|  | ExprValue(uint32_type, {2, 0, 0, 0})); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(3, out.value().GetAs<int64_t>()); | 
|  | EXPECT_EQ(BaseType::kBaseTypeUnsigned, out.value().type()->AsBaseType()->base_type()); | 
|  |  | 
|  | // Signed subtraction. | 
|  | out = SyncEvalBinaryOperator(ExprValue(static_cast<int8_t>(100)), ExprTokenType::kMinus, | 
|  | ExprValue(static_cast<int8_t>(-100))); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(200, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Overflow of input type with multiplication. | 
|  | out = SyncEvalBinaryOperator(ExprValue(static_cast<int8_t>(100)), ExprTokenType::kStar, | 
|  | ExprValue(static_cast<int8_t>(100))); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(10000, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Boundary condition, should promote to unsigned 64-bit and do the multiplication. | 
|  | out = SyncEvalBinaryOperator(ExprValue(static_cast<uint32_t>(0xffffffff)), ExprTokenType::kStar, | 
|  | ExprValue(static_cast<uint32_t>(0xffffffff))); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0xfffffffe00000001, out.value().GetAs<uint64_t>()); | 
|  |  | 
|  | // Signed integer division. | 
|  | out = SyncEvalBinaryOperator(ExprValue(100), ExprTokenType::kSlash, ExprValue(-12)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(-8, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Unsigned integer division. "100 / (unsigned)-12" does give 0. | 
|  | out = SyncEvalBinaryOperator(ExprValue(100), ExprTokenType::kSlash, | 
|  | ExprValue(static_cast<unsigned>(-12))); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Modulo. | 
|  | out = SyncEvalBinaryOperator(ExprValue(108), ExprTokenType::kPercent, | 
|  | ExprValue(static_cast<unsigned>(100))); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(8, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Division by 0. | 
|  | out = SyncEvalBinaryOperator(ExprValue(108), ExprTokenType::kSlash, ExprValue(0)); | 
|  | EXPECT_TRUE(out.has_error()); | 
|  | EXPECT_EQ("Division by 0.", out.err().msg()); | 
|  |  | 
|  | // Modulo by 0. | 
|  | out = SyncEvalBinaryOperator(ExprValue(108), ExprTokenType::kPercent, ExprValue(0)); | 
|  | EXPECT_TRUE(out.has_error()); | 
|  | EXPECT_EQ("Division by 0.", out.err().msg()); | 
|  |  | 
|  | // Bitwise | | 
|  | out = SyncEvalBinaryOperator(ExprValue(0b0100), ExprTokenType::kBitwiseOr, ExprValue(0b1100)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0b1100, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // Bitwise & | 
|  | out = SyncEvalBinaryOperator(ExprValue(0b0100), ExprTokenType::kAmpersand, ExprValue(0b1100)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0b0100, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // ^ | 
|  | out = SyncEvalBinaryOperator(ExprValue(0b0100), ExprTokenType::kCaret, ExprValue(0b1100)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0b1000, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // << | 
|  | out = SyncEvalBinaryOperator(ExprValue(0b0100), ExprTokenType::kShiftLeft, ExprValue(2)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0b10000, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // >> | 
|  | out = SyncEvalBinaryOperator(ExprValue(0b0100), ExprTokenType::kShiftRight, ExprValue(2)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0b1, out.value().GetAs<int64_t>()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, FloatArithmetic) { | 
|  | // Double-precision division. | 
|  | ErrOrValue out = SyncEvalBinaryOperator(ExprValue(21.0), ExprTokenType::kSlash, ExprValue(10.0)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(21.0 / 10.0, out.value().GetAs<double>()); | 
|  |  | 
|  | // Floating-point division. | 
|  | out = SyncEvalBinaryOperator(ExprValue(21.0f), ExprTokenType::kSlash, ExprValue(10.0f)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(21.0f / 10.0f, out.value().GetAs<float>()); | 
|  |  | 
|  | // Promotion from float to double. | 
|  | out = SyncEvalBinaryOperator(ExprValue(21.0f), ExprTokenType::kSlash, ExprValue(10.0)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(21.0 / 10.0, out.value().GetAs<double>()); | 
|  |  | 
|  | // Promotion from int to float. | 
|  | out = SyncEvalBinaryOperator(ExprValue(21), ExprTokenType::kSlash, ExprValue(10.0f)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(21.0f / 10.0f, out.value().GetAs<float>()); | 
|  |  | 
|  | // Division by 0. | 
|  | out = SyncEvalBinaryOperator(ExprValue(21.0), ExprTokenType::kSlash, ExprValue(0.0)); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(21.0 / 0.0, out.value().GetAs<double>());  // Should be "inf". | 
|  |  | 
|  | // Modulo is an error. | 
|  | out = SyncEvalBinaryOperator(ExprValue(21.0), ExprTokenType::kPercent, ExprValue(5)); | 
|  | EXPECT_TRUE(out.has_error()); | 
|  | // Note: empty '' is because the test infrastructure doesn't set up a "value" for the token is | 
|  | // passes in. In real life it will be '%'. | 
|  | EXPECT_EQ("Operator '' not defined for floating point.", out.err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, PointerArithmetic) { | 
|  | auto int32_type = MakeInt32Type(); | 
|  | auto int32_ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, int32_type); | 
|  |  | 
|  | auto int64_type = MakeInt64Type(); | 
|  | auto int64_ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, int64_type); | 
|  |  | 
|  | constexpr uint64_t kPtrVal1 = 0x123400; | 
|  | ExprValue int32_ptr(kPtrVal1, int32_ptr_type); | 
|  | ExprValue eight(8); | 
|  |  | 
|  | // int32_ptr + 8. | 
|  | ErrOrValue out = SyncEvalBinaryOperator(int32_ptr, ExprTokenType::kPlus, eight); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(kPtrVal1 + (8 * sizeof(int32_t)), out.value().GetAs<uint64_t>()); | 
|  |  | 
|  | // 8 + int32_ptr. | 
|  | out = SyncEvalBinaryOperator(eight, ExprTokenType::kPlus, int32_ptr); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(kPtrVal1 + (8 * sizeof(int32_t)), out.value().GetAs<uint64_t>()); | 
|  |  | 
|  | // int32_ptr - 8. | 
|  | out = SyncEvalBinaryOperator(int32_ptr, ExprTokenType::kMinus, eight); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(kPtrVal1 - (8 * sizeof(int32_t)), out.value().GetAs<uint64_t>()); | 
|  |  | 
|  | // 8 - int32_ptr -> Error. | 
|  | out = SyncEvalBinaryOperator(eight, ExprTokenType::kMinus, int32_ptr); | 
|  | EXPECT_TRUE(out.has_error()); | 
|  |  | 
|  | // int32_ptr - int32_ptr2. | 
|  | constexpr uint64_t kPtrVal2 = 0x120000; | 
|  | ExprValue int32_ptr2(kPtrVal2, int32_ptr_type); | 
|  | out = SyncEvalBinaryOperator(int32_ptr, ExprTokenType::kMinus, int32_ptr2); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | int64_t ptr1_2_diff = static_cast<int64_t>((kPtrVal1 - kPtrVal2) / sizeof(int32_t)); | 
|  | EXPECT_EQ(ptr1_2_diff, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // int32_ptr2 - int32_ptr. | 
|  | out = SyncEvalBinaryOperator(int32_ptr2, ExprTokenType::kMinus, int32_ptr); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(-ptr1_2_diff, out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // int32_ptr * 8 -> Error. | 
|  | out = SyncEvalBinaryOperator(int32_ptr, ExprTokenType::kStar, eight); | 
|  | EXPECT_TRUE(out.has_error()); | 
|  |  | 
|  | // int32_ptr2 + int32_ptr -> error; | 
|  | out = SyncEvalBinaryOperator(int32_ptr2, ExprTokenType::kPlus, int32_ptr); | 
|  | EXPECT_TRUE(out.has_error()); | 
|  |  | 
|  | // int32_ptr - int64_ptr -> Error. | 
|  | constexpr uint64_t kPtrVal3 = 0x9900; | 
|  | ExprValue int64_ptr(kPtrVal3, int64_ptr_type); | 
|  | out = SyncEvalBinaryOperator(int32_ptr, ExprTokenType::kMinus, int64_ptr); | 
|  | ASSERT_TRUE(out.has_error()); | 
|  | EXPECT_EQ("Can't subtract pointers of different types 'int32_t*' and 'int64_t*'.", | 
|  | out.err().msg()); | 
|  |  | 
|  | // Two pointers near overflow. | 
|  | constexpr uint64_t kLargePtr1 = 0xffffffffffffff00; | 
|  | ExprValue large_ptr1(kLargePtr1, int32_ptr_type); | 
|  | constexpr uint64_t kLargePtr2 = 0xffffffffffffff80; | 
|  | ExprValue large_ptr2(kLargePtr2, int32_ptr_type); | 
|  |  | 
|  | // large_ptr1 - large_ptr2. | 
|  | out = SyncEvalBinaryOperator(large_ptr1, ExprTokenType::kMinus, large_ptr2); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ((-0x80) / static_cast<int>(sizeof(int32_t)), out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // large_ptr2 - large_ptr1. | 
|  | out = SyncEvalBinaryOperator(large_ptr2, ExprTokenType::kMinus, large_ptr1); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0x80 / static_cast<int>(sizeof(int32_t)), out.value().GetAs<int64_t>()); | 
|  |  | 
|  | // large_ptr1 + 8. | 
|  | out = SyncEvalBinaryOperator(large_ptr1, ExprTokenType::kPlus, eight); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(kLargePtr1 + (8 * sizeof(int32_t)), out.value().GetAs<uint64_t>()); | 
|  |  | 
|  | // Wraparound of 64-bit pointer addition. This threshold will force 0xffffffffffffff00 to wrap | 
|  | // when doing int32_t operations. | 
|  | ExprValue threshold(static_cast<int>(0x100 / sizeof(uint32_t))); | 
|  | out = SyncEvalBinaryOperator(large_ptr1, ExprTokenType::kPlus, threshold); | 
|  | ASSERT_FALSE(out.has_error()) << out.err().msg(); | 
|  | EXPECT_EQ(0u, out.value().GetAs<uint64_t>()); | 
|  |  | 
|  | // Try | which should fail on pointers. | 
|  | out = SyncEvalBinaryOperator(large_ptr1, ExprTokenType::kBitwiseOr, eight); | 
|  | ASSERT_TRUE(out.has_error()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, UnaryMinus) { | 
|  | // Test the limits of all built-in types. | 
|  | DoUnaryMinusTypeTest<int8_t>(); | 
|  | DoUnaryMinusTypeTest<uint8_t>(); | 
|  | DoUnaryMinusTypeTest<int16_t>(); | 
|  | DoUnaryMinusTypeTest<uint16_t>(); | 
|  | DoUnaryMinusTypeTest<int32_t>(); | 
|  | DoUnaryMinusTypeTest<uint32_t>(); | 
|  | DoUnaryMinusTypeTest<int64_t>(); | 
|  | DoUnaryMinusTypeTest<uint64_t>(); | 
|  |  | 
|  | // Try an unsupported value (a 3-byte signed). This should throw an error and | 
|  | // compute an empty value. | 
|  | ExprValue original(fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeUnsigned, 3, "uint24_t"), | 
|  | {0, 0, 0}); | 
|  | ErrOrValue out = SyncEvalUnaryOperator(ExprTokenType::kMinus, original); | 
|  | ASSERT_TRUE(out.err().has_error()); | 
|  | // Note: in real life the operator string will be inside the '' but the test harness doesn't | 
|  | // set the actual operator text. | 
|  | EXPECT_EQ("Unsupported size for unary operator ''.", out.err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, UnaryBang) { | 
|  | // Nonzero char -> false. | 
|  | ErrOrValue out = SyncEvalUnaryOperator(ExprTokenType::kBang, ExprValue('a')); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | ASSERT_EQ(1u, out.value().data().size()); | 
|  | EXPECT_EQ(0, out.value().GetAs<uint8_t>()); | 
|  | EXPECT_EQ("bool", out.value().type()->GetFullName()); | 
|  |  | 
|  | // !0 in 64-bit = true. | 
|  | out = SyncEvalUnaryOperator(ExprTokenType::kBang, ExprValue(static_cast<uint64_t>(0))); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | EXPECT_EQ(1, out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // Pointer. | 
|  | auto ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, MakeInt32Type()); | 
|  | out = SyncEvalUnaryOperator(ExprTokenType::kBang, ExprValue(ptr_type, {1, 2, 3, 4, 5, 6, 7, 8})); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | EXPECT_EQ(0, out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // Double. | 
|  | out = SyncEvalUnaryOperator(ExprTokenType::kBang, ExprValue(0.0)); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | EXPECT_EQ(1, out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // Try one that's not a number. | 
|  | auto coll = MakeCollectionType(DwarfTag::kStructureType, "Struct", {}); | 
|  | coll->set_byte_size(4); | 
|  | out = SyncEvalUnaryOperator(ExprTokenType::kBang, ExprValue(coll, {0, 0, 0, 0})); | 
|  | ASSERT_TRUE(out.has_error()); | 
|  | EXPECT_EQ("Invalid non-numeric type 'Struct' for operator.", out.err().msg()); | 
|  |  | 
|  | // Use a typedef for the type to test concrete type resolution. | 
|  | auto myint_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kTypedef, MakeInt32Type()); | 
|  | out = SyncEvalUnaryOperator(ExprTokenType::kBang, ExprValue(myint_type, {42, 0, 0, 0})); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | EXPECT_EQ(0, out.value().GetAs<uint8_t>()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, Comparison) { | 
|  | // (int8_t)1 == (int)1 | 
|  | ExprValue char_one(static_cast<int8_t>(1)); | 
|  | EXPECT_EQ(1u, char_one.data().size());  // Validate construction. | 
|  | ExprValue int_one(static_cast<int32_t>(1)); | 
|  | ErrOrValue out = SyncEvalBinaryOperator(char_one, ExprTokenType::kEquality, int_one); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | ASSERT_EQ(1u, out.value().data().size()); | 
|  | EXPECT_EQ(1, out.value().GetAs<uint8_t>()); | 
|  | EXPECT_EQ("bool", out.value().type()->GetFullName()); | 
|  |  | 
|  | // (int)1 != (int8_t)1 | 
|  | out = SyncEvalBinaryOperator(char_one, ExprTokenType::kInequality, int_one); | 
|  | EXPECT_FALSE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 1.0 <= 1 | 
|  | ExprValue double_one(1.0); | 
|  | out = SyncEvalBinaryOperator(double_one, ExprTokenType::kLessEqual, int_one); | 
|  | EXPECT_TRUE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 1.0 < 1 | 
|  | out = SyncEvalBinaryOperator(double_one, ExprTokenType::kLess, int_one); | 
|  | EXPECT_FALSE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 0 > 1.0 | 
|  | ExprValue int_zero(0); | 
|  | out = SyncEvalBinaryOperator(int_zero, ExprTokenType::kGreater, double_one); | 
|  | EXPECT_FALSE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 0 >= 1.0 | 
|  | out = SyncEvalBinaryOperator(int_zero, ExprTokenType::kGreaterEqual, double_one); | 
|  | EXPECT_FALSE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 1 >= 1.0 | 
|  | out = SyncEvalBinaryOperator(int_one, ExprTokenType::kGreaterEqual, double_one); | 
|  | EXPECT_TRUE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // true > 0 | 
|  | ExprValue true_value(true); | 
|  | out = SyncEvalBinaryOperator(true_value, ExprTokenType::kGreater, int_zero); | 
|  | EXPECT_TRUE(out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 0 <=> 1 is recognised but an error. | 
|  | out = SyncEvalBinaryOperator(int_zero, ExprTokenType::kSpaceship, int_one); | 
|  | ASSERT_FALSE(out.ok()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalOperators, Logical) { | 
|  | // (int8_t)1 || (int)1 | 
|  | ExprValue char_one(static_cast<int8_t>(1)); | 
|  | ExprValue int_one(static_cast<int32_t>(1)); | 
|  | ErrOrValue out = SyncEvalBinaryOperator(char_one, ExprTokenType::kLogicalOr, int_one); | 
|  | ASSERT_TRUE(out.ok()); | 
|  | ASSERT_EQ(1u, out.value().data().size()); | 
|  | EXPECT_EQ(1, out.value().GetAs<uint8_t>()); | 
|  | EXPECT_EQ("bool", out.value().type()->GetFullName()); | 
|  |  | 
|  | // 1 || 0 | 
|  | ExprValue int_zero(0); | 
|  | out = SyncEvalBinaryOperator(int_one, ExprTokenType::kLogicalOr, int_zero); | 
|  | EXPECT_EQ(1, out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 0 || 0 | 
|  | out = SyncEvalBinaryOperator(int_zero, ExprTokenType::kLogicalOr, int_zero); | 
|  | EXPECT_EQ(0, out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 1 && 1 | 
|  | out = SyncEvalBinaryOperator(int_one, ExprTokenType::kDoubleAnd, int_one); | 
|  | EXPECT_EQ(1, out.value().GetAs<uint8_t>()); | 
|  |  | 
|  | // 0 && 1 | 
|  | out = SyncEvalBinaryOperator(int_zero, ExprTokenType::kDoubleAnd, int_one); | 
|  | EXPECT_EQ(0, out.value().GetAs<uint8_t>()); | 
|  | } | 
|  |  | 
|  | // Tests that && and || don't evaluate the right-hand side if not necessary. | 
|  | TEST_F(EvalOperators, LogicalShortCircuit) { | 
|  | // 1 || <error> | 
|  | auto or_node = fxl::MakeRefCounted<BinaryOpExprNode>( | 
|  | fxl::MakeRefCounted<MockExprNode>(true, ExprValue(1)), | 
|  | ExprToken(ExprTokenType::kLogicalOr, "||", 0), | 
|  | fxl::MakeRefCounted<MockExprNode>(true, Err("Should not eval."))); | 
|  |  | 
|  | // Should evalutate to true and not error. | 
|  | bool called = false; | 
|  | or_node->Eval(eval_context(), [&called](ErrOrValue v) { | 
|  | called = true; | 
|  | EXPECT_FALSE(v.has_error()); | 
|  | EXPECT_EQ(1, v.value().GetAs<uint8_t>()); | 
|  | }); | 
|  | EXPECT_TRUE(called);  // Should eval synchronously. | 
|  |  | 
|  | // 0 && <error> | 
|  | auto and_node = fxl::MakeRefCounted<BinaryOpExprNode>( | 
|  | fxl::MakeRefCounted<MockExprNode>(true, ExprValue(0)), | 
|  | ExprToken(ExprTokenType::kDoubleAnd, "&&", 0), | 
|  | fxl::MakeRefCounted<MockExprNode>(true, Err("Should not eval."))); | 
|  |  | 
|  | // Should evalutate to true and not error. | 
|  | called = false; | 
|  | and_node->Eval(eval_context(), [&called](ErrOrValue v) { | 
|  | called = true; | 
|  | EXPECT_FALSE(v.has_error()); | 
|  | EXPECT_EQ(0, v.value().GetAs<uint8_t>()); | 
|  | }); | 
|  | EXPECT_TRUE(called);  // Should eval synchronously. | 
|  | } | 
|  |  | 
|  | }  // namespace zxdb |