[Lldb-commits] [lldb] [LLDB] Add DIL code for handling plain variable names. (PR #120971)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Jan 14 16:51:40 PST 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 1/2] [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 00000000000000..64b3e7758229c2
--- /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 00000000000000..9f0a1a2221e388
--- /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 00000000000000..4006bb10630f24
--- /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 00000000000000..c794fb2bfc0ed3
--- /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 00000000000000..b718903b7bea49
--- /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 2633c976c13bf4..28450b1d1c1c1e 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 70cb3d6d53f071..92683916f5a522 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 00000000000000..1af0dc57ddcea7
--- /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 00000000000000..01da8521536503
--- /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 00000000000000..c63a83bea6f1df
--- /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 00000000000000..eb36de80be76a5
--- /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 00000000000000..99998b20bcb050
--- /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 00000000000000..b87de2ab27ebb2
--- /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 00000000000000..605142d169db28
--- /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 00000000000000..99998b20bcb050
--- /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 00000000000000..dcc408f1f37bd1
--- /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 00000000000000..276166f97a7bda
--- /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 00000000000000..99998b20bcb050
--- /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 00000000000000..c19f09f2f06b68
--- /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 00000000000000..c0dd24291429a3
--- /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 2/2] [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 64b3e7758229c2..7aca2b34a4929f 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 9f0a1a2221e388..dd041483b835b9 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 4006bb10630f24..6bb9a76cd81521 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 c794fb2bfc0ed3..669ddfb2d4a4b3 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 b718903b7bea49..f8db81b1aee5b3 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 28450b1d1c1c1e..357ec2e47a5328 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 1af0dc57ddcea7..9aebe7e2ca4f94 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 01da8521536503..7a6fcca58a2b9e 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 c63a83bea6f1df..79b699d0d2d2da 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 eb36de80be76a5..147a76ba8f9d63 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 b87de2ab27ebb2..252dcacb13bbd9 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 c19f09f2f06b68..b2f1d3a07912ac 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 c0dd24291429a3..21f0fd6d62c4af 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'"));
-}
-*/
More information about the lldb-commits
mailing list