TypePtr NewObjectExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
                                        bool coerce) {
  reset();
  ConstructPtr self = shared_from_this();
  if (!m_name.empty()) {
    ClassScopePtr cls = ar->resolveClass(m_name);
    if (cls) {
      m_name = cls->getName();
    }
    if (!cls || cls->isRedeclaring()) {
      if (cls) {
        m_redeclared = true;
        ar->getScope()->getVariables()->
          setAttribute(VariableTable::NeedGlobalPointer);
      }
      if (!cls && ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::UnknownClass, self);
      }
      if (m_params) m_params->inferAndCheck(ar, NEW_TYPE(Any), false);
      return NEW_TYPE(Object);
    }
    if (cls->isVolatile()) {
      ar->getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
    }
    m_dynamic = cls->derivesFromRedeclaring();
    m_validClass = true;
    FunctionScopePtr func = cls->findConstructor(ar, true);
    if (!func) {
      if (m_params) {
        if (!m_dynamic && m_params->getCount()) {
          if (ar->isFirstPass()) {
            ar->getCodeError()->record(self, CodeError::BadConstructorCall,
                                       self);
          }
          m_params->setOutputCount(0);
        }
        m_params->inferAndCheck(ar, NEW_TYPE(Any), false);
      }
    } else {
      m_extraArg = func->inferParamTypes(ar, self, m_params,
                                         m_validClass);
      m_variableArgument = func->isVariableArgument();
    }
    return Type::CreateObjectType(m_name);
  } else {
    ar->containsDynamicClass();
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseDynamicClass,
                                 self);
    }
    if (m_params) {
      m_params->markParams(false);
    }
  }

  m_nameExp->inferAndCheck(ar, Type::String, false);
  if (m_params) m_params->inferAndCheck(ar, NEW_TYPE(Any), false);
  return Type::Variant;//NEW_TYPE(Object);
}
Esempio n. 2
0
void CatchStatement::inferTypes(AnalysisResultPtr ar) {
  ClassScopePtr cls = ar->findClass(m_className);
  TypePtr type;
  m_valid = cls;
  if (!m_valid) {
    if (ar->isFirstPass()) {
      ConstructPtr self = shared_from_this();
      ar->getCodeError()->record(self, CodeError::UnknownClass, self);
    }
    type = NEW_TYPE(Object);
  } else if (cls->isRedeclaring()) {
    type = NEW_TYPE(Object);
  } else {
    type = Type::CreateObjectType(m_className);
  }

  BlockScopePtr scope = ar->getScope();
  VariableTablePtr variables = scope->getVariables();
  variables->add(m_variable, type, false, ar, shared_from_this(),
                 ModifierExpressionPtr(), false);
  if (ar->isFirstPass()) {
    FunctionScopePtr func = dynamic_pointer_cast<FunctionScope>(scope);
    if (func && variables->isParameter(m_variable)) {
      variables->addLvalParam(m_variable);
    }
  }
  if (m_stmt) m_stmt->inferTypes(ar);
}
TypePtr DynamicFunctionCall::inferTypes(AnalysisResultPtr ar, TypePtr type,
                                        bool coerce) {
    reset();
    ConstructPtr self = shared_from_this();
    if (!m_className.empty()) {
        ClassScopePtr cls = ar->resolveClass(m_className);
        if (!cls || cls->isRedeclaring()) {
            if (cls) {
                m_redeclared = true;
                ar->getScope()->getVariables()->
                setAttribute(VariableTable::NeedGlobalPointer);
            }
            if (!cls && ar->isFirstPass()) {
                ar->getCodeError()->record(self, CodeError::UnknownClass, self);
            }
        } else {
            m_validClass = true;
        }
    }

    ar->containsDynamicFunctionCall();

    if (ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::UseDynamicFunction, self);
    }
    m_nameExp->inferAndCheck(ar, Type::String, false);
    if (m_params) {
        for (int i = 0; i < m_params->getCount(); i++) {
            (*m_params)[i]->inferAndCheck(ar, Type::Variant, true);
        }
    }
    return Type::Variant;
}
Esempio n. 4
0
void SwitchStatement::inferTypes(AnalysisResultPtr ar) {
  // we optimize the most two common cases of switch statements
  bool allInteger = true;
  bool allString = true;
  if (m_cases && m_cases->getCount()) {
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      if (!stmt->getCondition()) {
        if (m_cases->getCount() == 1) allInteger = allString = false;
      } else {
        if (!stmt->isLiteralInteger()) allInteger = false;
        if (!stmt->isLiteralString()) allString = false;
      }
    }
  }
  if (allInteger && allString) {
    allInteger = allString = false;
  }

  TypePtr ret;
  if (allInteger) {
    ret = m_exp->inferAndCheck(ar, Type::Int64, false);
  } else if (allString) {
    // We're not able to do this, because switch($obj) may work on an object
    // that didn't implement __toString(), throwing an exception, which isn't
    // consistent with PHP.
    //ret = m_exp->inferAndCheck(ar, Type::String, false);
    ret = m_exp->inferAndCheck(ar, NEW_TYPE(Some), false);
  } else {
    ret = m_exp->inferAndCheck(ar, NEW_TYPE(Some), false);
  }
  if (ret->is(Type::KindOfObject) && ret->isSpecificObject()) {
    m_exp->setExpectedType(NEW_TYPE(Object));
  }
  ConstructPtr self = shared_from_this();
  if (m_cases && m_cases->getCount()) {
    bool checking = false;
    vector<int> defaults;
    int defaultCount = 0;
    for (int i = 0; i < m_cases->getCount(); i++) {
      CaseStatementPtr stmt =
        dynamic_pointer_cast<CaseStatement>((*m_cases)[i]);
      stmt->inferAndCheck(ar, NEW_TYPE(Some), false);
      ExpressionPtr cond = stmt->getCondition();
      if (!cond) {
        checking = true;
        defaultCount++;
      } else if (checking && cond && ar->isFirstPass()) {
        defaults.push_back(i);
        ar->getCodeError()->record(self, CodeError::CaseAfterDefault, stmt);
      }
    }
    if (defaultCount > 1 && ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::MoreThanOneDefault, m_cases);
    }
  }
}
Esempio n. 5
0
TypePtr ParameterExpression::getTypeSpec(AnalysisResultPtr ar, bool error) {
  TypePtr ret;
  if (m_type.empty() || m_defaultValue) {
    ret = Type::Some;
  } else if (m_type == "array") {
    ret = Type::Array;
  } else {
    ClassScopePtr cls = ar->findClass(m_type);
    if (!cls || cls->isRedeclaring()) {
      if (error && !cls && ar->isFirstPass()) {
        ConstructPtr self = shared_from_this();
        ar->getCodeError()->record(self, CodeError::UnknownClass, self);
      }
      ret = Type::Some;
    } else {
      ret = Type::CreateObjectType(m_type);
    }
  }
  // we still want the above to run, so to record errors and infer defaults
  if (m_ref) {
    ret = Type::Variant;
  }

  return ret;
}
Esempio n. 6
0
TypePtr ConstantTable::add(const std::string &name, TypePtr type,
                           ExpressionPtr exp, AnalysisResultPtr ar,
                           ConstructPtr construct) {

  if (name == "true" || name == "false") {
    return Type::Boolean;
  }

  StringToConstructPtrMap::const_iterator iter = m_values.find(name);
  if (iter == m_values.end()) {
    setType(ar, name, type, true);
    m_declarations[name] = construct;
    m_values[name] = exp;
    return type;
  }

  if (ar->isFirstPass()) {
    if (exp != iter->second) {
      ar->getCodeError()->record(CodeError::DeclaredConstantTwice, construct,
                                 m_declarations[name]);
      m_dynamic.insert(name);
      type = Type::Variant;
    }
    setType(ar, name, type, true);
  }

  return type;
}
Esempio n. 7
0
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();
  }
}
Esempio n. 8
0
TypePtr ConstantTable::add(const std::string &name, TypePtr type,
                           ExpressionPtr exp, AnalysisResultPtr ar,
                           ConstructPtr construct) {

  if (name == "true" || name == "false") {
    return Type::Boolean;
  }

  Symbol *sym = getSymbol(name, true);
  if (!sym->declarationSet()) {
    setType(ar, sym, type, true);
    sym->setDeclaration(construct);
    sym->setValue(exp);
    return type;
  }

  if (ar->isFirstPass()) {
    if (exp != sym->getValue()) {
      ar->getCodeError()->record(CodeError::DeclaredConstantTwice, construct,
                                 sym->getDeclaration());
      sym->setDynamic();
      m_hasDynamic = true;
      type = Type::Variant;
    }
    setType(ar, sym, type, true);
  }

  return type;
}
Esempio n. 9
0
void CatchStatement::analyzeProgramImpl(AnalysisResultPtr ar) {
  addUserClass(ar, m_className);
  if (ar->isFirstPass()) {
    ar->getScope()->getVariables()->addUsed(m_variable);
  }
  if (m_stmt) m_stmt->analyzeProgram(ar);
}
Esempio n. 10
0
void StatementList::analyzeProgramImpl(AnalysisResultPtr ar) {
  m_included = true;
  for (unsigned int i = 0; i < m_stmts.size(); i++) {
    StatementPtr stmt = m_stmts[i];

    // effect testing
    if (ar->isFirstPass() && !stmt->hasEffect() &&
        !stmt->is(Statement::KindOfStatementList)) {
      ar->getCodeError()->record(shared_from_this(),
                                 CodeError::StatementHasNoEffect, stmt);
    }

    // changing AUTOLOAD to includes
    if (ar->getPhase() == AnalysisResult::AnalyzeInclude &&
        stmt->is(Statement::KindOfExpStatement)) {
      ExpStatementPtr expStmt = dynamic_pointer_cast<ExpStatement>(stmt);
      if (stmt->isFileLevel()) {
        expStmt->analyzeAutoload(ar);
      }
      expStmt->analyzeShortCircuit(ar);
    }

    bool scopeStmt = stmt->is(Statement::KindOfFunctionStatement) ||
      stmt->is(Statement::KindOfClassStatement) ||
      stmt->is(Statement::KindOfInterfaceStatement);
    if (ar->getPhase() != AnalysisResult::AnalyzeTopLevel || !scopeStmt) {
      /* Recurse when analyzing include/all OR when not a scope */
      stmt->analyzeProgram(ar);
    }
  }
}
Esempio n. 11
0
TypePtr AssignmentExpression::
inferTypesImpl(AnalysisResultPtr ar, TypePtr type, bool coerce,
               ExpressionPtr variable,
               ExpressionPtr value /* = ExpressionPtr() */) {
  TypePtr ret = type;
  if (value) {
    if (coerce) {
      ret = value->inferAndCheck(ar, type, coerce);
    } else {
      ret = value->inferAndCheck(ar, NEW_TYPE(Some), coerce);
    }
  }

  BlockScopePtr scope = ar->getScope();
  if (variable->is(Expression::KindOfConstantExpression)) {
    // ...as in ClassConstant statement
    ConstantExpressionPtr exp =
      dynamic_pointer_cast<ConstantExpression>(variable);
    bool p;
    scope->getConstants()->check(exp->getName(), ret, true, ar, variable, p);
  } else if (variable->is(Expression::KindOfDynamicVariable)) {
    // simptodo: not too sure about this
    ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable);
  } else if (variable->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable);
    if (var->getName() == "this" && ar->getClassScope()) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(variable, CodeError::ReassignThis,
                                   variable);
      }
    }
    if (ar->getPhase() == AnalysisResult::LastInference && value) {
      if (!value->getExpectedType()) {
        value->setExpectedType(variable->getActualType());
      }
    }
  }
  // if the value may involve object, consider the variable as "referenced"
  // so that objects are not destructed prematurely.
  bool referenced = true;
  if (value && value->isScalar()) referenced = false;
  if (ret && ret->isNoObjectInvolved()) referenced = false;
  if (referenced && variable->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr var =
      dynamic_pointer_cast<SimpleVariable>(variable);
    const std::string &name = var->getName();
    VariableTablePtr variables = ar->getScope()->getVariables();
    variables->addReferenced(name);
  }

  TypePtr vt = variable->inferAndCheck(ar, ret, true);
  if (!coerce && type->is(Type::KindOfAny)) {
    ret = vt;
  }

  return ret;
}
Esempio n. 12
0
void MethodStatement::analyzeProgramImpl(AnalysisResultPtr ar) {
  FunctionScopePtr funcScope = m_funcScope.lock();

  // registering myself as a parent in dependency graph, so that
  // (1) we can tell orphaned parents
  // (2) overwrite non-master copy of function declarations
  if (ar->isFirstPass()) {
    ar->getDependencyGraph()->addParent(DependencyGraph::KindOfFunctionCall,
                                        "", getFullName(), shared_from_this());
    if (Option::AllDynamic || hasHphpNote("Dynamic") ||
        funcScope->isSepExtension() ||
        BuiltinSymbols::IsDeclaredDynamic(m_name) ||
        Option::IsDynamicFunction(m_method, m_name)) {
      funcScope->setDynamic();
    }
    if (hasHphpNote("Volatile")) funcScope->setVolatile();
  }

  funcScope->setIncludeLevel(ar->getIncludeLevel());
  ar->pushScope(funcScope);
  if (m_params) {
    m_params->analyzeProgram(ar);
    if (Option::GenRTTIProfileData &&
        ar->getPhase() == AnalysisResult::AnalyzeFinal) {
      addParamRTTI(ar);
    }
  }
  if (m_stmt) m_stmt->analyzeProgram(ar);

  if (ar->isFirstPass()) {
    if (!funcScope->isStatic() && ar->getClassScope() &&
        funcScope->getVariables()->
        getAttribute(VariableTable::ContainsDynamicVariable)) {
      // Add this to variable table if we'll need it in a lookup table
      // Use object because there's no point to specializing, just makes
      // code gen harder when dealing with redeclared classes.
      TypePtr tp(NEW_TYPE(Object));
      funcScope->getVariables()->add("this", tp, true, ar, shared_from_this(),
                                     ModifierExpressionPtr());
    }
    FunctionScope::RecordRefParamInfo(m_name, funcScope);
  }
  ar->popScope();
}
Esempio n. 13
0
TypePtr DynamicVariable::inferTypes(AnalysisResultPtr ar, TypePtr type,
                                    bool coerce) {
  ConstructPtr self = shared_from_this();
  if (m_context & (LValue | RefValue)) {
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseLDynamicVariable, self);
    }
    ar->getScope()->getVariables()->forceVariants(ar);
    ar->getScope()->
      getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable);
  } else {
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseRDynamicVariable, self);
    }
  }

  m_exp->inferAndCheck(ar, Type::String, false);
  return m_implementedType = Type::Variant;
}
Esempio n. 14
0
void ParameterExpression::analyzeProgram(AnalysisResultPtr ar) {
  if (!m_type.empty()) addUserClass(ar, m_type);

  if (m_defaultValue) m_defaultValue->analyzeProgram(ar);

  if (ar->isFirstPass()) {
    // Have to use non const ref params for magic methods
    FunctionScopePtr fs = ar->getFunctionScope();
    if (fs->isMagicMethod() || fs->getName() == "offsetget") {
      fs->getVariables()->addLvalParam(m_name);
    }
  }
}
Esempio n. 15
0
TypePtr ConstantTable::check(const std::string &name, TypePtr type,
                             bool coerce, AnalysisResultPtr ar,
                             ConstructPtr construct, bool &present) {
  TypePtr actualType;
  present = true;
  if (name == "true" || name == "false") {
    actualType = Type::Boolean;
  } else if (!ar->isFirstPass() && m_values.find(name) == m_values.end()) {
    ClassScopePtr parent = findParent(ar, name);
    if (parent) {
      return parent->checkConst(name, type, coerce, ar,
                                construct, present);
    }
    ar->getCodeError()->record(CodeError::UseUndeclaredConstant,
                               construct);
    if (m_blockScope.is(BlockScope::ClassScope)) {
      actualType = Type::Variant;
    } else {
      actualType = Type::String;
    }
    setType(ar, name, actualType, true);
    present = false;
  } else {
    StringToTypePtrMap::const_iterator iter = m_coerced.find(name);
    if (iter != m_coerced.end()) {
      actualType = iter->second;
      if (actualType->is(Type::KindOfSome) ||
          actualType->is(Type::KindOfAny)) {
        setType(ar, name, type, true);
        return type;
      }
    } else {
      ClassScopePtr parent = findParent(ar, name);
      if (parent) {
        return parent->checkConst(name, type, coerce, ar,
                                  construct, present);
      }
      present = false;
      actualType = NEW_TYPE(Some);
      setType(ar, name, actualType, true);
      m_declarations[name] = construct;
    }
  }

  if (Type::IsBadTypeConversion(ar, actualType, type, coerce)) {
    ar->getCodeError()->record(construct, type->getKindOf(),
                               actualType->getKindOf());
  }
  return actualType;
}
Esempio n. 16
0
void GlobalStatement::inferTypes(AnalysisResultPtr ar) {
  BlockScopePtr scope = ar->getScope();
  scope->getVariables()->setAttribute(VariableTable::InsideGlobalStatement);
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    if (exp->is(Expression::KindOfSimpleVariable)) {
      SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp);
      VariableTablePtr variables = scope->getVariables();
      const std::string &name = var->getName();
      /* If we have already seen this variable in the current scope and
         it is not a global variable, record this variable as "redeclared"
         which will force Variant type.
       */
      variables->checkRedeclared(name, KindOfGlobalStatement);

      /* If this is not a top-level global statement, the variable also
         needs to be Variant type. This should not be a common use case in
         php code.
       */
      if (!isTopLevel()) {
        variables->addNestedGlobal(name);
      }
      var->setContext(Expression::Declaration);
      var->inferAndCheck(ar, NEW_TYPE(Any), true);

      if (variables->needLocalCopy(name)) {
        variables->forceVariant(ar, name);
        variables->setAttribute(VariableTable::NeedGlobalPointer);
      }

      ConstructPtr decl =
        ar->getVariables()->getDeclaration(var->getName());
      if (decl) {
        ar->getDependencyGraph()->add(DependencyGraph::KindOfGlobalVariable,
                                      ar->getName(),
                                      var->getName(), var,
                                      var->getName(), decl);
      }
    } else {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(shared_from_this(), CodeError::UseDynamicGlobal, exp);
      }
      m_dynamicGlobal = true;
    }
  }
  FunctionScopePtr func = ar->getFunctionScope();
  scope->getVariables()->clearAttribute(VariableTable::InsideGlobalStatement);
}
Esempio n. 17
0
void FunctionStatement::analyzeProgramImpl(AnalysisResultPtr ar) {
  // registering myself as a parent in dependency graph, so that
  // (1) we can tell orphaned parents
  // (2) overwrite non-master copy of function declarations
  if (ar->isFirstPass()) {
    if (m_loc) {
      ar->getDependencyGraph()->addParent(DependencyGraph::KindOfFunctionCall,
                                          "", m_name, shared_from_this());
    } // else it's pseudoMain or artificial functions we added
  }
  FunctionScopePtr func = ar->getFunctionScope(); // containing function scope
  FunctionScopePtr fs = m_funcScope.lock();
  // redeclared functions are automatically volatile
  if (func && fs->isVolatile()) {
    func->getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
  }
  MethodStatement::analyzeProgramImpl(ar);
}
Esempio n. 18
0
TypePtr FunctionScope::setParamType(AnalysisResultPtr ar, int index,
                                    TypePtr type) {
  ASSERT(index >= 0 && index < (int)m_paramTypes.size());
  TypePtr paramType = m_paramTypes[index];

  if (!paramType) paramType = NEW_TYPE(Some);
  type = Type::Coerce(ar, paramType, type);
  if (type && !Type::SameType(paramType, type)) {
    ar->incNewlyInferred();
    if (!ar->isFirstPass()) {
      Logger::Verbose("Corrected type of parameter %d of %s: %s -> %s",
                      index, m_name.c_str(),
                      paramType->toString().c_str(), type->toString().c_str());
    }
  }
  m_paramTypes[index] = type;
  return type;
}
Esempio n. 19
0
void UnaryOpExpression::analyzeProgram(AnalysisResultPtr ar) {
    if (ar->getPhase() == AnalysisResult::AnalyzeFinal && m_op == '@') {
        StatementPtr stmt = ar->getStatementForSilencer();
        ASSERT(stmt);
        m_silencer = stmt->requireSilencers(1);
    }
    if (ar->isFirstPass()) {
        ConstructPtr self = shared_from_this();
        if (m_op == T_INCLUDE || m_op == T_REQUIRE) {
            ar->getCodeError()->record(self, CodeError::UseInclude, self);
        }
    }
    if (m_exp) m_exp->analyzeProgram(ar);
    if (ar->getPhase() == AnalysisResult::AnalyzeFinal && m_op == '@') {
        StatementPtr stmt = ar->getStatementForSilencer();
        ASSERT(stmt);
        stmt->endRequireSilencers(m_silencer);
    }
}
Esempio n. 20
0
void FunctionScope::setReturnType(AnalysisResultPtr ar, TypePtr type) {
  // no change can be made to virtual function's prototype
  if (m_overriding) return;

  if (m_returnType) {
    type = Type::Coerce(ar, m_returnType, type);
    if (type && !Type::SameType(m_returnType, type)) {
      ar->incNewlyInferred();
      if (!ar->isFirstPass()) {
        Logger::Verbose("Corrected function return type %s -> %s",
                        m_returnType->toString().c_str(),
                        type->toString().c_str());
      }
    }
  }
  if (!type->getName().empty()) {
    FileScopePtr fs = getFileScope();
    if (fs) fs->addClassDependency(ar, type->getName());
  }
  m_returnType = type;
}
Esempio n. 21
0
TypePtr ParameterExpression::inferTypes(AnalysisResultPtr ar, TypePtr type,
                                        bool coerce) {
  ASSERT(type->is(Type::KindOfSome) || type->is(Type::KindOfAny));
  TypePtr ret = getTypeSpec(ar, true);

  if (m_defaultValue && !m_ref) {
    ret = m_defaultValue->inferAndCheck(ar, ret, false);
  }

  // parameters are like variables, but we need to remember these are
  // parameters so when variable table is generated, they are not generated
  // as declared variables.
  VariableTablePtr variables = ar->getScope()->getVariables();
  if (ar->isFirstPass()) {
    ret = variables->addParam(m_name, ret, ar, shared_from_this());
  } else {
    // Functions that can be called dynamically have to have
    // variant parameters, even if they have a type hint
    if (ar->getFunctionScope()->isDynamic() ||
        ar->getFunctionScope()->isRedeclaring() ||
        ar->getFunctionScope()->isVirtual()) {
      variables->forceVariant(ar, m_name, VariableTable::AnyVars);
    }
    int p;
    ret = variables->checkVariable(m_name, ret, true, ar, shared_from_this(),
                                   p);
    if (ar->isSecondPass() && ret->is(Type::KindOfSome)) {
      // This is probably too conservative. The problem is that
      // a function never called will have parameter types of Any.
      // Functions that it calls won't be able to accept variant unless
      // it is forced here.
      variables->forceVariant(ar, m_name, VariableTable::AnyVars);
      ret = Type::Variant;
    } else if (ar->getPhase() == AnalysisResult::LastInference &&
               !ret->getName().empty()) {
      addUserClass(ar, ret->getName(), true);
    }
  }
  return ret;
}
Esempio n. 22
0
void IncludeExpression::analyzeProgram(AnalysisResultPtr ar) {
  if (ar->isFirstPass()) {
    ConstructPtr self = shared_from_this();
    if (m_op == T_INCLUDE || m_op == T_REQUIRE) {
      ar->getCodeError()->record(self, CodeError::UseInclude, self);
    }
  }

  string include = getCurrentInclude(ar);
  if (!include.empty()) {
    if (ar->getPhase() == AnalysisResult::AnalyzeInclude) {
      analyzeInclude(ar, include);
    }
  }
  if (!ar->getScope()->inPseudoMain()) {
    VariableTablePtr var = ar->getScope()->getVariables();
    var->setAttribute(VariableTable::ContainsLDynamicVariable);
    var->forceVariants(ar);
  }

  UnaryOpExpression::analyzeProgram(ar);
}
TypePtr ClassConstantExpression::inferTypes(AnalysisResultPtr ar,
                                            TypePtr type, bool coerce) {
  m_valid = false;
  ConstructPtr self = shared_from_this();
  ClassScopePtr cls = ar->resolveClass(m_className);
  if (!cls || cls->isRedeclaring()) {
    if (cls) {
      m_redeclared = true;
      ar->getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
    }
    if (!cls && ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UnknownClass, self);
    }
    return type;
  }
  if (cls->getConstants()->isDynamic(m_varName)) {
    ar->getScope()->getVariables()->
      setAttribute(VariableTable::NeedGlobalPointer);
  }
  if (cls->getConstants()->isExplicitlyDeclared(m_varName)) {
    string name = m_className + "::" + m_varName;
    ConstructPtr decl = cls->getConstants()->getDeclaration(m_varName);
    if (decl) { // No decl means an extension class.
      ar->getDependencyGraph()->add(DependencyGraph::KindOfConstant,
                                    ar->getName(),
                                    name, shared_from_this(), name, decl);
    }
    m_valid = true;
  }
  bool present;
  TypePtr t = cls->checkConst(m_varName, type, coerce, ar,
                              shared_from_this(), present);
  if (present) {
    m_valid = true;
  }

  return t;
}
/**
 * ArrayElementExpression comes from:
 *
 * reference_variable[|expr]
 * ->object_dim_list[|expr]
 * encaps T_VARIABLE[expr]
 * encaps ${T_STRING[expr]}
 */
TypePtr ArrayElementExpression::inferTypes(AnalysisResultPtr ar,
                                           TypePtr type, bool coerce) {
  ConstructPtr self = shared_from_this();

  // handling $GLOBALS[...]
  if (m_variable->is(Expression::KindOfSimpleVariable)) {
    SimpleVariablePtr var =
      dynamic_pointer_cast<SimpleVariable>(m_variable);
    if (var->getName() == "GLOBALS") {
      clearEffect(AccessorEffect);
      m_global = true;
      m_dynamicGlobal = true;
      ar->getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
      VariableTablePtr vars = ar->getVariables();


      if (m_offset && m_offset->is(Expression::KindOfScalarExpression)) {
        ScalarExpressionPtr offset =
          dynamic_pointer_cast<ScalarExpression>(m_offset);

        if (offset->isLiteralString()) {
          m_globalName = offset->getIdentifier();
          if (!m_globalName.empty()) {
            m_dynamicGlobal = false;
            ar->getScope()->getVariables()->
              setAttribute(VariableTable::NeedGlobalPointer);
            TypePtr ret;
            ConstructPtr decl = vars->getDeclaration(m_globalName);
            if (decl) {
              ar->getDependencyGraph()->
                add(DependencyGraph::KindOfGlobalVariable,
                    ar->getName(),
                    m_globalName, self, m_globalName, decl);
            }
            if (coerce) {
              ret = vars->add(m_globalName, type, true, ar, self,
                              ModifierExpressionPtr());
            } else {
              int p;
              ret =
                vars->checkVariable(m_globalName, type, coerce, ar, self, p);
            }
            ar->getScope()->getVariables()->addSuperGlobal(m_globalName);
            return ret;
          }
        }
      } else {
        vars->setAttribute(VariableTable::ContainsDynamicVariable);
      }


      if (hasContext(LValue) || hasContext(RefValue)) {
        if (ar->isFirstPass()) {
          ar->getCodeError()->record(self, CodeError::UseLDynamicVariable,
                                     self);
        }
        ar->getVariables()->forceVariants(ar);
        ar->getVariables()->
          setAttribute(VariableTable::ContainsLDynamicVariable);
      } else {
        if (ar->isFirstPass()) {
          ar->getCodeError()->record(self, CodeError::UseRDynamicVariable,
                                     self);
        }
      }
      if (m_offset) {
        m_offset->inferAndCheck(ar, NEW_TYPE(Primitive), false);
      }
      return m_implementedType = Type::Variant; // so not to lose values
    }
  }
  if ((hasContext(LValue) || hasContext(RefValue)) &&
      !hasContext(UnsetContext)) {
    m_variable->setContext(LValue);
  }

  TypePtr varType;
  if (m_offset) {
    varType = m_variable->inferAndCheck(ar, NEW_TYPE(Sequence), false);
    m_offset->inferAndCheck(ar, NEW_TYPE(Some), false);
  } else {
    if (hasContext(ExistContext) || hasContext(UnsetContext)) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::InvalidArrayElement,
                                   self);
      }
    }
    m_variable->inferAndCheck(ar, Type::Array, true);
  }

  if (varType && Type::SameType(varType, Type::String)) {
    clearEffect(AccessorEffect);
    m_implementedType.reset();
    return Type::String;
  }

  if (varType && Type::SameType(varType, Type::Array)) {
    clearEffect(AccessorEffect);
  }

  if (hasContext(LValue) || hasContext(RefValue)) setEffect(CreateEffect);

  TypePtr ret = propagateTypes(ar, Type::Variant);
  m_implementedType = Type::Variant;
  return ret; // so not to lose values
}
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;
}
void StaticStatement::inferTypes(AnalysisResultPtr ar) {
  BlockScopePtr scope = ar->getScope();
  if (scope->inPseudoMain()) { // static just means to unset at global level
    for (int i = 0; i < m_exp->getCount(); i++) {
      ExpressionPtr exp = (*m_exp)[i];
      if (exp->is(Expression::KindOfAssignmentExpression)) {
        AssignmentExpressionPtr assignment_exp =
          dynamic_pointer_cast<AssignmentExpression>(exp);
        ExpressionPtr variable = assignment_exp->getVariable();
        if (variable->is(Expression::KindOfSimpleVariable)) {
          SimpleVariablePtr var =
            dynamic_pointer_cast<SimpleVariable>(variable);
          var->setContext(Expression::Declaration);
          scope->getVariables()->forceVariant(ar, var->getName(),
                                              VariableTable::AnyStaticVars);
        } else {
          ASSERT(false);
        }
      } else {
        // Expression was optimized away; remove it
        m_exp->removeElement(i--);
      }
    }
    m_exp->inferTypes(ar, Type::Any, true);
    return;
  }
  scope->getVariables()->setAttribute(VariableTable::InsideStaticStatement);
  for (int i = 0; i < m_exp->getCount(); i++) {
    ExpressionPtr exp = (*m_exp)[i];
    VariableTablePtr variables = scope->getVariables();
    if (exp->is(Expression::KindOfAssignmentExpression)) {
      AssignmentExpressionPtr assignment_exp =
        dynamic_pointer_cast<AssignmentExpression>(exp);
      ExpressionPtr variable = assignment_exp->getVariable();
      if (variable->is(Expression::KindOfSimpleVariable)) {
        SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable);
        var->setContext(Expression::Declaration);
        const std::string &name = var->getName();
        /* If we have already seen this variable in the current scope and
           it is not a static variable, record this variable as "redeclared"
           to force Variant type.
         */
        if (ar->isFirstPass()) {
          variables->checkRedeclared(name, KindOfStaticStatement);
        }
        /* If this is not a top-level static statement, the variable also
           needs to be Variant type. This should not be a common use case in
           php code.
         */
        if (!isTopLevel()) {
          variables->addNestedStatic(name);
        }

        if (variables->needLocalCopy(name)) {
          variables->forceVariant(ar, name, VariableTable::AnyStaticVars);
        }
      } else {
        ASSERT(false);
      }
    } else {
      // Expression was optimized away; remove it
      m_exp->removeElement(i--);
      continue;
    }
    exp->inferAndCheck(ar, Type::Any, false);
  }
  scope->getVariables()->clearAttribute(VariableTable::InsideStaticStatement);
}
Esempio n. 27
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;
}
Esempio n. 28
0
FunctionScopePtr MethodStatement::onParseImpl(AnalysisResultPtr ar) {
  int minParam, maxParam;
  ConstructPtr self = shared_from_this();
  minParam = maxParam = 0;
  bool hasRef = false;
  if (m_params) {
    set<string> names;
    int i = 0;
    maxParam = m_params->getCount();
    for (; i < maxParam; i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      if (param->isRef()) hasRef = true;
      if (param->isOptional()) break;
      minParam++;
    }
    for (i++; i < maxParam; i++) {
      ParameterExpressionPtr param =
        dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
      if (param->isRef()) hasRef = true;
      if (!param->isOptional()) {
        ar->getCodeError()->record(self, CodeError::RequiredAfterOptionalParam,
                                   param);
        param->defaultToNull(ar);
      }
    }

    if (ar->isFirstPass()) {
      for (i = 0; i < maxParam; i++) {
        ParameterExpressionPtr param =
          dynamic_pointer_cast<ParameterExpression>((*m_params)[i]);
        if (names.find(param->getName()) == names.end()) {
          names.insert(param->getName());
        } else {
          ar->getCodeError()->record(self,
                                     CodeError::RedundantParameter, param);
          for (int j = 0; j < 1000; j++) {
            string name = param->getName() + lexical_cast<string>(j);
            if (names.find(name) == names.end()) {
              param->rename(name);
              break;
            }
          }
        }
      }
    }
  }

  if (hasRef || m_ref) {
    m_attribute |= FileScope::ContainsReference;
  }

  StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this());
  FunctionScopePtr funcScope
    (new FunctionScope(ar, m_method, m_name, stmt, m_ref, minParam, maxParam,
                       m_modifiers, m_attribute, m_docComment,
                       ar->getFileScope()));
  if (!m_stmt) {
    funcScope->setVirtual();
  }
  m_funcScope = funcScope;

  // TODO: this may have to expand to a concept of "virtual" functions...
  if (ar->getClassScope()) {
    funcScope->disableInline();
    if (m_name.length() > 2 && m_name.substr(0,2) == "__") {
      bool magic = true;
      int paramCount = 0;
      if (m_name == "__destruct") {
        funcScope->setOverriding(Type::Variant);
      } else if (m_name == "__call") {
        funcScope->setOverriding(Type::Variant, Type::String, Type::Array);
        paramCount = 2;
      } else if (m_name == "__set") {
        funcScope->setOverriding(Type::Variant, Type::String, Type::Variant);
        paramCount = 2;
      } else if (m_name == "__get") {
        funcScope->setOverriding(Type::Variant, Type::String);
        paramCount = 1;
      } else if (m_name == "__isset") {
        funcScope->setOverriding(Type::Boolean, Type::String);
        paramCount = 1;
      } else if (m_name == "__unset") {
        funcScope->setOverriding(Type::Variant, Type::String);
        paramCount = 1;
      } else if (m_name == "__sleep") {
        funcScope->setOverriding(Type::Variant);
      } else if (m_name == "__wakeup") {
        funcScope->setOverriding(Type::Variant);
      } else if (m_name == "__set_state") {
        funcScope->setOverriding(Type::Variant, Type::Variant);
        paramCount = 1;
      } else if (m_name == "__tostring") {
        funcScope->setOverriding(Type::String);
      } else if (m_name == "__clone") {
        funcScope->setOverriding(Type::Variant);
      } else {
        paramCount = -1;
        if (m_name != "__construct") {
          ar->getCodeError()->record(self, CodeError::UnknownMagicMethod,
                                     self);
          magic = false;
        }
      }
      if (paramCount >= 0 &&
          (paramCount != minParam || paramCount != maxParam)) {
        ar->getCodeError()->record(self, CodeError::InvalidMagicMethod,
                                   self);
        magic = false;
      }
      if (magic) funcScope->setMagicMethod();
    }
    // ArrayAccess methods
    else if (m_name.length() > 6 && m_name.substr(0, 6) == "offset") {
      if (m_name == "offsetexists") {
        funcScope->setOverriding(Type::Boolean, Type::Variant);
      } else if (m_name == "offsetget") {
        funcScope->setOverriding(Type::Variant, Type::Variant);
      } else if (m_name == "offsetset") {
        funcScope->setOverriding(Type::Variant, Type::Variant, Type::Variant);
      } else if (m_name == "offsetunset") {
        funcScope->setOverriding(Type::Variant, Type::Variant);
      }
    }
  }

  return funcScope;
}
Esempio n. 29
0
TypePtr SimpleFunctionCall::inferAndCheck(AnalysisResultPtr ar, TypePtr type,
                                          bool coerce) {
  reset();

  ConstructPtr self = shared_from_this();

  // handling define("CONSTANT", ...);
  if (m_className.empty()) {
    if (m_type == DefineFunction && 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];
          TypePtr varType = value->inferAndCheck(ar, NEW_TYPE(Some), false);
          ar->getDependencyGraph()->
            addParent(DependencyGraph::KindOfConstant,
                      ar->getName(), varName, self);
          ConstantTablePtr constants =
            ar->findConstantDeclarer(varName)->getConstants();
          if (constants != ar->getConstants()) {
            if (value && !value->isScalar()) {
              constants->setDynamic(ar, varName);
              varType = Type::Variant;
            }
            if (constants->isDynamic(varName)) {
              m_dynamicConstant = true;
              ar->getScope()->getVariables()->
                setAttribute(VariableTable::NeedGlobalPointer);
            } else {
              constants->setType(ar, varName, varType, true);
            }
            // in case the old 'value' has been optimized
            constants->setValue(ar, varName, value);
          }
          return checkTypesImpl(ar, type, Type::Boolean, coerce);
        }
      }
      if (varName.empty() && ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::BadDefine, self);
      }
    } else if (m_type == ExtractFunction) {
      ar->getScope()->getVariables()->forceVariants(ar);
    }
  }

  FunctionScopePtr func;

  // avoid raising both MissingObjectContext and UnknownFunction
  bool errorFlagged = false;

  if (m_className.empty()) {
    func = ar->findFunction(m_name);
  } else {
    ClassScopePtr cls = ar->resolveClass(m_className);
    if (cls && cls->isVolatile()) {
      ar->getScope()->getVariables()
        ->setAttribute(VariableTable::NeedGlobalPointer);
    }
    if (!cls || cls->isRedeclaring()) {
      if (cls) {
        m_redeclaredClass = true;
      }
      if (!cls && ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::UnknownClass, self);
      }
      if (m_params) {
        m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
      }
      return checkTypesImpl(ar, type, Type::Variant, coerce);
    }
    m_derivedFromRedeclaring = cls->derivesFromRedeclaring();
    m_validClass = true;

    if (m_name == "__construct") {
      // if the class is known, php will try to identify class-name ctor
      func = cls->findConstructor(ar, true);
    }
    else {
      func = cls->findFunction(ar, m_name, true, true);
    }

    if (func && !func->isStatic()) {
      ClassScopePtr clsThis = ar->getClassScope();
      FunctionScopePtr funcThis = ar->getFunctionScope();
      if (!clsThis ||
          (clsThis->getName() != m_className &&
           !clsThis->derivesFrom(ar, m_className)) ||
          funcThis->isStatic()) {
        // set the method static to avoid "unknown method" runtime exception
        if (Option::StaticMethodAutoFix && !func->containsThis()) {
          func->setStatic();
        }
        if (ar->isFirstPass()) {
          ar->getCodeError()->record(self, CodeError::MissingObjectContext,
                                     self);
          errorFlagged = true;
        }
        func.reset();
      }
    }
  }
  if (!func || func->isRedeclaring()) {
    if (func) {
      m_redeclared = true;
      ar->getScope()->getVariables()->
        setAttribute(VariableTable::NeedGlobalPointer);
    }
    if (!func && !errorFlagged && ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UnknownFunction, self);
    }
    if (m_params) {
      if (func) {
        FunctionScope::RefParamInfoPtr info =
          FunctionScope::GetRefParamInfo(m_name);
        ASSERT(info);
        for (int i = m_params->getCount(); i--; ) {
          if (info->isRefParam(i)) {
            m_params->markParam(i, canInvokeFewArgs());
          }
        }
      }
      m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
    }
    return checkTypesImpl(ar, type, Type::Variant, coerce);
  }
  m_builtinFunction = !func->isUserFunction();

  if (m_redeclared) {
    if (m_params) {
      m_params->inferAndCheck(ar, NEW_TYPE(Some), false);
    }
    return checkTypesImpl(ar, type, type, coerce);
  }

  CHECK_HOOK(beforeSimpleFunctionCallCheck);

  m_valid = true;
  type = checkParamsAndReturn(ar, type, coerce, func);

  if (!m_valid && m_params) {
    m_params->markParams(false);
  }

  CHECK_HOOK(afterSimpleFunctionCallCheck);

  return type;
}
TypePtr ObjectMethodExpression::inferAndCheck(AnalysisResultPtr ar,
                                              TypePtr type, bool coerce) {
  reset();

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

  if (m_name.empty()) {
    // if dynamic property or method, we have nothing to find out
    if (ar->isFirstPass()) {
      ar->getCodeError()->record(self, CodeError::UseDynamicMethod, self);
    }
    m_nameExp->inferAndCheck(ar, Type::String, false);
    setInvokeParams(ar);
    // we have to use a variant to hold dynamic value
    return checkTypesImpl(ar, type, Type::Variant, coerce);
  }

  ClassScopePtr cls = m_classScope;
  if (objectType && !objectType->getName().empty()) {
    cls = ar->findExactClass(objectType->getName());
  }

  if (!cls) {
    if (ar->isFirstPass()) {
      // call resolveClass to mark functions as dynamic
      // but we cant do anything else with the result.
      resolveClass(ar, m_name);
      if (!ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
        ar->getCodeError()->record(self, CodeError::UnknownObjectMethod, self);
      }
    }

    m_classScope.reset();
    m_funcScope.reset();

    setInvokeParams(ar);
    return checkTypesImpl(ar, type, Type::Variant, coerce);
  }

  if (m_classScope != cls) {
    m_classScope = cls;
    m_funcScope.reset();
  }

  FunctionScopePtr func = m_funcScope;
  if (!func) {
    func = cls->findFunction(ar, m_name, true, true);
    if (!func) {
      if (!cls->hasAttribute(ClassScope::HasUnknownMethodHandler, ar)) {
        if (ar->classMemberExists(m_name, AnalysisResult::MethodName)) {
          // TODO: we could try to find out class derivation is present...
          ar->getCodeError()->record(self,
                                     CodeError::DerivedObjectMethod, self);
          // we have to make sure the method is in invoke list
          setDynamicByIdentifier(ar, m_name);
        } else {
          ar->getCodeError()->record(self,
                                     CodeError::UnknownObjectMethod, self);
        }
      }

      m_valid = false;
      setInvokeParams(ar);
      return checkTypesImpl(ar, type, Type::Variant, coerce);
    }
    m_funcScope = func;
  }

  bool valid = true;
  m_bindClass = func->isStatic();

  // use $this inside a static function
  if (m_object->isThis()) {
    FunctionScopePtr localfunc = ar->getFunctionScope();
    if (localfunc->isStatic()) {
      if (ar->isFirstPass()) {
        ar->getCodeError()->record(self, CodeError::MissingObjectContext,
                                   self);
      }
      valid = false;
    }
  }

  // invoke() will return Variant
  if (func->isVirtual() || !m_object->getType()->isSpecificObject()) {
    valid = false;
  }

  if (!valid) {
    setInvokeParams(ar);
    checkTypesImpl(ar, type, Type::Variant, coerce);
    m_valid = false; // so we use invoke() syntax
    func->setDynamic();
    return m_actualType;
  }

  return checkParamsAndReturn(ar, type, coerce, func);
}