[Lldb-commits] [lldb] 6c599b1 - [lldb] Let 'v' command directly access ivars of _any_ self/this

Dave Lee via lldb-commits lldb-commits at lists.llvm.org
Wed Mar 8 11:19:53 PST 2023


Author: Dave Lee
Date: 2023-03-08T11:19:43-08:00
New Revision: 6c599b1e9b7e1b57952565468aed2de16af21082

URL: https://github.com/llvm/llvm-project/commit/6c599b1e9b7e1b57952565468aed2de16af21082
DIFF: https://github.com/llvm/llvm-project/commit/6c599b1e9b7e1b57952565468aed2de16af21082.diff

LOG: [lldb] Let 'v' command directly access ivars of _any_ self/this

The `v` (`frame variable`) command can directly access ivars/fields of `this` or `self`.
Such as `v field`, instead of `v this->field`. This change relaxes the criteria for
finding `this`/`self` variables.

There are cases where a `this`/`self` variable does exist, but up to now the `v` command
has not made use of it. The user would have to explicitly run `v this->field` or
`self->_ivar` to access ivars. This change allows such cases to also work (without
explicitly dereferencing `this`/`self`).

A very common example in Objective-C (and Swift) is weakly capturing `self`:

```
__weak Type *weakSelf = self;
void (^block)(void) = ^{
   Type *self = weakSelf; // Re-establish strong reference.
   // `v _ivar` should work just as well as `v self->_ivar`.
};
```

In this case, `self` exists but `v` would not have used it. With this change, the fact
that a variable named `self` exists is enough for it to be used.

Differential Revision: https://reviews.llvm.org/D145276

Added: 
    

Modified: 
    lldb/include/lldb/Symbol/CompilerDeclContext.h
    lldb/include/lldb/Symbol/SymbolContext.h
    lldb/include/lldb/Symbol/TypeSystem.h
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
    lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
    lldb/source/Symbol/CompilerDeclContext.cpp
    lldb/source/Symbol/SymbolContext.cpp
    lldb/source/Target/StackFrame.cpp
    lldb/test/API/commands/frame/var/direct-ivar/objc/Makefile
    lldb/test/API/commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py
    lldb/test/API/commands/frame/var/direct-ivar/objc/main.m

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Symbol/CompilerDeclContext.h b/lldb/include/lldb/Symbol/CompilerDeclContext.h
index ca404a6641d5e..63e5f7b680e63 100644
--- a/lldb/include/lldb/Symbol/CompilerDeclContext.h
+++ b/lldb/include/lldb/Symbol/CompilerDeclContext.h
@@ -61,15 +61,21 @@ class CompilerDeclContext {
 
   /// Checks if this decl context represents a method of a class.
   ///
-  /// \param[out] language_object_name_ptr
-  ///     If non NULL and \b true is returned from this function,
-  ///     this will indicate if implicit object name for the language
-  ///     like "this" for C++, and "self" for Objective C.
-  ///
   /// \return
   ///     Returns true if this is a decl context that represents a method
   ///     in a struct, union or class.
-  bool IsClassMethod(ConstString *language_object_name_ptr = nullptr);
+  bool IsClassMethod();
+
+  /// Determines the original language of the decl context.
+  lldb::LanguageType GetLanguage();
+
+  /// Determines the name of the instance variable for the this decl context.
+  ///
+  /// For C++ the name is "this", for Objective-C the name is "self".
+  ///
+  /// \return
+  ///     Returns a string for the name of the instance variable.
+  ConstString GetInstanceVariableName(lldb::LanguageType language);
 
   /// Check if the given other decl context is contained in the lookup
   /// of this decl context (for example because the other context is a nested

diff  --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h
index bb9e031daaaa4..73fa25514aff3 100644
--- a/lldb/include/lldb/Symbol/SymbolContext.h
+++ b/lldb/include/lldb/Symbol/SymbolContext.h
@@ -245,17 +245,13 @@ class SymbolContext {
   ///     represented by this symbol context object, nullptr otherwise.
   Block *GetFunctionBlock();
 
-  /// If this symbol context represents a function that is a method, return
-  /// true and provide information about the method.
+  /// Determines the name of the instance variable for the this decl context.
   ///
-  /// \param[out] language_object_name
-  ///     If \b true is returned, the name of the artificial variable
-  ///     for the language ("this" for C++, "self" for ObjC).
+  /// For C++ the name is "this", for Objective-C the name is "self".
   ///
   /// \return
-  ///     \b True if this symbol context represents a function that
-  ///     is a method of a class, \b false otherwise.
-  bool GetFunctionMethodInfo(ConstString &language_object_name);
+  ///     Returns a string for the name of the instance variable.
+  ConstString GetInstanceVariableName();
 
   /// Sorts the types in TypeMap according to SymbolContext to TypeList
   ///

diff  --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h
index 7681a700766a2..0777d4d5ad6f3 100644
--- a/lldb/include/lldb/Symbol/TypeSystem.h
+++ b/lldb/include/lldb/Symbol/TypeSystem.h
@@ -127,13 +127,13 @@ class TypeSystem : public PluginInterface,
   virtual ConstString
   DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) = 0;
 
-  virtual bool
-  DeclContextIsClassMethod(void *opaque_decl_ctx,
-                           ConstString *language_object_name_ptr) = 0;
+  virtual bool DeclContextIsClassMethod(void *opaque_decl_ctx) = 0;
 
   virtual bool DeclContextIsContainedInLookup(void *opaque_decl_ctx,
                                               void *other_opaque_decl_ctx) = 0;
 
+  virtual lldb::LanguageType DeclContextGetLanguage(void *opaque_decl_ctx) = 0;
+
   // Tests
 #ifndef NDEBUG
   /// Verify the integrity of the type to catch CompilerTypes that mix
@@ -202,6 +202,10 @@ class TypeSystem : public PluginInterface,
   // TypeSystems can support more than one language
   virtual bool SupportsLanguage(lldb::LanguageType language) = 0;
 
+  /// The name of the variable used for explicitly accessing data scoped to the
+  /// current instance (or type). C++ uses "this", ObjC uses "self".
+  virtual ConstString GetInstanceVariableName(lldb::LanguageType language) = 0;
+
   // Type Completion
 
   virtual bool GetCompleteType(lldb::opaque_compiler_type_t type) = 0;

diff  --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
index 81feea9e4a5dc..61d7dff2e00b8 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp
@@ -8,6 +8,8 @@
 
 #include "TypeSystemClang.h"
 
+#include "clang/AST/DeclBase.h"
+#include "llvm/Support/Casting.h"
 #include "llvm/Support/FormatAdapters.h"
 #include "llvm/Support/FormatVariadic.h"
 
@@ -3725,6 +3727,22 @@ bool TypeSystemClang::SupportsLanguage(lldb::LanguageType language) {
   return TypeSystemClangSupportsLanguage(language);
 }
 
+ConstString
+TypeSystemClang::GetInstanceVariableName(lldb::LanguageType language) {
+  switch (language) {
+  case LanguageType::eLanguageTypeC_plus_plus:
+  case LanguageType::eLanguageTypeC_plus_plus_03:
+  case LanguageType::eLanguageTypeC_plus_plus_11:
+  case LanguageType::eLanguageTypeC_plus_plus_14:
+    return ConstString("this");
+  case LanguageType::eLanguageTypeObjC:
+  case LanguageType::eLanguageTypeObjC_plus_plus:
+    return ConstString("self");
+  default:
+    return {};
+  }
+}
+
 std::optional<std::string>
 TypeSystemClang::GetCXXClassName(const CompilerType &type) {
   if (!type)
@@ -9751,32 +9769,21 @@ TypeSystemClang::DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) {
   return ConstString();
 }
 
-bool TypeSystemClang::DeclContextIsClassMethod(
-    void *opaque_decl_ctx, ConstString *language_object_name_ptr) {
-  if (opaque_decl_ctx) {
-    clang::DeclContext *decl_ctx = (clang::DeclContext *)opaque_decl_ctx;
-    if (ObjCMethodDecl *objc_method =
-            llvm::dyn_cast<clang::ObjCMethodDecl>(decl_ctx)) {
-      if (objc_method->isInstanceMethod())
-        if (language_object_name_ptr)
-          language_object_name_ptr->SetCString("self");
-      return true;
-    } else if (CXXMethodDecl *cxx_method =
-                   llvm::dyn_cast<clang::CXXMethodDecl>(decl_ctx)) {
-      if (cxx_method->isInstance())
-        if (language_object_name_ptr)
-          language_object_name_ptr->SetCString("this");
-      return true;
-    } else if (clang::FunctionDecl *function_decl =
-                   llvm::dyn_cast<clang::FunctionDecl>(decl_ctx)) {
-      ClangASTMetadata *metadata = GetMetadata(function_decl);
-      if (metadata && metadata->HasObjectPtr()) {
-        if (language_object_name_ptr)
-          language_object_name_ptr->SetCString(metadata->GetObjectPtrName());
-        return true;
-      }
-    }
+bool TypeSystemClang::DeclContextIsClassMethod(void *opaque_decl_ctx) {
+  if (!opaque_decl_ctx)
+    return false;
+
+  clang::DeclContext *decl_ctx = (clang::DeclContext *)opaque_decl_ctx;
+  if (llvm::isa<clang::ObjCMethodDecl>(decl_ctx)) {
+    return true;
+  } else if (llvm::isa<clang::CXXMethodDecl>(decl_ctx)) {
+    return true;
+  } else if (clang::FunctionDecl *fun_decl =
+                 llvm::dyn_cast<clang::FunctionDecl>(decl_ctx)) {
+    if (ClangASTMetadata *metadata = GetMetadata(fun_decl))
+      return metadata->HasObjectPtr();
   }
+
   return false;
 }
 
@@ -9797,6 +9804,24 @@ bool TypeSystemClang::DeclContextIsContainedInLookup(
   return false;
 }
 
+lldb::LanguageType
+TypeSystemClang::DeclContextGetLanguage(void *opaque_decl_ctx) {
+  if (!opaque_decl_ctx)
+    return eLanguageTypeUnknown;
+
+  auto *decl_ctx = (clang::DeclContext *)opaque_decl_ctx;
+  if (llvm::isa<clang::ObjCMethodDecl>(decl_ctx)) {
+    return eLanguageTypeObjC;
+  } else if (llvm::isa<clang::CXXMethodDecl>(decl_ctx)) {
+    return eLanguageTypeC_plus_plus;
+  } else if (auto *fun_decl = llvm::dyn_cast<clang::FunctionDecl>(decl_ctx)) {
+    if (ClangASTMetadata *metadata = GetMetadata(fun_decl))
+      return metadata->GetObjectPtrLanguage();
+  }
+
+  return eLanguageTypeUnknown;
+}
+
 static bool IsClangDeclContext(const CompilerDeclContext &dc) {
   return dc.IsValid() && isa<TypeSystemClang>(dc.GetTypeSystem());
 }

diff  --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
index 99021f9b76bda..baddf6253beb4 100644
--- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
+++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h
@@ -579,12 +579,13 @@ class TypeSystemClang : public TypeSystem {
 
   ConstString DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) override;
 
-  bool DeclContextIsClassMethod(void *opaque_decl_ctx,
-                                ConstString *language_object_name_ptr) override;
+  bool DeclContextIsClassMethod(void *opaque_decl_ctx) override;
 
   bool DeclContextIsContainedInLookup(void *opaque_decl_ctx,
                                       void *other_opaque_decl_ctx) override;
 
+  lldb::LanguageType DeclContextGetLanguage(void *opaque_decl_ctx) override;
+
   // Clang specific clang::DeclContext functions
 
   static clang::DeclContext *
@@ -710,6 +711,8 @@ class TypeSystemClang : public TypeSystem {
 
   bool SupportsLanguage(lldb::LanguageType language) override;
 
+  ConstString GetInstanceVariableName(lldb::LanguageType language) override;
+
   static std::optional<std::string> GetCXXClassName(const CompilerType &type);
 
   // Type Completion

diff  --git a/lldb/source/Symbol/CompilerDeclContext.cpp b/lldb/source/Symbol/CompilerDeclContext.cpp
index 5b3049f74bbc8..36b9131055f83 100644
--- a/lldb/source/Symbol/CompilerDeclContext.cpp
+++ b/lldb/source/Symbol/CompilerDeclContext.cpp
@@ -34,13 +34,25 @@ ConstString CompilerDeclContext::GetScopeQualifiedName() const {
   return ConstString();
 }
 
-bool CompilerDeclContext::IsClassMethod(ConstString *language_object_name_ptr) {
+bool CompilerDeclContext::IsClassMethod() {
   if (IsValid())
-    return m_type_system->DeclContextIsClassMethod(m_opaque_decl_ctx,
-                                                   language_object_name_ptr);
+    return m_type_system->DeclContextIsClassMethod(m_opaque_decl_ctx);
   return false;
 }
 
+lldb::LanguageType CompilerDeclContext::GetLanguage() {
+  if (IsValid())
+    return m_type_system->DeclContextGetLanguage(m_opaque_decl_ctx);
+  return {};
+}
+
+ConstString
+CompilerDeclContext::GetInstanceVariableName(lldb::LanguageType language) {
+  if (IsValid())
+    return m_type_system->GetInstanceVariableName(language);
+  return {};
+}
+
 bool CompilerDeclContext::IsContainedInLookup(CompilerDeclContext other) const {
   if (!IsValid())
     return false;

diff  --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp
index 8453c1f116fef..5d4fb1cec6969 100644
--- a/lldb/source/Symbol/SymbolContext.cpp
+++ b/lldb/source/Symbol/SymbolContext.cpp
@@ -539,14 +539,16 @@ Block *SymbolContext::GetFunctionBlock() {
   return nullptr;
 }
 
-bool SymbolContext::GetFunctionMethodInfo(ConstString &language_object_name) {
-  Block *function_block = GetFunctionBlock();
-  if (function_block) {
-    CompilerDeclContext decl_ctx = function_block->GetDeclContext();
-    if (decl_ctx)
-      return decl_ctx.IsClassMethod(&language_object_name);
-  }
-  return false;
+ConstString SymbolContext::GetInstanceVariableName() {
+  if (Block *function_block = GetFunctionBlock())
+    if (CompilerDeclContext decl_ctx = function_block->GetDeclContext()) {
+      auto language = decl_ctx.GetLanguage();
+      if (language == eLanguageTypeUnknown)
+        language = GetLanguage();
+      return decl_ctx.GetInstanceVariableName(language);
+    }
+
+  return {};
 }
 
 void SymbolContext::SortTypeList(TypeMap &type_map, TypeList &type_list) const {

diff  --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp
index fa74b5d0ac2c6..f828539057cbf 100644
--- a/lldb/source/Target/StackFrame.cpp
+++ b/lldb/source/Target/StackFrame.cpp
@@ -567,23 +567,20 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
     // Check for direct ivars access which helps us with implicit access to
     // ivars using "this" or "self".
     GetSymbolContext(eSymbolContextFunction | eSymbolContextBlock);
-    ConstString method_object_name;
-    if (m_sc.GetFunctionMethodInfo(method_object_name)) {
-      if (method_object_name) {
-        var_sp = variable_list->FindVariable(method_object_name);
-        if (var_sp) {
-          separator_idx = 0;
-          if (Type *var_type = var_sp->GetType())
-            if (auto compiler_type = var_type->GetForwardCompilerType())
-              if (!compiler_type.IsPointerType())
-                var_expr_storage = ".";
-
-          if (var_expr_storage.empty())
-            var_expr_storage = "->";
-          var_expr_storage += var_expr;
-          var_expr = var_expr_storage;
-          synthetically_added_instance_object = true;
-        }
+    if (auto instance_var_name = m_sc.GetInstanceVariableName()) {
+      var_sp = variable_list->FindVariable(instance_var_name);
+      if (var_sp) {
+        separator_idx = 0;
+        if (Type *var_type = var_sp->GetType())
+          if (auto compiler_type = var_type->GetForwardCompilerType())
+            if (!compiler_type.IsPointerType())
+              var_expr_storage = ".";
+
+        if (var_expr_storage.empty())
+          var_expr_storage = "->";
+        var_expr_storage += var_expr;
+        var_expr = var_expr_storage;
+        synthetically_added_instance_object = true;
       }
     }
   }

diff  --git a/lldb/test/API/commands/frame/var/direct-ivar/objc/Makefile b/lldb/test/API/commands/frame/var/direct-ivar/objc/Makefile
index d0aadc1af9e58..c07887aa2f1df 100644
--- a/lldb/test/API/commands/frame/var/direct-ivar/objc/Makefile
+++ b/lldb/test/API/commands/frame/var/direct-ivar/objc/Makefile
@@ -1,2 +1,4 @@
 OBJC_SOURCES := main.m
+CFLAGS_EXTRAS := -fblocks -fobjc-arc
+LD_EXTRAS := -lobjc
 include Makefile.rules

diff  --git a/lldb/test/API/commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py b/lldb/test/API/commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py
index 395e014b2d88f..c93dc9ea1d9a0 100644
--- a/lldb/test/API/commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py
+++ b/lldb/test/API/commands/frame/var/direct-ivar/objc/TestFrameVarDirectIvarObjC.py
@@ -10,3 +10,11 @@ def test_objc_self(self):
         self.build()
         lldbutil.run_to_source_breakpoint(self, "// check self", lldb.SBFileSpec("main.m"))
         self.expect("frame variable _ivar", startstr="(int) _ivar = 30")
+
+    @skipUnlessDarwin
+    def test_objc_self_capture_idiom(self):
+        self.build()
+        lldbutil.run_to_source_breakpoint(self, "// check idiomatic self", lldb.SBFileSpec("main.m"))
+        self.expect("frame variable weakSelf", startstr="(Classic *) weakSelf = 0x")
+        self.expect("frame variable self", startstr="(Classic *) self = 0x")
+        self.expect("frame variable _ivar", startstr="(int) _ivar = 30")

diff  --git a/lldb/test/API/commands/frame/var/direct-ivar/objc/main.m b/lldb/test/API/commands/frame/var/direct-ivar/objc/main.m
index 3d5ef38dd3187..156622ffb1e63 100644
--- a/lldb/test/API/commands/frame/var/direct-ivar/objc/main.m
+++ b/lldb/test/API/commands/frame/var/direct-ivar/objc/main.m
@@ -7,13 +7,25 @@ @interface Classic : NSObject {
 @end
 
 @implementation Classic
-- (int)fun {
+- (void)fun {
   // check self
 }
+
+- (void)run {
+  __weak Classic *weakSelf = self;
+  ^{
+    Classic *self = weakSelf;
+    // check idiomatic self
+
+    // Use `self` to extend its lifetime (for lldb to inspect the variable).
+    [self copy];
+  }();
+}
 @end
 
 int main() {
   Classic *c = [Classic new];
   c->_ivar = 30;
   [c fun];
+  [c run];
 }


        


More information about the lldb-commits mailing list