void SwitchStatement::analyzeProgram(AnalysisResultPtr ar) { m_exp->analyzeProgram(ar); if (m_cases) m_cases->analyzeProgram(ar); if (ar->getPhase() == AnalysisResult::AnalyzeAll && m_exp->is(Expression::KindOfSimpleVariable)) { auto exp = dynamic_pointer_cast<SimpleVariable>(m_exp); if (exp && exp->getSymbol() && exp->getSymbol()->isClassName()) { // Mark some classes as volatile since the name is used in switch for (int i = 0; i < m_cases->getCount(); i++) { auto stmt = dynamic_pointer_cast<CaseStatement>((*m_cases)[i]); assert(stmt); ExpressionPtr caseCond = stmt->getCondition(); if (caseCond && caseCond->isScalar()) { auto name = dynamic_pointer_cast<ScalarExpression>(caseCond); if (name && name->isLiteralString()) { string className = name->getLiteralString(); ClassScopePtr cls = ar->findClass(toLower(className)); if (cls && cls->isUserClass()) { cls->setVolatile(); } } } } // Also note this down as code error ConstructPtr self = shared_from_this(); Compiler::Error(Compiler::ConditionalClassLoading, self); } } }
void AnalysisResult::analyzeProgram() { AnalysisResultPtr ar = shared_from_this(); getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable); getVariables()->setAttribute(VariableTable::ForceGlobal); // Analyze Includes Logger::Verbose("Analyzing Includes"); sort(m_fileScopes.begin(), m_fileScopes.end(), by_filename); // fixed order for (auto& scope : m_fileScopes) { collectFunctionsAndClasses(scope); } // Keep generated code identical without randomness canonicalizeSymbolOrder(); markRedeclaringClasses(); // Analyze some special cases for (auto& cls_name : Option::VolatileClasses) { ClassScopePtr cls = findClass(toLower(cls_name)); if (cls && cls->isUserClass()) { cls->setVolatile(); } } checkClassDerivations(); resolveNSFallbackFuncs(); // Analyze All Logger::Verbose("Analyzing All"); analyzeProgram(AnalysisResult::AnalyzeAll); /* Note that cls->collectMethods() can add entries to m_classDecs, which can invalidate iterators. So we have to create an array and then iterate over that. The new entries added to m_classDecs are always empty, so it doesnt matter that we dont include them in the iteration */ std::vector<ClassScopePtr> classes; classes.reserve(m_classDecs.size()); for (auto& pair : m_classDecs) { for (auto cls : pair.second) { classes.push_back(cls); } } // Collect methods for (auto cls : classes) { StringToFunctionScopePtrMap methods; cls->collectMethods(ar, methods, true /* include privates */); } for (auto& item : m_systemClasses) { StringToFunctionScopePtrMap methods; item.second->collectMethods(ar, methods, true /* include privates */); } }
void ClassStatement::analyzeProgramImpl(AnalysisResultPtr ar) { vector<string> bases; if (!m_parent.empty()) bases.push_back(m_parent); if (m_base) m_base->getStrings(bases); for (unsigned int i = 0; i < bases.size(); i++) { string className = bases[i]; addUserClass(ar, bases[i]); } ClassScopePtr classScope = m_classScope.lock(); if (hasHphpNote("Volatile")) { classScope->setVolatile(); } checkVolatile(ar); if (m_stmt) { ar->pushScope(classScope); m_stmt->analyzeProgram(ar); ar->popScope(); } if (ar->getPhase() != AnalysisResult::AnalyzeAll) return; DependencyGraphPtr dependencies = ar->getDependencyGraph(); for (unsigned int i = 0; i < bases.size(); i++) { ClassScopePtr cls = ar->findClass(bases[i]); if (cls) { if ((!cls->isInterface() && (m_parent.empty() || i > 0 )) || (cls->isInterface() && (!m_parent.empty() && i == 0 ))) { ar->getCodeError()->record(CodeError::InvalidDerivation, shared_from_this(), ConstructPtr(), cls->getOriginalName().c_str()); } if (dependencies->checkCircle(DependencyGraph::KindOfClassDerivation, m_originalName, cls->getOriginalName())) { ar->getCodeError()->record(CodeError::InvalidDerivation, shared_from_this(), ConstructPtr(), cls->getOriginalName().c_str()); m_parent = ""; m_base = ExpressionListPtr(); classScope->clearBases(); } else if (cls->isUserClass()) { dependencies->add(DependencyGraph::KindOfClassDerivation, ar->getName(), m_originalName, shared_from_this(), cls->getOriginalName(), cls->getStmt()); } } } }
void InterfaceStatement::checkVolatile(AnalysisResultPtr ar) { ClassScopePtr classScope = m_classScope.lock(); // redeclared classes/interfaces are automatically volatile if (!classScope->isVolatile()) { if (checkVolatileBases(ar)) { // if any base is volatile, the class is volatile classScope->setVolatile(); } } if (classScope->isVolatile()) { ar->getFunctionScope()->getVariables()-> setAttribute(VariableTable::NeedGlobalPointer); } }
void ClassStatement::analyzeProgram(AnalysisResultPtr ar) { vector<string> bases; if (!m_parent.empty()) bases.push_back(m_parent); if (m_base) m_base->getStrings(bases); for (unsigned int i = 0; i < bases.size(); i++) { string className = bases[i]; addUserClass(ar, bases[i]); } ClassScopePtr classScope = m_classScope.lock(); if (hasHphpNote("Volatile")) classScope->setVolatile(); FunctionScopePtr func = ar->getFunctionScope(); // redeclared classes are automatically volatile if (classScope->isVolatile()) { func->getVariables()->setAttribute(VariableTable::NeedGlobalPointer); } if (m_stmt) { ar->pushScope(classScope); m_stmt->analyzeProgram(ar); ar->popScope(); } DependencyGraphPtr dependencies = ar->getDependencyGraph(); for (unsigned int i = 0; i < bases.size(); i++) { ClassScopePtr cls = ar->findClass(bases[i]); if (cls) { if (dependencies->checkCircle(DependencyGraph::KindOfClassDerivation, m_originalName, cls->getOriginalName())) { ClassScopePtr classScope = m_classScope.lock(); ar->getCodeError()->record(CodeError::InvalidDerivation, shared_from_this(), ConstructPtr(), cls->getOriginalName()); m_parent = ""; m_base = ExpressionListPtr(); classScope->clearBases(); } else if (cls->isUserClass()) { dependencies->add(DependencyGraph::KindOfClassDerivation, ar->getName(), m_originalName, shared_from_this(), cls->getOriginalName(), cls->getStmt()); } } } }
void InterfaceStatement::analyzeProgramImpl(AnalysisResultPtr ar) { ClassScopePtr classScope = m_classScope.lock(); if (hasHphpNote("Volatile")) classScope->setVolatile(); if (m_stmt) { classScope->setIncludeLevel(ar->getIncludeLevel()); ar->pushScope(classScope); m_stmt->analyzeProgram(ar); ar->popScope(); } ar->recordClassSource(m_name, ar->getFileScope()->getName()); checkVolatile(ar); if (ar->getPhase() != AnalysisResult::AnalyzeAll) return; vector<string> bases; if (m_base) m_base->getStrings(bases); DependencyGraphPtr dependencies = ar->getDependencyGraph(); for (unsigned int i = 0; i < bases.size(); i++) { ClassScopePtr cls = ar->findClass(bases[i]); if (cls) { if (!cls->isInterface()) { ar->getCodeError()->record(CodeError::InvalidDerivation, shared_from_this(), ConstructPtr(), cls->getOriginalName()); } if (dependencies->checkCircle(DependencyGraph::KindOfClassDerivation, m_originalName, cls->getOriginalName())) { ClassScopePtr classScope = m_classScope.lock(); ar->getCodeError()->record(CodeError::InvalidDerivation, shared_from_this(), ConstructPtr(), cls->getOriginalName()); m_base = ExpressionListPtr(); classScope->clearBases(); } else if (cls->isUserClass()) { dependencies->add(DependencyGraph::KindOfClassDerivation, ar->getName(), m_originalName, shared_from_this(), cls->getOriginalName(), cls->getStmt()); } } } }
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 ¶ms = *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); } }
void AnalysisResult::analyzeProgram(bool system /* = false */) { AnalysisResultPtr ar = shared_from_this(); getVariables()->setAttribute(VariableTable::ContainsLDynamicVariable); getVariables()->setAttribute(VariableTable::ContainsExtract); getVariables()->setAttribute(VariableTable::ForceGlobal); // Analyze Includes Logger::Verbose("Analyzing Includes"); sort(m_fileScopes.begin(), m_fileScopes.end(), by_filename); // fixed order unsigned int i = 0; for (i = 0; i < m_fileScopes.size(); i++) { collectFunctionsAndClasses(m_fileScopes[i]); } // Keep generated code identical without randomness canonicalizeSymbolOrder(); markRedeclaringClasses(); // Analyze some special cases for (auto& cls_name : Option::VolatileClasses) { ClassScopePtr cls = findClass(toLower(cls_name)); if (cls && cls->isUserClass()) { cls->setVolatile(); } } checkClassDerivations(); resolveNSFallbackFuncs(); // Analyze All Logger::Verbose("Analyzing All"); setPhase(AnalysisResult::AnalyzeAll); for (i = 0; i < m_fileScopes.size(); i++) { m_fileScopes[i]->analyzeProgram(ar); } /* Note that cls->collectMethods() can add entries to m_classDecs, which can invalidate iterators. So we have to create an array and then iterate over that. The new entries added to m_classDecs are always empty, so it doesnt matter that we dont include them in the iteration */ std::vector<ClassScopePtr> classes; classes.reserve(m_classDecs.size()); for (auto& pair : m_classDecs) { for (auto cls : pair.second) { classes.push_back(cls); } } // Collect methods for (auto cls : classes) { StringToFunctionScopePtrMap methods; cls->collectMethods(ar, methods, true /* include privates */); bool needAbstractMethodImpl = (!cls->isAbstract() && !cls->isInterface() && cls->derivesFromRedeclaring() == Derivation::Normal && !cls->getAttribute(ClassScope::UsesUnknownTrait)); for (auto& pair : methods) { auto func = pair.second; if (Option::WholeProgram && !func->hasImpl() && needAbstractMethodImpl) { auto tmpFunc = cls->findFunction(ar, func->getScopeName(), true, true); always_assert(!tmpFunc || !tmpFunc->hasImpl()); Compiler::Error(Compiler::MissingAbstractMethodImpl, func->getStmt(), cls->getStmt()); } } } for (auto& item : m_systemClasses) { StringToFunctionScopePtrMap methods; item.second->collectMethods(ar, methods, true /* include privates */); } }