void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, const CallEvent &CallTemplate, const EvalCallOptions &CallOpts) { // Make sure we have the most recent state attached to the call. ProgramStateRef State = Pred->getState(); CallEventRef<> Call = CallTemplate.cloneWithState(State); // Special-case trivial assignment operators. if (isTrivialObjectAssignment(*Call)) { performTrivialCopy(Bldr, Pred, *Call); return; } // Try to inline the call. // The origin expression here is just used as a kind of checksum; // this should still be safe even for CallEvents that don't come from exprs. const Expr *E = Call->getOriginExpr(); ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); if (InlinedFailedState) { // If we already tried once and failed, make sure we don't retry later. State = InlinedFailedState; } else { RuntimeDefinition RD = Call->getRuntimeDefinition(); const Decl *D = RD.getDecl(); if (shouldInlineCall(*Call, D, Pred, CallOpts)) { if (RD.mayHaveOtherDefinitions()) { AnalyzerOptions &Options = getAnalysisManager().options; // Explore with and without inlining the call. if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); return; } // Don't inline if we're not in any dynamic dispatch mode. if (Options.getIPAMode() != IPAK_DynamicDispatch) { conservativeEvalCall(*Call, Bldr, Pred, State); return; } } // We are not bifurcating and we do have a Decl, so just inline. if (inlineCall(*Call, D, Bldr, Pred, State)) return; } } // If we can't inline it, handle the return value and invalidate the regions. conservativeEvalCall(*Call, Bldr, Pred, State); }
void DanglingDelegateChecker::checkPostObjCMessage(const ObjCMethodCall &message, CheckerContext &context) const { // if the call was inlined, there is nothing else to do if (context.wasInlined) { return; } const ObjCMessageExpr *expr = message.getOriginExpr(); if (!expr) { assert(false); return; } // want an instance message to a non-null receiver const Expr *receiver = expr->getInstanceReceiver(); if (!receiver) { return; } if (isKnownToBeNil(message.getReceiverSVal(), context)) { // we are sure that the receiver is nil => abort mission return; } // retrieves the static facts on ivars const ObjCImplFacts *facts = getCurrentFacts(getCurrentTopClassInterface(context)); if (!facts) { return; } // First we try to detect the setting of an interesting property of self if (isObjCSelfExpr(receiver)) { const ObjCPropertyDecl *propDecl = matchObjCMessageWithPropertySetter(*expr); if (propDecl) { // To mitigate false positives, we verify only setters that have an unknown body. // (Setters with a known body are unfortunately not always inlined.) RuntimeDefinition runtimeDefinition = message.getRuntimeDefinition(); if (!runtimeDefinition.getDecl() || runtimeDefinition.getDecl()->isImplicit()) { verifyIvarDynamicStateAgainstStaticFacts(*expr, propDecl->getPropertyIvarDecl(), context); } // Next we deal with a possible assignment self.x = nil to prevent further warning const ObjCIvarDecl *ivarDecl = propDecl->getPropertyIvarDecl(); if (ivarDecl && facts->_ivarFactsMap.find(ivarDecl) != facts->_ivarFactsMap.end()) { SVal value = message.getArgSVal(0); if (isKnownToBeNil(value, context)) { // mark the corresponding ivar as cleared ProgramStateRef state = context.getState(); IvarDynamicState clearedStateForIvar(facts->_ivarFactsMap.at(ivarDecl)); state = state->set<IvarMap>(ivarDecl, clearedStateForIvar); context.addTransition(state); } } return; } } // What follows detects when we correctly clear the references inside an ivar // This is dual to FactFinder::VisitObjCMessageExpr StringRef selectorStr = expr->getSelector().getAsString(); // do we have a first argument equal to self? bool paramIsSelf = isObjCSelfExpr(getArgOfObjCMessageExpr(*expr, 0)); // is the receiver an interesting ivar? const ObjCIvarDecl *ivarDecl = matchIvarLValueExpression(*receiver); if (ivarDecl && facts->_ivarFactsMap.find(ivarDecl) != facts->_ivarFactsMap.end()) { // is this a release? if (selectorStr == "release" || selectorStr == "autorelease") { assert(!paramIsSelf); verifyIvarDynamicStateAgainstStaticFacts(*expr, ivarDecl, context); return; } // Prepare a new state to modify, associated with the receiver ProgramStateRef state = context.getState(); // Copy the previous state if present IvarDynamicState ivarState(state->get<IvarMap>(ivarDecl)); // is this a setter of an assign property? const ObjCPropertyDecl *propDecl = matchObjCMessageWithPropertySetter(*expr); if (propDecl) { if (propDecl->getSetterKind() != ObjCPropertyDecl::Assign) { return; } std::string propName = propDecl->getNameAsString(); if (!paramIsSelf) { // the property is now considered cleared ivarState._assignPropertyWasCleared.insert(propName); } else { // "unclear" the property since we just stored self again in it ivarState._assignPropertyWasCleared.erase(propName); } } else if (paramIsSelf && selectorStr.startswith("removeTarget:")) { ivarState._targetWasCleared = true; } else if (paramIsSelf && selectorStr.startswith("removeObserver:")) { ivarState._observerWasCleared = true; } else { // return to avoid transitioning to a new identical state return; } // write the new state state = state->set<IvarMap>(ivarDecl, ivarState); context.addTransition(state); return; } // TODO: is the receiver an interesting "observable singleton object"? // string receiverObjectName = matchObservableSingletonObject(receiver); // if (!receiverObjectName.empty()) { // // if (paramIsSelf && selectorStr.startswith("addObserver:")) { // // TODO // } // // } }