void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<ObjCMethodCall> Msg = CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); // Handle the previsits checks. ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, *Msg, *this); ExplodedNodeSet dstGenericPrevisit; getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, *Msg, *this); // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currentBuilderContext); for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), DE = dstGenericPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; ProgramStateRef State = Pred->getState(); CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); if (UpdatedMsg->isInstanceMessage()) { SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = cast<DefinedOrUnknownSVal>(recVal); ProgramStateRef notNilState, nilState; llvm::tie(notNilState, nilState) = State->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). // Revisit once we have lazier constraints. if (nilState && !notNilState) { continue; } // Check if the "raise" message was sent. assert(notNilState); if (Msg->getSelector() == RaiseSel) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. Bldr.generateNode(currentStmt, Pred, State, true); continue; } // Generate a transition to non-Nil state. if (notNilState != State) Pred = Bldr.generateNode(currentStmt, Pred, notNilState); } } else { // Check for special class methods. if (const ObjCInterfaceDecl *Iface = Msg->getReceiverInterface()) { if (!NSExceptionII) { ASTContext &Ctx = getContext(); NSExceptionII = &Ctx.Idents.get("NSException"); } if (isSubclass(Iface, NSExceptionII)) { enum { NUM_RAISE_SELECTORS = 2 }; // Lazily create a cache of the selectors. if (!NSExceptionInstanceRaiseSelectors) { ASTContext &Ctx = getContext(); NSExceptionInstanceRaiseSelectors = new Selector[NUM_RAISE_SELECTORS]; SmallVector<IdentifierInfo*, NUM_RAISE_SELECTORS> II; unsigned idx = 0; // raise:format: II.push_back(&Ctx.Idents.get("raise")); II.push_back(&Ctx.Idents.get("format")); NSExceptionInstanceRaiseSelectors[idx++] = Ctx.Selectors.getSelector(II.size(), &II[0]); // raise:format:arguments: II.push_back(&Ctx.Idents.get("arguments")); NSExceptionInstanceRaiseSelectors[idx++] = Ctx.Selectors.getSelector(II.size(), &II[0]); } Selector S = Msg->getSelector(); bool RaisesException = false; for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) { if (S == NSExceptionInstanceRaiseSelectors[i]) { RaisesException = true; break; } } if (RaisesException) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. Bldr.generateNode(currentStmt, Pred, Pred->getState(), true); continue; } } } } // Evaluate the call. defaultEvalCall(Bldr, Pred, *UpdatedMsg); } ExplodedNodeSet dstPostvisit; getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, *Msg, *this); // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit, *Msg, *this); }
void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, ExplodedNode *Pred, ExplodedNodeSet &Dst) { CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<ObjCMethodCall> Msg = CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); // Handle the previsits checks. ExplodedNodeSet dstPrevisit; getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, *Msg, *this); ExplodedNodeSet dstGenericPrevisit; getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, *Msg, *this); // Proceed with evaluate the message expression. ExplodedNodeSet dstEval; StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx); for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), DE = dstGenericPrevisit.end(); DI != DE; ++DI) { ExplodedNode *Pred = *DI; ProgramStateRef State = Pred->getState(); CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); if (UpdatedMsg->isInstanceMessage()) { SVal recVal = UpdatedMsg->getReceiverSVal(); if (!recVal.isUndef()) { // Bifurcate the state into nil and non-nil ones. DefinedOrUnknownSVal receiverVal = recVal.castAs<DefinedOrUnknownSVal>(); ProgramStateRef notNilState, nilState; std::tie(notNilState, nilState) = State->assume(receiverVal); // There are three cases: can be nil or non-nil, must be nil, must be // non-nil. We ignore must be nil, and merge the rest two into non-nil. // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). // Revisit once we have lazier constraints. if (nilState && !notNilState) { continue; } // Check if the "raise" message was sent. assert(notNilState); if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. Bldr.generateSink(ME, Pred, State); continue; } // Generate a transition to non-Nil state. if (notNilState != State) { Pred = Bldr.generateNode(ME, Pred, notNilState); assert(Pred && "Should have cached out already!"); } } } else { // Check for special class methods that are known to not return // and that we should treat as a sink. if (ObjCNoRet.isImplicitNoReturn(ME)) { // If we raise an exception, for now treat it as a sink. // Eventually we will want to handle exceptions properly. Bldr.generateSink(ME, Pred, Pred->getState()); continue; } } defaultEvalCall(Bldr, Pred, *UpdatedMsg); } ExplodedNodeSet dstPostvisit; getCheckerManager().runCheckersForPostCall(dstPostvisit, dstEval, *Msg, *this); // Finally, perform the post-condition check of the ObjCMessageExpr and store // the created nodes in 'Dst'. getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit, *Msg, *this); }