| // Copyright 2020 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef __EMSCRIPTEN__ |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <string_view> |
| #include <type_traits> |
| #include <unordered_map> |
| |
| #include "lldb-eval/api.h" |
| #include "lldb-eval/ast.h" |
| #include "lldb-eval/context.h" |
| #include "lldb-eval/runner.h" |
| #include "lldb-eval/traits.h" |
| #include "lldb/API/SBDebugger.h" |
| #include "lldb/API/SBFrame.h" |
| #include "lldb/API/SBProcess.h" |
| #include "lldb/API/SBTarget.h" |
| #include "lldb/API/SBThread.h" |
| #include "lldb/API/SBType.h" |
| #include "tools/cpp/runfiles/runfiles.h" |
| #endif |
| |
| // DISALLOW_COPY_AND_ASSIGN is also defined in |
| // lldb/lldb-defines.h |
| #undef DISALLOW_COPY_AND_ASSIGN |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #ifndef __EMSCRIPTEN__ |
| |
| using bazel::tools::cpp::runfiles::Runfiles; |
| |
| using ::testing::MakeMatcher; |
| using ::testing::Matcher; |
| using ::testing::MatcherInterface; |
| using ::testing::MatchResultListener; |
| |
| struct EvalResult { |
| lldb::SBError lldb_eval_error; |
| mutable lldb::SBValue lldb_eval_value; |
| mutable std::optional<lldb::SBValue> lldb_value; |
| |
| friend std::ostream& operator<<(std::ostream& os, const EvalResult& result) { |
| auto maybe_null = [](const char* str) { |
| return str == nullptr ? "NULL" : str; |
| }; |
| |
| os << "{ lldb-eval: " << maybe_null(result.lldb_eval_value.GetValue()); |
| |
| if (result.lldb_value.has_value()) { |
| os << ", lldb: " << maybe_null(result.lldb_value.value().GetValue()); |
| } |
| os << " }"; |
| |
| return os; |
| } |
| }; |
| |
| class EvaluatorHelper { |
| public: |
| EvaluatorHelper(lldb::SBFrame frame, bool lldb, bool side_effects) |
| : frame_(frame), lldb_(lldb), side_effects_(side_effects) {} |
| EvaluatorHelper(lldb::SBValue scope, bool lldb, bool side_effects) |
| : scope_(scope), lldb_(lldb), side_effects_(side_effects) {} |
| |
| public: |
| EvalResult Eval(const std::string& expr) { |
| EvalResult ret; |
| |
| lldb_eval::Options opts; |
| opts.allow_side_effects = side_effects_; |
| |
| if (scope_) { |
| // Evaluate in the variable context. |
| ret.lldb_eval_value = lldb_eval::EvaluateExpression( |
| scope_, expr.c_str(), opts, ret.lldb_eval_error); |
| |
| if (lldb_) { |
| ret.lldb_value = scope_.EvaluateExpression(expr.c_str()); |
| } |
| |
| } else { |
| // Evaluate in the frame context. |
| ret.lldb_eval_value = lldb_eval::EvaluateExpression( |
| frame_, expr.c_str(), opts, ret.lldb_eval_error); |
| |
| if (lldb_) { |
| ret.lldb_value = frame_.EvaluateExpression(expr.c_str()); |
| } |
| } |
| |
| return ret; |
| } |
| |
| EvalResult Eval(std::shared_ptr<lldb_eval::CompiledExpr> compiled_expr) { |
| assert(scope_ && "compiled expression requires a value context"); |
| |
| EvalResult ret; |
| ret.lldb_eval_value = lldb_eval::EvaluateExpression(scope_, compiled_expr, |
| ret.lldb_eval_error); |
| return ret; |
| } |
| |
| EvalResult EvalWithContext( |
| const std::string& expr, |
| const std::unordered_map<std::string, lldb::SBValue>& vars) { |
| EvalResult ret; |
| |
| std::vector<lldb_eval::ContextVariable> ctx_vec; |
| ctx_vec.reserve(vars.size()); |
| for (const auto& [name, value] : vars) { |
| ctx_vec.push_back({name.c_str(), value}); |
| } |
| |
| lldb_eval::Options opts; |
| opts.allow_side_effects = side_effects_; |
| opts.context_vars = {ctx_vec.data(), ctx_vec.size()}; |
| |
| if (scope_) { |
| // Evaluate in the variable context. |
| ret.lldb_eval_value = lldb_eval::EvaluateExpression( |
| scope_, expr.c_str(), opts, ret.lldb_eval_error); |
| |
| if (lldb_) { |
| ret.lldb_value = scope_.EvaluateExpression(expr.c_str()); |
| } |
| } else { |
| // Evaluate in the frame context. |
| ret.lldb_eval_value = lldb_eval::EvaluateExpression( |
| frame_, expr.c_str(), opts, ret.lldb_eval_error); |
| |
| if (lldb_) { |
| ret.lldb_value = frame_.EvaluateExpression(expr.c_str()); |
| } |
| } |
| |
| return ret; |
| } |
| |
| EvalResult EvalWithContext( |
| std::shared_ptr<lldb_eval::CompiledExpr> compiled_expr, |
| const std::unordered_map<std::string, lldb::SBValue>& vars) { |
| assert(scope_ && "compiled expression requires a value context"); |
| |
| std::vector<lldb_eval::ContextVariable> ctx_vec; |
| ctx_vec.reserve(vars.size()); |
| for (const auto& [name, value] : vars) { |
| ctx_vec.push_back({name.c_str(), value}); |
| } |
| lldb_eval::ContextVariableList context_vars{ctx_vec.data(), ctx_vec.size()}; |
| |
| EvalResult ret; |
| ret.lldb_eval_value = lldb_eval::EvaluateExpression( |
| scope_, compiled_expr, context_vars, ret.lldb_eval_error); |
| return ret; |
| } |
| |
| std::shared_ptr<lldb_eval::CompiledExpr> Compile(const std::string& expr, |
| lldb::SBError& error) { |
| assert(scope_ && "compiling an expression requires a type context"); |
| |
| lldb_eval::Options opts; |
| opts.allow_side_effects = side_effects_; |
| return lldb_eval::CompileExpression(scope_.GetTarget(), scope_.GetType(), |
| expr.c_str(), opts, error); |
| } |
| |
| std::shared_ptr<lldb_eval::CompiledExpr> CompileWithContext( |
| const std::string& expr, |
| const std::unordered_map<std::string, lldb::SBType>& args, |
| lldb::SBError& error) { |
| assert(scope_ && "compiling an expression requires a type context"); |
| |
| std::vector<lldb_eval::ContextArgument> ctx_vec; |
| ctx_vec.reserve(args.size()); |
| for (const auto& [name, type] : args) { |
| ctx_vec.push_back({name.c_str(), type}); |
| } |
| |
| lldb_eval::Options opts; |
| opts.allow_side_effects = side_effects_; |
| opts.context_args = {ctx_vec.data(), ctx_vec.size()}; |
| |
| return lldb_eval::CompileExpression(scope_.GetTarget(), scope_.GetType(), |
| expr.c_str(), opts, error); |
| } |
| |
| private: |
| lldb::SBFrame frame_; |
| lldb::SBValue scope_; |
| bool lldb_; |
| bool side_effects_; |
| }; |
| #endif |
| |
| void PrintError(::testing::MatchResultListener* listener, |
| const std::string& error) { |
| *listener << "error:"; |
| // Print multiline errors on a separate line. |
| if (error.find('\n') != std::string::npos) { |
| *listener << "\n"; |
| } else { |
| *listener << " "; |
| } |
| *listener << error; |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| class IsOkMatcher : public MatcherInterface<EvalResult> { |
| public: |
| explicit IsOkMatcher(bool compare_types) : compare_types_(compare_types) {} |
| |
| bool MatchAndExplain(EvalResult result, |
| MatchResultListener* listener) const override { |
| if (result.lldb_eval_error.GetError()) { |
| PrintError(listener, result.lldb_eval_error.GetCString()); |
| return false; |
| } |
| |
| std::string actual = result.lldb_eval_value.GetValue(); |
| // Compare only if we tried to evaluate with LLDB. |
| if (result.lldb_value.has_value()) { |
| if (result.lldb_value.value().GetError().GetError()) { |
| *listener << "values produced by lldb-eval and LLDB don't match\n" |
| << "lldb-eval: " << actual << "\n" |
| << "lldb : " |
| << result.lldb_value.value().GetError().GetCString(); |
| return false; |
| |
| } else if (actual != result.lldb_value.value().GetValue()) { |
| *listener << "values produced by lldb-eval and LLDB don't match\n" |
| << "lldb-eval: " << actual << "\n" |
| << "lldb : " << result.lldb_value.value().GetValue(); |
| return false; |
| } |
| |
| if (compare_types_) { |
| const char* lldb_eval_type = |
| result.lldb_eval_value.GetType().GetUnqualifiedType().GetName(); |
| const char* lldb_type = |
| result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); |
| if (strcmp(lldb_eval_type, lldb_type) != 0) { |
| *listener << "types produced by lldb-eval and LLDB don't match\n" |
| << "lldb-eval: " << lldb_eval_type << "\n" |
| << "lldb : " << lldb_type; |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void DescribeTo(std::ostream* os) const override { |
| *os << "evaluates without an error and equals to LLDB"; |
| } |
| |
| private: |
| bool compare_types_; |
| }; |
| #endif |
| |
| Matcher<EvalResult> IsOk(bool compare_types = true) { |
| return MakeMatcher(new IsOkMatcher(compare_types)); |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| class IsEqualMatcher : public MatcherInterface<EvalResult> { |
| public: |
| IsEqualMatcher(std::string value, bool compare_types) |
| : value_(std::move(value)), compare_types_(compare_types) {} |
| |
| public: |
| bool MatchAndExplain(EvalResult result, |
| MatchResultListener* listener) const override { |
| if (result.lldb_eval_error.GetError()) { |
| PrintError(listener, result.lldb_eval_error.GetCString()); |
| return false; |
| } |
| |
| std::string actual = result.lldb_eval_value.GetValue(); |
| if (actual != value_) { |
| *listener << "evaluated to '" << actual << "'"; |
| return false; |
| } |
| |
| // Compare only if we tried to evaluate with LLDB. |
| if (result.lldb_value.has_value()) { |
| if (result.lldb_value.value().GetError().GetError()) { |
| *listener << "values produced by lldb-eval and LLDB don't match\n" |
| << "lldb-eval: " << actual << "\n" |
| << "lldb : " |
| << result.lldb_value.value().GetError().GetCString(); |
| return false; |
| |
| } else if (actual != result.lldb_value.value().GetValue()) { |
| *listener << "values produced by lldb-eval and LLDB don't match\n" |
| << "lldb-eval: " << actual << "\n" |
| << "lldb : " << result.lldb_value.value().GetValue(); |
| return false; |
| } |
| |
| if (compare_types_) { |
| const char* lldb_eval_type = |
| result.lldb_eval_value.GetType().GetUnqualifiedType().GetName(); |
| const char* lldb_type = |
| result.lldb_value.value().GetType().GetUnqualifiedType().GetName(); |
| if (strcmp(lldb_eval_type, lldb_type) != 0) { |
| *listener << "types produced by lldb-eval and LLDB don't match\n" |
| << "lldb-eval: " << lldb_eval_type << "\n" |
| << "lldb : " << lldb_type; |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| void DescribeTo(std::ostream* os) const override { |
| *os << "evaluates to '" << value_ << "'"; |
| } |
| |
| private: |
| std::string value_; |
| bool compare_types_; |
| }; |
| #endif |
| |
| Matcher<EvalResult> IsEqual(std::string value, bool compare_types = true) { |
| return MakeMatcher(new IsEqualMatcher(std::move(value), compare_types)); |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| class IsErrorMatcher : public MatcherInterface<EvalResult> { |
| public: |
| explicit IsErrorMatcher(std::string value) : value_(std::move(value)) {} |
| |
| public: |
| bool MatchAndExplain(EvalResult result, |
| MatchResultListener* listener) const override { |
| if (!result.lldb_eval_error.GetError()) { |
| *listener << "evaluated to '" << result.lldb_eval_value.GetValue() << "'"; |
| return false; |
| } |
| std::string message = result.lldb_eval_error.GetCString(); |
| if (message.find(value_) == std::string::npos) { |
| PrintError(listener, message); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void DescribeTo(std::ostream* os) const override { |
| *os << "evaluates with an error: '" << value_ << "'"; |
| } |
| |
| private: |
| std::string value_; |
| }; |
| #endif |
| |
| Matcher<EvalResult> IsError(std::string value) { |
| return MakeMatcher(new IsErrorMatcher(std::move(value))); |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| class EvalTest : public ::testing::Test { |
| protected: |
| static void SetUpTestSuite() { |
| runfiles_ = Runfiles::CreateForTest(); |
| lldb_eval::SetupLLDBServerEnv(*runfiles_); |
| lldb::SBDebugger::Initialize(); |
| } |
| |
| static void TearDownTestSuite() { |
| lldb::SBDebugger::Terminate(); |
| delete runfiles_; |
| runfiles_ = nullptr; |
| } |
| |
| void SetUp() { |
| std::string test_name = |
| ::testing::UnitTest::GetInstance()->current_test_info()->name(); |
| std::string break_line = "// BREAK(" + test_name + ")"; |
| |
| auto binary_path = runfiles_->Rlocation("lldb_eval/testdata/test_binary"); |
| auto source_path = |
| runfiles_->Rlocation("lldb_eval/testdata/test_binary.cc"); |
| |
| debugger_ = lldb::SBDebugger::Create(false); |
| process_ = lldb_eval::LaunchTestProgram(debugger_, source_path, binary_path, |
| break_line); |
| frame_ = process_.GetSelectedThread().GetSelectedFrame(); |
| } |
| |
| void TearDown() { |
| process_.Destroy(); |
| lldb::SBDebugger::Destroy(debugger_); |
| } |
| |
| EvalResult Eval(const std::string& expr) { |
| return EvaluatorHelper(frame_, compare_with_lldb_, allow_side_effects_) |
| .Eval(expr); |
| } |
| |
| EvalResult EvalWithContext( |
| const std::string& expr, |
| const std::unordered_map<std::string, lldb::SBValue>& vars) { |
| return EvaluatorHelper(frame_, compare_with_lldb_, allow_side_effects_) |
| .EvalWithContext(expr, vars); |
| } |
| |
| EvaluatorHelper Scope(std::string scope) { |
| // Resolve the scope variable (assume it's a local variable). |
| lldb::SBValue scope_var = frame_.FindVariable(scope.c_str()); |
| return EvaluatorHelper(scope_var, compare_with_lldb_, allow_side_effects_); |
| } |
| |
| bool CreateContextVariable(std::string type, std::string name, bool is_array, |
| std::string assignment) { |
| std::string expr = type + " " + name + (is_array ? "[]" : "") + " = " + |
| assignment + "; " + name; |
| lldb::SBValue value = frame_.EvaluateExpression(expr.c_str()); |
| if (value.GetError().Fail()) { |
| return false; |
| } |
| vars_.emplace(name, value); |
| return true; |
| } |
| |
| bool CreateContextVariable(std::string name, std::string assignment) { |
| return CreateContextVariable("auto", name, false, assignment); |
| } |
| |
| bool CreateContextVariableArray(std::string type, std::string name, |
| std::string assignment) { |
| return CreateContextVariable(type, name, true, assignment); |
| } |
| |
| bool Is32Bit() const { |
| if (process_.GetAddressByteSize() == 4) { |
| return true; |
| } |
| return false; |
| } |
| |
| protected: |
| lldb::SBDebugger debugger_; |
| lldb::SBProcess process_; |
| lldb::SBFrame frame_; |
| |
| // Evaluate with both lldb-eval and LLDB by default. |
| bool compare_with_lldb_ = true; |
| |
| // Allow the expressions to have side-effects. |
| bool allow_side_effects_ = false; |
| |
| // Context variables. |
| std::unordered_map<std::string, lldb::SBValue> vars_; |
| |
| static Runfiles* runfiles_; |
| }; |
| |
| Runfiles* EvalTest::runfiles_ = nullptr; |
| |
| TEST_F(EvalTest, TestSymbols) { |
| EXPECT_GT(frame_.GetModule().GetNumSymbols(), 0) |
| << "No symbols might indicate that the test binary was built incorrectly"; |
| } |
| |
| #endif |
| |
| TEST_F(EvalTest, TestArithmetic) { |
| EXPECT_THAT(Eval("1 + 2"), IsEqual("3")); |
| EXPECT_THAT(Eval("1 + 2*3"), IsEqual("7")); |
| EXPECT_THAT(Eval("1 + (2 - 3)"), IsEqual("0")); |
| EXPECT_THAT(Eval("1 == 2"), IsEqual("false")); |
| EXPECT_THAT(Eval("1 == 1"), IsEqual("true")); |
| |
| // Note: Signed overflow is UB. |
| EXPECT_THAT(Eval("int_max + 1"), IsOk()); |
| EXPECT_THAT(Eval("int_min - 1"), IsOk()); |
| EXPECT_THAT(Eval("2147483647 + 1"), IsOk()); |
| EXPECT_THAT(Eval("-2147483648 - 1"), IsOk()); |
| |
| EXPECT_THAT(Eval("uint_max + 1"), IsEqual("0")); |
| EXPECT_THAT(Eval("uint_zero - 1"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("4294967295 + 1"), IsEqual("4294967296")); |
| EXPECT_THAT(Eval("4294967295U + 1"), IsEqual("0")); |
| |
| // Note: Signed overflow is UB. |
| EXPECT_THAT(Eval("ll_max + 1"), IsOk()); |
| EXPECT_THAT(Eval("ll_min - 1"), IsOk()); |
| EXPECT_THAT(Eval("9223372036854775807 + 1"), IsOk()); |
| EXPECT_THAT(Eval("-9223372036854775808 - 1"), IsOk()); |
| |
| EXPECT_THAT(Eval("ull_max + 1"), IsEqual("0")); |
| EXPECT_THAT(Eval("ull_zero - 1"), IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("9223372036854775807 + 1"), IsEqual("-9223372036854775808")); |
| EXPECT_THAT(Eval("9223372036854775807LL + 1"), |
| IsEqual("-9223372036854775808")); |
| EXPECT_THAT(Eval("18446744073709551615ULL + 1"), IsEqual("0")); |
| |
| // Integer literal is too large to be represented in a signed integer type, |
| // interpreting as unsigned. |
| EXPECT_THAT(Eval("-9223372036854775808"), IsEqual("9223372036854775808")); |
| EXPECT_THAT(Eval("-9223372036854775808 - 1"), IsEqual("9223372036854775807")); |
| EXPECT_THAT(Eval("-9223372036854775808 + 1"), IsEqual("9223372036854775809")); |
| EXPECT_THAT(Eval("-9223372036854775808LL / -1"), IsEqual("0")); |
| EXPECT_THAT(Eval("-9223372036854775808LL % -1"), |
| IsEqual("9223372036854775808")); |
| |
| EXPECT_THAT(Eval("-20 / 1U"), IsEqual("4294967276")); |
| EXPECT_THAT(Eval("-20LL / 1U"), IsEqual("-20")); |
| EXPECT_THAT(Eval("-20LL / 1ULL"), IsEqual("18446744073709551596")); |
| |
| // Unary arithmetic. |
| EXPECT_THAT(Eval("+0"), IsEqual("0")); |
| EXPECT_THAT(Eval("-0"), IsEqual("0")); |
| EXPECT_THAT(Eval("+1"), IsEqual("1")); |
| EXPECT_THAT(Eval("-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("c"), IsEqual("'\\n'")); |
| EXPECT_THAT(Eval("+c"), IsEqual("10")); |
| EXPECT_THAT(Eval("-c"), IsEqual("-10")); |
| EXPECT_THAT(Eval("uc"), IsEqual("'\\x01'")); |
| EXPECT_THAT(Eval("-uc"), IsEqual("-1")); |
| EXPECT_THAT(Eval("+p"), IsOk()); |
| EXPECT_THAT(Eval("-p"), |
| IsError("invalid argument type 'int *' to unary expression")); |
| |
| // Floating tricks. |
| EXPECT_THAT(Eval("+0.0"), IsEqual("0")); |
| EXPECT_THAT(Eval("-0.0"), IsEqual("-0")); |
| EXPECT_THAT(Eval("0.0 / 0"), IsEqual("NaN")); |
| EXPECT_THAT(Eval("0 / 0.0"), IsEqual("NaN")); |
| EXPECT_THAT(Eval("1 / +0.0"), IsEqual("+Inf")); |
| EXPECT_THAT(Eval("1 / -0.0"), IsEqual("-Inf")); |
| EXPECT_THAT(Eval("+0.0 / +0.0 != +0.0 / +0.0"), IsEqual("true")); |
| EXPECT_THAT(Eval("-1.f * 0"), IsEqual("-0")); |
| EXPECT_THAT(Eval("0x0.123p-1"), IsEqual("0.0355224609375")); |
| |
| EXPECT_THAT(Eval("fnan < fnan"), IsEqual("false")); |
| EXPECT_THAT(Eval("fnan <= fnan"), IsEqual("false")); |
| EXPECT_THAT(Eval("fnan == fnan"), IsEqual("false")); |
| EXPECT_THAT(Eval("(unsigned int) fdenorm"), IsEqual("0")); |
| EXPECT_THAT(Eval("(unsigned int) (1.0f + fdenorm)"), IsEqual("1")); |
| |
| // Invalid remainder. |
| EXPECT_THAT( |
| Eval("1.1 % 2"), |
| IsError("invalid operands to binary expression ('double' and 'int')")); |
| |
| // References and typedefs. |
| EXPECT_THAT(Eval("r + 1"), IsEqual("3")); |
| EXPECT_THAT(Eval("r - 1l"), IsEqual("1")); |
| EXPECT_THAT(Eval("r * 2u"), IsEqual("4")); |
| EXPECT_THAT(Eval("r / 2ull"), IsEqual("1")); |
| EXPECT_THAT(Eval("my_r + 1"), IsEqual("3")); |
| EXPECT_THAT(Eval("my_r - 1"), IsEqual("1")); |
| EXPECT_THAT(Eval("my_r * 2"), IsEqual("4")); |
| EXPECT_THAT(Eval("my_r / 2"), IsEqual("1")); |
| EXPECT_THAT(Eval("r + my_r"), IsEqual("4")); |
| EXPECT_THAT(Eval("r - my_r"), IsEqual("0")); |
| EXPECT_THAT(Eval("r * my_r"), IsEqual("4")); |
| EXPECT_THAT(Eval("r / my_r"), IsEqual("1")); |
| |
| // Some promotions and conversions. |
| EXPECT_THAT(Eval("(uint8_t)250 + (uint8_t)250"), IsEqual("500")); |
| |
| // Makes sure that the expression isn't parsed as two types `r<r>` and `r`. |
| EXPECT_THAT(Eval("(r < r > r)"), IsEqual("false")); |
| |
| // On Windows sizeof(int) == sizeof(long) == 4. |
| if constexpr (sizeof(int) == sizeof(long)) { |
| EXPECT_THAT(Eval("(unsigned int)4294967295 + (long)2"), IsEqual("1")); |
| EXPECT_THAT(Eval("((unsigned int)1 + (long)1) - 3"), IsEqual("4294967295")); |
| } else { |
| // On Linux sizeof(int) == 4 and sizeof(long) == 8. |
| EXPECT_THAT(Eval("(unsigned int)4294967295 + (long)2"), |
| IsEqual("4294967297")); |
| EXPECT_THAT(Eval("((unsigned int)1 + (long)1) - 3"), IsEqual("-1")); |
| } |
| } |
| |
| TEST_F(EvalTest, TestZeroDivision) { |
| // Zero division and remainder is UB and LLDB return garbage values. Our |
| // implementation returns zero, but that might change in the future. The |
| // important thing here is to avoid crashing with SIGFPE. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("1 / 0"), IsEqual("0")); |
| EXPECT_THAT(Eval("1 / uint_zero"), IsEqual("0")); |
| EXPECT_THAT(Eval("1ll / 0 + 1"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("1 % 0"), IsEqual("0")); |
| EXPECT_THAT(Eval("1 % uint_zero"), IsEqual("0")); |
| EXPECT_THAT(Eval("1 % uint_zero + 1"), IsEqual("1")); |
| } |
| |
| TEST_F(EvalTest, TestBitwiseOperators) { |
| EXPECT_THAT(Eval("~(-1)"), IsEqual("0")); |
| EXPECT_THAT(Eval("~~0"), IsEqual("0")); |
| EXPECT_THAT(Eval("~0"), IsEqual("-1")); |
| EXPECT_THAT(Eval("~1"), IsEqual("-2")); |
| EXPECT_THAT(Eval("~0LL"), IsEqual("-1")); |
| EXPECT_THAT(Eval("~1LL"), IsEqual("-2")); |
| EXPECT_THAT(Eval("~true"), IsEqual("-2")); |
| EXPECT_THAT(Eval("~false"), IsEqual("-1")); |
| EXPECT_THAT(Eval("~var_true"), IsEqual("-2")); |
| EXPECT_THAT(Eval("~var_false"), IsEqual("-1")); |
| EXPECT_THAT(Eval("~ull_max"), IsEqual("0")); |
| EXPECT_THAT(Eval("~ull_zero"), IsEqual("18446744073709551615")); |
| |
| EXPECT_THAT(Eval("~s"), |
| IsError("invalid argument type 'S' to unary expression")); |
| EXPECT_THAT( |
| Eval("~p"), |
| IsError("invalid argument type 'const char *' to unary expression")); |
| |
| EXPECT_THAT(Eval("(1 << 5)"), IsEqual("32")); |
| EXPECT_THAT(Eval("(32 >> 2)"), IsEqual("8")); |
| EXPECT_THAT(Eval("(-1 >> 10)"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(-100 >> 5)"), IsEqual("-4")); |
| EXPECT_THAT(Eval("(-3 << 6)"), IsEqual("-192")); |
| EXPECT_THAT(Eval("(2000000000U << 1)"), IsEqual("4000000000")); |
| EXPECT_THAT(Eval("(-1 >> 1U)"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(char)1 << 16"), IsEqual("65536")); |
| EXPECT_THAT(Eval("(signed char)-123 >> 8"), IsEqual("-1")); |
| |
| EXPECT_THAT(Eval("0b1011 & 0xFF"), IsEqual("11")); |
| EXPECT_THAT(Eval("0b1011 & mask_ff"), IsEqual("11")); |
| EXPECT_THAT(Eval("0b1011 & 0b0111"), IsEqual("3")); |
| EXPECT_THAT(Eval("0b1011 | 0b0111"), IsEqual("15")); |
| EXPECT_THAT(Eval("-0b1011 | 0xFF"), IsEqual("-1")); |
| EXPECT_THAT(Eval("-0b1011 | 0xFFu"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("0b1011 ^ 0b0111"), IsEqual("12")); |
| EXPECT_THAT(Eval("~0b1011"), IsEqual("-12")); |
| } |
| |
| TEST_F(EvalTest, TestPointerArithmetic) { |
| EXPECT_THAT(Eval("p_char1"), IsOk()); |
| EXPECT_THAT(Eval("p_char1 + 1"), IsOk()); |
| EXPECT_THAT(Eval("p_char1 + offset"), IsOk()); |
| |
| EXPECT_THAT(Eval("my_p_char1"), IsOk()); |
| EXPECT_THAT(Eval("my_p_char1 + 1"), IsOk()); |
| EXPECT_THAT(Eval("my_p_char1 + offset"), IsOk()); |
| |
| EXPECT_THAT(Eval("*(p_char1 + 0)"), IsEqual("'h'")); |
| EXPECT_THAT(Eval("*(1 + p_char1)"), IsEqual("'e'")); |
| EXPECT_THAT(Eval("*(p_char1 + 2)"), IsEqual("'l'")); |
| EXPECT_THAT(Eval("*(3 + p_char1)"), IsEqual("'l'")); |
| EXPECT_THAT(Eval("*(p_char1 + 4)"), IsEqual("'o'")); |
| EXPECT_THAT(Eval("*(p_char1 + offset - 1)"), IsEqual("'o'")); |
| |
| EXPECT_THAT(Eval("*p_int0"), IsEqual("0")); |
| EXPECT_THAT(Eval("*cp_int5"), IsEqual("5")); |
| EXPECT_THAT(Eval("*(&*(cp_int5 + 1) - 1)"), IsEqual("5")); |
| |
| EXPECT_THAT(Eval("p_int0 - p_int0"), IsEqual("0")); |
| EXPECT_THAT(Eval("cp_int5 - p_int0"), IsEqual("5")); |
| EXPECT_THAT(Eval("cp_int5 - td_int_ptr0"), IsEqual("5")); |
| EXPECT_THAT(Eval("td_int_ptr0 - cp_int5"), IsEqual("-5")); |
| |
| EXPECT_THAT( |
| Eval("-p_char1"), |
| IsError("invalid argument type 'const char *' to unary expression")); |
| EXPECT_THAT(Eval("cp_int5 - p_char1"), |
| IsError("'const int *' and 'const char *' are not pointers to " |
| "compatible types")); |
| EXPECT_THAT(Eval("p_int0 + cp_int5"), |
| IsError("invalid operands to binary expression ('int *' and " |
| "'const int *')")); |
| EXPECT_THAT(Eval("p_int0 > p_char1"), |
| IsError("comparison of distinct pointer types ('int *' and " |
| "'const char *')")); |
| |
| EXPECT_THAT(Eval("cp_int5 > td_int_ptr0"), IsEqual("true")); |
| EXPECT_THAT(Eval("cp_int5 < td_int_ptr0"), IsEqual("false")); |
| EXPECT_THAT(Eval("cp_int5 != td_int_ptr0"), IsEqual("true")); |
| EXPECT_THAT(Eval("cp_int5 == td_int_ptr0 + offset"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("p_void + 1"), IsError("arithmetic on a pointer to void")); |
| EXPECT_THAT(Eval("p_void - 1"), IsError("arithmetic on a pointer to void")); |
| EXPECT_THAT(Eval("p_void - p_char1"), |
| IsError("'void *' and 'const char *' are not pointers to " |
| "compatible types")); |
| EXPECT_THAT(Eval("p_void - p_void"), |
| IsError("arithmetic on pointers to void")); |
| |
| EXPECT_THAT(Eval("pp_void0 - p_char1"), |
| IsError("'void **' and 'const char *' are not pointers to " |
| "compatible types")); |
| EXPECT_THAT(Eval("pp_void0 == p_char1"), |
| IsError("comparison of distinct pointer types ('void **' and " |
| "'const char *')")); |
| |
| EXPECT_THAT(Eval("+array"), IsOk()); |
| EXPECT_THAT(Eval("+array_ref"), IsOk()); |
| EXPECT_THAT(Eval("-array"), |
| IsError("invalid argument type 'int *' to unary expression\n" |
| "-array\n" |
| "^")); |
| |
| EXPECT_THAT(Eval("array + 1"), IsOk()); |
| EXPECT_THAT(Eval("1 + array"), IsOk()); |
| EXPECT_THAT(Eval("array_ref + 1"), IsOk()); |
| EXPECT_THAT(Eval("1 + array_ref"), IsOk()); |
| |
| EXPECT_THAT(Eval("array - 1"), IsOk()); |
| EXPECT_THAT(Eval("array_ref - 1"), IsOk()); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("1 - array"), |
| IsError("invalid operands to binary expression ('int' and 'int [10]')\n" |
| "1 - array\n" |
| " ^")); |
| #else |
| EXPECT_THAT( |
| Eval("1 - array"), |
| IsError("invalid operands to binary expression ('int' and 'int[10]')\n" |
| "1 - array\n" |
| " ^")); |
| #endif |
| |
| EXPECT_THAT(Eval("array - array"), IsEqual("0")); |
| EXPECT_THAT(Eval("array - array_ref"), IsEqual("0")); |
| EXPECT_THAT(Eval("array_ref - array_ref"), IsEqual("0")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("array + array"), |
| IsError( |
| "invalid operands to binary expression ('int [10]' and 'int [10]')\n" |
| "array + array\n" |
| " ^")); |
| #else |
| EXPECT_THAT( |
| Eval("array + array"), |
| IsError( |
| "invalid operands to binary expression ('int[10]' and 'int[10]')\n" |
| "array + array\n" |
| " ^")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, PointerPointerArithmeticFloat) { |
| EXPECT_THAT( |
| Eval("(int*)0 + 1.1"), |
| IsError("invalid operands to binary expression ('int *' and 'double')")); |
| EXPECT_THAT( |
| Eval("1.1 + (int*)0"), |
| IsError("invalid operands to binary expression ('double' and 'int *')")); |
| EXPECT_THAT( |
| Eval("(int*)0 - 1.1"), |
| IsError("invalid operands to binary expression ('int *' and 'double')")); |
| } |
| |
| TEST_F(EvalTest, PointerPointerComparison) { |
| EXPECT_THAT(Eval("p_void == p_void"), IsEqual("true")); |
| EXPECT_THAT(Eval("p_void == p_char1"), IsEqual("true")); |
| EXPECT_THAT(Eval("p_void != p_char1"), IsEqual("false")); |
| EXPECT_THAT(Eval("p_void > p_char1"), IsEqual("false")); |
| EXPECT_THAT(Eval("p_void >= p_char1"), IsEqual("true")); |
| EXPECT_THAT(Eval("p_void < (p_char1 + 1)"), IsEqual("true")); |
| EXPECT_THAT(Eval("pp_void0 + 1 == pp_void1"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("(void*)1 == (void*)1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)1 != (void*)1"), IsEqual("false")); |
| EXPECT_THAT(Eval("(void*)2 > (void*)1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)2 < (void*)1"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("(void*)1 == (char*)1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(char*)1 != (void*)1"), IsEqual("false")); |
| EXPECT_THAT(Eval("(void*)2 > (char*)1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(char*)2 < (void*)1"), IsEqual("false")); |
| } |
| |
| TEST_F(EvalTest, PointerIntegerComparison) { |
| EXPECT_THAT(Eval("(void*)0 == 0"), IsEqual("true")); |
| EXPECT_THAT(Eval("0 != (void*)0"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("(void*)0 == nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)0 != nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("nullptr == (void*)1"), IsEqual("false")); |
| EXPECT_THAT(Eval("nullptr != (void*)1"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("nullptr == nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("nullptr != nullptr"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("nullptr == 0"), IsEqual("true")); |
| EXPECT_THAT(Eval("0 != nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("nullptr == 0U"), IsEqual("true")); |
| EXPECT_THAT(Eval("0L != nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("nullptr == 0UL"), IsEqual("true")); |
| EXPECT_THAT(Eval("0ULL != nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("nullptr == 0x0"), IsEqual("true")); |
| EXPECT_THAT(Eval("0b0 != nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("nullptr == 00"), IsEqual("true")); |
| EXPECT_THAT(Eval("0x0LLU != nullptr"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("0 == std_nullptr_t"), IsEqual("true")); |
| EXPECT_THAT(Eval("std_nullptr_t != 0"), IsEqual("false")); |
| |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("(void*)0 > nullptr"), |
| IsError( |
| "invalid operands to binary expression ('void *' and 'nullptr_t')")); |
| |
| EXPECT_THAT( |
| Eval("nullptr > 0"), |
| IsError("invalid operands to binary expression ('nullptr_t' and 'int')")); |
| |
| EXPECT_THAT( |
| Eval("1 == nullptr"), |
| IsError("invalid operands to binary expression ('int' and 'nullptr_t')")); |
| |
| EXPECT_THAT( |
| Eval("nullptr == (int)0"), |
| IsError("invalid operands to binary expression ('nullptr_t' and 'int')")); |
| |
| EXPECT_THAT( |
| Eval("false == nullptr"), |
| IsError( |
| "invalid operands to binary expression ('bool' and 'nullptr_t')")); |
| |
| EXPECT_THAT( |
| Eval("nullptr == (true ? 0 : 0)"), |
| IsError("invalid operands to binary expression ('nullptr_t' and 'int')")); |
| |
| // TODO: Enable when we support char literals. |
| // EXPECT_THAT( |
| // Eval("'\0' == nullptr"), |
| // IsError( |
| // "invalid operands to binary expression ('char' and 'nullptr_t')")); |
| |
| EXPECT_THAT(Eval("nullptr > nullptr"), |
| IsError("invalid operands to binary expression ('nullptr_t' and " |
| "'nullptr_t')")); |
| #else |
| EXPECT_THAT(Eval("(void*)0 > nullptr"), |
| IsError("invalid operands to binary expression ('void *' and " |
| "'std::nullptr_t')")); |
| |
| EXPECT_THAT(Eval("nullptr > 0"), |
| IsError("invalid operands to binary expression ('std::nullptr_t' " |
| "and 'int')")); |
| |
| EXPECT_THAT(Eval("1 == nullptr"), |
| IsError("invalid operands to binary expression ('int' and " |
| "'std::nullptr_t')")); |
| |
| EXPECT_THAT(Eval("nullptr == (int)0"), |
| IsError("invalid operands to binary expression ('std::nullptr_t' " |
| "and 'int')")); |
| |
| EXPECT_THAT(Eval("false == nullptr"), |
| IsError("invalid operands to binary expression ('bool' and " |
| "'std::nullptr_t')")); |
| |
| EXPECT_THAT(Eval("nullptr == (true ? 0 : 0)"), |
| IsError("invalid operands to binary expression ('std::nullptr_t' " |
| "and 'int')")); |
| |
| EXPECT_THAT( |
| Eval("nullptr > nullptr"), |
| IsError("invalid operands to binary expression ('std::nullptr_t' and " |
| "'std::nullptr_t')")); |
| #endif |
| |
| // These are not allowed by C++, but we support it as an extension. |
| EXPECT_THAT(Eval("(void*)1 == 1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)1 == 0"), IsEqual("false")); |
| EXPECT_THAT(Eval("(void*)1 > 0"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)1 < 0"), IsEqual("false")); |
| EXPECT_THAT(Eval("1 > (void*)0"), IsEqual("true")); |
| EXPECT_THAT(Eval("2 < (void*)3"), IsEqual("true")); |
| |
| // Integer is converted to uintptr_t, so negative numbers because large |
| // positive numbers. |
| EXPECT_THAT(Eval("(void*)0xffffffffffffffff == -1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)-1 == -1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(void*)1 > -1"), IsEqual("false")); |
| } |
| |
| TEST_F(EvalTest, TestPointerDereference) { |
| EXPECT_THAT(Eval("*p_int0"), IsEqual("0")); |
| EXPECT_THAT(Eval("*p_int0 + 1"), IsEqual("1")); |
| EXPECT_THAT(Eval("*cp_int5"), IsEqual("5")); |
| EXPECT_THAT(Eval("*cp_int5 - 1"), IsEqual("4")); |
| |
| EXPECT_THAT(Eval("&*p_null"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("&p_null[4]"), |
| IsEqual(Is32Bit() ? "0x00000010" : "0x0000000000000010")); |
| EXPECT_THAT(Eval("&*(int*)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("&((int*)0)[1]"), |
| IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); |
| |
| EXPECT_THAT(Eval("&p_void[0]"), |
| IsError("subscript of pointer to incomplete type 'void'")); |
| EXPECT_THAT(Eval("&*p_void"), IsOk()); |
| EXPECT_THAT(Eval("&pp_void0[2]"), IsOk()); |
| |
| EXPECT_THAT(Eval("**pp_int0"), IsEqual("0")); |
| EXPECT_THAT(Eval("**pp_int0 + 1"), IsEqual("1")); |
| EXPECT_THAT(Eval("&**pp_int0"), IsOk()); |
| EXPECT_THAT(Eval("&**pp_int0 + 1"), IsOk()); |
| |
| EXPECT_THAT(Eval("&(true ? *p_null : *p_null)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| |
| EXPECT_THAT(Eval("&(false ? *p_null : *p_null)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("&*(true ? p_null : nullptr)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| } |
| |
| TEST_F(EvalTest, TestLogicalOperators) { |
| EXPECT_THAT(Eval("1 > 2"), IsEqual("false")); |
| EXPECT_THAT(Eval("1 == 1"), IsEqual("true")); |
| EXPECT_THAT(Eval("1 > 0.1"), IsEqual("true")); |
| EXPECT_THAT(Eval("1 && 2"), IsEqual("true")); |
| EXPECT_THAT(Eval("0 && 1"), IsEqual("false")); |
| EXPECT_THAT(Eval("0 || 1"), IsEqual("true")); |
| EXPECT_THAT(Eval("0 || 0"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("!1"), IsEqual("false")); |
| EXPECT_THAT(Eval("!!1"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("!trueVar"), IsEqual("false")); |
| EXPECT_THAT(Eval("!!trueVar"), IsEqual("true")); |
| EXPECT_THAT(Eval("!falseVar"), IsEqual("true")); |
| EXPECT_THAT(Eval("!!falseVar"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("trueVar && true"), IsEqual("true")); |
| EXPECT_THAT(Eval("trueVar && (2 > 1)"), IsEqual("true")); |
| EXPECT_THAT(Eval("trueVar && (2 < 1)"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("falseVar || true"), IsEqual("true")); |
| EXPECT_THAT(Eval("falseVar && true"), IsEqual("false")); |
| EXPECT_THAT(Eval("falseVar || (2 > 1)"), IsEqual("true")); |
| EXPECT_THAT(Eval("falseVar || (2 < 1)"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("true || __doesnt_exist"), |
| IsError("use of undeclared identifier '__doesnt_exist'")); |
| EXPECT_THAT(Eval("false && __doesnt_exist"), |
| IsError("use of undeclared identifier '__doesnt_exist'")); |
| |
| EXPECT_THAT(Eval("!p_ptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("!!p_ptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("p_ptr && true"), IsEqual("true")); |
| EXPECT_THAT(Eval("p_ptr && false"), IsEqual("false")); |
| EXPECT_THAT(Eval("!p_nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("!!p_nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("p_nullptr || true"), IsEqual("true")); |
| EXPECT_THAT(Eval("p_nullptr || false"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("!array"), IsEqual("false")); |
| EXPECT_THAT(Eval("!!array"), IsEqual("true")); |
| EXPECT_THAT(Eval("array || true"), IsEqual("true")); |
| EXPECT_THAT(Eval("false || array"), IsEqual("true")); |
| EXPECT_THAT(Eval("array && true"), IsEqual("true")); |
| EXPECT_THAT(Eval("array && false"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("false || !s"), |
| IsError("invalid argument type 'S' to unary expression\n" |
| "false || !s\n" |
| " ^")); |
| EXPECT_THAT( |
| Eval("s || false"), |
| IsError("value of type 'S' is not contextually convertible to 'bool'\n" |
| "s || false\n" |
| "^")); |
| EXPECT_THAT( |
| Eval("true || s"), |
| IsError("value of type 'S' is not contextually convertible to 'bool'\n" |
| "true || s\n" |
| " ^")); |
| EXPECT_THAT( |
| Eval("s ? 1 : 2"), |
| IsError("value of type 'S' is not contextually convertible to 'bool'")); |
| } |
| |
| TEST_F(EvalTest, TestLocalVariables) { |
| EXPECT_THAT(Eval("a"), IsEqual("1")); |
| EXPECT_THAT(Eval("b"), IsEqual("2")); |
| EXPECT_THAT(Eval("a + b"), IsEqual("3")); |
| |
| EXPECT_THAT(Eval("c + 1"), IsEqual("-2")); |
| EXPECT_THAT(Eval("s + 1"), IsEqual("5")); |
| EXPECT_THAT(Eval("c + s"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("__test_non_variable + 1"), |
| IsError("use of undeclared identifier '__test_non_variable'")); |
| } |
| |
| TEST_F(EvalTest, TestMemberOf) { |
| EXPECT_THAT(Eval("s.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("s.r"), IsEqual("2")); |
| EXPECT_THAT(Eval("s.r + 1"), IsEqual("3")); |
| EXPECT_THAT(Eval("sr.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("sr.r"), IsEqual("2")); |
| EXPECT_THAT(Eval("sr.r + 1"), IsEqual("3")); |
| EXPECT_THAT(Eval("sp->x"), IsEqual("1")); |
| EXPECT_THAT(Eval("sp->r"), IsEqual("2")); |
| EXPECT_THAT(Eval("sp->r + 1"), IsEqual("3")); |
| EXPECT_THAT(Eval("sarr->x"), IsEqual("5")); |
| EXPECT_THAT(Eval("sarr->r"), IsEqual("2")); |
| EXPECT_THAT(Eval("sarr->r + 1"), IsEqual("3")); |
| EXPECT_THAT(Eval("(sarr + 1)->x"), IsEqual("1")); |
| |
| EXPECT_THAT( |
| Eval("sp->4"), |
| IsError( |
| "<expr>:1:5: expected 'identifier', got: <'4' (numeric_constant)>\n" |
| "sp->4\n" |
| " ^")); |
| EXPECT_THAT(Eval("sp->foo"), IsError("no member named 'foo' in 'Sx'")); |
| EXPECT_THAT( |
| Eval("sp->r / (void*)0"), |
| IsError("invalid operands to binary expression ('int' and 'void *')")); |
| |
| EXPECT_THAT(Eval("sp.x"), IsError("member reference type 'Sx *' is a " |
| "pointer; did you mean to use '->'")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("sarr.x"), |
| IsError( |
| "member reference base type 'Sx [2]' is not a structure or union")); |
| #else |
| EXPECT_THAT( |
| Eval("sarr.x"), |
| IsError( |
| "member reference base type 'Sx[2]' is not a structure or union")); |
| #endif |
| |
| // Test for record typedefs. |
| EXPECT_THAT(Eval("sa.x"), IsEqual("3")); |
| EXPECT_THAT(Eval("sa.y"), IsEqual("'\\x04'")); |
| |
| // TODO(werat): Implement address-of-member-or combination. |
| // EXPECT_THAT(Eval("&((Sx*)0)->x"), IsEqual("0x0000000000000000")); |
| // EXPECT_THAT(Eval("&((Sx*)0)->y"), IsEqual("0x0000000000000010")); |
| // EXPECT_THAT(Eval("&(*(Sx*)0).x"), IsEqual("0x0000000000000000")); |
| // EXPECT_THAT(Eval("&(*(Sx*)0).y"), IsEqual("0x0000000000000010")); |
| } |
| |
| TEST_F(EvalTest, TestMemberOfInheritance) { |
| EXPECT_THAT(Eval("a.a_"), IsEqual("1")); |
| EXPECT_THAT(Eval("b.b_"), IsEqual("2")); |
| EXPECT_THAT(Eval("c.a_"), IsEqual("1")); |
| EXPECT_THAT(Eval("c.b_"), IsEqual("2")); |
| EXPECT_THAT(Eval("c.c_"), IsEqual("3")); |
| EXPECT_THAT(Eval("d.a_"), IsEqual("1")); |
| EXPECT_THAT(Eval("d.b_"), IsEqual("2")); |
| EXPECT_THAT(Eval("d.c_"), IsEqual("3")); |
| EXPECT_THAT(Eval("d.d_"), IsEqual("4")); |
| EXPECT_THAT(Eval("d.fa_.a_"), IsEqual("5")); |
| |
| EXPECT_THAT(Eval("bat.weight_"), IsEqual("10")); |
| |
| EXPECT_THAT(Eval("plugin.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("plugin.y"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("engine.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("engine.y"), IsEqual("2")); |
| EXPECT_THAT(Eval("engine.z"), IsEqual("3")); |
| |
| EXPECT_THAT(Eval("parent_base->x"), IsEqual("1")); |
| EXPECT_THAT(Eval("parent_base->y"), IsEqual("2")); |
| EXPECT_THAT(Eval("parent->x"), IsEqual("1")); |
| EXPECT_THAT(Eval("parent->y"), IsEqual("2")); |
| EXPECT_THAT(Eval("parent->z"), IsEqual("3")); |
| } |
| |
| TEST_F(EvalTest, TestMemberOfAnonymousMember) { |
| EXPECT_THAT(Eval("a.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("a.y"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("b.x"), IsError("no member named 'x' in 'B'")); |
| EXPECT_THAT(Eval("b.y"), IsError("no member named 'y' in 'B'")); |
| EXPECT_THAT(Eval("b.z"), IsEqual("3")); |
| EXPECT_THAT(Eval("b.w"), IsEqual("4")); |
| EXPECT_THAT(Eval("b.a.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("b.a.y"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("c.x"), IsEqual("5")); |
| EXPECT_THAT(Eval("c.y"), IsEqual("6")); |
| |
| EXPECT_THAT(Eval("d.x"), IsEqual("7")); |
| EXPECT_THAT(Eval("d.y"), IsEqual("8")); |
| EXPECT_THAT(Eval("d.z"), IsEqual("9")); |
| EXPECT_THAT(Eval("d.w"), IsEqual("10")); |
| |
| EXPECT_THAT(Eval("e.x"), IsError("no member named 'x' in 'E'")); |
| EXPECT_THAT(Eval("f.x"), IsError("no member named 'x' in 'F'")); |
| EXPECT_THAT(Eval("f.named_field.x"), IsEqual("12")); |
| |
| EXPECT_THAT(Eval("unnamed_derived.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("unnamed_derived.y"), IsEqual("2")); |
| EXPECT_THAT(Eval("unnamed_derived.z"), IsEqual("13")); |
| |
| EXPECT_THAT(Eval("derb.x"), IsError("no member named 'x' in 'DerivedB'")); |
| EXPECT_THAT(Eval("derb.y"), IsError("no member named 'y' in 'DerivedB'")); |
| EXPECT_THAT(Eval("derb.z"), IsEqual("3")); |
| EXPECT_THAT(Eval("derb.w"), IsEqual("14")); |
| EXPECT_THAT(Eval("derb.k"), IsEqual("15")); |
| EXPECT_THAT(Eval("derb.a.x"), IsEqual("1")); |
| EXPECT_THAT(Eval("derb.a.y"), IsEqual("2")); |
| } |
| |
| TEST_F(EvalTest, TestGlobalVariableLookup) { |
| EXPECT_THAT(Eval("globalVar"), IsEqual("-559038737")); // 0xDEADBEEF |
| EXPECT_THAT(Eval("globalPtr"), IsOk()); |
| EXPECT_THAT(Eval("globalRef"), IsEqual("-559038737")); |
| EXPECT_THAT(Eval("::globalPtr"), IsOk()); |
| EXPECT_THAT(Eval("::globalRef"), IsEqual("-559038737")); |
| |
| EXPECT_THAT(Eval("externGlobalVar"), IsEqual("12648430")); // 0x00C0FFEE |
| EXPECT_THAT(Eval("::externGlobalVar"), IsEqual("12648430")); |
| |
| EXPECT_THAT(Eval("ns::globalVar"), IsEqual("13")); |
| EXPECT_THAT(Eval("ns::globalPtr"), IsOk()); |
| EXPECT_THAT(Eval("ns::globalRef"), IsEqual("13")); |
| EXPECT_THAT(Eval("::ns::globalVar"), IsEqual("13")); |
| EXPECT_THAT(Eval("::ns::globalPtr"), IsOk()); |
| } |
| |
| TEST_F(EvalTest, TestInstanceVariables) { |
| EXPECT_THAT(Eval("this->field_"), IsEqual("1")); |
| EXPECT_THAT(Eval("this.field_"), |
| IsError("member reference type 'TestMethods *' is a pointer; did " |
| "you mean to use '->'?")); |
| |
| EXPECT_THAT(Eval("c.field_"), IsEqual("-1")); |
| EXPECT_THAT(Eval("c_ref.field_"), IsEqual("-1")); |
| EXPECT_THAT(Eval("c_ptr->field_"), IsEqual("-1")); |
| EXPECT_THAT(Eval("c->field_"), IsError("member reference type 'C' is not a " |
| "pointer; did you mean to use '.'?")); |
| } |
| |
| TEST_F(EvalTest, TestIndirection) { |
| EXPECT_THAT(Eval("*p"), IsEqual("1")); |
| EXPECT_THAT(Eval("p"), IsOk()); |
| EXPECT_THAT(Eval("*my_p"), IsEqual("1")); |
| EXPECT_THAT(Eval("my_p"), IsOk()); |
| EXPECT_THAT(Eval("*my_pr"), IsEqual("1")); |
| EXPECT_THAT(Eval("my_pr"), IsOk()); |
| |
| EXPECT_THAT(Eval("*1"), |
| IsError("indirection requires pointer operand ('int' invalid)")); |
| EXPECT_THAT(Eval("*val"), |
| IsError("indirection requires pointer operand ('int' invalid)")); |
| } |
| |
| TEST_F(EvalTest, TestAddressOf) { |
| EXPECT_THAT(Eval("&x"), IsOk()); |
| EXPECT_THAT(Eval("r"), IsOk()); |
| EXPECT_THAT(Eval("&r"), IsOk()); |
| EXPECT_THAT(Eval("pr"), IsOk()); |
| EXPECT_THAT(Eval("&pr"), IsOk()); |
| EXPECT_THAT(Eval("my_pr"), IsOk()); |
| EXPECT_THAT(Eval("&my_pr"), IsOk()); |
| |
| EXPECT_THAT(Eval("&x == &r"), IsEqual("true")); |
| EXPECT_THAT(Eval("&x != &r"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("&p == &pr"), IsEqual("true")); |
| EXPECT_THAT(Eval("&p != &pr"), IsEqual("false")); |
| EXPECT_THAT(Eval("&p == &my_pr"), IsEqual("true")); |
| EXPECT_THAT(Eval("&p != &my_pr"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("&globalVar"), IsOk()); |
| EXPECT_THAT(Eval("&externGlobalVar"), IsOk()); |
| EXPECT_THAT(Eval("&s_str"), IsOk()); |
| EXPECT_THAT(Eval("¶m"), IsOk()); |
| |
| EXPECT_THAT(Eval("&1"), |
| IsError("cannot take the address of an rvalue of type 'int'")); |
| EXPECT_THAT(Eval("&0.1"), |
| IsError("cannot take the address of an rvalue of type 'double'")); |
| |
| EXPECT_THAT( |
| Eval("&this"), |
| IsError("cannot take the address of an rvalue of type 'TestMethods *'")); |
| EXPECT_THAT( |
| Eval("&(&s_str)"), |
| IsError("cannot take the address of an rvalue of type 'const char **'")); |
| |
| EXPECT_THAT(Eval("&(true ? x : x)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? 1 : 1)"), |
| IsError("cannot take the address of an rvalue of type 'int'")); |
| |
| EXPECT_THAT(Eval("&(true ? c : c)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? c : (char)1)"), |
| IsError("cannot take the address of an rvalue of type 'char'")); |
| EXPECT_THAT(Eval("&(true ? c : 1)"), |
| IsError("cannot take the address of an rvalue of type 'int'")); |
| } |
| |
| TEST_F(EvalTest, TestSubscript) { |
| // const char* |
| EXPECT_THAT(Eval("char_ptr[0]"), IsEqual("'l'")); |
| EXPECT_THAT(Eval("1[char_ptr]"), IsEqual("'o'")); |
| |
| // const char[] |
| EXPECT_THAT(Eval("char_arr[0]"), IsEqual("'i'")); |
| EXPECT_THAT(Eval("1[char_arr]"), IsEqual("'p'")); |
| |
| // Boolean types are integral too! |
| EXPECT_THAT(Eval("int_arr[false]"), IsEqual("1")); |
| EXPECT_THAT(Eval("true[int_arr]"), IsEqual("2")); |
| |
| // As well as unscoped enums. |
| EXPECT_THAT(Eval("int_arr[enum_one]"), IsEqual("2")); |
| EXPECT_THAT(Eval("enum_one[int_arr]"), IsEqual("2")); |
| |
| // But floats are not. |
| EXPECT_THAT(Eval("int_arr[1.0]"), |
| IsError("array subscript is not an integer")); |
| |
| // Base should be a "pointer to T" and index should be of an integral type. |
| EXPECT_THAT(Eval("char_arr[char_ptr]"), |
| IsError("array subscript is not an integer")); |
| EXPECT_THAT(Eval("1[2]"), |
| IsError("subscripted value is not an array or pointer")); |
| |
| // Test when base and index are references. |
| EXPECT_THAT(Eval("c_arr[0].field_"), IsEqual("0")); |
| EXPECT_THAT(Eval("c_arr[idx_1_ref].field_"), IsEqual("1")); |
| EXPECT_THAT(Eval("c_arr[enum_ref].field_"), IsEqual("1")); |
| EXPECT_THAT(Eval("c_arr_ref[0].field_"), IsEqual("0")); |
| EXPECT_THAT(Eval("c_arr_ref[idx_1_ref].field_"), IsEqual("1")); |
| EXPECT_THAT(Eval("c_arr_ref[enum_ref].field_"), IsEqual("1")); |
| |
| // Test when base and index are typedefs. |
| bool compare_types = true; |
| #if LLVM_VERSION_MAJOR < 12 |
| // Older LLVM versions return canonical types when accessing array elements. |
| compare_types = false; |
| #endif |
| EXPECT_THAT(Eval("td_int_arr[0]"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("td_int_arr[td_int_idx_1]"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("td_int_arr[td_td_int_idx_2]"), IsEqual("3", compare_types)); |
| EXPECT_THAT(Eval("td_int_ptr[0]"), IsEqual("1")); |
| EXPECT_THAT(Eval("td_int_ptr[td_int_idx_1]"), IsEqual("2")); |
| EXPECT_THAT(Eval("td_int_ptr[td_td_int_idx_2]"), IsEqual("3")); |
| // Both typedefs and refs! |
| EXPECT_THAT(Eval("td_int_arr_ref[td_int_idx_1_ref]"), |
| IsEqual("2", compare_types)); |
| |
| // Test for index out of bounds. |
| EXPECT_THAT(Eval("int_arr[42]"), IsOk()); |
| EXPECT_THAT(Eval("int_arr[100]"), IsOk()); |
| |
| // Test for negative index. |
| EXPECT_THAT(Eval("int_arr[-1]"), IsOk()); |
| EXPECT_THAT(Eval("int_arr[-42]"), IsOk()); |
| |
| // Test for "max unsigned char". |
| EXPECT_THAT(Eval("uint8_arr[uchar_idx]"), IsEqual("'\\xab'", compare_types)); |
| |
| // Test address-of of the subscripted value. |
| EXPECT_THAT(Eval("(&c_arr[1])->field_"), IsEqual("1")); |
| } |
| |
| TEST_F(EvalTest, TestCStyleCastBuiltins) { |
| EXPECT_THAT(Eval("(int)1"), IsOk()); |
| EXPECT_THAT(Eval("(long long)1"), IsOk()); |
| EXPECT_THAT(Eval("(unsigned long)1"), IsOk()); |
| EXPECT_THAT(Eval("(long const const)1"), IsOk()); |
| EXPECT_THAT(Eval("(long const long)1"), IsOk()); |
| |
| EXPECT_THAT(Eval("(char*)1"), IsOk()); |
| EXPECT_THAT(Eval("(long long**)1"), IsOk()); |
| EXPECT_THAT(Eval("(const long const long const* const const)1"), IsOk()); |
| |
| EXPECT_THAT( |
| Eval("(long&*)1"), |
| IsError( |
| "'type name' declared as a pointer to a reference of type 'long &'\n" |
| "(long&*)1\n" |
| " ^")); |
| |
| EXPECT_THAT(Eval("(long& &)1"), |
| IsError("type name declared as a reference to a reference\n" |
| "(long& &)1\n" |
| " ^")); |
| |
| EXPECT_THAT( |
| Eval("(long 1)1"), |
| IsError("<expr>:1:7: expected 'r_paren', got: <'1' (numeric_constant)>\n" |
| "(long 1)1\n" |
| " ^")); |
| } |
| |
| TEST_F(EvalTest, TestCStyleCastBasicType) { |
| // Test with integer literals. |
| EXPECT_THAT(Eval("(char)1"), IsEqual("'\\x01'")); |
| EXPECT_THAT(Eval("(unsigned char)-1"), IsEqual("'\\xff'")); |
| EXPECT_THAT(Eval("(short)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(unsigned short)-1"), IsEqual("65535")); |
| EXPECT_THAT(Eval("(long long)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(unsigned long long)-1"), IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("(short)65534"), IsEqual("-2")); |
| EXPECT_THAT(Eval("(unsigned short)100000"), IsEqual("34464")); |
| EXPECT_THAT(Eval("(int)false"), IsEqual("0")); |
| EXPECT_THAT(Eval("(int)true"), IsEqual("1")); |
| EXPECT_THAT(Eval("(float)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(float)1.1"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(float)1.1f"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(float)-1.1"), IsEqual("-1.10000002")); |
| EXPECT_THAT(Eval("(float)-1.1f"), IsEqual("-1.10000002")); |
| EXPECT_THAT(Eval("(float)false"), IsEqual("0")); |
| EXPECT_THAT(Eval("(float)true"), IsEqual("1")); |
| EXPECT_THAT(Eval("(double)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(double)1.1"), IsEqual("1.1000000000000001")); |
| EXPECT_THAT(Eval("(double)1.1f"), IsEqual("1.1000000238418579")); |
| EXPECT_THAT(Eval("(double)-1.1"), IsEqual("-1.1000000000000001")); |
| EXPECT_THAT(Eval("(double)-1.1f"), IsEqual("-1.1000000238418579")); |
| EXPECT_THAT(Eval("(double)false"), IsEqual("0")); |
| EXPECT_THAT(Eval("(double)true"), IsEqual("1")); |
| EXPECT_THAT(Eval("(int)1.1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(int)1.1f"), IsEqual("1")); |
| EXPECT_THAT(Eval("(int)-1.1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(long)1.1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(long)-1.1f"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(bool)0"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)0.0"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)0.0f"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)3"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)-3"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)-3.4"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)-0.1"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)-0.1f"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("&(int)1"), |
| IsError("cannot take the address of an rvalue of type 'int'")); |
| |
| // Test with variables. |
| EXPECT_THAT(Eval("(char)a"), IsEqual("'\\x01'")); |
| EXPECT_THAT(Eval("(unsigned char)na"), IsEqual("'\\xff'")); |
| EXPECT_THAT(Eval("(short)na"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(unsigned short)-a"), IsEqual("65535")); |
| EXPECT_THAT(Eval("(long long)a"), IsEqual("1")); |
| EXPECT_THAT(Eval("(unsigned long long)-1"), IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("(float)a"), IsEqual("1")); |
| EXPECT_THAT(Eval("(float)f"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(double)f"), IsEqual("1.1000000238418579")); |
| EXPECT_THAT(Eval("(int)f"), IsEqual("1")); |
| EXPECT_THAT(Eval("(long)f"), IsEqual("1")); |
| EXPECT_THAT(Eval("(bool)finf"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)fnan"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)fsnan"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)fmax"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)fdenorm"), IsEqual("true")); |
| |
| EXPECT_THAT( |
| Eval("(int)ns_foo_"), |
| IsError( |
| "cannot convert 'ns::Foo' to 'int' without a conversion operator")); |
| |
| // Test with typedefs and namespaces. |
| EXPECT_THAT(Eval("(myint)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(myint)1LL"), IsEqual("1")); |
| EXPECT_THAT(Eval("(ns::myint)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::myint)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::myint)myint_"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("(int)myint_"), IsEqual("1")); |
| EXPECT_THAT(Eval("(int)ns_myint_"), IsEqual("2")); |
| EXPECT_THAT(Eval("(long long)myint_"), IsEqual("1")); |
| EXPECT_THAT(Eval("(long long)ns_myint_"), IsEqual("2")); |
| EXPECT_THAT(Eval("(::ns::myint)myint_"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("(ns::inner::mydouble)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::inner::mydouble)1.2"), IsEqual("1.2")); |
| EXPECT_THAT(Eval("(ns::inner::mydouble)myint_"), IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::inner::mydouble)ns_inner_mydouble_"), |
| IsEqual("1.2")); |
| EXPECT_THAT(Eval("(myint)ns_inner_mydouble_"), IsEqual("1")); |
| |
| // Test with pointers and arrays. |
| EXPECT_THAT(Eval("(long long)ap"), IsOk()); |
| EXPECT_THAT(Eval("(unsigned long long)vp"), IsOk()); |
| EXPECT_THAT(Eval("(long long)arr"), IsOk()); |
| EXPECT_THAT(Eval("(bool)ap"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)(int*)0x00000000"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)arr"), IsEqual("true")); |
| EXPECT_THAT( |
| Eval("(char)ap"), |
| IsError("cast from pointer to smaller type 'char' loses information")); |
| if (Is32Bit()) { |
| EXPECT_THAT(Eval("(int)arr"), IsOk()); |
| } else { |
| EXPECT_THAT( |
| Eval("(int)arr"), |
| IsError("cast from pointer to smaller type 'int' loses information")); |
| } |
| |
| #ifdef _WIN32 |
| EXPECT_THAT( |
| Eval("(long)ap"), |
| IsError("cast from pointer to smaller type 'long' loses information")); |
| #endif |
| |
| EXPECT_THAT(Eval("(float)ap"), |
| IsError("C-style cast from 'int *' to 'float' is not allowed")); |
| EXPECT_THAT(Eval("(float)arr"), |
| IsError("C-style cast from 'int *' to 'float' is not allowed")); |
| } |
| |
| TEST_F(EvalTest, TestCStyleCastPointer) { |
| EXPECT_THAT(Eval("(void*)&a"), IsOk()); |
| EXPECT_THAT(Eval("(void*)ap"), IsOk()); |
| EXPECT_THAT(Eval("(long long*)vp"), IsOk()); |
| EXPECT_THAT(Eval("(short int*)vp"), IsOk()); |
| EXPECT_THAT(Eval("(unsigned long long*)vp"), IsOk()); |
| EXPECT_THAT(Eval("(unsigned short int*)vp"), IsOk()); |
| |
| EXPECT_THAT(Eval("(void*)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("(void*)1"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| EXPECT_THAT(Eval("(void*)a"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| EXPECT_THAT(Eval("(void*)na"), |
| IsEqual(Is32Bit() ? "0xffffffff" : "0xffffffffffffffff")); |
| EXPECT_THAT(Eval("(int*&)ap"), IsOk()); |
| |
| EXPECT_THAT( |
| Eval("(char*) 1.0"), |
| IsError("cannot cast from type 'double' to pointer type 'char *'")); |
| |
| EXPECT_THAT(Eval("*(const int* const)ap"), IsEqual("1")); |
| EXPECT_THAT(Eval("*(volatile int* const)ap"), IsEqual("1")); |
| EXPECT_THAT(Eval("*(const int* const)vp"), IsEqual("1")); |
| EXPECT_THAT(Eval("*(const int* const volatile const)vp"), IsEqual("1")); |
| EXPECT_THAT(Eval("*(int*)(void*)ap"), IsEqual("1")); |
| EXPECT_THAT(Eval("*(int*)(const void* const volatile)ap"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("(ns::Foo*)ns_inner_foo_ptr_"), IsOk()); |
| EXPECT_THAT(Eval("(ns::inner::Foo*)ns_foo_ptr_"), IsOk()); |
| |
| EXPECT_THAT(Eval("(int& &)ap"), |
| IsError("type name declared as a reference to a reference")); |
| EXPECT_THAT(Eval("(int&*)ap"), IsError("'type name' declared as a pointer " |
| "to a reference of type 'int &'")); |
| |
| EXPECT_THAT(Eval("(std::nullptr_t)nullptr"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("(std::nullptr_t)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Eval("(std::nullptr_t)1"), |
| IsError("C-style cast from 'int' to 'std::nullptr_t' (aka " |
| "'nullptr_t') is not allowed")); |
| EXPECT_THAT(Eval("(std::nullptr_t)ap"), |
| IsError("C-style cast from 'int *' to 'std::nullptr_t' (aka " |
| "'nullptr_t') is not allowed")); |
| #else |
| EXPECT_THAT( |
| Eval("(std::nullptr_t)1"), |
| IsError("C-style cast from 'int' to 'std::nullptr_t' is not allowed")); |
| EXPECT_THAT( |
| Eval("(std::nullptr_t)ap"), |
| IsError("C-style cast from 'int *' to 'std::nullptr_t' is not allowed")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestCStyleCastNullptrType) { |
| if (Is32Bit()) { |
| EXPECT_THAT(Eval("(int)nullptr"), IsOk()); |
| } else { |
| EXPECT_THAT( |
| Eval("(int)nullptr"), |
| IsError("cast from pointer to smaller type 'int' loses information")); |
| } |
| EXPECT_THAT(Eval("(uint64_t)nullptr"), IsEqual("0")); |
| |
| EXPECT_THAT(Eval("(void*)nullptr"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("(char*)nullptr"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| } |
| |
| TEST_F(EvalTest, TestCStyleCastArray) { |
| EXPECT_THAT(Eval("(int*)arr_1d"), IsOk()); |
| EXPECT_THAT(Eval("(char*)arr_1d"), IsOk()); |
| EXPECT_THAT(Eval("((char*)arr_1d)[0]"), IsEqual("'\\x01'")); |
| EXPECT_THAT(Eval("((char*)arr_1d)[1]"), IsEqual("'\\0'")); |
| |
| // 2D arrays. |
| EXPECT_THAT(Eval("(int*)arr_2d"), IsOk()); |
| EXPECT_THAT(Eval("((int*)arr_2d)[1]"), IsEqual("2")); |
| EXPECT_THAT(Eval("((int*)arr_2d)[2]"), IsEqual("3")); |
| EXPECT_THAT(Eval("((int*)arr_2d[1])[1]"), IsEqual("5")); |
| } |
| |
| TEST_F(EvalTest, TestArrayDereference) { |
| EXPECT_THAT(Eval("*arr_1d"), IsEqual("1")); |
| EXPECT_THAT(Eval("&*arr_1d"), IsOk()); |
| EXPECT_THAT(Eval("*(arr_1d + 1)"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("(int*)*arr_2d"), IsOk()); |
| EXPECT_THAT(Eval("&*arr_2d"), IsOk()); |
| EXPECT_THAT(Eval("**arr_2d"), IsEqual("1")); |
| EXPECT_THAT(Eval("*arr_2d[1]"), IsEqual("4")); |
| EXPECT_THAT(Eval("(*arr_2d)[1]"), IsEqual("2")); |
| EXPECT_THAT(Eval("**(arr_2d + 1)"), IsEqual("4")); |
| EXPECT_THAT(Eval("*(*(arr_2d + 1) + 1)"), IsEqual("5")); |
| } |
| |
| TEST_F(EvalTest, TestCStyleCastReference) { |
| EXPECT_THAT(Eval("((InnerFoo&)arr_1d[1]).a"), IsEqual("2")); |
| EXPECT_THAT(Eval("((InnerFoo&)arr_1d[1]).b"), IsEqual("3")); |
| |
| EXPECT_THAT(Eval("(int&)arr_1d[0]"), IsEqual("1")); |
| EXPECT_THAT(Eval("(int&)arr_1d[1]"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("(int&)0"), |
| IsError("C-style cast from rvalue to reference type 'int &'")); |
| EXPECT_THAT(Eval("&(int&)arr_1d"), IsOk()); |
| } |
| |
| TEST_F(EvalTest, TestCxxStaticCast) { |
| // Cast to scalars. |
| EXPECT_THAT(Eval("static_cast<int>(1.1)"), IsEqual("1")); |
| EXPECT_THAT(Eval("static_cast<double>(1)"), IsEqual("1")); |
| EXPECT_THAT(Eval("static_cast<char>(128)"), IsEqual("'\\x80'")); |
| EXPECT_THAT(Eval("static_cast<bool>(nullptr)"), IsEqual("false")); |
| EXPECT_THAT(Eval("static_cast<bool>((int*)0)"), IsEqual("false")); |
| EXPECT_THAT(Eval("static_cast<bool>(arr)"), IsEqual("true")); |
| EXPECT_THAT(Eval("static_cast<int>(u_enum)"), IsEqual("2")); |
| EXPECT_THAT(Eval("static_cast<float>(s_enum)"), IsEqual("1")); |
| EXPECT_THAT(Eval("static_cast<td_int_t>(5.3)"), IsEqual("5")); |
| EXPECT_THAT(Eval("static_cast<td_int_t>(4)"), IsEqual("4")); |
| EXPECT_THAT(Eval("static_cast<int>(td_int)"), IsEqual("13")); |
| |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("static_cast<long long>(nullptr)"), |
| IsError("static_cast from 'nullptr_t' to 'long long' is not allowed")); |
| #else |
| EXPECT_THAT(Eval("static_cast<long long>(nullptr)"), |
| IsError("static_cast from 'std::nullptr_t' to 'long long' is not " |
| "allowed")); |
| #endif |
| EXPECT_THAT( |
| Eval("static_cast<long long>(ptr)"), |
| IsError("static_cast from 'int *' to 'long long' is not allowed")); |
| EXPECT_THAT( |
| Eval("static_cast<long long>(arr)"), |
| IsError("static_cast from 'int *' to 'long long' is not allowed")); |
| EXPECT_THAT( |
| Eval("static_cast<int>(parent)"), |
| IsError( |
| "cannot convert 'CxxParent' to 'int' without a conversion operator")); |
| EXPECT_THAT( |
| Eval("static_cast<long long>(base)"), |
| IsError("static_cast from 'CxxBase *' to 'long long' is not allowed")); |
| |
| // Cast to enums. |
| EXPECT_THAT(Eval("static_cast<UEnum>(0)"), IsEqual("kUZero")); |
| EXPECT_THAT(Eval("static_cast<UEnum>(s_enum)"), IsEqual("kUOne")); |
| EXPECT_THAT(Eval("static_cast<UEnum>(2.1)"), IsEqual("kUTwo")); |
| EXPECT_THAT(Eval("static_cast<SEnum>(true)"), IsEqual("kSOne")); |
| EXPECT_THAT(Eval("static_cast<SEnum>(0.4f)"), IsEqual("kSZero")); |
| EXPECT_THAT(Eval("static_cast<SEnum>(td_senum)"), IsEqual("kSOne")); |
| EXPECT_THAT(Eval("static_cast<td_senum_t>(UEnum::kUOne)"), IsEqual("kSOne")); |
| |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("static_cast<UEnum>(nullptr)"), |
| IsError("static_cast from 'nullptr_t' to 'UEnum' is not allowed")); |
| #else |
| EXPECT_THAT( |
| Eval("static_cast<UEnum>(nullptr)"), |
| IsError("static_cast from 'std::nullptr_t' to 'UEnum' is not allowed")); |
| #endif |
| EXPECT_THAT(Eval("static_cast<UEnum>(ptr)"), |
| IsError("static_cast from 'int *' to 'UEnum' is not allowed")); |
| EXPECT_THAT( |
| Eval("static_cast<SEnum>(parent)"), |
| IsError("static_cast from 'CxxParent' to 'SEnum' is not allowed")); |
| |
| // Cast to pointers. |
| EXPECT_THAT(Eval("static_cast<char*>(0)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("static_cast<long*>(nullptr)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("*static_cast<int*>(arr)"), IsEqual("1")); |
| EXPECT_THAT(Eval("*static_cast<td_int_ptr_t>(arr)"), IsEqual("1")); |
| EXPECT_THAT(Eval("static_cast<void*>(ptr)"), IsOk()); |
| EXPECT_THAT(Eval("static_cast<int*>((void*)4)"), |
| IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); |
| EXPECT_THAT(Eval("static_cast<long*>(ptr)"), |
| IsError("static_cast from 'int *' to 'long *' is not allowed")); |
| EXPECT_THAT(Eval("static_cast<float*>(arr)"), |
| IsError("static_cast from 'int *' to 'float *' is not allowed")); |
| |
| // Cast to nullptr. |
| EXPECT_THAT(Eval("static_cast<std::nullptr_t>(nullptr)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("static_cast<std::nullptr_t>(0)"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Eval("static_cast<std::nullptr_t>((int)0)"), |
| IsError("static_cast from 'int' to 'std::nullptr_t' (aka " |
| "'nullptr_t') is not allowed")); |
| EXPECT_THAT(Eval("static_cast<std::nullptr_t>((void*)0)"), |
| IsError("static_cast from 'void *' to 'std::nullptr_t' (aka " |
| "'nullptr_t') is not allowed")); |
| #else |
| EXPECT_THAT( |
| Eval("static_cast<std::nullptr_t>((int)0)"), |
| IsError("static_cast from 'int' to 'std::nullptr_t' is not allowed")); |
| EXPECT_THAT( |
| Eval("static_cast<std::nullptr_t>((void*)0)"), |
| IsError("static_cast from 'void *' to 'std::nullptr_t' is not allowed")); |
| #endif |
| |
| // Cast to references. |
| EXPECT_THAT(Eval("static_cast<int&>(parent.b)"), IsEqual("2")); |
| EXPECT_THAT(Eval("&static_cast<int&>(parent.b)"), IsOk()); |
| EXPECT_THAT( |
| Eval("static_cast<int&>(parent.c)"), |
| IsError( |
| "static_cast from 'long long' to 'int &' is not implemented yet")); |
| EXPECT_THAT(Eval("static_cast<int&>(5)"), |
| IsError("static_cast from rvalue of type 'int' to reference type " |
| "'int &' is not implemented yet")); |
| |
| // Invalid expressions. |
| EXPECT_THAT(Eval("static_cast<1>(1)"), |
| IsError("type name requires a specifier or qualifier")); |
| EXPECT_THAT(Eval("static_cast<>(1)"), |
| IsError("type name requires a specifier or qualifier")); |
| EXPECT_THAT(Eval("static_cast<parent>(1)"), |
| IsError("unknown type name 'parent'")); |
| EXPECT_THAT(Eval("static_cast<T_1<int> CxxParent>(1)"), |
| IsError("two or more data types in declaration of 'type name'")); |
| } |
| |
| TEST_F(EvalTest, TestCastDerivedToBase) { |
| EXPECT_THAT(Eval("static_cast<CxxA*>(&a)->a"), IsEqual("1")); |
| EXPECT_THAT(Eval("static_cast<CxxA*>(&c)->a"), IsEqual("3")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&c)->b"), IsEqual("4")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&c)->c"), |
| IsError("no member named 'c' in 'CxxB'")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&e)->b"), IsEqual("8")); |
| EXPECT_THAT(Eval("static_cast<CxxC*>(&e)->a"), IsEqual("7")); |
| EXPECT_THAT(Eval("static_cast<CxxC*>(&e)->b"), IsEqual("8")); |
| EXPECT_THAT(Eval("static_cast<CxxC*>(&e)->c"), IsEqual("9")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&d)"), |
| IsError("static_cast from 'CxxD *' to 'CxxB *', which are not " |
| "related by inheritance, is not allowed")); |
| |
| // Cast via virtual inheritance. |
| EXPECT_THAT(Eval("static_cast<CxxA*>(&vc)->a"), IsEqual("12")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&vc)->b"), IsEqual("13")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&vc)->c"), |
| IsError("no member named 'c' in 'CxxB'")); |
| EXPECT_THAT(Eval("static_cast<CxxB*>(&ve)->b"), IsEqual("16")); |
| EXPECT_THAT(Eval("static_cast<CxxC*>(&ve)"), |
| IsError("static_cast from 'CxxVE *' to 'CxxC *', which are not " |
| "related by inheritance, is not allowed")); |
| |
| // Same with references. |
| EXPECT_THAT(Eval("static_cast<CxxA&>(a).a"), IsEqual("1")); |
| EXPECT_THAT(Eval("static_cast<CxxA&>(c).a"), IsEqual("3")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(c).b"), IsEqual("4")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(c).c"), |
| IsError("no member named 'c' in 'CxxB'")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(e).b"), IsEqual("8")); |
| EXPECT_THAT(Eval("static_cast<CxxC&>(e).a"), IsEqual("7")); |
| EXPECT_THAT(Eval("static_cast<CxxC&>(e).b"), IsEqual("8")); |
| EXPECT_THAT(Eval("static_cast<CxxC&>(e).c"), IsEqual("9")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(d)"), |
| IsError("static_cast from 'CxxD' to 'CxxB &', which are not " |
| "related by inheritance, is not allowed")); |
| |
| EXPECT_THAT(Eval("static_cast<CxxA&>(vc).a"), IsEqual("12")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(vc).b"), IsEqual("13")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(vc).c"), |
| IsError("no member named 'c' in 'CxxB'")); |
| EXPECT_THAT(Eval("static_cast<CxxB&>(ve).b"), IsEqual("16")); |
| EXPECT_THAT(Eval("static_cast<CxxC&>(ve)"), |
| IsError("static_cast from 'CxxVE' to 'CxxC &', which are not " |
| "related by inheritance, is not allowed")); |
| } |
| |
| TEST_F(EvalTest, TestCastBaseToDerived) { |
| EXPECT_THAT(Eval("static_cast<CxxE*>(e_as_b)->a"), IsEqual("7")); |
| EXPECT_THAT(Eval("static_cast<CxxE*>(e_as_b)->b"), IsEqual("8")); |
| EXPECT_THAT(Eval("static_cast<CxxE*>(e_as_b)->c"), IsEqual("9")); |
| EXPECT_THAT(Eval("static_cast<CxxE*>(e_as_b)->d"), IsEqual("10")); |
| EXPECT_THAT(Eval("static_cast<CxxE*>(e_as_b)->e"), IsEqual("11")); |
| |
| // Same with references. |
| EXPECT_THAT(Eval("static_cast<CxxE&>(*e_as_b).a"), IsEqual("7")); |
| EXPECT_THAT(Eval("static_cast<CxxE&>(*e_as_b).b"), IsEqual("8")); |
| EXPECT_THAT(Eval("static_cast<CxxE&>(*e_as_b).c"), IsEqual("9")); |
| EXPECT_THAT(Eval("static_cast<CxxE&>(*e_as_b).d"), IsEqual("10")); |
| EXPECT_THAT(Eval("static_cast<CxxE&>(*e_as_b).e"), IsEqual("11")); |
| |
| // Base-to-derived conversion isn't possible for virtually inhertied types. |
| EXPECT_THAT( |
| Eval("static_cast<CxxVE*>(ve_as_b)"), |
| IsError("cannot cast 'CxxB *' to 'CxxVE *' via virtual base 'CxxB'")); |
| EXPECT_THAT( |
| Eval("static_cast<CxxVE&>(*ve_as_b)"), |
| IsError("cannot cast 'CxxB' to 'CxxVE &' via virtual base 'CxxB'")); |
| } |
| |
| TEST_F(EvalTest, TestCxxDynamicCast) { |
| // LLDB doesn't support `dynamic_cast` in the expression evaluator. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("dynamic_cast<int>(0)"), |
| IsError("invalid target type 'int' for dynamic_cast")); |
| EXPECT_THAT(Eval("dynamic_cast<int*>(0)"), |
| IsError("'int' is not a class type")); |
| EXPECT_THAT( |
| Eval("dynamic_cast<CxxBase*>(1.1)"), |
| IsError( |
| "cannot use dynamic_cast to convert from 'double' to 'CxxBase *'")); |
| EXPECT_THAT(Eval("dynamic_cast<CxxBase*>((int*)0)"), |
| IsError("'int' is not a class type")); |
| EXPECT_THAT(Eval("dynamic_cast<CxxVirtualParent*>(base)"), |
| IsError("'CxxBase' is not polymorphic")); |
| EXPECT_THAT(Eval("dynamic_cast<CxxVirtualParent*>(v_base)"), |
| IsError("dynamic_cast is not supported in this context")); |
| } |
| |
| TEST_F(EvalTest, TestCxxReinterpretCast) { |
| // Integers and enums can be converted to its own type. |
| EXPECT_THAT(Eval("reinterpret_cast<bool>(true)"), IsEqual("true")); |
| EXPECT_THAT(Eval("reinterpret_cast<int>(5)"), IsEqual("5")); |
| EXPECT_THAT(Eval("reinterpret_cast<td_int_t>(6)"), IsEqual("6")); |
| EXPECT_THAT(Eval("reinterpret_cast<int>(td_int)"), IsEqual("13")); |
| EXPECT_THAT(Eval("reinterpret_cast<long long>(100LL)"), IsEqual("100")); |
| EXPECT_THAT(Eval("reinterpret_cast<UEnum>(u_enum)"), IsEqual("kUTwo")); |
| EXPECT_THAT(Eval("reinterpret_cast<SEnum>(s_enum)"), IsEqual("kSOne")); |
| EXPECT_THAT(Eval("reinterpret_cast<td_senum_t>(s_enum)"), IsEqual("kSOne")); |
| // Other scalar/enum to scalar/enum casts aren't allowed. |
| EXPECT_THAT( |
| Eval("reinterpret_cast<int>(5U)"), |
| IsError("reinterpret_cast from 'unsigned int' to 'int' is not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<int>(3.14f)"), |
| IsError("reinterpret_cast from 'float' to 'int' is not allowed")); |
| EXPECT_THAT( |
| Eval("reinterpret_cast<double>(2.71)"), |
| IsError("reinterpret_cast from 'double' to 'double' is not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<int>(s_enum)"), |
| IsError("reinterpret_cast from 'SEnum' to 'int' is not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<UEnum>(0)"), |
| IsError("reinterpret_cast from 'int' to 'UEnum' is not allowed")); |
| EXPECT_THAT( |
| Eval("reinterpret_cast<UEnum>(s_enum)"), |
| IsError("reinterpret_cast from 'SEnum' to 'UEnum' is not allowed")); |
| |
| // Pointers should be convertible to large enough integral types. |
| EXPECT_THAT(Eval("reinterpret_cast<long long>(ptr)"), IsOk()); |
| EXPECT_THAT(Eval("reinterpret_cast<long long>(arr)"), IsOk()); |
| EXPECT_THAT(Eval("reinterpret_cast<long long>(nullptr)"), IsEqual("0")); |
| if (Is32Bit()) { |
| EXPECT_THAT(Eval("reinterpret_cast<int>(ptr)"), IsOk()); |
| EXPECT_THAT(Eval("reinterpret_cast<td_int_t>(ptr)"), IsOk()); |
| } else { |
| EXPECT_THAT( |
| Eval("reinterpret_cast<int>(ptr)"), |
| IsError("cast from pointer to smaller type 'int' loses information")); |
| EXPECT_THAT(Eval("reinterpret_cast<td_int_t>(ptr)"), |
| IsError("cast from pointer to smaller type 'td_int_t' (aka " |
| "'int') loses information")); |
| } |
| EXPECT_THAT( |
| Eval("reinterpret_cast<bool>(arr)"), |
| IsError("cast from pointer to smaller type 'bool' loses information")); |
| EXPECT_THAT( |
| Eval("reinterpret_cast<bool>(nullptr)"), |
| IsError("cast from pointer to smaller type 'bool' loses information")); |
| #ifdef _WIN32 |
| EXPECT_THAT( |
| Eval("reinterpret_cast<long>(ptr)"), |
| IsError("cast from pointer to smaller type 'long' loses information")); |
| #else |
| EXPECT_THAT(Eval("reinterpret_cast<long>(ptr)"), IsOk()); |
| #endif |
| |
| // Integers, enums and pointers can be converted to pointers. |
| EXPECT_THAT(Eval("reinterpret_cast<int*>(true)"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| EXPECT_THAT(Eval("reinterpret_cast<float*>(6)"), |
| IsEqual(Is32Bit() ? "0x00000006" : "0x0000000000000006")); |
| EXPECT_THAT(Eval("reinterpret_cast<void*>(s_enum)"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| EXPECT_THAT(Eval("reinterpret_cast<CxxBase*>(u_enum)"), |
| IsEqual(Is32Bit() ? "0x00000002" : "0x0000000000000002")); |
| EXPECT_THAT(Eval("reinterpret_cast<td_int_ptr_t>(ptr)"), IsOk()); |
| EXPECT_THAT(Eval("*reinterpret_cast<UEnum**>(ptr)"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000200000001")); |
| EXPECT_THAT(Eval("*reinterpret_cast<int*>(arr)"), IsEqual("1")); |
| EXPECT_THAT(Eval("*reinterpret_cast<long long*>(arr)"), |
| IsEqual("8589934593")); // 8589934593 == 0x0000000200000001 |
| |
| // Casting to nullptr_t or nullptr_t to pointer types isn't allowed. |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT( |
| Eval("reinterpret_cast<void*>(nullptr)"), |
| IsError("reinterpret_cast from 'nullptr_t' to 'void *' is not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<std::nullptr_t>(ptr)"), |
| IsError("reinterpret_cast from 'int *' to 'std::nullptr_t' (aka " |
| "'nullptr_t') is not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<std::nullptr_t>(0)"), |
| IsError("reinterpret_cast from 'int' to 'std::nullptr_t' (aka " |
| "'nullptr_t') is not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<std::nullptr_t>(nullptr)"), |
| IsError("reinterpret_cast from 'nullptr_t' to 'std::nullptr_t' " |
| "(aka 'nullptr_t') is not allowed")); |
| #else |
| EXPECT_THAT( |
| Eval("reinterpret_cast<void*>(nullptr)"), |
| IsError("reinterpret_cast from 'std::nullptr_t' to 'void *' is not " |
| "allowed")); |
| EXPECT_THAT( |
| Eval("reinterpret_cast<std::nullptr_t>(ptr)"), |
| IsError("reinterpret_cast from 'int *' to 'std::nullptr_t' is not " |
| "allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<std::nullptr_t>(0)"), |
| IsError("reinterpret_cast from 'int' to 'std::nullptr_t' is " |
| "not allowed")); |
| EXPECT_THAT(Eval("reinterpret_cast<std::nullptr_t>(nullptr)"), |
| IsError("reinterpret_cast from 'std::nullptr_t' to " |
| "'std::nullptr_t' " |
| "is not allowed")); |
| #endif |
| |
| // L-values can be converted to reference type. |
| EXPECT_THAT(Eval("reinterpret_cast<CxxBase&>(arr[0]).a"), IsEqual("1")); |
| EXPECT_THAT(Eval("reinterpret_cast<CxxBase&>(arr).b"), IsEqual("2")); |
| EXPECT_THAT(Eval("reinterpret_cast<CxxParent&>(arr[0]).c"), |
| IsEqual("17179869187")); // 17179869187 == 0x0000000400000003 |
| EXPECT_THAT(Eval("reinterpret_cast<CxxParent&>(arr).d"), IsEqual("5")); |
| EXPECT_THAT(Eval("reinterpret_cast<int&>(parent)"), IsOk()); |
| EXPECT_THAT(Eval("reinterpret_cast<td_int_ref_t>(ptr)"), IsOk()); |
| EXPECT_THAT( |
| Eval("reinterpret_cast<int&>(5)"), |
| IsError("reinterpret_cast from rvalue to reference type 'int &'")); |
| |
| // Is result L-value or R-value? |
| EXPECT_THAT(Eval("&reinterpret_cast<int&>(arr[0])"), IsOk()); |
| EXPECT_THAT(Eval("&reinterpret_cast<int>(arr[0])"), |
| IsError("cannot take the address of an rvalue of type 'int'")); |
| EXPECT_THAT(Eval("&reinterpret_cast<UEnum>(u_enum)"), |
| IsError("cannot take the address of an rvalue of type 'UEnum'")); |
| EXPECT_THAT(Eval("&reinterpret_cast<int*>(arr)"), |
| IsError("cannot take the address of an rvalue of type 'int *'")); |
| } |
| |
| TEST_F(EvalTest, TestQualifiedId) { |
| EXPECT_THAT(Eval("::ns::i"), IsEqual("1")); |
| EXPECT_THAT(Eval("ns::i"), IsEqual("1")); |
| EXPECT_THAT(Eval("::ns::ns::i"), IsEqual("2")); |
| EXPECT_THAT(Eval("ns::ns::i"), IsEqual("2")); |
| } |
| |
| // This test depends on one of the following patches: |
| // * https://reviews.llvm.org/D92223 |
| // * https://reviews.llvm.org/D92643 |
| TEST_F(EvalTest, DISABLED_TestStaticConstDeclaredInline) { |
| // Upstream LLDB doesn't handle static const variables. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("::outer::inner::Vars::inline_static"), IsEqual("1.5")); |
| EXPECT_THAT(Eval("::outer::inner::Vars::static_constexpr"), IsEqual("2")); |
| EXPECT_THAT(Eval("outer::inner::Vars::inline_static"), IsEqual("1.5")); |
| EXPECT_THAT(Eval("outer::inner::Vars::static_constexpr"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("::outer::Vars::inline_static"), IsEqual("4.5")); |
| EXPECT_THAT(Eval("::outer::Vars::static_constexpr"), IsEqual("5")); |
| EXPECT_THAT(Eval("outer::Vars::inline_static"), IsEqual("4.5")); |
| EXPECT_THAT(Eval("outer::Vars::static_constexpr"), IsEqual("5")); |
| |
| EXPECT_THAT(Eval("::Vars::inline_static"), IsEqual("7.5")); |
| EXPECT_THAT(Eval("::Vars::static_constexpr"), IsEqual("8")); |
| EXPECT_THAT(Eval("Vars::inline_static"), IsEqual("7.5")); |
| EXPECT_THAT(Eval("Vars::static_constexpr"), IsEqual("8")); |
| |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Scope("outer_inner_vars").Eval("inline_static"), IsEqual("1.5")); |
| EXPECT_THAT(Scope("outer_inner_vars").Eval("static_constexpr"), IsEqual("2")); |
| EXPECT_THAT(Scope("outer_vars").Eval("inline_static"), IsEqual("4.5")); |
| EXPECT_THAT(Scope("outer_vars").Eval("static_constexpr"), IsEqual("5")); |
| EXPECT_THAT(Scope("vars").Eval("inline_static"), IsEqual("7.5")); |
| EXPECT_THAT(Scope("vars").Eval("inline_static"), IsEqual("8")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestStaticConstDeclaredOutsideTheClass) { |
| #if LLVM_VERSION_MAJOR < 12 |
| // Upstream LLDB doesn't handle static const variables. |
| this->compare_with_lldb_ = false; |
| #endif |
| |
| EXPECT_THAT(Eval("::outer::inner::Vars::static_const"), IsEqual("3")); |
| EXPECT_THAT(Eval("outer::inner::Vars::static_const"), IsEqual("3")); |
| EXPECT_THAT(Eval("::outer::Vars::static_const"), IsEqual("6")); |
| EXPECT_THAT(Eval("outer::Vars::static_const"), IsEqual("6")); |
| EXPECT_THAT(Eval("::Vars::static_const"), IsEqual("9")); |
| EXPECT_THAT(Eval("Vars::static_const"), IsEqual("9")); |
| |
| EXPECT_THAT(Eval("::outer::inner::Vars::Nested::static_const"), |
| IsEqual("10")); |
| EXPECT_THAT(Eval("outer::inner::Vars::Nested::static_const"), IsEqual("10")); |
| EXPECT_THAT(Eval("::outer::Vars::Nested::static_const"), IsEqual("20")); |
| EXPECT_THAT(Eval("outer::Vars::Nested::static_const"), IsEqual("20")); |
| EXPECT_THAT(Eval("::Vars::Nested::static_const"), IsEqual("30")); |
| EXPECT_THAT(Eval("Vars::Nested::static_const"), IsEqual("30")); |
| |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Scope("outer_inner_vars").Eval("static_const"), IsEqual("3")); |
| EXPECT_THAT(Scope("outer_vars").Eval("static_const"), IsEqual("6")); |
| EXPECT_THAT(Scope("vars").Eval("static_const"), IsEqual("9")); |
| |
| EXPECT_THAT(Scope("outer_inner_vars").Eval("Nested::static_const"), |
| IsEqual("10")); |
| EXPECT_THAT(Scope("outer_vars").Eval("Nested::static_const"), IsEqual("20")); |
| EXPECT_THAT(Scope("vars").Eval("Nested::static_const"), IsEqual("30")); |
| |
| EXPECT_THAT(Scope("vars").Eval("::static_const"), |
| IsError("use of undeclared identifier '::static_const'")); |
| EXPECT_THAT(Scope("vars").Eval("::Nested::static_const"), |
| IsError("use of undeclared identifier '::Nested::static_const'")); |
| |
| // Evaluate in value context where value is of alised type. |
| EXPECT_THAT(Scope("my_outer_inner_vars").Eval("static_const"), IsEqual("3")); |
| EXPECT_THAT(Scope("my_outer_vars").Eval("static_const"), IsEqual("6")); |
| EXPECT_THAT(Scope("my_vars").Eval("static_const"), IsEqual("9")); |
| |
| EXPECT_THAT(Scope("my_outer_inner_vars").Eval("Nested::static_const"), |
| IsEqual("10")); |
| EXPECT_THAT(Scope("my_outer_vars").Eval("Nested::static_const"), |
| IsEqual("20")); |
| EXPECT_THAT(Scope("my_vars").Eval("Nested::static_const"), IsEqual("30")); |
| |
| EXPECT_THAT(Scope("my_outer_inner_vars").Eval("::static_const"), |
| IsError("use of undeclared identifier '::static_const'")); |
| EXPECT_THAT(Scope("my_vars").Eval("::Nested::static_const"), |
| IsError("use of undeclared identifier '::Nested::static_const'")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestBasicTypeDeclaration) { |
| EXPECT_THAT(Eval("(char)65"), IsEqual("'A'")); |
| EXPECT_THAT(Eval("(char unsigned)65"), IsEqual("'A'")); |
| EXPECT_THAT(Eval("(signed char)65"), IsEqual("'A'")); |
| #ifndef __EMSCRIPTEN__ |
| if constexpr (sizeof(wchar_t) == 2) { |
| // Size of "wchar_t" is 2 bytes on Windows. |
| EXPECT_THAT(Eval("(wchar_t)0x4141"), IsEqual("AA")); |
| } else { |
| // Size of "wchar_t" is 4 bytes on Linux. |
| EXPECT_THAT(Eval("(wchar_t)0x41414141"), IsEqual("AAAA")); |
| } |
| EXPECT_THAT(Eval("(char16_t)0x4141"), IsEqual("U+4141")); |
| EXPECT_THAT(Eval("(char32_t)0x4141"), IsEqual("U+0x00004141")); |
| #endif |
| EXPECT_THAT(Eval("(int short)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(short int)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(short)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(unsigned short)-1"), IsEqual("65535")); |
| EXPECT_THAT(Eval("(short unsigned)-1"), IsEqual("65535")); |
| EXPECT_THAT(Eval("(int short unsigned)-1"), IsEqual("65535")); |
| EXPECT_THAT(Eval("(int)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(signed int)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(signed)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(unsigned)-1"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("(int unsigned)-1"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("(long)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(signed long)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(long int signed)-1"), IsEqual("-1")); |
| if constexpr (sizeof(long) == 4) { |
| // Size of "long" is 4 bytes on Windows. |
| EXPECT_THAT(Eval("(unsigned long)-1"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("(int long unsigned)-1"), IsEqual("4294967295")); |
| } else { |
| // Size of "long" is 8 bytes on Linux. |
| EXPECT_THAT(Eval("(unsigned long)-1"), IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("(int long unsigned)-1"), IsEqual("18446744073709551615")); |
| } |
| EXPECT_THAT(Eval("(long long)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(long long int)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(int signed long long)-1"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(long int long unsigned)-1"), |
| IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("(int long unsigned long)-1"), |
| IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("(unsigned long long)-1"), IsEqual("18446744073709551615")); |
| |
| EXPECT_THAT(Eval("(float)1.5"), IsEqual("1.5")); |
| EXPECT_THAT(Eval("(double)1.5"), IsEqual("1.5")); |
| #ifdef _WIN32 |
| // Casting to "long double" results in "NaN" on Linux. |
| EXPECT_THAT(Eval("(long double)1.5"), IsEqual("1.5")); |
| EXPECT_THAT(Eval("(double long)1.5"), IsEqual("1.5")); |
| #endif |
| EXPECT_THAT(Eval("(bool)1.5"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("(void*)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("(unsigned**)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| |
| EXPECT_THAT( |
| Eval("(int int)0"), |
| IsError("cannot combine with previous 'int' declaration specifier\n" |
| "(int int)0\n" |
| " ^")); |
| EXPECT_THAT( |
| Eval("(char int)0"), |
| IsError("cannot combine with previous 'char' declaration specifier")); |
| EXPECT_THAT( |
| Eval("(int char)0"), |
| IsError("cannot combine with previous 'int' declaration specifier")); |
| EXPECT_THAT( |
| Eval("(long long long)0"), |
| IsError( |
| "cannot combine with previous 'long long' declaration specifier")); |
| EXPECT_THAT( |
| Eval("(long long double)0"), |
| IsError( |
| "cannot combine with previous 'long long' declaration specifier")); |
| EXPECT_THAT( |
| Eval("(long double long)0"), |
| IsError( |
| "cannot combine with previous 'long double' declaration specifier")); |
| EXPECT_THAT(Eval("(unsigned long double)0"), |
| IsError("'double' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(long double signed)0"), |
| IsError("'long double' cannot be signed or unsigned")); |
| EXPECT_THAT( |
| Eval("(short float)0"), |
| IsError("cannot combine with previous 'short' declaration specifier")); |
| EXPECT_THAT( |
| Eval("(unsigned signed)0"), |
| IsError("cannot combine with previous 'unsigned' declaration specifier")); |
| EXPECT_THAT( |
| Eval("(unsigned unsigned)0"), |
| IsError("cannot combine with previous 'unsigned' declaration specifier")); |
| EXPECT_THAT(Eval("(signed wchar_t)0"), |
| IsError("'wchar_t' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(signed char16_t)0"), |
| IsError("'char16_t' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(signed char32_t)0"), |
| IsError("'char32_t' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(unsigned float)0"), |
| IsError("'float' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(unsigned double)0"), |
| IsError("'double' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(unsigned bool)0"), |
| IsError("'bool' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(unsigned void)0"), |
| IsError("'void' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(bool unsigned)0"), |
| IsError("'bool' cannot be signed or unsigned")); |
| EXPECT_THAT(Eval("(bool signed)0"), |
| IsError("'bool' cannot be signed or unsigned")); |
| |
| // Error reporting works with other kinds of expression. |
| EXPECT_THAT( |
| Eval("static_cast<int int>(0)"), |
| IsError("cannot combine with previous 'int' declaration specifier")); |
| EXPECT_THAT( |
| Eval("sizeof(int int)"), |
| IsError("cannot combine with previous 'int' declaration specifier")); |
| } |
| |
| TEST_F(EvalTest, TestUserTypeDeclaration) { |
| EXPECT_THAT(Eval("(unsigned mylong)0"), |
| IsError("cannot combine with previous declaration specifier")); |
| EXPECT_THAT(Eval("static_cast<mylong mylong>(0)"), |
| IsError("two or more data types in declaration of 'type name'")); |
| EXPECT_THAT(Eval("static_cast<mylong unsigned>(0)"), |
| IsError("cannot combine with previous declaration specifier")); |
| // TODO: Should this be a type name error instead? |
| EXPECT_THAT(Eval("(mylong unsigned)0"), |
| IsError("undeclared identifier 'mylong'")); |
| } |
| |
| TEST_F(EvalTest, TestTemplateTypes) { |
| // Template types lookup doesn't work well in the upstream LLDB. |
| this->compare_with_lldb_ = false; |
| #ifndef __EMSCRIPTEN__ |
| // Get the pointer value and use it to check the expressions with lldb-eval. |
| auto expected = frame_.EvaluateExpression("p").GetValue(); |
| |
| for (std::string arg : {"int", "int*", "int**", "int&", "int*&", "double"}) { |
| EXPECT_THAT(Eval("(T_1<" + arg + ">*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::T_1<" + arg + ">*)p"), IsEqual(expected)); |
| } |
| EXPECT_THAT(Eval("(T_2<int, char>*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::T_2<int, char>*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(T_2<char, int>*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::T_2<char, int>*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(T_2<T_1<int>, T_1<char> >*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::T_2<T_1<int>, T_1<char> >*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(T_2<T_1<T_1<int> >, T_1<char> >*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::T_2<T_1<T_1<int> >, T_1<char> >*)p"), IsEqual(expected)); |
| |
| EXPECT_THAT(Eval("(ns::T_1<int>*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::ns::T_1<int>*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(ns::T_1<ns::T_1<int> >*)p"), IsEqual(expected)); |
| EXPECT_THAT(Eval("(::ns::T_1<ns::T_1<int> >*)p"), IsEqual(expected)); |
| #endif |
| #ifdef _WIN32 |
| EXPECT_THAT( |
| Eval("ns::T_1<ns::T_1<int> >::cx"), |
| IsError("use of undeclared identifier 'ns::T_1<ns::T_1<int> >::cx'")); |
| #else |
| EXPECT_THAT(Eval("ns::T_1<ns::T_1<int> >::cx"), IsEqual("46")); |
| #endif |
| |
| EXPECT_THAT(Eval("T_1<int>::cx"), IsEqual("24")); |
| EXPECT_THAT(Eval("T_1<double>::cx"), IsEqual("42")); |
| EXPECT_THAT(Eval("ns::T_1<int>::cx"), IsEqual("64")); |
| |
| for (std::string arg : {"int", "int*", "int**", "int&", "int*&"}) { |
| EXPECT_THAT(Eval("(T_1<" + arg + ">::myint)1.2"), IsEqual("1.2")); |
| EXPECT_THAT(Eval("(::T_1<" + arg + ">::myint)1.2"), IsEqual("1.2")); |
| EXPECT_THAT(Eval("(T_1<T_1<" + arg + "> >::myint)1.2"), IsEqual("1.2")); |
| EXPECT_THAT(Eval("(::T_1<T_1<" + arg + "> >::myint)1.2"), IsEqual("1.2")); |
| |
| EXPECT_THAT(Eval("(ns::T_1<" + arg + ">::myint)1.1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::T_1<" + arg + ">::myint)1.1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(ns::T_1<T_1<" + arg + "> >::myint)1.1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::T_1<T_1<" + arg + "> >::myint)1.1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(ns::T_1<ns::T_1<" + arg + "> >::myint)1.1"), |
| IsEqual("1")); |
| EXPECT_THAT(Eval("(::ns::T_1<ns::T_1<" + arg + "> >::myint)1.1"), |
| IsEqual("1")); |
| } |
| |
| EXPECT_THAT(Eval("(T_2<int, char>::myint)1.1f"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(::T_2<int, char>::myint)1.1f"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(T_2<int*, char&>::myint)1.1f"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(::T_2<int&, char*>::myint)1.1f"), IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(T_2<T_1<T_1<int> >, T_1<char> >::myint)1.1"), |
| IsEqual("1.10000002")); |
| EXPECT_THAT(Eval("(::T_2<T_1<T_1<int> >, T_1<char> >::myint)1.1"), |
| IsEqual("1.10000002")); |
| } |
| |
| TEST_F(EvalTest, TestTemplateCpp11) { |
| // Template types lookup doesn't work well in the upstream LLDB. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("(T_1<T_1<int>>::myint)1"), IsEqual("1")); |
| EXPECT_THAT(Eval("(T_1<T_1<T_1<int>>>::myint)2"), IsEqual("2")); |
| EXPECT_THAT(Eval("(T_2<T_1<T_1<int>>, T_1<char>>::myint)1.5"), |
| IsEqual("1.5")); |
| |
| // Here T_1 is a local variable. |
| EXPECT_THAT(Eval("T_1<2>1"), IsEqual("false")); // (p < 2) > 1 |
| EXPECT_THAT(Eval("T_1<2>>1"), IsEqual("false")); // (p < 2) >> 1 |
| // And here it's a template. |
| EXPECT_THAT(Eval("T_1<int>::cx + 1"), IsEqual("25")); |
| } |
| |
| TEST_F(EvalTest, TestTemplateWithNumericArguments) { |
| // Template types lookup doesn't work well in the upstream LLDB. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("(Allocator<4>*)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("(TArray<int, Allocator<4> >::ElementType*)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| // Test C++11's ">>" syntax. |
| EXPECT_THAT(Eval("(TArray<int, Allocator<4>>::ElementType*)0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| TEST_F(EvalTest, TestValueScope) { |
| EXPECT_THAT(Scope("var").Eval("x_"), IsEqual("1")); |
| EXPECT_THAT(Scope("var").Eval("y_"), IsEqual("2.5")); |
| EXPECT_THAT(Scope("var").Eval("z_"), |
| IsError("use of undeclared identifier 'z_'")); |
| |
| // In "value" scope `this` refers to the scope object. |
| EXPECT_THAT(Scope("var").Eval("this->y_"), IsEqual("2.5")); |
| EXPECT_THAT(Scope("var").Eval("(*this).y_"), IsEqual("2.5")); |
| |
| // Test for the "artificial" value, i.e. created by the expression. |
| lldb::SBError error; |
| lldb::SBValue scope_var = |
| lldb_eval::EvaluateExpression(frame_, "(test_scope::Value&)bytes", error); |
| EXPECT_TRUE(scope_var.IsValid()); |
| EXPECT_TRUE(error.Success()); |
| |
| EvaluatorHelper scope(scope_var, true, false); |
| EXPECT_THAT(scope.Eval("this->y_"), IsEqual("2.5")); |
| EXPECT_THAT(scope.Eval("(*this).y_"), IsEqual("2.5")); |
| |
| EXPECT_THAT(Eval("x_"), IsError("use of undeclared identifier 'x_'")); |
| EXPECT_THAT(Eval("y_"), IsError("use of undeclared identifier 'y_'")); |
| EXPECT_THAT(Eval("z_"), IsEqual("3")); |
| |
| // In the frame context `this` is not available here. |
| EXPECT_THAT( |
| Eval("this->y_"), |
| IsError("invalid use of 'this' outside of a non-static member function")); |
| EXPECT_THAT( |
| Eval("(*this)->y_"), |
| IsError("invalid use of 'this' outside of a non-static member function")); |
| |
| EXPECT_THAT(Scope("var").Eval("this - (test_scope::Value*)this"), |
| IsEqual("0")); |
| } |
| |
| TEST_F(EvalTest, TestReferenceScope) { |
| // Member access in "reference" context doesn't work in LLDB. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Scope("var_ref").Eval("x_"), IsEqual("1")); |
| EXPECT_THAT(Scope("var_ref").Eval("y_"), IsEqual("2.5")); |
| EXPECT_THAT(Scope("var_ref").Eval("z_"), |
| IsError("use of undeclared identifier 'z_'")); |
| EXPECT_THAT(Scope("var_ref").Eval("this"), IsOk()); |
| EXPECT_THAT(Scope("var_ref").Eval("this->y_"), IsEqual("2.5")); |
| EXPECT_THAT(Scope("var_ref").Eval("(*this).y_"), IsEqual("2.5")); |
| EXPECT_THAT(Scope("var_ref").Eval("ValueEnum::B"), IsEqual("B")); |
| EXPECT_THAT(Scope("var_ref").Eval("static_var"), IsEqual("3.5")); |
| EXPECT_THAT(Scope("var_ref").Eval("this->static_var"), |
| IsError("no member named 'static_var' in 'test_scope::Value'")); |
| } |
| #endif |
| |
| TEST_F(EvalTest, TestBitField) { |
| EXPECT_THAT(Eval("bf.a"), IsEqual("1023")); |
| EXPECT_THAT(Eval("bf.b"), IsEqual("9")); |
| EXPECT_THAT(Eval("bf.c"), IsEqual("false")); |
| EXPECT_THAT(Eval("bf.d"), IsEqual("true")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Scope("bf").Eval("a"), IsEqual("1023")); |
| EXPECT_THAT(Scope("bf").Eval("b"), IsEqual("9")); |
| EXPECT_THAT(Scope("bf").Eval("c"), IsEqual("false")); |
| EXPECT_THAT(Scope("bf").Eval("d"), IsEqual("true")); |
| #endif |
| |
| // Perform an operation to ensure we actually read the value. |
| EXPECT_THAT(Eval("0 + bf.a"), IsEqual("1023")); |
| EXPECT_THAT(Eval("0 + bf.b"), IsEqual("9")); |
| EXPECT_THAT(Eval("0 + bf.c"), IsEqual("0")); |
| EXPECT_THAT(Eval("0 + bf.d"), IsEqual("1")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Scope("bf").Eval("0 + a"), IsEqual("1023")); |
| // TODO: Enable type comparison after fixing bitfield promotion in value |
| // context. |
| EXPECT_THAT(Scope("bf").Eval("0 + b"), IsEqual("9", /*compare_types*/ false)); |
| EXPECT_THAT(Scope("bf").Eval("0 + c"), IsEqual("0")); |
| EXPECT_THAT(Scope("bf").Eval("0 + d"), IsEqual("1")); |
| #endif |
| |
| EXPECT_THAT(Eval("abf.a"), IsEqual("1023")); |
| EXPECT_THAT(Eval("abf.b"), IsEqual("'\\x0f'")); |
| EXPECT_THAT(Eval("abf.c"), IsEqual("3")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Scope("abf").Eval("a"), IsEqual("1023")); |
| EXPECT_THAT(Scope("abf").Eval("b"), IsEqual("'\\x0f'")); |
| EXPECT_THAT(Scope("abf").Eval("c"), IsEqual("3")); |
| #endif |
| |
| // Perform an operation to ensure we actually read the value. |
| EXPECT_THAT(Eval("abf.a + 0"), IsEqual("1023")); |
| EXPECT_THAT(Eval("abf.b + 0"), IsEqual("15")); |
| EXPECT_THAT(Eval("abf.c + 0"), IsEqual("3")); |
| #ifndef __EMSCRIPTEN__ |
| EXPECT_THAT(Scope("abf").Eval("0 + a"), IsEqual("1023")); |
| EXPECT_THAT(Scope("abf").Eval("0 + b"), IsEqual("15")); |
| EXPECT_THAT(Scope("abf").Eval("0 + c"), IsEqual("3")); |
| #endif |
| |
| // Address-of is not allowed for bit-fields. |
| EXPECT_THAT(Eval("&bf.a"), IsError("address of bit-field requested")); |
| EXPECT_THAT(Eval("&(true ? bf.a : bf.a)"), |
| IsError("address of bit-field requested")); |
| } |
| |
| TEST_F(EvalTest, TestBitFieldPromotion) { |
| EXPECT_THAT(Eval("bf.b - 10"), IsEqual("-1")); |
| EXPECT_THAT(Eval("bf.e - 2"), IsEqual("-1")); |
| EXPECT_THAT(Eval("bf.f - 2"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("bf.g - 2"), IsEqual("-1")); |
| EXPECT_THAT(Eval("bf.h - 2"), IsEqual("-1")); |
| EXPECT_THAT(Eval("bf.i - 2"), IsEqual("18446744073709551615")); |
| EXPECT_THAT(Eval("bf.g - bf.b"), IsEqual("-8")); |
| |
| EXPECT_THAT(Eval("-(true ? bf.b : bf.a)"), IsEqual("-9")); |
| EXPECT_THAT(Eval("-(true ? bf.b : bf.e)"), IsEqual("-9")); |
| EXPECT_THAT(Eval("-(true ? bf.b : bf.f)"), IsEqual("4294967287")); |
| EXPECT_THAT(Eval("-(true ? bf.b : bf.g)"), IsEqual("4294967287")); |
| EXPECT_THAT(Eval("-(true ? bf.b : bf.h)"), IsEqual("-9")); |
| |
| if (HAS_METHOD(lldb::SBType, GetEnumerationIntegerType())) { |
| EXPECT_THAT(Eval("bf.j - 2"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("-(true ? bf.b : bf.j)"), IsEqual("4294967287")); |
| EXPECT_THAT(Eval("-(true ? bf.e : bf.j)"), IsEqual("4294967295")); |
| } |
| |
| // TODO: Repeat tests in the value context once the bitfield information is |
| // correctly retrieved from identifier lookup. |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| TEST_F(EvalTest, TestBitFieldWithSideEffects) { |
| this->compare_with_lldb_ = false; |
| this->allow_side_effects_ = true; |
| |
| EXPECT_THAT(Eval("bf.b -= 10"), IsEqual("15")); |
| EXPECT_THAT(Eval("bf.e -= 10"), IsEqual("-9")); |
| EXPECT_THAT(Eval("bf.e++"), IsEqual("-9")); |
| EXPECT_THAT(Eval("++bf.e"), IsEqual("-7")); |
| |
| // TODO: Enable test once the issue is fixed: |
| // EXPECT_THAT(Eval("bf.b++"), IsEqual("15")); |
| } |
| |
| TEST_F(EvalTest, TestContextVariables) { |
| // Context variables don't exist yet. |
| EXPECT_THAT(EvalWithContext("$var", vars_), |
| IsError("use of undeclared identifier '$var'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var", vars_), |
| IsError("use of undeclared identifier '$var'")); |
| |
| EXPECT_TRUE(CreateContextVariable("$var", "13")); |
| EXPECT_THAT(EvalWithContext("$var", vars_), IsEqual("13")); |
| EXPECT_THAT(EvalWithContext("$var + 2", vars_), IsEqual("15")); |
| EXPECT_THAT(EvalWithContext("$var - s.a", vars_), IsEqual("3")); |
| EXPECT_THAT(EvalWithContext("var", vars_), |
| IsError("use of undeclared identifier 'var'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var", vars_), IsEqual("13")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var + 2", vars_), IsEqual("15")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var - a", vars_), IsEqual("3")); |
| EXPECT_THAT(Scope("s").EvalWithContext("var", vars_), |
| IsError("use of undeclared identifier 'var'")); |
| |
| // Context variable is a pointer. |
| EXPECT_TRUE(CreateContextVariable("$ptr", "s.ptr")); |
| EXPECT_THAT(EvalWithContext("$ptr == s.ptr", vars_), IsEqual("true")); |
| EXPECT_THAT(EvalWithContext("*$ptr", vars_), IsEqual("'h'")); |
| EXPECT_THAT(EvalWithContext("$ptr[1]", vars_), IsEqual("'e'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$ptr == ptr", vars_), |
| IsEqual("true")); |
| EXPECT_THAT(Scope("s").EvalWithContext("*$ptr", vars_), IsEqual("'h'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$ptr[1]", vars_), IsEqual("'e'")); |
| |
| EXPECT_THAT(EvalWithContext("$var + *$ptr", vars_), IsEqual("117")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var + *$ptr", vars_), |
| IsEqual("117")); |
| } |
| |
| TEST_F(EvalTest, TestContextVariablesSubset) { |
| // All context variables that are created in this test are visible by LLDB. |
| // Disable comparisons with LLDB to test subsets of created context variables. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_TRUE(CreateContextVariable("$var", "13")); |
| EXPECT_TRUE(CreateContextVariable("$ptr", "s.ptr")); |
| |
| // Evaluate without the context. |
| EXPECT_THAT(Eval("$var"), IsError("use of undeclared identifier '$var'")); |
| EXPECT_THAT(Eval("$var + 1"), IsError("use of undeclared identifier '$var'")); |
| EXPECT_THAT(Scope("s").Eval("$var"), |
| IsError("use of undeclared identifier '$var'")); |
| EXPECT_THAT(Scope("s").Eval("$var + 1"), |
| IsError("use of undeclared identifier '$var'")); |
| |
| std::unordered_map<std::string, lldb::SBValue> var; |
| std::unordered_map<std::string, lldb::SBValue> ptr; |
| var.emplace("$var", vars_["$var"]); |
| ptr.emplace("$ptr", vars_["$ptr"]); |
| |
| EXPECT_THAT(EvalWithContext("$var + 0", var), IsEqual("13")); |
| EXPECT_THAT(EvalWithContext("*$ptr", var), |
| IsError("use of undeclared identifier '$ptr'")); |
| EXPECT_THAT(EvalWithContext("$var + *$ptr", var), |
| IsError("use of undeclared identifier '$ptr'")); |
| EXPECT_THAT(EvalWithContext("$var + *$ptr", ptr), |
| IsError("use of undeclared identifier '$var'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var + 0", var), IsEqual("13")); |
| EXPECT_THAT(Scope("s").EvalWithContext("*$ptr", var), |
| IsError("use of undeclared identifier '$ptr'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var + *$ptr", var), |
| IsError("use of undeclared identifier '$ptr'")); |
| EXPECT_THAT(Scope("s").EvalWithContext("$var + *$ptr", ptr), |
| IsError("use of undeclared identifier '$var'")); |
| } |
| #endif |
| |
| TEST_F(EvalTest, TestScopedEnum) { |
| EXPECT_THAT(Eval("enum_foo"), IsEqual("kFoo")); |
| EXPECT_THAT(Eval("enum_bar"), IsEqual("kBar")); |
| |
| EXPECT_THAT(Eval("enum_foo == enum_foo"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_foo != enum_foo"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_foo == enum_bar"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_foo < enum_bar"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("enum_foo == ScopedEnum::kFoo"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_foo == ScopedEnum::kBar"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_foo != ScopedEnum::kBar"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_foo < ScopedEnum::kBar"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_foo < enum_neg"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_neg < enum_bar"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("(ScopedEnum)0"), IsEqual("kFoo")); |
| EXPECT_THAT(Eval("(ScopedEnum)1"), IsEqual("kBar")); |
| EXPECT_THAT(Eval("(ScopedEnum)0.1"), IsEqual("kFoo")); |
| EXPECT_THAT(Eval("(ScopedEnum)1.1"), IsEqual("kBar")); |
| EXPECT_THAT(Eval("(ScopedEnum)-1"), IsOk()); |
| EXPECT_THAT(Eval("(ScopedEnum)-1.1"), IsOk()); |
| EXPECT_THAT(Eval("(ScopedEnum)256"), IsOk()); |
| EXPECT_THAT(Eval("(ScopedEnum)257"), IsOk()); |
| EXPECT_THAT(Eval("(ScopedEnum)false"), IsEqual("kFoo")); |
| EXPECT_THAT(Eval("(ScopedEnum)true"), IsEqual("kBar")); |
| |
| EXPECT_THAT(Eval("(int)enum_foo"), IsEqual("0")); |
| EXPECT_THAT(Eval("(int)enum_neg"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(unsigned short)enum_neg"), IsEqual("65535")); |
| EXPECT_THAT(Eval("(short)ScopedEnum::kBar"), IsEqual("1")); |
| EXPECT_THAT(Eval("(short*)enum_neg"), |
| IsEqual(Is32Bit() ? "0xffffffff" : "0xffffffffffffffff")); |
| EXPECT_THAT(Eval("(char*)enum_u8_bar"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| EXPECT_THAT(Eval("(float)enum_bar"), IsEqual("1")); |
| EXPECT_THAT(Eval("(float)enum_foo"), IsEqual("0")); |
| EXPECT_THAT(Eval("(float)enum_neg"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(double)enum_neg"), IsEqual("-1")); |
| EXPECT_THAT(Eval("(bool)enum_foo"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)enum_bar"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)enum_neg"), IsEqual("true")); |
| EXPECT_THAT(Eval("(double)ScopedEnumUInt8::kBar"), IsEqual("1")); |
| EXPECT_THAT(Eval("(ScopedEnum)ScopedEnum::kBar"), IsEqual("kBar")); |
| |
| // TODO: Enable the following test once the underlying enumeration type |
| // becomes fully available. Information about underlying enumeration type |
| // isn't available in LLDB 11 or lower, which are currently used by GitHub |
| // actions. |
| // EXPECT_THAT(Eval("(int)(ScopedEnumUInt8)enum_neg"), IsEqual("255")); |
| } |
| |
| TEST_F(EvalTest, TestScopedEnumArithmetic) { |
| if (!HAS_METHOD(lldb::SBType, IsScopedEnumerationType())) { |
| GTEST_SKIP(); |
| } |
| |
| EXPECT_THAT(Eval("enum_foo == 1"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_foo + 1"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_foo * 2"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_bar / 2"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_foo % 2"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_bar & 2"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_bar | 0x01"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_bar ^ 0b11"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_bar >> 1"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("enum_bar << 1"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("1 >> enum_bar"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("1 << enum_bar"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("(int*)1 + enum_foo"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("(int*)5 - enum_foo"), |
| IsError("invalid operands to binary expression")); |
| EXPECT_THAT(Eval("+enum_foo"), IsError("invalid argument type")); |
| EXPECT_THAT(Eval("-enum_foo"), IsError("invalid argument type")); |
| EXPECT_THAT(Eval("~enum_foo"), IsError("invalid argument type")); |
| EXPECT_THAT(Eval("!enum_foo"), |
| IsError("invalid argument type 'ScopedEnum' to unary expression\n" |
| "!enum_foo\n" |
| "^")); |
| } |
| |
| TEST_F(EvalTest, TestScopedEnumWithUnderlyingType) { |
| EXPECT_THAT(Eval("(ScopedEnumUInt8)-1"), IsOk()); |
| EXPECT_THAT(Eval("(ScopedEnumUInt8)256"), IsEqual("kFoo")); |
| EXPECT_THAT(Eval("(ScopedEnumUInt8)257"), IsEqual("kBar")); |
| } |
| |
| TEST_F(EvalTest, TestUnscopedEnum) { |
| EXPECT_THAT(Eval("enum_one"), IsEqual("kOne")); |
| EXPECT_THAT(Eval("enum_two"), IsEqual("kTwo")); |
| |
| EXPECT_THAT(Eval("enum_one == enum_one"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one != enum_one"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_one == enum_two"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_one < enum_two"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("enum_one == UnscopedEnum::kOne"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one == UnscopedEnum::kTwo"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_one != UnscopedEnum::kTwo"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one < UnscopedEnum::kTwo"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("(UnscopedEnum)0"), IsEqual("kZero")); |
| EXPECT_THAT(Eval("(UnscopedEnum)1"), IsEqual("kOne")); |
| EXPECT_THAT(Eval("(UnscopedEnum)0.1"), IsEqual("kZero")); |
| EXPECT_THAT(Eval("(UnscopedEnum)1.1"), IsEqual("kOne")); |
| EXPECT_THAT(Eval("(UnscopedEnum)-1"), IsOk()); |
| EXPECT_THAT(Eval("(UnscopedEnum)256"), IsOk()); |
| EXPECT_THAT(Eval("(UnscopedEnum)257"), IsOk()); |
| EXPECT_THAT(Eval("(UnscopedEnum)false"), IsEqual("kZero")); |
| EXPECT_THAT(Eval("(UnscopedEnum)true"), IsEqual("kOne")); |
| EXPECT_THAT(Eval("(UnscopedEnum)(UnscopedEnum)0"), IsEqual("kZero")); |
| EXPECT_THAT(Eval("(void*)(UnscopedEnum)1"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| |
| bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); |
| |
| EXPECT_THAT(Eval("enum_one == 1"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one + 1"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("enum_one * 2"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("enum_two / 2"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_one % 2"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_two & 2"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("enum_two | 0x01"), IsEqual("3", compare_types)); |
| EXPECT_THAT(Eval("enum_two ^ 0b11"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_two >> 1"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_two << 1"), IsEqual("4", compare_types)); |
| EXPECT_THAT(Eval("8 >> enum_two"), IsEqual("2")); |
| EXPECT_THAT(Eval("1 << enum_two"), IsEqual("4")); |
| // TODO: Enable type comparison after the bug is addressed in LLDB. |
| EXPECT_THAT(Eval("UnscopedEnumUInt8::kTwoU8 << 1"), |
| IsEqual("4", /*compare_types*/ false)); |
| EXPECT_THAT(Eval("UnscopedEnumInt8::kTwo8 << 1U"), IsEqual("4")); |
| EXPECT_THAT(Eval("+enum_one"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("!enum_one"), IsEqual("false")); |
| EXPECT_THAT(Eval("(int*)1 + enum_one"), |
| IsEqual(Is32Bit() ? "0x00000005" : "0x0000000000000005")); |
| EXPECT_THAT(Eval("(int*)5 - enum_one"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| |
| // Use references. |
| EXPECT_THAT(Eval("enum_one_ref"), IsEqual("kOne")); |
| EXPECT_THAT(Eval("enum_two_ref"), IsEqual("kTwo")); |
| EXPECT_THAT(Eval("enum_one_ref == enum_one"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one_ref != enum_one"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_one_ref == enum_two"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_one_ref < enum_two"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one_ref == UnscopedEnum::kOne"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one_ref == UnscopedEnum::kTwo"), IsEqual("false")); |
| EXPECT_THAT(Eval("enum_one_ref != UnscopedEnum::kTwo"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one_ref < UnscopedEnum::kTwo"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one_ref == 1"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_one_ref + 1"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("enum_one_ref * 2"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("enum_two_ref / 2"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_one_ref % 2"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_two_ref & 2"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("enum_two_ref | 0x01"), IsEqual("3", compare_types)); |
| EXPECT_THAT(Eval("enum_two_ref ^ 0b11"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_two_ref >> 1"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("enum_two_ref << 1"), IsEqual("4", compare_types)); |
| EXPECT_THAT(Eval("8 >> enum_two_ref"), IsEqual("2")); |
| EXPECT_THAT(Eval("1 << enum_two_ref"), IsEqual("4")); |
| EXPECT_THAT(Eval("+enum_one_ref"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("!enum_one_ref"), IsEqual("false")); |
| EXPECT_THAT(Eval("(int*)1 + enum_one_ref"), |
| IsEqual(Is32Bit() ? "0x00000005" : "0x0000000000000005")); |
| EXPECT_THAT(Eval("(int*)5 - enum_one_ref"), |
| IsEqual(Is32Bit() ? "0x00000001" : "0x0000000000000001")); |
| |
| EXPECT_THAT(Eval("(long long)UnscopedEnum::kTwo"), IsEqual("2")); |
| EXPECT_THAT(Eval("(unsigned int)enum_one"), IsEqual("1")); |
| EXPECT_THAT(Eval("(short*)UnscopedEnumUInt8::kTwoU8"), |
| IsEqual(Is32Bit() ? "0x00000002" : "0x0000000000000002")); |
| EXPECT_THAT(Eval("(float)UnscopedEnum::kOne"), IsEqual("1")); |
| EXPECT_THAT(Eval("(float)enum_two"), IsEqual("2")); |
| EXPECT_THAT(Eval("(double)enum_one"), IsEqual("1")); |
| EXPECT_THAT(Eval("(bool)enum_two"), IsEqual("true")); |
| EXPECT_THAT(Eval("(bool)(UnscopedEnum)0"), IsEqual("false")); |
| EXPECT_THAT(Eval("(bool)(UnscopedEnum)-1"), IsEqual("true")); |
| } |
| |
| TEST_F(EvalTest, TestUnscopedEnumNegation) { |
| // Sometimes LLDB is confused about signedness and uses "unsigned int" as an |
| // underlying type. Technically, underlying type for the unscoped enum is |
| // implementation defined, but it should convert to "int" nonetheless. |
| this->compare_with_lldb_ = false; |
| |
| bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); |
| EXPECT_THAT(Eval("-enum_one"), IsEqual("-1", compare_types)); |
| EXPECT_THAT(Eval("-enum_one_ref"), IsEqual("-1", compare_types)); |
| EXPECT_THAT(Eval("-(UnscopedEnumEmpty)1"), IsOk(compare_types)); |
| } |
| |
| TEST_F(EvalTest, TestUnscopedEnumWithUnderlyingType) { |
| EXPECT_THAT(Eval("(UnscopedEnumUInt8)-1"), IsOk()); |
| EXPECT_THAT(Eval("(UnscopedEnumUInt8)256"), IsEqual("kZeroU8")); |
| EXPECT_THAT(Eval("(UnscopedEnumUInt8)257"), IsEqual("kOneU8")); |
| |
| EXPECT_THAT(Eval("(UnscopedEnumInt8)-2.1"), IsOk()); |
| EXPECT_THAT(Eval("(int)enum_neg_8"), IsEqual("-1")); |
| EXPECT_THAT(Eval("enum_neg_8 < enum_one_8"), IsEqual("true")); |
| EXPECT_THAT(Eval("enum_neg_8 > enum_one_8"), IsEqual("false")); |
| } |
| |
| TEST_F(EvalTest, TestUnscopedEnumEmpty) { |
| EXPECT_THAT(Eval("(UnscopedEnumEmpty)1"), IsOk()); |
| EXPECT_THAT(Eval("(int)enum_empty"), IsOk()); |
| } |
| |
| TEST_F(EvalTest, TestTernaryOperator) { |
| EXPECT_THAT(Eval("true ? c : c"), IsEqual("'\\x02'")); |
| EXPECT_THAT(Eval("true ? c : (char)1"), IsEqual("'\\x02'")); |
| EXPECT_THAT(Eval("true ? c : 1"), IsEqual("2")); |
| EXPECT_THAT(Eval("false ? 1 : c"), IsEqual("2")); |
| |
| bool compare_types = HAS_METHOD(lldb::SBType, GetEnumerationIntegerType()); |
| EXPECT_THAT(Eval("false ? a_enum : EnumA::kOneA"), IsEqual("kOneA")); |
| EXPECT_THAT(Eval("false ? b_enum : a_enum"), IsEqual("2", compare_types)); |
| EXPECT_THAT(Eval("false ? c : b_enum"), IsEqual("1", compare_types)); |
| EXPECT_THAT(Eval("false ? b_enum : c"), IsEqual("2", compare_types)); |
| |
| EXPECT_THAT(Eval("true ? (int*)15 : 0"), |
| IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); |
| EXPECT_THAT(Eval("true ? 0 : (int*)15"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("true ? (int*)15 : nullptr"), |
| IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); |
| EXPECT_THAT(Eval("true ? nullptr : (int*)15"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("true ? 0 : nullptr"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("true ? nullptr : 0"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| |
| EXPECT_THAT(Eval("+(true ? arr2 : arr2)"), IsOk()); |
| EXPECT_THAT(Eval("true ? arr2 : arr3"), IsOk()); |
| EXPECT_THAT(Eval("true ? arr2 : 0"), IsOk()); |
| EXPECT_THAT(Eval("true ? 0 : arr2"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("true ? arr2 : nullptr"), IsOk()); |
| EXPECT_THAT(Eval("true ? nullptr : arr2"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("true ? arr2 : (int*)15"), IsOk()); |
| EXPECT_THAT(Eval("true ? (int*)15 : arr2"), |
| IsEqual(Is32Bit() ? "0x0000000f" : "0x000000000000000f")); |
| |
| EXPECT_THAT(Eval("&(true ? arr2 : arr2)"), IsOk()); |
| |
| EXPECT_THAT(Eval("true ? t : 1"), |
| IsError("incompatible operand types ('T' and 'int')\n" |
| "true ? t : 1\n" |
| " ^")); |
| EXPECT_THAT(Eval("true ? 1 : t"), |
| IsError("incompatible operand types ('int' and 'T')\n" |
| "true ? 1 : t\n" |
| " ^")); |
| EXPECT_THAT(Eval("true ? (int*)15 : 1"), |
| IsError("incompatible operand types ('int *' and 'int')\n" |
| "true ? (int*)15 : 1\n" |
| " ^")); |
| EXPECT_THAT(Eval("true ? arr2 : dbl_arr"), |
| IsError("incompatible operand types ('int *' and 'double *')\n" |
| "true ? arr2 : dbl_arr\n" |
| " ^")); |
| |
| EXPECT_THAT(Eval("&(true ? *pi : 0)"), |
| IsError("cannot take the address of an rvalue of type 'int'\n" |
| "&(true ? *pi : 0)\n" |
| "^")); |
| EXPECT_THAT(Eval("&(true ? arr2 : pi)"), |
| IsError("cannot take the address of an rvalue of type 'int *'\n" |
| "&(true ? arr2 : pi)\n" |
| "^")); |
| EXPECT_THAT(Eval("&(true ? arr2 : arr3)"), |
| IsError("cannot take the address of an rvalue of type 'int *'\n" |
| "&(true ? arr2 : arr3)\n" |
| "^")); |
| |
| EXPECT_THAT(Eval("true ? nullptr : pi"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(Eval("true ? pi : nullptr"), IsOk()); |
| EXPECT_THAT(Eval("false ? nullptr : pi"), IsOk()); |
| EXPECT_THAT(Eval("false ? pi : nullptr"), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| |
| // Use pointers and arrays in bool context. |
| EXPECT_THAT(Eval("pi ? 1 : 2"), IsEqual("1")); |
| EXPECT_THAT(Eval("nullptr ? 1 : 2"), IsEqual("2")); |
| EXPECT_THAT(Eval("arr2 ? 1 : 2"), IsEqual("1")); |
| } |
| |
| TEST_F(EvalTest, TestSizeOf) { |
| EXPECT_THAT(Eval("sizeof(int)"), IsEqual("4")); |
| EXPECT_THAT(Eval("sizeof(int&)"), IsEqual("4")); |
| EXPECT_THAT(Eval("sizeof(int*)"), IsEqual(Is32Bit() ? "4" : "8")); |
| EXPECT_THAT(Eval("sizeof(int***)"), IsEqual(Is32Bit() ? "4" : "8")); |
| |
| EXPECT_THAT(Eval("sizeof(i)"), IsEqual("4")); |
| EXPECT_THAT(Eval("sizeof i "), IsEqual("4")); |
| EXPECT_THAT(Eval("sizeof p "), IsEqual(Is32Bit() ? "4" : "8")); |
| EXPECT_THAT(Eval("sizeof(arr)"), IsEqual("12")); |
| EXPECT_THAT(Eval("sizeof arr "), IsEqual("12")); |
| EXPECT_THAT(Eval("sizeof(arr + 1)"), IsEqual(Is32Bit() ? "4" : "8")); |
| EXPECT_THAT(Eval("sizeof arr + 1 "), IsEqual("13")); |
| |
| EXPECT_THAT(Eval("sizeof(foo)"), IsEqual("8")); |
| EXPECT_THAT(Eval("sizeof foo "), IsEqual("8")); |
| EXPECT_THAT(Eval("sizeof(SizeOfFoo)"), IsEqual("8")); |
| |
| // Makes sure that the expression isn't parsed as two types `i<i>` and `i`. |
| EXPECT_THAT(Eval("sizeof(i < i > i)"), IsEqual("1")); |
| |
| EXPECT_THAT( |
| Eval("sizeof(int & *)"), |
| IsError( |
| "'type name' declared as a pointer to a reference of type 'int &'\n" |
| "sizeof(int & *)\n" |
| " ^")); |
| |
| EXPECT_THAT(Eval("sizeof(intt + 1)"), |
| IsError("use of undeclared identifier 'intt'\n" |
| "sizeof(intt + 1)\n" |
| " ^")); |
| } |
| |
| TEST_F(EvalTest, TestBuiltinFunction_Log2) { |
| // LLDB doesn't support `__log2` intrinsic function. |
| this->compare_with_lldb_ = false; |
| |
| // Note: Visual Studio debugger gives an error in this case: |
| // |
| // > __log(0) |
| // Attempt to calculate __log2(0). |
| // |
| EXPECT_THAT(Eval("__log2(0)"), IsEqual("4294967295")); |
| EXPECT_THAT(Eval("__log2(1LL << 32)"), IsEqual("4294967295")); |
| |
| EXPECT_THAT(Eval("__log2(1)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__log2(8)"), IsEqual("3")); |
| EXPECT_THAT(Eval("__log2(12345)"), IsEqual("13")); |
| EXPECT_THAT(Eval("__log2(-1)"), IsEqual("31")); |
| EXPECT_THAT(Eval("__log2((1LL << 32)+128)"), IsEqual("7")); |
| |
| EXPECT_THAT(Eval("__log2(32.3f)"), IsEqual("5")); |
| EXPECT_THAT(Eval("__log2(12345.12345)"), IsEqual("13")); |
| |
| EXPECT_THAT(Eval("__log2(c_enum)"), IsEqual("7")); |
| EXPECT_THAT(Eval("__log2(cxx_enum)"), IsEqual("7")); |
| EXPECT_THAT(Eval("__log2(c_enum_ref)"), IsEqual("7")); |
| EXPECT_THAT(Eval("__log2(cxx_enum_ref)"), IsEqual("7")); |
| EXPECT_THAT(Eval("__log2(CEnum::kFoo)"), IsEqual("7")); |
| EXPECT_THAT(Eval("__log2(CxxEnum::kFoo)"), IsEqual("7")); |
| |
| EXPECT_THAT(Eval("__log2(foo)"), |
| IsError("no known conversion from 'Foo' to 'unsigned int'\n" |
| "__log2(foo)\n" |
| " ^")); |
| |
| EXPECT_THAT(Eval("__log2()"), |
| IsError("no matching function for call to '__log2': requires 1 " |
| "argument(s), but 0 argument(s) were provided\n" |
| "__log2()\n" |
| "^")); |
| |
| EXPECT_THAT(Eval("1 + __log2(1, 2)"), |
| IsError("no matching function for call to '__log2': requires 1 " |
| "argument(s), but 2 argument(s) were provided\n" |
| "1 + __log2(1, 2)\n" |
| " ^")); |
| |
| EXPECT_THAT(Eval("dummy(1)"), |
| IsError("function 'dummy' is not a supported builtin intrinsic")); |
| EXPECT_THAT( |
| Eval("::dummy(1)"), |
| IsError("function '::dummy' is not a supported builtin intrinsic")); |
| EXPECT_THAT( |
| Eval("ns::dummy(1)"), |
| IsError("function 'ns::dummy' is not a supported builtin intrinsic")); |
| EXPECT_THAT( |
| Eval("::ns::dummy(1)"), |
| IsError("function '::ns::dummy' is not a supported builtin intrinsic")); |
| } |
| |
| TEST_F(EvalTest, TestPrefixIncDec) { |
| EXPECT_THAT(Eval("++1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("--i"), |
| IsError("side effects are not supported in this context")); |
| |
| #ifndef __EMSCRIPTEN__ |
| ASSERT_TRUE(CreateContextVariableArray("int", "$arr", "{1,2,3}")); |
| EXPECT_THAT(EvalWithContext("++$arr", vars_), |
| IsError("cannot increment value of type 'int [3]'")); |
| |
| ASSERT_TRUE(CreateContextVariable("$enum_foo", "ScopedEnum::kFoo")); |
| EXPECT_THAT(EvalWithContext("++$enum_foo", vars_), |
| IsError("cannot increment expression of enum type 'ScopedEnum'")); |
| |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("++$i", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("++$i + 1", vars_), IsEqual("4")); |
| EXPECT_THAT(EvalWithContext("--$i", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("--$i - 1", vars_), IsEqual("0")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT(EvalWithContext("++$f", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("++$f + 1", vars_), IsEqual("4.5")); |
| EXPECT_THAT(EvalWithContext("--$f", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("--$f - 1", vars_), IsEqual("0.5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$d", "1.5")); |
| EXPECT_THAT(EvalWithContext("++$d", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("++$d + 1", vars_), IsEqual("4.5")); |
| EXPECT_THAT(EvalWithContext("--$d", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("--$d - 1", vars_), IsEqual("0.5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$p", "(int*)4")); |
| EXPECT_THAT(EvalWithContext("++$p", vars_), |
| IsEqual(Is32Bit() ? "0x00000008" : "0x0000000000000008")); |
| EXPECT_THAT(EvalWithContext("++$p + 1", vars_), |
| IsEqual(Is32Bit() ? "0x00000010" : "0x0000000000000010")); |
| EXPECT_THAT(EvalWithContext("--$p", vars_), |
| IsEqual(Is32Bit() ? "0x00000008" : "0x0000000000000008")); |
| EXPECT_THAT(EvalWithContext("--$p - 1", vars_), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestPostfixIncDec) { |
| EXPECT_THAT(Eval("1++"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i--"), |
| IsError("side effects are not supported in this context")); |
| |
| #ifndef __EMSCRIPTEN__ |
| ASSERT_TRUE(CreateContextVariableArray("int", "$arr", "{1,2,3}")); |
| EXPECT_THAT(EvalWithContext("$arr--", vars_), |
| IsError("cannot decrement value of type 'int [3]'")); |
| |
| ASSERT_TRUE(CreateContextVariable("$enum_foo", "ScopedEnum::kFoo")); |
| EXPECT_THAT(EvalWithContext("$enum_foo++", vars_), |
| IsError("cannot increment expression of enum type 'ScopedEnum'")); |
| |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("$i++", vars_), IsEqual("1")); |
| EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i++ + 1", vars_), IsEqual("3")); |
| EXPECT_THAT(EvalWithContext("$i--", vars_), IsEqual("3")); |
| EXPECT_THAT(EvalWithContext("$i-- - 1", vars_), IsEqual("1")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT(EvalWithContext("$f++", vars_), IsEqual("1.5")); |
| EXPECT_THAT(EvalWithContext("$f", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("$f++ + 1", vars_), IsEqual("3.5")); |
| EXPECT_THAT(EvalWithContext("$f--", vars_), IsEqual("3.5")); |
| EXPECT_THAT(EvalWithContext("$f-- - 1", vars_), IsEqual("1.5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$d", "1.5")); |
| EXPECT_THAT(EvalWithContext("$d++", vars_), IsEqual("1.5")); |
| EXPECT_THAT(EvalWithContext("$d++ + 1", vars_), IsEqual("3.5")); |
| EXPECT_THAT(EvalWithContext("$d--", vars_), IsEqual("3.5")); |
| EXPECT_THAT(EvalWithContext("$d-- - 1", vars_), IsEqual("1.5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$p", "(int*)4")); |
| EXPECT_THAT(EvalWithContext("$p++", vars_), |
| IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); |
| EXPECT_THAT(EvalWithContext("$p++ + 1", vars_), |
| IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); |
| EXPECT_THAT(EvalWithContext("$p--", vars_), |
| IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); |
| EXPECT_THAT(EvalWithContext("$p-- - 1", vars_), |
| IsEqual(Is32Bit() ? "0x00000004" : "0x0000000000000004")); |
| #endif |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| TEST_F(EvalTest, TestDereferencedType) { |
| // When dereferencing the value LLDB also removes the typedef and the |
| // resulting value has the canonical type. We have to mitigate that, because |
| // having the correct type is important for visualization logic. |
| // |
| // In this example we have: |
| // |
| // ``` |
| // struct TTuple { ... }; |
| // using TPair = TTuple; |
| // const TPair& p_ref = ...; |
| // const TPair* p_ptr = ...; |
| // ``` |
| // |
| // The expression evaluator removes the references from all values and we must |
| // verify that the resulting type is correct. |
| |
| for (auto&& expr : {"p_ref", "*p_ptr"}) { |
| // Directly evaluate the expression and verify the types. |
| auto ret = Eval(expr); |
| |
| ASSERT_TRUE(ret.lldb_eval_error.Success()); |
| ASSERT_TRUE(ret.lldb_value.has_value()); |
| |
| ASSERT_STREQ(ret.lldb_eval_value.GetTypeName(), "TPair"); |
| ASSERT_STREQ(ret.lldb_value.value().GetTypeName(), "TPair"); |
| } |
| } |
| #endif |
| |
| TEST_F(EvalTest, TestMemberFunctionCall) { |
| // LLDB supports function calls, so disable it. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("c.m()"), |
| IsError("member function calls are not supported")); |
| } |
| |
| TEST_F(EvalTest, TestAssignment) { |
| EXPECT_THAT(Eval("1 = 1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i = 1"), IsError("side effects are not supported")); |
| |
| EXPECT_THAT(Eval("p = 1"), |
| IsError("no known conversion from 'int' to 'float *'")); |
| EXPECT_THAT(Eval("eOne = 1"), |
| IsError("no known conversion from 'int' to 'Enum'")); |
| |
| #ifndef __EMSCRIPTEN__ |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("$i = 2", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i = -2", vars_), IsEqual("-2")); |
| EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("-2")); |
| EXPECT_THAT(EvalWithContext("$i = eOne", vars_), IsEqual("0")); |
| EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("0")); |
| EXPECT_THAT(EvalWithContext("$i = eTwo", vars_), IsEqual("1")); |
| EXPECT_THAT(EvalWithContext("$i", vars_), IsEqual("1")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT(EvalWithContext("$f = 2.5", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("$f", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("$f = 3.5f", vars_), IsEqual("3.5")); |
| EXPECT_THAT(EvalWithContext("$f", vars_), IsEqual("3.5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s = 100000", vars_), IsEqual("-31072")); |
| EXPECT_THAT(EvalWithContext("$s", vars_), IsEqual("-31072")); |
| |
| ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); |
| EXPECT_THAT(EvalWithContext("$p = 1", vars_), |
| IsError("no known conversion from 'int' to 'int *'")); |
| EXPECT_THAT(EvalWithContext("$p = (int*)12", vars_), |
| IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); |
| EXPECT_THAT(EvalWithContext("$p", vars_), |
| IsEqual(Is32Bit() ? "0x0000000c" : "0x000000000000000c")); |
| EXPECT_THAT(EvalWithContext("$p = 0", vars_), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| EXPECT_THAT(EvalWithContext("$p = nullptr", vars_), |
| IsEqual(Is32Bit() ? "0x00000000" : "0x0000000000000000")); |
| |
| ASSERT_TRUE(CreateContextVariableArray("int", "$arr", "{1, 2}")); |
| EXPECT_THAT(EvalWithContext("$p = $arr", vars_), IsOk()); |
| |
| ASSERT_TRUE(CreateContextVariableArray("float", "$farr", "{1.f, 2.f}")); |
| EXPECT_THAT(EvalWithContext("$p = $farr", vars_), |
| IsError("no known conversion from 'float [2]' to 'int *'")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestCompositeAssignmentInvalid) { |
| EXPECT_THAT(Eval("1 += 1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i += 1"), IsError("side effects are not supported")); |
| |
| EXPECT_THAT(Eval("1 -= 1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i -= 1"), IsError("side effects are not supported")); |
| |
| EXPECT_THAT(Eval("1 *= 1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i *= 1"), IsError("side effects are not supported")); |
| |
| EXPECT_THAT(Eval("1 /= 1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i /= 1"), IsError("side effects are not supported")); |
| |
| EXPECT_THAT(Eval("1 %= 1"), IsError("expression is not assignable")); |
| EXPECT_THAT(Eval("i %= 1"), IsError("side effects are not supported")); |
| EXPECT_THAT( |
| Eval("f %= 1"), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| |
| #ifndef __EMSCRIPTEN__ |
| ASSERT_TRUE(CreateContextVariable("Enum", "$e", false, "Enum::ONE")); |
| EXPECT_THAT( |
| EvalWithContext("$e *= 1", vars_), |
| // TODO(werat): This should actually be: |
| // > assigning to 'Enum' from incompatible type 'int' |
| IsError("invalid operands to binary expression ('Enum' and 'int')")); |
| |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("($i += 1) -= 2", vars_), |
| IsError("side effects are not supported in this context")); |
| #endif |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| TEST_F(EvalTest, TestCompositeAssignmentAdd) { |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("$i += 1", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i += 2", vars_), IsEqual("4")); |
| EXPECT_THAT(EvalWithContext("$i += -4", vars_), IsEqual("0")); |
| EXPECT_THAT(EvalWithContext("$i += eOne", vars_), IsEqual("0")); |
| EXPECT_THAT(EvalWithContext("$i += eTwo", vars_), IsEqual("1")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT(EvalWithContext("$f += 1", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("$f += -2", vars_), IsEqual("0.5")); |
| EXPECT_THAT(EvalWithContext("$f += 2.5", vars_), IsEqual("3")); |
| EXPECT_THAT(EvalWithContext("$f += eTwo", vars_), IsEqual("4")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s += 1000", vars_), IsEqual("1100")); |
| EXPECT_THAT(EvalWithContext("$s += 100000", vars_), IsEqual("-29972")); |
| |
| ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); |
| EXPECT_THAT(EvalWithContext("$p += 1", vars_), |
| IsEqual(Is32Bit() ? "0x0000000e" : "0x000000000000000e")); |
| EXPECT_THAT( |
| EvalWithContext("$p += 1.5", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'double')")); |
| EXPECT_THAT( |
| EvalWithContext("$p += $p", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'int *')")); |
| EXPECT_THAT(EvalWithContext("$i += $p", vars_), |
| IsError("no known conversion from 'int *' to 'int'")); |
| } |
| |
| TEST_F(EvalTest, TestCompositeAssignmentSub) { |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("$i -= 1", vars_), IsEqual("0")); |
| EXPECT_THAT(EvalWithContext("$i -= 2", vars_), IsEqual("-2")); |
| EXPECT_THAT(EvalWithContext("$i -= -4", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i -= eOne", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i -= eTwo", vars_), IsEqual("1")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT(EvalWithContext("$f -= 1", vars_), IsEqual("0.5")); |
| EXPECT_THAT(EvalWithContext("$f -= -2", vars_), IsEqual("2.5")); |
| EXPECT_THAT(EvalWithContext("$f -= -2.5", vars_), IsEqual("5")); |
| EXPECT_THAT(EvalWithContext("$f -= eTwo", vars_), IsEqual("4")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s -= 1000", vars_), IsEqual("-900")); |
| EXPECT_THAT(EvalWithContext("$s -= 100000", vars_), IsEqual("30172")); |
| |
| ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); |
| EXPECT_THAT(EvalWithContext("$p -= 1", vars_), |
| IsEqual(Is32Bit() ? "0x00000006" : "0x0000000000000006")); |
| |
| #ifdef _WIN32 |
| // On Windows, 'ptrdiff_t' is 'long long'. |
| EXPECT_THAT(EvalWithContext("$p -= $p", vars_), |
| IsError("no known conversion from 'long long' to 'int *'")); |
| #else |
| // On Linux, 'ptrdiff_t' is 'long'. |
| EXPECT_THAT(EvalWithContext("$p -= $p", vars_), |
| IsError("no known conversion from 'long' to 'int *'")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestCompositeAssignmentMul) { |
| ASSERT_TRUE(CreateContextVariable("$i", "1")); |
| EXPECT_THAT(EvalWithContext("$i *= 1", vars_), IsEqual("1")); |
| EXPECT_THAT(EvalWithContext("$i *= 2", vars_), IsEqual("2")); |
| EXPECT_THAT(EvalWithContext("$i *= -2.5", vars_), IsEqual("-5")); |
| EXPECT_THAT(EvalWithContext("$i *= eTwo", vars_), IsEqual("-5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT(EvalWithContext("$f *= 1", vars_), IsEqual("1.5")); |
| EXPECT_THAT(EvalWithContext("$f *= 2", vars_), IsEqual("3")); |
| EXPECT_THAT(EvalWithContext("$f *= -2.5", vars_), IsEqual("-7.5")); |
| EXPECT_THAT(EvalWithContext("$f *= eTwo", vars_), IsEqual("-7.5")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s *= 1000", vars_), IsEqual("-31072")); |
| EXPECT_THAT(EvalWithContext("$s *= -1000", vars_), IsEqual("7936")); |
| } |
| |
| TEST_F(EvalTest, TestCompositeAssignmentDiv) { |
| ASSERT_TRUE(CreateContextVariable("$i", "15")); |
| EXPECT_THAT(EvalWithContext("$i /= 1", vars_), IsEqual("15")); |
| EXPECT_THAT(EvalWithContext("$i /= 2", vars_), IsEqual("7")); |
| EXPECT_THAT(EvalWithContext("$i /= -2", vars_), IsEqual("-3")); |
| EXPECT_THAT(EvalWithContext("$i /= eTwo", vars_), IsEqual("-3")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "15.5f")); |
| EXPECT_THAT(EvalWithContext("$f /= 1", vars_), IsEqual("15.5")); |
| EXPECT_THAT(EvalWithContext("$f /= 2", vars_), IsEqual("7.75")); |
| EXPECT_THAT(EvalWithContext("$f /= -2.5", vars_), IsEqual("-3.0999999")); |
| EXPECT_THAT(EvalWithContext("$f /= eTwo", vars_), IsEqual("-3.0999999")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s /= 10", vars_), IsEqual("10")); |
| EXPECT_THAT(EvalWithContext("$s /= -3", vars_), IsEqual("-3")); |
| } |
| |
| TEST_F(EvalTest, TestCompositeAssignmentRem) { |
| ASSERT_TRUE(CreateContextVariable("$i", "15")); |
| EXPECT_THAT(EvalWithContext("$i %= 8", vars_), IsEqual("7")); |
| EXPECT_THAT(EvalWithContext("$i %= -3", vars_), IsEqual("1")); |
| EXPECT_THAT(EvalWithContext("$i %= eTwo", vars_), IsEqual("0")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "15.5f")); |
| EXPECT_THAT( |
| EvalWithContext("$f %= 1", vars_), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s %= 23", vars_), IsEqual("8")); |
| } |
| |
| TEST_F(EvalTest, TestCompositeAssignmentBitwise) { |
| ASSERT_TRUE(CreateContextVariable("$i", "0b11111111")); |
| EXPECT_THAT(EvalWithContext("$i &= 0b11110000", vars_), IsEqual("240")); |
| EXPECT_THAT(EvalWithContext("$i |= 0b01100011", vars_), IsEqual("243")); |
| EXPECT_THAT(EvalWithContext("$i ^= 0b00100010", vars_), IsEqual("209")); |
| EXPECT_THAT(EvalWithContext("$i <<= 2", vars_), IsEqual("836")); |
| EXPECT_THAT(EvalWithContext("$i >>= 3", vars_), IsEqual("104")); |
| EXPECT_THAT(EvalWithContext("$i <<= eTwo", vars_), IsEqual("208")); |
| EXPECT_THAT(EvalWithContext("$i >>= eTwo", vars_), IsEqual("104")); |
| EXPECT_THAT(EvalWithContext("$i <<= 1U", vars_), IsEqual("208")); |
| EXPECT_THAT(EvalWithContext("$i >>= 1LL", vars_), IsEqual("104")); |
| |
| ASSERT_TRUE(CreateContextVariable("$c", "(signed char)-1")); |
| EXPECT_THAT(EvalWithContext("$c &= 0b11110011", vars_), IsEqual("'\\xf3'")); |
| EXPECT_THAT(EvalWithContext("$c |= 0b01001011", vars_), IsEqual("'\\xfb'")); |
| EXPECT_THAT(EvalWithContext("$c ^= 0b00000110", vars_), IsEqual("'\\xfd'")); |
| EXPECT_THAT(EvalWithContext("$c <<= 5", vars_), IsEqual("'\\xa0'")); |
| EXPECT_THAT(EvalWithContext("$c >>= 20", vars_), IsEqual("'\\xff'")); |
| EXPECT_THAT(EvalWithContext("$c <<= 2U", vars_), IsEqual("'\\xfc'")); |
| EXPECT_THAT(EvalWithContext("$c >>= eTwo", vars_), IsEqual("'\\xfe'")); |
| |
| ASSERT_TRUE(CreateContextVariable("$f", "1.5f")); |
| EXPECT_THAT( |
| EvalWithContext("$f &= 1", vars_), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| EXPECT_THAT( |
| EvalWithContext("$f |= 1", vars_), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| EXPECT_THAT( |
| EvalWithContext("$f ^= 1", vars_), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| EXPECT_THAT( |
| EvalWithContext("$f <<= 1", vars_), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| EXPECT_THAT( |
| EvalWithContext("$f >>= 1", vars_), |
| IsError("invalid operands to binary expression ('float' and 'int')")); |
| |
| ASSERT_TRUE(CreateContextVariable("$s", "(short)100")); |
| EXPECT_THAT(EvalWithContext("$s >>= 2", vars_), IsEqual("25")); |
| EXPECT_THAT(EvalWithContext("$s <<= 6", vars_), IsEqual("1600")); |
| EXPECT_THAT(EvalWithContext("$s <<= 12", vars_), IsEqual("0")); |
| |
| ASSERT_TRUE(CreateContextVariable("$e", "eTwo")); |
| std::vector<std::string> ops = {"&=", "|=", "^=", "<<=", ">>="}; |
| for (const auto& op : ops) { |
| const std::string expr = "$e " + op + " 1"; // e.g. "$e ^= 1" |
| EXPECT_THAT( |
| EvalWithContext(expr, vars_), |
| IsError("invalid operands to binary expression ('Enum' and 'int')")); |
| } |
| |
| ASSERT_TRUE(CreateContextVariable("$p", "(int*)10")); |
| EXPECT_THAT( |
| EvalWithContext("$p &= 1", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'int')")); |
| EXPECT_THAT( |
| EvalWithContext("$p |= (char)1", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'char')")); |
| EXPECT_THAT( |
| EvalWithContext("$p ^= &f", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'float *')")); |
| EXPECT_THAT( |
| EvalWithContext("$p <<= 1", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'int')")); |
| EXPECT_THAT( |
| EvalWithContext("$p >>= $p", vars_), |
| IsError("invalid operands to binary expression ('int *' and 'int *')")); |
| } |
| |
| TEST_F(EvalTest, TestSideEffects) { |
| // Comparing with LLDB is not possible with side effects enabled -- results |
| // will always be different (because the same expression is evaluated twice). |
| this->compare_with_lldb_ = false; |
| this->allow_side_effects_ = true; |
| |
| EXPECT_THAT(Eval("x++"), IsEqual("1")); |
| EXPECT_THAT(Eval("x"), IsEqual("2")); |
| EXPECT_THAT(Eval("++x"), IsEqual("3")); |
| |
| EXPECT_THAT(Eval("xa[0] = 4"), IsEqual("4")); |
| EXPECT_THAT(Eval("xa[0]"), IsEqual("4")); |
| EXPECT_THAT(Eval("xa[1] += xa[0]"), IsEqual("6")); |
| |
| EXPECT_THAT(Eval("*p = 5.2"), IsEqual("5")); |
| EXPECT_THAT(Eval("*p"), IsEqual("5")); |
| EXPECT_THAT(Eval("x"), IsEqual("5")); // `p` is `&x` |
| } |
| #endif |
| |
| TEST_F(EvalTest, TestBuiltinFunction_findnonnull) { |
| // LLDB doesn't support `__findnonnull` intrinsic function. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("__findnonnull(array_of_pointers, 0)"), IsEqual("-1")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_pointers, 1)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_pointers, 5)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_pointers+2, 3)"), IsEqual("2")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_pointers+3, 2)"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers, 0)"), IsEqual("-1")); |
| EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers, 1)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers, 5)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers+2, 3)"), IsEqual("2")); |
| EXPECT_THAT(Eval("__findnonnull(pointer_to_pointers+3, 2)"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("__findnonnull(0, 0)"), |
| IsError("no known conversion from 'int' to 'T*' for 1st argument " |
| "of __findnonnull()\n" |
| "__findnonnull(0, 0)\n" |
| " ^")); |
| EXPECT_THAT( |
| Eval("__findnonnull(1.0f, 0)"), |
| IsError("no known conversion from 'float' to 'T*' for 1st argument " |
| "of __findnonnull()\n" |
| "__findnonnull(1.0f, 0)\n" |
| " ^")); |
| EXPECT_THAT( |
| Eval("__findnonnull(array_of_pointers, -1)"), |
| IsError("passing in a buffer size ('-1') that is negative or in excess " |
| "of 100 million to __findnonnull() is not allowed.\n" |
| "__findnonnull(array_of_pointers, -1)\n" |
| " ^")); |
| EXPECT_THAT( |
| Eval("__findnonnull(array_of_pointers, 100000001)"), |
| IsError("passing in a buffer size ('100000001') that is negative " |
| "or in excess of 100 million to __findnonnull() is not allowed.\n" |
| "__findnonnull(array_of_pointers, 100000001)\n" |
| " ^")); |
| |
| if (process_.GetAddressByteSize() == 4) { |
| EXPECT_THAT(Eval("__findnonnull(array_of_uint8, 6)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_uint8+4, 5)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_uint8+8, 4)"), IsEqual("2")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_uint8+12, 3)"), IsEqual("1")); |
| } else if (process_.GetAddressByteSize() == 8) { |
| EXPECT_THAT(Eval("__findnonnull(array_of_uint8, 3)"), IsEqual("0")); |
| EXPECT_THAT(Eval("__findnonnull(array_of_uint8+8, 2)"), IsEqual("1")); |
| } else { |
| GTEST_FATAL_FAILURE_("Unknown pointer size in the target process"); |
| } |
| } |
| |
| TEST_F(EvalTest, TestUniquePtr) { |
| #ifdef _WIN32 |
| // On Windows we're not using `libc++` and therefore the layout of |
| // `std::unique_ptr` is different. |
| GTEST_SKIP() << "not supported on Windows"; |
| #else |
| // On Linux this assumes the usage of libc++ standard library. |
| |
| EXPECT_THAT(Eval("*(NodeU**)&ptr_node.__ptr_"), IsOk()); |
| EXPECT_THAT(Eval("(*(NodeU**)&ptr_node.__ptr_)->value"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("ptr_node.__ptr_.__value_"), IsOk()); |
| EXPECT_THAT(Eval("ptr_node.__ptr_.__value_->value"), IsEqual("1")); |
| EXPECT_THAT(Eval("ptr_node.__ptr_.__value_->next.__ptr_.__value_->value"), |
| IsEqual("2")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestUniquePtrDeref) { |
| #ifdef _WIN32 |
| // On Windows we're not using `libc++` and therefore the layout of |
| // `std::unique_ptr` is different. |
| GTEST_SKIP() << "not supported on Windows"; |
| #else |
| // On Linux this assumes the usage of libc++ standard library. |
| this->compare_with_lldb_ = false; |
| |
| // Test member-of dereference. |
| EXPECT_THAT(Eval("ptr_node->value"), IsEqual("1")); |
| EXPECT_THAT(Eval("ptr_node->next->value"), IsEqual("2")); |
| |
| // Test ptr dereference. |
| EXPECT_THAT(Eval("(*ptr_node).value"), IsEqual("1")); |
| EXPECT_THAT(Eval("(*(*ptr_node).next).value"), IsEqual("2")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestUniquePtrCompare) { |
| #ifdef _WIN32 |
| // On Windows we're not using `libc++` and therefore the layout of |
| // `std::unique_ptr` is different. |
| GTEST_SKIP() << "not supported on Windows"; |
| #else |
| // On Linux this assumes the usage of libc++ standard library. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("ptr_int == nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_int != nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_int == ptr_int"), IsEqual("true")); |
| |
| // C++ doesn't allow comparing unique_ptr with raw pointers, but we allow it |
| // for convenience. |
| EXPECT_THAT(Eval("ptr_int == 0"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_int == (int*)0"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("ptr_float == nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_float != nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_float == (float*)0"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("ptr_float == (int*)0"), |
| IsError("comparison of distinct pointer types")); |
| EXPECT_THAT(Eval("ptr_int == ptr_float"), |
| IsError("comparison of distinct pointer types")); |
| |
| EXPECT_THAT(Eval("ptr_null == nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_null != nullptr"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("ptr_void == nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_void != nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_void == ptr_void"), IsEqual("true")); |
| |
| // Void pointer can be compared with everything. |
| EXPECT_THAT(Eval("ptr_void == (int*)0"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_void == (void*)0"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_int == ptr_void"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_float == ptr_void"), IsEqual("false")); |
| |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestSharedPtr) { |
| #ifdef _WIN32 |
| // On Windows we're not using `libc++` and therefore the layout of |
| // `std::unique_ptr` is different. |
| GTEST_SKIP() << "not supported on Windows"; |
| #else |
| // On Linux this assumes the usage of libc++ standard library. |
| |
| EXPECT_THAT(Eval("*(NodeS**)&ptr_node.__ptr_"), IsOk()); |
| EXPECT_THAT(Eval("(*(NodeS**)&ptr_node.__ptr_)->value"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("ptr_node.__ptr_"), IsOk()); |
| EXPECT_THAT(Eval("ptr_node.__ptr_->value"), IsEqual("1")); |
| EXPECT_THAT(Eval("ptr_node.__ptr_->next.__ptr_->value"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("ptr_int.__ptr_"), IsOk()); |
| EXPECT_THAT(Eval("*ptr_int.__ptr_"), IsEqual("1")); |
| EXPECT_THAT(Eval("ptr_int_weak.__ptr_"), IsOk()); |
| EXPECT_THAT(Eval("*ptr_int_weak.__ptr_"), IsEqual("1")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestSharedPtrDeref) { |
| #ifdef _WIN32 |
| // On Windows we're not using `libc++` and therefore the layout of |
| // `std::shared_ptr` is different. |
| GTEST_SKIP() << "not supported on Windows"; |
| #else |
| // On Linux this assumes the usage of libc++ standard library. |
| this->compare_with_lldb_ = false; |
| |
| // Test member-of dereference. |
| EXPECT_THAT(Eval("ptr_node->value"), IsEqual("1")); |
| EXPECT_THAT(Eval("ptr_node->next->value"), IsEqual("2")); |
| |
| // Test ptr dereference. |
| EXPECT_THAT(Eval("(*ptr_node).value"), IsEqual("1")); |
| EXPECT_THAT(Eval("(*(*ptr_node).next).value"), IsEqual("2")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestSharedPtrCompare) { |
| #ifdef _WIN32 |
| // On Windows we're not using `libc++` and therefore the layout of |
| // `std::shared_ptr` is different. |
| GTEST_SKIP() << "not supported on Windows"; |
| #else |
| // On Linux this assumes the usage of libc++ standard library. |
| this->compare_with_lldb_ = false; |
| |
| EXPECT_THAT(Eval("ptr_int == nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_int != nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_int == ptr_int"), IsEqual("true")); |
| |
| // C++ doesn't allow comparing shared_ptr with raw pointers, but we allow it |
| // for convenience. |
| EXPECT_THAT(Eval("ptr_int == 0"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_int == (int*)0"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("ptr_float == nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_float != nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_float == (float*)0"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("ptr_float == (int*)0"), |
| IsError("comparison of distinct pointer types")); |
| EXPECT_THAT(Eval("ptr_int == ptr_float"), |
| IsError("comparison of distinct pointer types")); |
| |
| EXPECT_THAT(Eval("ptr_null == nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_null != nullptr"), IsEqual("false")); |
| |
| EXPECT_THAT(Eval("ptr_void == nullptr"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_void != nullptr"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_void == ptr_void"), IsEqual("true")); |
| |
| // Void pointer can be compared with everything. |
| EXPECT_THAT(Eval("ptr_void == (int*)0"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_void == (void*)0"), IsEqual("false")); |
| EXPECT_THAT(Eval("ptr_int == ptr_void"), IsEqual("true")); |
| EXPECT_THAT(Eval("ptr_float == ptr_void"), IsEqual("false")); |
| #endif |
| } |
| |
| TEST_F(EvalTest, TestTypeComparison) { |
| // This test is for border-case situations in the CompareTypes function. |
| |
| // Taking an address of ternary expression require operands of the same type. |
| EXPECT_THAT(Eval("&(true ? ip : icpc)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? mipp : ipp)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? ipp : icpcpc)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? ipp : mipp)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? ipp : micpcpc)"), IsOk()); |
| // TODO: Enable type comparison once the type mismatch is fixed. |
| // LLDB results in "int ***", while lldb-eval results in "MyInt ***". |
| EXPECT_THAT(Eval("&(true ? mipp : icpcpc)"), IsOk(/*compare_types*/ false)); |
| EXPECT_THAT(Eval("&(true ? mipp : micpcpc)"), IsOk(/*compare_types*/ false)); |
| EXPECT_THAT(Eval("&(true ? icpcpc : micpcpc)"), IsOk()); |
| |
| // Ensure that "signed char" and "char" are different types. |
| EXPECT_THAT(Eval("true ? c : sc"), IsEqual("2")); // int |
| EXPECT_THAT(Eval("true ? sc : (signed char)67"), IsEqual("'A'")); |
| EXPECT_THAT(Eval("true ? (char)66 : (signed char)65"), IsEqual("66")); |
| EXPECT_THAT(Eval("true ? cc : mc"), IsEqual("'B'")); |
| EXPECT_THAT(Eval("true ? cc : sc"), IsEqual("66")); |
| EXPECT_THAT(Eval("true ? sc : mc"), IsEqual("65")); |
| EXPECT_THAT(Eval("&(true ? c : c)"), IsOk()); |
| EXPECT_THAT(Eval("&(true ? c : sc)"), |
| IsError("cannot take the address of an rvalue of type 'int'")); |
| } |
| |
| TEST_F(EvalTest, TestTypeVsIdentifier) { |
| EXPECT_THAT(Eval("(StructOrVar)+1"), IsEqual("3")); |
| EXPECT_THAT(Eval("(StructOrVar)1"), |
| IsError("expected 'eof', got: <'1' (numeric_constant)>")); |
| EXPECT_THAT(Eval("(ClassOrVar.x - 1)"), IsEqual("2")); |
| EXPECT_THAT(Eval("(ClassOrVar).x - 1"), IsEqual("2")); |
| EXPECT_THAT( |
| Eval("(ClassOrVar)+1"), |
| IsError( |
| "invalid operands to binary expression ('ClassOrVar' and 'int')")); |
| EXPECT_THAT(Eval("(UnionOrVar)[1]"), IsEqual("2")); |
| EXPECT_THAT(Eval("*(UnionOrVar)"), IsEqual("1")); |
| EXPECT_THAT(Eval("(*UnionOrVar)"), IsEqual("1")); |
| |
| EXPECT_THAT(Eval("sizeof(StructOrVar)"), IsEqual("2")); |
| |
| EXPECT_THAT(Eval("static_cast<OnlyVar>(0)"), |
| IsError("unknown type name 'OnlyVar'")); |
| EXPECT_THAT(Eval("static_cast<StructOrVar>(0)"), |
| IsError("must use 'struct' tag to refer to type 'StructOrVar' in " |
| "this scope")); |
| EXPECT_THAT( |
| Eval("static_cast<ClassOrVar>(0)"), |
| IsError( |
| "must use 'class' tag to refer to type 'ClassOrVar' in this scope")); |
| EXPECT_THAT( |
| Eval("static_cast<UnionOrVar>(0)"), |
| IsError( |
| "must use 'union' tag to refer to type 'UnionOrVar' in this scope")); |
| EXPECT_THAT( |
| Eval("static_cast<EnumOrVar>(0)"), |
| IsError( |
| "must use 'enum' tag to refer to type 'EnumOrVar' in this scope")); |
| EXPECT_THAT( |
| Eval("static_cast<CxxEnumOrVar>(0)"), |
| IsError( |
| "must use 'enum' tag to refer to type 'CxxEnumOrVar' in this scope")); |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| TEST_F(EvalTest, TestSeparateParsing) { |
| lldb::SBError error; |
| |
| auto expr_a = Scope("a").Compile("a_", error); |
| ASSERT_TRUE(error.Success()); |
| |
| auto expr_b = Scope("b").Compile("b_", error); |
| ASSERT_TRUE(error.Success()); |
| |
| auto expr_c = Scope("c").Compile("a_ * b_ * c_", error); |
| ASSERT_TRUE(error.Success()); |
| |
| auto expr_d = Scope("d").Compile("a_ * b_ * c_ * d_", error); |
| ASSERT_TRUE(error.Success()); |
| |
| auto expr_c_this = Scope("c").Compile("this", error); |
| ASSERT_TRUE(error.Success()); |
| |
| EXPECT_THAT(Scope("a").Eval(expr_a), IsEqual("1")); |
| EXPECT_THAT(Scope("b").Eval(expr_b), IsEqual("2")); |
| EXPECT_THAT(Scope("c").Eval(expr_c), IsEqual("60")); |
| EXPECT_THAT(Scope("d").Eval(expr_d), IsEqual("3024")); |
| |
| EXPECT_THAT(Scope("c").Eval(expr_a), IsEqual("3")); |
| EXPECT_THAT(Scope("c").Eval(expr_b), IsEqual("4")); |
| EXPECT_THAT(Scope("d").Eval(expr_a), IsEqual("6")); |
| EXPECT_THAT(Scope("d").Eval(expr_b), IsEqual("7")); |
| EXPECT_THAT(Scope("d").Eval(expr_c), IsEqual("336")); |
| |
| // Expression parsed in derived-type scope, evaluated in base-type scope. |
| EXPECT_THAT( |
| Scope("c").Eval(expr_d), |
| IsError("expression isn't parsed in the context of compatible type")); |
| } |
| |
| TEST_F(EvalTest, TestSeparateParsingWithContextVars) { |
| ASSERT_TRUE(CreateContextVariable("$x", "1")); |
| ASSERT_TRUE(CreateContextVariable("$y", "2.5")); |
| |
| std::unordered_map<std::string, lldb::SBType> args = { |
| {"$x", vars_["$x"].GetType()}, {"$y", vars_["$y"].GetType()}}; |
| |
| lldb::SBError error; |
| |
| auto expr_c = Scope("c").CompileWithContext("c_ + $x + $y", args, error); |
| ASSERT_TRUE(error.Success()); |
| |
| EXPECT_THAT(Scope("c").EvalWithContext(expr_c, vars_), IsEqual("8.5")); |
| EXPECT_THAT(Scope("d").EvalWithContext(expr_c, vars_), IsEqual("11.5")); |
| |
| // Parsed context arguments don't match variables' types in evaluation. |
| std::unordered_map<std::string, lldb::SBValue> wrong_vars = { |
| {"$x", vars_["$y"]}, {"$y", vars_["$x"]}}; |
| EXPECT_THAT(Scope("c").EvalWithContext(expr_c, wrong_vars), |
| IsError("unexpected type of context variable '$x' (expected " |
| "'int', got 'double')")); |
| |
| // Context variable missing for parsed argument. |
| std::unordered_map<std::string, lldb::SBValue> incomplete_vars = { |
| {"$x", vars_["$x"]}}; |
| EXPECT_THAT(Scope("c").EvalWithContext(expr_c, incomplete_vars), |
| IsError("use of undeclared identifier '$y'")); |
| } |
| |
| TEST_F(EvalTest, TestRegisters) { |
| // LLDB loses the value formatter when evaluating registers and prints their |
| // value "as is". In lldb-eval the value formatter is preserved and the |
| // register can be "pretty-printed" depending on its type (e.g. vector |
| // registers are printed as vectors). |
| // Add an explicit cast so that the values are the same and can be compared. |
| EXPECT_THAT(Eval("(uint64_t) $rax"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rbx"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rcx"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rdx"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rdi"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rsi"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rbp"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $rsp"), IsOk()); |
| EXPECT_THAT(Eval("(uint64_t) $flags"), IsOk()); |
| } |
| |
| TEST_F(EvalTest, TestRegistersNoDollar) { |
| this->compare_with_lldb_ = false; |
| |
| // lldb-eval also supports accessing registers without `$`. Any other |
| // identifier (e.g. local variable) will shadow the register. |
| |
| // Shadowed by a local variable. |
| EXPECT_THAT(Eval("rax"), IsEqual("42")); |
| // Shadowed by a member field. |
| EXPECT_THAT(Eval("rbx"), IsEqual("42")); |
| // Shadowed by a global field. |
| EXPECT_THAT(Eval("rcx"), IsEqual("42")); |
| |
| // Not shadowed by anything. |
| for (std::string reg_name : {"rdx", "rdi", "rsi", "rbp", "rsp", "flags"}) { |
| lldb::SBValue reg = frame_.FindRegister(reg_name.c_str()); |
| EXPECT_THAT(Eval(reg_name), IsEqual(reg.GetValue())); |
| } |
| } |
| #endif |
| |
| TEST_F(EvalTest, TestCharParsing) { |
| EXPECT_THAT(Eval("1 + 'A'"), IsEqual("66")); |
| EXPECT_THAT(Eval("'B' - 'A'"), IsEqual("1")); |
| EXPECT_THAT(Eval("'A' == 'B'"), IsEqual("false")); |
| EXPECT_THAT(Eval("'A' == 'A'"), IsEqual("true")); |
| |
| // Following test fails detecting different results from lldb |
| // as u8 prefix is not handled in lldb |
| // EXPECT_THAT(Eval("1 + u8'A'"), IsEqual("66")); |
| // EXPECT_THAT(Eval("u8'B' - u8'A'"), IsEqual("1")); |
| // EXPECT_THAT(Eval("u8'A' == u8'B'"), IsEqual("false")); |
| // EXPECT_THAT(Eval("u8'A' == u8'A'"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("1 + u'A'"), IsEqual("66")); |
| EXPECT_THAT(Eval("u'B' - u'A'"), IsEqual("1")); |
| EXPECT_THAT(Eval("u'A' == u'B'"), IsEqual("false")); |
| EXPECT_THAT(Eval("u'A' == u'A'"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("1 + U'A'"), IsEqual("66")); |
| EXPECT_THAT(Eval("U'B' - U'A'"), IsEqual("1")); |
| EXPECT_THAT(Eval("U'A' == U'B'"), IsEqual("false")); |
| EXPECT_THAT(Eval("U'A' == U'A'"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("1 + L'A'"), IsEqual("66")); |
| EXPECT_THAT(Eval("L'B' - L'A'"), IsEqual("1")); |
| EXPECT_THAT(Eval("L'A' == L'B'"), IsEqual("false")); |
| EXPECT_THAT(Eval("L'A' == L'A'"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("1 + 'ABC'"), IsEqual("4276804")); |
| EXPECT_THAT(Eval("'ABD' - 'ABC'"), IsEqual("1")); |
| EXPECT_THAT(Eval("'ABC' == 'BCD'"), IsEqual("false")); |
| EXPECT_THAT(Eval("'ABC' == 'ABC'"), IsEqual("true")); |
| |
| EXPECT_THAT(Eval("U'ъ' == U'猫'"), IsEqual("false")); |
| EXPECT_THAT(Eval("U'ü' == U'ü'"), IsEqual("true")); |
| EXPECT_THAT(Eval("U'u' == U'ü'"), IsEqual("false")); |
| |
| // Multichar contants with more than 4 chars generate |
| // compiler warning in clang |
| // Only the rightmost 4 characters are considered |
| // to compute the constant's value |
| EXPECT_THAT(Eval("'Xabcd' == 'Yabcd'"), IsEqual("true")); |
| EXPECT_THAT(Eval("'abcdX' == 'abcdY'"), IsEqual("false")); |
| } |
| |
| #ifndef __EMSCRIPTEN__ |
| TEST_F(EvalTest, TestStringParsing) { |
| lldb::SBError ignore; |
| // Comparing is done manually (instead of using IsOk and IsEqual matchers). |
| // This is because `SBValue::GetValue()` returns a nullptr for array types. |
| { |
| auto result = Eval("\"abc\""); |
| EXPECT_TRUE(result.lldb_eval_error.Success()); |
| EXPECT_TRUE(result.lldb_eval_value.IsValid()); |
| auto data = result.lldb_eval_value.GetData(); |
| EXPECT_EQ(data.GetByteSize(), 4); |
| EXPECT_STREQ(data.GetString(ignore, 0), "abc"); |
| } |
| { |
| auto result = Eval("\"\""); |
| EXPECT_TRUE(result.lldb_eval_error.Success()); |
| EXPECT_TRUE(result.lldb_eval_value.IsValid()); |
| auto data = result.lldb_eval_value.GetData(); |
| EXPECT_EQ(data.GetByteSize(), 1); |
| EXPECT_STREQ(data.GetString(ignore, 0), ""); |
| } |
| { |
| auto result = Eval("u8\"abc\""); |
| EXPECT_TRUE(result.lldb_eval_value.IsValid()); |
| auto data = result.lldb_eval_value.GetData(); |
| EXPECT_EQ(data.GetByteSize(), 4); |
| EXPECT_STREQ(data.GetString(ignore, 0), "abc"); |
| } |
| { |
| auto result = Eval("u\"abc\""); |
| EXPECT_TRUE(result.lldb_eval_value.IsValid()); |
| auto data = result.lldb_eval_value.GetData(); |
| EXPECT_EQ(data.GetByteSize(), 8); |
| char16_t val[4]; |
| EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast<void*>(val), 8), 8); |
| EXPECT_EQ(val[0], u'a'); |
| EXPECT_EQ(val[1], u'b'); |
| EXPECT_EQ(val[2], u'c'); |
| EXPECT_EQ(val[3], 0); |
| } |
| { |
| auto result = Eval("U\"猫ъü\""); |
| EXPECT_TRUE(result.lldb_eval_value.IsValid()); |
| auto data = result.lldb_eval_value.GetData(); |
| EXPECT_EQ(data.GetByteSize(), 16); |
| char32_t val[4]; |
| EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast<void*>(val), 16), |
| 16); |
| EXPECT_EQ(val[0], U'猫'); |
| EXPECT_EQ(val[1], U'ъ'); |
| EXPECT_EQ(val[2], U'ü'); |
| EXPECT_EQ(val[3], 0); |
| } |
| { |
| auto result = Eval("L\"abc\""); |
| EXPECT_TRUE(result.lldb_eval_value.IsValid()); |
| auto data = result.lldb_eval_value.GetData(); |
| wchar_t val[4]; |
| #ifdef _WIN32 |
| // On Windows it holds sizeof(wchar_t) == 2. |
| EXPECT_EQ(data.GetByteSize(), 8); |
| EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast<void*>(val), 8), 8); |
| #else |
| EXPECT_EQ(data.GetByteSize(), 16); |
| EXPECT_EQ(data.ReadRawData(ignore, 0, reinterpret_cast<void*>(val), 16), |
| 16); |
| #endif |
| EXPECT_EQ(val[0], L'a'); |
| EXPECT_EQ(val[1], L'b'); |
| EXPECT_EQ(val[2], L'c'); |
| EXPECT_EQ(val[3], 0); |
| } |
| |
| // TODO: lldb-eval should be able to handle these cases once string-literals |
| // are properly supported. |
| EXPECT_THAT(Eval("\"abc\" \"def\""), |
| IsError("string literals are not supported")); |
| EXPECT_THAT(Eval("\"abc\" + 1"), |
| IsError("string literals are not supported")); |
| EXPECT_THAT(Eval("*\"abc\""), IsError("string literals are not supported")); |
| } |
| #endif |