diff options
author | Anna Zaks <ganna@apple.com> | 2012-07-30 20:31:29 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2012-07-30 20:31:29 +0000 |
commit | 2d18419a7c8f9a2975d4ed74a202de6467308ad1 (patch) | |
tree | 464106fbed3a6d596f46a672e71ad907345084ea | |
parent | 3738db9445b60d6d7cab5367122308f5f2c302fc (diff) |
[analyzer] Very simple ObjC instance method inlining
- Retrieves the type of the object/receiver from the state.
- Binds self during stack setup.
- Only explores the path on which the method is inlined (no
bifurcation to explore the path on which the method is not inlined).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@160991 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h | 19 | ||||
-rw-r--r-- | include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h | 15 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/CallEvent.cpp | 50 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/ProgramState.cpp | 9 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Core/Store.cpp | 7 | ||||
-rw-r--r-- | test/Analysis/inlining/InlineObjCInstanceMethod.h | 19 | ||||
-rw-r--r-- | test/Analysis/inlining/InlineObjCInstanceMethod.m | 80 |
7 files changed, 156 insertions, 43 deletions
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h index 97612d60f1..74ac253d99 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -695,8 +695,6 @@ protected: virtual void getExtraInvalidatedRegions(RegionList &Regions) const; virtual QualType getDeclaredResultType() const; - ObjCMethodDecl *LookupClassMethodDefinition(Selector Sel, - ObjCInterfaceDecl *ClassDecl) const; public: virtual const ObjCMessageExpr *getOriginExpr() const { @@ -752,22 +750,7 @@ public: // TODO: We might want to only compute this once (or change the API for // getting the parameters). Currently, this gets called 3 times during // inlining. - virtual const Decl *getRuntimeDefinition() const { - const ObjCMessageExpr *E = getOriginExpr(); - assert(E); - - if (E->isInstanceMessage()) { - return 0; - } else { - // This is a class method. - // If we have type info for the receiver class, we are calling via - // class name. - if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) - return LookupClassMethodDefinition(E->getSelector(), IDecl); - } - - return 0; - } + virtual const Decl *getRuntimeDefinition() const; virtual param_iterator param_begin(bool UseDefinitionParams = false) const; virtual param_iterator param_end(bool UseDefinitionParams = false) const; diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h index 0d1579f368..4e92873860 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -56,6 +56,18 @@ template <typename T> struct ProgramStateTrait { } }; +/// \class Stores the dynamic type information. +/// Information about type of an object at runtime. This is used by dynamic +/// dispatch implementation. +class DynamicTypeInfo { + QualType T; + +public: + DynamicTypeInfo() : T(QualType()) {} + DynamicTypeInfo(QualType WithType) : T(WithType) {} + QualType getType() {return T;} +}; + /// \class ProgramState /// ProgramState - This class encapsulates: /// @@ -313,6 +325,9 @@ public: bool isTainted(SymbolRef Sym, TaintTagType Kind = TaintTagGeneric) const; bool isTainted(const MemRegion *Reg, TaintTagType Kind=TaintTagGeneric) const; + /// Get dynamic type information for a region. + DynamicTypeInfo getDynamicTypeInfo(const MemRegion *Reg) const; + //==---------------------------------------------------------------------==// // Accessing the Generic Data Map (GDM). //==---------------------------------------------------------------------==// diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index ab4b3832ee..6a5d0f0fa9 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -590,34 +590,34 @@ ObjCMessageKind ObjCMethodCall::getMessageKind() const { return static_cast<ObjCMessageKind>(Info.getInt()); } -// TODO: This implementation is copied from SemaExprObjC.cpp, needs to be -// factored into the ObjCInterfaceDecl. -ObjCMethodDecl *ObjCMethodCall::LookupClassMethodDefinition(Selector Sel, - ObjCInterfaceDecl *ClassDecl) const { - ObjCMethodDecl *Method = 0; - // Lookup in class and all superclasses. - while (ClassDecl && !Method) { - if (ObjCImplementationDecl *ImpDecl = ClassDecl->getImplementation()) - Method = ImpDecl->getClassMethod(Sel); - - // Look through local category implementations associated with the class. - if (!Method) - Method = ClassDecl->getCategoryClassMethod(Sel); - - // Before we give up, check if the selector is an instance method. - // But only in the root. This matches gcc's behavior and what the - // runtime expects. - if (!Method && !ClassDecl->getSuperClass()) { - Method = ClassDecl->lookupInstanceMethod(Sel); - // Look through local category implementations associated - // with the root class. - //if (!Method) - // Method = LookupPrivateInstanceMethod(Sel, ClassDecl); +const Decl *ObjCMethodCall::getRuntimeDefinition() const { + const ObjCMessageExpr *E = getOriginExpr(); + Selector Sel = E->getSelector(); + assert(E); + + if (E->isInstanceMessage()) { + const MemRegion *Receiver = getReceiverSVal().getAsRegion(); + DynamicTypeInfo TI = getState()->getDynamicTypeInfo(Receiver); + const ObjCObjectPointerType *T = + dyn_cast<ObjCObjectPointerType>(TI.getType().getTypePtr()); + if (!T) + return 0; + if (ObjCInterfaceDecl *IDecl = T->getInterfaceDecl()) { + // Find the method implementation. + return IDecl->lookupPrivateMethod(Sel); } - ClassDecl = ClassDecl->getSuperClass(); + } else { + // This is a class method. + // If we have type info for the receiver class, we are calling via + // class name. + if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) { + // Find/Return the method implementation. + return IDecl->lookupPrivateClassMethod(Sel); + } } - return Method; + + return 0; } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index 6adc18c67c..20f1e226b8 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -731,3 +731,12 @@ bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const { return Tainted; } + +DynamicTypeInfo ProgramState::getDynamicTypeInfo(const MemRegion *Reg) const { + if (const TypedRegion *TR = dyn_cast<TypedRegion>(Reg)) + return DynamicTypeInfo(TR->getLocationType()); + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg)) + return DynamicTypeInfo(SR->getSymbol() + ->getType(getStateManager().getContext())); + return DynamicTypeInfo(); +} diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 98d815f328..ed221c5419 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -53,6 +53,13 @@ StoreRef StoreManager::enterStackFrame(Store OldStore, Store = Bind(Store.getStore(), ThisRegion, ThisVal); } + if (const ObjCMethodCall *MCall = dyn_cast<ObjCMethodCall>(&Call)) { + SVal SelfVal = MCall->getReceiverSVal(); + const VarDecl *SelfDecl = LCtx->getAnalysisDeclContext()->getSelfDecl(); + Store = Bind(Store.getStore(), + svalBuilder.makeLoc(MRMgr.getVarRegion(SelfDecl, LCtx)), + SelfVal); + } return Store; } diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.h b/test/Analysis/inlining/InlineObjCInstanceMethod.h new file mode 100644 index 0000000000..18131c8673 --- /dev/null +++ b/test/Analysis/inlining/InlineObjCInstanceMethod.h @@ -0,0 +1,19 @@ + +// Define a public header for the ObjC methods that are "visible" externally +// and, thus, could be sub-classed. We should explore the path on which these +// are sub-classed with unknown class by not inlining them. + +typedef signed char BOOL; +typedef struct objc_class *Class; +typedef struct objc_object { + Class isa; +} *id; +@protocol NSObject - (BOOL)isEqual:(id)object; @end +@interface NSObject <NSObject> {} ++(id)alloc; +-(id)init; +-(id)autorelease; +-(id)copy; +- (Class)class; +-(id)retain; +@end diff --git a/test/Analysis/inlining/InlineObjCInstanceMethod.m b/test/Analysis/inlining/InlineObjCInstanceMethod.m new file mode 100644 index 0000000000..682d02aa15 --- /dev/null +++ b/test/Analysis/inlining/InlineObjCInstanceMethod.m @@ -0,0 +1,80 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic -verify %s + +#include "InlineObjCInstanceMethod.h" + +// Method is defined in the parent; called through self. +@interface MyParent : NSObject +- (int)getInt; +@end +@implementation MyParent +- (int)getInt { + return 0; +} +@end + +@interface MyClass : MyParent +@end +@implementation MyClass +- (int)testDynDispatchSelf { + int y = [self getInt]; + return 5/y; // expected-warning {{Division by zero}} +} + +// Method is called on inited object. ++ (int)testAllocInit { + MyClass *a = [[self alloc] init]; + return 5/[a getInt]; // todo +} + +// Method is called on inited object. ++ (int)testAllocInit2 { + MyClass *a = [[MyClass alloc] init]; + return 5/[a getInt]; // todo +} + +// Method is called on a parameter. ++ (int)testParam: (MyClass*) a { + return 5/[a getInt]; // expected-warning {{Division by zero}} +} + +// Method is called on a parameter of unnown type. ++ (int)testParamUnknownType: (id) a { + return 5/[a getInt]; // no warning +} + +@end + +// TODO: When method is inlined, the attribute reset should be visible. +@interface TestSettingAnAttributeInCallee : NSObject { + int _attribute; +} + - (void) method2; +@end + +@implementation TestSettingAnAttributeInCallee +- (int) method1 { + [self method2]; + return 5/_attribute; // expected-warning {{Division by zero}} +} + +- (void) method2 { + _attribute = 0; +} +@end + +@interface TestSettingAnAttributeInCaller : NSObject { + int _attribute; +} + - (int) method2; +@end + +@implementation TestSettingAnAttributeInCaller +- (void) method1 { + _attribute = 0; + [self method2]; +} + +- (int) method2 { + return 5/_attribute; // expected-warning {{Division by zero}} +} +@end
\ No newline at end of file |