TypePtr ParameterExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { assert(type->is(Type::KindOfSome) || type->is(Type::KindOfAny)); TypePtr ret = getTypeSpec(ar, true); VariableTablePtr variables = getScope()->getVariables(); // Functions that can be called dynamically have to have // variant parameters, even if they have a type hint if ((Option::AllDynamic || getFunctionScope()->isDynamic()) || getFunctionScope()->isRedeclaring() || getFunctionScope()->isVirtual()) { if (!Option::HardTypeHints || !ret->isExactType()) { variables->forceVariant(ar, m_name, VariableTable::AnyVars); ret = Type::Variant; } } if (m_defaultValue && !m_ref) { TypePtr r = m_defaultValue->inferAndCheck(ar, Type::Some, false); if (!m_defaultValue->is(KindOfConstantExpression) || !static_pointer_cast<ConstantExpression>(m_defaultValue)->isNull()) { ret = Type::Coerce(ar, r, ret); } } // 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. return variables->addParamLike(m_name, ret, ar, shared_from_this(), getScope()->isFirstPass()); }
void GlobalStatement::inferTypes(AnalysisResultPtr ar) { IMPLEMENT_INFER_AND_CHECK_ASSERT(getScope()); BlockScopePtr scope = getScope(); for (int i = 0; i < m_exp->getCount(); i++) { ExpressionPtr exp = (*m_exp)[i]; VariableTablePtr variables = scope->getVariables(); variables->setAttribute(VariableTable::NeedGlobalPointer); if (exp->is(Expression::KindOfSimpleVariable)) { SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(exp); 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->setAttribute(VariableTable::InsideGlobalStatement); variables->checkRedeclared(name, KindOfGlobalStatement); variables->addLocalGlobal(name); var->setContext(Expression::Declaration); var->inferAndCheck(ar, Type::Any, true); variables->forceVariant(ar, name, VariableTable::AnyVars); variables->clearAttribute(VariableTable::InsideGlobalStatement); } else { variables->forceVariants(ar, VariableTable::AnyVars); variables->setAttribute(VariableTable::ContainsLDynamicVariable); assert(exp->is(Expression::KindOfDynamicVariable)); exp->inferAndCheck(ar, Type::Any, true); } } }
TypePtr ClosureExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { if (m_vars) { assert(m_values && m_values->getCount() == m_vars->getCount()); // containing function's variable table (not closure function's) VariableTablePtr variables = getScope()->getVariables(); // closure function's variable table VariableTablePtr cvariables = m_func->getFunctionScope()->getVariables(); // force all reference use vars into variant for this function scope for (int i = 0; i < m_vars->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_vars)[i]); const string &name = param->getName(); if (param->isRef()) { variables->forceVariant(ar, name, VariableTable::AnyVars); } } // infer the types of the values m_values->inferAndCheck(ar, Type::Some, false); // coerce the types inferred from m_values into m_vars for (int i = 0; i < m_vars->getCount(); i++) { ExpressionPtr value = (*m_values)[i]; ParameterExpressionPtr var = dynamic_pointer_cast<ParameterExpression>((*m_vars)[i]); assert(!var->getExpectedType()); assert(!var->getImplementedType()); if (var->isRef()) { var->setActualType(Type::Variant); } else { TypePtr origVarType(var->getActualType() ? var->getActualType() : Type::Some); var->setActualType(Type::Coerce(ar, origVarType, value->getType())); } } { // this lock isn't technically needed for thread-safety, since // the dependencies are all set up. however, the lock assertions // will fail if we don't acquire it. GET_LOCK(m_func->getFunctionScope()); // bootstrap the closure function's variable table with // the types from m_vars for (int i = 0; i < m_vars->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_vars)[i]); const string &name = param->getName(); cvariables->addParamLike(name, param->getType(), ar, shared_from_this(), getScope()->isFirstPass()); } } } return s_ClosureType; }
TypePtr ParameterExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ASSERT(type->is(Type::KindOfSome) || type->is(Type::KindOfAny)); TypePtr ret = getTypeSpec(ar, true); VariableTablePtr variables = getScope()->getVariables(); // Functions that can be called dynamically have to have // variant parameters, even if they have a type hint if (getFunctionScope()->isDynamic() || getFunctionScope()->isRedeclaring() || getFunctionScope()->isVirtual()) { if (Option::HardTypeHints && (ret->is(Type::KindOfArray) || ret->is(Type::KindOfObject))) { } else { variables->forceVariant(ar, m_name, VariableTable::AnyVars); ret = Type::Variant; } } 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. if (getScope()->isFirstPass()) { ret = variables->add(m_name, ret, false, ar, shared_from_this(), ModifierExpressionPtr()); } else { int p; ret = variables->checkVariable(m_name, ret, true, ar, shared_from_this(), p); if (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; } } return ret; }
TypePtr ParameterExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { ASSERT(type->is(Type::KindOfSome) || type->is(Type::KindOfAny)); TypePtr ret = getTypeSpec(ar); 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); } 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); ret = Type::Variant; } else if (ar->getPhase() == AnalysisResult::LastInference && !ret->getName().empty()) { addUserClass(ar, ret->getName(), true); } } return ret; }
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); }
TypePtr ClosureExpression::inferTypes(AnalysisResultPtr ar, TypePtr type, bool coerce) { m_func->inferTypes(ar); if (m_values) m_values->inferAndCheck(ar, Type::Some, false); if (m_vars) { // containing function's variable table (not closure function's) VariableTablePtr variables = getScope()->getVariables(); for (int i = 0; i < m_vars->getCount(); i++) { ParameterExpressionPtr param = dynamic_pointer_cast<ParameterExpression>((*m_vars)[i]); string name = param->getName(); if (param->isRef()) { variables->forceVariant(ar, name, VariableTable::AnyVars); } } } return Type::CreateObjectType("Closure"); }
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); }