Beispiel #1
0
void ForEachStatement::inferTypes(AnalysisResultPtr ar) {
  IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope());

  m_array->inferAndCheck(ar, m_ref ? Type::Variant : Type::Array, m_ref);
  if (m_name) {
    if (m_name->is(Expression::KindOfListAssignment)) {
      m_name->inferTypes(ar, TypePtr(), false);
    } else {
      m_name->inferAndCheck(ar, Type::Primitive, true);
    }
  }

  if (m_value->is(Expression::KindOfListAssignment)) {
    m_value->inferTypes(ar, TypePtr(), false);
  } else {
    m_value->inferAndCheck(ar, Type::Variant, true);
  }

  if (m_ref) {
    TypePtr actualType = m_array->getActualType();
    if (!actualType ||
        actualType->is(Type::KindOfVariant) ||
        actualType->is(Type::KindOfObject)) {
      ar->forceClassVariants(getClassScope(), false, true);
    }
  }
  if (m_stmt) {
    getScope()->incLoopNestedLevel();
    m_stmt->inferTypes(ar);
    getScope()->decLoopNestedLevel();
  }
}
void ForEachStatement::inferTypes(AnalysisResultPtr ar) {
  if (ar->isFirstPass() &&
      !m_array->is(Expression::KindOfSimpleVariable) &&
      !m_array->is(Expression::KindOfArrayElementExpression) &&
      !m_array->is(Expression::KindOfObjectPropertyExpression)) {
    ConstructPtr self = shared_from_this();
    ar->getCodeError()->record(self, CodeError::ComplexForEach, self);
  }

  m_array->inferAndCheck(ar, Type::Array, true);
  if (m_name) {
    m_name->inferAndCheck(ar, NEW_TYPE(Primitive), true);
  }
  m_value->inferAndCheck(ar, Type::Variant, true);
  if (m_ref) {
    TypePtr actualType = m_array->getActualType();
    if (!actualType ||
        actualType->is(Type::KindOfVariant) ||
        actualType->is(Type::KindOfObject)) {
      ar->forceClassVariants();
    }
  }
  if (m_stmt) {
    ar->getScope()->incLoopNestedLevel();
    m_stmt->inferTypes(ar);
    ar->getScope()->decLoopNestedLevel();
  }
}
void ForEachStatement::inferTypes(AnalysisResultPtr ar) {
  m_array->inferAndCheck(ar, m_ref ? Type::Variant : Type::Array, m_ref);
  if (m_name) {
    m_name->inferAndCheck(ar, Type::Primitive, true);
  }
  m_value->inferAndCheck(ar, Type::Variant, true);
  if (m_ref) {
    TypePtr actualType = m_array->getActualType();
    if (!actualType ||
        actualType->is(Type::KindOfVariant) ||
        actualType->is(Type::KindOfObject)) {
      ar->forceClassVariants(getClassScope(), false);
    }
  }
  if (m_stmt) {
    getScope()->incLoopNestedLevel();
    m_stmt->inferTypes(ar);
    getScope()->decLoopNestedLevel();
  }
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
                                             TypePtr type, bool coerce) {
  m_valid = false;

  ConstructPtr self = shared_from_this();
  TypePtr objectType = m_object->inferAndCheck(ar, NEW_TYPE(Object), false);

  if (!m_property->is(Expression::KindOfScalarExpression)) {
    // if dynamic property or method, we have nothing to find out
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseDynamicProperty, self);
    }
    m_property->inferAndCheck(ar, Type::String, false);

    // we also lost track of which class variable an expression is about, hence
    // any type inference could be wrong. Instead, we just force variants on
    // all class variables.
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants();
    }

    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
  string name = exp->getString();
  ASSERT(!name.empty());

  m_property->inferAndCheck(ar, Type::String, false);

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    // what object-> has told us
    cls = ar->findExactClass(objectType->getName());
  } else {
    // what ->property has told us
    cls = ar->findClass(name, AnalysisResult::PropertyName);
    if (cls) {
      objectType =
        m_object->inferAndCheck(ar, Type::CreateObjectType(cls->getName()),
                                false);
    }
    if ((m_context & LValue) &&
        objectType && !objectType->is(Type::KindOfObject) &&
                      !objectType->is(Type::KindOfVariant) &&
                      !objectType->is(Type::KindOfSome) &&
                      !objectType->is(Type::KindOfAny)) {
      m_object->inferAndCheck(ar, NEW_TYPE(Object), true);
    }
  }

  if (!cls) {
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(name);
    }
    return Type::Variant;
  }

  const char *accessorName = hasContext(DeepAssignmentLHS) ? "__set" :
    hasContext(ExistContext) ? "__isset" :
    hasContext(UnsetContext) ? "__unset" : "__get";
  if (!cls->implementsAccessor(ar, accessorName)) clearEffect(AccessorEffect);

  // resolved to this class
  int present = 0;
  if (m_context & RefValue) {
    type = Type::Variant;
    coerce = true;
  }

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr func = ar->getFunctionScope();
    if (func->isStatic()) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::MissingObjectContext,
                                   self);
      }
      m_actualType = Type::Variant;
      return m_actualType;
    }
  }

  TypePtr ret;
  if (!cls->derivesFromRedeclaring()) { // Have to use dynamic.
    ret = cls->checkProperty(name, type, coerce, ar, self, present);
    // Private only valid if in the defining class
    if (present && (getOriginalScope(ar) == cls ||
                    !(present & VariableTable::VariablePrivate))) {
      m_valid = true;
      m_static = present & VariableTable::VariableStatic;
      if (m_static) {
        ar->getScope()->getVariables()->
          setAttribute(VariableTable::NeedGlobalPointer);
      }
      m_class = cls;
    }
  }

  // get() will return Variant
  if (!m_valid || !m_object->getType()->isSpecificObject()) {
    m_actualType = Type::Variant;
    return m_actualType;
  }

  clearEffect(AccessorEffect);

  if (ar->getPhase() == AnalysisResult::LastInference) {
    if (!(m_context & ObjectContext)) {
      m_object->clearContext(Expression::LValue);
    }
    setContext(Expression::NoLValueWrapper);
  }
  return ret;
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
                                             TypePtr type, bool coerce) {
  m_valid = false;

  ConstructPtr self = shared_from_this();
  TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);

  if (!m_property->is(Expression::KindOfScalarExpression)) {
    m_property->inferAndCheck(ar, Type::String, false);
    // we also lost track of which class variable an expression is about, hence
    // any type inference could be wrong. Instead, we just force variants on
    // all class variables.
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(getOriginalClass(), false, true);
    }
    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
  const string &name = exp->getLiteralString();
  if (name.empty()) {
    m_property->inferAndCheck(ar, Type::String, false);
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(getOriginalClass(), false, true);
    }
    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  m_property->inferAndCheck(ar, Type::String, false);

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    // what object-> has told us
    cls = ar->findExactClass(shared_from_this(), objectType->getName());
  } else {
    if ((m_context & LValue) && objectType &&
        !objectType->is(Type::KindOfObject) &&
        !objectType->is(Type::KindOfVariant) &&
        !objectType->is(Type::KindOfSome) &&
        !objectType->is(Type::KindOfAny)) {
      m_object->inferAndCheck(ar, Type::Object, true);
    }
  }

  if (!cls) {
    if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
      ar->forceClassVariants(name, getOriginalClass(), false, true);
    }
    return Type::Variant;
  }

  // resolved to this class
  if (m_context & RefValue) {
    type = Type::Variant;
    coerce = true;
  }

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr func = m_object->getOriginalFunction();
    if (!func || func->isStatic()) {
      if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::MissingObjectContext, self);
      }
      m_actualType = Type::Variant;
      return m_actualType;
    }
  }

  assert(cls);
  if (!m_propSym || cls != m_objectClass.lock()) {
    m_objectClass = cls;
    ClassScopePtr parent;
    m_propSym = cls->findProperty(parent, name, ar);
    if (m_propSym) {
      if (!parent) {
        parent = cls;
      }
      m_symOwner = parent;
      always_assert(m_propSym->isPresent());
      m_propSymValid =
        (!m_propSym->isPrivate() || getOriginalClass() == parent) &&
        !m_propSym->isStatic();

      if (m_propSymValid) {
        m_symOwner->addUse(getScope(),
                           BlockScope::GetNonStaticRefUseKind(
                             m_propSym->getHash()));
      }
    }
  }

  TypePtr ret;
  if (m_propSymValid && (!cls->derivesFromRedeclaring() ||
                         m_propSym->isPrivate())) {
    always_assert(m_symOwner);
    TypePtr t(m_propSym->getType());
    if (t && t->is(Type::KindOfVariant)) {
      // only check property if we could possibly do some work
      ret = t;
    } else {
      if (coerce && type->is(Type::KindOfAutoSequence) &&
          (!t || t->is(Type::KindOfVoid) ||
           t->is(Type::KindOfSome) || t->is(Type::KindOfArray))) {
        type = Type::Array;
      }
      assert(getScope()->is(BlockScope::FunctionScope));
      GET_LOCK(m_symOwner);
      ret = m_symOwner->checkProperty(getScope(), m_propSym, type, coerce, ar);
    }
    always_assert(m_object->getActualType() &&
           m_object->getActualType()->isSpecificObject());
    m_valid = true;
    return ret;
  } else {
    m_actualType = Type::Variant;
    return m_actualType;
  }
}
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);
  }
}
TypePtr ObjectPropertyExpression::inferTypes(AnalysisResultPtr ar,
                                             TypePtr type, bool coerce) {
  m_valid = false;

  ConstructPtr self = shared_from_this();
  TypePtr objectType = m_object->inferAndCheck(ar, Type::Some, false);

  if (!m_property->is(Expression::KindOfScalarExpression)) {
    m_property->inferAndCheck(ar, Type::String, false);

    // we also lost track of which class variable an expression is about, hence
    // any type inference could be wrong. Instead, we just force variants on
    // all class variables.
    if (m_context & (LValue | RefValue)) {
      ar->forceClassVariants(getOriginalClass(), false);
    }

    return Type::Variant; // we have to use a variant to hold dynamic value
  }

  ScalarExpressionPtr exp = dynamic_pointer_cast<ScalarExpression>(m_property);
  string name = exp->getString();
  ASSERT(!name.empty());

  m_property->inferAndCheck(ar, Type::String, false);

  ClassScopePtr cls;
  if (objectType && !objectType->getName().empty()) {
    // what object-> has told us
    cls = ar->findExactClass(shared_from_this(), objectType->getName());
  } else {
    if ((m_context & LValue) && objectType &&
        !objectType->is(Type::KindOfObject) &&
        !objectType->is(Type::KindOfVariant) &&
        !objectType->is(Type::KindOfSome) &&
        !objectType->is(Type::KindOfAny)) {
      m_object->inferAndCheck(ar, Type::Object, true);
    }
  }

  if (!cls) {
    if (m_context & (LValue | RefValue | DeepReference | UnsetContext)) {
      ar->forceClassVariants(name, getOriginalClass(), false);
    }
    return Type::Variant;
  }

  int prop = hasContext(AssignmentLHS) ? ClassScope::MayHaveUnknownPropSetter :
    hasContext(ExistContext) ? ClassScope::MayHaveUnknownPropTester :
    hasContext(UnsetContext) && hasContext(LValue) ?
    ClassScope::MayHavePropUnsetter : ClassScope::MayHaveUnknownPropGetter;
  if ((m_context & (AssignmentLHS|OprLValue)) ||
      !cls->implementsAccessor(prop)) {
    clearEffect(AccessorEffect);
  }

  // resolved to this class
  if (m_context & RefValue) {
    type = Type::Variant;
    coerce = true;
  }

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr func = m_object->getOriginalFunction();
    if (!func || func->isStatic()) {
      if (getScope()->isFirstPass()) {
        Compiler::Error(Compiler::MissingObjectContext, self);
      }
      m_actualType = Type::Variant;
      return m_actualType;
    }
  }

  if (!m_propSym || cls != m_objectClass.lock()) {
    m_objectClass = cls;
    ClassScopePtr parent;
    m_propSym = cls->findProperty(parent, name, ar, self);
    assert(m_propSym);
    if (!parent) {
      parent = cls;
    }
    m_propSymValid = m_propSym->isPresent() &&
      (!m_propSym->isPrivate() ||
       getOriginalClass() == parent) &&
      !m_propSym->isStatic();

    if (m_propSymValid) {
      parent->addUse(getScope(), BlockScope::UseKindNonStaticRef);
    }
  }

  TypePtr ret;
  if (m_propSymValid && (!cls->derivesFromRedeclaring() ||
                         m_propSym->isPrivate())) {
    ret = cls->checkProperty(m_propSym, type, coerce, ar);
    assert(m_object->getType()->isSpecificObject());
    m_valid = true;

    clearEffect(AccessorEffect);
    clearEffect(CreateEffect);
    return ret;
  } else {
    m_actualType = Type::Variant;
    return m_actualType;
  }
}