Example #1
0
void DataFlowWalker::process(ExpressionPtr e, bool doAccessChains) {
  if ((e->getContext() & (Expression::AssignmentLHS|Expression::OprLValue)) ||
      (!doAccessChains && e->hasContext(Expression::AccessContext))) {
    return;
  }

  switch (e->getKindOf()) {
    case Expression::KindOfListAssignment:
      processAccessChainLA(static_pointer_cast<ListAssignment>(e));
      processAccess(e);
      break;
    case Expression::KindOfArrayElementExpression:
    case Expression::KindOfObjectPropertyExpression:
      if (!e->hasContext(Expression::AccessContext)) {
        processAccessChain(e);
      }
      // fall through
    case Expression::KindOfObjectMethodExpression:
    case Expression::KindOfDynamicFunctionCall:
    case Expression::KindOfSimpleFunctionCall:
    case Expression::KindOfNewObjectExpression:
    case Expression::KindOfIncludeExpression:
    case Expression::KindOfSimpleVariable:
    case Expression::KindOfDynamicVariable:
    case Expression::KindOfStaticMemberExpression:
    case Expression::KindOfConstantExpression:
      processAccess(e);
      break;
    case Expression::KindOfAssignmentExpression:
    case Expression::KindOfBinaryOpExpression:
    case Expression::KindOfUnaryOpExpression: {
      ExpressionPtr var = e->getStoreVariable();
      if (var && var->getContext() & (Expression::AssignmentLHS|
                                      Expression::OprLValue)) {
        processAccessChain(var);
        processAccess(var);
      }
      // fall through
    }
    default:
      processAccess(e);
      break;
  }
}
Example #2
0
static bool isReadOnlyAccess(ExpressionPtr e) {
  if (e->getContext() & (Expression::UnsetContext|
                         Expression::RefValue|
                         Expression::LValue)) {
    return false;
  }
  switch (e->getKindOf()) {
  case Expression::KindOfConstantExpression:
  case Expression::KindOfSimpleVariable:
  case Expression::KindOfArrayElementExpression:
  case Expression::KindOfDynamicVariable:
    return true;
  default:
    return false;
  }
}
Example #3
0
static ExpressionPtr cloneForInlineRecur(InlineCloneInfo &info,
                                         ExpressionPtr exp,
                                         const std::string &prefix,
                                         AnalysisResultConstPtr ar,
                                         FunctionScopePtr scope) {
  exp->getOriginalScope(); // make sure to cache the original scope
  exp->setBlockScope(scope);
  for (int i = 0, n = exp->getKidCount(); i < n; i++) {
    if (ExpressionPtr k = exp->getNthExpr(i)) {
      exp->setNthKid(i, cloneForInlineRecur(info, k, prefix, ar, scope));
    }
  }
  StaticClassName *scn = dynamic_cast<StaticClassName*>(exp.get());
  if (scn && scn->isStatic() && !info.staticClass.empty()) {
    scn->resolveStatic(info.staticClass);
  }
  switch (exp->getKindOf()) {
  case Expression::KindOfSimpleVariable:
    {
      SimpleVariablePtr sv(dynamic_pointer_cast<SimpleVariable>(exp));
      if (sv->isSuperGlobal()) break;
      string name;
      if (sv->isThis()) {
        if (!info.callWithThis) {
          if (!sv->hasContext(Expression::ObjectContext)) {
            exp = sv->makeConstant(ar, "null");
          } else {
            // This will produce the wrong error
            // we really want a "throw_fatal" ast node.
            exp = sv->makeConstant(ar, "null");
          }
          break;
        }
        if (info.localThis.empty()) break;
        name = info.localThis;
      } else {
        name = prefix + sv->getName();
      }
      SimpleVariablePtr rep(new SimpleVariable(
                              exp->getScope(), exp->getLocation(), name));
      rep->copyContext(sv);
      rep->updateSymbol(SimpleVariablePtr());
      rep->getSymbol()->setHidden();
      // Conservatively set flags to prevent
      // the alias manager from getting confused.
      // On the next pass, it will correct the values,
      // and optimize appropriately.
      rep->getSymbol()->setUsed();
      rep->getSymbol()->setReferenced();
      if (exp->getContext() & (Expression::LValue|
                               Expression::RefValue|
                               Expression::RefParameter)) {
        info.sepm[name] = rep;
      }
      exp = rep;
    }
    break;
  case Expression::KindOfObjectMethodExpression:
  {
    FunctionCallPtr call(
      static_pointer_cast<FunctionCall>(exp));
    if (call->getFuncScope() == info.func) {
      call->setNoInline();
    }
    break;
  }
  case Expression::KindOfSimpleFunctionCall:
    {
      SimpleFunctionCallPtr call(static_pointer_cast<SimpleFunctionCall>(exp));
      call->addLateDependencies(ar);
      call->setLocalThis(info.localThis);
      if (call->getFuncScope() == info.func) {
        call->setNoInline();
      }
    }
  default:
    break;
  }
  return exp;
}
Example #4
0
/*
  The classical use/def isnt quite enough here. There are two unusual
  issues:
   - the ref/non-ref issue.
     An assignment to a var which is referenced doesnt end its
     lifetime. In fact it could just be a "use" of the var. But it
     also counts as a def.
   - the destructor issue.
     variables which might need to be destroyed later are technically
     alive, but dont interfere with any other variables in the same state.
     They /do/ interfere with any truly "live" variables, however.
     These are "dying".

   So we end up defining, use, kill, def and dying.

  use : a read of the variable, an ordinary assignment if it could
        be referenced

  kill : an unset, a ref assignment, or, for non referenced vars, any assignment

  def : ref or normal assignment

  dying : a variable whose destructor is (partially/locally) anticipated.
*/
void LiveDict::updateAccess(ExpressionPtr e) {
  int cls = e->getExprClass();
  if (cls & Expression::Store) {
    /*
      Handled when we see the lhs
    */
    return;
  }

  int eid = e->getCanonID();
  int context = e->getContext();
  bool unset = false;
  bool store = false;
  if (context & Expression::LValue && context & Expression::UnsetContext) {
    unset = true;
  } else if (context & Expression::AssignmentLHS) {
    store = true;
  }

  if (e->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr sv(static_pointer_cast<SimpleVariable>(e));
    bool use = false, kill = false, def = false;
    Symbol *sym = sv->getSymbol();
    bool isReferenced =
      e->isReferencedValid() ?
        e->isReferenced() :
        sym && sym->isReferenced();
    bool isNeeded =
      e->isNeededValid() ?
        e->isNeeded() :
        sym && sym->isNeeded();
    if (unset) {
      kill = true;
    } else if (store) {
      if (context & Expression::RefAssignmentLHS ||
          (!m_am.hasWildRefs() && !isReferenced)) {
        kill = true;
      }
      def = true;
    } else if ((context & Expression::Declaration) == Expression::Declaration) {
      // a global declaration
      def = kill = true;
    } else if (context & (Expression::LValue|
                          Expression::RefValue|
                          Expression::DeepReference|
                          Expression::UnsetContext|
                          Expression::OprLValue)) {
      use = def = true;
    } else {
      use = true;
    }
    if (kill && (!sym || isNeeded || isReferenced) &&
        !BitOps::get_bit(eid, m_altered) &&
        !BitOps::get_bit(eid, m_available)) {
      BitOps::set_bit(eid, m_dying, true);
    }
    if (use &&
        !BitOps::get_bit(eid, m_altered) &&
        !BitOps::get_bit(eid, m_available)) {
      BitOps::set_bit(eid, m_anticipated, true);
      e->setAnticipated();
    }
    if (kill) {
      BitOps::set_bit(eid, m_altered, true);
      BitOps::set_bit(eid, m_available, def);
    } else if (def) {
      BitOps::set_bit(eid, m_available, true);
    }

    if (!m_am.couldBeAliased(sv)) {
      return;
    }
  } else if (!e->is(Expression::KindOfDynamicVariable) &&
             (unset || (context & Expression::RefAssignmentLHS))) {
    // An unset, or a reference assignment to anything other
    // than a simple or dynamic variable can never affect a simple
    // variable (outside of pseudoMain).
    return;
  }

  if (store || cls & (Expression::Load|Expression::Call)) {
    bool mod =
      store ||
      (cls & Expression::Load &&
       e->getContext() & (Expression::LValue|
                          Expression::RefValue|
                          Expression::UnsetContext|
                          Expression::DeepReference|
                          Expression::OprLValue));

    ExpressionPtr cur = m_refs, prev;
    bool isLoad;
    int depth = 0, effects = 0;
    while (cur) {
      ExpressionPtr next = cur->getCanonLVal();
      int cid = cur->getCanonID();
      if (cid != eid &&
          m_am.checkAnyInterf(e, cur, isLoad, depth, effects) !=
          AliasManager::DisjointAccess) {
        if (mod) {
          BitOps::set_bit(cid, m_available, true);
        }
        if (!BitOps::get_bit(cid, m_altered) &&
            !BitOps::get_bit(cid, m_available)) {
          BitOps::set_bit(cid, m_anticipated, true);
        }
        if (!prev) {
          m_refs = next;
        } else {
          prev->setCanonPtr(next);
        }
      } else {
        prev = cur;
      }
      cur = next;
    }
  }
}
void SimpleFunctionCall::analyzeProgram(AnalysisResultPtr ar) {
  if (m_className.empty()) {
    addUserFunction(ar, m_name);
  } else if (m_className != "parent") {
    addUserClass(ar, m_className);
  } else {
    m_parentClass = true;
  }

  if (ar->getPhase() == AnalysisResult::AnalyzeInclude) {

    CHECK_HOOK(onSimpleFunctionCallAnalyzeInclude);

    ConstructPtr self = shared_from_this();

    // We need to know the name of the constant so that we can associate it
    // with this file before we do type inference.
    if (m_className.empty() && m_type == DefineFunction) {
      ScalarExpressionPtr name =
        dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
      string varName;
      if (name) {
        varName = name->getIdentifier();
        if (!varName.empty()) {
          ar->getFileScope()->declareConstant(ar, varName);
        }
      }
      // handling define("CONSTANT", ...);
      if (m_params && m_params->getCount() >= 2) {
        ScalarExpressionPtr name =
          dynamic_pointer_cast<ScalarExpression>((*m_params)[0]);
        string varName;
        if (name) {
          varName = name->getIdentifier();
          if (!varName.empty()) {
            ExpressionPtr value = (*m_params)[1];
            ConstantTablePtr constants =
              ar->findConstantDeclarer(varName)->getConstants();
            if (constants != ar->getConstants()) {
              constants->add(varName, NEW_TYPE(Some), value, ar, self);

              if (name->hasHphpNote("Dynamic")) {
                constants->setDynamic(ar, varName);
              }
            }
          }
        }
      }
    }

    if (m_type == UnserializeFunction) {
      ar->forceClassVariants();
    }
  }

  if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
    // Look up the corresponding FunctionScope and ClassScope
    // for this function call
    {
      FunctionScopePtr func;
      ClassScopePtr cls;
      if (m_className.empty()) {
        func = ar->findFunction(m_name);
      } else {
        cls = ar->resolveClass(m_className);
        if (cls) {
          if (m_name == "__construct") {
            func = cls->findConstructor(ar, true);
          } else {
            func = cls->findFunction(ar, m_name, true, true);
          }
        }
      }
      if (func && !func->isRedeclaring()) {
        if (m_funcScope != func) {
          m_funcScope = func;
          Construct::recomputeEffects();
        }
      }
      if (cls && !cls->isRedeclaring())
        m_classScope = cls;
    }
    // check for dynamic constant and volatile function/class
    if (m_className.empty() &&
      (m_type == DefinedFunction ||
       m_type == FunctionExistsFunction ||
       m_type == ClassExistsFunction ||
       m_type == InterfaceExistsFunction) &&
      m_params && m_params->getCount() >= 1) {
      ExpressionPtr value = (*m_params)[0];
      if (value->isScalar()) {
        ScalarExpressionPtr name =
          dynamic_pointer_cast<ScalarExpression>(value);
        if (name && name->isLiteralString()) {
          string symbol = name->getLiteralString();
          switch (m_type) {
          case DefinedFunction: {
            ConstantTablePtr constants = ar->getConstants();
            if (!constants->isPresent(symbol)) {
              // user constant
              BlockScopePtr block = ar->findConstantDeclarer(symbol);
              if (block) { // found the constant
                constants = block->getConstants();
                // set to be dynamic
                constants->setDynamic(ar, symbol);
              }
            }
            break;
          }
          case FunctionExistsFunction: {
            FunctionScopePtr func = ar->findFunction(Util::toLower(symbol));
            if (func && func->isUserFunction()) {
              func->setVolatile();
            }
            break;
          }
          case InterfaceExistsFunction:
          case ClassExistsFunction: {
            ClassScopePtr cls = ar->findClass(Util::toLower(symbol));
            if (cls && cls->isUserClass()) {
              cls->setVolatile();
            }
            break;
          }
          default:
            ASSERT(false);
          }
        }
      }
    }
  }
  if (m_params) {
    if (ar->getPhase() == AnalysisResult::AnalyzeAll) {
      if (m_funcScope) {
        ExpressionList &params = *m_params;
        int mpc = m_funcScope->getMaxParamCount();
        for (int i = params.getCount(); i--; ) {
          ExpressionPtr p = params[i];
          if (i < mpc ? m_funcScope->isRefParam(i) :
              m_funcScope->isReferenceVariableArgument()) {
            p->setContext(Expression::RefValue);
          } else if (!(p->getContext() & Expression::RefParameter)) {
            p->clearContext(Expression::RefValue);
          }
        }
      } else {
        FunctionScopePtr func = ar->findFunction(m_name);
        if (func && func->isRedeclaring()) {
          FunctionScope::RefParamInfoPtr info =
            FunctionScope::GetRefParamInfo(m_name);
          if (info) {
            for (int i = m_params->getCount(); i--; ) {
              if (info->isRefParam(i)) {
                m_params->markParam(i, canInvokeFewArgs());
              }
            }
          }
        } else {
          m_params->markParams(false);
        }
      }
    }

    m_params->analyzeProgram(ar);
  }
}
Example #6
0
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp,
                                   ExpressionListPtr params, bool &valid) {
  if (!params) {
    if (m_minParam > 0) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt);
      }
      valid = false;
      setDynamic();
    }
    return 0;
  }

  int ret = 0;
  if (params->getCount() < m_minParam) {
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(CodeError::TooFewArgument, exp, m_stmt);
    }
    valid = false;
    setDynamic();
  }
  if (params->getCount() > m_maxParam) {
    if (isVariableArgument()) {
      ret = params->getCount() - m_maxParam;
    } else {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(CodeError::TooManyArgument, exp, m_stmt);
      }
      valid = false;
      setDynamic();
    }
  }

  bool canSetParamType = isUserFunction() && !m_overriding;
  for (int i = 0; i < params->getCount(); i++) {
    ExpressionPtr param = (*params)[i];
    if (valid && param->hasContext(Expression::InvokeArgument)) {
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::NoRefWrapper);
    }
    TypePtr expType;
    if (!canSetParamType && i < m_maxParam) {
      expType = param->inferAndCheck(ar, getParamType(i), false);
    } else {
      expType = param->inferAndCheck(ar, NEW_TYPE(Some), false);
    }
    bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument());
    if ((i < m_maxParam && isRefParam(i)) || isRefVararg) {
      param->setContext(Expression::LValue);
      param->setContext(Expression::RefValue);
      param->inferAndCheck(ar, Type::Variant, true);
    } else if (!(param->getContext() & Expression::RefParameter)) {
      param->clearContext(Expression::LValue);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::NoRefWrapper);
    }
    if (i < m_maxParam) {
      if (m_paramTypeSpecs[i] && ar->isFirstPass()) {
        if (!Type::Inferred(ar, expType, m_paramTypeSpecs[i])) {
          const char *file = m_stmt->getLocation()->file;
          Logger::Error("%s: parameter %d of %s requires %s, called with %s",
                        file, i, m_name.c_str(),
                        m_paramTypeSpecs[i]->toString().c_str(),
                        expType->toString().c_str());
          ar->getCodeError()->record(CodeError::BadArgumentType, m_stmt);
        }
      }
      TypePtr paramType = getParamType(i);
      if (canSetParamType) {
        paramType = setParamType(ar, i, expType);
      }
      if (!Type::IsLegalCast(ar, expType, paramType) &&
          paramType->isNonConvertibleType()) {
        param->inferAndCheck(ar, paramType, true);
      }
      param->setExpectedType(paramType);
    }
    // we do a best-effort check for bad pass-by-reference and do not report
    // error for some vararg case (e.g., array_multisort can have either ref
    // or value for the same vararg).
    if (!isRefVararg || !isMixedVariableArgument()) {
      Expression::checkPassByReference(ar, param);
    }
  }
  return ret;
}
Example #7
0
void ExprDict::updateAccess(ExpressionPtr e) {
  int cls = e->getExprClass();
  int eid = e->getCanonID();
  e->clearAnticipated();
  e->clearAvailable();

  // bail on non-canonical expressions
  if (!isCanonicalStructure(eid)) {
    // but record we saw a type assertion belonging to this block
    m_avlTypeAsserts.push_back(eid);
    return;
  }

  if (m_anticipated &&
      (cls & Expression::Update ?
       !BitOps::get_bit(eid, m_altered) : !e->getLocalEffects())) {
    /*
      Anticipated can be computed bottom up as we go. But note that we
      only know altered for Load/Store expressions.
    */
    int i = e->getKidCount();
    while (true) {
      if (!i--) {
        e->setAnticipated();
        if (!e->hasContext(Expression::AssignmentLHS)) {
          setStructureOps(eid, m_anticipated, true);
        }
        break;
      }
      if (ExpressionPtr k = e->getNthExpr(i)) {
        if (!isCanonicalStructure(k->getCanonID())) continue;
        if (!k->isAnticipated()) {
          break;
        }
      }
    }
  }

  if (m_available) {
    /*
      Available has to be computed optimistically, because we dont yet
      know what is going to be altered between here and the end of the block
      So keep a list of the potentially-available accesses (avlAccess), and
      for each id, the last potentially-available expression (avlExpr).
      For each modifying expression that we process, we remove expressions
      from avlAccess, and at the end, we build up the available expressions
      bottom up.
    */
    if ((cls & (Expression::Store|Expression::Call)) ||
        (cls & Expression::Load &&
         e->getContext() & (Expression::LValue|
                            Expression::RefValue|
                            Expression::UnsetContext|
                            Expression::DeepReference))) {
      bool isLoad;
      int depth = 0, effects = 0;
      for (int i = 0, n = m_avlAccess.size(); i < n; ) {
        ExpressionRawPtr a = m_avlAccess[i];
        if (m_am.checkAnyInterf(e, a, isLoad, depth, effects) !=
            AliasManager::DisjointAccess) {
          int aid = a->getCanonID();
          assert(isCanonicalStructure(aid));
          if (eid != aid || cls == Expression::Load) {
            BitOps::set_bit(aid, m_altered, true);
          }
          if (!(cls & Expression::Store) ||
              a != e->getStoreVariable()) {
            a->clearAvailable();
            m_avlAccess[i] = m_avlAccess[--n];
            m_avlAccess.resize(n);
            continue;
          }
        }
        i++;
      }
    }
    if (cls & Expression::Update ||
        !e->getContainedEffects()) {
      int i = e->getKidCount();
      while (true) {
        if (!i--) {
          e->setAvailable();
          if (cls & Expression::Update) {
            m_avlAccess.push_back(e);
          }
          m_avlExpr[eid] = e;
          break;
        }
        if (ExpressionPtr k = e->getNthExpr(i)) {
          if (!isCanonicalStructure(k->getCanonID())) continue;
          if (!k->isAvailable()) {
            break;
          }
        }
      }
    }
  }

  if ((cls & (Expression::Store|Expression::Call)) ||
      (cls & Expression::Load &&
       e->getContext() & (Expression::LValue|
                          Expression::RefValue|
                          Expression::UnsetContext|
                          Expression::DeepReference))) {
    ExpressionPtr cur = m_active, prev;
    bool isLoad;
    int depth = 0, effects = 0;
    while (cur) {
      ExpressionPtr next = cur->getCanonLVal();
      int cid = cur->getCanonID();
      assert(isCanonicalStructure(cid));
      if ((cid != eid || cls == Expression::Load) &&
          (BitOps::get_bit(cid, m_altered) ||
           m_am.checkAnyInterf(e, cur, isLoad, depth, effects) !=
           AliasManager::DisjointAccess)) {
        BitOps::set_bit(cid, m_altered, true);
        if (!prev) {
          m_active = next;
        } else {
          prev->setCanonPtr(next);
        }
      } else {
        prev = cur;
      }
      cur = next;
    }
  }
}
Example #8
0
void RefDict::updateAccess(ExpressionPtr e) {
  always_assert(!e->getScope()->inPseudoMain());

  int eid     = e->getCanonID();
  int context = e->getContext();

  if (first_pass) {
    if (!e->is(Expression::KindOfSimpleVariable) &&
        !e->is(Expression::KindOfDynamicVariable)) return;

    e->clearAvailable();
    e->clearReferencedValid();
    e->clearReferenced();

    SimpleVariablePtr ptr(dynamic_pointer_cast<SimpleVariable>(e));
    if (ptr && (ptr->isSuperGlobal() || ptr->isThis())) return;

    if (e->is(Expression::KindOfSimpleVariable)) {
      if (BitOps::get_bit(eid, m_referenced)) {
        e->setReferenced();
      } else if (!BitOps::get_bit(eid, m_killed)) {
        // use as a temp place holder
        e->setAvailable();
      }
    }
  }

  // let the first pass information propagate for both passes, since
  // we need it in both contexts
  if (context & Expression::RefAssignmentLHS ||
      context & Expression::RefValue ||
      context & Expression::RefParameter ||
      ((context & Expression::Declaration) == Expression::Declaration)) {
    if (e->is(Expression::KindOfSimpleVariable)) {
      BitOps::set_bit(eid, m_referenced, true);
      BitOps::set_bit(eid, m_killed, false);
    } else {
      // for dynamic variables, we must assume the worst
      BitOps::set(size(), m_referenced, -1);
      BitOps::set(size(), m_killed, 0);
    }
  } else if (e->is(Expression::KindOfSimpleVariable) &&
             context & Expression::LValue &&
             context & Expression::UnsetContext) {
    BitOps::set_bit(eid, m_referenced, false);
    BitOps::set_bit(eid, m_killed, true);
  }

  if (first_pass) return;

  // now we're on the second pass

  if (context & Expression::AssignmentLHS ||
      context & Expression::OprLValue) {
    // we dealt with this node as a store expression
    return;
  }

  int cls = e->getExprClass();

  bool isRhsNeeded = false;
  bool canKill     = false;

  ExpressionPtr lhs;
  ExpressionPtr rhs;

  if (cls & Expression::Store) {
    // we care about two cases here
    switch (e->getKindOf()) {
      case Expression::KindOfAssignmentExpression:
        // $x = ...
        {
          AssignmentExpressionPtr assign(
              static_pointer_cast<AssignmentExpression>(e));
          lhs = assign->getVariable();
          rhs = assign->getValue();
          isRhsNeeded = Expression::CheckNeededRHS(rhs);
          canKill = true;
        }
        break;
      case Expression::KindOfBinaryOpExpression:
        // $x += ...
        {
          BinaryOpExpressionPtr binop(
              static_pointer_cast<BinaryOpExpression>(e));
          if (binop->getOp() == T_PLUS_EQUAL) {
            lhs = binop->getExp1();
            rhs = binop->getExp2();
            isRhsNeeded = Expression::CheckNeededRHS(rhs);
          }
        }
        break;
      default:
        break;
    }
  }

  bool isLhsSimpleVar = false;
  bool isLhsDynamic   = false;
  bool isRefd         = false;
  if (lhs) {
    isLhsSimpleVar = lhs->is(Expression::KindOfSimpleVariable);

    // TODO: can a variable only be simple or dynamic?
    // If so, this is un-necessary
    isLhsDynamic   = lhs->is(Expression::KindOfDynamicVariable);

    if (isLhsSimpleVar) {
      // clean up the LHS AST
      lhs->clearAvailable();
      lhs->clearNeeded();
      lhs->clearNeededValid();

      if (BitOps::get_bit(lhs->getCanonID(), m_obj)) {
        lhs->setNeeded();
        lhs->setNeededValid();
      } else if (!BitOps::get_bit(lhs->getCanonID(), m_noobj)) {
        lhs->setAvailable();
      }
    }

    if (lhs->isReferencedValid() && lhs->isReferenced() && isRhsNeeded) {
      // could we possibly have modified another referenced variable?
      isRefd = true;
    }

    if (isLhsSimpleVar && isRhsNeeded) {
      // we see this case:
      // $x = new ...
      // so we mark $x as being needed
      BitOps::set_bit(lhs->getCanonID(), m_obj, true);
      BitOps::set_bit(lhs->getCanonID(), m_noobj, false);
    } else if (isLhsSimpleVar && canKill && !isRhsNeeded) {
      // we saw an assignment that was of the form
      // $x = <primitive>
      // we can now set $x to be not an object
      BitOps::set_bit(lhs->getCanonID(), m_obj, false);
      BitOps::set_bit(lhs->getCanonID(), m_noobj, true);
    }
  }

  if (isLhsDynamic && isRhsNeeded) {
    // in this case, we must set EVERY variable to contain an object
    BitOps::set(size(), m_obj, -1 /* true for each bit */);
    BitOps::set(size(), m_noobj, 0 /* false for each bit */);

    // we're done, since it can be no worse (no more conservative) than this
    return;
  }

  // do we see a load which could cause the value of this expr to be changed?
  // for example:
  // function foo(&$x) { $x = 10; }
  // $x = 30;
  // foo($x); /* <-- this is what we care about */
  if ((cls & (Expression::Load|Expression::Call)) &&
      (context & (Expression::RefValue|Expression::DeepReference))) {
    isRefd = true;
  }

  // we want to propagate this information to other simple vars we see
  if (e->is(Expression::KindOfSimpleVariable)) {
    // clean up the AST
    e->clearAvailable();
    e->clearNeeded();
    e->clearNeededValid();

    SimpleVariablePtr svp(static_pointer_cast<SimpleVariable>(e));
    if (svp->isSuperGlobal() || svp->isThis()) return;

    // update the AST to *before* the modification
    if (BitOps::get_bit(eid, m_obj)) {
      e->setNeeded();
      e->setNeededValid();
    } else if (!BitOps::get_bit(eid, m_noobj)) {
      // use as a temp place holder
      e->setAvailable();
    }

    if (context & Expression::LValue &&
        context & Expression::UnsetContext) {
      always_assert(!isRefd);
      // unset($x);
      BitOps::set_bit(eid, m_obj, false);
      BitOps::set_bit(eid, m_noobj, true);
    } else if (isRefd ||
        ((context & Expression::Declaration) == Expression::Declaration)) {
      // if a simple variable has isRefd, then we need to mark it
      // as potentially containing an object.
      // also, if the simple variable is in global context
      // then we also mark it as potentially containing an object
      BitOps::set_bit(eid, m_obj, true);
      BitOps::set_bit(eid, m_noobj, false);
    }
  }

  if (isRefd) {
    // do a scan for every simple variable referenced value
    // in the dictionary and mark it as potentially
    // containing an object (in the bit vector)
    for (int i = size(); i--; ) {
      if (ExpressionPtr e = get(i)) {
        always_assert(e->is(Expression::KindOfSimpleVariable));
        always_assert(((unsigned int)i) == e->getCanonID());
        if (BitOps::get_bit(i, m_referenced)) {
          BitOps::set_bit(i, m_obj, true);
          BitOps::set_bit(i, m_noobj, false);
        }
      }
    }
  }
}
Example #9
0
int FunctionScope::inferParamTypes(AnalysisResultPtr ar, ConstructPtr exp,
                                   ExpressionListPtr params, bool &valid) {
  if (!params) {
    if (m_minParam > 0) {
      if (exp->getScope()->isFirstPass()) {
        Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
      }
      valid = false;
      if (!Option::AllDynamic) setDynamic();
    }
    return 0;
  }

  int ret = 0;
  if (params->getCount() < m_minParam) {
    if (exp->getScope()->isFirstPass()) {
      Compiler::Error(Compiler::TooFewArgument, exp, m_stmt);
    }
    valid = false;
    if (!Option::AllDynamic) setDynamic();
  }
  if (params->getCount() > m_maxParam) {
    if (isVariableArgument()) {
      ret = params->getCount() - m_maxParam;
    } else {
      if (exp->getScope()->isFirstPass()) {
        Compiler::Error(Compiler::TooManyArgument, exp, m_stmt);
      }
      valid = false;
      if (!Option::AllDynamic) setDynamic();
    }
  }

  bool canSetParamType = isUserFunction() && !m_overriding && !m_perfectVirtual;
  for (int i = 0; i < params->getCount(); i++) {
    ExpressionPtr param = (*params)[i];
    if (i < m_maxParam && param->hasContext(Expression::RefParameter)) {
      /**
       * This should be very un-likely, since call time pass by ref is a
       * deprecated, not very widely used (at least in FB codebase) feature.
       */
      TRY_LOCK_THIS();
      Symbol *sym = getVariables()->addSymbol(m_paramNames[i]);
      sym->setLvalParam();
      sym->setCallTimeRef();
    }
    if (valid && param->hasContext(Expression::InvokeArgument)) {
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::NoRefWrapper);
    }
    bool isRefVararg = (i >= m_maxParam && isReferenceVariableArgument());
    if ((i < m_maxParam && isRefParam(i)) || isRefVararg) {
      param->setContext(Expression::LValue);
      param->setContext(Expression::RefValue);
      param->inferAndCheck(ar, Type::Variant, true);
    } else if (!(param->getContext() & Expression::RefParameter)) {
      param->clearContext(Expression::LValue);
      param->clearContext(Expression::RefValue);
      param->clearContext(Expression::InvokeArgument);
      param->clearContext(Expression::NoRefWrapper);
    }
    TypePtr expType;
    /**
     * Duplicate the logic of getParamType(i), w/o the mutation
     */
    TypePtr paramType(i < m_maxParam && !isZendParamMode() ?
                      m_paramTypes[i] : TypePtr());
    if (!paramType) paramType = Type::Some;
    if (valid && !canSetParamType && i < m_maxParam &&
        (!Option::HardTypeHints || !m_paramTypeSpecs[i])) {
      /**
       * What is this magic, you might ask?
       *
       * Here, we take advantage of implicit conversion from every type to
       * Variant. Essentially, we don't really care what type comes out of this
       * expression since it'll just get converted anyways. Doing it this way
       * allows us to generate less temporaries along the way.
       */
      TypePtr optParamType(paramType->is(Type::KindOfVariant) ?
                           Type::Some : paramType);
      expType = param->inferAndCheck(ar, optParamType, false);
    } else {
      expType = param->inferAndCheck(ar, Type::Some, false);
    }
    if (i < m_maxParam) {
      if (!Option::HardTypeHints || !m_paramTypeSpecs[i]) {
        if (canSetParamType) {
          if (!Type::SameType(paramType, expType) &&
              !paramType->is(Type::KindOfVariant)) {
            TRY_LOCK_THIS();
            paramType = setParamType(ar, i, expType);
          } else {
            // do nothing - how is this safe?  well, if we ever observe
            // paramType == expType, then this means at some point in the past,
            // somebody called setParamType() with expType.  thus, by calling
            // setParamType() again with expType, we contribute no "new"
            // information. this argument also still applies in the face of
            // concurrency
          }
        }
        // See note above. If we have an implemented type, however, we
        // should set the paramType to the implemented type to avoid an
        // un-necessary cast
        if (paramType->is(Type::KindOfVariant)) {
          TypePtr it(param->getImplementedType());
          paramType = it ? it : expType;
        }
        if (valid) {
          if (!Type::IsLegalCast(ar, expType, paramType) &&
              paramType->isNonConvertibleType()) {
            param->inferAndCheck(ar, paramType, true);
          }
          param->setExpectedType(paramType);
        }
      }
    }
    // we do a best-effort check for bad pass-by-reference and do not report
    // error for some vararg case (e.g., array_multisort can have either ref
    // or value for the same vararg).
    if (!isRefVararg || !isMixedVariableArgument()) {
      Expression::CheckPassByReference(ar, param);
    }
  }
  return ret;
}
Example #10
0
void AliasManager::collectAliasInfoRecur(ConstructPtr cs) {
  if (!cs) {
    return;
  }

  if (StatementPtr s = dpc(Statement, cs)) {
    switch (s->getKindOf()) {
      case Statement::KindOfFunctionStatement:
      case Statement::KindOfMethodStatement:
      case Statement::KindOfClassStatement:
      case Statement::KindOfInterfaceStatement:
        return;
      default:
        break;
    }
  }

  int nkid = cs->getKidCount();

  for (int i = 0; i < nkid; i++) {
    ConstructPtr kid = cs->getNthKid(i);
    if (kid) {
      collectAliasInfoRecur(kid);
    }
  }

  if (ExpressionPtr e = dpc(Expression, cs)) {
    int context = e->getContext();
    switch (e->getKindOf()) {
    case Expression::KindOfAssignmentExpression:
      {
        AssignmentExpressionPtr ae = spc(AssignmentExpression, e);
        ExpressionPtr var = ae->getVariable();
        ExpressionPtr val = ae->getValue();
        if (var->is(Expression::KindOfSimpleVariable)) {
          const std::string &name = spc(SimpleVariable, var)->getName();
          AliasInfo &ai = m_aliasInfo[name];
          if (val->getContext() & Expression::RefValue) {
            ai.setIsRefTo();
            m_variables->addUsed(name);
          } else {
            Expression::checkUsed(m_arp, var, val);
          }
        }
      }
      break;
    case Expression::KindOfListAssignment:
      {
        ListAssignmentPtr la = spc(ListAssignment, e);
        ExpressionListPtr vars = la->getVariables();
        for (int i = vars->getCount(); i--; ) {
          ExpressionPtr v = (*vars)[i];
          if (v && v->is(Expression::KindOfSimpleVariable)) {
            SimpleVariablePtr sv = spc(SimpleVariable, v);
            m_variables->addUsed(sv->getName());
          }
        }
      }
      break;
    case Expression::KindOfSimpleVariable:
      {
        const std::string &name = spc(SimpleVariable, e)->getName();
        if (context & Expression::RefValue) {
          AliasInfo &ai = m_aliasInfo[name];
          ai.addRefLevel(0);
        }
        if (!(context & (Expression::AssignmentLHS |
                         Expression::UnsetContext))) {
          m_variables->addUsed(name);
        }
      }
      break;
    case Expression::KindOfDynamicVariable:
      if (context & Expression::RefValue) {
        m_wildRefs = true;
      }
      break;
    case Expression::KindOfArrayElementExpression:
      {
        int n = 1;
        while (n < 10 &&
               e->is(Expression::KindOfArrayElementExpression)) {
          e = spc(ArrayElementExpression, e)->getVariable();
        }
        if (e->is(Expression::KindOfSimpleVariable)) {
          const std::string &name = spc(SimpleVariable, e)->getName();
          if (context & Expression::RefValue) {
            AliasInfo &ai = m_aliasInfo[name];
            ai.addRefLevel(n);
          }
          m_variables->addUsed(name); // need this for UnsetContext
        }
      }
      break;
    case Expression::KindOfObjectPropertyExpression:
      {
        e = spc(ObjectPropertyExpression, e)->getObject();
        if (e->is(Expression::KindOfSimpleVariable)) {
          const std::string &name = spc(SimpleVariable, e)->getName();
          if (context & Expression::RefValue) {
            AliasInfo &ai = m_aliasInfo[name];
            ai.addRefLevel(1);
          }
          m_variables->addUsed(name); // need this for UnsetContext
        }
      }
      break;
    default:
      break;
    }
  } else {
    StatementPtr s = spc(Statement, cs);
    switch (s->getKindOf()) {
    case Statement::KindOfCatchStatement:
      {
        const std::string &name = spc(CatchStatement, s)->getVariable();
        m_variables->addUsed(name);
        break;
      }
    default:
      break;
    }
  }
}
Example #11
0
ExpressionPtr AliasManager::canonicalizeNode(ExpressionPtr e) {
  e->setCanonPtr(ExpressionPtr());
  e->setCanonID(0);

  switch (e->getKindOf()) {
  case Expression::KindOfObjectMethodExpression:
  case Expression::KindOfDynamicFunctionCall:
  case Expression::KindOfSimpleFunctionCall:
  case Expression::KindOfNewObjectExpression:
    add(m_bucketMap[0], e);
    break;

  case Expression::KindOfListAssignment:
    add(m_bucketMap[0], e);
    break;

  case Expression::KindOfAssignmentExpression: {
    AssignmentExpressionPtr ae = spc(AssignmentExpression,e);
    if (e->getContext() & Expression::DeadStore) {
      Construct::recomputeEffects();
      return ae->getValue();
    }
    ExpressionPtr rep;
    int interf = findInterf(ae->getVariable(), false, rep);
    if (interf == SameAccess) {
      switch (rep->getKindOf()) {
      default:
        break;
      case Expression::KindOfAssignmentExpression:
        {
          AssignmentExpressionPtr a = spc(AssignmentExpression, rep);
          ExpressionPtr value = a->getValue();
          if (a->getValue()->getContext() & Expression::RefValue) {
            break;
          }
        }
      case Expression::KindOfUnaryOpExpression:
      case Expression::KindOfBinaryOpExpression:
        rep->setContext(Expression::DeadStore);
        break;
      }
    }
    add(m_bucketMap[0], e);
    break;
  }

  case Expression::KindOfConstantExpression:
  case Expression::KindOfSimpleVariable:
  case Expression::KindOfDynamicVariable:
  case Expression::KindOfArrayElementExpression:
  case Expression::KindOfObjectPropertyExpression:
  case Expression::KindOfStaticMemberExpression:
    if (!(e->getContext() & (Expression::AssignmentLHS|
                             Expression::DeepAssignmentLHS|
                             Expression::OprLValue))) {
      if (!(e->getContext() & (Expression::LValue|
                               Expression::RefValue|
                               Expression::RefParameter|
                               Expression::UnsetContext))) {
        ExpressionPtr rep;
        int interf = findInterf(e, true, rep);
        if (interf == SameAccess) {
          if (rep->getKindOf() == e->getKindOf()) {
            e->setCanonID(rep->getCanonID());
            e->setCanonPtr(rep);
            return ExpressionPtr();
          }
          if (rep->getKindOf() == Expression::KindOfAssignmentExpression) {
            ExpressionPtr rhs = spc(AssignmentExpression,rep)->getValue();
            if (rhs->is(Expression::KindOfScalarExpression)) {
              rhs = rhs->clone();
              getCanonical(rhs);
              return rhs;
            }
            e->setCanonPtr(rhs);
          }
        }
      }
      add(m_bucketMap[0], e);
    } else {
      getCanonical(e);
    }
    break;

  case Expression::KindOfBinaryOpExpression: {
    BinaryOpExpressionPtr bop = spc(BinaryOpExpression, e);

    int rop = getOpForAssignmentOp(bop->getOp());
    if (rop) {
      ExpressionPtr lhs = bop->getExp1();
      ExpressionPtr rep;
      if (bop->getContext() & Expression::DeadStore) {
        Construct::recomputeEffects();
        ExpressionPtr rhs = bop->getExp2()->clone();
        lhs = lhs->clone();
        lhs->clearContext(Expression::LValue);
        lhs->clearContext(Expression::NoLValueWrapper);
        lhs->clearContext(Expression::OprLValue);
        rep = ExpressionPtr
          (new BinaryOpExpression(e->getLocation(),
                                  Expression::KindOfBinaryOpExpression,
                                  lhs, rhs, rop));

      } else {
        ExpressionPtr alt;
        int interf = findInterf(lhs, true, alt);
        if (interf == SameAccess &&
            alt->is(Expression::KindOfAssignmentExpression)) {
          ExpressionPtr op0 = spc(AssignmentExpression,alt)->getValue();
          if (op0->is(Expression::KindOfScalarExpression)) {
            ExpressionPtr op1 = bop->getExp2();
            ExpressionPtr rhs
              (new BinaryOpExpression(e->getLocation(),
                                      Expression::KindOfBinaryOpExpression,
                                      op0->clone(), op1->clone(), rop));

            lhs = lhs->clone();
            lhs->clearContext(Expression::OprLValue);
            rep = ExpressionPtr
              (new AssignmentExpression(e->getLocation(),
                                        Expression::KindOfAssignmentExpression,
                                        lhs, rhs, false));
          }
        }
      }
      if (rep) {
        ExpressionPtr c = canonicalizeRecur(rep);
        return c ? c : rep;
      }
      add(m_bucketMap[0], e);
    } else {
      getCanonical(e);
    }
    break;
  }

  case Expression::KindOfUnaryOpExpression:
    {
      UnaryOpExpressionPtr uop = spc(UnaryOpExpression, e);
      switch (uop->getOp()) {
      case T_INC:
      case T_DEC:
        if (uop->getContext() & Expression::DeadStore) {
          Construct::recomputeEffects();
          ExpressionPtr val = uop->getExpression()->clone();
          val->clearContext(Expression::LValue);
          val->clearContext(Expression::NoLValueWrapper);
          val->clearContext(Expression::OprLValue);
          if (uop->getFront()) {
            ExpressionPtr inc
              (new ScalarExpression(uop->getLocation(),
                                    Expression::KindOfScalarExpression,
                                    T_LNUMBER, string("1")));

            val = ExpressionPtr
              (new BinaryOpExpression(uop->getLocation(),
                                      Expression::KindOfBinaryOpExpression,
                                      val, inc,
                                      uop->getOp() == T_INC ? '+' : '-'));

          }

          ExpressionPtr r = canonicalizeRecur(val);
          return r ? r : val;
        }
        add(m_bucketMap[0], e);
        break;
      default:
        getCanonical(e);
        break;
      }
      break;
    }

  default:
    getCanonical(e);
    break;
  }

  return ExpressionPtr();
}