[Lldb-commits] [lldb] [LLDB] Add DIL code for handling plain variable names. (PR #120971)
via lldb-commits
lldb-commits at lists.llvm.org
Mon Mar 24 20:53:29 PDT 2025
https://github.com/cmtice updated https://github.com/llvm/llvm-project/pull/120971
>From c14a0e3f80c1cc49a6717b41c8b90e2258168399 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 23 Dec 2024 06:41:46 -0800
Subject: [PATCH 01/21] [LLDB] Add DIL code for handling plain variable names.
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.
---
lldb/docs/dil-expr-lang.ebnf | 40 ++
lldb/include/lldb/ValueObject/DILAST.h | 161 ++++++++
lldb/include/lldb/ValueObject/DILEval.h | 62 +++
lldb/include/lldb/ValueObject/DILLexer.h | 166 ++++++++
lldb/include/lldb/ValueObject/DILParser.h | 105 ++++++
lldb/source/Target/StackFrame.cpp | 52 ++-
lldb/source/ValueObject/CMakeLists.txt | 4 +
lldb/source/ValueObject/DILAST.cpp | 228 +++++++++++
lldb/source/ValueObject/DILEval.cpp | 117 ++++++
lldb/source/ValueObject/DILLexer.cpp | 191 ++++++++++
lldb/source/ValueObject/DILParser.cpp | 356 ++++++++++++++++++
.../basics/GlobalVariableLookup/Makefile | 3 +
.../TestFrameVarDILGlobalVariableLookup.py | 82 ++++
.../basics/GlobalVariableLookup/main.cpp | 18 +
.../var-dil/basics/InstanceVariables/Makefile | 3 +
.../TestFrameVarDILInstanceVariables.py | 62 +++
.../var-dil/basics/InstanceVariables/main.cpp | 25 ++
.../frame/var-dil/basics/LocalVars/Makefile | 3 +
.../LocalVars/TestFrameVarDILLocalVars.py | 65 ++++
.../frame/var-dil/basics/LocalVars/main.cpp | 26 ++
20 files changed, 1762 insertions(+), 7 deletions(-)
create mode 100644 lldb/docs/dil-expr-lang.ebnf
create mode 100644 lldb/include/lldb/ValueObject/DILAST.h
create mode 100644 lldb/include/lldb/ValueObject/DILEval.h
create mode 100644 lldb/include/lldb/ValueObject/DILLexer.h
create mode 100644 lldb/include/lldb/ValueObject/DILParser.h
create mode 100644 lldb/source/ValueObject/DILAST.cpp
create mode 100644 lldb/source/ValueObject/DILEval.cpp
create mode 100644 lldb/source/ValueObject/DILLexer.cpp
create mode 100644 lldb/source/ValueObject/DILParser.cpp
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
create mode 100644 lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
new file mode 100644
index 0000000000000..64b3e7758229c
--- /dev/null
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -0,0 +1,40 @@
+(* 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
+ | "this"
+ | "(" expression ")";
+
+id_expression = unqualified_id
+ | qualified_id ;
+
+unqualified_id = identifier ;
+
+qualified_id = ["::"] [nested_name_specifier] unqualified_id
+ | ["::"] identifier ;
+
+identifier = ? dil::TokenKind::identifier ? ;
+
+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 ;
+
+
+
+
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
new file mode 100644
index 0000000000000..9f0a1a2221e38
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -0,0 +1,161 @@
+//===-- 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 <memory>
+#include <string>
+#include <vector>
+
+#include "lldb/ValueObject/ValueObject.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+/// The various types DIL AST nodes (used by the DIL parser).
+enum class NodeKind {
+ eErrorNode,
+ eIdentifierNode,
+};
+
+/// Class used to store & manipulate information about identifiers.
+class IdentifierInfo {
+public:
+ enum class Kind {
+ eValue,
+ eContextArg,
+ };
+
+ static std::unique_ptr<IdentifierInfo> FromValue(ValueObject &valobj) {
+ CompilerType type;
+ type = valobj.GetCompilerType();
+ return std::unique_ptr<IdentifierInfo>(
+ new IdentifierInfo(Kind::eValue, type, valobj.GetSP(), {}));
+ }
+
+ static std::unique_ptr<IdentifierInfo> FromContextArg(CompilerType type) {
+ lldb::ValueObjectSP empty_value;
+ return std::unique_ptr<IdentifierInfo>(
+ new IdentifierInfo(Kind::eContextArg, type, empty_value, {}));
+ }
+
+ Kind GetKind() const { return m_kind; }
+ lldb::ValueObjectSP GetValue() const { return m_value; }
+
+ CompilerType GetType() { return m_type; }
+ bool IsValid() const { return m_type.IsValid(); }
+
+ IdentifierInfo(Kind kind, CompilerType type, lldb::ValueObjectSP value,
+ std::vector<uint32_t> path)
+ : m_kind(kind), m_type(type), m_value(std::move(value)) {}
+
+private:
+ Kind m_kind;
+ CompilerType m_type;
+ lldb::ValueObjectSP m_value;
+};
+
+/// Given the name of an identifier (variable name, member name, type name,
+/// etc.), find the ValueObject for that name (if it exists) and create and
+/// return an IdentifierInfo object containing all the relevant information
+/// about that object (for DIL parsing and evaluating).
+std::unique_ptr<IdentifierInfo> LookupIdentifier(
+ const std::string &name, std::shared_ptr<ExecutionContextScope> ctx_scope,
+ lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr);
+
+/// 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 DILASTNode {
+public:
+ DILASTNode(uint32_t location, NodeKind kind)
+ : m_location(location), m_kind(kind) {}
+ virtual ~DILASTNode() = default;
+
+ virtual void 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 DILASTNodeUP = std::unique_ptr<DILASTNode>;
+
+class ErrorNode : public DILASTNode {
+public:
+ ErrorNode(CompilerType empty_type)
+ : DILASTNode(0, NodeKind::eErrorNode), m_empty_type(empty_type) {}
+ void Accept(Visitor *v) const override;
+
+ static bool classof(const DILASTNode *node) {
+ return node->GetKind() == NodeKind::eErrorNode;
+ }
+
+private:
+ CompilerType m_empty_type;
+};
+
+class IdentifierNode : public DILASTNode {
+public:
+ IdentifierNode(uint32_t location, std::string name,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
+ : DILASTNode(location, NodeKind::eIdentifierNode),
+ m_name(std::move(name)), m_use_dynamic(use_dynamic),
+ m_ctx_scope(exe_ctx_scope) {}
+
+ void Accept(Visitor *v) const override;
+
+ lldb::DynamicValueType use_dynamic() const { return m_use_dynamic; }
+ std::string name() const { return m_name; }
+ std::shared_ptr<ExecutionContextScope> get_exe_context() const {
+ return m_ctx_scope;
+ }
+
+ static bool classof(const DILASTNode *node) {
+ return node->GetKind() == NodeKind::eIdentifierNode;
+ }
+
+private:
+ std::string m_name;
+ lldb::DynamicValueType m_use_dynamic;
+ std::shared_ptr<ExecutionContextScope> m_ctx_scope;
+};
+
+/// 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 void Visit(const ErrorNode *node) = 0;
+ virtual void Visit(const IdentifierNode *node) = 0;
+};
+
+} // namespace dil
+
+} // namespace lldb_private
+
+#endif // LLDB_VALUEOBJECT_DILAST_H
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
new file mode 100644
index 0000000000000..4006bb10630f2
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -0,0 +1,62 @@
+//===-- 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 <memory>
+#include <vector>
+
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILParser.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+class DILInterpreter : Visitor {
+public:
+ DILInterpreter(lldb::TargetSP target, std::shared_ptr<std::string> sm);
+ DILInterpreter(lldb::TargetSP target, std::shared_ptr<std::string> sm,
+ lldb::ValueObjectSP scope);
+ DILInterpreter(lldb::TargetSP target, std::shared_ptr<std::string> sm,
+ lldb::DynamicValueType use_dynamic);
+
+ lldb::ValueObjectSP DILEval(const DILASTNode *tree, lldb::TargetSP target_sp,
+ Status &error);
+
+private:
+ lldb::ValueObjectSP DILEvalNode(const DILASTNode *node);
+
+ bool Success() { return m_error.Success(); }
+
+ void SetError(ErrorCode error_code, std::string error, uint32_t loc);
+
+ void Visit(const ErrorNode *node) override;
+ void Visit(const IdentifierNode *node) override;
+
+private:
+ // Used by the interpreter to create objects, perform casts, etc.
+ lldb::TargetSP m_target;
+
+ std::shared_ptr<std::string> m_sm;
+
+ lldb::ValueObjectSP m_result;
+
+ lldb::ValueObjectSP m_scope;
+
+ lldb::DynamicValueType m_default_dynamic;
+
+ Status m_error;
+};
+
+} // namespace dil
+
+} // namespace lldb_private
+
+#endif // LLDB_VALUEOBJECT_DILEVAL_H_
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
new file mode 100644
index 0000000000000..c794fb2bfc0ed
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -0,0 +1,166 @@
+//===-- DILLexer.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_DILLEXER_H_
+#define LLDB_VALUEOBJECT_DILLEXER_H_
+
+#include <limits.h>
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+enum class TokenKind {
+ coloncolon,
+ eof,
+ identifier,
+ invalid,
+ kw_namespace,
+ kw_this,
+ l_paren,
+ none,
+ r_paren,
+ unknown,
+};
+
+/// Class defining the tokens generated by the DIL lexer and used by the
+/// DIL parser.
+class DILToken {
+public:
+ DILToken(dil::TokenKind kind, std::string spelling, uint32_t start,
+ uint32_t len)
+ : m_kind(kind), m_spelling(spelling), m_start_pos(start), m_length(len) {}
+
+ DILToken()
+ : m_kind(dil::TokenKind::none), m_spelling(""), m_start_pos(0),
+ m_length(0) {}
+
+ void setKind(dil::TokenKind kind) { m_kind = kind; }
+ dil::TokenKind getKind() const { return m_kind; }
+
+ std::string getSpelling() const { return m_spelling; }
+
+ uint32_t getLength() const { return m_length; }
+
+ bool is(dil::TokenKind kind) const { return m_kind == kind; }
+
+ bool isNot(dil::TokenKind kind) const { return m_kind != kind; }
+
+ bool isOneOf(dil::TokenKind kind1, dil::TokenKind kind2) const {
+ return is(kind1) || is(kind2);
+ }
+
+ template <typename... Ts> bool isOneOf(dil::TokenKind kind, Ts... Ks) const {
+ return is(kind) || isOneOf(Ks...);
+ }
+
+ uint32_t getLocation() const { return m_start_pos; }
+
+ void setValues(dil::TokenKind kind, std::string spelling, uint32_t start,
+ uint32_t len) {
+ m_kind = kind;
+ m_spelling = spelling;
+ m_start_pos = start;
+ m_length = len;
+ }
+
+ static const std::string getTokenName(dil::TokenKind kind);
+
+private:
+ dil::TokenKind m_kind;
+ std::string m_spelling;
+ uint32_t m_start_pos; // within entire expression string
+ uint32_t m_length;
+};
+
+/// Class for doing the simple lexing required by DIL.
+class DILLexer {
+public:
+ DILLexer(std::shared_ptr<std::string> dil_sm) : m_expr(*dil_sm) {
+ m_cur_pos = m_expr.begin();
+ // Use UINT_MAX to indicate invalid/uninitialized value.
+ m_tokens_idx = UINT_MAX;
+ }
+
+ bool Lex(DILToken &result, bool look_ahead = false);
+
+ bool Is_Word(std::string::iterator start, uint32_t &length);
+
+ uint32_t GetLocation() { return m_cur_pos - m_expr.begin(); }
+
+ /// Update 'result' with the other paremeter values, create a
+ /// duplicate token, and push the duplicate token onto the vector of
+ /// lexed tokens.
+ void UpdateLexedTokens(DILToken &result, dil::TokenKind tok_kind,
+ std::string tok_str, uint32_t tok_pos,
+ uint32_t tok_len);
+
+ /// Return the lexed token N+1 positions ahead of the 'current' token
+ /// being handled by the DIL parser.
+ const DILToken &LookAhead(uint32_t N);
+
+ const DILToken &AcceptLookAhead(uint32_t N);
+
+ /// Return the index for the 'current' token being handled by the DIL parser.
+ uint32_t GetCurrentTokenIdx() { return m_tokens_idx; }
+
+ /// Return the current token to be handled by the DIL parser.
+ DILToken &GetCurrentToken() { return m_lexed_tokens[m_tokens_idx]; }
+
+ /// Update the index for the 'current' token, to point to the next lexed
+ /// token.
+ bool IncrementTokenIdx() {
+ if (m_tokens_idx >= m_lexed_tokens.size() - 1)
+ return false;
+
+ m_tokens_idx++;
+ return true;
+ }
+
+ /// Set the index for the 'current' token (to be handled by the parser)
+ /// to a particular position. Used for either committing 'look ahead' parsing
+ /// or rolling back tentative parsing.
+ bool ResetTokenIdx(uint32_t new_value) {
+ if (new_value > m_lexed_tokens.size() - 1)
+ return false;
+
+ m_tokens_idx = new_value;
+ return true;
+ }
+
+private:
+ // The input string we are lexing & parsing.
+ std::string m_expr;
+
+ // The current position of the lexer within m_expr (the character position,
+ // within the string, of the next item to be lexed).
+ std::string::iterator m_cur_pos;
+
+ // Holds all of the tokens lexed so far.
+ std::vector<DILToken> m_lexed_tokens;
+
+ // Index into m_lexed_tokens; indicates which token the DIL parser is
+ // currently trying to parse/handle.
+ uint32_t m_tokens_idx;
+
+ // "invalid" token; to be returned by lexer when 'look ahead' fails.
+ DILToken m_invalid_token;
+};
+
+} // namespace dil
+
+} // namespace lldb_private
+
+#endif // LLDB_VALUEOBJECT_DILLEXER_H_
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
new file mode 100644
index 0000000000000..b718903b7bea4
--- /dev/null
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -0,0 +1,105 @@
+//===-- 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 <memory>
+#include <optional>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILLexer.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+enum class ErrorCode : unsigned char {
+ kOk = 0,
+ kInvalidExpressionSyntax,
+ kUndeclaredIdentifier,
+ kUnknown,
+};
+
+std::string FormatDiagnostics(std::shared_ptr<std::string> input_expr,
+ const std::string &message, uint32_t loc);
+
+/// 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:
+ explicit DILParser(std::shared_ptr<std::string> dil_input_expr,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member);
+
+ DILASTNodeUP Run(Status &error);
+
+ ~DILParser() { m_ctx_scope.reset(); }
+
+ bool UseSynthetic() { return m_use_synthetic; }
+
+ lldb::DynamicValueType UseDynamic() { return m_use_dynamic; }
+
+ using PtrOperator = std::tuple<dil::TokenKind, uint32_t>;
+
+private:
+ DILASTNodeUP ParseExpression();
+ DILASTNodeUP ParsePrimaryExpression();
+
+ std::string ParseNestedNameSpecifier();
+
+ std::string ParseIdExpression();
+ std::string ParseUnqualifiedId();
+
+ void ConsumeToken();
+
+ void BailOut(ErrorCode error_code, const std::string &error, uint32_t loc);
+
+ void BailOut(Status error);
+
+ void Expect(dil::TokenKind kind);
+
+ std::string TokenDescription(const DILToken &token);
+
+ template <typename... Ts> void ExpectOneOf(dil::TokenKind k, Ts... ks);
+
+ void TentativeParsingRollback(uint32_t saved_idx) {
+ m_error.Clear();
+ m_dil_lexer.ResetTokenIdx(saved_idx);
+ m_dil_token = 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<ExecutionContextScope> m_ctx_scope;
+
+ std::shared_ptr<std::string> m_input_expr;
+ // The token lexer is stopped at (aka "current token").
+ DILToken m_dil_token;
+ // Holds an error if it occures during parsing.
+ Status m_error;
+
+ lldb::DynamicValueType m_use_dynamic;
+ bool m_use_synthetic;
+ bool m_fragile_ivar;
+ bool m_check_ptr_vs_member;
+ DILLexer m_dil_lexer;
+}; // class DILParser
+
+} // namespace dil
+
+} // namespace lldb_private
+
+#endif // LLDB_VALUEOBJECT_DILPARSER_H_
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 2633c976c13bf..28450b1d1c1c1 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -31,6 +31,8 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
+#include "lldb/ValueObject/DILEval.h"
+#include "lldb/ValueObject/DILParser.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/ValueObject/ValueObjectMemory.h"
#include "lldb/ValueObject/ValueObjectVariable.h"
@@ -511,22 +513,58 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
VariableSP &var_sp, Status &error) {
ExecutionContext exe_ctx;
CalculateExecutionContext(exe_ctx);
+
bool use_DIL = exe_ctx.GetTargetRef().GetUseDIL(&exe_ctx);
+
if (use_DIL)
return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
var_sp, error);
-
- return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
- var_sp, error);
+ else
+ return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic,
+ options, var_sp, error);
}
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error) {
- // This is a place-holder for the calls into the DIL parser and
- // evaluator. For now, just call the "real" frame variable implementation.
- return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
- var_sp, error);
+ ValueObjectSP ret_val;
+ std::shared_ptr<std::string> source =
+ std::make_shared<std::string>(var_expr.data());
+
+ const bool check_ptr_vs_member =
+ (options & eExpressionPathOptionCheckPtrVsMember) != 0;
+ const bool no_fragile_ivar =
+ (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
+ const bool no_synth_child =
+ (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
+
+ // Parse the expression.
+ Status parse_error, eval_error;
+ dil::DILParser parser(source, shared_from_this(), use_dynamic,
+ !no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
+ dil::DILASTNodeUP tree = parser.Run(parse_error);
+ if (parse_error.Fail()) {
+ error = std::move(parse_error);
+ return ValueObjectSP();
+ }
+
+ // Evaluate the parsed expression.
+ lldb::TargetSP target = this->CalculateTarget();
+ dil::DILInterpreter interpreter(target, source, use_dynamic);
+
+ ret_val = interpreter.DILEval(tree.get(), target, eval_error);
+ if (eval_error.Fail()) {
+ error = std::move(eval_error);
+ return ValueObjectSP();
+ }
+
+ if (ret_val) {
+ var_sp = ret_val->GetVariable();
+ if (!var_sp && ret_val->GetParent()) {
+ var_sp = ret_val->GetParent()->GetVariable();
+ }
+ }
+ return ret_val;
}
ValueObjectSP StackFrame::LegacyGetValueForVariableExpressionPath(
diff --git a/lldb/source/ValueObject/CMakeLists.txt b/lldb/source/ValueObject/CMakeLists.txt
index 70cb3d6d53f07..92683916f5a52 100644
--- a/lldb/source/ValueObject/CMakeLists.txt
+++ b/lldb/source/ValueObject/CMakeLists.txt
@@ -1,4 +1,8 @@
add_lldb_library(lldbValueObject
+ DILAST.cpp
+ DILEval.cpp
+ DILLexer.cpp
+ DILParser.cpp
ValueObject.cpp
ValueObjectCast.cpp
ValueObjectChild.cpp
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
new file mode 100644
index 0000000000000..1af0dc57ddcea
--- /dev/null
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -0,0 +1,228 @@
+//===-- DILAST.cpp --------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILAST.h"
+
+#include "lldb/API/SBType.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/ValueObject/ValueObjectRegister.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
+#include "llvm/ADT/StringRef.h"
+
+#include <vector>
+
+namespace lldb_private {
+
+namespace dil {
+
+static lldb::ValueObjectSP
+LookupStaticIdentifier(lldb::TargetSP target_sp,
+ const llvm::StringRef &name_ref,
+ ConstString unqualified_name) {
+ // List global variable with the same "basename". There can be many matches
+ // from other scopes (namespaces, classes), so we do additional filtering
+ // later.
+ VariableList variable_list;
+ ConstString name(name_ref);
+ target_sp->GetImages().FindGlobalVariables(name, 1, variable_list);
+ if (!variable_list.Empty()) {
+ ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target_sp.get();
+ for (const lldb::VariableSP &var_sp : variable_list) {
+ lldb::ValueObjectSP valobj_sp(
+ ValueObjectVariable::Create(exe_scope, var_sp));
+ if (valobj_sp && valobj_sp->GetVariable() &&
+ (valobj_sp->GetVariable()->NameMatches(unqualified_name) ||
+ valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
+ return valobj_sp;
+ }
+ }
+ return nullptr;
+}
+
+static lldb::VariableSP DILFindVariable(ConstString name,
+ VariableList *variable_list) {
+ lldb::VariableSP exact_match;
+ std::vector<lldb::VariableSP> possible_matches;
+
+ typedef std::vector<lldb::VariableSP> collection;
+ typedef collection::iterator iterator;
+
+ iterator pos, end = variable_list->end();
+ for (pos = variable_list->begin(); pos != end; ++pos) {
+ llvm::StringRef str_ref_name = pos->get()->GetName().GetStringRef();
+ // Check for global vars, which might start with '::'.
+ str_ref_name.consume_front("::");
+
+ if (str_ref_name == name.GetStringRef())
+ possible_matches.push_back(*pos);
+ else if (pos->get()->NameMatches(name))
+ possible_matches.push_back(*pos);
+ }
+
+ // Look for exact matches (favors local vars over global vars)
+ auto exact_match_it =
+ llvm::find_if(possible_matches, [&](lldb::VariableSP var_sp) {
+ return var_sp->GetName() == name;
+ });
+
+ if (exact_match_it != llvm::adl_end(possible_matches))
+ exact_match = *exact_match_it;
+
+ if (!exact_match)
+ // Look for a global var exact match.
+ for (auto var_sp : possible_matches) {
+ llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
+ if (str_ref_name.size() > 2 && str_ref_name[0] == ':' &&
+ str_ref_name[1] == ':')
+ str_ref_name = str_ref_name.drop_front(2);
+ ConstString tmp_name(str_ref_name);
+ if (tmp_name == name) {
+ exact_match = var_sp;
+ break;
+ }
+ }
+
+ // Take any match at this point.
+ if (!exact_match && possible_matches.size() > 0)
+ exact_match = possible_matches[0];
+
+ return exact_match;
+}
+
+std::unique_ptr<IdentifierInfo>
+LookupIdentifier(const std::string &name,
+ std::shared_ptr<ExecutionContextScope> ctx_scope,
+ lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
+ ConstString name_str(name);
+ llvm::StringRef name_ref = name_str.GetStringRef();
+
+ // Support $rax as a special syntax for accessing registers.
+ // Will return an invalid value in case the requested register doesn't exist.
+ if (name_ref.starts_with("$")) {
+ lldb::ValueObjectSP value_sp;
+ const char *reg_name = name_ref.drop_front(1).data();
+ Target *target = ctx_scope->CalculateTarget().get();
+ Process *process = ctx_scope->CalculateProcess().get();
+ if (target && process) {
+ StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+ if (stack_frame) {
+ lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+ if (reg_ctx) {
+ if (const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(reg_name))
+ value_sp =
+ ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+ }
+ }
+ }
+ if (value_sp)
+ return IdentifierInfo::FromValue(*value_sp);
+ else
+ return nullptr;
+ }
+
+ // Internally values don't have global scope qualifier in their names and
+ // LLDB doesn't support queries with it too.
+ bool global_scope = false;
+ if (name_ref.starts_with("::")) {
+ name_ref = name_ref.drop_front(2);
+ global_scope = true;
+ }
+
+ // If the identifier doesn't refer to the global scope and doesn't have any
+ // other scope qualifiers, try looking among the local and instance variables.
+ if (!global_scope && !name_ref.contains("::")) {
+ if (!scope_ptr || !scope_ptr->IsValid()) {
+ // Lookup in the current frame.
+ lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
+ // Try looking for a local variable in current scope.
+ lldb::ValueObjectSP value_sp;
+ lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
+ VariableList *variable_list = var_list_sp.get();
+ if (variable_list) {
+ lldb::VariableSP var_sp =
+ DILFindVariable(ConstString(name_ref), variable_list);
+ if (var_sp)
+ value_sp = frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ }
+ if (!value_sp)
+ value_sp = frame->FindVariable(ConstString(name_ref));
+
+ if (value_sp)
+ // Force static value, otherwise we can end up with the "real" type.
+ return IdentifierInfo::FromValue(*value_sp);
+
+ // Try looking for an instance variable (class member).
+ ConstString this_string("this");
+ value_sp = frame->FindVariable(this_string);
+ if (value_sp)
+ value_sp = value_sp->GetChildMemberWithName(name_ref.data());
+
+ if (value_sp)
+ // Force static value, otherwise we can end up with the "real" type.
+ return IdentifierInfo::FromValue(*(value_sp->GetStaticValue()));
+ }
+ }
+
+ // Try looking for a global or static variable.
+
+ lldb::ValueObjectSP value;
+ if (!global_scope) {
+ // Try looking for static member of the current scope value, e.g.
+ // `ScopeType::NAME`. NAME can include nested struct (`Nested::SUBNAME`),
+ // but it cannot be part of the global scope (start with "::").
+ const char *type_name = "";
+ if (scope_ptr)
+ type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
+ std::string name_with_type_prefix =
+ llvm::formatv("{0}::{1}", type_name, name_ref).str();
+ value = LookupStaticIdentifier(ctx_scope->CalculateTarget(),
+ name_with_type_prefix, name_str);
+ }
+
+ // Lookup a regular global variable.
+ if (!value)
+ value = LookupStaticIdentifier(ctx_scope->CalculateTarget(), name_ref,
+ name_str);
+
+ // Last resort, lookup as a register (e.g. `rax` or `rip`).
+ if (!value) {
+ Target *target = ctx_scope->CalculateTarget().get();
+ Process *process = ctx_scope->CalculateProcess().get();
+ if (target && process) {
+ StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+ if (stack_frame) {
+ lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+ if (reg_ctx) {
+ if (const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(name_ref.data()))
+ value = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+ }
+ }
+ }
+ }
+
+ // Force static value, otherwise we can end up with the "real" type.
+ if (value)
+ return IdentifierInfo::FromValue(*value);
+ else
+ return nullptr;
+}
+
+void ErrorNode::Accept(Visitor *v) const { v->Visit(this); }
+
+void IdentifierNode::Accept(Visitor *v) const { v->Visit(this); }
+
+} // namespace dil
+
+} // namespace lldb_private
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
new file mode 100644
index 0000000000000..01da852153650
--- /dev/null
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -0,0 +1,117 @@
+//===-- DILEval.cpp -------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILEval.h"
+
+#include <memory>
+
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/ValueObject.h"
+#include "llvm/Support/FormatAdapters.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+DILInterpreter::DILInterpreter(lldb::TargetSP target,
+ std::shared_ptr<std::string> sm)
+ : m_target(std::move(target)), m_sm(std::move(sm)) {
+ m_default_dynamic = lldb::eNoDynamicValues;
+}
+
+DILInterpreter::DILInterpreter(lldb::TargetSP target,
+ std::shared_ptr<std::string> sm,
+ lldb::DynamicValueType use_dynamic)
+ : m_target(std::move(target)), m_sm(std::move(sm)),
+ m_default_dynamic(use_dynamic) {}
+
+DILInterpreter::DILInterpreter(lldb::TargetSP target,
+ std::shared_ptr<std::string> sm,
+ lldb::ValueObjectSP scope)
+ : m_target(std::move(target)), m_sm(std::move(sm)),
+ m_scope(std::move(scope)) {
+ m_default_dynamic = lldb::eNoDynamicValues;
+ // If `m_scope` is a reference, dereference it. All operations on a reference
+ // should be operations on the referent.
+ if (m_scope->GetCompilerType().IsValid() &&
+ m_scope->GetCompilerType().IsReferenceType()) {
+ Status error;
+ m_scope = m_scope->Dereference(error);
+ }
+}
+
+lldb::ValueObjectSP DILInterpreter::DILEval(const DILASTNode *tree,
+ lldb::TargetSP target_sp,
+ Status &error) {
+ m_error.Clear();
+ // Evaluate an AST.
+ DILEvalNode(tree);
+ // Set the error.
+ error = std::move(m_error);
+ // Return the computed result. If there was an error, it will be invalid.
+ return m_result;
+}
+
+lldb::ValueObjectSP DILInterpreter::DILEvalNode(const DILASTNode *node) {
+
+ // Traverse an AST pointed by the `node`.
+ node->Accept(this);
+
+ // Return the computed value for convenience. The caller is responsible for
+ // checking if an error occured during the evaluation.
+ return m_result;
+}
+
+void DILInterpreter::SetError(ErrorCode code, std::string error, uint32_t loc) {
+ assert(m_error.Success() && "interpreter can error only once");
+ m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
+ FormatDiagnostics(m_sm, error, loc));
+}
+
+void DILInterpreter::Visit(const ErrorNode *node) {
+ // The AST is not valid.
+ m_result = lldb::ValueObjectSP();
+}
+
+void DILInterpreter::Visit(const IdentifierNode *node) {
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope =
+ node->get_exe_context();
+ lldb::DynamicValueType use_dynamic = node->use_dynamic();
+
+ std::unique_ptr<IdentifierInfo> identifier =
+ LookupIdentifier(node->name(), exe_ctx_scope, use_dynamic);
+
+ if (!identifier) {
+ std::string errMsg;
+ std::string name = node->name();
+ if (name == "this")
+ errMsg = "invalid use of 'this' outside of a non-static member function";
+ else
+ errMsg = llvm::formatv("use of undeclared identifier '{0}'", name);
+ SetError(ErrorCode::kUndeclaredIdentifier, errMsg, node->GetLocation());
+ m_result = lldb::ValueObjectSP();
+ return;
+ }
+ lldb::ValueObjectSP val;
+ lldb::TargetSP target_sp;
+ Status error;
+
+ assert(identifier->GetKind() == IdentifierInfo::Kind::eValue &&
+ "Unrecognized identifier kind");
+
+ val = identifier->GetValue();
+ target_sp = val->GetTargetSP();
+ assert(target_sp && target_sp->IsValid() &&
+ "identifier doesn't resolve to a valid value");
+
+ m_result = val;
+}
+
+} // namespace dil
+
+} // namespace lldb_private
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
new file mode 100644
index 0000000000000..c63a83bea6f1d
--- /dev/null
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -0,0 +1,191 @@
+//===-- DILLexer.cpp ------------------------------------------------------===//
+//
+// 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
+//
+// This implements the recursive descent parser for the Data Inspection
+// Language (DIL), and its helper functions, which will eventually underlie the
+// 'frame variable' command. The language that this parser recognizes is
+// described in lldb/docs/dil-expr-lang.ebnf
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILLexer.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+const std::string DILToken::getTokenName(dil::TokenKind kind) {
+ std::string retval;
+ switch (kind) {
+ case dil::TokenKind::coloncolon:
+ retval = "coloncolon";
+ break;
+ case dil::TokenKind::eof:
+ retval = "eof";
+ break;
+ case dil::TokenKind::identifier:
+ retval = "identifier";
+ break;
+ case dil::TokenKind::kw_namespace:
+ retval = "namespace";
+ break;
+ case dil::TokenKind::kw_this:
+ retval = "this";
+ break;
+ case dil::TokenKind::l_paren:
+ retval = "l_paren";
+ break;
+ case dil::TokenKind::r_paren:
+ retval = "r_paren";
+ break;
+ case dil::TokenKind::unknown:
+ retval = "unknown";
+ break;
+ default:
+ retval = "token_name";
+ break;
+ }
+ return retval;
+}
+
+static bool Is_Letter(char c) {
+ if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
+ return true;
+ return false;
+}
+
+static bool Is_Digit(char c) { return ('0' <= c && c <= '9'); }
+
+bool DILLexer::Is_Word(std::string::iterator start, uint32_t &length) {
+ bool done = false;
+ for (; m_cur_pos != m_expr.end() && !done; ++m_cur_pos) {
+ char c = *m_cur_pos;
+ if (!Is_Letter(c) && !Is_Digit(c) && c != '_') {
+ done = true;
+ break;
+ } else
+ length++;
+ }
+ if (length > 0)
+ return true;
+ else
+ m_cur_pos = start;
+ return false;
+}
+
+void DILLexer::UpdateLexedTokens(DILToken &result, dil::TokenKind tok_kind,
+ std::string tok_str, uint32_t tok_pos,
+ uint32_t tok_len) {
+ DILToken new_token;
+ result.setValues(tok_kind, tok_str, tok_pos, tok_len);
+ new_token = result;
+ m_lexed_tokens.push_back(std::move(new_token));
+}
+
+bool DILLexer::Lex(DILToken &result, bool look_ahead) {
+ bool retval = true;
+
+ if (!look_ahead) {
+ // We're being asked for the 'next' token, and not a part of a LookAhead.
+ // Check to see if we've already lexed it and pushed it onto our tokens
+ // vector; if so, return the next token from the vector, rather than doing
+ // more lexing.
+ if ((m_tokens_idx != UINT_MAX) &&
+ (m_tokens_idx < m_lexed_tokens.size() - 1)) {
+ result = m_lexed_tokens[m_tokens_idx + 1];
+ return retval;
+ }
+ }
+
+ // Skip over whitespace (spaces).
+ while (m_cur_pos != m_expr.end() && *m_cur_pos == ' ')
+ m_cur_pos++;
+
+ // Check to see if we've reached the end of our input string.
+ if (m_cur_pos == m_expr.end()) {
+ UpdateLexedTokens(result, dil::TokenKind::eof, "", m_expr.length(), 0);
+ return retval;
+ }
+
+ uint32_t position = m_cur_pos - m_expr.begin();
+ ;
+ std::string::iterator start = m_cur_pos;
+ uint32_t length = 0;
+ if (Is_Word(start, length)) {
+ dil::TokenKind kind;
+ std::string word = m_expr.substr(position, length);
+ if (word == "this")
+ kind = dil::TokenKind::kw_this;
+ else if (word == "namespace")
+ kind = dil::TokenKind::kw_namespace;
+ else
+ kind = dil::TokenKind::identifier;
+
+ UpdateLexedTokens(result, kind, word, position, length);
+ return true;
+ }
+
+ switch (*m_cur_pos) {
+ case '(':
+ m_cur_pos++;
+ UpdateLexedTokens(result, dil::TokenKind::l_paren, "(", position, 1);
+ return true;
+ case ')':
+ m_cur_pos++;
+ UpdateLexedTokens(result, dil::TokenKind::r_paren, ")", position, 1);
+ return true;
+ case ':':
+ if (position + 1 < m_expr.size() && m_expr[position + 1] == ':') {
+ m_cur_pos += 2;
+ UpdateLexedTokens(result, dil::TokenKind::coloncolon, "::", position, 2);
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ // Empty Token
+ result.setValues(dil::TokenKind::none, "", m_expr.length(), 0);
+ return false;
+}
+
+const DILToken &DILLexer::LookAhead(uint32_t N) {
+ uint32_t extra_lexed_tokens = m_lexed_tokens.size() - m_tokens_idx - 1;
+
+ if (N + 1 < extra_lexed_tokens)
+ return m_lexed_tokens[m_tokens_idx + N + 1];
+
+ uint32_t remaining_tokens =
+ (m_tokens_idx + N + 1) - m_lexed_tokens.size() + 1;
+
+ bool done = false;
+ bool look_ahead = true;
+ while (!done && remaining_tokens > 0) {
+ DILToken tok;
+ Lex(tok, look_ahead);
+ if (tok.getKind() == dil::TokenKind::eof)
+ done = true;
+ remaining_tokens--;
+ };
+
+ if (remaining_tokens > 0) {
+ m_invalid_token.setValues(dil::TokenKind::invalid, "", 0, 0);
+ return m_invalid_token;
+ } else
+ return m_lexed_tokens[m_tokens_idx + N + 1];
+}
+
+const DILToken &DILLexer::AcceptLookAhead(uint32_t N) {
+ if (m_tokens_idx + N + 1 > m_lexed_tokens.size())
+ return m_invalid_token;
+
+ m_tokens_idx += N + 1;
+ return m_lexed_tokens[m_tokens_idx];
+}
+
+} // namespace dil
+
+} // namespace lldb_private
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
new file mode 100644
index 0000000000000..eb36de80be76a
--- /dev/null
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -0,0 +1,356 @@
+//===-- DILParser.cpp -----------------------------------------------------===//
+//
+// 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
+//
+// This implements the recursive descent parser for the Data Inspection
+// Language (DIL), and its helper functions, which will eventually underlie the
+// 'frame variable' command. The language that this parser recognizes is
+// described in lldb/docs/dil-expr-lang.ebnf
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/ValueObject/DILParser.h"
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILEval.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/FormatAdapters.h"
+
+namespace lldb_private {
+
+namespace dil {
+
+inline void TokenKindsJoinImpl(std::ostringstream &os, dil::TokenKind k) {
+ os << "'" << DILToken::getTokenName(k) << "'";
+}
+
+template <typename... Ts>
+inline void TokenKindsJoinImpl(std::ostringstream &os, dil::TokenKind k,
+ Ts... ks) {
+ TokenKindsJoinImpl(os, k);
+ os << ", ";
+ TokenKindsJoinImpl(os, ks...);
+}
+
+template <typename... Ts>
+inline std::string TokenKindsJoin(dil::TokenKind k, Ts... ks) {
+ std::ostringstream os;
+ TokenKindsJoinImpl(os, k, ks...);
+
+ return os.str();
+}
+
+std::string FormatDiagnostics(std::shared_ptr<std::string> input_expr,
+ const std::string &message, uint32_t loc) {
+ // Get the source buffer and the location of the current token.
+ llvm::StringRef text(*input_expr);
+ size_t loc_offset = (size_t)loc;
+
+ // Look for the start of the line.
+ size_t line_start = text.rfind('\n', loc_offset);
+ line_start = line_start == llvm::StringRef::npos ? 0 : line_start + 1;
+
+ // Look for the end of the line.
+ size_t line_end = text.find('\n', loc_offset);
+ line_end = line_end == llvm::StringRef::npos ? text.size() : line_end;
+
+ // Get a view of the current line in the source code and the position of the
+ // diagnostics pointer.
+ llvm::StringRef line = text.slice(line_start, line_end);
+ int32_t arrow = loc + 1; // Column offset starts at 1, not 0.
+
+ // Calculate the padding in case we point outside of the expression (this can
+ // happen if the parser expected something, but got EOF).˚
+ size_t expr_rpad = std::max(0, arrow - static_cast<int32_t>(line.size()));
+ size_t arrow_rpad = std::max(0, static_cast<int32_t>(line.size()) - arrow);
+
+ return llvm::formatv("<expr:1:{0}>: {1}\n{2}\n{3}", loc, message,
+ llvm::fmt_pad(line, 0, expr_rpad),
+ llvm::fmt_pad("^", arrow - 1, arrow_rpad));
+}
+
+DILParser::DILParser(std::shared_ptr<std::string> dil_input_expr,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member)
+ : m_ctx_scope(exe_ctx_scope), m_input_expr(dil_input_expr),
+ m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
+ m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member),
+ m_dil_lexer(DILLexer(dil_input_expr)) {
+ // Initialize the token.
+ m_dil_token.setKind(dil::TokenKind::unknown);
+}
+
+DILASTNodeUP DILParser::Run(Status &error) {
+ ConsumeToken();
+
+ DILASTNodeUP expr;
+
+ expr = ParseExpression();
+
+ Expect(dil::TokenKind::eof);
+
+ error = std::move(m_error);
+ m_error.Clear();
+
+ // Explicitly return ErrorNode if there was an error during the parsing.
+ // Some routines raise an error, but don't change the return value (e.g.
+ // Expect).
+ if (error.Fail()) {
+ CompilerType bad_type;
+ return std::make_unique<ErrorNode>(bad_type);
+ }
+ return expr;
+}
+
+// Parse an expression.
+//
+// expression:
+// primary_expression
+//
+DILASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
+
+// Parse a primary_expression.
+//
+// primary_expression:
+// id_expression
+// "this"
+// "(" expression ")"
+//
+DILASTNodeUP DILParser::ParsePrimaryExpression() {
+ CompilerType bad_type;
+ if (m_dil_token.isOneOf(dil::TokenKind::coloncolon,
+ dil::TokenKind::identifier)) {
+ // Save the source location for the diagnostics message.
+ uint32_t loc = m_dil_token.getLocation();
+ auto identifier = ParseIdExpression();
+
+ return std::make_unique<IdentifierNode>(loc, identifier, m_use_dynamic,
+ m_ctx_scope);
+ } else if (m_dil_token.is(dil::TokenKind::kw_this)) {
+ // Save the source location for the diagnostics message.
+ uint32_t loc = m_dil_token.getLocation();
+ ConsumeToken();
+
+ // Special case for "this" pointer. As per C++ standard, it's a prvalue.
+ return std::make_unique<IdentifierNode>(loc, "this", m_use_dynamic,
+ m_ctx_scope);
+ } else if (m_dil_token.is(dil::TokenKind::l_paren)) {
+ ConsumeToken();
+ auto expr = ParseExpression();
+ Expect(dil::TokenKind::r_paren);
+ ConsumeToken();
+ return expr;
+ }
+
+ BailOut(ErrorCode::kInvalidExpressionSyntax,
+ llvm::formatv("Unexpected token: {0}", TokenDescription(m_dil_token)),
+ m_dil_token.getLocation());
+ return std::make_unique<ErrorNode>(bad_type);
+}
+
+// Parse nested_name_specifier.
+//
+// nested_name_specifier:
+// type_name "::"
+// namespace_name "::"
+// nested_name_specifier identifier "::"
+//
+std::string DILParser::ParseNestedNameSpecifier() {
+ // The first token in nested_name_specifier is always an identifier, or
+ // '(anonymous namespace)'.
+ if (m_dil_token.isNot(dil::TokenKind::identifier) &&
+ m_dil_token.isNot(dil::TokenKind::l_paren)) {
+ return "";
+ }
+
+ // Anonymous namespaces need to be treated specially: They are represented
+ // the the string '(anonymous namespace)', which has a space in it (throwing
+ // off normal parsing) and is not actually proper C++> Check to see if we're
+ // looking at '(anonymous namespace)::...'
+ if (m_dil_token.is(dil::TokenKind::l_paren)) {
+ // Look for all the pieces, in order:
+ // l_paren 'anonymous' 'namespace' r_paren coloncolon
+ if (m_dil_lexer.LookAhead(0).is(dil::TokenKind::identifier) &&
+ (((DILToken)m_dil_lexer.LookAhead(0)).getSpelling() == "anonymous") &&
+ m_dil_lexer.LookAhead(1).is(dil::TokenKind::kw_namespace) &&
+ m_dil_lexer.LookAhead(2).is(dil::TokenKind::r_paren) &&
+ m_dil_lexer.LookAhead(3).is(dil::TokenKind::coloncolon)) {
+ m_dil_token = m_dil_lexer.AcceptLookAhead(3);
+
+ assert((m_dil_token.is(dil::TokenKind::identifier) ||
+ m_dil_token.is(dil::TokenKind::l_paren)) &&
+ "Expected an identifier or anonymous namespace, but not found.");
+ // Continue parsing the nested_namespace_specifier.
+ std::string identifier2 = ParseNestedNameSpecifier();
+ if (identifier2.empty()) {
+ Expect(dil::TokenKind::identifier);
+ identifier2 = m_dil_token.getSpelling();
+ ConsumeToken();
+ }
+ return "(anonymous namespace)::" + identifier2;
+ } else {
+ return "";
+ }
+ } // end of special handling for '(anonymous namespace)'
+
+ // If the next token is scope ("::"), then this is indeed a
+ // nested_name_specifier
+ if (m_dil_lexer.LookAhead(0).is(dil::TokenKind::coloncolon)) {
+ // This nested_name_specifier is a single identifier.
+ std::string identifier = m_dil_token.getSpelling();
+ m_dil_token = m_dil_lexer.AcceptLookAhead(0);
+ Expect(dil::TokenKind::coloncolon);
+ ConsumeToken();
+ // Continue parsing the nested_name_specifier.
+ return identifier + "::" + ParseNestedNameSpecifier();
+ }
+
+ return "";
+}
+
+// Parse an id_expression.
+//
+// id_expression:
+// unqualified_id
+// qualified_id
+//
+// qualified_id:
+// ["::"] [nested_name_specifier] unqualified_id
+// ["::"] identifier
+//
+// identifier:
+// ? dil::TokenKind::identifier ?
+//
+std::string DILParser::ParseIdExpression() {
+ // Try parsing optional global scope operator.
+ bool global_scope = false;
+ if (m_dil_token.is(dil::TokenKind::coloncolon)) {
+ global_scope = true;
+ ConsumeToken();
+ }
+
+ // Try parsing optional nested_name_specifier.
+ auto nested_name_specifier = ParseNestedNameSpecifier();
+
+ // If nested_name_specifier is present, then it's qualified_id production.
+ // Follow the first production rule.
+ if (!nested_name_specifier.empty()) {
+ // Parse unqualified_id and construct a fully qualified id expression.
+ auto unqualified_id = ParseUnqualifiedId();
+
+ return llvm::formatv("{0}{1}{2}", global_scope ? "::" : "",
+ nested_name_specifier, unqualified_id);
+ }
+
+ // No nested_name_specifier, but with global scope -- this is also a
+ // qualified_id production. Follow the second production rule.
+ else if (global_scope) {
+ Expect(dil::TokenKind::identifier);
+ std::string identifier = m_dil_token.getSpelling();
+ ConsumeToken();
+ return llvm::formatv("{0}{1}", global_scope ? "::" : "", identifier);
+ }
+
+ // This is unqualified_id production.
+ return ParseUnqualifiedId();
+}
+
+// Parse an unqualified_id.
+//
+// unqualified_id:
+// identifier
+//
+// identifier:
+// ? dil::TokenKind::identifier ?
+//
+std::string DILParser::ParseUnqualifiedId() {
+ Expect(dil::TokenKind::identifier);
+ std::string identifier = m_dil_token.getSpelling();
+ ConsumeToken();
+ return identifier;
+}
+
+void DILParser::BailOut(ErrorCode code, const std::string &error,
+ uint32_t loc) {
+ if (m_error.Fail()) {
+ // If error is already set, then the parser is in the "bail-out" mode. Don't
+ // do anything and keep the original error.
+ return;
+ }
+
+ m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
+ FormatDiagnostics(m_input_expr, error, loc));
+ m_dil_token.setKind(dil::TokenKind::eof);
+}
+
+void DILParser::BailOut(Status error) {
+ if (m_error.Fail()) {
+ // If error is already set, then the parser is in the "bail-out" mode. Don't
+ // do anything and keep the original error.
+ return;
+ }
+ m_error = std::move(error);
+ m_dil_token.setKind(dil::TokenKind::eof);
+}
+
+void DILParser::ConsumeToken() {
+ if (m_dil_token.is(dil::TokenKind::eof)) {
+ // Don't do anything if we're already at eof. This can happen if an error
+ // occurred during parsing and we're trying to bail out.
+ return;
+ }
+ bool all_ok;
+ m_dil_lexer.Lex(m_dil_token);
+ if (m_dil_lexer.GetCurrentTokenIdx() == UINT_MAX)
+ all_ok = m_dil_lexer.ResetTokenIdx(0);
+ else
+ all_ok = m_dil_lexer.IncrementTokenIdx();
+ if (!all_ok)
+ BailOut(ErrorCode::kUnknown, "Invalid lexer token index", 0);
+}
+
+void DILParser::Expect(dil::TokenKind kind) {
+ if (m_dil_token.isNot(kind)) {
+ BailOut(ErrorCode::kUnknown,
+ llvm::formatv("expected {0}, got: {1}", TokenKindsJoin(kind),
+ TokenDescription(m_dil_token)),
+ m_dil_token.getLocation());
+ }
+}
+
+template <typename... Ts>
+void DILParser::ExpectOneOf(dil::TokenKind k, Ts... ks) {
+ static_assert((std::is_same_v<Ts, dil::TokenKind> && ...),
+ "ExpectOneOf can be only called with values of type "
+ "dil::TokenKind");
+
+ if (!m_dil_token.isOneOf(k, ks...)) {
+ BailOut(ErrorCode::kUnknown,
+ llvm::formatv("expected any of ({0}), got: {1}",
+ TokenKindsJoin(k, ks...),
+ TokenDescription(m_dil_token)),
+ m_dil_token.getLocation());
+ }
+}
+
+std::string DILParser::TokenDescription(const DILToken &token) {
+ const auto &spelling = ((DILToken)token).getSpelling();
+ const std::string kind_name =
+ DILToken::getTokenName(((DILToken)token).getKind());
+ return llvm::formatv("<'{0}' ({1})>", spelling, kind_name);
+}
+
+} // namespace dil
+
+} // namespace lldb_private
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
new file mode 100644
index 0000000000000..b87de2ab27ebb
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -0,0 +1,82 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILGlobalVariableLookup(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ self.do_test()
+
+ def do_test(self):
+ target = self.createTestTarget()
+
+ # Now create a breakpoint in main.c at the source matching
+ # "Set a breakpoint here"
+ breakpoint = target.BreakpointCreateBySourceRegex(
+ "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+ self.assertTrue(
+ breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
+ )
+
+ error = lldb.SBError()
+ # This is the launch info. If you want to launch with arguments or
+ # environment variables, add them using SetArguments or
+ # SetEnvironmentEntries
+
+ launch_info = target.GetLaunchInfo()
+ process = target.Launch(launch_info, error)
+ self.assertTrue(process, PROCESS_IS_VALID)
+
+ # Did we hit our breakpoint?
+ from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
+
+ threads = get_threads_stopped_at_breakpoint(process, breakpoint)
+ self.assertEqual(
+ len(threads), 1, "There should be a thread stopped at our breakpoint"
+ )
+ # The hit count for the breakpoint should be 1.
+ self.assertEquals(breakpoint.GetHitCount(), 1)
+
+ frame = threads[0].GetFrameAtIndex(0)
+ command_result = lldb.SBCommandReturnObject()
+ interp = self.dbg.GetCommandInterpreter()
+
+
+ self.expect("settings set target.experimental.use-DIL true",
+ substrs=[""])
+ self.expect("frame variable 'globalVar'", substrs=["-559038737"]) # 0xDEADBEEF
+ self.expect("frame variable 'globalPtr'", patterns=["0x[0-9]+"])
+ self.expect("frame variable 'globalRef'", substrs=["-559038737"])
+ self.expect("frame variable '::globalPtr'", patterns=["0x[0-9]+"])
+ self.expect("frame variable '::globalRef'", substrs=["-559038737"])
+
+ self.expect("frame variable 'externGlobalVar'", error=True,
+ substrs=["use of undeclared identifier"]) # 0x00C0FFEE
+ #substrs=["no variable named 'externGlobalVar' found in this frame"]) # 0x00C0FFEE
+ self.expect("frame variable '::externGlobalVar'", error=True,
+ substrs=["use of undeclared identifier"]) # ["12648430"])
+ #substrs=["no variable named '::externGlobalVar' found in this frame"]) # ["12648430"])
+ # "use of undeclared identifier"
+
+ self.expect("frame variable 'ns::globalVar'", substrs=["13"])
+ self.expect("frame variable 'ns::globalPtr'",
+ patterns=["0x[0-9]+"])
+ self.expect("frame variable 'ns::globalRef'", substrs=["13"])
+ self.expect("frame variable '::ns::globalVar'", substrs=["13"])
+ self.expect("frame variable '::ns::globalPtr'",
+ patterns=["0x[0-9]+"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
new file mode 100644
index 0000000000000..605142d169db2
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
@@ -0,0 +1,18 @@
+int globalVar = 0xDEADBEEF;
+extern int externGlobalVar;
+
+int* globalPtr = &globalVar;
+int& globalRef = globalVar;
+
+namespace ns {
+int globalVar = 13;
+int* globalPtr = &globalVar;
+int& globalRef = globalVar;
+} // namespace ns
+
+int
+main(int argc, char **argv)
+{
+
+ return 0; // Set a breakpoint here
+}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
new file mode 100644
index 0000000000000..dcc408f1f37bd
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -0,0 +1,62 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILInstanceVariables(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ self.do_test()
+
+ def do_test(self):
+ target = self.createTestTarget()
+
+ # Now create a breakpoint in main.c at the source matching
+ # "Set a breakpoint here"
+ breakpoint = target.BreakpointCreateBySourceRegex(
+ "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+ self.assertTrue(
+ breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
+ )
+
+ error = lldb.SBError()
+ # This is the launch info. If you want to launch with arguments or
+ # environment variables, add them using SetArguments or
+ # SetEnvironmentEntries
+
+ launch_info = target.GetLaunchInfo()
+ process = target.Launch(launch_info, error)
+ self.assertTrue(process, PROCESS_IS_VALID)
+
+ # Did we hit our breakpoint?
+ from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
+
+ threads = get_threads_stopped_at_breakpoint(process, breakpoint)
+ self.assertEqual(
+ len(threads), 1, "There should be a thread stopped at our breakpoint"
+ )
+ # The hit count for the breakpoint should be 1.
+ self.assertEquals(breakpoint.GetHitCount(), 1)
+
+ frame = threads[0].GetFrameAtIndex(0)
+ command_result = lldb.SBCommandReturnObject()
+ interp = self.dbg.GetCommandInterpreter()
+
+ self.expect("settings set target.experimental.use-DIL true",
+ substrs=[""])
+ self.expect("frame variable 'this'", patterns=["0x[0-9]+"])
+ self.expect("frame variable 'c'", substrs=["(field_ = -1)"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
new file mode 100644
index 0000000000000..276166f97a7bd
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
@@ -0,0 +1,25 @@
+#include <string>
+
+class C {
+ public:
+ int field_ = 1337;
+};
+
+class TestMethods {
+ public:
+ void TestInstanceVariables() {
+ C c;
+ c.field_ = -1;
+
+ return; // Set a breakpoint here
+ }
+};
+
+int
+main(int argc, char **argv)
+{
+ TestMethods tm;
+
+ tm.TestInstanceVariables();
+ return 0;
+}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile
new file mode 100644
index 0000000000000..99998b20bcb05
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/Makefile
@@ -0,0 +1,3 @@
+CXX_SOURCES := main.cpp
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
new file mode 100644
index 0000000000000..c19f09f2f06b6
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -0,0 +1,65 @@
+"""
+Make sure 'frame var' using DIL parser/evaultor works for local variables.
+"""
+
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test import lldbutil
+
+import os
+import shutil
+import time
+
+class TestFrameVarDILLocalVars(TestBase):
+ # If your test case doesn't stress debug info, then
+ # set this to true. That way it won't be run once for
+ # each debug info format.
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def test_frame_var(self):
+ self.build()
+ self.do_test()
+
+ def do_test(self):
+ target = self.createTestTarget()
+
+ # Now create a breakpoint in main.c at the source matching
+ # "Set a breakpoint here"
+ breakpoint = target.BreakpointCreateBySourceRegex(
+ "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
+ self.assertTrue(
+ breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
+ )
+
+ error = lldb.SBError()
+ # This is the launch info. If you want to launch with arguments or
+ # environment variables, add them using SetArguments or
+ # SetEnvironmentEntries
+
+ launch_info = target.GetLaunchInfo()
+ process = target.Launch(launch_info, error)
+ self.assertTrue(process, PROCESS_IS_VALID)
+
+ # Did we hit our breakpoint?
+ from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
+
+ threads = get_threads_stopped_at_breakpoint(process, breakpoint)
+ self.assertEqual(
+ len(threads), 1, "There should be a thread stopped at our breakpoint"
+ )
+ # The hit count for the breakpoint should be 1.
+ self.assertEquals(breakpoint.GetHitCount(), 1)
+
+ frame = threads[0].GetFrameAtIndex(0)
+ command_result = lldb.SBCommandReturnObject()
+ interp = self.dbg.GetCommandInterpreter()
+
+ # Test 'a' is 1
+ self.expect("settings set target.experimental.use-DIL true",
+ substrs=[""])
+ self.expect("frame variable a", substrs=["1"])
+ self.expect("frame variable b", substrs=["2"])
+ self.expect("frame variable c", substrs=["\\xfd"])
+ self.expect("frame variable s", substrs=["4"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
new file mode 100644
index 0000000000000..c0dd24291429a
--- /dev/null
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
@@ -0,0 +1,26 @@
+int
+main(int argc, char **argv)
+{
+ int a = 1;
+ int b = 2;
+
+ char c = -3;
+ unsigned short s = 4;
+
+ return 0; // Set a breakpoint here
+}
+
+/*
+TEST_F(EvalTest, TestLocalVariables) {
+ EXPECT_THAT(Eval("a"), IsEqual("1"));
+ EXPECT_THAT(Eval("b"), IsEqual("2"));
+ EXPECT_THAT(Eval("a + b"), IsEqual("3"));
+
+ EXPECT_THAT(Eval("c + 1"), IsEqual("-2"));
+ EXPECT_THAT(Eval("s + 1"), IsEqual("5"));
+ EXPECT_THAT(Eval("c + s"), IsEqual("1"));
+
+ EXPECT_THAT(Eval("__test_non_variable + 1"),
+ IsError("use of undeclared identifier '__test_non_variable'"));
+}
+*/
>From 680f558bb6e8e0d802f47380f3fa2324d4b3e79f Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Tue, 14 Jan 2025 16:50:02 -0800
Subject: [PATCH 02/21] [LLDB] Add DIL code for handling plain variable names.
Address many review comments.
- Move class IdentifierInfo & funciton LookupIdentifier from DILAST to DILEval.
- Fix include statement formatting.
- Use .str() instead of .data() when converting StringRefs to strings.
- Remove 'else' stmts immediatly following 'return' stmts.
- Clean up some things accidentally left in the code.
- Remove extra, unused DILInterpreter constructors.
- Convert use of std::shared_ptr<std::string> to llvm::StringRef
- Remove unnecessary length member from DILToken class
- Remove currently ununsed function 'ExpectOneOf' from parser
- Update grammar: Remove 'this', use 'C99 Identifier' to define identifier.
- Rename use_dynamic() and name() to GetUseDynamic() and GetName()
- Return strings directly rather than through a return variable
- Remove special handling for "this" from Lexer & from Parser
- Remove unneeded casts for DILToken (old leftover code)
- Small cleanups in tests (removing unneeded comments)
---
lldb/docs/dil-expr-lang.ebnf | 3 +-
lldb/include/lldb/ValueObject/DILAST.h | 60 +----
lldb/include/lldb/ValueObject/DILEval.h | 62 ++++-
lldb/include/lldb/ValueObject/DILLexer.h | 27 +-
lldb/include/lldb/ValueObject/DILParser.h | 17 +-
lldb/source/Target/StackFrame.cpp | 7 +-
lldb/source/ValueObject/DILAST.cpp | 207 ---------------
lldb/source/ValueObject/DILEval.cpp | 236 +++++++++++++++---
lldb/source/ValueObject/DILLexer.cpp | 61 ++---
lldb/source/ValueObject/DILParser.cpp | 64 ++---
.../TestFrameVarDILGlobalVariableLookup.py | 4 -
.../LocalVars/TestFrameVarDILLocalVars.py | 1 -
.../frame/var-dil/basics/LocalVars/main.cpp | 15 --
13 files changed, 324 insertions(+), 440 deletions(-)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 64b3e7758229c..7aca2b34a4929 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -6,7 +6,6 @@
expression = primary_expression ;
primary_expression = id_expression
- | "this"
| "(" expression ")";
id_expression = unqualified_id
@@ -17,7 +16,7 @@ unqualified_id = identifier ;
qualified_id = ["::"] [nested_name_specifier] unqualified_id
| ["::"] identifier ;
-identifier = ? dil::TokenKind::identifier ? ;
+identifier = ? C99 Identifier ? ;
nested_name_specifier = type_name "::"
| namespace_name '::'
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 9f0a1a2221e38..dd041483b835b 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -9,11 +9,8 @@
#ifndef LLDB_VALUEOBJECT_DILAST_H
#define LLDB_VALUEOBJECT_DILAST_H
-#include <memory>
-#include <string>
-#include <vector>
-
#include "lldb/ValueObject/ValueObject.h"
+#include <string>
namespace lldb_private {
@@ -25,51 +22,6 @@ enum class NodeKind {
eIdentifierNode,
};
-/// Class used to store & manipulate information about identifiers.
-class IdentifierInfo {
-public:
- enum class Kind {
- eValue,
- eContextArg,
- };
-
- static std::unique_ptr<IdentifierInfo> FromValue(ValueObject &valobj) {
- CompilerType type;
- type = valobj.GetCompilerType();
- return std::unique_ptr<IdentifierInfo>(
- new IdentifierInfo(Kind::eValue, type, valobj.GetSP(), {}));
- }
-
- static std::unique_ptr<IdentifierInfo> FromContextArg(CompilerType type) {
- lldb::ValueObjectSP empty_value;
- return std::unique_ptr<IdentifierInfo>(
- new IdentifierInfo(Kind::eContextArg, type, empty_value, {}));
- }
-
- Kind GetKind() const { return m_kind; }
- lldb::ValueObjectSP GetValue() const { return m_value; }
-
- CompilerType GetType() { return m_type; }
- bool IsValid() const { return m_type.IsValid(); }
-
- IdentifierInfo(Kind kind, CompilerType type, lldb::ValueObjectSP value,
- std::vector<uint32_t> path)
- : m_kind(kind), m_type(type), m_value(std::move(value)) {}
-
-private:
- Kind m_kind;
- CompilerType m_type;
- lldb::ValueObjectSP m_value;
-};
-
-/// Given the name of an identifier (variable name, member name, type name,
-/// etc.), find the ValueObject for that name (if it exists) and create and
-/// return an IdentifierInfo object containing all the relevant information
-/// about that object (for DIL parsing and evaluating).
-std::unique_ptr<IdentifierInfo> LookupIdentifier(
- const std::string &name, std::shared_ptr<ExecutionContextScope> ctx_scope,
- lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr);
-
/// Forward declaration, for use in DIL AST nodes. Definition is at the very
/// end of this file.
class Visitor;
@@ -104,16 +56,12 @@ using DILASTNodeUP = std::unique_ptr<DILASTNode>;
class ErrorNode : public DILASTNode {
public:
- ErrorNode(CompilerType empty_type)
- : DILASTNode(0, NodeKind::eErrorNode), m_empty_type(empty_type) {}
+ ErrorNode() : DILASTNode(0, NodeKind::eErrorNode) {}
void Accept(Visitor *v) const override;
static bool classof(const DILASTNode *node) {
return node->GetKind() == NodeKind::eErrorNode;
}
-
-private:
- CompilerType m_empty_type;
};
class IdentifierNode : public DILASTNode {
@@ -127,8 +75,8 @@ class IdentifierNode : public DILASTNode {
void Accept(Visitor *v) const override;
- lldb::DynamicValueType use_dynamic() const { return m_use_dynamic; }
- std::string name() const { return m_name; }
+ lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
+ std::string GetName() const { return m_name; }
std::shared_ptr<ExecutionContextScope> get_exe_context() const {
return m_ctx_scope;
}
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 4006bb10630f2..6bb9a76cd8152 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -9,23 +9,65 @@
#ifndef LLDB_VALUEOBJECT_DILEVAL_H_
#define LLDB_VALUEOBJECT_DILEVAL_H_
-#include <memory>
-#include <vector>
-
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILParser.h"
+#include <memory>
+#include <vector>
namespace lldb_private {
namespace dil {
+/// Class used to store & manipulate information about identifiers.
+class IdentifierInfo {
+public:
+ enum class Kind {
+ eValue,
+ eContextArg,
+ };
+
+ static std::unique_ptr<IdentifierInfo> FromValue(ValueObject &valobj) {
+ CompilerType type;
+ type = valobj.GetCompilerType();
+ return std::unique_ptr<IdentifierInfo>(
+ new IdentifierInfo(Kind::eValue, type, valobj.GetSP(), {}));
+ }
+
+ static std::unique_ptr<IdentifierInfo> FromContextArg(CompilerType type) {
+ lldb::ValueObjectSP empty_value;
+ return std::unique_ptr<IdentifierInfo>(
+ new IdentifierInfo(Kind::eContextArg, type, empty_value, {}));
+ }
+
+ Kind GetKind() const { return m_kind; }
+ lldb::ValueObjectSP GetValue() const { return m_value; }
+
+ CompilerType GetType() { return m_type; }
+ bool IsValid() const { return m_type.IsValid(); }
+
+ IdentifierInfo(Kind kind, CompilerType type, lldb::ValueObjectSP value,
+ std::vector<uint32_t> path)
+ : m_kind(kind), m_type(type), m_value(std::move(value)) {}
+
+private:
+ Kind m_kind;
+ CompilerType m_type;
+ lldb::ValueObjectSP m_value;
+};
+
+/// Given the name of an identifier (variable name, member name, type name,
+/// etc.), find the ValueObject for that name (if it exists) and create and
+/// return an IdentifierInfo object containing all the relevant information
+/// about that object (for DIL parsing and evaluating).
+std::unique_ptr<IdentifierInfo> LookupIdentifier(
+ const std::string &name, std::shared_ptr<ExecutionContextScope> ctx_scope,
+ lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr);
+
class DILInterpreter : Visitor {
public:
- DILInterpreter(lldb::TargetSP target, std::shared_ptr<std::string> sm);
- DILInterpreter(lldb::TargetSP target, std::shared_ptr<std::string> sm,
- lldb::ValueObjectSP scope);
- DILInterpreter(lldb::TargetSP target, std::shared_ptr<std::string> sm,
- lldb::DynamicValueType use_dynamic);
+ DILInterpreter(lldb::TargetSP target, llvm::StringRef expr,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope);
lldb::ValueObjectSP DILEval(const DILASTNode *tree, lldb::TargetSP target_sp,
Status &error);
@@ -44,7 +86,7 @@ class DILInterpreter : Visitor {
// Used by the interpreter to create objects, perform casts, etc.
lldb::TargetSP m_target;
- std::shared_ptr<std::string> m_sm;
+ llvm::StringRef m_expr;
lldb::ValueObjectSP m_result;
@@ -53,6 +95,8 @@ class DILInterpreter : Visitor {
lldb::DynamicValueType m_default_dynamic;
Status m_error;
+
+ std::shared_ptr<ExecutionContextScope> m_exe_ctx_scope;
};
} // namespace dil
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index c794fb2bfc0ed..669ddfb2d4a4b 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -9,15 +9,13 @@
#ifndef LLDB_VALUEOBJECT_DILLEXER_H_
#define LLDB_VALUEOBJECT_DILLEXER_H_
-#include <limits.h>
-
+#include "llvm/ADT/StringRef.h"
#include <cstdint>
+#include <limits.h>
#include <memory>
#include <string>
#include <vector>
-#include "llvm/ADT/StringRef.h"
-
namespace lldb_private {
namespace dil {
@@ -39,20 +37,17 @@ enum class TokenKind {
/// DIL parser.
class DILToken {
public:
- DILToken(dil::TokenKind kind, std::string spelling, uint32_t start,
- uint32_t len)
- : m_kind(kind), m_spelling(spelling), m_start_pos(start), m_length(len) {}
+ DILToken(dil::TokenKind kind, std::string spelling, uint32_t start)
+ : m_kind(kind), m_spelling(spelling), m_start_pos(start) {}
- DILToken()
- : m_kind(dil::TokenKind::none), m_spelling(""), m_start_pos(0),
- m_length(0) {}
+ DILToken() : m_kind(dil::TokenKind::none), m_spelling(""), m_start_pos(0) {}
void setKind(dil::TokenKind kind) { m_kind = kind; }
dil::TokenKind getKind() const { return m_kind; }
std::string getSpelling() const { return m_spelling; }
- uint32_t getLength() const { return m_length; }
+ uint32_t getLength() const { return m_spelling.size(); }
bool is(dil::TokenKind kind) const { return m_kind == kind; }
@@ -68,12 +63,10 @@ class DILToken {
uint32_t getLocation() const { return m_start_pos; }
- void setValues(dil::TokenKind kind, std::string spelling, uint32_t start,
- uint32_t len) {
+ void setValues(dil::TokenKind kind, std::string spelling, uint32_t start) {
m_kind = kind;
m_spelling = spelling;
m_start_pos = start;
- m_length = len;
}
static const std::string getTokenName(dil::TokenKind kind);
@@ -82,13 +75,12 @@ class DILToken {
dil::TokenKind m_kind;
std::string m_spelling;
uint32_t m_start_pos; // within entire expression string
- uint32_t m_length;
};
/// Class for doing the simple lexing required by DIL.
class DILLexer {
public:
- DILLexer(std::shared_ptr<std::string> dil_sm) : m_expr(*dil_sm) {
+ DILLexer(llvm::StringRef dil_expr) : m_expr(dil_expr.str()) {
m_cur_pos = m_expr.begin();
// Use UINT_MAX to indicate invalid/uninitialized value.
m_tokens_idx = UINT_MAX;
@@ -104,8 +96,7 @@ class DILLexer {
/// duplicate token, and push the duplicate token onto the vector of
/// lexed tokens.
void UpdateLexedTokens(DILToken &result, dil::TokenKind tok_kind,
- std::string tok_str, uint32_t tok_pos,
- uint32_t tok_len);
+ std::string tok_str, uint32_t tok_pos);
/// Return the lexed token N+1 positions ahead of the 'current' token
/// being handled by the DIL parser.
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index b718903b7bea4..f8db81b1aee5b 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -9,17 +9,16 @@
#ifndef LLDB_VALUEOBJECT_DILPARSER_H_
#define LLDB_VALUEOBJECT_DILPARSER_H_
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/ValueObject/DILAST.h"
+#include "lldb/ValueObject/DILLexer.h"
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <vector>
-#include "lldb/Target/ExecutionContextScope.h"
-#include "lldb/Utility/Status.h"
-#include "lldb/ValueObject/DILAST.h"
-#include "lldb/ValueObject/DILLexer.h"
-
namespace lldb_private {
namespace dil {
@@ -31,14 +30,14 @@ enum class ErrorCode : unsigned char {
kUnknown,
};
-std::string FormatDiagnostics(std::shared_ptr<std::string> input_expr,
+std::string FormatDiagnostics(llvm::StringRef input_expr,
const std::string &message, uint32_t loc);
/// 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:
- explicit DILParser(std::shared_ptr<std::string> dil_input_expr,
+ explicit DILParser(llvm::StringRef dil_input_expr,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member);
@@ -72,8 +71,6 @@ class DILParser {
std::string TokenDescription(const DILToken &token);
- template <typename... Ts> void ExpectOneOf(dil::TokenKind k, Ts... ks);
-
void TentativeParsingRollback(uint32_t saved_idx) {
m_error.Clear();
m_dil_lexer.ResetTokenIdx(saved_idx);
@@ -85,7 +82,7 @@ class DILParser {
// context will outlive the parser.
std::shared_ptr<ExecutionContextScope> m_ctx_scope;
- std::shared_ptr<std::string> m_input_expr;
+ llvm::StringRef m_input_expr;
// The token lexer is stopped at (aka "current token").
DILToken m_dil_token;
// Holds an error if it occures during parsing.
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 28450b1d1c1c1..357ec2e47a532 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -528,8 +528,6 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error) {
ValueObjectSP ret_val;
- std::shared_ptr<std::string> source =
- std::make_shared<std::string>(var_expr.data());
const bool check_ptr_vs_member =
(options & eExpressionPathOptionCheckPtrVsMember) != 0;
@@ -540,7 +538,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
// Parse the expression.
Status parse_error, eval_error;
- dil::DILParser parser(source, shared_from_this(), use_dynamic,
+ dil::DILParser parser(var_expr, shared_from_this(), use_dynamic,
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
dil::DILASTNodeUP tree = parser.Run(parse_error);
if (parse_error.Fail()) {
@@ -550,7 +548,8 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
// Evaluate the parsed expression.
lldb::TargetSP target = this->CalculateTarget();
- dil::DILInterpreter interpreter(target, source, use_dynamic);
+ dil::DILInterpreter interpreter(target, var_expr, use_dynamic,
+ shared_from_this());
ret_val = interpreter.DILEval(tree.get(), target, eval_error);
if (eval_error.Fail()) {
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index 1af0dc57ddcea..9aebe7e2ca4f9 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -8,217 +8,10 @@
#include "lldb/ValueObject/DILAST.h"
-#include "lldb/API/SBType.h"
-#include "lldb/Symbol/TypeList.h"
-#include "lldb/Symbol/VariableList.h"
-#include "lldb/Target/LanguageRuntime.h"
-#include "lldb/Target/RegisterContext.h"
-#include "lldb/ValueObject/ValueObjectRegister.h"
-#include "lldb/ValueObject/ValueObjectVariable.h"
-#include "llvm/ADT/StringRef.h"
-
-#include <vector>
-
namespace lldb_private {
namespace dil {
-static lldb::ValueObjectSP
-LookupStaticIdentifier(lldb::TargetSP target_sp,
- const llvm::StringRef &name_ref,
- ConstString unqualified_name) {
- // List global variable with the same "basename". There can be many matches
- // from other scopes (namespaces, classes), so we do additional filtering
- // later.
- VariableList variable_list;
- ConstString name(name_ref);
- target_sp->GetImages().FindGlobalVariables(name, 1, variable_list);
- if (!variable_list.Empty()) {
- ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
- if (exe_scope == nullptr)
- exe_scope = target_sp.get();
- for (const lldb::VariableSP &var_sp : variable_list) {
- lldb::ValueObjectSP valobj_sp(
- ValueObjectVariable::Create(exe_scope, var_sp));
- if (valobj_sp && valobj_sp->GetVariable() &&
- (valobj_sp->GetVariable()->NameMatches(unqualified_name) ||
- valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
- return valobj_sp;
- }
- }
- return nullptr;
-}
-
-static lldb::VariableSP DILFindVariable(ConstString name,
- VariableList *variable_list) {
- lldb::VariableSP exact_match;
- std::vector<lldb::VariableSP> possible_matches;
-
- typedef std::vector<lldb::VariableSP> collection;
- typedef collection::iterator iterator;
-
- iterator pos, end = variable_list->end();
- for (pos = variable_list->begin(); pos != end; ++pos) {
- llvm::StringRef str_ref_name = pos->get()->GetName().GetStringRef();
- // Check for global vars, which might start with '::'.
- str_ref_name.consume_front("::");
-
- if (str_ref_name == name.GetStringRef())
- possible_matches.push_back(*pos);
- else if (pos->get()->NameMatches(name))
- possible_matches.push_back(*pos);
- }
-
- // Look for exact matches (favors local vars over global vars)
- auto exact_match_it =
- llvm::find_if(possible_matches, [&](lldb::VariableSP var_sp) {
- return var_sp->GetName() == name;
- });
-
- if (exact_match_it != llvm::adl_end(possible_matches))
- exact_match = *exact_match_it;
-
- if (!exact_match)
- // Look for a global var exact match.
- for (auto var_sp : possible_matches) {
- llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
- if (str_ref_name.size() > 2 && str_ref_name[0] == ':' &&
- str_ref_name[1] == ':')
- str_ref_name = str_ref_name.drop_front(2);
- ConstString tmp_name(str_ref_name);
- if (tmp_name == name) {
- exact_match = var_sp;
- break;
- }
- }
-
- // Take any match at this point.
- if (!exact_match && possible_matches.size() > 0)
- exact_match = possible_matches[0];
-
- return exact_match;
-}
-
-std::unique_ptr<IdentifierInfo>
-LookupIdentifier(const std::string &name,
- std::shared_ptr<ExecutionContextScope> ctx_scope,
- lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
- ConstString name_str(name);
- llvm::StringRef name_ref = name_str.GetStringRef();
-
- // Support $rax as a special syntax for accessing registers.
- // Will return an invalid value in case the requested register doesn't exist.
- if (name_ref.starts_with("$")) {
- lldb::ValueObjectSP value_sp;
- const char *reg_name = name_ref.drop_front(1).data();
- Target *target = ctx_scope->CalculateTarget().get();
- Process *process = ctx_scope->CalculateProcess().get();
- if (target && process) {
- StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
- if (stack_frame) {
- lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
- if (reg_ctx) {
- if (const RegisterInfo *reg_info =
- reg_ctx->GetRegisterInfoByName(reg_name))
- value_sp =
- ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
- }
- }
- }
- if (value_sp)
- return IdentifierInfo::FromValue(*value_sp);
- else
- return nullptr;
- }
-
- // Internally values don't have global scope qualifier in their names and
- // LLDB doesn't support queries with it too.
- bool global_scope = false;
- if (name_ref.starts_with("::")) {
- name_ref = name_ref.drop_front(2);
- global_scope = true;
- }
-
- // If the identifier doesn't refer to the global scope and doesn't have any
- // other scope qualifiers, try looking among the local and instance variables.
- if (!global_scope && !name_ref.contains("::")) {
- if (!scope_ptr || !scope_ptr->IsValid()) {
- // Lookup in the current frame.
- lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
- // Try looking for a local variable in current scope.
- lldb::ValueObjectSP value_sp;
- lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
- VariableList *variable_list = var_list_sp.get();
- if (variable_list) {
- lldb::VariableSP var_sp =
- DILFindVariable(ConstString(name_ref), variable_list);
- if (var_sp)
- value_sp = frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
- }
- if (!value_sp)
- value_sp = frame->FindVariable(ConstString(name_ref));
-
- if (value_sp)
- // Force static value, otherwise we can end up with the "real" type.
- return IdentifierInfo::FromValue(*value_sp);
-
- // Try looking for an instance variable (class member).
- ConstString this_string("this");
- value_sp = frame->FindVariable(this_string);
- if (value_sp)
- value_sp = value_sp->GetChildMemberWithName(name_ref.data());
-
- if (value_sp)
- // Force static value, otherwise we can end up with the "real" type.
- return IdentifierInfo::FromValue(*(value_sp->GetStaticValue()));
- }
- }
-
- // Try looking for a global or static variable.
-
- lldb::ValueObjectSP value;
- if (!global_scope) {
- // Try looking for static member of the current scope value, e.g.
- // `ScopeType::NAME`. NAME can include nested struct (`Nested::SUBNAME`),
- // but it cannot be part of the global scope (start with "::").
- const char *type_name = "";
- if (scope_ptr)
- type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
- std::string name_with_type_prefix =
- llvm::formatv("{0}::{1}", type_name, name_ref).str();
- value = LookupStaticIdentifier(ctx_scope->CalculateTarget(),
- name_with_type_prefix, name_str);
- }
-
- // Lookup a regular global variable.
- if (!value)
- value = LookupStaticIdentifier(ctx_scope->CalculateTarget(), name_ref,
- name_str);
-
- // Last resort, lookup as a register (e.g. `rax` or `rip`).
- if (!value) {
- Target *target = ctx_scope->CalculateTarget().get();
- Process *process = ctx_scope->CalculateProcess().get();
- if (target && process) {
- StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
- if (stack_frame) {
- lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
- if (reg_ctx) {
- if (const RegisterInfo *reg_info =
- reg_ctx->GetRegisterInfoByName(name_ref.data()))
- value = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
- }
- }
- }
- }
-
- // Force static value, otherwise we can end up with the "real" type.
- if (value)
- return IdentifierInfo::FromValue(*value);
- else
- return nullptr;
-}
-
void ErrorNode::Accept(Visitor *v) const { v->Visit(this); }
void IdentifierNode::Accept(Visitor *v) const { v->Visit(this); }
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 01da852153650..7a6fcca58a2b9 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -7,44 +7,222 @@
//===----------------------------------------------------------------------===//
#include "lldb/ValueObject/DILEval.h"
-
-#include <memory>
-
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/RegisterContext.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/ValueObject.h"
+#include "lldb/ValueObject/ValueObjectRegister.h"
+#include "lldb/ValueObject/ValueObjectVariable.h"
#include "llvm/Support/FormatAdapters.h"
+#include <memory>
namespace lldb_private {
namespace dil {
-DILInterpreter::DILInterpreter(lldb::TargetSP target,
- std::shared_ptr<std::string> sm)
- : m_target(std::move(target)), m_sm(std::move(sm)) {
- m_default_dynamic = lldb::eNoDynamicValues;
+static lldb::ValueObjectSP
+LookupStaticIdentifier(lldb::TargetSP target_sp,
+ const llvm::StringRef &name_ref,
+ ConstString unqualified_name) {
+ // List global variable with the same "basename". There can be many matches
+ // from other scopes (namespaces, classes), so we do additional filtering
+ // later.
+ VariableList variable_list;
+ ConstString name(name_ref);
+ target_sp->GetImages().FindGlobalVariables(name, 1, variable_list);
+ if (!variable_list.Empty()) {
+ ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target_sp.get();
+ for (const lldb::VariableSP &var_sp : variable_list) {
+ lldb::ValueObjectSP valobj_sp(
+ ValueObjectVariable::Create(exe_scope, var_sp));
+ if (valobj_sp && valobj_sp->GetVariable() &&
+ (valobj_sp->GetVariable()->NameMatches(unqualified_name) ||
+ valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
+ return valobj_sp;
+ }
+ }
+ return nullptr;
}
-DILInterpreter::DILInterpreter(lldb::TargetSP target,
- std::shared_ptr<std::string> sm,
- lldb::DynamicValueType use_dynamic)
- : m_target(std::move(target)), m_sm(std::move(sm)),
- m_default_dynamic(use_dynamic) {}
-
-DILInterpreter::DILInterpreter(lldb::TargetSP target,
- std::shared_ptr<std::string> sm,
- lldb::ValueObjectSP scope)
- : m_target(std::move(target)), m_sm(std::move(sm)),
- m_scope(std::move(scope)) {
- m_default_dynamic = lldb::eNoDynamicValues;
- // If `m_scope` is a reference, dereference it. All operations on a reference
- // should be operations on the referent.
- if (m_scope->GetCompilerType().IsValid() &&
- m_scope->GetCompilerType().IsReferenceType()) {
- Status error;
- m_scope = m_scope->Dereference(error);
+static lldb::VariableSP DILFindVariable(ConstString name,
+ VariableList *variable_list) {
+ lldb::VariableSP exact_match;
+ std::vector<lldb::VariableSP> possible_matches;
+
+ typedef std::vector<lldb::VariableSP> collection;
+ typedef collection::iterator iterator;
+
+ iterator pos, end = variable_list->end();
+ for (pos = variable_list->begin(); pos != end; ++pos) {
+ llvm::StringRef str_ref_name = pos->get()->GetName().GetStringRef();
+ // Check for global vars, which might start with '::'.
+ str_ref_name.consume_front("::");
+
+ if (str_ref_name == name.GetStringRef())
+ possible_matches.push_back(*pos);
+ else if (pos->get()->NameMatches(name))
+ possible_matches.push_back(*pos);
+ }
+
+ // Look for exact matches (favors local vars over global vars)
+ auto exact_match_it =
+ llvm::find_if(possible_matches, [&](lldb::VariableSP var_sp) {
+ return var_sp->GetName() == name;
+ });
+
+ if (exact_match_it != llvm::adl_end(possible_matches))
+ exact_match = *exact_match_it;
+
+ if (!exact_match)
+ // Look for a global var exact match.
+ for (auto var_sp : possible_matches) {
+ llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
+ if (str_ref_name.size() > 2 && str_ref_name[0] == ':' &&
+ str_ref_name[1] == ':')
+ str_ref_name = str_ref_name.drop_front(2);
+ ConstString tmp_name(str_ref_name);
+ if (tmp_name == name) {
+ exact_match = var_sp;
+ break;
+ }
+ }
+
+ // Take any match at this point.
+ if (!exact_match && possible_matches.size() > 0)
+ exact_match = possible_matches[0];
+
+ return exact_match;
+}
+
+std::unique_ptr<IdentifierInfo>
+LookupIdentifier(const std::string &name,
+ std::shared_ptr<ExecutionContextScope> ctx_scope,
+ lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
+ ConstString name_str(name);
+ llvm::StringRef name_ref = name_str.GetStringRef();
+
+ // Support $rax as a special syntax for accessing registers.
+ // Will return an invalid value in case the requested register doesn't exist.
+ if (name_ref.starts_with("$")) {
+ lldb::ValueObjectSP value_sp;
+ const char *reg_name = name_ref.drop_front(1).data();
+ Target *target = ctx_scope->CalculateTarget().get();
+ Process *process = ctx_scope->CalculateProcess().get();
+ if (target && process) {
+ StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+ if (stack_frame) {
+ lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+ if (reg_ctx) {
+ if (const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(reg_name))
+ value_sp =
+ ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+ }
+ }
+ }
+ if (value_sp)
+ return IdentifierInfo::FromValue(*value_sp);
+
+ return nullptr;
+ }
+
+ // Internally values don't have global scope qualifier in their names and
+ // LLDB doesn't support queries with it too.
+ bool global_scope = false;
+ if (name_ref.starts_with("::")) {
+ name_ref = name_ref.drop_front(2);
+ global_scope = true;
}
+
+ // If the identifier doesn't refer to the global scope and doesn't have any
+ // other scope qualifiers, try looking among the local and instance variables.
+ if (!global_scope && !name_ref.contains("::")) {
+ if (!scope_ptr || !scope_ptr->IsValid()) {
+ // Lookup in the current frame.
+ lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
+ // Try looking for a local variable in current scope.
+ lldb::ValueObjectSP value_sp;
+ lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
+ VariableList *variable_list = var_list_sp.get();
+ if (variable_list) {
+ lldb::VariableSP var_sp =
+ DILFindVariable(ConstString(name_ref), variable_list);
+ if (var_sp)
+ value_sp = frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ }
+ if (!value_sp)
+ value_sp = frame->FindVariable(ConstString(name_ref));
+
+ if (value_sp)
+ // Force static value, otherwise we can end up with the "real" type.
+ return IdentifierInfo::FromValue(*value_sp);
+
+ // Try looking for an instance variable (class member).
+ ConstString this_string("this");
+ value_sp = frame->FindVariable(this_string);
+ if (value_sp)
+ value_sp = value_sp->GetChildMemberWithName(name_ref);
+
+ if (value_sp)
+ // Force static value, otherwise we can end up with the "real" type.
+ return IdentifierInfo::FromValue(*(value_sp->GetStaticValue()));
+ }
+ }
+
+ // Try looking for a global or static variable.
+
+ lldb::ValueObjectSP value;
+ if (!global_scope) {
+ // Try looking for static member of the current scope value, e.g.
+ // `ScopeType::NAME`. NAME can include nested struct (`Nested::SUBNAME`),
+ // but it cannot be part of the global scope (start with "::").
+ const char *type_name = "";
+ if (scope_ptr)
+ type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
+ std::string name_with_type_prefix =
+ llvm::formatv("{0}::{1}", type_name, name_ref).str();
+ value = LookupStaticIdentifier(ctx_scope->CalculateTarget(),
+ name_with_type_prefix, name_str);
+ }
+
+ // Lookup a regular global variable.
+ if (!value)
+ value = LookupStaticIdentifier(ctx_scope->CalculateTarget(), name_ref,
+ name_str);
+
+ // Last resort, lookup as a register (e.g. `rax` or `rip`).
+ if (!value) {
+ Target *target = ctx_scope->CalculateTarget().get();
+ Process *process = ctx_scope->CalculateProcess().get();
+ if (target && process) {
+ StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+ if (stack_frame) {
+ lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+ if (reg_ctx) {
+ if (const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(name_ref))
+ value = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+ }
+ }
+ }
+ }
+
+ // Force static value, otherwise we can end up with the "real" type.
+ if (value)
+ return IdentifierInfo::FromValue(*value);
+
+ return nullptr;
}
+DILInterpreter::DILInterpreter(
+ lldb::TargetSP target, llvm::StringRef expr,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
+ : m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
+ m_exe_ctx_scope(exe_ctx_scope) {}
+
lldb::ValueObjectSP DILInterpreter::DILEval(const DILASTNode *tree,
lldb::TargetSP target_sp,
Status &error) {
@@ -70,7 +248,7 @@ lldb::ValueObjectSP DILInterpreter::DILEvalNode(const DILASTNode *node) {
void DILInterpreter::SetError(ErrorCode code, std::string error, uint32_t loc) {
assert(m_error.Success() && "interpreter can error only once");
m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
- FormatDiagnostics(m_sm, error, loc));
+ FormatDiagnostics(m_expr, error, loc));
}
void DILInterpreter::Visit(const ErrorNode *node) {
@@ -81,14 +259,14 @@ void DILInterpreter::Visit(const ErrorNode *node) {
void DILInterpreter::Visit(const IdentifierNode *node) {
std::shared_ptr<ExecutionContextScope> exe_ctx_scope =
node->get_exe_context();
- lldb::DynamicValueType use_dynamic = node->use_dynamic();
+ lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
std::unique_ptr<IdentifierInfo> identifier =
- LookupIdentifier(node->name(), exe_ctx_scope, use_dynamic);
+ LookupIdentifier(node->GetName(), exe_ctx_scope, use_dynamic);
if (!identifier) {
std::string errMsg;
- std::string name = node->name();
+ std::string name = node->GetName();
if (name == "this")
errMsg = "invalid use of 'this' outside of a non-static member function";
else
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
index c63a83bea6f1d..79b699d0d2d2d 100644
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ b/lldb/source/ValueObject/DILLexer.cpp
@@ -18,37 +18,24 @@ namespace lldb_private {
namespace dil {
const std::string DILToken::getTokenName(dil::TokenKind kind) {
- std::string retval;
switch (kind) {
case dil::TokenKind::coloncolon:
- retval = "coloncolon";
- break;
+ return "coloncolon";
case dil::TokenKind::eof:
- retval = "eof";
- break;
+ return "eof";
case dil::TokenKind::identifier:
- retval = "identifier";
- break;
+ return "identifier";
case dil::TokenKind::kw_namespace:
- retval = "namespace";
- break;
- case dil::TokenKind::kw_this:
- retval = "this";
- break;
+ return "namespace";
case dil::TokenKind::l_paren:
- retval = "l_paren";
- break;
+ return "l_paren";
case dil::TokenKind::r_paren:
- retval = "r_paren";
- break;
+ return "r_paren";
case dil::TokenKind::unknown:
- retval = "unknown";
- break;
+ return "unknown";
default:
- retval = "token_name";
- break;
+ return "token_name";
}
- return retval;
}
static bool Is_Letter(char c) {
@@ -71,16 +58,15 @@ bool DILLexer::Is_Word(std::string::iterator start, uint32_t &length) {
}
if (length > 0)
return true;
- else
- m_cur_pos = start;
+
+ m_cur_pos = start;
return false;
}
void DILLexer::UpdateLexedTokens(DILToken &result, dil::TokenKind tok_kind,
- std::string tok_str, uint32_t tok_pos,
- uint32_t tok_len) {
+ std::string tok_str, uint32_t tok_pos) {
DILToken new_token;
- result.setValues(tok_kind, tok_str, tok_pos, tok_len);
+ result.setValues(tok_kind, tok_str, tok_pos);
new_token = result;
m_lexed_tokens.push_back(std::move(new_token));
}
@@ -106,7 +92,7 @@ bool DILLexer::Lex(DILToken &result, bool look_ahead) {
// Check to see if we've reached the end of our input string.
if (m_cur_pos == m_expr.end()) {
- UpdateLexedTokens(result, dil::TokenKind::eof, "", m_expr.length(), 0);
+ UpdateLexedTokens(result, dil::TokenKind::eof, "", m_expr.length());
return retval;
}
@@ -117,30 +103,28 @@ bool DILLexer::Lex(DILToken &result, bool look_ahead) {
if (Is_Word(start, length)) {
dil::TokenKind kind;
std::string word = m_expr.substr(position, length);
- if (word == "this")
- kind = dil::TokenKind::kw_this;
- else if (word == "namespace")
+ if (word == "namespace")
kind = dil::TokenKind::kw_namespace;
else
kind = dil::TokenKind::identifier;
- UpdateLexedTokens(result, kind, word, position, length);
+ UpdateLexedTokens(result, kind, word, position);
return true;
}
switch (*m_cur_pos) {
case '(':
m_cur_pos++;
- UpdateLexedTokens(result, dil::TokenKind::l_paren, "(", position, 1);
+ UpdateLexedTokens(result, dil::TokenKind::l_paren, "(", position);
return true;
case ')':
m_cur_pos++;
- UpdateLexedTokens(result, dil::TokenKind::r_paren, ")", position, 1);
+ UpdateLexedTokens(result, dil::TokenKind::r_paren, ")", position);
return true;
case ':':
if (position + 1 < m_expr.size() && m_expr[position + 1] == ':') {
m_cur_pos += 2;
- UpdateLexedTokens(result, dil::TokenKind::coloncolon, "::", position, 2);
+ UpdateLexedTokens(result, dil::TokenKind::coloncolon, "::", position);
return true;
}
break;
@@ -148,7 +132,7 @@ bool DILLexer::Lex(DILToken &result, bool look_ahead) {
break;
}
// Empty Token
- result.setValues(dil::TokenKind::none, "", m_expr.length(), 0);
+ result.setValues(dil::TokenKind::none, "", m_expr.length());
return false;
}
@@ -172,10 +156,11 @@ const DILToken &DILLexer::LookAhead(uint32_t N) {
};
if (remaining_tokens > 0) {
- m_invalid_token.setValues(dil::TokenKind::invalid, "", 0, 0);
+ m_invalid_token.setValues(dil::TokenKind::invalid, "", 0);
return m_invalid_token;
- } else
- return m_lexed_tokens[m_tokens_idx + N + 1];
+ }
+
+ return m_lexed_tokens[m_tokens_idx + N + 1];
}
const DILToken &DILLexer::AcceptLookAhead(uint32_t N) {
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index eb36de80be76a..147a76ba8f9d6 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -12,19 +12,16 @@
//===----------------------------------------------------------------------===//
#include "lldb/ValueObject/DILParser.h"
-
-#include <limits.h>
-#include <stdlib.h>
-
-#include <memory>
-#include <sstream>
-#include <string>
-
#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILEval.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatAdapters.h"
+#include <limits.h>
+#include <memory>
+#include <sstream>
+#include <stdlib.h>
+#include <string>
namespace lldb_private {
@@ -50,10 +47,9 @@ inline std::string TokenKindsJoin(dil::TokenKind k, Ts... ks) {
return os.str();
}
-std::string FormatDiagnostics(std::shared_ptr<std::string> input_expr,
- const std::string &message, uint32_t loc) {
+std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
+ uint32_t loc) {
// Get the source buffer and the location of the current token.
- llvm::StringRef text(*input_expr);
size_t loc_offset = (size_t)loc;
// Look for the start of the line.
@@ -79,7 +75,7 @@ std::string FormatDiagnostics(std::shared_ptr<std::string> input_expr,
llvm::fmt_pad("^", arrow - 1, arrow_rpad));
}
-DILParser::DILParser(std::shared_ptr<std::string> dil_input_expr,
+DILParser::DILParser(llvm::StringRef dil_input_expr,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member)
@@ -106,10 +102,9 @@ DILASTNodeUP DILParser::Run(Status &error) {
// Explicitly return ErrorNode if there was an error during the parsing.
// Some routines raise an error, but don't change the return value (e.g.
// Expect).
- if (error.Fail()) {
- CompilerType bad_type;
- return std::make_unique<ErrorNode>(bad_type);
- }
+ if (error.Fail())
+ return std::make_unique<ErrorNode>();
+
return expr;
}
@@ -128,7 +123,6 @@ DILASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
// "(" expression ")"
//
DILASTNodeUP DILParser::ParsePrimaryExpression() {
- CompilerType bad_type;
if (m_dil_token.isOneOf(dil::TokenKind::coloncolon,
dil::TokenKind::identifier)) {
// Save the source location for the diagnostics message.
@@ -137,14 +131,6 @@ DILASTNodeUP DILParser::ParsePrimaryExpression() {
return std::make_unique<IdentifierNode>(loc, identifier, m_use_dynamic,
m_ctx_scope);
- } else if (m_dil_token.is(dil::TokenKind::kw_this)) {
- // Save the source location for the diagnostics message.
- uint32_t loc = m_dil_token.getLocation();
- ConsumeToken();
-
- // Special case for "this" pointer. As per C++ standard, it's a prvalue.
- return std::make_unique<IdentifierNode>(loc, "this", m_use_dynamic,
- m_ctx_scope);
} else if (m_dil_token.is(dil::TokenKind::l_paren)) {
ConsumeToken();
auto expr = ParseExpression();
@@ -156,7 +142,7 @@ DILASTNodeUP DILParser::ParsePrimaryExpression() {
BailOut(ErrorCode::kInvalidExpressionSyntax,
llvm::formatv("Unexpected token: {0}", TokenDescription(m_dil_token)),
m_dil_token.getLocation());
- return std::make_unique<ErrorNode>(bad_type);
+ return std::make_unique<ErrorNode>();
}
// Parse nested_name_specifier.
@@ -182,7 +168,7 @@ std::string DILParser::ParseNestedNameSpecifier() {
// Look for all the pieces, in order:
// l_paren 'anonymous' 'namespace' r_paren coloncolon
if (m_dil_lexer.LookAhead(0).is(dil::TokenKind::identifier) &&
- (((DILToken)m_dil_lexer.LookAhead(0)).getSpelling() == "anonymous") &&
+ ((m_dil_lexer.LookAhead(0)).getSpelling() == "anonymous") &&
m_dil_lexer.LookAhead(1).is(dil::TokenKind::kw_namespace) &&
m_dil_lexer.LookAhead(2).is(dil::TokenKind::r_paren) &&
m_dil_lexer.LookAhead(3).is(dil::TokenKind::coloncolon)) {
@@ -199,9 +185,9 @@ std::string DILParser::ParseNestedNameSpecifier() {
ConsumeToken();
}
return "(anonymous namespace)::" + identifier2;
- } else {
- return "";
}
+
+ return "";
} // end of special handling for '(anonymous namespace)'
// If the next token is scope ("::"), then this is indeed a
@@ -329,25 +315,9 @@ void DILParser::Expect(dil::TokenKind kind) {
}
}
-template <typename... Ts>
-void DILParser::ExpectOneOf(dil::TokenKind k, Ts... ks) {
- static_assert((std::is_same_v<Ts, dil::TokenKind> && ...),
- "ExpectOneOf can be only called with values of type "
- "dil::TokenKind");
-
- if (!m_dil_token.isOneOf(k, ks...)) {
- BailOut(ErrorCode::kUnknown,
- llvm::formatv("expected any of ({0}), got: {1}",
- TokenKindsJoin(k, ks...),
- TokenDescription(m_dil_token)),
- m_dil_token.getLocation());
- }
-}
-
std::string DILParser::TokenDescription(const DILToken &token) {
- const auto &spelling = ((DILToken)token).getSpelling();
- const std::string kind_name =
- DILToken::getTokenName(((DILToken)token).getKind());
+ const auto &spelling = token.getSpelling();
+ const std::string kind_name = DILToken::getTokenName(token.getKind());
return llvm::formatv("<'{0}' ({1})>", spelling, kind_name);
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index b87de2ab27ebb..252dcacb13bbd 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -67,12 +67,8 @@ def do_test(self):
self.expect("frame variable 'externGlobalVar'", error=True,
substrs=["use of undeclared identifier"]) # 0x00C0FFEE
- #substrs=["no variable named 'externGlobalVar' found in this frame"]) # 0x00C0FFEE
self.expect("frame variable '::externGlobalVar'", error=True,
substrs=["use of undeclared identifier"]) # ["12648430"])
- #substrs=["no variable named '::externGlobalVar' found in this frame"]) # ["12648430"])
- # "use of undeclared identifier"
-
self.expect("frame variable 'ns::globalVar'", substrs=["13"])
self.expect("frame variable 'ns::globalPtr'",
patterns=["0x[0-9]+"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index c19f09f2f06b6..b2f1d3a07912a 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -56,7 +56,6 @@ def do_test(self):
command_result = lldb.SBCommandReturnObject()
interp = self.dbg.GetCommandInterpreter()
- # Test 'a' is 1
self.expect("settings set target.experimental.use-DIL true",
substrs=[""])
self.expect("frame variable a", substrs=["1"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
index c0dd24291429a..21f0fd6d62c4a 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
@@ -9,18 +9,3 @@ main(int argc, char **argv)
return 0; // Set a breakpoint here
}
-
-/*
-TEST_F(EvalTest, TestLocalVariables) {
- EXPECT_THAT(Eval("a"), IsEqual("1"));
- EXPECT_THAT(Eval("b"), IsEqual("2"));
- EXPECT_THAT(Eval("a + b"), IsEqual("3"));
-
- EXPECT_THAT(Eval("c + 1"), IsEqual("-2"));
- EXPECT_THAT(Eval("s + 1"), IsEqual("5"));
- EXPECT_THAT(Eval("c + s"), IsEqual("1"));
-
- EXPECT_THAT(Eval("__test_non_variable + 1"),
- IsError("use of undeclared identifier '__test_non_variable'"));
-}
-*/
>From e21728a4fc20e9856573bb8ebf9d845722fa969a Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Wed, 15 Jan 2025 13:08:34 -0800
Subject: [PATCH 03/21] [LLDB] Add DIL code for handling plain variable names.
Address the remaining review comments:
- Remove code that updates var_sp.
- Remove ExecutionContextScope member from IdentifierNode class; add to DILInte\
rpreter class.
- Update DILInterpreter::EvalNode and DILParser::Run to return llvm::Expected v\
alues; update error handling accordingly.
- Remove code to update var_sp from DILGetValueForVariableExpressionPath.
- Remove special handling for "this" from DILEval; use GetInstanceVariableName \
instead.
---
lldb/include/lldb/ValueObject/DILAST.h | 7 +------
lldb/include/lldb/ValueObject/DILEval.h | 4 ++--
lldb/include/lldb/ValueObject/DILParser.h | 2 +-
lldb/source/Target/StackFrame.cpp | 22 +++++++---------------
lldb/source/ValueObject/DILEval.cpp | 23 ++++++++++++-----------
lldb/source/ValueObject/DILParser.cpp | 12 +++---------
6 files changed, 26 insertions(+), 44 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index dd041483b835b..1bb11695e3a4d 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -70,16 +70,12 @@ class IdentifierNode : public DILASTNode {
lldb::DynamicValueType use_dynamic,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
: DILASTNode(location, NodeKind::eIdentifierNode),
- m_name(std::move(name)), m_use_dynamic(use_dynamic),
- m_ctx_scope(exe_ctx_scope) {}
+ m_name(std::move(name)), m_use_dynamic(use_dynamic) {}
void Accept(Visitor *v) const override;
lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
std::string GetName() const { return m_name; }
- std::shared_ptr<ExecutionContextScope> get_exe_context() const {
- return m_ctx_scope;
- }
static bool classof(const DILASTNode *node) {
return node->GetKind() == NodeKind::eIdentifierNode;
@@ -88,7 +84,6 @@ class IdentifierNode : public DILASTNode {
private:
std::string m_name;
lldb::DynamicValueType m_use_dynamic;
- std::shared_ptr<ExecutionContextScope> m_ctx_scope;
};
/// This class contains one Visit method for each specialized type of
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 6bb9a76cd8152..87dc61129850e 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -69,8 +69,8 @@ class DILInterpreter : Visitor {
lldb::DynamicValueType use_dynamic,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope);
- lldb::ValueObjectSP DILEval(const DILASTNode *tree, lldb::TargetSP target_sp,
- Status &error);
+ llvm::Expected<lldb::ValueObjectSP> DILEval(const DILASTNode *tree,
+ lldb::TargetSP target_sp);
private:
lldb::ValueObjectSP DILEvalNode(const DILASTNode *node);
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index f8db81b1aee5b..1ff38808cd093 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -42,7 +42,7 @@ class DILParser {
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member);
- DILASTNodeUP Run(Status &error);
+ llvm::Expected<DILASTNodeUP> Run();
~DILParser() { m_ctx_scope.reset(); }
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 357ec2e47a532..2eb5afef0139d 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -527,7 +527,6 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic,
uint32_t options, lldb::VariableSP &var_sp, Status &error) {
- ValueObjectSP ret_val;
const bool check_ptr_vs_member =
(options & eExpressionPathOptionCheckPtrVsMember) != 0;
@@ -537,12 +536,11 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
(options & eExpressionPathOptionsNoSyntheticChildren) != 0;
// Parse the expression.
- Status parse_error, eval_error;
dil::DILParser parser(var_expr, shared_from_this(), use_dynamic,
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
- dil::DILASTNodeUP tree = parser.Run(parse_error);
- if (parse_error.Fail()) {
- error = std::move(parse_error);
+ auto tree_or_error = parser.Run();
+ if (!tree_or_error) {
+ error = Status::FromError(tree_or_error.takeError());
return ValueObjectSP();
}
@@ -551,19 +549,13 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
dil::DILInterpreter interpreter(target, var_expr, use_dynamic,
shared_from_this());
- ret_val = interpreter.DILEval(tree.get(), target, eval_error);
- if (eval_error.Fail()) {
- error = std::move(eval_error);
+ auto valobj_or_error = interpreter.DILEval((*tree_or_error).get(), target);
+ if (!valobj_or_error) {
+ error = Status::FromError(valobj_or_error.takeError());
return ValueObjectSP();
}
- if (ret_val) {
- var_sp = ret_val->GetVariable();
- if (!var_sp && ret_val->GetParent()) {
- var_sp = ret_val->GetParent()->GetVariable();
- }
- }
- return ret_val;
+ return *valobj_or_error;
}
ValueObjectSP StackFrame::LegacyGetValueForVariableExpressionPath(
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 7a6fcca58a2b9..a22ea5c4a126f 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -160,8 +160,10 @@ LookupIdentifier(const std::string &name,
return IdentifierInfo::FromValue(*value_sp);
// Try looking for an instance variable (class member).
- ConstString this_string("this");
- value_sp = frame->FindVariable(this_string);
+ SymbolContext sc = frame->GetSymbolContext(lldb::eSymbolContextFunction |
+ lldb::eSymbolContextBlock);
+ llvm::StringRef ivar_name = sc.GetInstanceVariableName();
+ value_sp = frame->FindVariable(ConstString(ivar_name));
if (value_sp)
value_sp = value_sp->GetChildMemberWithName(name_ref);
@@ -223,15 +225,16 @@ DILInterpreter::DILInterpreter(
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
m_exe_ctx_scope(exe_ctx_scope) {}
-lldb::ValueObjectSP DILInterpreter::DILEval(const DILASTNode *tree,
- lldb::TargetSP target_sp,
- Status &error) {
+llvm::Expected<lldb::ValueObjectSP>
+DILInterpreter::DILEval(const DILASTNode *tree, lldb::TargetSP target_sp) {
m_error.Clear();
// Evaluate an AST.
DILEvalNode(tree);
- // Set the error.
- error = std::move(m_error);
- // Return the computed result. If there was an error, it will be invalid.
+ // Check for errors.
+ if (m_error.Fail())
+ return m_error.ToError();
+
+ // Return the computed result.
return m_result;
}
@@ -257,12 +260,10 @@ void DILInterpreter::Visit(const ErrorNode *node) {
}
void DILInterpreter::Visit(const IdentifierNode *node) {
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope =
- node->get_exe_context();
lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
std::unique_ptr<IdentifierInfo> identifier =
- LookupIdentifier(node->GetName(), exe_ctx_scope, use_dynamic);
+ LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
if (!identifier) {
std::string errMsg;
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 147a76ba8f9d6..6d0b0f086b89f 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -87,7 +87,7 @@ DILParser::DILParser(llvm::StringRef dil_input_expr,
m_dil_token.setKind(dil::TokenKind::unknown);
}
-DILASTNodeUP DILParser::Run(Status &error) {
+llvm::Expected<DILASTNodeUP> DILParser::Run() {
ConsumeToken();
DILASTNodeUP expr;
@@ -96,14 +96,8 @@ DILASTNodeUP DILParser::Run(Status &error) {
Expect(dil::TokenKind::eof);
- error = std::move(m_error);
- m_error.Clear();
-
- // Explicitly return ErrorNode if there was an error during the parsing.
- // Some routines raise an error, but don't change the return value (e.g.
- // Expect).
- if (error.Fail())
- return std::make_unique<ErrorNode>();
+ if (m_error.Fail())
+ return m_error.ToError();
return expr;
}
>From e4e9cc4e6a4832ad7e937d9a2d8ee6bdc03ffeea Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Wed, 15 Jan 2025 13:28:18 -0800
Subject: [PATCH 04/21] [LLDB] Add DIL code for handling plain variable names.
Remove DILLexer from this PR; will create a separate PR with the
DILLexer in it. Note I have *not* removed code that calls into or
uses definitions from the DILLexer.
---
lldb/include/lldb/ValueObject/DILLexer.h | 157 --------------------
lldb/source/ValueObject/DILLexer.cpp | 176 -----------------------
2 files changed, 333 deletions(-)
delete mode 100644 lldb/include/lldb/ValueObject/DILLexer.h
delete mode 100644 lldb/source/ValueObject/DILLexer.cpp
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
deleted file mode 100644
index 669ddfb2d4a4b..0000000000000
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ /dev/null
@@ -1,157 +0,0 @@
-//===-- DILLexer.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_DILLEXER_H_
-#define LLDB_VALUEOBJECT_DILLEXER_H_
-
-#include "llvm/ADT/StringRef.h"
-#include <cstdint>
-#include <limits.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-namespace lldb_private {
-
-namespace dil {
-
-enum class TokenKind {
- coloncolon,
- eof,
- identifier,
- invalid,
- kw_namespace,
- kw_this,
- l_paren,
- none,
- r_paren,
- unknown,
-};
-
-/// Class defining the tokens generated by the DIL lexer and used by the
-/// DIL parser.
-class DILToken {
-public:
- DILToken(dil::TokenKind kind, std::string spelling, uint32_t start)
- : m_kind(kind), m_spelling(spelling), m_start_pos(start) {}
-
- DILToken() : m_kind(dil::TokenKind::none), m_spelling(""), m_start_pos(0) {}
-
- void setKind(dil::TokenKind kind) { m_kind = kind; }
- dil::TokenKind getKind() const { return m_kind; }
-
- std::string getSpelling() const { return m_spelling; }
-
- uint32_t getLength() const { return m_spelling.size(); }
-
- bool is(dil::TokenKind kind) const { return m_kind == kind; }
-
- bool isNot(dil::TokenKind kind) const { return m_kind != kind; }
-
- bool isOneOf(dil::TokenKind kind1, dil::TokenKind kind2) const {
- return is(kind1) || is(kind2);
- }
-
- template <typename... Ts> bool isOneOf(dil::TokenKind kind, Ts... Ks) const {
- return is(kind) || isOneOf(Ks...);
- }
-
- uint32_t getLocation() const { return m_start_pos; }
-
- void setValues(dil::TokenKind kind, std::string spelling, uint32_t start) {
- m_kind = kind;
- m_spelling = spelling;
- m_start_pos = start;
- }
-
- static const std::string getTokenName(dil::TokenKind kind);
-
-private:
- dil::TokenKind m_kind;
- std::string m_spelling;
- uint32_t m_start_pos; // within entire expression string
-};
-
-/// Class for doing the simple lexing required by DIL.
-class DILLexer {
-public:
- DILLexer(llvm::StringRef dil_expr) : m_expr(dil_expr.str()) {
- m_cur_pos = m_expr.begin();
- // Use UINT_MAX to indicate invalid/uninitialized value.
- m_tokens_idx = UINT_MAX;
- }
-
- bool Lex(DILToken &result, bool look_ahead = false);
-
- bool Is_Word(std::string::iterator start, uint32_t &length);
-
- uint32_t GetLocation() { return m_cur_pos - m_expr.begin(); }
-
- /// Update 'result' with the other paremeter values, create a
- /// duplicate token, and push the duplicate token onto the vector of
- /// lexed tokens.
- void UpdateLexedTokens(DILToken &result, dil::TokenKind tok_kind,
- std::string tok_str, uint32_t tok_pos);
-
- /// Return the lexed token N+1 positions ahead of the 'current' token
- /// being handled by the DIL parser.
- const DILToken &LookAhead(uint32_t N);
-
- const DILToken &AcceptLookAhead(uint32_t N);
-
- /// Return the index for the 'current' token being handled by the DIL parser.
- uint32_t GetCurrentTokenIdx() { return m_tokens_idx; }
-
- /// Return the current token to be handled by the DIL parser.
- DILToken &GetCurrentToken() { return m_lexed_tokens[m_tokens_idx]; }
-
- /// Update the index for the 'current' token, to point to the next lexed
- /// token.
- bool IncrementTokenIdx() {
- if (m_tokens_idx >= m_lexed_tokens.size() - 1)
- return false;
-
- m_tokens_idx++;
- return true;
- }
-
- /// Set the index for the 'current' token (to be handled by the parser)
- /// to a particular position. Used for either committing 'look ahead' parsing
- /// or rolling back tentative parsing.
- bool ResetTokenIdx(uint32_t new_value) {
- if (new_value > m_lexed_tokens.size() - 1)
- return false;
-
- m_tokens_idx = new_value;
- return true;
- }
-
-private:
- // The input string we are lexing & parsing.
- std::string m_expr;
-
- // The current position of the lexer within m_expr (the character position,
- // within the string, of the next item to be lexed).
- std::string::iterator m_cur_pos;
-
- // Holds all of the tokens lexed so far.
- std::vector<DILToken> m_lexed_tokens;
-
- // Index into m_lexed_tokens; indicates which token the DIL parser is
- // currently trying to parse/handle.
- uint32_t m_tokens_idx;
-
- // "invalid" token; to be returned by lexer when 'look ahead' fails.
- DILToken m_invalid_token;
-};
-
-} // namespace dil
-
-} // namespace lldb_private
-
-#endif // LLDB_VALUEOBJECT_DILLEXER_H_
diff --git a/lldb/source/ValueObject/DILLexer.cpp b/lldb/source/ValueObject/DILLexer.cpp
deleted file mode 100644
index 79b699d0d2d2d..0000000000000
--- a/lldb/source/ValueObject/DILLexer.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-//===-- DILLexer.cpp ------------------------------------------------------===//
-//
-// 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
-//
-// This implements the recursive descent parser for the Data Inspection
-// Language (DIL), and its helper functions, which will eventually underlie the
-// 'frame variable' command. The language that this parser recognizes is
-// described in lldb/docs/dil-expr-lang.ebnf
-//
-//===----------------------------------------------------------------------===//
-
-#include "lldb/ValueObject/DILLexer.h"
-
-namespace lldb_private {
-
-namespace dil {
-
-const std::string DILToken::getTokenName(dil::TokenKind kind) {
- switch (kind) {
- case dil::TokenKind::coloncolon:
- return "coloncolon";
- case dil::TokenKind::eof:
- return "eof";
- case dil::TokenKind::identifier:
- return "identifier";
- case dil::TokenKind::kw_namespace:
- return "namespace";
- case dil::TokenKind::l_paren:
- return "l_paren";
- case dil::TokenKind::r_paren:
- return "r_paren";
- case dil::TokenKind::unknown:
- return "unknown";
- default:
- return "token_name";
- }
-}
-
-static bool Is_Letter(char c) {
- if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
- return true;
- return false;
-}
-
-static bool Is_Digit(char c) { return ('0' <= c && c <= '9'); }
-
-bool DILLexer::Is_Word(std::string::iterator start, uint32_t &length) {
- bool done = false;
- for (; m_cur_pos != m_expr.end() && !done; ++m_cur_pos) {
- char c = *m_cur_pos;
- if (!Is_Letter(c) && !Is_Digit(c) && c != '_') {
- done = true;
- break;
- } else
- length++;
- }
- if (length > 0)
- return true;
-
- m_cur_pos = start;
- return false;
-}
-
-void DILLexer::UpdateLexedTokens(DILToken &result, dil::TokenKind tok_kind,
- std::string tok_str, uint32_t tok_pos) {
- DILToken new_token;
- result.setValues(tok_kind, tok_str, tok_pos);
- new_token = result;
- m_lexed_tokens.push_back(std::move(new_token));
-}
-
-bool DILLexer::Lex(DILToken &result, bool look_ahead) {
- bool retval = true;
-
- if (!look_ahead) {
- // We're being asked for the 'next' token, and not a part of a LookAhead.
- // Check to see if we've already lexed it and pushed it onto our tokens
- // vector; if so, return the next token from the vector, rather than doing
- // more lexing.
- if ((m_tokens_idx != UINT_MAX) &&
- (m_tokens_idx < m_lexed_tokens.size() - 1)) {
- result = m_lexed_tokens[m_tokens_idx + 1];
- return retval;
- }
- }
-
- // Skip over whitespace (spaces).
- while (m_cur_pos != m_expr.end() && *m_cur_pos == ' ')
- m_cur_pos++;
-
- // Check to see if we've reached the end of our input string.
- if (m_cur_pos == m_expr.end()) {
- UpdateLexedTokens(result, dil::TokenKind::eof, "", m_expr.length());
- return retval;
- }
-
- uint32_t position = m_cur_pos - m_expr.begin();
- ;
- std::string::iterator start = m_cur_pos;
- uint32_t length = 0;
- if (Is_Word(start, length)) {
- dil::TokenKind kind;
- std::string word = m_expr.substr(position, length);
- if (word == "namespace")
- kind = dil::TokenKind::kw_namespace;
- else
- kind = dil::TokenKind::identifier;
-
- UpdateLexedTokens(result, kind, word, position);
- return true;
- }
-
- switch (*m_cur_pos) {
- case '(':
- m_cur_pos++;
- UpdateLexedTokens(result, dil::TokenKind::l_paren, "(", position);
- return true;
- case ')':
- m_cur_pos++;
- UpdateLexedTokens(result, dil::TokenKind::r_paren, ")", position);
- return true;
- case ':':
- if (position + 1 < m_expr.size() && m_expr[position + 1] == ':') {
- m_cur_pos += 2;
- UpdateLexedTokens(result, dil::TokenKind::coloncolon, "::", position);
- return true;
- }
- break;
- default:
- break;
- }
- // Empty Token
- result.setValues(dil::TokenKind::none, "", m_expr.length());
- return false;
-}
-
-const DILToken &DILLexer::LookAhead(uint32_t N) {
- uint32_t extra_lexed_tokens = m_lexed_tokens.size() - m_tokens_idx - 1;
-
- if (N + 1 < extra_lexed_tokens)
- return m_lexed_tokens[m_tokens_idx + N + 1];
-
- uint32_t remaining_tokens =
- (m_tokens_idx + N + 1) - m_lexed_tokens.size() + 1;
-
- bool done = false;
- bool look_ahead = true;
- while (!done && remaining_tokens > 0) {
- DILToken tok;
- Lex(tok, look_ahead);
- if (tok.getKind() == dil::TokenKind::eof)
- done = true;
- remaining_tokens--;
- };
-
- if (remaining_tokens > 0) {
- m_invalid_token.setValues(dil::TokenKind::invalid, "", 0);
- return m_invalid_token;
- }
-
- return m_lexed_tokens[m_tokens_idx + N + 1];
-}
-
-const DILToken &DILLexer::AcceptLookAhead(uint32_t N) {
- if (m_tokens_idx + N + 1 > m_lexed_tokens.size())
- return m_invalid_token;
-
- m_tokens_idx += N + 1;
- return m_lexed_tokens[m_tokens_idx];
-}
-
-} // namespace dil
-
-} // namespace lldb_private
>From fbeba18ee53e651f3e2b238f8a4e5bd32e99665f Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Wed, 15 Jan 2025 13:48:24 -0800
Subject: [PATCH 05/21] Fix clang format issues.
---
.../TestFrameVarDILGlobalVariableLookup.py | 26 +++++++++++--------
.../basics/GlobalVariableLookup/main.cpp | 15 +++++------
.../TestFrameVarDILInstanceVariables.py | 6 ++---
.../var-dil/basics/InstanceVariables/main.cpp | 8 +++---
.../LocalVars/TestFrameVarDILLocalVars.py | 6 ++---
.../frame/var-dil/basics/LocalVars/main.cpp | 4 +--
6 files changed, 31 insertions(+), 34 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index 252dcacb13bbd..8a455906f3af5 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -11,6 +11,7 @@
import shutil
import time
+
class TestFrameVarDILGlobalVariableLookup(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
@@ -49,7 +50,7 @@ def do_test(self):
self.assertEqual(
len(threads), 1, "There should be a thread stopped at our breakpoint"
)
- # The hit count for the breakpoint should be 1.
+ # The hit count for the breakpoint should be 1.
self.assertEquals(breakpoint.GetHitCount(), 1)
frame = threads[0].GetFrameAtIndex(0)
@@ -57,22 +58,25 @@ def do_test(self):
interp = self.dbg.GetCommandInterpreter()
- self.expect("settings set target.experimental.use-DIL true",
- substrs=[""])
+ self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable 'globalVar'", substrs=["-559038737"]) # 0xDEADBEEF
self.expect("frame variable 'globalPtr'", patterns=["0x[0-9]+"])
self.expect("frame variable 'globalRef'", substrs=["-559038737"])
self.expect("frame variable '::globalPtr'", patterns=["0x[0-9]+"])
self.expect("frame variable '::globalRef'", substrs=["-559038737"])
- self.expect("frame variable 'externGlobalVar'", error=True,
- substrs=["use of undeclared identifier"]) # 0x00C0FFEE
- self.expect("frame variable '::externGlobalVar'", error=True,
- substrs=["use of undeclared identifier"]) # ["12648430"])
+ self.expect(
+ "frame variable 'externGlobalVar'",
+ error=True,
+ substrs=["use of undeclared identifier"]
+ ) # 0x00C0FFEE
+ self.expect(
+ "frame variable '::externGlobalVar'",
+ error=True,
+ substrs=["use of undeclared identifier"]
+ ) # ["12648430"])
self.expect("frame variable 'ns::globalVar'", substrs=["13"])
- self.expect("frame variable 'ns::globalPtr'",
- patterns=["0x[0-9]+"])
+ self.expect("frame variable 'ns::globalPtr'", patterns=["0x[0-9]+"])
self.expect("frame variable 'ns::globalRef'", substrs=["13"])
self.expect("frame variable '::ns::globalVar'", substrs=["13"])
- self.expect("frame variable '::ns::globalPtr'",
- patterns=["0x[0-9]+"])
+ self.expect("frame variable '::ns::globalPtr'", patterns=["0x[0-9]+"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
index 605142d169db2..5bae4fd423e32 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/main.cpp
@@ -1,18 +1,15 @@
int globalVar = 0xDEADBEEF;
extern int externGlobalVar;
-int* globalPtr = &globalVar;
-int& globalRef = globalVar;
+int *globalPtr = &globalVar;
+int &globalRef = globalVar;
namespace ns {
int globalVar = 13;
-int* globalPtr = &globalVar;
-int& globalRef = globalVar;
-} // namespace ns
-
-int
-main(int argc, char **argv)
-{
+int *globalPtr = &globalVar;
+int &globalRef = globalVar;
+} // namespace ns
+int main(int argc, char **argv) {
return 0; // Set a breakpoint here
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index dcc408f1f37bd..4ef645c15f680 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -11,6 +11,7 @@
import shutil
import time
+
class TestFrameVarDILInstanceVariables(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
@@ -49,14 +50,13 @@ def do_test(self):
self.assertEqual(
len(threads), 1, "There should be a thread stopped at our breakpoint"
)
- # The hit count for the breakpoint should be 1.
+ # The hit count for the breakpoint should be 1.
self.assertEquals(breakpoint.GetHitCount(), 1)
frame = threads[0].GetFrameAtIndex(0)
command_result = lldb.SBCommandReturnObject()
interp = self.dbg.GetCommandInterpreter()
- self.expect("settings set target.experimental.use-DIL true",
- substrs=[""])
+ self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable 'this'", patterns=["0x[0-9]+"])
self.expect("frame variable 'c'", substrs=["(field_ = -1)"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
index 276166f97a7bd..7a559c4007415 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/main.cpp
@@ -1,12 +1,12 @@
#include <string>
class C {
- public:
+public:
int field_ = 1337;
};
class TestMethods {
- public:
+public:
void TestInstanceVariables() {
C c;
c.field_ = -1;
@@ -15,9 +15,7 @@ class TestMethods {
}
};
-int
-main(int argc, char **argv)
-{
+int main(int argc, char **argv) {
TestMethods tm;
tm.TestInstanceVariables();
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index b2f1d3a07912a..17bce7b9de95b 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -11,6 +11,7 @@
import shutil
import time
+
class TestFrameVarDILLocalVars(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
@@ -49,15 +50,14 @@ def do_test(self):
self.assertEqual(
len(threads), 1, "There should be a thread stopped at our breakpoint"
)
- # The hit count for the breakpoint should be 1.
+ # The hit count for the breakpoint should be 1.
self.assertEquals(breakpoint.GetHitCount(), 1)
frame = threads[0].GetFrameAtIndex(0)
command_result = lldb.SBCommandReturnObject()
interp = self.dbg.GetCommandInterpreter()
- self.expect("settings set target.experimental.use-DIL true",
- substrs=[""])
+ self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable a", substrs=["1"])
self.expect("frame variable b", substrs=["2"])
self.expect("frame variable c", substrs=["\\xfd"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
index 21f0fd6d62c4a..04c73539c5f89 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/main.cpp
@@ -1,6 +1,4 @@
-int
-main(int argc, char **argv)
-{
+int main(int argc, char **argv) {
int a = 1;
int b = 2;
>From aa0c72118149d12c7eff729ba9c89d2a3c55d840 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 20 Jan 2025 09:54:21 -0800
Subject: [PATCH 06/21] [LLDB] Add DIL code for handling plain variable names.
Update grammar to allow register names.
---
lldb/docs/dil-expr-lang.ebnf | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lldb/docs/dil-expr-lang.ebnf b/lldb/docs/dil-expr-lang.ebnf
index 7aca2b34a4929..0bbbecbdc78c1 100644
--- a/lldb/docs/dil-expr-lang.ebnf
+++ b/lldb/docs/dil-expr-lang.ebnf
@@ -9,7 +9,8 @@ primary_expression = id_expression
| "(" expression ")";
id_expression = unqualified_id
- | qualified_id ;
+ | qualified_id
+ | register ;
unqualified_id = identifier ;
@@ -18,6 +19,8 @@ qualified_id = ["::"] [nested_name_specifier] unqualified_id
identifier = ? C99 Identifier ? ;
+register = "$" ? Register name ? ;
+
nested_name_specifier = type_name "::"
| namespace_name '::'
| nested_name_specifier identifier "::" ;
>From d3a9ca6742917a9522ea15b8c97481ecd059e11c Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 20 Jan 2025 10:17:30 -0800
Subject: [PATCH 07/21] [LLDB] Add DIL code for handling plain variable names.
Remove Status member variable ('m_error') from DILInterpreter class.
---
lldb/include/lldb/ValueObject/DILAST.h | 11 ++---
lldb/include/lldb/ValueObject/DILEval.h | 18 ++------
lldb/source/Target/StackFrame.cpp | 2 +-
lldb/source/ValueObject/DILAST.cpp | 8 +++-
lldb/source/ValueObject/DILEval.cpp | 57 ++++++++++---------------
5 files changed, 40 insertions(+), 56 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 1bb11695e3a4d..b435bdf92ebdc 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -42,7 +42,7 @@ class DILASTNode {
: m_location(location), m_kind(kind) {}
virtual ~DILASTNode() = default;
- virtual void Accept(Visitor *v) const = 0;
+ virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
uint32_t GetLocation() const { return m_location; }
NodeKind GetKind() const { return m_kind; }
@@ -57,7 +57,7 @@ using DILASTNodeUP = std::unique_ptr<DILASTNode>;
class ErrorNode : public DILASTNode {
public:
ErrorNode() : DILASTNode(0, NodeKind::eErrorNode) {}
- void Accept(Visitor *v) const override;
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
static bool classof(const DILASTNode *node) {
return node->GetKind() == NodeKind::eErrorNode;
@@ -72,7 +72,7 @@ class IdentifierNode : public DILASTNode {
: DILASTNode(location, NodeKind::eIdentifierNode),
m_name(std::move(name)), m_use_dynamic(use_dynamic) {}
- void Accept(Visitor *v) const override;
+ llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
std::string GetName() const { return m_name; }
@@ -93,8 +93,9 @@ class IdentifierNode : public DILASTNode {
class Visitor {
public:
virtual ~Visitor() = default;
- virtual void Visit(const ErrorNode *node) = 0;
- virtual void Visit(const IdentifierNode *node) = 0;
+ virtual llvm::Expected<lldb::ValueObjectSP> Visit(const ErrorNode *node) = 0;
+ virtual llvm::Expected<lldb::ValueObjectSP>
+ Visit(const IdentifierNode *node) = 0;
};
} // namespace dil
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 87dc61129850e..0bd69ff91ef30 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -69,18 +69,12 @@ class DILInterpreter : Visitor {
lldb::DynamicValueType use_dynamic,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope);
- llvm::Expected<lldb::ValueObjectSP> DILEval(const DILASTNode *tree,
- lldb::TargetSP target_sp);
+ llvm::Expected<lldb::ValueObjectSP> DILEvalNode(const DILASTNode *node);
private:
- lldb::ValueObjectSP DILEvalNode(const DILASTNode *node);
-
- bool Success() { return m_error.Success(); }
-
- void SetError(ErrorCode error_code, std::string error, uint32_t loc);
-
- void Visit(const ErrorNode *node) override;
- void Visit(const IdentifierNode *node) override;
+ llvm::Expected<lldb::ValueObjectSP> Visit(const ErrorNode *node) override;
+ llvm::Expected<lldb::ValueObjectSP>
+ Visit(const IdentifierNode *node) override;
private:
// Used by the interpreter to create objects, perform casts, etc.
@@ -88,14 +82,10 @@ class DILInterpreter : Visitor {
llvm::StringRef m_expr;
- lldb::ValueObjectSP m_result;
-
lldb::ValueObjectSP m_scope;
lldb::DynamicValueType m_default_dynamic;
- Status m_error;
-
std::shared_ptr<ExecutionContextScope> m_exe_ctx_scope;
};
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 2eb5afef0139d..f375f47beb736 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -549,7 +549,7 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
dil::DILInterpreter interpreter(target, var_expr, use_dynamic,
shared_from_this());
- auto valobj_or_error = interpreter.DILEval((*tree_or_error).get(), target);
+ auto valobj_or_error = interpreter.DILEvalNode((*tree_or_error).get());
if (!valobj_or_error) {
error = Status::FromError(valobj_or_error.takeError());
return ValueObjectSP();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index 9aebe7e2ca4f9..bad0723f1db1e 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -12,9 +12,13 @@ namespace lldb_private {
namespace dil {
-void ErrorNode::Accept(Visitor *v) const { v->Visit(this); }
+llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const {
+ return v->Visit(this);
+}
-void IdentifierNode::Accept(Visitor *v) const { v->Visit(this); }
+llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
+ return v->Visit(this);
+}
} // namespace dil
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index a22ea5c4a126f..4e5d4144b1492 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -226,40 +226,23 @@ DILInterpreter::DILInterpreter(
m_exe_ctx_scope(exe_ctx_scope) {}
llvm::Expected<lldb::ValueObjectSP>
-DILInterpreter::DILEval(const DILASTNode *tree, lldb::TargetSP target_sp) {
- m_error.Clear();
- // Evaluate an AST.
- DILEvalNode(tree);
- // Check for errors.
- if (m_error.Fail())
- return m_error.ToError();
-
- // Return the computed result.
- return m_result;
-}
-
-lldb::ValueObjectSP DILInterpreter::DILEvalNode(const DILASTNode *node) {
+DILInterpreter::DILEvalNode(const DILASTNode *node) {
// Traverse an AST pointed by the `node`.
- node->Accept(this);
+ auto value_or_error = node->Accept(this);
- // Return the computed value for convenience. The caller is responsible for
- // checking if an error occured during the evaluation.
- return m_result;
+ // Return the computed value or error.
+ return value_or_error;
}
-void DILInterpreter::SetError(ErrorCode code, std::string error, uint32_t loc) {
- assert(m_error.Success() && "interpreter can error only once");
- m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
- FormatDiagnostics(m_expr, error, loc));
-}
-
-void DILInterpreter::Visit(const ErrorNode *node) {
+llvm::Expected<lldb::ValueObjectSP>
+DILInterpreter::Visit(const ErrorNode *node) {
// The AST is not valid.
- m_result = lldb::ValueObjectSP();
+ return lldb::ValueObjectSP();
}
-void DILInterpreter::Visit(const IdentifierNode *node) {
+llvm::Expected<lldb::ValueObjectSP>
+DILInterpreter::Visit(const IdentifierNode *node) {
lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
std::unique_ptr<IdentifierInfo> identifier =
@@ -268,13 +251,11 @@ void DILInterpreter::Visit(const IdentifierNode *node) {
if (!identifier) {
std::string errMsg;
std::string name = node->GetName();
- if (name == "this")
- errMsg = "invalid use of 'this' outside of a non-static member function";
- else
- errMsg = llvm::formatv("use of undeclared identifier '{0}'", name);
- SetError(ErrorCode::kUndeclaredIdentifier, errMsg, node->GetLocation());
- m_result = lldb::ValueObjectSP();
- return;
+ errMsg = llvm::formatv("use of undeclared identifier '{0}'", name);
+ Status error = Status(
+ (uint32_t)ErrorCode::kUndeclaredIdentifier, lldb::eErrorTypeGeneric,
+ FormatDiagnostics(m_expr, errMsg, node->GetLocation()));
+ return error.ToError();
}
lldb::ValueObjectSP val;
lldb::TargetSP target_sp;
@@ -284,11 +265,19 @@ void DILInterpreter::Visit(const IdentifierNode *node) {
"Unrecognized identifier kind");
val = identifier->GetValue();
+
+ if (val->GetCompilerType().IsReferenceType()) {
+ Status error;
+ val = val->Dereference(error);
+ if (error.Fail())
+ return error.ToError();
+ }
+
target_sp = val->GetTargetSP();
assert(target_sp && target_sp->IsValid() &&
"identifier doesn't resolve to a valid value");
- m_result = val;
+ return val;
}
} // namespace dil
>From c7eb5fd9616b3337ea71f1667fecf2b97e9f11f4 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 20 Jan 2025 10:39:12 -0800
Subject: [PATCH 08/21] Fix clang format issue in test.
---
.../TestFrameVarDILGlobalVariableLookup.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index 8a455906f3af5..ed41757bad2af 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -57,7 +57,6 @@ def do_test(self):
command_result = lldb.SBCommandReturnObject()
interp = self.dbg.GetCommandInterpreter()
-
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable 'globalVar'", substrs=["-559038737"]) # 0xDEADBEEF
self.expect("frame variable 'globalPtr'", patterns=["0x[0-9]+"])
@@ -68,13 +67,13 @@ def do_test(self):
self.expect(
"frame variable 'externGlobalVar'",
error=True,
- substrs=["use of undeclared identifier"]
+ substrs=["use of undeclared identifier"],
) # 0x00C0FFEE
self.expect(
"frame variable '::externGlobalVar'",
error=True,
- substrs=["use of undeclared identifier"]
- ) # ["12648430"])
+ substrs=["use of undeclared identifier"],
+ ) # ["12648430"])
self.expect("frame variable 'ns::globalVar'", substrs=["13"])
self.expect("frame variable 'ns::globalPtr'", patterns=["0x[0-9]+"])
self.expect("frame variable 'ns::globalRef'", substrs=["13"])
>From 9d881e2b65ba7f0ebfcf2840991299301bba3bb7 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 20 Jan 2025 11:13:54 -0800
Subject: [PATCH 09/21] Re-do some if-statements to facilitate early exits.
---
lldb/source/ValueObject/DILEval.cpp | 55 ++++++++++++++++-------------
1 file changed, 30 insertions(+), 25 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 4e5d4144b1492..356da5c51c4a8 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -30,19 +30,21 @@ LookupStaticIdentifier(lldb::TargetSP target_sp,
VariableList variable_list;
ConstString name(name_ref);
target_sp->GetImages().FindGlobalVariables(name, 1, variable_list);
- if (!variable_list.Empty()) {
- ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
- if (exe_scope == nullptr)
- exe_scope = target_sp.get();
- for (const lldb::VariableSP &var_sp : variable_list) {
- lldb::ValueObjectSP valobj_sp(
- ValueObjectVariable::Create(exe_scope, var_sp));
- if (valobj_sp && valobj_sp->GetVariable() &&
- (valobj_sp->GetVariable()->NameMatches(unqualified_name) ||
- valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
- return valobj_sp;
- }
+ if (variable_list.Empty())
+ return nullptr;
+
+ ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
+ if (exe_scope == nullptr)
+ exe_scope = target_sp.get();
+ for (const lldb::VariableSP &var_sp : variable_list) {
+ lldb::ValueObjectSP valobj_sp(
+ ValueObjectVariable::Create(exe_scope, var_sp));
+ if (valobj_sp && valobj_sp->GetVariable() &&
+ (valobj_sp->GetVariable()->NameMatches(unqualified_name) ||
+ valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
+ return valobj_sp;
}
+
return nullptr;
}
@@ -110,18 +112,22 @@ LookupIdentifier(const std::string &name,
const char *reg_name = name_ref.drop_front(1).data();
Target *target = ctx_scope->CalculateTarget().get();
Process *process = ctx_scope->CalculateProcess().get();
- if (target && process) {
- StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
- if (stack_frame) {
- lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
- if (reg_ctx) {
- if (const RegisterInfo *reg_info =
- reg_ctx->GetRegisterInfoByName(reg_name))
- value_sp =
- ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
- }
- }
- }
+ if (!target || !process)
+ return nullptr;
+
+ StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+ if (!stack_frame)
+ return nullptr;
+
+ lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
+ if (!reg_ctx)
+ return nullptr;
+
+ if (const RegisterInfo *reg_info =
+ reg_ctx->GetRegisterInfoByName(reg_name))
+ value_sp =
+ ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+
if (value_sp)
return IdentifierInfo::FromValue(*value_sp);
@@ -211,7 +217,6 @@ LookupIdentifier(const std::string &name,
}
}
- // Force static value, otherwise we can end up with the "real" type.
if (value)
return IdentifierInfo::FromValue(*value);
>From d2665f01e6316fe0ef9c08cfa536799d891a6b75 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 20 Jan 2025 11:30:26 -0800
Subject: [PATCH 10/21] FIx clang-format issue.
---
lldb/source/ValueObject/DILEval.cpp | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 356da5c51c4a8..5496a23312d58 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -123,10 +123,8 @@ LookupIdentifier(const std::string &name,
if (!reg_ctx)
return nullptr;
- if (const RegisterInfo *reg_info =
- reg_ctx->GetRegisterInfoByName(reg_name))
- value_sp =
- ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+ if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name))
+ value_sp = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
if (value_sp)
return IdentifierInfo::FromValue(*value_sp);
>From b5cf8b21ab2cb37b0ae11b6bece818e812d8d0d7 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Sat, 8 Feb 2025 15:03:36 -0800
Subject: [PATCH 11/21] Update code to match changes in the recently committed
DILLexer.
Changed method names, class names, arguments, etc. Also small cleanups
in header files (include-stmt ordering, namespaces, etc.).
---
lldb/include/lldb/ValueObject/DILAST.h | 8 +-
lldb/include/lldb/ValueObject/DILEval.h | 14 +--
lldb/include/lldb/ValueObject/DILParser.h | 27 +++--
lldb/source/Target/StackFrame.cpp | 11 +-
lldb/source/ValueObject/DILAST.cpp | 8 +-
lldb/source/ValueObject/DILEval.cpp | 8 +-
lldb/source/ValueObject/DILParser.cpp | 122 ++++++++++------------
7 files changed, 88 insertions(+), 110 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index b435bdf92ebdc..6103476d3ee46 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -12,9 +12,7 @@
#include "lldb/ValueObject/ValueObject.h"
#include <string>
-namespace lldb_private {
-
-namespace dil {
+namespace lldb_private::dil {
/// The various types DIL AST nodes (used by the DIL parser).
enum class NodeKind {
@@ -98,8 +96,6 @@ class Visitor {
Visit(const IdentifierNode *node) = 0;
};
-} // namespace dil
-
-} // namespace lldb_private
+} // namespace lldb_private::dil
#endif // LLDB_VALUEOBJECT_DILAST_H
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 0bd69ff91ef30..05e85a94750ad 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -6,17 +6,15 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLDB_VALUEOBJECT_DILEVAL_H_
-#define LLDB_VALUEOBJECT_DILEVAL_H_
+#ifndef LLDB_VALUEOBJECT_DILEVAL_H
+#define LLDB_VALUEOBJECT_DILEVAL_H
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILParser.h"
#include <memory>
#include <vector>
-namespace lldb_private {
-
-namespace dil {
+namespace lldb_private::dil {
/// Class used to store & manipulate information about identifiers.
class IdentifierInfo {
@@ -89,8 +87,6 @@ class DILInterpreter : Visitor {
std::shared_ptr<ExecutionContextScope> m_exe_ctx_scope;
};
-} // namespace dil
-
-} // namespace lldb_private
+} // namespace lldb_private::dil
-#endif // LLDB_VALUEOBJECT_DILEVAL_H_
+#endif // LLDB_VALUEOBJECT_DILEVAL_H
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 1ff38808cd093..574b300ce90c4 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLDB_VALUEOBJECT_DILPARSER_H_
-#define LLDB_VALUEOBJECT_DILPARSER_H_
+#ifndef LLDB_VALUEOBJECT_DILPARSER_H
+#define LLDB_VALUEOBJECT_DILPARSER_H
#include "lldb/Target/ExecutionContextScope.h"
#include "lldb/Utility/Status.h"
@@ -19,9 +19,7 @@
#include <tuple>
#include <vector>
-namespace lldb_private {
-
-namespace dil {
+namespace lldb_private::dil {
enum class ErrorCode : unsigned char {
kOk = 0,
@@ -37,7 +35,7 @@ std::string FormatDiagnostics(llvm::StringRef input_expr,
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
class DILParser {
public:
- explicit DILParser(llvm::StringRef dil_input_expr,
+ explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member);
@@ -50,7 +48,7 @@ class DILParser {
lldb::DynamicValueType UseDynamic() { return m_use_dynamic; }
- using PtrOperator = std::tuple<dil::TokenKind, uint32_t>;
+ using PtrOperator = std::tuple<Token::Kind, uint32_t>;
private:
DILASTNodeUP ParseExpression();
@@ -67,9 +65,9 @@ class DILParser {
void BailOut(Status error);
- void Expect(dil::TokenKind kind);
+ void Expect(Token::Kind kind);
- std::string TokenDescription(const DILToken &token);
+ std::string TokenDescription(const Token &token);
void TentativeParsingRollback(uint32_t saved_idx) {
m_error.Clear();
@@ -83,8 +81,10 @@ class DILParser {
std::shared_ptr<ExecutionContextScope> m_ctx_scope;
llvm::StringRef m_input_expr;
+
+ DILLexer m_dil_lexer;
// The token lexer is stopped at (aka "current token").
- DILToken m_dil_token;
+ Token m_dil_token;
// Holds an error if it occures during parsing.
Status m_error;
@@ -92,11 +92,8 @@ class DILParser {
bool m_use_synthetic;
bool m_fragile_ivar;
bool m_check_ptr_vs_member;
- DILLexer m_dil_lexer;
}; // class DILParser
-} // namespace dil
-
-} // namespace lldb_private
+} // namespace lldb_private::dil
-#endif // LLDB_VALUEOBJECT_DILPARSER_H_
+#endif // LLDB_VALUEOBJECT_DILPARSER_H
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 8ed52d4516cd9..5d672ee696019 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -32,6 +32,7 @@
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
#include "lldb/ValueObject/DILEval.h"
+#include "lldb/ValueObject/DILLexer.h"
#include "lldb/ValueObject/DILParser.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
#include "lldb/ValueObject/ValueObjectMemory.h"
@@ -535,8 +536,16 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
const bool no_synth_child =
(options & eExpressionPathOptionsNoSyntheticChildren) != 0;
+ // Lex the expression.
+ auto lex_or_err = dil::DILLexer::Create(var_expr);
+ if (!lex_or_err) {
+ error = Status::FromError(lex_or_err.takeError());
+ return ValueObjectSP();
+ }
+ dil::DILLexer lexer = *lex_or_err;
+
// Parse the expression.
- dil::DILParser parser(var_expr, shared_from_this(), use_dynamic,
+ dil::DILParser parser(var_expr, lexer, shared_from_this(), use_dynamic,
!no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
auto tree_or_error = parser.Run();
if (!tree_or_error) {
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index bad0723f1db1e..34c8d1038250b 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -8,9 +8,7 @@
#include "lldb/ValueObject/DILAST.h"
-namespace lldb_private {
-
-namespace dil {
+namespace lldb_private::dil {
llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const {
return v->Visit(this);
@@ -20,6 +18,4 @@ llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
return v->Visit(this);
}
-} // namespace dil
-
-} // namespace lldb_private
+} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 5496a23312d58..a72f9b3c136b9 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -16,9 +16,7 @@
#include "llvm/Support/FormatAdapters.h"
#include <memory>
-namespace lldb_private {
-
-namespace dil {
+namespace lldb_private::dil {
static lldb::ValueObjectSP
LookupStaticIdentifier(lldb::TargetSP target_sp,
@@ -283,6 +281,4 @@ DILInterpreter::Visit(const IdentifierNode *node) {
return val;
}
-} // namespace dil
-
-} // namespace lldb_private
+} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 6d0b0f086b89f..e37513b1a8550 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -23,16 +23,14 @@
#include <stdlib.h>
#include <string>
-namespace lldb_private {
+namespace lldb_private::dil {
-namespace dil {
-
-inline void TokenKindsJoinImpl(std::ostringstream &os, dil::TokenKind k) {
- os << "'" << DILToken::getTokenName(k) << "'";
+inline void TokenKindsJoinImpl(std::ostringstream &os, Token::Kind k) {
+ os << "'" << Token::GetTokenName(k).str() << "'";
}
template <typename... Ts>
-inline void TokenKindsJoinImpl(std::ostringstream &os, dil::TokenKind k,
+inline void TokenKindsJoinImpl(std::ostringstream &os, Token::Kind k,
Ts... ks) {
TokenKindsJoinImpl(os, k);
os << ", ";
@@ -40,7 +38,7 @@ inline void TokenKindsJoinImpl(std::ostringstream &os, dil::TokenKind k,
}
template <typename... Ts>
-inline std::string TokenKindsJoin(dil::TokenKind k, Ts... ks) {
+inline std::string TokenKindsJoin(Token::Kind k, Ts... ks) {
std::ostringstream os;
TokenKindsJoinImpl(os, k, ks...);
@@ -75,26 +73,22 @@ std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
llvm::fmt_pad("^", arrow - 1, arrow_rpad));
}
-DILParser::DILParser(llvm::StringRef dil_input_expr,
+DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member)
: m_ctx_scope(exe_ctx_scope), m_input_expr(dil_input_expr),
+ m_dil_lexer(lexer), m_dil_token(lexer.GetCurrentToken()),
m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
- m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member),
- m_dil_lexer(DILLexer(dil_input_expr)) {
- // Initialize the token.
- m_dil_token.setKind(dil::TokenKind::unknown);
+ m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member) {
}
llvm::Expected<DILASTNodeUP> DILParser::Run() {
- ConsumeToken();
-
DILASTNodeUP expr;
expr = ParseExpression();
- Expect(dil::TokenKind::eof);
+ Expect(Token::Kind::eof);
if (m_error.Fail())
return m_error.ToError();
@@ -117,25 +111,24 @@ DILASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
// "(" expression ")"
//
DILASTNodeUP DILParser::ParsePrimaryExpression() {
- if (m_dil_token.isOneOf(dil::TokenKind::coloncolon,
- dil::TokenKind::identifier)) {
+ if (m_dil_token.IsOneOf(Token::coloncolon, Token::identifier)) {
// Save the source location for the diagnostics message.
- uint32_t loc = m_dil_token.getLocation();
+ uint32_t loc = m_dil_token.GetLocation();
auto identifier = ParseIdExpression();
return std::make_unique<IdentifierNode>(loc, identifier, m_use_dynamic,
m_ctx_scope);
- } else if (m_dil_token.is(dil::TokenKind::l_paren)) {
+ } else if (m_dil_token.Is(Token::l_paren)) {
ConsumeToken();
auto expr = ParseExpression();
- Expect(dil::TokenKind::r_paren);
+ Expect(Token::r_paren);
ConsumeToken();
return expr;
}
BailOut(ErrorCode::kInvalidExpressionSyntax,
llvm::formatv("Unexpected token: {0}", TokenDescription(m_dil_token)),
- m_dil_token.getLocation());
+ m_dil_token.GetLocation());
return std::make_unique<ErrorNode>();
}
@@ -149,8 +142,8 @@ DILASTNodeUP DILParser::ParsePrimaryExpression() {
std::string DILParser::ParseNestedNameSpecifier() {
// The first token in nested_name_specifier is always an identifier, or
// '(anonymous namespace)'.
- if (m_dil_token.isNot(dil::TokenKind::identifier) &&
- m_dil_token.isNot(dil::TokenKind::l_paren)) {
+ if (m_dil_token.IsNot(Token::identifier) &&
+ m_dil_token.IsNot(Token::l_paren)) {
return "";
}
@@ -158,24 +151,26 @@ std::string DILParser::ParseNestedNameSpecifier() {
// the the string '(anonymous namespace)', which has a space in it (throwing
// off normal parsing) and is not actually proper C++> Check to see if we're
// looking at '(anonymous namespace)::...'
- if (m_dil_token.is(dil::TokenKind::l_paren)) {
+ if (m_dil_token.Is(Token::l_paren)) {
// Look for all the pieces, in order:
// l_paren 'anonymous' 'namespace' r_paren coloncolon
- if (m_dil_lexer.LookAhead(0).is(dil::TokenKind::identifier) &&
- ((m_dil_lexer.LookAhead(0)).getSpelling() == "anonymous") &&
- m_dil_lexer.LookAhead(1).is(dil::TokenKind::kw_namespace) &&
- m_dil_lexer.LookAhead(2).is(dil::TokenKind::r_paren) &&
- m_dil_lexer.LookAhead(3).is(dil::TokenKind::coloncolon)) {
- m_dil_token = m_dil_lexer.AcceptLookAhead(3);
-
- assert((m_dil_token.is(dil::TokenKind::identifier) ||
- m_dil_token.is(dil::TokenKind::l_paren)) &&
+ if (m_dil_lexer.LookAhead(1).Is(Token::identifier) &&
+ (m_dil_lexer.LookAhead(1).GetSpelling() == "anonymous") &&
+ m_dil_lexer.LookAhead(2).Is(Token::identifier) &&
+ (m_dil_lexer.LookAhead(2).GetSpelling() == "namespace") &&
+ m_dil_lexer.LookAhead(3).Is(Token::r_paren) &&
+ m_dil_lexer.LookAhead(4).Is(Token::coloncolon)) {
+ m_dil_lexer.Advance(4);
+ m_dil_token = m_dil_lexer.GetCurrentToken();
+
+ assert((m_dil_token.Is(Token::identifier) ||
+ m_dil_token.Is(Token::l_paren)) &&
"Expected an identifier or anonymous namespace, but not found.");
// Continue parsing the nested_namespace_specifier.
std::string identifier2 = ParseNestedNameSpecifier();
if (identifier2.empty()) {
- Expect(dil::TokenKind::identifier);
- identifier2 = m_dil_token.getSpelling();
+ Expect(Token::identifier);
+ identifier2 = m_dil_token.GetSpelling();
ConsumeToken();
}
return "(anonymous namespace)::" + identifier2;
@@ -186,11 +181,12 @@ std::string DILParser::ParseNestedNameSpecifier() {
// If the next token is scope ("::"), then this is indeed a
// nested_name_specifier
- if (m_dil_lexer.LookAhead(0).is(dil::TokenKind::coloncolon)) {
+ if (m_dil_lexer.LookAhead(1).Is(Token::coloncolon)) {
// This nested_name_specifier is a single identifier.
- std::string identifier = m_dil_token.getSpelling();
- m_dil_token = m_dil_lexer.AcceptLookAhead(0);
- Expect(dil::TokenKind::coloncolon);
+ std::string identifier = m_dil_token.GetSpelling();
+ m_dil_lexer.Advance(1);
+ m_dil_token = m_dil_lexer.GetCurrentToken();
+ Expect(Token::coloncolon);
ConsumeToken();
// Continue parsing the nested_name_specifier.
return identifier + "::" + ParseNestedNameSpecifier();
@@ -210,12 +206,12 @@ std::string DILParser::ParseNestedNameSpecifier() {
// ["::"] identifier
//
// identifier:
-// ? dil::TokenKind::identifier ?
+// ? Token::identifier ?
//
std::string DILParser::ParseIdExpression() {
// Try parsing optional global scope operator.
bool global_scope = false;
- if (m_dil_token.is(dil::TokenKind::coloncolon)) {
+ if (m_dil_token.Is(Token::coloncolon)) {
global_scope = true;
ConsumeToken();
}
@@ -236,8 +232,8 @@ std::string DILParser::ParseIdExpression() {
// No nested_name_specifier, but with global scope -- this is also a
// qualified_id production. Follow the second production rule.
else if (global_scope) {
- Expect(dil::TokenKind::identifier);
- std::string identifier = m_dil_token.getSpelling();
+ Expect(Token::identifier);
+ std::string identifier = m_dil_token.GetSpelling();
ConsumeToken();
return llvm::formatv("{0}{1}", global_scope ? "::" : "", identifier);
}
@@ -252,11 +248,11 @@ std::string DILParser::ParseIdExpression() {
// identifier
//
// identifier:
-// ? dil::TokenKind::identifier ?
+// ? Token::identifier ?
//
std::string DILParser::ParseUnqualifiedId() {
- Expect(dil::TokenKind::identifier);
- std::string identifier = m_dil_token.getSpelling();
+ Expect(Token::identifier);
+ std::string identifier = m_dil_token.GetSpelling();
ConsumeToken();
return identifier;
}
@@ -271,7 +267,7 @@ void DILParser::BailOut(ErrorCode code, const std::string &error,
m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
FormatDiagnostics(m_input_expr, error, loc));
- m_dil_token.setKind(dil::TokenKind::eof);
+ m_dil_token = Token(Token::eof, "", 0);
}
void DILParser::BailOut(Status error) {
@@ -281,40 +277,32 @@ void DILParser::BailOut(Status error) {
return;
}
m_error = std::move(error);
- m_dil_token.setKind(dil::TokenKind::eof);
+ m_dil_token = Token(Token::eof, "", 0);
}
void DILParser::ConsumeToken() {
- if (m_dil_token.is(dil::TokenKind::eof)) {
+ if (m_dil_token.Is(Token::eof)) {
// Don't do anything if we're already at eof. This can happen if an error
// occurred during parsing and we're trying to bail out.
return;
}
- bool all_ok;
- m_dil_lexer.Lex(m_dil_token);
- if (m_dil_lexer.GetCurrentTokenIdx() == UINT_MAX)
- all_ok = m_dil_lexer.ResetTokenIdx(0);
- else
- all_ok = m_dil_lexer.IncrementTokenIdx();
- if (!all_ok)
- BailOut(ErrorCode::kUnknown, "Invalid lexer token index", 0);
+ m_dil_lexer.Advance();
+ m_dil_token = m_dil_lexer.GetCurrentToken();
}
-void DILParser::Expect(dil::TokenKind kind) {
- if (m_dil_token.isNot(kind)) {
+void DILParser::Expect(Token::Kind kind) {
+ if (m_dil_token.IsNot(kind)) {
BailOut(ErrorCode::kUnknown,
llvm::formatv("expected {0}, got: {1}", TokenKindsJoin(kind),
TokenDescription(m_dil_token)),
- m_dil_token.getLocation());
+ m_dil_token.GetLocation());
}
}
-std::string DILParser::TokenDescription(const DILToken &token) {
- const auto &spelling = token.getSpelling();
- const std::string kind_name = DILToken::getTokenName(token.getKind());
- return llvm::formatv("<'{0}' ({1})>", spelling, kind_name);
+std::string DILParser::TokenDescription(const Token &token) {
+ const auto &spelling = token.GetSpelling();
+ llvm::StringRef kind_name = Token::GetTokenName(token.GetKind());
+ return llvm::formatv("<'{0}' ({1})>", spelling, kind_name.str());
}
-} // namespace dil
-
-} // namespace lldb_private
+} // namespace lldb_private::dil
>From dc9b456c60df0981e4c4dc83fb1e4bbe9d66a62d Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Sun, 23 Feb 2025 23:18:39 -0800
Subject: [PATCH 12/21] Address many of the review comments:
- Remove else statements immediately following returns.
- Create a static 'Parse' method in DILParser, which calls the private
constructor and Run methods & makes sure status/error is short-lived &
handled properly.
- Make the m_error member of DILParser a reference.
- Update AST ErrorNode: Add llvm_unreachable to 'Accept' method and remove
call to Visit.
- Update code that looks up global variables to use GetInScopeVariablesList
rather than FindGlobalVariables.
- Remove code that checks for register names missing the initial '$'.
- Various StringRef cleanups suggested by reviewer.
- Rename 'DILInterpreter' to 'Interpreter'
- Clean up code in FormatDiagnostics function.
- Rename 'DILASTNode' to 'ASTNode'
- Rename 'DILASTNodeUP' to 'ASTNodeUP'
- Remove m_dil_token member var from DILParser; replace references to it with
new method, CurToken().
- Remove DILParser::ConsumeToken method.
- Fix 'assertEquals' calls in API tests.
---
lldb/include/lldb/ValueObject/DILAST.h | 23 ++-
lldb/include/lldb/ValueObject/DILEval.h | 19 +--
lldb/include/lldb/ValueObject/DILParser.h | 33 +++--
lldb/source/Target/StackFrame.cpp | 18 +--
lldb/source/ValueObject/DILAST.cpp | 4 +-
lldb/source/ValueObject/DILEval.cpp | 134 ++++++------------
lldb/source/ValueObject/DILParser.cpp | 126 +++++++---------
.../TestFrameVarDILGlobalVariableLookup.py | 2 +-
.../TestFrameVarDILInstanceVariables.py | 2 +-
.../LocalVars/TestFrameVarDILLocalVars.py | 2 +-
10 files changed, 156 insertions(+), 207 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index 6103476d3ee46..e7c37f25aed85 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -34,11 +34,11 @@ class Visitor;
/// 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 DILASTNode {
+class ASTNode {
public:
- DILASTNode(uint32_t location, NodeKind kind)
+ ASTNode(uint32_t location, NodeKind kind)
: m_location(location), m_kind(kind) {}
- virtual ~DILASTNode() = default;
+ virtual ~ASTNode() = default;
virtual llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const = 0;
@@ -50,32 +50,32 @@ class DILASTNode {
const NodeKind m_kind;
};
-using DILASTNodeUP = std::unique_ptr<DILASTNode>;
+using ASTNodeUP = std::unique_ptr<ASTNode>;
-class ErrorNode : public DILASTNode {
+class ErrorNode : public ASTNode {
public:
- ErrorNode() : DILASTNode(0, NodeKind::eErrorNode) {}
+ ErrorNode() : ASTNode(0, NodeKind::eErrorNode) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- static bool classof(const DILASTNode *node) {
+ static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eErrorNode;
}
};
-class IdentifierNode : public DILASTNode {
+class IdentifierNode : public ASTNode {
public:
IdentifierNode(uint32_t location, std::string name,
lldb::DynamicValueType use_dynamic,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
- : DILASTNode(location, NodeKind::eIdentifierNode),
- m_name(std::move(name)), m_use_dynamic(use_dynamic) {}
+ : ASTNode(location, NodeKind::eIdentifierNode), m_name(std::move(name)),
+ m_use_dynamic(use_dynamic) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
std::string GetName() const { return m_name; }
- static bool classof(const DILASTNode *node) {
+ static bool classof(const ASTNode *node) {
return node->GetKind() == NodeKind::eIdentifierNode;
}
@@ -91,7 +91,6 @@ class IdentifierNode : public DILASTNode {
class Visitor {
public:
virtual ~Visitor() = default;
- virtual llvm::Expected<lldb::ValueObjectSP> Visit(const ErrorNode *node) = 0;
virtual llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) = 0;
};
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 05e85a94750ad..cdb3c0e0f11c3 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -57,20 +57,21 @@ class IdentifierInfo {
/// etc.), find the ValueObject for that name (if it exists) and create and
/// return an IdentifierInfo object containing all the relevant information
/// about that object (for DIL parsing and evaluating).
-std::unique_ptr<IdentifierInfo> LookupIdentifier(
- const std::string &name, std::shared_ptr<ExecutionContextScope> ctx_scope,
- lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr = nullptr);
+std::unique_ptr<IdentifierInfo>
+LookupIdentifier(llvm::StringRef name_ref,
+ std::shared_ptr<ExecutionContextScope> ctx_scope,
+ lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr = nullptr);
-class DILInterpreter : Visitor {
+class Interpreter : Visitor {
public:
- DILInterpreter(lldb::TargetSP target, llvm::StringRef expr,
- lldb::DynamicValueType use_dynamic,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope);
+ Interpreter(lldb::TargetSP target, llvm::StringRef expr,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope);
- llvm::Expected<lldb::ValueObjectSP> DILEvalNode(const DILASTNode *node);
+ llvm::Expected<lldb::ValueObjectSP> DILEvalNode(const ASTNode *node);
private:
- llvm::Expected<lldb::ValueObjectSP> Visit(const ErrorNode *node) override;
llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) override;
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 574b300ce90c4..80a4b83ac181d 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -35,12 +35,11 @@ std::string FormatDiagnostics(llvm::StringRef input_expr,
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
class DILParser {
public:
- explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
- lldb::DynamicValueType use_dynamic, bool use_synthetic,
- bool fragile_ivar, bool check_ptr_vs_member);
-
- llvm::Expected<DILASTNodeUP> Run();
+ static llvm::Expected<ASTNodeUP>
+ Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member);
~DILParser() { m_ctx_scope.reset(); }
@@ -51,16 +50,22 @@ class DILParser {
using PtrOperator = std::tuple<Token::Kind, uint32_t>;
private:
- DILASTNodeUP ParseExpression();
- DILASTNodeUP ParsePrimaryExpression();
+ explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member,
+ Status &error);
+
+ llvm::Expected<ASTNodeUP> Run();
+
+ ASTNodeUP ParseExpression();
+ ASTNodeUP ParsePrimaryExpression();
std::string ParseNestedNameSpecifier();
std::string ParseIdExpression();
std::string ParseUnqualifiedId();
- void ConsumeToken();
-
void BailOut(ErrorCode error_code, const std::string &error, uint32_t loc);
void BailOut(Status error);
@@ -72,9 +77,10 @@ class DILParser {
void TentativeParsingRollback(uint32_t saved_idx) {
m_error.Clear();
m_dil_lexer.ResetTokenIdx(saved_idx);
- m_dil_token = m_dil_lexer.GetCurrentToken();
}
+ 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.
@@ -83,10 +89,9 @@ class DILParser {
llvm::StringRef m_input_expr;
DILLexer m_dil_lexer;
- // The token lexer is stopped at (aka "current token").
- Token m_dil_token;
+
// Holds an error if it occures during parsing.
- Status m_error;
+ Status &m_error;
lldb::DynamicValueType m_use_dynamic;
bool m_use_synthetic;
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index 5d672ee696019..a0acc3e8229de 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -520,9 +520,9 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
if (use_DIL)
return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
var_sp, error);
- else
- return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic,
- options, var_sp, error);
+
+ return LegacyGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
+ var_sp, error);
}
ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
@@ -542,12 +542,12 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
error = Status::FromError(lex_or_err.takeError());
return ValueObjectSP();
}
- dil::DILLexer lexer = *lex_or_err;
+ dil::DILLexer &lexer = *lex_or_err;
// Parse the expression.
- dil::DILParser parser(var_expr, lexer, shared_from_this(), use_dynamic,
- !no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
- auto tree_or_error = parser.Run();
+ auto tree_or_error = dil::DILParser::Parse(
+ var_expr, lexer, shared_from_this(), use_dynamic, !no_synth_child,
+ !no_fragile_ivar, check_ptr_vs_member);
if (!tree_or_error) {
error = Status::FromError(tree_or_error.takeError());
return ValueObjectSP();
@@ -555,8 +555,8 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
// Evaluate the parsed expression.
lldb::TargetSP target = this->CalculateTarget();
- dil::DILInterpreter interpreter(target, var_expr, use_dynamic,
- shared_from_this());
+ dil::Interpreter interpreter(target, var_expr, use_dynamic,
+ shared_from_this());
auto valobj_or_error = interpreter.DILEvalNode((*tree_or_error).get());
if (!valobj_or_error) {
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index 34c8d1038250b..0181a6aefcb16 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -7,11 +7,13 @@
//===----------------------------------------------------------------------===//
#include "lldb/ValueObject/DILAST.h"
+#include "llvm/Support/ErrorHandling.h"
namespace lldb_private::dil {
llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const {
- return v->Visit(this);
+ llvm_unreachable("Attempting to Visit a DIL ErrorNode.");
+ return lldb::ValueObjectSP();
}
llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index a72f9b3c136b9..5826e956b5371 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -19,27 +19,30 @@
namespace lldb_private::dil {
static lldb::ValueObjectSP
-LookupStaticIdentifier(lldb::TargetSP target_sp,
- const llvm::StringRef &name_ref,
- ConstString unqualified_name) {
- // List global variable with the same "basename". There can be many matches
- // from other scopes (namespaces, classes), so we do additional filtering
- // later.
- VariableList variable_list;
- ConstString name(name_ref);
- target_sp->GetImages().FindGlobalVariables(name, 1, variable_list);
- if (variable_list.Empty())
+LookupStaticIdentifier(VariableList &variable_list,
+ std::shared_ptr<ExecutionContextScope> exe_scope,
+ llvm::StringRef name_ref,
+ llvm::StringRef unqualified_name) {
+ // First look for an exact match to the (possibly) qualified name.
+ for (const lldb::VariableSP &var_sp : variable_list) {
+ lldb::ValueObjectSP valobj_sp(
+ ValueObjectVariable::Create(exe_scope.get(), var_sp));
+ if (valobj_sp && valobj_sp->GetVariable() &&
+ (valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
+ return valobj_sp;
+ }
+
+ // If the qualified name is the same as the unqualfied name, there's nothing
+ // more to be done.
+ if (name_ref == unqualified_name)
return nullptr;
- ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get();
- if (exe_scope == nullptr)
- exe_scope = target_sp.get();
+ // We didn't match the qualified name; try to match the unqualified name.
for (const lldb::VariableSP &var_sp : variable_list) {
lldb::ValueObjectSP valobj_sp(
- ValueObjectVariable::Create(exe_scope, var_sp));
+ ValueObjectVariable::Create(exe_scope.get(), var_sp));
if (valobj_sp && valobj_sp->GetVariable() &&
- (valobj_sp->GetVariable()->NameMatches(unqualified_name) ||
- valobj_sp->GetVariable()->NameMatches(ConstString(name_ref))))
+ (valobj_sp->GetVariable()->NameMatches(ConstString(unqualified_name))))
return valobj_sp;
}
@@ -79,41 +82,33 @@ static lldb::VariableSP DILFindVariable(ConstString name,
// Look for a global var exact match.
for (auto var_sp : possible_matches) {
llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
- if (str_ref_name.size() > 2 && str_ref_name[0] == ':' &&
- str_ref_name[1] == ':')
- str_ref_name = str_ref_name.drop_front(2);
- ConstString tmp_name(str_ref_name);
- if (tmp_name == name) {
+ str_ref_name.consume_front("::");
+ if (str_ref_name == name.GetStringRef()) {
exact_match = var_sp;
break;
}
}
- // Take any match at this point.
- if (!exact_match && possible_matches.size() > 0)
+ if (!exact_match && possible_matches.size() == 1)
exact_match = possible_matches[0];
return exact_match;
}
std::unique_ptr<IdentifierInfo>
-LookupIdentifier(const std::string &name,
+LookupIdentifier(llvm::StringRef name_ref,
std::shared_ptr<ExecutionContextScope> ctx_scope,
- lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
- ConstString name_str(name);
- llvm::StringRef name_ref = name_str.GetStringRef();
-
+ lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr) {
// Support $rax as a special syntax for accessing registers.
// Will return an invalid value in case the requested register doesn't exist.
- if (name_ref.starts_with("$")) {
+ if (name_ref.consume_front("$")) {
lldb::ValueObjectSP value_sp;
- const char *reg_name = name_ref.drop_front(1).data();
- Target *target = ctx_scope->CalculateTarget().get();
Process *process = ctx_scope->CalculateProcess().get();
- if (!target || !process)
+ if (!target_sp || !process)
return nullptr;
- StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
+ StackFrame *stack_frame = (StackFrame *)ctx_scope.get();
if (!stack_frame)
return nullptr;
@@ -121,7 +116,7 @@ LookupIdentifier(const std::string &name,
if (!reg_ctx)
return nullptr;
- if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name))
+ if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name_ref))
value_sp = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
if (value_sp)
@@ -130,24 +125,21 @@ LookupIdentifier(const std::string &name,
return nullptr;
}
+ lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
+ lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
+ VariableList *variable_list = var_list_sp.get();
+
// Internally values don't have global scope qualifier in their names and
// LLDB doesn't support queries with it too.
- bool global_scope = false;
- if (name_ref.starts_with("::")) {
- name_ref = name_ref.drop_front(2);
- global_scope = true;
- }
+ bool global_scope = name_ref.consume_front("::");
// If the identifier doesn't refer to the global scope and doesn't have any
// other scope qualifiers, try looking among the local and instance variables.
if (!global_scope && !name_ref.contains("::")) {
if (!scope_ptr || !scope_ptr->IsValid()) {
// Lookup in the current frame.
- lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
// Try looking for a local variable in current scope.
lldb::ValueObjectSP value_sp;
- lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
- VariableList *variable_list = var_list_sp.get();
if (variable_list) {
lldb::VariableSP var_sp =
DILFindVariable(ConstString(name_ref), variable_list);
@@ -158,7 +150,6 @@ LookupIdentifier(const std::string &name,
value_sp = frame->FindVariable(ConstString(name_ref));
if (value_sp)
- // Force static value, otherwise we can end up with the "real" type.
return IdentifierInfo::FromValue(*value_sp);
// Try looking for an instance variable (class member).
@@ -170,47 +161,23 @@ LookupIdentifier(const std::string &name,
value_sp = value_sp->GetChildMemberWithName(name_ref);
if (value_sp)
- // Force static value, otherwise we can end up with the "real" type.
- return IdentifierInfo::FromValue(*(value_sp->GetStaticValue()));
+ return IdentifierInfo::FromValue(*(value_sp));
}
}
// Try looking for a global or static variable.
-
lldb::ValueObjectSP value;
- if (!global_scope) {
- // Try looking for static member of the current scope value, e.g.
- // `ScopeType::NAME`. NAME can include nested struct (`Nested::SUBNAME`),
- // but it cannot be part of the global scope (start with "::").
+ if (variable_list) {
const char *type_name = "";
if (scope_ptr)
type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
std::string name_with_type_prefix =
llvm::formatv("{0}::{1}", type_name, name_ref).str();
- value = LookupStaticIdentifier(ctx_scope->CalculateTarget(),
- name_with_type_prefix, name_str);
- }
-
- // Lookup a regular global variable.
- if (!value)
- value = LookupStaticIdentifier(ctx_scope->CalculateTarget(), name_ref,
- name_str);
-
- // Last resort, lookup as a register (e.g. `rax` or `rip`).
- if (!value) {
- Target *target = ctx_scope->CalculateTarget().get();
- Process *process = ctx_scope->CalculateProcess().get();
- if (target && process) {
- StackFrame *stack_frame = ctx_scope->CalculateStackFrame().get();
- if (stack_frame) {
- lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
- if (reg_ctx) {
- if (const RegisterInfo *reg_info =
- reg_ctx->GetRegisterInfoByName(name_ref))
- value = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
- }
- }
- }
+ value = LookupStaticIdentifier(*variable_list, ctx_scope,
+ name_with_type_prefix, name_ref);
+ if (!value)
+ value =
+ LookupStaticIdentifier(*variable_list, ctx_scope, name_ref, name_ref);
}
if (value)
@@ -219,15 +186,14 @@ LookupIdentifier(const std::string &name,
return nullptr;
}
-DILInterpreter::DILInterpreter(
- lldb::TargetSP target, llvm::StringRef expr,
- lldb::DynamicValueType use_dynamic,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
+Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
+ lldb::DynamicValueType use_dynamic,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
m_exe_ctx_scope(exe_ctx_scope) {}
llvm::Expected<lldb::ValueObjectSP>
-DILInterpreter::DILEvalNode(const DILASTNode *node) {
+Interpreter::DILEvalNode(const ASTNode *node) {
// Traverse an AST pointed by the `node`.
auto value_or_error = node->Accept(this);
@@ -237,17 +203,11 @@ DILInterpreter::DILEvalNode(const DILASTNode *node) {
}
llvm::Expected<lldb::ValueObjectSP>
-DILInterpreter::Visit(const ErrorNode *node) {
- // The AST is not valid.
- return lldb::ValueObjectSP();
-}
-
-llvm::Expected<lldb::ValueObjectSP>
-DILInterpreter::Visit(const IdentifierNode *node) {
+Interpreter::Visit(const IdentifierNode *node) {
lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
std::unique_ptr<IdentifierInfo> identifier =
- LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
+ LookupIdentifier(node->GetName(), m_exe_ctx_scope, m_target, use_dynamic);
if (!identifier) {
std::string errMsg;
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index e37513b1a8550..4522a2352d01d 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -47,44 +47,38 @@ inline std::string TokenKindsJoin(Token::Kind k, Ts... ks) {
std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
uint32_t loc) {
- // Get the source buffer and the location of the current token.
- size_t loc_offset = (size_t)loc;
-
- // Look for the start of the line.
- size_t line_start = text.rfind('\n', loc_offset);
- line_start = line_start == llvm::StringRef::npos ? 0 : line_start + 1;
-
- // Look for the end of the line.
- size_t line_end = text.find('\n', loc_offset);
- line_end = line_end == llvm::StringRef::npos ? text.size() : line_end;
-
- // Get a view of the current line in the source code and the position of the
- // diagnostics pointer.
- llvm::StringRef line = text.slice(line_start, line_end);
+ // Get the position, in the current line of text, of the diagnostics pointer.
+ // ('loc' is the location of the start of the current token/error within the
+ // overal text line).
int32_t arrow = loc + 1; // Column offset starts at 1, not 0.
- // Calculate the padding in case we point outside of the expression (this can
- // happen if the parser expected something, but got EOF).˚
- size_t expr_rpad = std::max(0, arrow - static_cast<int32_t>(line.size()));
- size_t arrow_rpad = std::max(0, static_cast<int32_t>(line.size()) - arrow);
-
return llvm::formatv("<expr:1:{0}>: {1}\n{2}\n{3}", loc, message,
- llvm::fmt_pad(line, 0, expr_rpad),
- llvm::fmt_pad("^", arrow - 1, arrow_rpad));
+ llvm::fmt_pad(text, 0, 0),
+ llvm::fmt_pad("^", arrow - 1, 0));
+}
+
+llvm::Expected<ASTNodeUP>
+DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
+ std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ lldb::DynamicValueType use_dynamic, bool use_synthetic,
+ bool fragile_ivar, bool check_ptr_vs_member) {
+ Status error;
+ DILParser parser(dil_input_expr, lexer, exe_ctx_scope, use_dynamic,
+ use_synthetic, fragile_ivar, check_ptr_vs_member, error);
+ return parser.Run();
}
DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
- bool fragile_ivar, bool check_ptr_vs_member)
+ bool fragile_ivar, bool check_ptr_vs_member, Status &error)
: m_ctx_scope(exe_ctx_scope), m_input_expr(dil_input_expr),
- m_dil_lexer(lexer), m_dil_token(lexer.GetCurrentToken()),
- m_use_dynamic(use_dynamic), m_use_synthetic(use_synthetic),
- m_fragile_ivar(fragile_ivar), m_check_ptr_vs_member(check_ptr_vs_member) {
-}
+ m_dil_lexer(lexer), m_error(error), m_use_dynamic(use_dynamic),
+ m_use_synthetic(use_synthetic), m_fragile_ivar(fragile_ivar),
+ m_check_ptr_vs_member(check_ptr_vs_member) {}
-llvm::Expected<DILASTNodeUP> DILParser::Run() {
- DILASTNodeUP expr;
+llvm::Expected<ASTNodeUP> DILParser::Run() {
+ ASTNodeUP expr;
expr = ParseExpression();
@@ -101,34 +95,33 @@ llvm::Expected<DILASTNodeUP> DILParser::Run() {
// expression:
// primary_expression
//
-DILASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
+ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
// Parse a primary_expression.
//
// primary_expression:
// id_expression
-// "this"
// "(" expression ")"
//
-DILASTNodeUP DILParser::ParsePrimaryExpression() {
- if (m_dil_token.IsOneOf(Token::coloncolon, Token::identifier)) {
+ASTNodeUP DILParser::ParsePrimaryExpression() {
+ if (CurToken().IsOneOf(Token::coloncolon, Token::identifier)) {
// Save the source location for the diagnostics message.
- uint32_t loc = m_dil_token.GetLocation();
+ uint32_t loc = CurToken().GetLocation();
auto identifier = ParseIdExpression();
return std::make_unique<IdentifierNode>(loc, identifier, m_use_dynamic,
m_ctx_scope);
- } else if (m_dil_token.Is(Token::l_paren)) {
- ConsumeToken();
+ } else if (CurToken().Is(Token::l_paren)) {
+ m_dil_lexer.Advance();
auto expr = ParseExpression();
Expect(Token::r_paren);
- ConsumeToken();
+ m_dil_lexer.Advance();
return expr;
}
BailOut(ErrorCode::kInvalidExpressionSyntax,
- llvm::formatv("Unexpected token: {0}", TokenDescription(m_dil_token)),
- m_dil_token.GetLocation());
+ llvm::formatv("Unexpected token: {0}", TokenDescription(CurToken())),
+ CurToken().GetLocation());
return std::make_unique<ErrorNode>();
}
@@ -142,8 +135,7 @@ DILASTNodeUP DILParser::ParsePrimaryExpression() {
std::string DILParser::ParseNestedNameSpecifier() {
// The first token in nested_name_specifier is always an identifier, or
// '(anonymous namespace)'.
- if (m_dil_token.IsNot(Token::identifier) &&
- m_dil_token.IsNot(Token::l_paren)) {
+ if (CurToken().IsNot(Token::identifier) && CurToken().IsNot(Token::l_paren)) {
return "";
}
@@ -151,7 +143,7 @@ std::string DILParser::ParseNestedNameSpecifier() {
// the the string '(anonymous namespace)', which has a space in it (throwing
// off normal parsing) and is not actually proper C++> Check to see if we're
// looking at '(anonymous namespace)::...'
- if (m_dil_token.Is(Token::l_paren)) {
+ if (CurToken().Is(Token::l_paren)) {
// Look for all the pieces, in order:
// l_paren 'anonymous' 'namespace' r_paren coloncolon
if (m_dil_lexer.LookAhead(1).Is(Token::identifier) &&
@@ -161,17 +153,16 @@ std::string DILParser::ParseNestedNameSpecifier() {
m_dil_lexer.LookAhead(3).Is(Token::r_paren) &&
m_dil_lexer.LookAhead(4).Is(Token::coloncolon)) {
m_dil_lexer.Advance(4);
- m_dil_token = m_dil_lexer.GetCurrentToken();
- assert((m_dil_token.Is(Token::identifier) ||
- m_dil_token.Is(Token::l_paren)) &&
- "Expected an identifier or anonymous namespace, but not found.");
+ assert(
+ (CurToken().Is(Token::identifier) || CurToken().Is(Token::l_paren)) &&
+ "Expected an identifier or anonymous namespace, but not found.");
// Continue parsing the nested_namespace_specifier.
std::string identifier2 = ParseNestedNameSpecifier();
if (identifier2.empty()) {
Expect(Token::identifier);
- identifier2 = m_dil_token.GetSpelling();
- ConsumeToken();
+ identifier2 = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
}
return "(anonymous namespace)::" + identifier2;
}
@@ -183,11 +174,10 @@ std::string DILParser::ParseNestedNameSpecifier() {
// nested_name_specifier
if (m_dil_lexer.LookAhead(1).Is(Token::coloncolon)) {
// This nested_name_specifier is a single identifier.
- std::string identifier = m_dil_token.GetSpelling();
+ std::string identifier = CurToken().GetSpelling();
m_dil_lexer.Advance(1);
- m_dil_token = m_dil_lexer.GetCurrentToken();
Expect(Token::coloncolon);
- ConsumeToken();
+ m_dil_lexer.Advance();
// Continue parsing the nested_name_specifier.
return identifier + "::" + ParseNestedNameSpecifier();
}
@@ -211,9 +201,9 @@ std::string DILParser::ParseNestedNameSpecifier() {
std::string DILParser::ParseIdExpression() {
// Try parsing optional global scope operator.
bool global_scope = false;
- if (m_dil_token.Is(Token::coloncolon)) {
+ if (CurToken().Is(Token::coloncolon)) {
global_scope = true;
- ConsumeToken();
+ m_dil_lexer.Advance();
}
// Try parsing optional nested_name_specifier.
@@ -233,8 +223,8 @@ std::string DILParser::ParseIdExpression() {
// qualified_id production. Follow the second production rule.
else if (global_scope) {
Expect(Token::identifier);
- std::string identifier = m_dil_token.GetSpelling();
- ConsumeToken();
+ std::string identifier = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
return llvm::formatv("{0}{1}", global_scope ? "::" : "", identifier);
}
@@ -252,8 +242,8 @@ std::string DILParser::ParseIdExpression() {
//
std::string DILParser::ParseUnqualifiedId() {
Expect(Token::identifier);
- std::string identifier = m_dil_token.GetSpelling();
- ConsumeToken();
+ std::string identifier = CurToken().GetSpelling();
+ m_dil_lexer.Advance();
return identifier;
}
@@ -267,7 +257,8 @@ void DILParser::BailOut(ErrorCode code, const std::string &error,
m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
FormatDiagnostics(m_input_expr, error, loc));
- m_dil_token = Token(Token::eof, "", 0);
+ // Advance the lexer token index to the end of the lexed tokens vector.
+ m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
void DILParser::BailOut(Status error) {
@@ -277,32 +268,23 @@ void DILParser::BailOut(Status error) {
return;
}
m_error = std::move(error);
- m_dil_token = Token(Token::eof, "", 0);
-}
-
-void DILParser::ConsumeToken() {
- if (m_dil_token.Is(Token::eof)) {
- // Don't do anything if we're already at eof. This can happen if an error
- // occurred during parsing and we're trying to bail out.
- return;
- }
- m_dil_lexer.Advance();
- m_dil_token = m_dil_lexer.GetCurrentToken();
+ // Advance the lexer token index to the end of the lexed tokens vector.
+ m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
void DILParser::Expect(Token::Kind kind) {
- if (m_dil_token.IsNot(kind)) {
+ if (CurToken().IsNot(kind)) {
BailOut(ErrorCode::kUnknown,
llvm::formatv("expected {0}, got: {1}", TokenKindsJoin(kind),
- TokenDescription(m_dil_token)),
- m_dil_token.GetLocation());
+ TokenDescription(CurToken())),
+ CurToken().GetLocation());
}
}
std::string DILParser::TokenDescription(const Token &token) {
const auto &spelling = token.GetSpelling();
llvm::StringRef kind_name = Token::GetTokenName(token.GetKind());
- return llvm::formatv("<'{0}' ({1})>", spelling, kind_name.str());
+ return llvm::formatv("<'{0}' ({1})>", spelling, kind_name);
}
} // namespace lldb_private::dil
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index ed41757bad2af..74245511c4520 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -51,7 +51,7 @@ def do_test(self):
len(threads), 1, "There should be a thread stopped at our breakpoint"
)
# The hit count for the breakpoint should be 1.
- self.assertEquals(breakpoint.GetHitCount(), 1)
+ self.assertEqual(breakpoint.GetHitCount(), 1)
frame = threads[0].GetFrameAtIndex(0)
command_result = lldb.SBCommandReturnObject()
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index 4ef645c15f680..d84f4f5ef7bb7 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -51,7 +51,7 @@ def do_test(self):
len(threads), 1, "There should be a thread stopped at our breakpoint"
)
# The hit count for the breakpoint should be 1.
- self.assertEquals(breakpoint.GetHitCount(), 1)
+ self.assertEqual(breakpoint.GetHitCount(), 1)
frame = threads[0].GetFrameAtIndex(0)
command_result = lldb.SBCommandReturnObject()
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index 17bce7b9de95b..42b2060a7694c 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -51,7 +51,7 @@ def do_test(self):
len(threads), 1, "There should be a thread stopped at our breakpoint"
)
# The hit count for the breakpoint should be 1.
- self.assertEquals(breakpoint.GetHitCount(), 1)
+ self.assertEqual(breakpoint.GetHitCount(), 1)
frame = threads[0].GetFrameAtIndex(0)
command_result = lldb.SBCommandReturnObject()
>From 06ff79b02f50f55a0ef08b81c9ded25279eba6dc Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 27 Feb 2025 23:21:36 -0800
Subject: [PATCH 13/21] Address remaining review comments:
- Pull code for looking up global variables out of LookupIdentifer; create a
separate function, LookupGlobalIdentifier, to look for global variables.
Only call LookupGlobalIdentifier if LookupIdentifier fails to find anything.
- Update the ExecutionContextScope (shared pointer) members in the DIL parser
and interpreter to actually be StackFrame shared pointers, initialized to the
current stack frame (passed in from DILGetValueForVariableExpressionPath).
- Replace custom formatters for Token::Kind and Token with LLVM
format_providers.
- Minor code cleanups suggested by reviewer.
---
lldb/include/lldb/ValueObject/DILEval.h | 25 ++--
lldb/include/lldb/ValueObject/DILLexer.h | 33 +++++
lldb/include/lldb/ValueObject/DILParser.h | 17 ++-
lldb/source/ValueObject/DILEval.cpp | 139 +++++++++++++---------
lldb/source/ValueObject/DILParser.cpp | 43 ++-----
5 files changed, 150 insertions(+), 107 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index cdb3c0e0f11c3..f80ac9ddf6238 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -54,20 +54,29 @@ class IdentifierInfo {
};
/// Given the name of an identifier (variable name, member name, type name,
-/// etc.), find the ValueObject for that name (if it exists) and create and
-/// return an IdentifierInfo object containing all the relevant information
-/// about that object (for DIL parsing and evaluating).
+/// 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).
std::unique_ptr<IdentifierInfo>
-LookupIdentifier(llvm::StringRef name_ref,
- std::shared_ptr<ExecutionContextScope> ctx_scope,
- lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
+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.
+std::unique_ptr<IdentifierInfo> 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<ExecutionContextScope> exe_ctx_scope);
+ std::shared_ptr<StackFrame> frame_sp);
llvm::Expected<lldb::ValueObjectSP> DILEvalNode(const ASTNode *node);
@@ -85,7 +94,7 @@ class Interpreter : Visitor {
lldb::DynamicValueType m_default_dynamic;
- std::shared_ptr<ExecutionContextScope> m_exe_ctx_scope;
+ std::shared_ptr<StackFrame> m_exe_ctx_scope;
};
} // namespace lldb_private::dil
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index e1182da5b20ab..d65306d35f511 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -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>
@@ -43,6 +44,18 @@ class Token {
bool IsOneOf(Kind kind1, Kind kind2) const { return Is(kind1) || Is(kind2); }
+ bool IsOneOf(std::vector<Kind> kinds) const {
+ if (kinds.empty())
+ return false;
+
+ if (kinds.size() == 1)
+ return Is(kinds[0]);
+
+ Kind k = kinds.back();
+ kinds.pop_back();
+ return Is(k) || IsOneOf(kinds);
+ }
+
template <typename... Ts> bool IsOneOf(Kind kind, Ts... Ks) const {
return Is(kind) || IsOneOf(Ks...);
}
@@ -120,4 +133,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
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 80a4b83ac181d..939fc50cd4ee3 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -35,11 +35,12 @@ std::string FormatDiagnostics(llvm::StringRef input_expr,
/// 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<ExecutionContextScope> exe_ctx_scope,
- lldb::DynamicValueType use_dynamic, bool use_synthetic,
- bool fragile_ivar, bool check_ptr_vs_member);
+ 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() { m_ctx_scope.reset(); }
@@ -51,7 +52,7 @@ class DILParser {
private:
explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member,
Status &error);
@@ -72,8 +73,6 @@ class DILParser {
void Expect(Token::Kind kind);
- std::string TokenDescription(const Token &token);
-
void TentativeParsingRollback(uint32_t saved_idx) {
m_error.Clear();
m_dil_lexer.ResetTokenIdx(saved_idx);
@@ -84,7 +83,7 @@ class DILParser {
// 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<ExecutionContextScope> m_ctx_scope;
+ std::shared_ptr<StackFrame> m_ctx_scope;
llvm::StringRef m_input_expr;
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 5826e956b5371..9f50d1329325f 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -18,11 +18,9 @@
namespace lldb_private::dil {
-static lldb::ValueObjectSP
-LookupStaticIdentifier(VariableList &variable_list,
- std::shared_ptr<ExecutionContextScope> exe_scope,
- llvm::StringRef name_ref,
- llvm::StringRef unqualified_name) {
+static lldb::ValueObjectSP LookupStaticIdentifier(
+ VariableList &variable_list, std::shared_ptr<StackFrame> exe_scope,
+ llvm::StringRef name_ref, llvm::StringRef unqualified_name) {
// First look for an exact match to the (possibly) qualified name.
for (const lldb::VariableSP &var_sp : variable_list) {
lldb::ValueObjectSP valobj_sp(
@@ -50,7 +48,7 @@ LookupStaticIdentifier(VariableList &variable_list,
}
static lldb::VariableSP DILFindVariable(ConstString name,
- VariableList *variable_list) {
+ lldb::VariableListSP variable_list) {
lldb::VariableSP exact_match;
std::vector<lldb::VariableSP> possible_matches;
@@ -75,7 +73,7 @@ static lldb::VariableSP DILFindVariable(ConstString name,
return var_sp->GetName() == name;
});
- if (exact_match_it != llvm::adl_end(possible_matches))
+ if (exact_match_it != possible_matches.end())
exact_match = *exact_match_it;
if (!exact_match)
@@ -95,29 +93,82 @@ static lldb::VariableSP DILFindVariable(ConstString name,
return exact_match;
}
+std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
+ llvm::StringRef name_ref, std::shared_ptr<StackFrame> stack_frame,
+ lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr) {
+ // First look for match in "local" global variables.
+ lldb::VariableListSP variable_list(stack_frame->GetInScopeVariableList(true));
+ name_ref.consume_front("::");
+
+ lldb::ValueObjectSP value_sp;
+ if (variable_list) {
+ lldb::VariableSP var_sp =
+ DILFindVariable(ConstString(name_ref), variable_list);
+ if (var_sp)
+ value_sp =
+ stack_frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ }
+
+ if (value_sp)
+ return IdentifierInfo::FromValue(*value_sp);
+
+ // Also check for static global vars.
+ if (variable_list) {
+ const char *type_name = "";
+ if (scope_ptr)
+ type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
+ std::string name_with_type_prefix =
+ llvm::formatv("{0}::{1}", type_name, name_ref).str();
+ value_sp = LookupStaticIdentifier(*variable_list, stack_frame,
+ name_with_type_prefix, name_ref);
+ if (!value_sp)
+ value_sp = LookupStaticIdentifier(*variable_list, stack_frame, name_ref,
+ name_ref);
+ }
+
+ if (value_sp)
+ return IdentifierInfo::FromValue(*value_sp);
+
+ // Check for match in modules global variables.
+ VariableList modules_var_list;
+ target_sp->GetImages().FindGlobalVariables(
+ ConstString(name_ref), std::numeric_limits<uint32_t>::max(),
+ modules_var_list);
+ if (modules_var_list.Empty())
+ return nullptr;
+
+ for (const lldb::VariableSP &var_sp : modules_var_list) {
+ std::string qualified_name = llvm::formatv("::{0}", name_ref).str();
+ if (var_sp->NameMatches(ConstString(name_ref)) ||
+ var_sp->NameMatches(ConstString(qualified_name))) {
+ value_sp = ValueObjectVariable::Create(stack_frame.get(), var_sp);
+ break;
+ }
+ }
+
+ if (value_sp)
+ return IdentifierInfo::FromValue(*value_sp);
+
+ return nullptr;
+}
+
std::unique_ptr<IdentifierInfo>
LookupIdentifier(llvm::StringRef name_ref,
- std::shared_ptr<ExecutionContextScope> ctx_scope,
- lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
- CompilerType *scope_ptr) {
+ std::shared_ptr<StackFrame> stack_frame,
+ lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
// Support $rax as a special syntax for accessing registers.
// Will return an invalid value in case the requested register doesn't exist.
if (name_ref.consume_front("$")) {
lldb::ValueObjectSP value_sp;
- Process *process = ctx_scope->CalculateProcess().get();
- if (!target_sp || !process)
- return nullptr;
-
- StackFrame *stack_frame = (StackFrame *)ctx_scope.get();
- if (!stack_frame)
- return nullptr;
lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
if (!reg_ctx)
return nullptr;
if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name_ref))
- value_sp = ValueObjectRegister::Create(stack_frame, reg_ctx, reg_info);
+ value_sp =
+ ValueObjectRegister::Create(stack_frame.get(), reg_ctx, reg_info);
if (value_sp)
return IdentifierInfo::FromValue(*value_sp);
@@ -125,17 +176,10 @@ LookupIdentifier(llvm::StringRef name_ref,
return nullptr;
}
- lldb::StackFrameSP frame = ctx_scope->CalculateStackFrame();
- lldb::VariableListSP var_list_sp(frame->GetInScopeVariableList(true));
- VariableList *variable_list = var_list_sp.get();
+ lldb::VariableListSP variable_list(
+ stack_frame->GetInScopeVariableList(false));
- // Internally values don't have global scope qualifier in their names and
- // LLDB doesn't support queries with it too.
- bool global_scope = name_ref.consume_front("::");
-
- // If the identifier doesn't refer to the global scope and doesn't have any
- // other scope qualifiers, try looking among the local and instance variables.
- if (!global_scope && !name_ref.contains("::")) {
+ if (!name_ref.contains("::")) {
if (!scope_ptr || !scope_ptr->IsValid()) {
// Lookup in the current frame.
// Try looking for a local variable in current scope.
@@ -144,19 +188,20 @@ LookupIdentifier(llvm::StringRef name_ref,
lldb::VariableSP var_sp =
DILFindVariable(ConstString(name_ref), variable_list);
if (var_sp)
- value_sp = frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
+ value_sp =
+ stack_frame->GetValueObjectForFrameVariable(var_sp, use_dynamic);
}
if (!value_sp)
- value_sp = frame->FindVariable(ConstString(name_ref));
+ value_sp = stack_frame->FindVariable(ConstString(name_ref));
if (value_sp)
return IdentifierInfo::FromValue(*value_sp);
// Try looking for an instance variable (class member).
- SymbolContext sc = frame->GetSymbolContext(lldb::eSymbolContextFunction |
- lldb::eSymbolContextBlock);
+ SymbolContext sc = stack_frame->GetSymbolContext(
+ lldb::eSymbolContextFunction | lldb::eSymbolContextBlock);
llvm::StringRef ivar_name = sc.GetInstanceVariableName();
- value_sp = frame->FindVariable(ConstString(ivar_name));
+ value_sp = stack_frame->FindVariable(ConstString(ivar_name));
if (value_sp)
value_sp = value_sp->GetChildMemberWithName(name_ref);
@@ -164,33 +209,14 @@ LookupIdentifier(llvm::StringRef name_ref,
return IdentifierInfo::FromValue(*(value_sp));
}
}
-
- // Try looking for a global or static variable.
- lldb::ValueObjectSP value;
- if (variable_list) {
- const char *type_name = "";
- if (scope_ptr)
- type_name = scope_ptr->GetCanonicalType().GetTypeName().AsCString();
- std::string name_with_type_prefix =
- llvm::formatv("{0}::{1}", type_name, name_ref).str();
- value = LookupStaticIdentifier(*variable_list, ctx_scope,
- name_with_type_prefix, name_ref);
- if (!value)
- value =
- LookupStaticIdentifier(*variable_list, ctx_scope, name_ref, name_ref);
- }
-
- if (value)
- return IdentifierInfo::FromValue(*value);
-
return nullptr;
}
Interpreter::Interpreter(lldb::TargetSP target, llvm::StringRef expr,
lldb::DynamicValueType use_dynamic,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
+ std::shared_ptr<StackFrame> frame_sp)
: m_target(std::move(target)), m_expr(expr), m_default_dynamic(use_dynamic),
- m_exe_ctx_scope(exe_ctx_scope) {}
+ m_exe_ctx_scope(frame_sp) {}
llvm::Expected<lldb::ValueObjectSP>
Interpreter::DILEvalNode(const ASTNode *node) {
@@ -207,8 +233,11 @@ Interpreter::Visit(const IdentifierNode *node) {
lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
std::unique_ptr<IdentifierInfo> identifier =
- LookupIdentifier(node->GetName(), m_exe_ctx_scope, m_target, use_dynamic);
+ LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
+ if (!identifier)
+ identifier = LookupGlobalIdentifier(node->GetName(), m_exe_ctx_scope,
+ m_target, use_dynamic);
if (!identifier) {
std::string errMsg;
std::string name = node->GetName();
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 4522a2352d01d..ee32a8b878cd3 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -25,26 +25,6 @@
namespace lldb_private::dil {
-inline void TokenKindsJoinImpl(std::ostringstream &os, Token::Kind k) {
- os << "'" << Token::GetTokenName(k).str() << "'";
-}
-
-template <typename... Ts>
-inline void TokenKindsJoinImpl(std::ostringstream &os, Token::Kind k,
- Ts... ks) {
- TokenKindsJoinImpl(os, k);
- os << ", ";
- TokenKindsJoinImpl(os, ks...);
-}
-
-template <typename... Ts>
-inline std::string TokenKindsJoin(Token::Kind k, Ts... ks) {
- std::ostringstream os;
- TokenKindsJoinImpl(os, k, ks...);
-
- return os.str();
-}
-
std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
uint32_t loc) {
// Get the position, in the current line of text, of the diagnostics pointer.
@@ -59,21 +39,21 @@ std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
llvm::Expected<ASTNodeUP>
DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member) {
Status error;
- DILParser parser(dil_input_expr, lexer, exe_ctx_scope, use_dynamic,
- use_synthetic, fragile_ivar, check_ptr_vs_member, error);
+ DILParser parser(dil_input_expr, lexer, frame_sp, use_dynamic, use_synthetic,
+ fragile_ivar, check_ptr_vs_member, error);
return parser.Run();
}
DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
- std::shared_ptr<ExecutionContextScope> exe_ctx_scope,
+ std::shared_ptr<StackFrame> frame_sp,
lldb::DynamicValueType use_dynamic, bool use_synthetic,
bool fragile_ivar, bool check_ptr_vs_member, Status &error)
- : m_ctx_scope(exe_ctx_scope), m_input_expr(dil_input_expr),
- m_dil_lexer(lexer), m_error(error), m_use_dynamic(use_dynamic),
+ : m_ctx_scope(frame_sp), m_input_expr(dil_input_expr), m_dil_lexer(lexer),
+ m_error(error), m_use_dynamic(use_dynamic),
m_use_synthetic(use_synthetic), m_fragile_ivar(fragile_ivar),
m_check_ptr_vs_member(check_ptr_vs_member) {}
@@ -120,7 +100,7 @@ ASTNodeUP DILParser::ParsePrimaryExpression() {
}
BailOut(ErrorCode::kInvalidExpressionSyntax,
- llvm::formatv("Unexpected token: {0}", TokenDescription(CurToken())),
+ llvm::formatv("Unexpected token: {0}", CurToken()),
CurToken().GetLocation());
return std::make_unique<ErrorNode>();
}
@@ -275,16 +255,9 @@ void DILParser::BailOut(Status error) {
void DILParser::Expect(Token::Kind kind) {
if (CurToken().IsNot(kind)) {
BailOut(ErrorCode::kUnknown,
- llvm::formatv("expected {0}, got: {1}", TokenKindsJoin(kind),
- TokenDescription(CurToken())),
+ llvm::formatv("expected {0}, got: {1}", kind, CurToken()),
CurToken().GetLocation());
}
}
-std::string DILParser::TokenDescription(const Token &token) {
- const auto &spelling = token.GetSpelling();
- llvm::StringRef kind_name = Token::GetTokenName(token.GetKind());
- return llvm::formatv("<'{0}' ({1})>", spelling, kind_name);
-}
-
} // namespace lldb_private::dil
>From 5d592e3d3912fd3e0b811940ba311642de54fe42 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Tue, 11 Mar 2025 21:49:43 -0700
Subject: [PATCH 14/21] Address some review comments:
- Remove FromContextArg and GetType methods from IdentifierInfo.
- Update Token::IsOneOf to take an ArrayRef rather than variadic list.
- Various minor code cleanups.
- Reviewer sugggested code cleanups in DILFindVariable and
LookupGlobalIdentifier.
- Use lldbutil.run_to_source_breakpont to greatly simplify the DIL python API
tests.
- Update the DIL lexer unit tests to use the new IsOneOf token parameter
format.
---
lldb/include/lldb/ValueObject/DILEval.h | 7 ---
lldb/include/lldb/ValueObject/DILLexer.h | 18 +------
lldb/include/lldb/ValueObject/DILParser.h | 2 +-
lldb/source/Target/StackFrame.cpp | 7 +--
lldb/source/ValueObject/DILAST.cpp | 1 -
lldb/source/ValueObject/DILEval.cpp | 52 +++++++------------
lldb/source/ValueObject/DILParser.cpp | 10 ++--
.../TestFrameVarDILGlobalVariableLookup.py | 38 +-------------
.../TestFrameVarDILInstanceVariables.py | 38 +-------------
.../LocalVars/TestFrameVarDILLocalVars.py | 38 +-------------
lldb/unittests/ValueObject/DILLexerTests.cpp | 10 ++--
11 files changed, 40 insertions(+), 181 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index f80ac9ddf6238..0b94ea5b3bb46 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -31,16 +31,9 @@ class IdentifierInfo {
new IdentifierInfo(Kind::eValue, type, valobj.GetSP(), {}));
}
- static std::unique_ptr<IdentifierInfo> FromContextArg(CompilerType type) {
- lldb::ValueObjectSP empty_value;
- return std::unique_ptr<IdentifierInfo>(
- new IdentifierInfo(Kind::eContextArg, type, empty_value, {}));
- }
-
Kind GetKind() const { return m_kind; }
lldb::ValueObjectSP GetValue() const { return m_value; }
- CompilerType GetType() { return m_type; }
bool IsValid() const { return m_type.IsValid(); }
IdentifierInfo(Kind kind, CompilerType type, lldb::ValueObjectSP value,
diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h
index d65306d35f511..d15fc382d1623 100644
--- a/lldb/include/lldb/ValueObject/DILLexer.h
+++ b/lldb/include/lldb/ValueObject/DILLexer.h
@@ -42,22 +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); }
-
- bool IsOneOf(std::vector<Kind> kinds) const {
- if (kinds.empty())
- return false;
-
- if (kinds.size() == 1)
- return Is(kinds[0]);
-
- Kind k = kinds.back();
- kinds.pop_back();
- return Is(k) || IsOneOf(kinds);
- }
-
- 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; }
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 939fc50cd4ee3..4532d874aed98 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -42,7 +42,7 @@ class DILParser {
bool use_synthetic, bool fragile_ivar,
bool check_ptr_vs_member);
- ~DILParser() { m_ctx_scope.reset(); }
+ ~DILParser() = default;
bool UseSynthetic() { return m_use_synthetic; }
diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index a0acc3e8229de..398823f33694d 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -514,9 +514,7 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
VariableSP &var_sp, Status &error) {
ExecutionContext exe_ctx;
CalculateExecutionContext(exe_ctx);
-
bool use_DIL = exe_ctx.GetTargetRef().GetUseDIL(&exe_ctx);
-
if (use_DIL)
return DILGetValueForVariableExpressionPath(var_expr, use_dynamic, options,
var_sp, error);
@@ -542,12 +540,11 @@ ValueObjectSP StackFrame::DILGetValueForVariableExpressionPath(
error = Status::FromError(lex_or_err.takeError());
return ValueObjectSP();
}
- dil::DILLexer &lexer = *lex_or_err;
// Parse the expression.
auto tree_or_error = dil::DILParser::Parse(
- var_expr, lexer, shared_from_this(), use_dynamic, !no_synth_child,
- !no_fragile_ivar, check_ptr_vs_member);
+ var_expr, std::move(*lex_or_err), shared_from_this(), use_dynamic,
+ !no_synth_child, !no_fragile_ivar, check_ptr_vs_member);
if (!tree_or_error) {
error = Status::FromError(tree_or_error.takeError());
return ValueObjectSP();
diff --git a/lldb/source/ValueObject/DILAST.cpp b/lldb/source/ValueObject/DILAST.cpp
index 0181a6aefcb16..e75958d784627 100644
--- a/lldb/source/ValueObject/DILAST.cpp
+++ b/lldb/source/ValueObject/DILAST.cpp
@@ -13,7 +13,6 @@ namespace lldb_private::dil {
llvm::Expected<lldb::ValueObjectSP> ErrorNode::Accept(Visitor *v) const {
llvm_unreachable("Attempting to Visit a DIL ErrorNode.");
- return lldb::ValueObjectSP();
}
llvm::Expected<lldb::ValueObjectSP> IdentifierNode::Accept(Visitor *v) const {
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 9f50d1329325f..f44f894e636cb 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -52,19 +52,15 @@ static lldb::VariableSP DILFindVariable(ConstString name,
lldb::VariableSP exact_match;
std::vector<lldb::VariableSP> possible_matches;
- typedef std::vector<lldb::VariableSP> collection;
- typedef collection::iterator iterator;
-
- iterator pos, end = variable_list->end();
- for (pos = variable_list->begin(); pos != end; ++pos) {
- llvm::StringRef str_ref_name = pos->get()->GetName().GetStringRef();
+ for (lldb::VariableSP var_sp : *variable_list) {
+ llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
// Check for global vars, which might start with '::'.
str_ref_name.consume_front("::");
if (str_ref_name == name.GetStringRef())
- possible_matches.push_back(*pos);
- else if (pos->get()->NameMatches(name))
- possible_matches.push_back(*pos);
+ possible_matches.push_back(var_sp);
+ else if (var_sp->NameMatches(name))
+ possible_matches.push_back(var_sp);
}
// Look for exact matches (favors local vars over global vars)
@@ -74,23 +70,21 @@ static lldb::VariableSP DILFindVariable(ConstString name,
});
if (exact_match_it != possible_matches.end())
- exact_match = *exact_match_it;
-
- if (!exact_match)
- // Look for a global var exact match.
- for (auto var_sp : possible_matches) {
- llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
- str_ref_name.consume_front("::");
- if (str_ref_name == name.GetStringRef()) {
- exact_match = var_sp;
- break;
- }
- }
+ return *exact_match_it;
- if (!exact_match && possible_matches.size() == 1)
- exact_match = possible_matches[0];
+ // Look for a global var exact match.
+ for (auto var_sp : possible_matches) {
+ llvm::StringRef str_ref_name = var_sp->GetName().GetStringRef();
+ str_ref_name.consume_front("::");
+ if (str_ref_name == name.GetStringRef())
+ return var_sp;
+ }
+
+ // If there's a single non-exact match, take it.
+ if (possible_matches.size() == 1)
+ return possible_matches[0];
- return exact_match;
+ return nullptr;
}
std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
@@ -239,9 +233,8 @@ Interpreter::Visit(const IdentifierNode *node) {
identifier = LookupGlobalIdentifier(node->GetName(), m_exe_ctx_scope,
m_target, use_dynamic);
if (!identifier) {
- std::string errMsg;
- std::string name = node->GetName();
- errMsg = llvm::formatv("use of undeclared identifier '{0}'", name);
+ std::string errMsg =
+ llvm::formatv("use of undeclared identifier '{0}'", node->GetName());
Status error = Status(
(uint32_t)ErrorCode::kUndeclaredIdentifier, lldb::eErrorTypeGeneric,
FormatDiagnostics(m_expr, errMsg, node->GetLocation()));
@@ -249,7 +242,6 @@ Interpreter::Visit(const IdentifierNode *node) {
}
lldb::ValueObjectSP val;
lldb::TargetSP target_sp;
- Status error;
assert(identifier->GetKind() == IdentifierInfo::Kind::eValue &&
"Unrecognized identifier kind");
@@ -263,10 +255,6 @@ Interpreter::Visit(const IdentifierNode *node) {
return error.ToError();
}
- target_sp = val->GetTargetSP();
- assert(target_sp && target_sp->IsValid() &&
- "identifier doesn't resolve to a valid value");
-
return val;
}
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index ee32a8b878cd3..63ee60af94967 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -20,7 +20,7 @@
#include <limits.h>
#include <memory>
#include <sstream>
-#include <stdlib.h>
+#include <cstdlib>
#include <string>
namespace lldb_private::dil {
@@ -29,7 +29,7 @@ std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
uint32_t loc) {
// Get the position, in the current line of text, of the diagnostics pointer.
// ('loc' is the location of the start of the current token/error within the
- // overal text line).
+ // overall text line).
int32_t arrow = loc + 1; // Column offset starts at 1, not 0.
return llvm::formatv("<expr:1:{0}>: {1}\n{2}\n{3}", loc, message,
@@ -58,9 +58,7 @@ DILParser::DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
m_check_ptr_vs_member(check_ptr_vs_member) {}
llvm::Expected<ASTNodeUP> DILParser::Run() {
- ASTNodeUP expr;
-
- expr = ParseExpression();
+ ASTNodeUP expr = ParseExpression();
Expect(Token::Kind::eof);
@@ -84,7 +82,7 @@ ASTNodeUP DILParser::ParseExpression() { return ParsePrimaryExpression(); }
// "(" expression ")"
//
ASTNodeUP DILParser::ParsePrimaryExpression() {
- if (CurToken().IsOneOf(Token::coloncolon, Token::identifier)) {
+ if (CurToken().IsOneOf({Token::coloncolon, Token::identifier})) {
// Save the source location for the diagnostics message.
uint32_t loc = CurToken().GetLocation();
auto identifier = ParseIdExpression();
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index 74245511c4520..45502819fb128 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -20,42 +20,8 @@ class TestFrameVarDILGlobalVariableLookup(TestBase):
def test_frame_var(self):
self.build()
- self.do_test()
-
- def do_test(self):
- target = self.createTestTarget()
-
- # Now create a breakpoint in main.c at the source matching
- # "Set a breakpoint here"
- breakpoint = target.BreakpointCreateBySourceRegex(
- "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
- )
- self.assertTrue(
- breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
- )
-
- error = lldb.SBError()
- # This is the launch info. If you want to launch with arguments or
- # environment variables, add them using SetArguments or
- # SetEnvironmentEntries
-
- launch_info = target.GetLaunchInfo()
- process = target.Launch(launch_info, error)
- self.assertTrue(process, PROCESS_IS_VALID)
-
- # Did we hit our breakpoint?
- from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
-
- threads = get_threads_stopped_at_breakpoint(process, breakpoint)
- self.assertEqual(
- len(threads), 1, "There should be a thread stopped at our breakpoint"
- )
- # The hit count for the breakpoint should be 1.
- self.assertEqual(breakpoint.GetHitCount(), 1)
-
- frame = threads[0].GetFrameAtIndex(0)
- command_result = lldb.SBCommandReturnObject()
- interp = self.dbg.GetCommandInterpreter()
+ lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+ lldb.SBFileSpec("main.cpp"))
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable 'globalVar'", substrs=["-559038737"]) # 0xDEADBEEF
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index d84f4f5ef7bb7..5e723575c056c 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -20,42 +20,8 @@ class TestFrameVarDILInstanceVariables(TestBase):
def test_frame_var(self):
self.build()
- self.do_test()
-
- def do_test(self):
- target = self.createTestTarget()
-
- # Now create a breakpoint in main.c at the source matching
- # "Set a breakpoint here"
- breakpoint = target.BreakpointCreateBySourceRegex(
- "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
- )
- self.assertTrue(
- breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
- )
-
- error = lldb.SBError()
- # This is the launch info. If you want to launch with arguments or
- # environment variables, add them using SetArguments or
- # SetEnvironmentEntries
-
- launch_info = target.GetLaunchInfo()
- process = target.Launch(launch_info, error)
- self.assertTrue(process, PROCESS_IS_VALID)
-
- # Did we hit our breakpoint?
- from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
-
- threads = get_threads_stopped_at_breakpoint(process, breakpoint)
- self.assertEqual(
- len(threads), 1, "There should be a thread stopped at our breakpoint"
- )
- # The hit count for the breakpoint should be 1.
- self.assertEqual(breakpoint.GetHitCount(), 1)
-
- frame = threads[0].GetFrameAtIndex(0)
- command_result = lldb.SBCommandReturnObject()
- interp = self.dbg.GetCommandInterpreter()
+ lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+ lldb.SBFileSpec("main.cpp"))
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable 'this'", patterns=["0x[0-9]+"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index 42b2060a7694c..e8654c5a4fec0 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -20,42 +20,8 @@ class TestFrameVarDILLocalVars(TestBase):
def test_frame_var(self):
self.build()
- self.do_test()
-
- def do_test(self):
- target = self.createTestTarget()
-
- # Now create a breakpoint in main.c at the source matching
- # "Set a breakpoint here"
- breakpoint = target.BreakpointCreateBySourceRegex(
- "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
- )
- self.assertTrue(
- breakpoint and breakpoint.GetNumLocations() >= 1, VALID_BREAKPOINT
- )
-
- error = lldb.SBError()
- # This is the launch info. If you want to launch with arguments or
- # environment variables, add them using SetArguments or
- # SetEnvironmentEntries
-
- launch_info = target.GetLaunchInfo()
- process = target.Launch(launch_info, error)
- self.assertTrue(process, PROCESS_IS_VALID)
-
- # Did we hit our breakpoint?
- from lldbsuite.test.lldbutil import get_threads_stopped_at_breakpoint
-
- threads = get_threads_stopped_at_breakpoint(process, breakpoint)
- self.assertEqual(
- len(threads), 1, "There should be a thread stopped at our breakpoint"
- )
- # The hit count for the breakpoint should be 1.
- self.assertEqual(breakpoint.GetHitCount(), 1)
-
- frame = threads[0].GetFrameAtIndex(0)
- command_result = lldb.SBCommandReturnObject()
- interp = self.dbg.GetCommandInterpreter()
+ lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
+ lldb.SBFileSpec("main.cpp"))
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect("frame variable a", substrs=["1"])
diff --git a/lldb/unittests/ValueObject/DILLexerTests.cpp b/lldb/unittests/ValueObject/DILLexerTests.cpp
index 9e5b8efd7af80..9afa957901ae7 100644
--- a/lldb/unittests/ValueObject/DILLexerTests.cpp
+++ b/lldb/unittests/ValueObject/DILLexerTests.cpp
@@ -54,9 +54,9 @@ TEST(DILLexerTests, TokenKindTest) {
EXPECT_TRUE(token.Is(Token::identifier));
EXPECT_FALSE(token.Is(Token::l_paren));
- EXPECT_TRUE(token.IsOneOf(Token::eof, Token::identifier));
- EXPECT_FALSE(token.IsOneOf(Token::l_paren, Token::r_paren, Token::coloncolon,
- Token::eof));
+ EXPECT_TRUE(token.IsOneOf({Token::eof, Token::identifier}));
+ EXPECT_FALSE(token.IsOneOf(
+ {Token::l_paren, Token::r_paren, Token::coloncolon, Token::eof}));
}
TEST(DILLexerTests, LookAheadTest) {
@@ -150,7 +150,7 @@ 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}));
}
}
>From 33a9971005cd5ab227f0de96cfd37c11c5ad356c Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 13 Mar 2025 10:16:42 -0700
Subject: [PATCH 15/21] Update python tests to use expect_var_path where
possible.
---
.../TestFrameVarDILGlobalVariableLookup.py | 22 ++++++++++---------
.../TestFrameVarDILInstanceVariables.py | 2 +-
.../LocalVars/TestFrameVarDILLocalVars.py | 8 +++----
3 files changed, 17 insertions(+), 15 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index 45502819fb128..d4a92e1f7fec7 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -24,11 +24,12 @@ def test_frame_var(self):
lldb.SBFileSpec("main.cpp"))
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
- self.expect("frame variable 'globalVar'", substrs=["-559038737"]) # 0xDEADBEEF
- self.expect("frame variable 'globalPtr'", patterns=["0x[0-9]+"])
- self.expect("frame variable 'globalRef'", substrs=["-559038737"])
- self.expect("frame variable '::globalPtr'", patterns=["0x[0-9]+"])
- self.expect("frame variable '::globalRef'", substrs=["-559038737"])
+ self.expect_var_path("globalVar", type="int", value="-559038737") # 0xDEADBEEF
+ self.expect_var_path("globalPtr", type="int *")
+ self.expect_var_path("globalRef", value="-559038737")
+ self.expect_var_path("::globalVar", value="-559038737")
+ self.expect_var_path("::globalPtr", type="int *")
+ self.expect_var_path("::globalRef", value="-559038737")
self.expect(
"frame variable 'externGlobalVar'",
@@ -40,8 +41,9 @@ def test_frame_var(self):
error=True,
substrs=["use of undeclared identifier"],
) # ["12648430"])
- self.expect("frame variable 'ns::globalVar'", substrs=["13"])
- self.expect("frame variable 'ns::globalPtr'", patterns=["0x[0-9]+"])
- self.expect("frame variable 'ns::globalRef'", substrs=["13"])
- self.expect("frame variable '::ns::globalVar'", substrs=["13"])
- self.expect("frame variable '::ns::globalPtr'", patterns=["0x[0-9]+"])
+
+ self.expect_var_path("ns::globalVar", value="13")
+ self.expect_var_path("ns::globalPtr", type="int *")
+ self.expect_var_path("ns::globalRef", value="13")
+ self.expect_var_path("::ns::globalVar", value="13")
+ self.expect_var_path("::ns::globalPtr", type="int *")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index 5e723575c056c..e1440fe86a7c3 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -24,5 +24,5 @@ def test_frame_var(self):
lldb.SBFileSpec("main.cpp"))
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
- self.expect("frame variable 'this'", patterns=["0x[0-9]+"])
+ self.expect_var_path("this", type="TestMethods *")
self.expect("frame variable 'c'", substrs=["(field_ = -1)"])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index e8654c5a4fec0..b89692872c33b 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -24,7 +24,7 @@ def test_frame_var(self):
lldb.SBFileSpec("main.cpp"))
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
- self.expect("frame variable a", substrs=["1"])
- self.expect("frame variable b", substrs=["2"])
- self.expect("frame variable c", substrs=["\\xfd"])
- self.expect("frame variable s", substrs=["4"])
+ self.expect_var_path("a", value="1")
+ self.expect_var_path("b", value="2")
+ self.expect_var_path("c", value="'\\xfd'")
+ self.expect_var_path("s", value="4")
>From 07d0d15cac1737abd120d21bca3214373100562c Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 13 Mar 2025 14:55:17 -0700
Subject: [PATCH 16/21] Update error message handling to use DiagnosticDetails.
---
lldb/include/lldb/ValueObject/DILParser.h | 8 ++++--
lldb/source/ValueObject/DILEval.cpp | 3 +-
lldb/source/ValueObject/DILParser.cpp | 34 +++++++++++++++++++----
3 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 4532d874aed98..f2084a6ccc4f3 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -28,8 +28,12 @@ enum class ErrorCode : unsigned char {
kUnknown,
};
-std::string FormatDiagnostics(llvm::StringRef input_expr,
- const std::string &message, uint32_t loc);
+std::string NewFormatDiagnostics(llvm::StringRef input_expr,
+ const std::string &message, uint32_t loc,
+ uint16_t err_len);
+
+std::string OldFormatDiagnostics(llvm::StringRef input_expr,
+ const std::string &message, uint32_t loc);
/// Pure recursive descent parser for C++ like expressions.
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index f44f894e636cb..02d8c52ae076a 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -237,7 +237,8 @@ Interpreter::Visit(const IdentifierNode *node) {
llvm::formatv("use of undeclared identifier '{0}'", node->GetName());
Status error = Status(
(uint32_t)ErrorCode::kUndeclaredIdentifier, lldb::eErrorTypeGeneric,
- FormatDiagnostics(m_expr, errMsg, node->GetLocation()));
+ NewFormatDiagnostics(m_expr, errMsg, node->GetLocation(),
+ node->GetName().size()));
return error.ToError();
}
lldb::ValueObjectSP val;
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 63ee60af94967..c455cf1303f16 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -13,26 +13,49 @@
#include "lldb/ValueObject/DILParser.h"
#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Utility/DiagnosticsRendering.h"
#include "lldb/ValueObject/DILAST.h"
#include "lldb/ValueObject/DILEval.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatAdapters.h"
+#include <cstdlib>
#include <limits.h>
#include <memory>
#include <sstream>
-#include <cstdlib>
#include <string>
namespace lldb_private::dil {
-std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
- uint32_t loc) {
+std::string NewFormatDiagnostics(llvm::StringRef text,
+ const std::string &message, uint32_t loc,
+ uint16_t err_len) {
+ DiagnosticDetail::SourceLocation sloc = {
+ FileSpec{}, /*line=*/1, loc + 1, err_len, false, /*in_user_input=*/true};
+ std::string arrow_str = "^";
+ std::string rendered_msg =
+ llvm::formatv("<user expression 0>:1:{0}: {1}\n 1 | {2}\n | ^",
+ loc + 1, message, text);
+ DiagnosticDetail detail;
+ detail.source_location = sloc;
+ detail.severity = lldb::eSeverityError;
+ detail.message = message;
+ detail.rendered = rendered_msg;
+ std::vector<DiagnosticDetail> diagnostics;
+ diagnostics.push_back(detail);
+ StreamString diag_stream(true);
+ RenderDiagnosticDetails(diag_stream, 7, true, diagnostics);
+ std::string ret_str = text.str() + "\n" + diag_stream.GetString().str();
+ return ret_str;
+}
+
+std::string OldFormatDiagnostics(llvm::StringRef text,
+ const std::string &message, uint32_t loc) {
// Get the position, in the current line of text, of the diagnostics pointer.
// ('loc' is the location of the start of the current token/error within the
// overall text line).
int32_t arrow = loc + 1; // Column offset starts at 1, not 0.
- return llvm::formatv("<expr:1:{0}>: {1}\n{2}\n{3}", loc, message,
+ return llvm::formatv("<expr:1:{0}>: {1}\n{2}\n{3}", loc + 1, message,
llvm::fmt_pad(text, 0, 0),
llvm::fmt_pad("^", arrow - 1, 0));
}
@@ -234,7 +257,8 @@ void DILParser::BailOut(ErrorCode code, const std::string &error,
}
m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
- FormatDiagnostics(m_input_expr, error, loc));
+ NewFormatDiagnostics(m_input_expr, error, loc,
+ CurToken().GetSpelling().length()));
// Advance the lexer token index to the end of the lexed tokens vector.
m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
>From 42c52f3146faf2f6a00cc7a6679da5bc7c36a4e9 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Sun, 16 Mar 2025 10:40:17 -0700
Subject: [PATCH 17/21] Address/fix remaining reviewer comments:
- Rename 'NewFormatDiagnostics' to 'FormatDiagnostics', keeping the code that
uses DiagnosticDetail; remove 'OldFormatDiagnostics'.
- Remove the code that automatically dereferences reference variables before
returning them.
- Remove IdentifierInfo class, replacing it directly with lldb::ValueObjectSP
values.
---
lldb/include/lldb/ValueObject/DILEval.h | 52 ++++---------------
lldb/include/lldb/ValueObject/DILParser.h | 9 ++--
lldb/source/ValueObject/DILEval.cpp | 48 ++++++-----------
lldb/source/ValueObject/DILParser.cpp | 21 ++------
.../TestFrameVarDILGlobalVariableLookup.py | 7 +--
5 files changed, 37 insertions(+), 100 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h
index 0b94ea5b3bb46..ef612a4867134 100644
--- a/lldb/include/lldb/ValueObject/DILEval.h
+++ b/lldb/include/lldb/ValueObject/DILEval.h
@@ -16,54 +16,25 @@
namespace lldb_private::dil {
-/// Class used to store & manipulate information about identifiers.
-class IdentifierInfo {
-public:
- enum class Kind {
- eValue,
- eContextArg,
- };
-
- static std::unique_ptr<IdentifierInfo> FromValue(ValueObject &valobj) {
- CompilerType type;
- type = valobj.GetCompilerType();
- return std::unique_ptr<IdentifierInfo>(
- new IdentifierInfo(Kind::eValue, type, valobj.GetSP(), {}));
- }
-
- Kind GetKind() const { return m_kind; }
- lldb::ValueObjectSP GetValue() const { return m_value; }
-
- bool IsValid() const { return m_type.IsValid(); }
-
- IdentifierInfo(Kind kind, CompilerType type, lldb::ValueObjectSP value,
- std::vector<uint32_t> path)
- : m_kind(kind), m_type(type), m_value(std::move(value)) {}
-
-private:
- Kind m_kind;
- CompilerType m_type;
- lldb::ValueObjectSP m_value;
-};
-
/// 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).
-std::unique_ptr<IdentifierInfo>
-LookupIdentifier(llvm::StringRef name_ref, std::shared_ptr<StackFrame> frame_sp,
- lldb::DynamicValueType use_dynamic,
- CompilerType *scope_ptr = nullptr);
+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.
-std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
- llvm::StringRef name_ref, std::shared_ptr<StackFrame> frame_sp,
- lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
- CompilerType *scope_ptr = nullptr);
+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:
@@ -77,16 +48,11 @@ class Interpreter : Visitor {
llvm::Expected<lldb::ValueObjectSP>
Visit(const IdentifierNode *node) override;
-private:
// 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;
};
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index f2084a6ccc4f3..6ee6b668b6a5d 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -28,12 +28,9 @@ enum class ErrorCode : unsigned char {
kUnknown,
};
-std::string NewFormatDiagnostics(llvm::StringRef input_expr,
- const std::string &message, uint32_t loc,
- uint16_t err_len);
-
-std::string OldFormatDiagnostics(llvm::StringRef input_expr,
- const std::string &message, uint32_t loc);
+std::string FormatDiagnostics(llvm::StringRef input_expr,
+ const std::string &message, uint32_t loc,
+ uint16_t err_len);
/// Pure recursive descent parser for C++ like expressions.
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index 02d8c52ae076a..bd87190aee7ca 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -87,7 +87,7 @@ static lldb::VariableSP DILFindVariable(ConstString name,
return nullptr;
}
-std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
+lldb::ValueObjectSP LookupGlobalIdentifier(
llvm::StringRef name_ref, std::shared_ptr<StackFrame> stack_frame,
lldb::TargetSP target_sp, lldb::DynamicValueType use_dynamic,
CompilerType *scope_ptr) {
@@ -105,7 +105,7 @@ std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
}
if (value_sp)
- return IdentifierInfo::FromValue(*value_sp);
+ return value_sp;
// Also check for static global vars.
if (variable_list) {
@@ -122,7 +122,7 @@ std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
}
if (value_sp)
- return IdentifierInfo::FromValue(*value_sp);
+ return value_sp;
// Check for match in modules global variables.
VariableList modules_var_list;
@@ -142,15 +142,15 @@ std::unique_ptr<IdentifierInfo> LookupGlobalIdentifier(
}
if (value_sp)
- return IdentifierInfo::FromValue(*value_sp);
+ return value_sp;
return nullptr;
}
-std::unique_ptr<IdentifierInfo>
-LookupIdentifier(llvm::StringRef name_ref,
- std::shared_ptr<StackFrame> stack_frame,
- lldb::DynamicValueType use_dynamic, CompilerType *scope_ptr) {
+lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
+ std::shared_ptr<StackFrame> stack_frame,
+ lldb::DynamicValueType use_dynamic,
+ CompilerType *scope_ptr) {
// Support $rax as a special syntax for accessing registers.
// Will return an invalid value in case the requested register doesn't exist.
if (name_ref.consume_front("$")) {
@@ -165,7 +165,7 @@ LookupIdentifier(llvm::StringRef name_ref,
ValueObjectRegister::Create(stack_frame.get(), reg_ctx, reg_info);
if (value_sp)
- return IdentifierInfo::FromValue(*value_sp);
+ return value_sp;
return nullptr;
}
@@ -189,7 +189,7 @@ LookupIdentifier(llvm::StringRef name_ref,
value_sp = stack_frame->FindVariable(ConstString(name_ref));
if (value_sp)
- return IdentifierInfo::FromValue(*value_sp);
+ return value_sp;
// Try looking for an instance variable (class member).
SymbolContext sc = stack_frame->GetSymbolContext(
@@ -200,7 +200,7 @@ LookupIdentifier(llvm::StringRef name_ref,
value_sp = value_sp->GetChildMemberWithName(name_ref);
if (value_sp)
- return IdentifierInfo::FromValue(*(value_sp));
+ return value_sp;
}
}
return nullptr;
@@ -226,7 +226,7 @@ llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const IdentifierNode *node) {
lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
- std::unique_ptr<IdentifierInfo> identifier =
+ lldb::ValueObjectSP identifier =
LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
if (!identifier)
@@ -235,28 +235,14 @@ Interpreter::Visit(const IdentifierNode *node) {
if (!identifier) {
std::string errMsg =
llvm::formatv("use of undeclared identifier '{0}'", node->GetName());
- Status error = Status(
- (uint32_t)ErrorCode::kUndeclaredIdentifier, lldb::eErrorTypeGeneric,
- NewFormatDiagnostics(m_expr, errMsg, node->GetLocation(),
- node->GetName().size()));
+ Status error = Status((uint32_t)ErrorCode::kUndeclaredIdentifier,
+ lldb::eErrorTypeGeneric,
+ FormatDiagnostics(m_expr, errMsg, node->GetLocation(),
+ node->GetName().size()));
return error.ToError();
}
- lldb::ValueObjectSP val;
- lldb::TargetSP target_sp;
- assert(identifier->GetKind() == IdentifierInfo::Kind::eValue &&
- "Unrecognized identifier kind");
-
- val = identifier->GetValue();
-
- if (val->GetCompilerType().IsReferenceType()) {
- Status error;
- val = val->Dereference(error);
- if (error.Fail())
- return error.ToError();
- }
-
- return val;
+ return identifier;
}
} // namespace lldb_private::dil
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index c455cf1303f16..bb94732bb23ea 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -26,9 +26,8 @@
namespace lldb_private::dil {
-std::string NewFormatDiagnostics(llvm::StringRef text,
- const std::string &message, uint32_t loc,
- uint16_t err_len) {
+std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
+ uint32_t loc, uint16_t err_len) {
DiagnosticDetail::SourceLocation sloc = {
FileSpec{}, /*line=*/1, loc + 1, err_len, false, /*in_user_input=*/true};
std::string arrow_str = "^";
@@ -48,18 +47,6 @@ std::string NewFormatDiagnostics(llvm::StringRef text,
return ret_str;
}
-std::string OldFormatDiagnostics(llvm::StringRef text,
- const std::string &message, uint32_t loc) {
- // Get the position, in the current line of text, of the diagnostics pointer.
- // ('loc' is the location of the start of the current token/error within the
- // overall text line).
- int32_t arrow = loc + 1; // Column offset starts at 1, not 0.
-
- return llvm::formatv("<expr:1:{0}>: {1}\n{2}\n{3}", loc + 1, message,
- llvm::fmt_pad(text, 0, 0),
- llvm::fmt_pad("^", arrow - 1, 0));
-}
-
llvm::Expected<ASTNodeUP>
DILParser::Parse(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
@@ -257,8 +244,8 @@ void DILParser::BailOut(ErrorCode code, const std::string &error,
}
m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
- NewFormatDiagnostics(m_input_expr, error, loc,
- CurToken().GetSpelling().length()));
+ FormatDiagnostics(m_input_expr, error, loc,
+ CurToken().GetSpelling().length()));
// Advance the lexer token index to the end of the lexed tokens vector.
m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index d4a92e1f7fec7..d4778f6ebfc0f 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -26,10 +26,10 @@ def test_frame_var(self):
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect_var_path("globalVar", type="int", value="-559038737") # 0xDEADBEEF
self.expect_var_path("globalPtr", type="int *")
- self.expect_var_path("globalRef", value="-559038737")
+ self.expect_var_path("globalRef", type="int &")
self.expect_var_path("::globalVar", value="-559038737")
self.expect_var_path("::globalPtr", type="int *")
- self.expect_var_path("::globalRef", value="-559038737")
+ self.expect_var_path("::globalRef", type="int &")
self.expect(
"frame variable 'externGlobalVar'",
@@ -44,6 +44,7 @@ def test_frame_var(self):
self.expect_var_path("ns::globalVar", value="13")
self.expect_var_path("ns::globalPtr", type="int *")
- self.expect_var_path("ns::globalRef", value="13")
+ self.expect_var_path("ns::globalRef", type="int &")
self.expect_var_path("::ns::globalVar", value="13")
self.expect_var_path("::ns::globalPtr", type="int *")
+ self.expect_var_path("::ns::globalRef", type="int &")
>From 08a918d3db9e14ba53f926203b22cb2b46375469 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Sun, 16 Mar 2025 10:54:51 -0700
Subject: [PATCH 18/21] Fix format errors in Python tests.
---
.../TestFrameVarDILGlobalVariableLookup.py | 5 +++--
.../InstanceVariables/TestFrameVarDILInstanceVariables.py | 5 +++--
.../var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py | 5 +++--
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index d4778f6ebfc0f..3b732e2384723 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -20,8 +20,9 @@ class TestFrameVarDILGlobalVariableLookup(TestBase):
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
- lldb.SBFileSpec("main.cpp"))
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect_var_path("globalVar", type="int", value="-559038737") # 0xDEADBEEF
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index e1440fe86a7c3..da03a0ed956b3 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -20,8 +20,9 @@ class TestFrameVarDILInstanceVariables(TestBase):
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
- lldb.SBFileSpec("main.cpp"))
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect_var_path("this", type="TestMethods *")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index b89692872c33b..dac4147fa71c7 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -20,8 +20,9 @@ class TestFrameVarDILLocalVars(TestBase):
def test_frame_var(self):
self.build()
- lldbutil.run_to_source_breakpoint(self, "Set a breakpoint here",
- lldb.SBFileSpec("main.cpp"))
+ lldbutil.run_to_source_breakpoint(
+ self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
+ )
self.expect("settings set target.experimental.use-DIL true", substrs=[""])
self.expect_var_path("a", value="1")
>From 27723dbb08ce82388bbff1d44e36b778d0b2efc2 Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Wed, 19 Mar 2025 13:44:36 -0700
Subject: [PATCH 19/21] Add static cast to fix buildkite error.
---
lldb/source/ValueObject/DILParser.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index bb94732bb23ea..8f066909f19cb 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -29,7 +29,8 @@ namespace lldb_private::dil {
std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
uint32_t loc, uint16_t err_len) {
DiagnosticDetail::SourceLocation sloc = {
- FileSpec{}, /*line=*/1, loc + 1, err_len, false, /*in_user_input=*/true};
+ FileSpec{}, /*line=*/1, static_cast<uint16_t>(loc + 1),
+ err_len, false, /*in_user_input=*/true};
std::string arrow_str = "^";
std::string rendered_msg =
llvm::formatv("<user expression 0>:1:{0}: {1}\n 1 | {2}\n | ^",
>From 07b6a4f03b115c2129b54eef96efb2444e8859bc Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Thu, 20 Mar 2025 11:41:42 -0700
Subject: [PATCH 20/21] Minor style & format cleanups; also update error
handling.
Created a new DILDiagnosticError class, patterend after
OptionParserError to use DiagnosticError and DiagnosticDetail for
creating DIL errors. Replaced calls for FormatDiagnostics with
calls to create DILDiagnosticError. However, at this time, must
then convert these structured errors in LLDB::Status errors,
because the code in CommandObjectFrame that calls into
GetValueForVariableExpressionPath does NOT allow for passing
structured diagnostics in/out, nor have any code for handling them.
---
lldb/include/lldb/ValueObject/DILAST.h | 5 +--
lldb/include/lldb/ValueObject/DILParser.h | 37 ++++++++++++++++---
lldb/source/ValueObject/DILEval.cpp | 23 ++++--------
lldb/source/ValueObject/DILParser.cpp | 29 ++++++++-------
.../TestFrameVarDILGlobalVariableLookup.py | 2 +-
.../TestFrameVarDILInstanceVariables.py | 5 ++-
.../LocalVars/TestFrameVarDILLocalVars.py | 2 +-
7 files changed, 61 insertions(+), 42 deletions(-)
diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h
index e7c37f25aed85..e5da44bc27df6 100644
--- a/lldb/include/lldb/ValueObject/DILAST.h
+++ b/lldb/include/lldb/ValueObject/DILAST.h
@@ -67,12 +67,10 @@ class IdentifierNode : public ASTNode {
IdentifierNode(uint32_t location, std::string name,
lldb::DynamicValueType use_dynamic,
std::shared_ptr<ExecutionContextScope> exe_ctx_scope)
- : ASTNode(location, NodeKind::eIdentifierNode), m_name(std::move(name)),
- m_use_dynamic(use_dynamic) {}
+ : ASTNode(location, NodeKind::eIdentifierNode), m_name(std::move(name)) {}
llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override;
- lldb::DynamicValueType GetUseDynamic() const { return m_use_dynamic; }
std::string GetName() const { return m_name; }
static bool classof(const ASTNode *node) {
@@ -81,7 +79,6 @@ class IdentifierNode : public ASTNode {
private:
std::string m_name;
- lldb::DynamicValueType m_use_dynamic;
};
/// This class contains one Visit method for each specialized type of
diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h
index 6ee6b668b6a5d..03f5b70f69617 100644
--- a/lldb/include/lldb/ValueObject/DILParser.h
+++ b/lldb/include/lldb/ValueObject/DILParser.h
@@ -10,12 +10,15 @@
#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>
@@ -28,9 +31,35 @@ enum class ErrorCode : unsigned char {
kUnknown,
};
-std::string FormatDiagnostics(llvm::StringRef input_expr,
- const std::string &message, uint32_t loc,
- uint16_t err_len);
+// The following is modeled on class OptionParseError.
+class DILDiagnosticError
+ : public llvm::ErrorInfo<DILDiagnosticError, DiagnosticError> {
+ std::vector<DiagnosticDetail> m_details;
+
+public:
+ using llvm::ErrorInfo<DILDiagnosticError, DiagnosticError>::ErrorInfo;
+ DILDiagnosticError(DiagnosticDetail detail)
+ : ErrorInfo(std::error_code(EINVAL, std::generic_category())),
+ m_details({detail}) {}
+
+ DILDiagnosticError(ErrorCode ec, 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_details[0]);
+ }
+
+ llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {
+ return m_details;
+ }
+};
+
+// This is needed, at the moment, because the code that calls the 'frame
+// var' implementation from CommandObjectFrame ONLY passes Status in/out
+// as a way to determine if an error occurred. We probably want to change that
+// in the future.
+Status GetStatusError(DILDiagnosticError dil_error);
/// Pure recursive descent parser for C++ like expressions.
/// EBNF grammar for the parser is described in lldb/docs/dil-expr-lang.ebnf
@@ -49,8 +78,6 @@ class DILParser {
lldb::DynamicValueType UseDynamic() { return m_use_dynamic; }
- using PtrOperator = std::tuple<Token::Kind, uint32_t>;
-
private:
explicit DILParser(llvm::StringRef dil_input_expr, DILLexer lexer,
std::shared_ptr<StackFrame> frame_sp,
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index bd87190aee7ca..a9d28d284356b 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -154,18 +154,12 @@ lldb::ValueObjectSP LookupIdentifier(llvm::StringRef name_ref,
// Support $rax as a special syntax for accessing registers.
// Will return an invalid value in case the requested register doesn't exist.
if (name_ref.consume_front("$")) {
- lldb::ValueObjectSP value_sp;
-
lldb::RegisterContextSP reg_ctx(stack_frame->GetRegisterContext());
if (!reg_ctx)
return nullptr;
if (const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name_ref))
- value_sp =
- ValueObjectRegister::Create(stack_frame.get(), reg_ctx, reg_info);
-
- if (value_sp)
- return value_sp;
+ return ValueObjectRegister::Create(stack_frame.get(), reg_ctx, reg_info);
return nullptr;
}
@@ -216,15 +210,13 @@ llvm::Expected<lldb::ValueObjectSP>
Interpreter::DILEvalNode(const ASTNode *node) {
// Traverse an AST pointed by the `node`.
- auto value_or_error = node->Accept(this);
-
- // Return the computed value or error.
- return value_or_error;
+ return node->Accept(this);
}
+
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const IdentifierNode *node) {
- lldb::DynamicValueType use_dynamic = node->GetUseDynamic();
+ lldb::DynamicValueType use_dynamic = m_default_dynamic;
lldb::ValueObjectSP identifier =
LookupIdentifier(node->GetName(), m_exe_ctx_scope, use_dynamic);
@@ -235,10 +227,9 @@ Interpreter::Visit(const IdentifierNode *node) {
if (!identifier) {
std::string errMsg =
llvm::formatv("use of undeclared identifier '{0}'", node->GetName());
- Status error = Status((uint32_t)ErrorCode::kUndeclaredIdentifier,
- lldb::eErrorTypeGeneric,
- FormatDiagnostics(m_expr, errMsg, node->GetLocation(),
- node->GetName().size()));
+ Status error = GetStatusError(DILDiagnosticError(
+ ErrorCode::kUndeclaredIdentifier, m_expr, errMsg, node->GetLocation(),
+ node->GetName().size()));
return error.ToError();
}
diff --git a/lldb/source/ValueObject/DILParser.cpp b/lldb/source/ValueObject/DILParser.cpp
index 8f066909f19cb..f8b6fdf289be0 100644
--- a/lldb/source/ValueObject/DILParser.cpp
+++ b/lldb/source/ValueObject/DILParser.cpp
@@ -26,26 +26,23 @@
namespace lldb_private::dil {
-std::string FormatDiagnostics(llvm::StringRef text, const std::string &message,
- uint32_t loc, uint16_t err_len) {
+DILDiagnosticError::DILDiagnosticError(ErrorCode ec, llvm::StringRef expr,
+ const std::string &message, uint32_t loc,
+ uint16_t err_len)
+ : // ErrorInfo(ec) {
+ ErrorInfo(std::error_code(EINVAL, std::generic_category())) {
DiagnosticDetail::SourceLocation sloc = {
FileSpec{}, /*line=*/1, static_cast<uint16_t>(loc + 1),
err_len, false, /*in_user_input=*/true};
- std::string arrow_str = "^";
std::string rendered_msg =
llvm::formatv("<user expression 0>:1:{0}: {1}\n 1 | {2}\n | ^",
- loc + 1, message, text);
+ loc + 1, message, expr);
DiagnosticDetail detail;
detail.source_location = sloc;
detail.severity = lldb::eSeverityError;
detail.message = message;
detail.rendered = rendered_msg;
- std::vector<DiagnosticDetail> diagnostics;
- diagnostics.push_back(detail);
- StreamString diag_stream(true);
- RenderDiagnosticDetails(diag_stream, 7, true, diagnostics);
- std::string ret_str = text.str() + "\n" + diag_stream.GetString().str();
- return ret_str;
+ m_details.push_back(detail);
}
llvm::Expected<ASTNodeUP>
@@ -236,6 +233,13 @@ std::string DILParser::ParseUnqualifiedId() {
return identifier;
}
+// Must somehow convert the DILDiagnosticError to a lldb::Status, since *that*
+// is was must be returned to the place where CommandObjectFrame::DoExecute
+// calls GetVariableValueForExpressionPath (at the moment).
+Status GetStatusError(DILDiagnosticError dil_error) {
+ return Status(dil_error.GetDetails()[0].message);
+}
+
void DILParser::BailOut(ErrorCode code, const std::string &error,
uint32_t loc) {
if (m_error.Fail()) {
@@ -244,9 +248,8 @@ void DILParser::BailOut(ErrorCode code, const std::string &error,
return;
}
- m_error = Status((uint32_t)code, lldb::eErrorTypeGeneric,
- FormatDiagnostics(m_input_expr, error, loc,
- CurToken().GetSpelling().length()));
+ m_error = GetStatusError(DILDiagnosticError(
+ code, m_input_expr, error, loc, CurToken().GetSpelling().length()));
// Advance the lexer token index to the end of the lexed tokens vector.
m_dil_lexer.ResetTokenIdx(m_dil_lexer.NumLexedTokens() - 1);
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
index 3b732e2384723..edb013c7b3526 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/GlobalVariableLookup/TestFrameVarDILGlobalVariableLookup.py
@@ -24,7 +24,7 @@ def test_frame_var(self):
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
- self.expect("settings set target.experimental.use-DIL true", substrs=[""])
+ self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("globalVar", type="int", value="-559038737") # 0xDEADBEEF
self.expect_var_path("globalPtr", type="int *")
self.expect_var_path("globalRef", type="int &")
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index da03a0ed956b3..ba6acd5d6089b 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -24,6 +24,7 @@ def test_frame_var(self):
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
- self.expect("settings set target.experimental.use-DIL true", substrs=[""])
+ self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("this", type="TestMethods *")
- self.expect("frame variable 'c'", substrs=["(field_ = -1)"])
+ self.expect_var_path("c", children=[ValueCheck(name="field_",
+ value="-1")])
diff --git a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
index dac4147fa71c7..0f6618fe47984 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/LocalVars/TestFrameVarDILLocalVars.py
@@ -24,7 +24,7 @@ def test_frame_var(self):
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)
- self.expect("settings set target.experimental.use-DIL true", substrs=[""])
+ self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("a", value="1")
self.expect_var_path("b", value="2")
self.expect_var_path("c", value="'\\xfd'")
>From eb4a3753549ce5e7a95cc1ec7c89e45079b1125e Mon Sep 17 00:00:00 2001
From: Caroline Tice <cmtice at google.com>
Date: Mon, 24 Mar 2025 20:52:41 -0700
Subject: [PATCH 21/21] Fix clang-format issues.
---
lldb/source/ValueObject/DILEval.cpp | 7 +++----
.../InstanceVariables/TestFrameVarDILInstanceVariables.py | 3 +--
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/lldb/source/ValueObject/DILEval.cpp b/lldb/source/ValueObject/DILEval.cpp
index a9d28d284356b..8469ba5c6b96c 100644
--- a/lldb/source/ValueObject/DILEval.cpp
+++ b/lldb/source/ValueObject/DILEval.cpp
@@ -213,7 +213,6 @@ Interpreter::DILEvalNode(const ASTNode *node) {
return node->Accept(this);
}
-
llvm::Expected<lldb::ValueObjectSP>
Interpreter::Visit(const IdentifierNode *node) {
lldb::DynamicValueType use_dynamic = m_default_dynamic;
@@ -227,9 +226,9 @@ Interpreter::Visit(const IdentifierNode *node) {
if (!identifier) {
std::string errMsg =
llvm::formatv("use of undeclared identifier '{0}'", node->GetName());
- Status error = GetStatusError(DILDiagnosticError(
- ErrorCode::kUndeclaredIdentifier, m_expr, errMsg, node->GetLocation(),
- node->GetName().size()));
+ Status error = GetStatusError(
+ DILDiagnosticError(ErrorCode::kUndeclaredIdentifier, m_expr, errMsg,
+ node->GetLocation(), node->GetName().size()));
return error.ToError();
}
diff --git a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
index ba6acd5d6089b..cf230928fc117 100644
--- a/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
+++ b/lldb/test/API/commands/frame/var-dil/basics/InstanceVariables/TestFrameVarDILInstanceVariables.py
@@ -26,5 +26,4 @@ def test_frame_var(self):
self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("this", type="TestMethods *")
- self.expect_var_path("c", children=[ValueCheck(name="field_",
- value="-1")])
+ self.expect_var_path("c", children=[ValueCheck(name="field_", value="-1")])
More information about the lldb-commits
mailing list