[cfe-commits] r161681 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h lib/StaticAnalyzer/Core/CallEvent.cpp test/Analysis/inlining/DynDispatchBifurcate.m test/Analysis/inlining/InlineObjCInstanceMethod.h

Anna Zaks ganna at apple.com
Fri Aug 10 11:55:53 PDT 2012


Author: zaks
Date: Fri Aug 10 13:55:53 2012
New Revision: 161681

URL: http://llvm.org/viewvc/llvm-project?rev=161681&view=rev
Log:
[analyzer] Optimize dynamic dispatch bifurcation by detecting the cases
when we don't need to split.

In some cases we know that a method cannot have a different
implementation in a subclass:
 - the class is declared in the main file (private)
 - all the method declarations (including the ones coming from super
classes) are in the main file.

This can be improved further, but might be enough for the heuristic.
(When we are too aggressive splitting the state, efficiency suffers.
When we fail to split the state coverage might suffer.)

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
    cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
    cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m
    cfe/trunk/test/Analysis/inlining/InlineObjCInstanceMethod.h

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h?rev=161681&r1=161680&r2=161681&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h Fri Aug 10 13:55:53 2012
@@ -754,6 +754,10 @@
 
   virtual QualType getDeclaredResultType() const;
 
+  /// Check if the selector may have multiple definitions (may have overrides).
+  virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
+                                        Selector Sel) const;
+
 public:
   virtual const ObjCMessageExpr *getOriginExpr() const {
     return cast<ObjCMessageExpr>(CallEvent::getOriginExpr());

Modified: cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp?rev=161681&r1=161680&r2=161681&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp Fri Aug 10 13:55:53 2012
@@ -659,6 +659,59 @@
   return static_cast<ObjCMessageKind>(Info.getInt());
 }
 
+
+bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
+                                             Selector Sel) const {
+  assert(IDecl);
+  const SourceManager &SM =
+    getState()->getStateManager().getContext().getSourceManager();
+
+  // If the class interface is declared inside the main file, assume it is not
+  // subcassed. 
+  // TODO: It could actually be subclassed if the subclass is private as well.
+  // This is probably very rare.
+  SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
+  if (InterfLoc.isValid() && SM.isFromMainFile(InterfLoc))
+    return false;
+
+
+  // We assume that if the method is public (declared outside of main file) or
+  // has a parent which publicly declares the method, the method could be
+  // overridden in a subclass.
+
+  // Find the first declaration in the class hierarchy that declares
+  // the selector.
+  ObjCMethodDecl *D = 0;
+  while (true) {
+    D = IDecl->lookupMethod(Sel, true);
+
+    // Cannot find a public definition.
+    if (!D)
+      return false;
+
+    // If outside the main file,
+    if (D->getLocation().isValid() && !SM.isFromMainFile(D->getLocation()))
+      return true;
+
+    if (D->isOverriding()) {
+      // Search in the superclass on the next iteration.
+      IDecl = D->getClassInterface();
+      if (!IDecl)
+        return false;
+
+      IDecl = IDecl->getSuperClass();
+      if (!IDecl)
+        return false;
+
+      continue;
+    }
+
+    return false;
+  };
+
+  llvm_unreachable("The while loop should always terminate.");
+}
+
 RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
   const ObjCMessageExpr *E = getOriginExpr();
   assert(E);
@@ -686,8 +739,12 @@
 
     // Lookup the method implementation.
     if (ReceiverT)
-      if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
-        return RuntimeDefinition(IDecl->lookupPrivateMethod(Sel), Receiver);
+      if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
+        if (canBeOverridenInSubclass(IDecl, Sel))
+          return RuntimeDefinition(IDecl->lookupPrivateMethod(Sel), Receiver);
+        else
+          return RuntimeDefinition(IDecl->lookupPrivateMethod(Sel), 0);
+      }
 
   } else {
     // This is a class method.

Modified: cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m?rev=161681&r1=161680&r2=161681&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m (original)
+++ cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m Fri Aug 10 13:55:53 2012
@@ -1,19 +1,6 @@
 // RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic-bifurcate -verify %s
 
-typedef signed char BOOL;
-typedef struct objc_class *Class;
-typedef struct objc_object {
-    Class isa;
-} *id;
- at protocol NSObject  - (BOOL)isEqual:(id)object; @end
- at interface NSObject <NSObject> {}
-+(id)alloc;
--(id)init;
--(id)autorelease;
--(id)copy;
-- (Class)class;
--(id)retain;
- at end
+#include "InlineObjCInstanceMethod.h"
 
 @interface MyParent : NSObject
 - (int)getZero;
@@ -24,28 +11,92 @@
 }
 @end
 
+ at implementation PublicClass
+- (int)getZeroPublic {
+    return 0;
+}
+ at end
+
+ at interface MyClassWithPublicParent : PublicClass
+- (int)getZeroPublic;
+ at end
+ at implementation MyClassWithPublicParent
+- (int)getZeroPublic {
+    return 0;
+}
+ at end
+
+// Category overrides a public method.
+ at interface PublicSubClass (PrvateCat)
+  - (int) getZeroPublic;
+ at end
+ at implementation PublicSubClass (PrvateCat)
+- (int)getZeroPublic {
+    return 0;
+}
+ at end
+
+
 @interface MyClass : MyParent
 - (int)getZero;
 @end
 
-MyClass *getObj();
-
-// Check that we explore both paths - on one the calla are inlined and they are 
-// not inlined on the other.
-// In this case, p can be either the object of type MyParent* or MyClass*:
-// - If it's MyParent*, getZero returns 0.
-// - If it's MyClass*, getZero returns 1 and 'return 5/m' is reachable.
+// Since class is private, we assume that it cannot be subclassed.
+// False negative: this class is "privately subclassed". this is very rare 
+// in practice.
 @implementation MyClass
 + (int) testTypeFromParam:(MyParent*) p {
   int m = 0;
   int z = [p getZero];
   if (z)
-    return 5/m; // expected-warning {{Division by zero}}
+    return 5/m; // false negative
   return 5/[p getZero];// expected-warning {{Division by zero}}
 }
 
+// Here only one definition is possible, since the declaration is not visible 
+// from outside. 
++ (int) testTypeFromParamPrivateChild:(MyClass*) c {
+  int m = 0;
+  int z = [c getZero]; // MyClass overrides getZero to return '1'.
+  if (z)
+    return 5/m; // expected-warning {{Division by zero}}
+  return 5/[c getZero];//no warning
+}
+
 - (int)getZero {
     return 1;
 }
+ at end
 
- at end
\ No newline at end of file
+// The class is prvate and is not subclassed.
+int testCallToPublicAPIInParent(MyClassWithPublicParent *p) {
+  int m = 0;
+  int z = [p getZeroPublic];
+  if (z)
+    return 5/m; // no warning
+  return 5/[p getZeroPublic];// expected-warning {{Division by zero}}  
+}
+
+// When the called method is public (due to it being defined outside of main file),
+// split the path and analyze both branches.
+// In this case, p can be either the object of type MyParent* or MyClass*:
+// - If it's MyParent*, getZero returns 0.
+// - If it's MyClass*, getZero returns 1 and 'return 5/m' is reachable.
+// Declaration is provate, but p can be a subclass (MyClass*).
+int testCallToPublicAPI(PublicClass *p) {
+  int m = 0;
+  int z = [p getZeroPublic];
+  if (z)
+    return 5/m; // expected-warning {{Division by zero}}
+  return 5/[p getZeroPublic];// expected-warning {{Division by zero}}  
+}
+
+// Even though the method is privately declared in the category, the parent 
+// declares the method as public. Assume the instance can be subclassed.
+int testCallToPublicAPICat(PublicSubClass *p) {
+  int m = 0;
+  int z = [p getZeroPublic];
+  if (z)
+    return 5/m; // expected-warning {{Division by zero}}
+  return 5/[p getZeroPublic];// expected-warning {{Division by zero}}  
+}

Modified: cfe/trunk/test/Analysis/inlining/InlineObjCInstanceMethod.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/InlineObjCInstanceMethod.h?rev=161681&r1=161680&r2=161681&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/inlining/InlineObjCInstanceMethod.h (original)
+++ cfe/trunk/test/Analysis/inlining/InlineObjCInstanceMethod.h Fri Aug 10 13:55:53 2012
@@ -17,3 +17,10 @@
 - (Class)class;
 -(id)retain;
 @end
+
+ at interface PublicClass : NSObject
+- (int)getZeroPublic;
+ at end
+
+ at interface PublicSubClass : PublicClass
+ at end





More information about the cfe-commits mailing list