https://github.com/kuilpd created https://github.com/llvm/llvm-project/pull/138551
None >From cfe7359bd16c1e87932e2ebb8bcdfc88130e9729 Mon Sep 17 00:00:00 2001 From: Ilia Kuklin <ikuk...@accesssoftek.com> Date: Wed, 30 Apr 2025 22:03:50 +0500 Subject: [PATCH] [LLDB] Add array subscription and integer parsing to DIL --- lldb/docs/dil-expr-lang.ebnf | 12 +- lldb/include/lldb/ValueObject/DILAST.h | 46 +++++ lldb/include/lldb/ValueObject/DILEval.h | 6 + lldb/include/lldb/ValueObject/DILLexer.h | 3 + lldb/include/lldb/ValueObject/DILParser.h | 3 + lldb/source/ValueObject/DILAST.cpp | 10 ++ lldb/source/ValueObject/DILEval.cpp | 159 ++++++++++++++++++ lldb/source/ValueObject/DILLexer.cpp | 43 ++++- lldb/source/ValueObject/DILParser.cpp | 79 ++++++++- .../var-dil/basics/ArraySubscript/Makefile | 3 + .../TestFrameVarDILArraySubscript.py | 88 ++++++++++ .../var-dil/basics/ArraySubscript/main.cpp | 31 ++++ lldb/unittests/ValueObject/DILLexerTests.cpp | 34 +++- 13 files changed, 506 insertions(+), 11 deletions(-) create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py create mode 100644 lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf index c8bf4231b3e4a..0cbb5403785db 100644 --- a/lldb/docs/dil-expr-lang.ebnf +++ b/lldb/docs/dil-expr-lang.ebnf @@ -6,16 +6,20 @@ expression = unary_expression ; unary_expression = unary_operator expression - | primary_expression ; + | postfix_expression ; unary_operator = "*" | "&" ; -primary_expression = id_expression +postfix_expression = primary_expression + | postfix_expression "[" expression "]"; + +primary_expression = numeric_literal + | id_expression | "(" expression ")"; id_expression = unqualified_id | qualified_id - | register ; + | register ; unqualified_id = identifier ; @@ -24,6 +28,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id identifier = ? C99 Identifier ? ; +numeric_literal = ? C99 Integer constant ? ; + register = "$" ? Register name ? ; nested_name_specifier = type_name "::" diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index fe3827ef0516a..6908deed7aee3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -18,8 +18,10 @@ namespace lldb_private::dil { /// The various types DIL AST nodes (used by the DIL parser). enum class NodeKind { + eArraySubscriptNode, eErrorNode, eIdentifierNode, + eScalarLiteralNode, eUnaryOpNode, }; @@ -71,6 +73,26 @@ class ErrorNode : public ASTNode { } }; +class ScalarLiteralNode : public ASTNode { +public: + ScalarLiteralNode(uint32_t location, lldb::BasicType type, Scalar value) + : ASTNode(location, NodeKind::eScalarLiteralNode), m_type(type), + m_value(value) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + lldb::BasicType GetType() const { return m_type; } + Scalar GetValue() const & { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eScalarLiteralNode; + } + +private: + lldb::BasicType m_type; + Scalar m_value; +}; + class IdentifierNode : public ASTNode { public: IdentifierNode(uint32_t location, std::string name) @@ -108,6 +130,26 @@ class UnaryOpNode : public ASTNode { ASTNodeUP m_operand; }; +class ArraySubscriptNode : public ASTNode { +public: + ArraySubscriptNode(uint32_t location, ASTNodeUP lhs, ASTNodeUP rhs) + : ASTNode(location, NodeKind::eArraySubscriptNode), m_lhs(std::move(lhs)), + m_rhs(std::move(rhs)) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + ASTNode *lhs() const { return m_lhs.get(); } + ASTNode *rhs() const { return m_rhs.get(); } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eArraySubscriptNode; + } + +private: + ASTNodeUP m_lhs; + ASTNodeUP m_rhs; +}; + /// This class contains one Visit method for each specialized type of /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to /// the correct function in the DIL expression evaluator for evaluating that @@ -116,9 +158,13 @@ class Visitor { public: virtual ~Visitor() = default; virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const ScalarLiteralNode *node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> Visit(const IdentifierNode *node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const ArraySubscriptNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index b1dd3fdb49739..e3df80862b082 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -47,9 +47,15 @@ class Interpreter : Visitor { llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node); private: + llvm::Expected<lldb::ValueObjectSP> + Visit(const ScalarLiteralNode *node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const IdentifierNode *node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const UnaryOpNode *node) override; + llvm::Expected<lldb::ValueObjectSP> + Visit(const ArraySubscriptNode *node) override; + + lldb::ValueObjectSP PointerAdd(lldb::ValueObjectSP lhs, int64_t offset); // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index 3508b8b7a85c6..0176db73835e9 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -29,7 +29,10 @@ class Token { eof, identifier, l_paren, + l_square, + numeric_constant, r_paren, + r_square, star, }; diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index f5c00b1040ef4..af237ece0228d 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -84,12 +84,15 @@ class DILParser { ASTNodeUP ParseExpression(); ASTNodeUP ParseUnaryExpression(); + ASTNodeUP ParsePostfixExpression(); ASTNodeUP ParsePrimaryExpression(); std::string ParseNestedNameSpecifier(); std::string ParseIdExpression(); std::string ParseUnqualifiedId(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseNumericConstant(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp index ea847587501ee..ceb4a4aa99c4f 100644 --- a/lldb/source/ValueObject/DILAST.cpp +++ b/lldb/source/ValueObject/DILAST.cpp @@ -15,6 +15,11 @@ llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const { llvm_unreachable("Attempting to Visit a DIL ErrorNode."); } +llvm::Expected<lldb::ValueObjectSP> +ScalarLiteralNode::Accept(Visitor *v) const { + return v->Visit(this); +} + llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const { return v->Visit(this); } @@ -23,4 +28,9 @@ llvm::Expected<lldb::ValueObjectSP> UnaryOpNode::Accept(Visitor *v) const { return v->Visit(this); } +llvm::Expected<lldb::ValueObjectSP> +ArraySubscriptNode::Accept(Visitor *v) const { + return v->Visit(this); +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp index 15a66d4866305..527017da7c019 100644 --- a/lldb/source/ValueObject/DILEval.cpp +++ b/lldb/source/ValueObject/DILEval.cpp @@ -18,6 +18,22 @@ namespace lldb_private::dil { +static lldb::ValueObjectSP +ArrayToPointerConversion(lldb::ValueObjectSP valobj, + std::shared_ptr<ExecutionContextScope> ctx) { + assert(valobj->IsArrayType() && + "an argument to array-to-pointer conversion must be an array"); + + uint64_t addr = valobj->GetLoadAddress(); + llvm::StringRef name = "result"; + ExecutionContext exe_ctx; + ctx->CalculateExecutionContext(exe_ctx); + return ValueObject::CreateValueObjectFromAddress( + name, addr, exe_ctx, + valobj->GetCompilerType().GetArrayElementType(ctx.get()).GetPointerType(), + /* do_deref */ false); +} + static lldb::ValueObjectSP LookupStaticIdentifier( VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope, llvm::StringRef name_ref, llvm::StringRef unqualified_name) { @@ -214,6 +230,45 @@ llvm::Expected<lldb::ValueObjectSP> Interpreter::Evaluate(const ASTNode *node) { return value_or_error; } +static CompilerType GetBasicType(std::shared_ptr<ExecutionContextScope> ctx, + lldb::BasicType basic_type) { + static std::unordered_map<lldb::BasicType, CompilerType> basic_types; + auto type = basic_types.find(basic_type); + if (type != basic_types.end()) { + std::string type_name((type->second).GetTypeName().AsCString()); + // Only return the found type if it's valid. + if (type_name != "<invalid>") + return type->second; + } + + lldb::TargetSP target_sp = ctx->CalculateTarget(); + if (target_sp) { + for (auto type_system_sp : target_sp->GetScratchTypeSystems()) + if (auto compiler_type = + type_system_sp->GetBasicTypeFromAST(basic_type)) { + basic_types.insert({basic_type, compiler_type}); + return compiler_type; + } + } + CompilerType empty_type; + return empty_type; +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const ScalarLiteralNode *node) { + CompilerType result_type = GetBasicType(m_exe_ctx_scope, node->GetType()); + Scalar value = node->GetValue(); + + if (result_type.IsInteger() || result_type.IsNullPtrType() || + result_type.IsPointerType()) { + llvm::APInt val = value.GetAPSInt(); + return ValueObject::CreateValueObjectFromAPInt(m_target, val, result_type, + "result"); + } + + return lldb::ValueObjectSP(); +} + llvm::Expected<lldb::ValueObjectSP> Interpreter::Visit(const IdentifierNode *node) { lldb::DynamicValueType use_dynamic = m_default_dynamic; @@ -272,4 +327,108 @@ Interpreter::Visit(const UnaryOpNode *node) { m_expr, "invalid ast: unexpected binary operator", node->GetLocation()); } +lldb::ValueObjectSP Interpreter::PointerAdd(lldb::ValueObjectSP lhs, + int64_t offset) { + uint64_t byte_size = 0; + if (auto temp = lhs->GetCompilerType().GetPointeeType().GetByteSize( + lhs->GetTargetSP().get())) + byte_size = *temp; + uintptr_t addr = lhs->GetValueAsUnsigned(0) + offset * byte_size; + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + return ValueObject::CreateValueObjectFromAddress(name, addr, exe_ctx, + lhs->GetCompilerType(), + /* do_deref */ false); +} + +llvm::Expected<lldb::ValueObjectSP> +Interpreter::Visit(const ArraySubscriptNode *node) { + auto lhs_or_err = Evaluate(node->lhs()); + if (!lhs_or_err) { + return lhs_or_err; + } + lldb::ValueObjectSP base = *lhs_or_err; + auto rhs_or_err = Evaluate(node->rhs()); + if (!rhs_or_err) { + return rhs_or_err; + } + lldb::ValueObjectSP index = *rhs_or_err; + + Status error; + if (base->GetCompilerType().IsReferenceType()) { + base = base->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + if (index->GetCompilerType().IsReferenceType()) { + index = index->Dereference(error); + if (error.Fail()) + return error.ToError(); + } + + auto index_type = index->GetCompilerType(); + if (!index_type.IsIntegerOrUnscopedEnumerationType()) + return llvm::make_error<DILDiagnosticError>( + m_expr, "array subscript is not an integer", node->GetLocation()); + + // Check to see if 'base' has a synthetic value; if so, try using that. + if (base->HasSyntheticValue()) { + lldb::ValueObjectSP synthetic = base->GetSyntheticValue(); + if (synthetic && synthetic != base) { + uint32_t num_children = synthetic->GetNumChildrenIgnoringErrors(); + // Verify that the 'index' is not out-of-range for the declared type. + if (index->GetValueAsSigned(0) >= num_children) { + auto message = + llvm::formatv("array index {0} is not valid for \"({1}) {2}\"", + index->GetValueAsSigned(0), + base->GetTypeName().AsCString("<invalid type>"), + base->GetName().AsCString()); + return llvm::make_error<DILDiagnosticError>(m_expr, message, + node->GetLocation()); + } + + uint64_t child_idx = index->GetValueAsUnsigned(0); + if (static_cast<uint32_t>(child_idx) < + synthetic->GetNumChildrenIgnoringErrors()) { + lldb::ValueObjectSP child_valobj_sp = + synthetic->GetChildAtIndex(child_idx); + if (child_valobj_sp) { + return child_valobj_sp; + } + } + } + } + + auto base_type = base->GetCompilerType(); + if (!base_type.IsPointerType() && !base_type.IsArrayType()) + return llvm::make_error<DILDiagnosticError>( + m_expr, "subscripted value is not an array or pointer", + node->GetLocation()); + if (base_type.IsPointerToVoid()) + return llvm::make_error<DILDiagnosticError>( + m_expr, "subscript of pointer to incomplete type 'void'", + node->GetLocation()); + + if (base_type.IsArrayType()) + base = ArrayToPointerConversion(base, m_exe_ctx_scope); + + CompilerType item_type = base->GetCompilerType().GetPointeeType(); + lldb::addr_t base_addr = base->GetValueAsUnsigned(0); + + llvm::StringRef name = "result"; + ExecutionContext exe_ctx(m_target.get(), false); + // Create a pointer and add the index, i.e. "base + index". + lldb::ValueObjectSP value = + PointerAdd(ValueObject::CreateValueObjectFromAddress( + name, base_addr, exe_ctx, item_type.GetPointerType(), + /*do_deref=*/false), + index->GetValueAsSigned(0)); + + lldb::ValueObjectSP val2 = value->Dereference(error); + if (error.Fail()) + return error.ToError(); + return val2; +} + } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp index b9c2e7971e3b4..3222032feef19 100644 --- a/lldb/source/ValueObject/DILLexer.cpp +++ b/lldb/source/ValueObject/DILLexer.cpp @@ -13,6 +13,7 @@ #include "lldb/ValueObject/DILLexer.h" #include "lldb/Utility/Status.h" +#include "lldb/ValueObject/DILParser.h" #include "llvm/ADT/StringSwitch.h" namespace lldb_private::dil { @@ -29,8 +30,14 @@ llvm::StringRef Token::GetTokenName(Kind kind) { return "identifier"; case Kind::l_paren: return "l_paren"; + case Kind::l_square: + return "l_square"; + case Kind::numeric_constant: + return "numeric_constant"; case Kind::r_paren: return "r_paren"; + case Kind::r_square: + return "r_square"; case Token::star: return "star"; } @@ -57,6 +64,29 @@ static std::optional<llvm::StringRef> IsWord(llvm::StringRef expr, return candidate; } +static void ConsumeNumberBody(char &prev_ch, llvm::StringRef::iterator &cur_pos, + llvm::StringRef expr) { + while (cur_pos != expr.end() && + (IsDigit(*cur_pos) || IsLetter(*cur_pos) || *cur_pos == '_')) { + prev_ch = *cur_pos; + cur_pos++; + } +} + +static std::optional<llvm::StringRef> IsNumber(llvm::StringRef expr, + llvm::StringRef &remainder) { + llvm::StringRef::iterator cur_pos = remainder.begin(); + llvm::StringRef::iterator start = cur_pos; + char prev_ch = 0; + if (IsDigit(*start)) { + ConsumeNumberBody(prev_ch, cur_pos, expr); + llvm::StringRef number = expr.substr(start - expr.begin(), cur_pos - start); + if (remainder.consume_front(number)) + return number; + } + return std::nullopt; +} + llvm::Expected<DILLexer> DILLexer::Create(llvm::StringRef expr) { std::vector<Token> tokens; llvm::StringRef remainder = expr; @@ -81,13 +111,19 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, return Token(Token::eof, "", (uint32_t)expr.size()); uint32_t position = cur_pos - expr.begin(); + std::optional<llvm::StringRef> maybe_number = IsNumber(expr, remainder); + if (maybe_number) { + std::string number = (*maybe_number).str(); + return Token(Token::numeric_constant, number, position); + } std::optional<llvm::StringRef> maybe_word = IsWord(expr, remainder); if (maybe_word) return Token(Token::identifier, maybe_word->str(), position); constexpr std::pair<Token::Kind, const char *> operators[] = { - {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, - {Token::r_paren, ")"}, {Token::star, "*"}, + {Token::amp, "&"}, {Token::coloncolon, "::"}, {Token::l_paren, "("}, + {Token::l_square, "["}, {Token::r_paren, ")"}, {Token::r_square, "]"}, + {Token::star, "*"}, }; for (auto [kind, str] : operators) { if (remainder.consume_front(str)) @@ -95,7 +131,8 @@ llvm::Expected<Token> DILLexer::Lex(llvm::StringRef expr, } // Unrecognized character(s) in string; unable to lex it. - return llvm::createStringError("Unable to lex input string"); + return llvm::make_error<DILDiagnosticError>(expr, "unrecognized token", + position); } } // namespace lldb_private::dil diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp index 2c78eae8cf6bf..24a6f0c909a0a 100644 --- a/lldb/source/ValueObject/DILParser.cpp +++ b/lldb/source/ValueObject/DILParser.cpp @@ -111,7 +111,36 @@ ASTNodeUP DILParser::ParseUnaryExpression() { llvm_unreachable("invalid token kind"); } } - return ParsePrimaryExpression(); + return ParsePostfixExpression(); +} + +// Parse a postfix_expression. +// +// postfix_expression: +// primary_expression +// postfix_expression "[" expression "]" +// +ASTNodeUP DILParser::ParsePostfixExpression() { + ASTNodeUP lhs = ParsePrimaryExpression(); + while (CurToken().Is(Token::l_square)) { + uint32_t loc = CurToken().GetLocation(); + Token token = CurToken(); + switch (token.GetKind()) { + case Token::l_square: { + m_dil_lexer.Advance(); + auto rhs = ParseExpression(); + Expect(Token::r_square); + m_dil_lexer.Advance(); + lhs = std::make_unique<ArraySubscriptNode>(loc, std::move(lhs), + std::move(rhs)); + break; + } + default: + llvm_unreachable("invalid token"); + } + } + + return lhs; } // Parse a primary_expression. @@ -121,6 +150,8 @@ ASTNodeUP DILParser::ParseUnaryExpression() { // "(" expression ")" // ASTNodeUP DILParser::ParsePrimaryExpression() { + if (CurToken().Is(Token::numeric_constant)) + return ParseNumericLiteral(); if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) { // Save the source location for the diagnostics message. uint32_t loc = CurToken().GetLocation(); @@ -280,6 +311,52 @@ void DILParser::BailOut(const std::string &error, uint32_t loc, m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1); } +// Parse a numeric_literal. +// +// numeric_literal: +// ? Token::numeric_constant ? +// +ASTNodeUP DILParser::ParseNumericLiteral() { + Expect(Token::numeric_constant); + ASTNodeUP numeric_constant = ParseNumericConstant(); + if (numeric_constant->GetKind() == NodeKind::eErrorNode) { + BailOut(llvm::formatv("Failed to parse token as numeric-constant: {0}", + CurToken()), + CurToken().GetLocation(), CurToken().GetSpelling().length()); + return std::make_unique<ErrorNode>(); + } + m_dil_lexer.Advance(); + return numeric_constant; +} + +static constexpr std::pair<const char *, lldb::BasicType> type_suffixes[] = { + {"ull", lldb::eBasicTypeUnsignedLongLong}, + {"ul", lldb::eBasicTypeUnsignedLong}, + {"u", lldb::eBasicTypeUnsignedInt}, + {"ll", lldb::eBasicTypeLongLong}, + {"l", lldb::eBasicTypeLong}, +}; + +ASTNodeUP DILParser::ParseNumericConstant() { + Token token = CurToken(); + auto spelling = token.GetSpelling(); + llvm::StringRef spelling_ref = spelling; + lldb::BasicType type = lldb::eBasicTypeInt; + for (auto [suffix, t] : type_suffixes) { + if (spelling_ref.consume_back_insensitive(suffix)) { + type = t; + break; + } + } + llvm::APInt raw_value; + if (!spelling_ref.getAsInteger(0, raw_value)) { + Scalar scalar_value(raw_value); + return std::make_unique<ScalarLiteralNode>(token.GetLocation(), type, + scalar_value); + } + return std::make_unique<ErrorNode>(); +} + void DILParser::Expect(Token::Kind kind) { if (CurToken().IsNot(kind)) { BailOut(llvm::formatv("expected {0}, got: {1}", kind, CurToken()), diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile new file mode 100644 index 0000000000000..99998b20bcb05 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp + +include Makefile.rules diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py new file mode 100644 index 0000000000000..e142889124613 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py @@ -0,0 +1,88 @@ +""" +Test DIL array subscript. +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * +from lldbsuite.test import lldbutil + + +class TestFrameVarDILGlobalVariableLookup(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + def expect_var_path(self, expr, compare_to_framevar=False, value=None, type=None): + value_dil = super().expect_var_path(expr, value=value, type=type) + if compare_to_framevar: + self.runCmd("settings set target.experimental.use-DIL false") + value_frv = super().expect_var_path(expr, value=value, type=type) + self.runCmd("settings set target.experimental.use-DIL true") + self.assertEqual(value_dil.GetValue(), value_frv.GetValue()) + + def test_dereference(self): + self.build() + lldbutil.run_to_source_breakpoint( + self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp") + ) + + self.runCmd("settings set target.experimental.use-DIL true") + + # Test int[] and int* + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_ptr[1]", True, value="2") + self.expect_var_path("int_arr[enum_one]", value="2") + + # Test when base and index are references. + self.expect_var_path("int_arr[0]", True, value="1") + self.expect_var_path("int_arr[idx_1_ref]", value="2") + self.expect_var_path("int_arr[enum_ref]", value="2") + self.expect_var_path("int_arr_ref[0]", value="1") + self.expect_var_path("int_arr_ref[idx_1_ref]", value="2") + self.expect_var_path("int_arr_ref[enum_ref]", value="2") + + # Test when base and index are typedefs. + self.expect_var_path("td_int_arr[0]", True, value="1") + self.expect_var_path("td_int_arr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_arr[td_td_int_idx_2]", value="3") + self.expect_var_path("td_int_ptr[0]", True, value="1") + self.expect_var_path("td_int_ptr[td_int_idx_1]", value="2") + self.expect_var_path("td_int_ptr[td_td_int_idx_2]", value="3") + + # Both typedefs and refs + self.expect_var_path("td_int_arr_ref[td_int_idx_1_ref]", value="2") + + # Test for index out of bounds. + self.expect_var_path("int_arr[42]", True, type="int") + self.expect_var_path("int_arr[100]", True, type="int") + + # Test address-of of the subscripted value. + self.expect_var_path("*(&int_arr[1])", value="2") + + # Test synthetic value subscription + self.expect_var_path("vector[1]", value="2") + + # Test for negative index. + self.expect( + "frame var 'int_arr[-1]'", + error=True, + substrs=["unrecognized token"], + ) + + # Test for floating point index + self.expect( + "frame var 'int_arr[1.0]'", + error=True, + substrs=["unrecognized token"], + ) + + # Base should be a "pointer to T" and index should be of an integral type. + self.expect( + "frame var 'int_arr[int_ptr]'", + error=True, + substrs=["array subscript is not an integer"], + ) + self.expect( + "frame var '1[2]'", + error=True, + substrs=["subscripted value is not an array or pointer"], + ) diff --git a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp new file mode 100644 index 0000000000000..b34e4670b9db6 --- /dev/null +++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/main.cpp @@ -0,0 +1,31 @@ +#include <vector> + +int main(int argc, char **argv) { + int int_arr[] = {1, 2, 3}; + int *int_ptr = int_arr; + int(&int_arr_ref)[3] = int_arr; + + int idx_1 = 1; + const int &idx_1_ref = idx_1; + + typedef int td_int_t; + typedef td_int_t td_td_int_t; + typedef int *td_int_ptr_t; + typedef int &td_int_ref_t; + + td_int_t td_int_idx_1 = 1; + td_td_int_t td_td_int_idx_2 = 2; + + td_int_t td_int_arr[3] = {1, 2, 3}; + td_int_ptr_t td_int_ptr = td_int_arr; + + td_int_ref_t td_int_idx_1_ref = td_int_idx_1; + td_int_t(&td_int_arr_ref)[3] = td_int_arr; + + enum Enum { kZero, kOne } enum_one = kOne; + Enum &enum_ref = enum_one; + + std::vector<int> vector = {1, 2, 3}; + + return 0; // Set a breakpoint here +} diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp index 9afa957901ae7..203763b91afc4 100644 --- a/lldb/unittests/ValueObject/DILLexerTests.cpp +++ b/lldb/unittests/ValueObject/DILLexerTests.cpp @@ -121,11 +121,11 @@ TEST(DILLexerTests, IdentifiersTest) { "a_b", "this", "self", "a", "MyName", "namespace"}; // The lexer can lex these strings, but they should not be identifiers. - std::vector<std::string> invalid_identifiers = {"", "::", "(", ")"}; + std::vector<std::string> invalid_identifiers = {"", "::", "(", ")", "0abc"}; // The lexer is expected to fail attempting to lex these strings (it cannot // create valid tokens out of them). - std::vector<std::string> invalid_tok_strings = {"234", "2a", "2", "1MyName"}; + std::vector<std::string> invalid_tok_strings = {"#include", "a@a"}; // Verify that all of the valid identifiers come out as identifier tokens. for (auto &str : valid_identifiers) { @@ -150,7 +150,33 @@ TEST(DILLexerTests, IdentifiersTest) { DILLexer lexer(*maybe_lexer); Token token = lexer.GetCurrentToken(); EXPECT_TRUE(token.IsNot(Token::identifier)); - EXPECT_TRUE(token.IsOneOf( - {Token::eof, Token::coloncolon, Token::l_paren, Token::r_paren})); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::coloncolon, Token::l_paren, + Token::r_paren, Token::numeric_constant})); + } +} + +TEST(DILLexerTests, NumbersTest) { + // These strings should lex into number tokens. + std::vector<std::string> valid_numbers = {"123", "0x123", "0123", "0b101", + "1_000"}; + + // The lexer can lex these strings, but they should not be numbers. + std::vector<std::string> invalid_numbers = {"", "x123", "b123"}; + + for (auto &str : valid_numbers) { + SCOPED_TRACE(str); + EXPECT_THAT_EXPECTED(ExtractTokenData(str), + llvm::HasValue(testing::ElementsAre( + testing::Pair(Token::numeric_constant, str)))); + } + // Verify that none of the invalid numbers come out as numeric tokens. + for (auto &str : invalid_numbers) { + SCOPED_TRACE(str); + llvm::Expected<DILLexer> maybe_lexer = DILLexer::Create(str); + EXPECT_THAT_EXPECTED(maybe_lexer, llvm::Succeeded()); + DILLexer lexer(*maybe_lexer); + Token token = lexer.GetCurrentToken(); + EXPECT_TRUE(token.IsNot(Token::numeric_constant)); + EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier})); } } _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits