Skip to content

[LLDB] Add DIL code for handling plain variable names. #120971

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
Apr 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c14a0e3
[LLDB] Add DIL code for handling plain variable names.
cmtice Dec 23, 2024
680f558
[LLDB] Add DIL code for handling plain variable names.
cmtice Jan 15, 2025
e21728a
[LLDB] Add DIL code for handling plain variable names.
cmtice Jan 15, 2025
e4e9cc4
[LLDB] Add DIL code for handling plain variable names.
cmtice Jan 15, 2025
fbeba18
Fix clang format issues.
cmtice Jan 15, 2025
aa0c721
[LLDB] Add DIL code for handling plain variable names.
cmtice Jan 20, 2025
d3a9ca6
[LLDB] Add DIL code for handling plain variable names.
cmtice Jan 20, 2025
c7eb5fd
Fix clang format issue in test.
cmtice Jan 20, 2025
9d881e2
Re-do some if-statements to facilitate early exits.
cmtice Jan 20, 2025
d2665f0
FIx clang-format issue.
cmtice Jan 20, 2025
fe8c0bf
Merge remote-tracking branch 'origin/main' into DIL-variables
cmtice Feb 5, 2025
b5cf8b2
Update code to match changes in the recently committed DILLexer.
cmtice Feb 8, 2025
dc9b456
Address many of the review comments:
cmtice Feb 24, 2025
06ff79b
Address remaining review comments:
cmtice Feb 28, 2025
5d592e3
Address some review comments:
cmtice Mar 12, 2025
33a9971
Update python tests to use expect_var_path where possible.
cmtice Mar 13, 2025
07d0d15
Update error message handling to use DiagnosticDetails.
cmtice Mar 13, 2025
42c52f3
Address/fix remaining reviewer comments:
cmtice Mar 16, 2025
08a918d
Fix format errors in Python tests.
cmtice Mar 16, 2025
27723db
Add static cast to fix buildkite error.
cmtice Mar 19, 2025
07b6a4f
Minor style & format cleanups; also update error handling.
cmtice Mar 20, 2025
eb4a375
Fix clang-format issues.
cmtice Mar 25, 2025
8b448ea
Code cleanups and reviewer suggestions around error handling.
cmtice Mar 27, 2025
179bc8c
Clean up error handling.
cmtice Apr 2, 2025
ebc9e0a
Remove unused overload of BailOut function.
cmtice Apr 2, 2025
49341ad
Use llvm::Error::success() to initialize llvm::Error (remove hack).
cmtice Apr 3, 2025
40bd61a
Remove unused arguments from IdentifierNode class.
cmtice Apr 3, 2025
1d9bb1c
Minor code cleanups requested by reviewer.
cmtice Apr 4, 2025
358d28a
Check for & rollback errors in TentativeParsingRollaback.
cmtice Apr 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions lldb/docs/dil-expr-lang.ebnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(* Data Inspection Language (DIL) definition - LLDB Debug Expressions *)

(* This is currently a subset of the final DIL Language, matching the current
DIL implementation. *)

expression = primary_expression ;

primary_expression = id_expression
| "(" expression ")";

id_expression = unqualified_id
| qualified_id
| register ;

unqualified_id = identifier ;

qualified_id = ["::"] [nested_name_specifier] unqualified_id
| ["::"] identifier ;

identifier = ? C99 Identifier ? ;

register = "$" ? Register name ? ;

nested_name_specifier = type_name "::"
| namespace_name '::'
| nested_name_specifier identifier "::" ;

type_name = class_name
| enum_name
| typedef_name;

class_name = identifier ;

enum_name = identifier ;

typedef_name = identifier ;

namespace_name = identifier ;




97 changes: 97 additions & 0 deletions lldb/include/lldb/ValueObject/DILAST.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===-- DILAST.h ------------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_VALUEOBJECT_DILAST_H
#define LLDB_VALUEOBJECT_DILAST_H

#include "lldb/ValueObject/ValueObject.h"
#include "llvm/Support/Error.h"
#include <cstdint>
#include <string>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <string>
#include <string>
#include <cstdint>
#include "llvm/Support/Error.h"


namespace lldb_private::dil {

/// The various types DIL AST nodes (used by the DIL parser).
enum class NodeKind {
eErrorNode,
eIdentifierNode,
};

/// Forward declaration, for use in DIL AST nodes. Definition is at the very
/// end of this file.
class Visitor;

/// The rest of the classes in this file, except for the Visitor class at the
/// very end, define all the types of AST nodes used by the DIL parser and
/// expression evaluator. The DIL parser parses the input string and creates
/// the AST parse tree from the AST nodes. The resulting AST node tree gets
/// passed to the DIL expression evaluator, which evaluates the DIL AST nodes
/// and creates/returns a ValueObjectSP containing the result.

/// Base class for AST nodes used by the Data Inspection Language (DIL) parser.
/// All of the specialized types of AST nodes inherit from this (virtual) base
/// class.
class ASTNode {
public:
ASTNode(uint32_t location, NodeKind kind)
: m_location(location), m_kind(kind) {}
virtual ~ASTNode() = default;

virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;

uint32_t GetLocation() const { return m_location; }
NodeKind GetKind() const { return m_kind; }

private:
uint32_t m_location;
const NodeKind m_kind;
};

using ASTNodeUP = std::unique_ptr<ASTNode>;

class ErrorNode : public ASTNode {
public:
ErrorNode() : ASTNode(0, NodeKind::eErrorNode) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;

static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eErrorNode;
}
};

class IdentifierNode : public ASTNode {
public:
IdentifierNode(uint32_t location, std::string name)
: ASTNode(location, NodeKind::eIdentifierNode), m_name(std::move(name)) {}

llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;

std::string GetName() const { return m_name; }

static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eIdentifierNode;
}

private:
std::string m_name;
};

/// 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
/// type of AST node.
class Visitor {
public:
virtual ~Visitor() = default;
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) = 0;
};

} // namespace lldb_private::dil

#endif // LLDB_VALUEOBJECT_DILAST_H
63 changes: 63 additions & 0 deletions lldb/include/lldb/ValueObject/DILEval.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//===-- DILEval.h -----------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_VALUEOBJECT_DILEVAL_H
#define LLDB_VALUEOBJECT_DILEVAL_H

#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILParser.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <vector>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#include <vector>
#include <vector>
#include "llvm/Support/StringRef.h"
#include "llvm/Support/Error.h"


namespace lldb_private::dil {

/// Given the name of an identifier (variable name, member name, type name,
/// etc.), find the ValueObject for that name (if it exists), excluding global
/// variables, and create and return an IdentifierInfo object containing all
/// the relevant information about that object (for DIL parsing and
/// evaluating).
lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic,
CompilerType *scope_ptr = nullptr);

/// Given the name of an identifier, check to see if it matches the name of a
/// global variable. If so, find the ValueObject for that global variable, and
/// create and return an IdentifierInfo object containing all the relevant
/// informatin about it.
lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
std::shared_ptr<StackFrame> frame_sp,
lldb::TargetSP target_sp,
lldb::DynamicValueType use_dynamic,
CompilerType *scope_ptr = nullptr);

class Interpreter : Visitor {
public:
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
lldb::DynamicValueType use_dynamic,
std::shared_ptr<StackFrame> frame_sp);

llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);

private:
llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) override;

// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
llvm::StringRef m_expr;
lldb::ValueObjectSP m_scope;
lldb::DynamicValueType m_default_dynamic;
std::shared_ptr<StackFrame> m_exe_ctx_scope;
};

} // namespace lldb_private::dil

#endif // LLDB_VALUEOBJECT_DILEVAL_H
27 changes: 23 additions & 4 deletions lldb/include/lldb/ValueObject/DILLexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <memory>
#include <string>
Expand Down Expand Up @@ -41,10 +42,8 @@ class Token {

bool IsNot(Kind kind) const { return m_kind != kind; }

bool IsOneOf(Kind kind1, Kind kind2) const { return Is(kind1) || Is(kind2); }

template <typename... Ts> bool IsOneOf(Kind kind, Ts... Ks) const {
return Is(kind) || IsOneOf(Ks...);
bool IsOneOf(llvm::ArrayRef<Kind> kinds) const {
return llvm::is_contained(kinds, m_kind);
}

uint32_t GetLocation() const { return m_start_pos; }
Expand Down Expand Up @@ -120,4 +119,24 @@ class DILLexer {

} // namespace lldb_private::dil

namespace llvm {

template <> struct format_provider<lldb_private::dil::Token::Kind> {
static void format(const lldb_private::dil::Token::Kind &k, raw_ostream &OS,
llvm::StringRef Options) {
OS << "'" << lldb_private::dil::Token::GetTokenName(k) << "'";
}
};

template <> struct format_provider<lldb_private::dil::Token> {
static void format(const lldb_private::dil::Token &t, raw_ostream &OS,
llvm::StringRef Options) {
lldb_private::dil::Token::Kind kind = t.GetKind();
OS << "<'" << t.GetSpelling() << "' ("
<< lldb_private::dil::Token::GetTokenName(kind) << ")>";
}
};

} // namespace llvm

#endif // LLDB_VALUEOBJECT_DILLEXER_H
125 changes: 125 additions & 0 deletions lldb/include/lldb/ValueObject/DILParser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//===-- DILParser.h ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLDB_VALUEOBJECT_DILPARSER_H
#define LLDB_VALUEOBJECT_DILPARSER_H

#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/Utility/Status.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILLexer.h"
#include "llvm/Support/Error.h"
#include <memory>
#include <optional>
#include <string>
#include <system_error>
#include <tuple>
#include <vector>

namespace lldb_private::dil {

enum class ErrorCode : unsigned char {
kOk = 0,
kInvalidExpressionSyntax,
kUndeclaredIdentifier,
kUnknown,
};

// The following is modeled on class OptionParseError.
class DILDiagnosticError
: public llvm::ErrorInfo<DILDiagnosticError, DiagnosticError> {
DiagnosticDetail m_detail;

public:
using llvm::ErrorInfo<DILDiagnosticError, DiagnosticError>::ErrorInfo;
DILDiagnosticError(DiagnosticDetail detail)
: ErrorInfo(make_error_code(std::errc::invalid_argument)),
m_detail(std::move(detail)) {}

DILDiagnosticError(llvm::StringRef expr, const std::string &message,
uint32_t loc, uint16_t err_len);

std::unique_ptr<CloneableError> Clone() const override {
return std::make_unique<DILDiagnosticError>(m_detail);
}

llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {
return {m_detail};
}

std::string message() const override { return m_detail.rendered; }
};

/// Pure recursive descent parser for C++ like expressions.
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
class DILParser {
public:
static llvm::Expected<ASTNodeUP> Parse(llvm::StringRef dil_input_expr,
DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic,
bool use_synthetic, bool fragile_ivar,
bool check_ptr_vs_member);

~DILParser() = default;

bool UseSynthetic() { return m_use_synthetic; }

lldb::DynamicValueType UseDynamic() { return m_use_dynamic; }

private:
explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member,
llvm::Error &error);

ASTNodeUP Run();

ASTNodeUP ParseExpression();
ASTNodeUP ParsePrimaryExpression();

std::string ParseNestedNameSpecifier();

std::string ParseIdExpression();
std::string ParseUnqualifiedId();

void BailOut(const std::string &error, uint32_t loc, uint16_t err_len);

void Expect(Token::Kind kind);

void TentativeParsingRollback(uint32_t saved_idx) {
if (m_error)
llvm::consumeError(std::move(m_error));
m_dil_lexer.ResetTokenIdx(saved_idx);
}

Token CurToken() { return m_dil_lexer.GetCurrentToken(); }

// Parser doesn't own the evaluation context. The produced AST may depend on
// it (for example, for source locations), so it's expected that expression
// context will outlive the parser.
std::shared_ptr<StackFrame> m_ctx_scope;

llvm::StringRef m_input_expr;

DILLexer m_dil_lexer;

// Holds an error if it occures during parsing.
llvm::Error &m_error;

lldb::DynamicValueType m_use_dynamic;
bool m_use_synthetic;
bool m_fragile_ivar;
bool m_check_ptr_vs_member;
}; // class DILParser

} // namespace lldb_private::dil

#endif // LLDB_VALUEOBJECT_DILPARSER_H
Loading