예제 #1
0
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
//      }
//
//    }
  }