Michael137 created this revision.
Michael137 added reviewers: aprantl, jingham.
Herald added a project: All.
Michael137 requested review of this revision.
Herald added a reviewer: jdoerfert.
Herald added subscribers: lldb-commits, sstefan1.
Herald added a project: LLDB.
This patch adds support for evaluating expressions which reference
a captured `this` from within the context of a C++ lambda expression.
Currently LLDB doesn't provide Clang with enough information to
determine that we're inside a lambda expression and are allowed to
access variables on a captured `this`; instead Clang simply fails
to parse the expression.
There are two problems to solve here:
1. Make sure `clang::Sema` doesn't reject the expression due to illegal member
access.
2. Materialize all the necessary captured variables/member variables required
to evaluate the expression.
To address (1), we currently import the outer structure's AST context
onto `$__lldb_class`, making the `contextClass` and the `NamingClass`
match, a requirement by `clang::Sema::BuildPossibleImplicitMemberExpr`.
To address (2), we inject all captured variables as locals into the
expression source code.
**Testing**
- Added API test
Repository:
rG LLVM Github Monorepo
https://reviews.llvm.org/D129078
Files:
lldb/include/lldb/Expression/Materializer.h
lldb/include/lldb/Expression/UserExpression.h
lldb/source/Expression/Materializer.cpp
lldb/source/Expression/UserExpression.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
lldb/test/API/commands/expression/expr_inside_lambda/Makefile
lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
lldb/test/API/commands/expression/expr_inside_lambda/main.cpp
Index: lldb/test/API/commands/expression/expr_inside_lambda/main.cpp
===================================================================
--- /dev/null
+++ lldb/test/API/commands/expression/expr_inside_lambda/main.cpp
@@ -0,0 +1,99 @@
+#include <cassert>
+#include <cstdio>
+
+namespace {
+int global_var = -5;
+} // namespace
+
+struct Baz {
+ virtual ~Baz() = default;
+
+ virtual int baz_virt() = 0;
+
+ int base_base_var = 12;
+};
+
+struct Bar : public Baz {
+ virtual ~Bar() = default;
+
+ virtual int baz_virt() override {
+ base_var = 10;
+ return 1;
+ }
+
+ int base_var = 15;
+};
+
+struct Foo : public Bar {
+ int class_var = 9;
+ int shadowed = -137;
+ int *class_ptr;
+
+ virtual ~Foo() = default;
+
+ virtual int baz_virt() override {
+ shadowed = -1;
+ return 2;
+ }
+
+ void method() {
+ int local_var = 137;
+ int shadowed;
+ class_ptr = &local_var;
+ auto lambda = [&shadowed, this, &local_var,
+ local_var_copy = local_var]() mutable {
+ int lambda_local_var = 5;
+ shadowed = 5;
+ class_var = 109;
+ --base_var;
+ --base_base_var;
+ std::puts("break here");
+
+ auto nested_lambda = [this, &lambda_local_var] {
+ std::puts("break here");
+ lambda_local_var = 0;
+ };
+
+ nested_lambda();
+ --local_var_copy;
+ std::puts("break here");
+
+ struct LocalLambdaClass {
+ int lambda_class_local = -12345;
+ Foo *outer_ptr;
+
+ void inner_method() {
+ auto lambda = [this] {
+ std::puts("break here");
+ lambda_class_local = -2;
+ outer_ptr->class_var *= 2;
+ };
+
+ lambda();
+ }
+ };
+
+ LocalLambdaClass l;
+ l.outer_ptr = this;
+ l.inner_method();
+ };
+ lambda();
+ }
+
+ void non_capturing_method() {
+ int local = 5;
+ int local2 = 10;
+
+ class_var += [=] {
+ std::puts("break here");
+ return local + local2;
+ }();
+ }
+};
+
+int main() {
+ Foo f;
+ f.method();
+ f.non_capturing_method();
+ return global_var;
+}
Index: lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
===================================================================
--- /dev/null
+++ lldb/test/API/commands/expression/expr_inside_lambda/TestExprInsideLambdas.py
@@ -0,0 +1,123 @@
+""" Test that evaluating expressions from within C++ lambdas works
+ Particularly, we test the case of capturing "this" and
+ using members of the captured object in expression evaluation
+ while we're on a breakpoint inside a lambda.
+"""
+
+
+import lldb
+from lldbsuite.test.lldbtest import *
+
+
+class ExprInsideLambdaTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def expectExprError(self, expr : str, expected : str):
+ frame = self.thread.GetFrameAtIndex(0)
+ value = frame.EvaluateExpression(expr)
+ errmsg = value.GetError().GetCString()
+ self.assertIn(expected, errmsg)
+
+ def test_expr_inside_lambda(self):
+ """Test that lldb evaluating expressions inside lambda expressions works correctly."""
+ self.build()
+ (target, process, self.thread, bkpt) = \
+ lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.cpp"))
+
+ # Inside 'Foo::method'
+
+ # Check access to captured 'this'
+ self.expect_expr("class_var", result_type="int", result_value="109")
+ self.expect_expr("this->class_var", result_type="int", result_value="109")
+
+ # Check that captured shadowed variables take preference over the
+ # corresponding member variable
+ self.expect_expr("shadowed", result_type="int", result_value="5")
+ self.expect_expr("this->shadowed", result_type="int", result_value="-137")
+
+ # Check access to local captures
+ self.expect_expr("local_var", result_type="int", result_value="137")
+ self.expect_expr("*class_ptr", result_type="int", result_value="137")
+
+ # Check access to base class variables
+ self.expect_expr("base_var", result_type="int", result_value="14")
+ self.expect_expr("base_base_var", result_type="int", result_value="11")
+
+ # Check access to global variable
+ self.expect_expr("global_var", result_type="int", result_value="-5")
+
+ # Check access to multiple captures/member variables
+ self.expect_expr("(shadowed + this->shadowed) * (base_base_var + local_var - class_var)",
+ result_type="int", result_value="-5148")
+
+ # Check base-class function call
+ self.expect_expr("baz_virt()", result_type="int", result_value="2")
+ self.expect_expr("base_var", result_type="int", result_value="14")
+ self.expect_expr("this->shadowed", result_type="int", result_value="-1")
+
+ # 'p this' should yield 'struct Foo*'
+ frame = self.thread.GetFrameAtIndex(0)
+ outer_class_addr = frame.GetValueForVariablePath("this->this")
+ self.expect_expr("this", result_value=outer_class_addr.GetValue())
+
+ lldbutil.continue_to_breakpoint(process, bkpt)
+
+ # Inside 'nested_lambda'
+
+ # Check access to captured 'this'. Should still be 'struct Foo*'
+ self.expect_expr("class_var", result_type="int", result_value="109")
+ self.expect_expr("global_var", result_type="int", result_value="-5")
+ self.expect_expr("this", result_value=outer_class_addr.GetValue())
+
+ # Check access to capture
+ self.expect_expr("lambda_local_var", result_type="int", result_value="5")
+
+ # Check access to variable in previous frame which we didn't capture
+ self.expectExprError("local_var_copy", "use of undeclared identifier")
+
+ lldbutil.continue_to_breakpoint(process, bkpt)
+
+ # By-ref mutates source variable
+ self.expect_expr("lambda_local_var", result_type="int", result_value="0")
+
+ # By-value doesn't mutate source variable
+ self.expect_expr("local_var_copy", result_type="int", result_value="136")
+ self.expect_expr("local_var", result_type="int", result_value="137")
+
+ lldbutil.continue_to_breakpoint(process, bkpt)
+
+ # Inside 'LocalLambdaClass::inner_method'
+
+ # Check access to captured 'this'
+ self.expect_expr("lambda_class_local", result_type="int", result_value="-12345")
+ self.expect_expr("this->lambda_class_local", result_type="int", result_value="-12345")
+ self.expect_expr("outer_ptr->class_var", result_type="int", result_value="109")
+
+ # 'p this' should yield 'struct LocalLambdaClass*'
+ frame = self.thread.GetFrameAtIndex(0)
+ local_class_addr = frame.GetValueForVariablePath("this->this")
+ self.assertNotEqual(local_class_addr, outer_class_addr)
+ self.expect_expr("this", result_value=local_class_addr.GetValue())
+
+ # Can still access global variable
+ self.expect_expr("global_var", result_type="int", result_value="-5")
+
+ # Check access to outer top-level structure's members
+ self.expectExprError("class_var", ("use of non-static data member"
+ " 'class_var' of 'Foo' from nested type"))
+
+ self.expectExprError("base_var", ("use of non-static data member"
+ " 'base_var'"))
+
+ self.expectExprError("local_var", ("use of non-static data member 'local_var'"
+ " of '' from nested type 'LocalLambdaClass'"))
+
+ # Inside non_capturing_method
+ lldbutil.continue_to_breakpoint(process, bkpt)
+ self.expect_expr("local", result_type="int", result_value="5")
+ self.expect_expr("local2", result_type="int", result_value="10")
+ self.expect_expr("local2 * local", result_type="int", result_value="50")
+
+ self.expectExprError("class_var", ("use of non-static data member"
+ " 'class_var' of 'Foo' from nested type"))
Index: lldb/test/API/commands/expression/expr_inside_lambda/Makefile
===================================================================
--- /dev/null
+++ lldb/test/API/commands/expression/expr_inside_lambda/Makefile
@@ -0,0 +1,5 @@
+CXX_SOURCES := main.cpp
+
+CXXFLAGS_EXTRAS := -std=c++14 -O0 -g
+
+include Makefile.rules
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.h
@@ -198,6 +198,10 @@
ExecutionContext &exe_ctx,
std::vector<std::string> modules_to_import,
bool for_completion);
+
+ lldb::addr_t GetCppObjectPointer(StackFrame *frame, ConstString &object_name,
+ Status &err);
+
/// Defines how the current expression should be wrapped.
ClangExpressionSourceCode::WrapKind GetWrapKind() const;
bool SetupPersistentState(DiagnosticManager &diagnostic_manager,
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangUserExpression.cpp
@@ -872,6 +872,34 @@
return true;
}
+lldb::addr_t ClangUserExpression::GetCppObjectPointer(StackFrame *frame,
+ ConstString &object_name,
+ Status &err) {
+ auto valobj_sp = GetObjectValueObject(frame, object_name, err);
+
+ // We're inside a C++ class method. This could potentially be an unnamed
+ // lambda structure. If the lambda captured a "this", that should be
+ // the object pointer.
+ if (auto thisChildSP =
+ valobj_sp->GetChildMemberWithName(ConstString("this"), true)) {
+ valobj_sp = thisChildSP;
+ }
+
+ if (!err.Success() || !valobj_sp.get())
+ return LLDB_INVALID_ADDRESS;
+
+ lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+
+ if (ret == LLDB_INVALID_ADDRESS) {
+ err.SetErrorStringWithFormat(
+ "Couldn't load '%s' because its value couldn't be evaluated",
+ object_name.AsCString());
+ return LLDB_INVALID_ADDRESS;
+ }
+
+ return ret;
+}
+
bool ClangUserExpression::AddArguments(ExecutionContext &exe_ctx,
std::vector<lldb::addr_t> &args,
lldb::addr_t struct_address,
@@ -906,8 +934,14 @@
address_type != eAddressTypeLoad)
object_ptr_error.SetErrorString("Can't get context object's "
"debuggee address");
- } else
- object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error);
+ } else {
+ if (m_in_cplusplus_method) {
+ object_ptr =
+ GetCppObjectPointer(frame_sp.get(), object_name, object_ptr_error);
+ } else {
+ object_ptr = GetObjectPointer(frame_sp, object_name, object_ptr_error);
+ }
+ }
if (!object_ptr_error.Success()) {
exe_ctx.GetTargetRef().GetDebugger().GetAsyncOutputStream()->Printf(
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionVariable.h
@@ -116,7 +116,7 @@
/// The following values should not live beyond parsing
class ParserVars {
public:
- ParserVars() : m_lldb_value(), m_lldb_var() {}
+ ParserVars() = default;
const clang::NamedDecl *m_named_decl =
nullptr; ///< The Decl corresponding to this variable
@@ -129,6 +129,10 @@
const lldb_private::Symbol *m_lldb_sym =
nullptr; ///< The original symbol for this
/// variable, if it was a symbol
+ lldb::ValueObjectSP m_lldb_value_object; ///< ValueObject for this variable.
+ ///< Used when only an ivar for
+ ///< is available but we want
+ ///< materialize the variable.
};
private:
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h
@@ -78,9 +78,17 @@
Wrapping wrap, WrapKind wrap_kind);
private:
- void AddLocalVariableDecls(const lldb::VariableListSP &var_list_sp,
- StreamString &stream,
- const std::string &expr) const;
+ /// Writes "using" declarations for local variables into the specified stream.
+ ///
+ /// Behaviour is undefined if 'frame == nullptr'.
+ ///
+ /// \param[out] stream Stream that this function generates "using"
+ /// declarations into.
+ /// \param[in] expr Expression source that we're evaluating.
+ /// \param[in] frame StackFrame which carries information about the local
+ /// variables that we're generating "using" declarations for.
+ void AddLocalVariableDecls(StreamString &stream, const std::string &expr,
+ StackFrame *frame) const;
/// String marking the start of the user expression.
std::string m_start_marker;
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp
@@ -27,6 +27,7 @@
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/lldb-forward.h"
using namespace lldb_private;
@@ -200,6 +201,44 @@
return m_tokens.find(token) != m_tokens.end();
}
};
+
+// Return ValueObject of 'this' if we are currently
+// inside the unnamed structure of a C++ lambda.
+// In such cases, the unnamed structure will have a
+// named variable called 'this', which points to the
+// captured object's 'this' pointer.
+lldb::ValueObjectSP GetLambdaValueObject(StackFrame *frame) {
+ assert(frame);
+
+ if (auto thisValSP = frame->FindVariable(ConstString("this")))
+ if (thisValSP->GetChildMemberWithName(ConstString("this"), true))
+ return thisValSP;
+
+ return nullptr;
+}
+
+// If we're evaluating from inside a lambda, add a "using" declaration
+// to 'stream' for each capture used in the expression (tokenized by
+// 'verifier').
+void AddLambdaCaptureDecls(StreamString &stream, StackFrame *frame,
+ TokenVerifier const &verifier) {
+ assert(frame);
+
+ if (auto thisValSP = GetLambdaValueObject(frame)) {
+ uint32_t numChildren = thisValSP->GetNumChildren();
+ for (uint32_t i = 0; i < numChildren; ++i) {
+ auto childVal = thisValSP->GetChildAtIndex(i, true);
+ ConstString childName(childVal ? childVal->GetName() : ConstString(""));
+
+ if (!childName.IsEmpty() && verifier.hasToken(childName.GetStringRef()) &&
+ childName != "this") {
+ stream.Printf("using $__lldb_local_vars::%s;\n",
+ childName.GetCString());
+ }
+ }
+ }
+}
+
} // namespace
TokenVerifier::TokenVerifier(std::string body) {
@@ -264,16 +303,24 @@
}
}
-void ClangExpressionSourceCode::AddLocalVariableDecls(
- const lldb::VariableListSP &var_list_sp, StreamString &stream,
- const std::string &expr) const {
+void ClangExpressionSourceCode::AddLocalVariableDecls(StreamString &stream,
+ const std::string &expr,
+ StackFrame *frame) const {
+ assert(frame);
TokenVerifier tokens(expr);
+ lldb::VariableListSP var_list_sp = frame->GetInScopeVariableList(false, true);
+
for (size_t i = 0; i < var_list_sp->GetSize(); i++) {
lldb::VariableSP var_sp = var_list_sp->GetVariableAtIndex(i);
ConstString var_name = var_sp->GetName();
+ if (var_name == "this" && m_wrap_kind == WrapKind::CppMemberFunction) {
+ AddLambdaCaptureDecls(stream, frame, tokens);
+
+ continue;
+ }
// We can check for .block_descriptor w/o checking for langauge since this
// is not a valid identifier in either C or C++.
@@ -288,9 +335,6 @@
if ((var_name == "self" || var_name == "_cmd") && is_objc)
continue;
- if (var_name == "this" && m_wrap_kind == WrapKind::CppMemberFunction)
- continue;
-
stream.Printf("using $__lldb_local_vars::%s;\n", var_name.AsCString());
}
}
@@ -376,10 +420,8 @@
if (add_locals)
if (target->GetInjectLocalVariables(&exe_ctx)) {
- lldb::VariableListSP var_list_sp =
- frame->GetInScopeVariableList(false, true);
- AddLocalVariableDecls(var_list_sp, lldb_local_var_decls,
- force_add_all_locals ? "" : m_body);
+ AddLocalVariableDecls(lldb_local_var_decls,
+ force_add_all_locals ? "" : m_body, frame);
}
}
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.h
@@ -561,6 +561,14 @@
/// (treated as a variable), and put it in the list of found entities.
void AddOneGenericVariable(NameSearchContext &context, const Symbol &symbol);
+ bool AddLambdaCapture(NameSearchContext &context, lldb::ValueObjectSP capture,
+ lldb::ValueObjectSP lambda, SymbolContext &sym_ctx);
+
+ bool GetVariableFromValueObject(CompilerType &comp_type,
+ lldb_private::Value &var_location,
+ TypeFromUser *user_type,
+ TypeFromParser *parser_type);
+
/// Use the NameSearchContext to generate a Decl for the given function.
/// (Functions are not placed in the Tuple list.) Can handle both fully
/// typed functions and generic functions.
Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
===================================================================
--- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
+++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp
@@ -62,6 +62,23 @@
static const char *g_lldb_local_vars_namespace_cstr = "$__lldb_local_vars";
+namespace {
+// Return ValueObject of 'this' if we are currently
+// inside the unnamed structure of a C++ lambda.
+// In such cases, the unnamed structure will have a
+// named variable called 'this', which points to the
+// captured object's 'this' pointer.
+lldb::ValueObjectSP GetLambdaValueObject(StackFrame *frame) {
+ assert(frame);
+
+ if (auto thisValSP = frame->FindVariable(ConstString("this")))
+ if (thisValSP->GetChildMemberWithName(ConstString("this"), true))
+ return thisValSP;
+
+ return nullptr;
+}
+} // namespace
+
ClangExpressionDeclMap::ClangExpressionDeclMap(
bool keep_result_in_memory,
Materializer::PersistentVariableDelegate *result_delegate,
@@ -394,6 +411,9 @@
else if (parser_vars->m_lldb_var)
offset = m_parser_vars->m_materializer->AddVariable(
parser_vars->m_lldb_var, err);
+ else if (parser_vars->m_lldb_value_object)
+ offset = m_parser_vars->m_materializer->AddValueObject(
+ parser_vars->m_lldb_value_object, err);
}
if (!err.Success())
@@ -799,6 +819,24 @@
TypeSystemClang::DeclContextGetAsCXXMethodDecl(function_decl_ctx);
if (method_decl) {
+ if (auto thisVarSP = frame->FindVariable(ConstString("this"))) {
+ if (auto thisThisVarSP =
+ thisVarSP->GetChildMemberWithName(ConstString("this"), true)) {
+ // We're inside a lambda...
+ TypeFromUser pointee_type =
+ thisThisVarSP->GetCompilerType().GetPointeeType();
+ TypeFromUser pointer_type = thisThisVarSP->GetCompilerType();
+
+ LLDB_LOG(log, " CEDM::FEVD Adding type for $__lldb_class: {0}",
+ thisThisVarSP->GetName());
+
+ AddContextClassType(context, pointee_type);
+ m_struct_vars->m_object_pointer_type = pointer_type;
+
+ return;
+ }
+ }
+
clang::CXXRecordDecl *class_decl = method_decl->getParent();
QualType class_qual_type(class_decl->getTypeForDecl(), 0);
@@ -1101,6 +1139,17 @@
context.m_found_variable = true;
}
}
+
+ if (!variable_found) {
+ if (auto lambda = GetLambdaValueObject(frame)) {
+ if (auto capture = lambda->GetChildMemberWithName(name, true)) {
+ variable_found = true;
+ context.m_found_variable = true;
+ AddLambdaCapture(context, capture, lambda, sym_ctx);
+ }
+ }
+ }
+
return variable_found;
}
@@ -1411,6 +1460,7 @@
return;
if (target) {
+
ValueObjectSP valobj;
VariableSP var;
var = FindGlobalVariable(*target, module_sp, name, namespace_decl);
@@ -1457,6 +1507,41 @@
}
}
+bool ClangExpressionDeclMap::GetVariableFromValueObject(
+ CompilerType &var_clang_type, lldb_private::Value &var_location,
+ TypeFromUser *user_type, TypeFromParser *parser_type) {
+ assert(var_clang_type);
+ Log *log = GetLog(LLDBLog::Expressions);
+
+ TypeSystemClang *clang_ast =
+ llvm::dyn_cast_or_null<TypeSystemClang>(var_clang_type.GetTypeSystem());
+
+ if (!clang_ast) {
+ LLDB_LOG(log, "Skipped a definition because it has no Clang AST");
+ return false;
+ }
+
+ CompilerType type_to_use = GuardedCopyType(var_clang_type);
+
+ if (!type_to_use) {
+ LLDB_LOG(log,
+ "Couldn't copy a variable's type into the parser's AST context");
+
+ return false;
+ }
+
+ if (parser_type)
+ *parser_type = TypeFromParser(type_to_use);
+
+ if (var_location.GetContextType() == Value::ContextType::Invalid)
+ var_location.SetCompilerType(type_to_use);
+
+ if (user_type)
+ *user_type = TypeFromUser(var_clang_type);
+
+ return true;
+}
+
bool ClangExpressionDeclMap::GetVariableValue(VariableSP &var,
lldb_private::Value &var_location,
TypeFromUser *user_type,
@@ -1542,6 +1627,64 @@
return true;
}
+bool ClangExpressionDeclMap::AddLambdaCapture(NameSearchContext &context,
+ ValueObjectSP capture,
+ ValueObjectSP lambda,
+ SymbolContext &sym_ctx) {
+ Log *log = GetLog(LLDBLog::Expressions);
+
+ TypeFromUser ut;
+ TypeFromParser pt;
+ Value var_location = lambda->GetValue();
+
+ CompilerType var_clang_type = capture->GetCompilerType();
+ if (!GetVariableFromValueObject(var_clang_type, var_location, &ut, &pt))
+ return false;
+
+ clang::QualType parser_opaque_type =
+ QualType::getFromOpaquePtr(pt.GetOpaqueQualType());
+
+ if (parser_opaque_type.isNull())
+ return false;
+
+ if (const clang::Type *parser_type = parser_opaque_type.getTypePtr()) {
+ if (const TagType *tag_type = dyn_cast<TagType>(parser_type))
+ CompleteType(tag_type->getDecl());
+ }
+
+ bool is_reference = pt.IsReferenceType();
+
+ NamedDecl *var_decl = nullptr;
+ if (is_reference)
+ var_decl = context.AddVarDecl(pt);
+ else
+ var_decl = context.AddVarDecl(pt.GetLValueReferenceType());
+
+ std::string decl_name(context.m_decl_name.getAsString());
+ ConstString entity_name(decl_name.c_str());
+ ClangExpressionVariable *entity(new ClangExpressionVariable(capture));
+ m_found_entities.AddNewlyConstructedVariable(entity);
+
+ assert(entity);
+ entity->EnableParserVars(GetParserID());
+ ClangExpressionVariable::ParserVars *parser_vars =
+ entity->GetParserVars(GetParserID());
+ parser_vars->m_named_decl = var_decl;
+ parser_vars->m_llvm_value = nullptr;
+ parser_vars->m_lldb_value = var_location;
+ parser_vars->m_lldb_value_object = capture;
+
+ if (is_reference)
+ entity->m_flags |= ClangExpressionVariable::EVTypeIsReference;
+
+ LLDB_LOG(log,
+ " CEDM::FEVD Found lambda capture variable {0}, returned\n{1} "
+ "(original {2})",
+ decl_name, ClangUtil::DumpDecl(var_decl), ClangUtil::ToString(ut));
+
+ return true;
+}
+
void ClangExpressionDeclMap::AddOneVariable(NameSearchContext &context,
VariableSP var,
ValueObjectSP valobj) {
Index: lldb/source/Expression/UserExpression.cpp
===================================================================
--- lldb/source/Expression/UserExpression.cpp
+++ lldb/source/Expression/UserExpression.cpp
@@ -98,28 +98,33 @@
return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp);
}
-lldb::addr_t UserExpression::GetObjectPointer(lldb::StackFrameSP frame_sp,
- ConstString &object_name,
- Status &err) {
+lldb::ValueObjectSP UserExpression::GetObjectValueObject(
+ StackFrame *frame, ConstString const &object_name, Status &err) {
err.Clear();
- if (!frame_sp) {
+ if (!frame) {
err.SetErrorStringWithFormat(
"Couldn't load '%s' because the context is incomplete",
object_name.AsCString());
- return LLDB_INVALID_ADDRESS;
+ return {};
}
lldb::VariableSP var_sp;
lldb::ValueObjectSP valobj_sp;
- valobj_sp = frame_sp->GetValueForVariableExpressionPath(
+ return frame->GetValueForVariableExpressionPath(
object_name.GetStringRef(), lldb::eNoDynamicValues,
StackFrame::eExpressionPathOptionCheckPtrVsMember |
StackFrame::eExpressionPathOptionsNoFragileObjcIvar |
StackFrame::eExpressionPathOptionsNoSyntheticChildren |
StackFrame::eExpressionPathOptionsNoSyntheticArrayRange,
var_sp, err);
+}
+
+lldb::addr_t UserExpression::GetObjectPointer(lldb::StackFrameSP frame_sp,
+ ConstString &object_name,
+ Status &err) {
+ auto valobj_sp = GetObjectValueObject(frame_sp.get(), object_name, err);
if (!err.Success() || !valobj_sp.get())
return LLDB_INVALID_ADDRESS;
Index: lldb/source/Expression/Materializer.cpp
===================================================================
--- lldb/source/Expression/Materializer.cpp
+++ lldb/source/Expression/Materializer.cpp
@@ -22,6 +22,7 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/RegisterValue.h"
+#include "lldb/lldb-forward.h"
#include <memory>
@@ -412,18 +413,26 @@
return ret;
}
-class EntityVariable : public Materializer::Entity {
+class EntityVariableBase : public Materializer::Entity {
public:
- EntityVariable(lldb::VariableSP &variable_sp)
- : Entity(), m_variable_sp(variable_sp) {
+ virtual ~EntityVariableBase() = default;
+
+ EntityVariableBase() {
// Hard-coding to maximum size of a pointer since all variables are
// materialized by reference
m_size = 8;
m_alignment = 8;
- m_is_reference =
- m_variable_sp->GetType()->GetForwardCompilerType().IsReferenceType();
}
+ virtual ConstString GetName() const = 0;
+ virtual lldb::ValueObjectSP
+ GetValueObject(ExecutionContextScope *scope) const = 0;
+ virtual llvm::Optional<uint64_t>
+ GetByteSize(ExecutionContextScope *scope) const = 0;
+ virtual bool LocationExpressionIsValid() const = 0;
+ virtual llvm::Optional<size_t>
+ GetTypeBitAlign(ExecutionContextScope *scope) const = 0;
+
void Materialize(lldb::StackFrameSP &frame_sp, IRMemoryMap &map,
lldb::addr_t process_address, Status &err) override {
Log *log = GetLog(LLDBLog::Expressions);
@@ -433,7 +442,7 @@
LLDB_LOGF(log,
"EntityVariable::Materialize [address = 0x%" PRIx64
", m_variable_sp = %s]",
- (uint64_t)load_addr, m_variable_sp->GetName().AsCString());
+ (uint64_t)load_addr, GetName().GetCString());
}
ExecutionContextScope *scope = frame_sp.get();
@@ -441,13 +450,11 @@
if (!scope)
scope = map.GetBestExecutionContextScope();
- lldb::ValueObjectSP valobj_sp =
- ValueObjectVariable::Create(scope, m_variable_sp);
+ lldb::ValueObjectSP valobj_sp = GetValueObject(scope);
if (!valobj_sp) {
err.SetErrorStringWithFormat(
- "couldn't get a value object for variable %s",
- m_variable_sp->GetName().AsCString());
+ "couldn't get a value object for variable %s", GetName().AsCString());
return;
}
@@ -455,7 +462,7 @@
if (valobj_error.Fail()) {
err.SetErrorStringWithFormat("couldn't get the value of variable %s: %s",
- m_variable_sp->GetName().AsCString(),
+ GetName().AsCString(),
valobj_error.AsCString());
return;
}
@@ -468,7 +475,7 @@
if (!extract_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't read contents of reference variable %s: %s",
- m_variable_sp->GetName().AsCString(), extract_error.AsCString());
+ GetName().AsCString(), extract_error.AsCString());
return;
}
@@ -481,7 +488,7 @@
if (!write_error.Success()) {
err.SetErrorStringWithFormat("couldn't write the contents of reference "
"variable %s to memory: %s",
- m_variable_sp->GetName().AsCString(),
+ GetName().AsCString(),
write_error.AsCString());
return;
}
@@ -497,7 +504,7 @@
if (!write_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't write the address of variable %s to memory: %s",
- m_variable_sp->GetName().AsCString(), write_error.AsCString());
+ GetName().AsCString(), write_error.AsCString());
return;
}
} else {
@@ -506,7 +513,7 @@
valobj_sp->GetData(data, extract_error);
if (!extract_error.Success()) {
err.SetErrorStringWithFormat("couldn't get the value of %s: %s",
- m_variable_sp->GetName().AsCString(),
+ GetName().AsCString(),
extract_error.AsCString());
return;
}
@@ -514,32 +521,29 @@
if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
err.SetErrorStringWithFormat(
"trying to create a temporary region for %s but one exists",
- m_variable_sp->GetName().AsCString());
+ GetName().AsCString());
return;
}
- if (data.GetByteSize() < m_variable_sp->GetType()->GetByteSize(scope)) {
- if (data.GetByteSize() == 0 &&
- !m_variable_sp->LocationExpression().IsValid()) {
+ if (data.GetByteSize() < GetByteSize(scope)) {
+ if (data.GetByteSize() == 0 && !LocationExpressionIsValid()) {
err.SetErrorStringWithFormat("the variable '%s' has no location, "
"it may have been optimized out",
- m_variable_sp->GetName().AsCString());
+ GetName().AsCString());
} else {
err.SetErrorStringWithFormat(
"size of variable %s (%" PRIu64
") is larger than the ValueObject's size (%" PRIu64 ")",
- m_variable_sp->GetName().AsCString(),
- m_variable_sp->GetType()->GetByteSize(scope).value_or(0),
+ GetName().AsCString(), GetByteSize(scope).value_or(0),
data.GetByteSize());
}
return;
}
- llvm::Optional<size_t> opt_bit_align =
- m_variable_sp->GetType()->GetLayoutCompilerType().GetTypeBitAlign(scope);
+ llvm::Optional<size_t> opt_bit_align = GetTypeBitAlign(scope);
if (!opt_bit_align) {
err.SetErrorStringWithFormat("can't get the type alignment for %s",
- m_variable_sp->GetName().AsCString());
+ GetName().AsCString());
return;
}
@@ -561,7 +565,7 @@
if (!alloc_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't allocate a temporary region for %s: %s",
- m_variable_sp->GetName().AsCString(), alloc_error.AsCString());
+ GetName().AsCString(), alloc_error.AsCString());
return;
}
@@ -573,7 +577,7 @@
if (!write_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't write to the temporary region for %s: %s",
- m_variable_sp->GetName().AsCString(), write_error.AsCString());
+ GetName().AsCString(), write_error.AsCString());
return;
}
@@ -585,8 +589,7 @@
if (!pointer_write_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't write the address of the temporary region for %s: %s",
- m_variable_sp->GetName().AsCString(),
- pointer_write_error.AsCString());
+ GetName().AsCString(), pointer_write_error.AsCString());
}
}
}
@@ -602,7 +605,7 @@
LLDB_LOGF(log,
"EntityVariable::Dematerialize [address = 0x%" PRIx64
", m_variable_sp = %s]",
- (uint64_t)load_addr, m_variable_sp->GetName().AsCString());
+ (uint64_t)load_addr, GetName().AsCString());
}
if (m_temporary_allocation != LLDB_INVALID_ADDRESS) {
@@ -611,13 +614,12 @@
if (!scope)
scope = map.GetBestExecutionContextScope();
- lldb::ValueObjectSP valobj_sp =
- ValueObjectVariable::Create(scope, m_variable_sp);
+ lldb::ValueObjectSP valobj_sp = GetValueObject(scope);
if (!valobj_sp) {
err.SetErrorStringWithFormat(
"couldn't get a value object for variable %s",
- m_variable_sp->GetName().AsCString());
+ GetName().AsCString());
return;
}
@@ -630,7 +632,7 @@
if (!extract_error.Success()) {
err.SetErrorStringWithFormat("couldn't get the data for variable %s",
- m_variable_sp->GetName().AsCString());
+ GetName().AsCString());
return;
}
@@ -652,7 +654,7 @@
if (!set_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't write the new contents of %s back into the variable",
- m_variable_sp->GetName().AsCString());
+ GetName().AsCString());
return;
}
}
@@ -664,7 +666,7 @@
if (!free_error.Success()) {
err.SetErrorStringWithFormat(
"couldn't free the temporary region for %s: %s",
- m_variable_sp->GetName().AsCString(), free_error.AsCString());
+ GetName().AsCString(), free_error.AsCString());
return;
}
@@ -747,14 +749,76 @@
}
}
-private:
- lldb::VariableSP m_variable_sp;
+protected:
bool m_is_reference = false;
lldb::addr_t m_temporary_allocation = LLDB_INVALID_ADDRESS;
size_t m_temporary_allocation_size = 0;
lldb::DataBufferSP m_original_data;
};
+class EntityVariable : public EntityVariableBase {
+public:
+ EntityVariable(lldb::VariableSP &variable_sp) : m_variable_sp(variable_sp) {
+ m_is_reference =
+ m_variable_sp->GetType()->GetForwardCompilerType().IsReferenceType();
+ }
+
+ ConstString GetName() const override { return m_variable_sp->GetName(); }
+
+ lldb::ValueObjectSP
+ GetValueObject(ExecutionContextScope *scope) const override {
+ return ValueObjectVariable::Create(scope, m_variable_sp);
+ }
+
+ llvm::Optional<uint64_t>
+ GetByteSize(ExecutionContextScope *scope) const override {
+ return m_variable_sp->GetType()->GetByteSize(scope);
+ }
+
+ bool LocationExpressionIsValid() const override {
+ return m_variable_sp->LocationExpression().IsValid();
+ }
+
+ llvm::Optional<size_t>
+ GetTypeBitAlign(ExecutionContextScope *scope) const override {
+ return m_variable_sp->GetType()->GetLayoutCompilerType().GetTypeBitAlign(
+ scope);
+ }
+
+private:
+ lldb::VariableSP m_variable_sp;
+};
+
+class EntityValueObject : public EntityVariableBase {
+public:
+ EntityValueObject(lldb::ValueObjectSP valobj_sp)
+ : m_valobj_sp(std::move(valobj_sp)) {
+ m_is_reference = m_valobj_sp->GetCompilerType().IsReferenceType();
+ }
+
+ ConstString GetName() const override { return m_valobj_sp->GetName(); }
+
+ lldb::ValueObjectSP
+ GetValueObject(ExecutionContextScope *scope) const override {
+ return m_valobj_sp;
+ }
+
+ llvm::Optional<uint64_t>
+ GetByteSize(ExecutionContextScope *scope) const override {
+ return m_valobj_sp->GetCompilerType().GetByteSize(scope);
+ }
+
+ bool LocationExpressionIsValid() const override { return true; }
+
+ llvm::Optional<size_t>
+ GetTypeBitAlign(ExecutionContextScope *scope) const override {
+ return m_valobj_sp->GetCompilerType().GetTypeBitAlign(scope);
+ }
+
+private:
+ lldb::ValueObjectSP m_valobj_sp;
+};
+
uint32_t Materializer::AddVariable(lldb::VariableSP &variable_sp, Status &err) {
EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
*iter = std::make_unique<EntityVariable>(variable_sp);
@@ -763,6 +827,15 @@
return ret;
}
+uint32_t Materializer::AddValueObject(lldb::ValueObjectSP valobj_sp,
+ Status &err) {
+ EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP());
+ *iter = std::make_unique<EntityValueObject>(std::move(valobj_sp));
+ uint32_t ret = AddStructMember(**iter);
+ (*iter)->SetOffset(ret);
+ return ret;
+}
+
class EntityResultVariable : public Materializer::Entity {
public:
EntityResultVariable(const CompilerType &type, bool is_program_reference,
Index: lldb/include/lldb/Expression/UserExpression.h
===================================================================
--- lldb/include/lldb/Expression/UserExpression.h
+++ lldb/include/lldb/Expression/UserExpression.h
@@ -280,6 +280,10 @@
static lldb::addr_t GetObjectPointer(lldb::StackFrameSP frame_sp,
ConstString &object_name, Status &err);
+ static lldb::ValueObjectSP
+ GetObjectValueObject(StackFrame *frame, ConstString const &object_name,
+ Status &err);
+
/// Populate m_in_cplusplus_method and m_in_objectivec_method based on the
/// environment.
Index: lldb/include/lldb/Expression/Materializer.h
===================================================================
--- lldb/include/lldb/Expression/Materializer.h
+++ lldb/include/lldb/Expression/Materializer.h
@@ -78,6 +78,7 @@
AddPersistentVariable(lldb::ExpressionVariableSP &persistent_variable_sp,
PersistentVariableDelegate *delegate, Status &err);
uint32_t AddVariable(lldb::VariableSP &variable_sp, Status &err);
+ uint32_t AddValueObject(lldb::ValueObjectSP valobj_sp, Status &err);
uint32_t AddResultVariable(const CompilerType &type, bool is_lvalue,
bool keep_in_memory,
PersistentVariableDelegate *delegate, Status &err);
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits