[lldb] Add support for calling objc_direct methods from LLDB's expression evaluator.
authorRaphael Isemann <teemperor@gmail.com>
Tue, 17 Dec 2019 08:48:54 +0000 (09:48 +0100)
committerRaphael Isemann <teemperor@gmail.com>
Tue, 17 Dec 2019 09:28:40 +0000 (10:28 +0100)
Summary:
D69991 introduced `__attribute__((objc_direct))` that allows directly calling methods without message passing.
This patch adds support for calling methods with this attribute to LLDB's expression evaluator.

The patch can be summarised in that LLDB just adds the same attribute to our module AST when we find a
method with `__attribute__((objc_direct))` in our debug information.

Reviewers: aprantl, shafik

Reviewed By: shafik

Subscribers: JDevlieghere, lldb-commits

Tags: #lldb

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

lldb/include/lldb/Symbol/ClangASTContext.h
lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/Makefile [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/TestObjCDirectMethods.py [new file with mode: 0644]
lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/main.m [new file with mode: 0644]
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp
lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.h
lldb/source/Symbol/ClangASTContext.cpp

index 9537f33..5d228e7 100644 (file)
@@ -845,7 +845,7 @@ public:
                         // (lldb::opaque_compiler_type_t type, "-[NString
                         // stringWithCString:]")
       const CompilerType &method_compiler_type, lldb::AccessType access,
-      bool is_artificial, bool is_variadic);
+      bool is_artificial, bool is_variadic, bool is_objc_direct_call);
 
   static bool SetHasExternalStorage(lldb::opaque_compiler_type_t type,
                                     bool has_extern);
diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/Makefile b/lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/Makefile
new file mode 100644 (file)
index 0000000..afecbf9
--- /dev/null
@@ -0,0 +1,4 @@
+OBJC_SOURCES := main.m
+LD_EXTRAS := -lobjc -framework Foundation
+
+include Makefile.rules
diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/TestObjCDirectMethods.py b/lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/TestObjCDirectMethods.py
new file mode 100644 (file)
index 0000000..f0152de
--- /dev/null
@@ -0,0 +1,5 @@
+from lldbsuite.test import lldbinline
+from lldbsuite.test import decorators
+
+lldbinline.MakeInlineTest(
+    __file__, globals(), [decorators.skipUnlessDarwin])
diff --git a/lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/main.m b/lldb/packages/Python/lldbsuite/test/lang/objc/objc_direct-methods/main.m
new file mode 100644 (file)
index 0000000..1a199ac
--- /dev/null
@@ -0,0 +1,79 @@
+#import <Foundation/Foundation.h>
+
+int side_effect = 0;
+
+NSString *str = @"some string";
+
+const char *directCallConflictingName() {
+  return "wrong function";
+}
+
+@interface Foo : NSObject {
+  int instance_var;
+}
+-(int) entryPoint;
+@end
+
+@implementation Foo
+-(int) entryPoint
+{
+  // Try calling directly with self. Same as in the main method otherwise.
+  return 0; //%self.expect("expr [self directCallNoArgs]", substrs=["called directCallNoArgs"])
+            //%self.expect("expr [self directCallArgs: 1111]", substrs=["= 2345"])
+            //%self.expect("expr side_effect = 0; [self directCallVoidReturn]; side_effect", substrs=["= 4321"])
+            //%self.expect("expr [self directCallNSStringArg: str]", substrs=['@"some string"'])
+            //%self.expect("expr [self directCallIdArg: (id)str]", substrs=['@"some string appendix"'])
+            //%self.expect("expr [self directCallConflictingName]", substrs=["correct function"])
+}
+
+// Declare several objc_direct functions we can test.
+-(const char *) directCallNoArgs __attribute__((objc_direct))
+{
+  return "called directCallNoArgs";
+}
+
+-(void) directCallVoidReturn __attribute__((objc_direct))
+{
+  side_effect = 4321;
+}
+
+-(int) directCallArgs:(int)i __attribute__((objc_direct))
+{
+  // Use the arg in some way to make sure that gets passed correctly.
+  return i + 1234;
+}
+
+-(NSString *) directCallNSStringArg:(NSString *)str __attribute__((objc_direct))
+{
+  return str;
+}
+
+-(NSString *) directCallIdArg:(id)param __attribute__((objc_direct))
+{
+  return [param stringByAppendingString:@" appendix"];
+}
+
+// We have another function with the same name above. Make sure this doesn't influence
+// what we call.
+-(const char *) directCallConflictingName  __attribute__((objc_direct))
+{
+  return "correct function";
+}
+@end
+
+int main()
+{
+  Foo *foo = [[Foo alloc] init];
+  [foo directCallNoArgs];
+  [foo directCallArgs: 1];
+  [foo directCallVoidReturn];
+  [foo directCallNSStringArg: str];
+  [foo directCallIdArg: (id)str];
+  [foo entryPoint];  //%self.expect("expr [foo directCallNoArgs]", substrs=["called directCallNoArgs"])
+                     //%self.expect("expr [foo directCallArgs: 1111]", substrs=["= 2345"])
+                     //%self.expect("expr side_effect = 0; [foo directCallVoidReturn]; side_effect", substrs=["= 4321"])
+                     //%self.expect("expr [foo directCallNSStringArg: str]", substrs=['@"some string"'])
+                     //%self.expect("expr [foo directCallIdArg: (id)str]", substrs=['@"some string appendix"'])
+                     //%self.expect("expr [foo directCallConflictingName]", substrs=["correct function"])
+  return 0;
+}
index 0e16153..bd97c68 100644 (file)
@@ -390,6 +390,10 @@ ParsedDWARFTypeAttributes::ParsedDWARFTypeAttributes(const DWARFDIE &die) {
       is_complete_objc_class = form_value.Signed();
       break;
 
+    case DW_AT_APPLE_objc_direct:
+      is_objc_direct_call = true;
+      break;
+
     case DW_AT_APPLE_runtime_class:
       class_language = (LanguageType)form_value.Signed();
       break;
@@ -958,7 +962,8 @@ TypeSP DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die,
           clang::ObjCMethodDecl *objc_method_decl =
               m_ast.AddMethodToObjCObjectType(
                   class_opaque_type, attrs.name.GetCString(), clang_type,
-                  attrs.accessibility, attrs.is_artificial, is_variadic);
+                  attrs.accessibility, attrs.is_artificial, is_variadic,
+                  attrs.is_objc_direct_call);
           type_handled = objc_method_decl != NULL;
           if (type_handled) {
             LinkDeclContextToDIE(objc_method_decl, die);
index c5b630e..4ad7572 100644 (file)
@@ -244,6 +244,7 @@ struct ParsedDWARFTypeAttributes {
   bool is_scoped_enum = false;
   bool is_vector = false;
   bool is_virtual = false;
+  bool is_objc_direct_call = false;
   bool exports_symbols = false;
   clang::StorageClass storage = clang::SC_None;
   const char *mangled_name = nullptr;
index 95bb455..3b6b0db 100644 (file)
@@ -7769,7 +7769,7 @@ clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType(
                       // (lldb::opaque_compiler_type_t type, "-[NString
                       // stringWithCString:]")
     const CompilerType &method_clang_type, lldb::AccessType access,
-    bool is_artificial, bool is_variadic) {
+    bool is_artificial, bool is_variadic, bool is_objc_direct_call) {
   if (!type || !method_clang_type.IsValid())
     return nullptr;
 
@@ -7875,6 +7875,18 @@ clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType(
         llvm::ArrayRef<clang::SourceLocation>());
   }
 
+  if (is_objc_direct_call) {
+    // Add a the objc_direct attribute to the declaration we generate that
+    // we generate a direct method call for this ObjCMethodDecl.
+    objc_method_decl->addAttr(
+        clang::ObjCDirectAttr::CreateImplicit(*ast, SourceLocation()));
+    // Usually Sema is creating implicit parameters (e.g., self) when it
+    // parses the method. We don't have a parsing Sema when we build our own
+    // AST here so we manually need to create these implicit parameters to
+    // make the direct call code generation happy.
+    objc_method_decl->createImplicitParams(*ast, class_interface_decl);
+  }
+
   class_interface_decl->addDecl(objc_method_decl);
 
 #ifdef LLDB_CONFIGURATION_DEBUG