[Lldb-commits] [lldb] [LLDB] Add array subscription and integer parsing to DIL (PR #138551)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Thu May 8 09:46:16 PDT 2025
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/138551
>From cfe7359bd16c1e87932e2ebb8bcdfc88130e9729 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 30 Apr 2025 22:03:50 +0500
Subject: [PATCH 1/4] [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 at 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}));
}
}
>From df1ef931315375ba9e8c15c092f8548f24afc1ce Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 6 May 2025 23:07:12 +0500
Subject: [PATCH 2/4] Use `GetSyntheticArrayMember` to subscript pointers and
out of bounds index from arrays
---
lldb/include/lldb/ValueObject/DILEval.h | 2 -
lldb/source/ValueObject/DILEval.cpp | 71 +++++--------------------
2 files changed, 14 insertions(+), 59 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index e3df80862b082..a03b1a138798f 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -55,8 +55,6 @@ class Interpreter : Visitor {
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;
llvm::StringRef m_expr;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 527017da7c019..a3e1380ea1de4 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -18,22 +18,6 @@
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) {
@@ -327,21 +311,6 @@ 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());
@@ -373,22 +342,21 @@ Interpreter::Visit(const ArraySubscriptNode *node) {
m_expr, "array subscript is not an integer", node->GetLocation());
// Check to see if 'base' has a synthetic value; if so, try using that.
+ uint64_t child_idx = index->GetValueAsUnsigned(0);
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());
+ if (child_idx >= num_children) {
+ auto message = llvm::formatv(
+ "array index {0} is not valid for \"({1}) {2}\"", child_idx,
+ 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 =
@@ -410,25 +378,14 @@ Interpreter::Visit(const ArraySubscriptNode *node) {
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;
+ if (base_type.IsArrayType()) {
+ uint32_t num_children = base->GetNumChildrenIgnoringErrors();
+ if (child_idx < num_children)
+ return base->GetChildAtIndex(child_idx);
+ }
+
+ int64_t signed_child_idx = index->GetValueAsSigned(0);
+ return base->GetSyntheticArrayMember(signed_child_idx, true);
}
} // namespace lldb_private::dil
>From 05da91b5a9fa77570c5157068bc858f4359e6d3a Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 6 May 2025 23:22:47 +0500
Subject: [PATCH 3/4] Rename getters, refactor number lexing and remove '_'
---
lldb/include/lldb/ValueObject/DILAST.h | 8 ++++----
lldb/source/ValueObject/DILEval.cpp | 8 ++++----
lldb/source/ValueObject/DILLexer.cpp | 21 +++++---------------
lldb/unittests/ValueObject/DILLexerTests.cpp | 3 +--
4 files changed, 14 insertions(+), 26 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 6908deed7aee3..b6deff19a2f8b 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -118,8 +118,8 @@ class UnaryOpNode : public ASTNode {
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- UnaryOpKind kind() const { return m_kind; }
- ASTNode *operand() const { return m_operand.get(); }
+ UnaryOpKind GetKind() const { return m_kind; }
+ ASTNode *GetOperand() const { return m_operand.get(); }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eUnaryOpNode;
@@ -138,8 +138,8 @@ class ArraySubscriptNode : public ASTNode {
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- ASTNode *lhs() const { return m_lhs.get(); }
- ASTNode *rhs() const { return m_rhs.get(); }
+ ASTNode *GetLHS() const { return m_lhs.get(); }
+ ASTNode *GetRHS() const { return m_rhs.get(); }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eArraySubscriptNode;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index a3e1380ea1de4..604f9da777223 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -276,13 +276,13 @@ Interpreter::Visit(const IdentifierNode *node) {
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const UnaryOpNode *node) {
Status error;
- auto rhs_or_err = Evaluate(node->operand());
+ auto rhs_or_err = Evaluate(node->GetOperand());
if (!rhs_or_err)
return rhs_or_err;
lldb::ValueObjectSP rhs = *rhs_or_err;
- switch (node->kind()) {
+ switch (node->GetKind()) {
case UnaryOpKind::Deref: {
lldb::ValueObjectSP dynamic_rhs = rhs->GetDynamicValue(m_default_dynamic);
if (dynamic_rhs)
@@ -313,12 +313,12 @@ Interpreter::Visit(const UnaryOpNode *node) {
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const ArraySubscriptNode *node) {
- auto lhs_or_err = Evaluate(node->lhs());
+ auto lhs_or_err = Evaluate(node->GetLHS());
if (!lhs_or_err) {
return lhs_or_err;
}
lldb::ValueObjectSP base = *lhs_or_err;
- auto rhs_or_err = Evaluate(node->rhs());
+ auto rhs_or_err = Evaluate(node->GetRHS());
if (!rhs_or_err) {
return rhs_or_err;
}
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index 3222032feef19..6e41ff50bd571 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -64,25 +64,14 @@ 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 bool IsNumberBodyChar(char ch) { return IsDigit(ch) || IsLetter(ch); }
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;
+ if (IsDigit(remainder[0])) {
+ llvm::StringRef number = remainder.take_while(IsNumberBodyChar);
+ remainder = remainder.drop_front(number.size());
+ return number;
}
return std::nullopt;
}
diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp
index 203763b91afc4..f65034c1dbea3 100644
--- a/lldb/unittests/ValueObject/DILLexerTests.cpp
+++ b/lldb/unittests/ValueObject/DILLexerTests.cpp
@@ -157,8 +157,7 @@ TEST(DILLexerTests, IdentifiersTest) {
TEST(DILLexerTests, NumbersTest) {
// These strings should lex into number tokens.
- std::vector<std::string> valid_numbers = {"123", "0x123", "0123", "0b101",
- "1_000"};
+ std::vector<std::string> valid_numbers = {"123", "0x123", "0123", "0b101"};
// The lexer can lex these strings, but they should not be numbers.
std::vector<std::string> invalid_numbers = {"", "x123", "b123"};
>From b80eb87a21839da927806d0423ec15a9932f5367 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 8 May 2025 20:46:41 +0500
Subject: [PATCH 4/4] Remove scalar literal node, store subscript index as
APInt
---
lldb/docs/dil-expr-lang.ebnf | 7 +--
lldb/include/lldb/ValueObject/DILAST.h | 36 +++--------
lldb/include/lldb/ValueObject/DILEval.h | 2 -
lldb/include/lldb/ValueObject/DILParser.h | 3 +-
lldb/source/ValueObject/DILAST.cpp | 5 --
lldb/source/ValueObject/DILEval.cpp | 61 ++-----------------
lldb/source/ValueObject/DILParser.cpp | 61 ++++++-------------
.../TestFrameVarDILArraySubscript.py | 24 ++++----
8 files changed, 45 insertions(+), 154 deletions(-)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 0cbb5403785db..d54f65df15865 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -11,10 +11,9 @@ unary_expression = unary_operator expression
unary_operator = "*" | "&" ;
postfix_expression = primary_expression
- | postfix_expression "[" expression "]";
+ | postfix_expression "[" integer_literal "]";
-primary_expression = numeric_literal
- | id_expression
+primary_expression = id_expression
| "(" expression ")";
id_expression = unqualified_id
@@ -28,7 +27,7 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id
identifier = ? C99 Identifier ? ;
-numeric_literal = ? C99 Integer constant ? ;
+integer_literal = ? Integer constant: hexademical, decimal, octal, binary ? ;
register = "$" ? Register name ? ;
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index b6deff19a2f8b..7c8dfbfafdccc 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -73,26 +73,6 @@ 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)
@@ -132,22 +112,22 @@ class UnaryOpNode : public ASTNode {
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)) {}
+ ArraySubscriptNode(uint32_t location, ASTNodeUP base, llvm::APInt index)
+ : ASTNode(location, NodeKind::eArraySubscriptNode),
+ m_base(std::move(base)), m_index(std::move(index)) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- ASTNode *GetLHS() const { return m_lhs.get(); }
- ASTNode *GetRHS() const { return m_rhs.get(); }
+ ASTNode *GetBase() const { return m_base.get(); }
+ const llvm::APInt *GetIndex() const { return &m_index; }
static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eArraySubscriptNode;
}
private:
- ASTNodeUP m_lhs;
- ASTNodeUP m_rhs;
+ ASTNodeUP m_base;
+ llvm::APInt m_index;
};
/// This class contains one Visit method for each specialized type of
@@ -158,8 +138,6 @@ 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;
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index a03b1a138798f..03f869297c18f 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -47,8 +47,6 @@ 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;
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index af237ece0228d..50680b4c6abb5 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -91,8 +91,7 @@ class DILParser {
std::string ParseIdExpression();
std::string ParseUnqualifiedId();
- ASTNodeUP ParseNumericLiteral();
- ASTNodeUP ParseNumericConstant();
+ std::optional<llvm::APInt> ParseIntegerConstant();
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 ceb4a4aa99c4f..330b5a3f3c586 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -15,11 +15,6 @@ 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);
}
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 604f9da777223..76ca6723c36fe 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -214,45 +214,6 @@ 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;
@@ -313,16 +274,12 @@ Interpreter::Visit(const UnaryOpNode *node) {
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const ArraySubscriptNode *node) {
- auto lhs_or_err = Evaluate(node->GetLHS());
+ auto lhs_or_err = Evaluate(node->GetBase());
if (!lhs_or_err) {
return lhs_or_err;
}
lldb::ValueObjectSP base = *lhs_or_err;
- auto rhs_or_err = Evaluate(node->GetRHS());
- if (!rhs_or_err) {
- return rhs_or_err;
- }
- lldb::ValueObjectSP index = *rhs_or_err;
+ const llvm::APInt *index = node->GetIndex();
Status error;
if (base->GetCompilerType().IsReferenceType()) {
@@ -330,19 +287,9 @@ Interpreter::Visit(const ArraySubscriptNode *node) {
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.
- uint64_t child_idx = index->GetValueAsUnsigned(0);
+ uint64_t child_idx = index->getZExtValue();
if (base->HasSyntheticValue()) {
lldb::ValueObjectSP synthetic = base->GetSyntheticValue();
if (synthetic && synthetic != base) {
@@ -384,7 +331,7 @@ Interpreter::Visit(const ArraySubscriptNode *node) {
return base->GetChildAtIndex(child_idx);
}
- int64_t signed_child_idx = index->GetValueAsSigned(0);
+ int64_t signed_child_idx = index->getSExtValue();
return base->GetSyntheticArrayMember(signed_child_idx, true);
}
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 24a6f0c909a0a..cca2b483cb57e 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -118,7 +118,7 @@ ASTNodeUP DILParser::ParseUnaryExpression() {
//
// postfix_expression:
// primary_expression
-// postfix_expression "[" expression "]"
+// postfix_expression "[" integer_literal "]"
//
ASTNodeUP DILParser::ParsePostfixExpression() {
ASTNodeUP lhs = ParsePrimaryExpression();
@@ -128,11 +128,17 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
switch (token.GetKind()) {
case Token::l_square: {
m_dil_lexer.Advance();
- auto rhs = ParseExpression();
+ auto rhs = ParseIntegerConstant();
+ if (!rhs) {
+ BailOut(
+ llvm::formatv("failed to parse integer constant: {0}", CurToken()),
+ CurToken().GetLocation(), CurToken().GetSpelling().length());
+ return std::make_unique<ErrorNode>();
+ }
Expect(Token::r_square);
m_dil_lexer.Advance();
lhs = std::make_unique<ArraySubscriptNode>(loc, std::move(lhs),
- std::move(rhs));
+ std::move(*rhs));
break;
}
default:
@@ -150,8 +156,6 @@ ASTNodeUP DILParser::ParsePostfixExpression() {
// "(" 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();
@@ -311,50 +315,21 @@ void DILParser::BailOut(const std::string &error, uint32_t loc,
m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
-// Parse a numeric_literal.
+// Parse a integer_literal.
//
-// numeric_literal:
-// ? Token::numeric_constant ?
+// integer_literal:
+// ? Integer 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();
+std::optional<llvm::APInt> DILParser::ParseIntegerConstant() {
+ auto spelling = CurToken().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);
+ m_dil_lexer.Advance();
+ return raw_value;
}
- return std::make_unique<ErrorNode>();
+
+ return std::nullopt;
}
void DILParser::Expect(Token::Kind kind) {
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
index e142889124613..42e9e1de9f6f3 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/ArraySubscript/TestFrameVarDILArraySubscript.py
@@ -30,26 +30,26 @@ def test_dereference(self):
# 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")
+ self.expect("frame var 'int_arr[enum_one]'", error=True)
# 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("frame var 'int_arr[idx_1_ref]'", error=True)
+ self.expect("frame var 'int_arr[enum_ref]'", error=True)
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")
+ self.expect("frame var 'int_arr_ref[idx_1_ref]'", error=True)
+ self.expect("frame var 'int_arr_ref[enum_ref]'", error=True)
# 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("frame var 'td_int_arr[td_int_idx_1]'", error=True)
+ self.expect("frame var 'td_int_arr[td_td_int_idx_2]'", error=True)
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")
+ self.expect("frame var 'td_int_ptr[td_int_idx_1]'", error=True)
+ self.expect("frame var 'td_int_ptr[td_td_int_idx_2]'", error=True)
# Both typedefs and refs
- self.expect_var_path("td_int_arr_ref[td_int_idx_1_ref]", value="2")
+ self.expect("frame var 'td_int_arr_ref[td_int_idx_1_ref]'", error=True)
# Test for index out of bounds.
self.expect_var_path("int_arr[42]", True, type="int")
@@ -79,10 +79,10 @@ def test_dereference(self):
self.expect(
"frame var 'int_arr[int_ptr]'",
error=True,
- substrs=["array subscript is not an integer"],
+ substrs=["failed to parse integer constant"],
)
self.expect(
"frame var '1[2]'",
error=True,
- substrs=["subscripted value is not an array or pointer"],
+ substrs=["Unexpected token"],
)
More information about the lldb-commits
mailing list