Skip to content

Commit 46e2c07

Browse files
authored
[LLDB] Add DIL code for handling plain variable names. (#120971)
Add the Data Inspection Language (DIL) implementation pieces for handling plain local and global variable names. See https://discourse.llvm.org/t/rfc-data-inspection-language/69893 for information about DIL. This change includes the basic AST, Lexer, Parser and Evaluator pieces, as well as some tests.
1 parent 6333f84 commit 46e2c07

File tree

20 files changed

+1081
-13
lines changed

20 files changed

+1081
-13
lines changed

lldb/docs/dil-expr-lang.ebnf

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
(* Data Inspection Language (DIL) definition - LLDB Debug Expressions *)
2+
3+
(* This is currently a subset of the final DIL Language, matching the current
4+
DIL implementation. *)
5+
6+
expression = primary_expression ;
7+
8+
primary_expression = id_expression
9+
| "(" expression ")";
10+
11+
id_expression = unqualified_id
12+
| qualified_id
13+
| register ;
14+
15+
unqualified_id = identifier ;
16+
17+
qualified_id = ["::"] [nested_name_specifier] unqualified_id
18+
| ["::"] identifier ;
19+
20+
identifier = ? C99 Identifier ? ;
21+
22+
register = "$" ? Register name ? ;
23+
24+
nested_name_specifier = type_name "::"
25+
| namespace_name '::'
26+
| nested_name_specifier identifier "::" ;
27+
28+
type_name = class_name
29+
| enum_name
30+
| typedef_name;
31+
32+
class_name = identifier ;
33+
34+
enum_name = identifier ;
35+
36+
typedef_name = identifier ;
37+
38+
namespace_name = identifier ;
39+
40+
41+
42+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//===-- DILAST.h ------------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_VALUEOBJECT_DILAST_H
10+
#define LLDB_VALUEOBJECT_DILAST_H
11+
12+
#include "lldb/ValueObject/ValueObject.h"
13+
#include "llvm/Support/Error.h"
14+
#include <cstdint>
15+
#include <string>
16+
17+
namespace lldb_private::dil {
18+
19+
/// The various types DIL AST nodes (used by the DIL parser).
20+
enum class NodeKind {
21+
eErrorNode,
22+
eIdentifierNode,
23+
};
24+
25+
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
26+
/// end of this file.
27+
class Visitor;
28+
29+
/// The rest of the classes in this file, except for the Visitor class at the
30+
/// very end, define all the types of AST nodes used by the DIL parser and
31+
/// expression evaluator. The DIL parser parses the input string and creates
32+
/// the AST parse tree from the AST nodes. The resulting AST node tree gets
33+
/// passed to the DIL expression evaluator, which evaluates the DIL AST nodes
34+
/// and creates/returns a ValueObjectSP containing the result.
35+
36+
/// Base class for AST nodes used by the Data Inspection Language (DIL) parser.
37+
/// All of the specialized types of AST nodes inherit from this (virtual) base
38+
/// class.
39+
class ASTNode {
40+
public:
41+
ASTNode(uint32_t location, NodeKind kind)
42+
: m_location(location), m_kind(kind) {}
43+
virtual ~ASTNode() = default;
44+
45+
virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
46+
47+
uint32_t GetLocation() const { return m_location; }
48+
NodeKind GetKind() const { return m_kind; }
49+
50+
private:
51+
uint32_t m_location;
52+
const NodeKind m_kind;
53+
};
54+
55+
using ASTNodeUP = std::unique_ptr<ASTNode>;
56+
57+
class ErrorNode : public ASTNode {
58+
public:
59+
ErrorNode() : ASTNode(0, NodeKind::eErrorNode) {}
60+
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
61+
62+
static bool classof(const ASTNode *node) {
63+
return node->GetKind() == NodeKind::eErrorNode;
64+
}
65+
};
66+
67+
class IdentifierNode : public ASTNode {
68+
public:
69+
IdentifierNode(uint32_t location, std::string name)
70+
: ASTNode(location, NodeKind::eIdentifierNode), m_name(std::move(name)) {}
71+
72+
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
73+
74+
std::string GetName() const { return m_name; }
75+
76+
static bool classof(const ASTNode *node) {
77+
return node->GetKind() == NodeKind::eIdentifierNode;
78+
}
79+
80+
private:
81+
std::string m_name;
82+
};
83+
84+
/// This class contains one Visit method for each specialized type of
85+
/// DIL AST node. The Visit methods are used to dispatch a DIL AST node to
86+
/// the correct function in the DIL expression evaluator for evaluating that
87+
/// type of AST node.
88+
class Visitor {
89+
public:
90+
virtual ~Visitor() = default;
91+
virtual llvm::Expected<lldb::ValueObjectSP>
92+
Visit(const IdentifierNode *node) = 0;
93+
};
94+
95+
} // namespace lldb_private::dil
96+
97+
#endif // LLDB_VALUEOBJECT_DILAST_H
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//===-- DILEval.h -----------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_VALUEOBJECT_DILEVAL_H
10+
#define LLDB_VALUEOBJECT_DILEVAL_H
11+
12+
#include "lldb/ValueObject/DILAST.h"
13+
#include "lldb/ValueObject/DILParser.h"
14+
#include "llvm/ADT/StringRef.h"
15+
#include "llvm/Support/Error.h"
16+
#include <memory>
17+
#include <vector>
18+
19+
namespace lldb_private::dil {
20+
21+
/// Given the name of an identifier (variable name, member name, type name,
22+
/// etc.), find the ValueObject for that name (if it exists), excluding global
23+
/// variables, and create and return an IdentifierInfo object containing all
24+
/// the relevant information about that object (for DIL parsing and
25+
/// evaluating).
26+
lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
27+
std::shared_ptr<StackFrame> frame_sp,
28+
lldb::DynamicValueType use_dynamic,
29+
CompilerType *scope_ptr = nullptr);
30+
31+
/// Given the name of an identifier, check to see if it matches the name of a
32+
/// global variable. If so, find the ValueObject for that global variable, and
33+
/// create and return an IdentifierInfo object containing all the relevant
34+
/// informatin about it.
35+
lldb::ValueObjectSP LookupGlobalIdentifier(llvm::StringRef name_ref,
36+
std::shared_ptr<StackFrame> frame_sp,
37+
lldb::TargetSP target_sp,
38+
lldb::DynamicValueType use_dynamic,
39+
CompilerType *scope_ptr = nullptr);
40+
41+
class Interpreter : Visitor {
42+
public:
43+
Interpreter(lldb::TargetSP target, llvm::StringRef expr,
44+
lldb::DynamicValueType use_dynamic,
45+
std::shared_ptr<StackFrame> frame_sp);
46+
47+
llvm::Expected<lldb::ValueObjectSP> Evaluate(const ASTNode *node);
48+
49+
private:
50+
llvm::Expected<lldb::ValueObjectSP>
51+
Visit(const IdentifierNode *node) override;
52+
53+
// Used by the interpreter to create objects, perform casts, etc.
54+
lldb::TargetSP m_target;
55+
llvm::StringRef m_expr;
56+
lldb::ValueObjectSP m_scope;
57+
lldb::DynamicValueType m_default_dynamic;
58+
std::shared_ptr<StackFrame> m_exe_ctx_scope;
59+
};
60+
61+
} // namespace lldb_private::dil
62+
63+
#endif // LLDB_VALUEOBJECT_DILEVAL_H

lldb/include/lldb/ValueObject/DILLexer.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "llvm/ADT/StringRef.h"
1313
#include "llvm/Support/Error.h"
14+
#include "llvm/Support/FormatVariadic.h"
1415
#include <cstdint>
1516
#include <memory>
1617
#include <string>
@@ -41,10 +42,8 @@ class Token {
4142

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

44-
bool IsOneOf(Kind kind1, Kind kind2) const { return Is(kind1) || Is(kind2); }
45-
46-
template <typename... Ts> bool IsOneOf(Kind kind, Ts... Ks) const {
47-
return Is(kind) || IsOneOf(Ks...);
45+
bool IsOneOf(llvm::ArrayRef<Kind> kinds) const {
46+
return llvm::is_contained(kinds, m_kind);
4847
}
4948

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

121120
} // namespace lldb_private::dil
122121

122+
namespace llvm {
123+
124+
template <> struct format_provider<lldb_private::dil::Token::Kind> {
125+
static void format(const lldb_private::dil::Token::Kind &k, raw_ostream &OS,
126+
llvm::StringRef Options) {
127+
OS << "'" << lldb_private::dil::Token::GetTokenName(k) << "'";
128+
}
129+
};
130+
131+
template <> struct format_provider<lldb_private::dil::Token> {
132+
static void format(const lldb_private::dil::Token &t, raw_ostream &OS,
133+
llvm::StringRef Options) {
134+
lldb_private::dil::Token::Kind kind = t.GetKind();
135+
OS << "<'" << t.GetSpelling() << "' ("
136+
<< lldb_private::dil::Token::GetTokenName(kind) << ")>";
137+
}
138+
};
139+
140+
} // namespace llvm
141+
123142
#endif // LLDB_VALUEOBJECT_DILLEXER_H
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
//===-- DILParser.h ---------------------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_VALUEOBJECT_DILPARSER_H
10+
#define LLDB_VALUEOBJECT_DILPARSER_H
11+
12+
#include "lldb/Target/ExecutionContextScope.h"
13+
#include "lldb/Utility/DiagnosticsRendering.h"
14+
#include "lldb/Utility/Status.h"
15+
#include "lldb/ValueObject/DILAST.h"
16+
#include "lldb/ValueObject/DILLexer.h"
17+
#include "llvm/Support/Error.h"
18+
#include <memory>
19+
#include <optional>
20+
#include <string>
21+
#include <system_error>
22+
#include <tuple>
23+
#include <vector>
24+
25+
namespace lldb_private::dil {
26+
27+
enum class ErrorCode : unsigned char {
28+
kOk = 0,
29+
kInvalidExpressionSyntax,
30+
kUndeclaredIdentifier,
31+
kUnknown,
32+
};
33+
34+
// The following is modeled on class OptionParseError.
35+
class DILDiagnosticError
36+
: public llvm::ErrorInfo<DILDiagnosticError, DiagnosticError> {
37+
DiagnosticDetail m_detail;
38+
39+
public:
40+
using llvm::ErrorInfo<DILDiagnosticError, DiagnosticError>::ErrorInfo;
41+
DILDiagnosticError(DiagnosticDetail detail)
42+
: ErrorInfo(make_error_code(std::errc::invalid_argument)),
43+
m_detail(std::move(detail)) {}
44+
45+
DILDiagnosticError(llvm::StringRef expr, const std::string &message,
46+
uint32_t loc, uint16_t err_len);
47+
48+
std::unique_ptr<CloneableError> Clone() const override {
49+
return std::make_unique<DILDiagnosticError>(m_detail);
50+
}
51+
52+
llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {
53+
return {m_detail};
54+
}
55+
56+
std::string message() const override { return m_detail.rendered; }
57+
};
58+
59+
/// Pure recursive descent parser for C++ like expressions.
60+
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
61+
class DILParser {
62+
public:
63+
static llvm::Expected<ASTNodeUP> Parse(llvm::StringRef dil_input_expr,
64+
DILLexer lexer,
65+
std::shared_ptr<StackFrame> frame_sp,
66+
lldb::DynamicValueType use_dynamic,
67+
bool use_synthetic, bool fragile_ivar,
68+
bool check_ptr_vs_member);
69+
70+
~DILParser() = default;
71+
72+
bool UseSynthetic() { return m_use_synthetic; }
73+
74+
lldb::DynamicValueType UseDynamic() { return m_use_dynamic; }
75+
76+
private:
77+
explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
78+
std::shared_ptr<StackFrame> frame_sp,
79+
lldb::DynamicValueType use_dynamic, bool use_synthetic,
80+
bool fragile_ivar, bool check_ptr_vs_member,
81+
llvm::Error &error);
82+
83+
ASTNodeUP Run();
84+
85+
ASTNodeUP ParseExpression();
86+
ASTNodeUP ParsePrimaryExpression();
87+
88+
std::string ParseNestedNameSpecifier();
89+
90+
std::string ParseIdExpression();
91+
std::string ParseUnqualifiedId();
92+
93+
void BailOut(const std::string &error, uint32_t loc, uint16_t err_len);
94+
95+
void Expect(Token::Kind kind);
96+
97+
void TentativeParsingRollback(uint32_t saved_idx) {
98+
if (m_error)
99+
llvm::consumeError(std::move(m_error));
100+
m_dil_lexer.ResetTokenIdx(saved_idx);
101+
}
102+
103+
Token CurToken() { return m_dil_lexer.GetCurrentToken(); }
104+
105+
// Parser doesn't own the evaluation context. The produced AST may depend on
106+
// it (for example, for source locations), so it's expected that expression
107+
// context will outlive the parser.
108+
std::shared_ptr<StackFrame> m_ctx_scope;
109+
110+
llvm::StringRef m_input_expr;
111+
112+
DILLexer m_dil_lexer;
113+
114+
// Holds an error if it occures during parsing.
115+
llvm::Error &m_error;
116+
117+
lldb::DynamicValueType m_use_dynamic;
118+
bool m_use_synthetic;
119+
bool m_fragile_ivar;
120+
bool m_check_ptr_vs_member;
121+
}; // class DILParser
122+
123+
} // namespace lldb_private::dil
124+
125+
#endif // LLDB_VALUEOBJECT_DILPARSER_H

0 commit comments

Comments
 (0)