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); }
void ClassVariable::onParse(AnalysisResultPtr ar) { ModifierExpressionPtr modifiers = ar->getScope()->setModifiers(m_modifiers); for (int i = 0; i < m_declaration->getCount(); i++) { VariableTablePtr variables = ar->getScope()->getVariables(); ExpressionPtr exp = (*m_declaration)[i]; if (exp->is(Expression::KindOfAssignmentExpression)) { AssignmentExpressionPtr assignment = dynamic_pointer_cast<AssignmentExpression>(exp); ExpressionPtr var = assignment->getVariable(); const std::string &name = dynamic_pointer_cast<SimpleVariable>(var)->getName(); if (variables->isPresent(name)) { ar->getCodeError()->record(CodeError::DeclaredVariableTwice, exp); } IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>(exp); ph->onParse(ar); } else { const std::string &name = dynamic_pointer_cast<SimpleVariable>(exp)->getName(); if (variables->isPresent(name)) { ar->getCodeError()->record(CodeError::DeclaredVariableTwice, exp); } variables->add(name, TypePtr(), false, ar, exp, m_modifiers); } } ar->getScope()->setModifiers(modifiers); }
void IncludeExpression::analyzeInclude(AnalysisResultPtr ar, const std::string &include) { ASSERT(ar->getPhase() == AnalysisResult::AnalyzeInclude); ConstructPtr self = shared_from_this(); FileScopePtr file = ar->findFileScope(include, true); if (!file) { ar->getCodeError()->record(self, CodeError::PHPIncludeFileNotFound, self); return; } if (include.find("compiler/") != 0 || include.find("/compiler/") == string::npos) { ar->getCodeError()->record(self, CodeError::PHPIncludeFileNotInLib, self, ConstructPtr(), include.c_str()); } if (!isFileLevel()) { // Not unsupported but potentially bad ar->getCodeError()->record(self, CodeError::UseDynamicInclude, self); } ar->getDependencyGraph()->add (DependencyGraph::KindOfProgramMaxInclude, ar->getName(), file->getName(), StatementPtr()); ar->getDependencyGraph()->addParent (DependencyGraph::KindOfProgramMinInclude, ar->getName(), file->getName(), StatementPtr()); FunctionScopePtr func = ar->getFunctionScope(); ar->getFileScope()->addIncludeDependency(ar, m_include, func && func->isInlined()); }
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; }
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); } } }
void SimpleFunctionCall::onParse(AnalysisResultPtr ar) { if (m_class) return; FileScopePtr fs = ar->getFileScope(); ConstructPtr self = shared_from_this(); if (m_className.empty()) { CodeErrorPtr codeError = ar->getCodeError(); switch (m_type) { case CreateFunction: if (m_params->getCount() == 2 && (*m_params)[0]->isLiteralString() && (*m_params)[1]->isLiteralString()) { FunctionScopePtr func = ar->getFunctionScope(); if (func) func->disableInline(); string params = (*m_params)[0]->getLiteralString(); string body = (*m_params)[1]->getLiteralString(); m_lambda = CodeGenerator::GetNewLambda(); string code = "function " + m_lambda + "(" + params + ") " "{" + body + "}"; ar->appendExtraCode(code); } break; case VariableArgumentFunction: ar->getFileScope()->setAttribute(FileScope::VariableArgument); break; case ExtractFunction: ar->getCodeError()->record(self, CodeError::UseExtract, self); ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable); ar->getFileScope()->setAttribute(FileScope::ContainsExtract); break; case CompactFunction: ar->getFileScope()->setAttribute(FileScope::ContainsDynamicVariable); ar->getFileScope()->setAttribute(FileScope::ContainsCompact); break; case ShellExecFunction: ar->getCodeError()->record(self, CodeError::UseShellExec, self); break; case GetDefinedVarsFunction: ar->getFileScope()->setAttribute(FileScope::ContainsGetDefinedVars); ar->getFileScope()->setAttribute(FileScope::ContainsCompact); break; default: CHECK_HOOK(onSimpleFunctionCallFuncType); break; } } string call = getText(); string name = m_name; if (!m_className.empty()) { name = m_className + "::" + name; } ar->getDependencyGraph()->add(DependencyGraph::KindOfFunctionCall, call, shared_from_this(), name); }
TypePtr ConstantTable::check(const std::string &name, TypePtr type, bool coerce, AnalysisResultPtr ar, ConstructPtr construct, const std::vector<std::string> &bases, BlockScope *&defScope) { TypePtr actualType; defScope = NULL; if (name == "true" || name == "false") { actualType = Type::Boolean; } else { Symbol *sym = getSymbol(name, true); if (!sym->valueSet()) { if (ar->getPhase() != AnalysisResult::AnalyzeInclude) { actualType = checkBases(name, type, coerce, ar, construct, bases, defScope); if (defScope) return actualType; ar->getCodeError()->record(CodeError::UseUndeclaredConstant, construct); if (m_blockScope.is(BlockScope::ClassScope)) { actualType = Type::Variant; } else { actualType = Type::String; } setType(ar, sym, actualType, true); } } else { if (sym->getCoerced()) { defScope = &m_blockScope; actualType = sym->getCoerced(); if (actualType->is(Type::KindOfSome) || actualType->is(Type::KindOfAny)) { setType(ar, sym, type, true); return type; } } else { actualType = checkBases(name, type, coerce, ar, construct, bases, defScope); if (defScope) return actualType; actualType = Type::Some; setType(ar, sym, actualType, true); sym->setDeclaration(construct); } } } if (Type::IsBadTypeConversion(ar, actualType, type, coerce)) { ar->getCodeError()->record(construct, type->getKindOf(), actualType->getKindOf()); } return actualType; }
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; }
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()); } } } }
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; }
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; }
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; }
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); } } }
void UnaryOpExpression::onParse(AnalysisResultPtr ar) { if (m_op == T_EVAL) { ConstructPtr self = shared_from_this(); ar->getCodeError()->record(self, CodeError::UseEvaluation, self); ar->getFileScope()->setAttribute(FileScope::ContainsLDynamicVariable); } }
TypePtr FunctionCall::checkParamsAndReturn(AnalysisResultPtr ar, TypePtr type, bool coerce, FunctionScopePtr func) { ConstructPtr self = shared_from_this(); ar->getDependencyGraph()->add(DependencyGraph::KindOfFunctionCall, ar->getName(), getText(), self, func->getFullName(), func->getStmt()); TypePtr frt = func->getReturnType(); if (!frt) { m_voidReturn = true; setActualType(TypePtr()); if (!type->is(Type::KindOfAny)) { if (!m_allowVoidReturn && ar->isSecondPass() && !func->isAbstract()) { ar->getCodeError()->record(self, CodeError::UseVoidReturn, self); } m_voidWrapper = true; } } else { m_voidReturn = false; m_voidWrapper = false; type = checkTypesImpl(ar, type, frt, coerce); } m_extraArg = func->inferParamTypes(ar, self, m_params, m_valid); m_variableArgument = func->isVariableArgument(); if (m_valid) { m_implementedType.reset(); } else { m_implementedType = Type::Variant; } return type; }
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); }
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 IncludeExpression::onParse(AnalysisResultPtr ar) { // See if we can get a string literal preOptimize(ar); m_include = ar->getDependencyGraph()->add (DependencyGraph::KindOfPHPInclude, shared_from_this(), m_exp, ar->getCodeError(), m_documentRoot); }
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; }
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; }
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 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); }
void StaticStatement::analyzeProgramImpl(AnalysisResultPtr ar) { m_exp->analyzeProgram(ar); BlockScopePtr scope = ar->getScope(); for (int i = 0; i < m_exp->getCount(); i++) { ExpressionPtr exp = (*m_exp)[i]; ExpressionPtr variable; ExpressionPtr value; if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { // turn static $a; into static $a = null; if (exp->is(Expression::KindOfSimpleVariable)) { variable = dynamic_pointer_cast<SimpleVariable>(exp); exp = AssignmentExpressionPtr (new AssignmentExpression(exp->getLocation(), Expression::KindOfAssignmentExpression, variable, CONSTANT("null"), false)); (*m_exp)[i] = exp; } } if (exp->is(Expression::KindOfAssignmentExpression)) { AssignmentExpressionPtr assignment_exp = dynamic_pointer_cast<AssignmentExpression>(exp); variable = assignment_exp->getVariable(); value = assignment_exp->getValue(); } else { ASSERT(false); } SimpleVariablePtr var = dynamic_pointer_cast<SimpleVariable>(variable); if (ar->getPhase() == AnalysisResult::AnalyzeInclude) { if (scope->getVariables()->setStaticInitVal(var->getName(), value)) { ar->getCodeError()->record(CodeError::DeclaredStaticVariableTwice, exp); } } else if (ar->getPhase() == AnalysisResult::AnalyzeAll) { // update initial value const string &name = var->getName(); ExpressionPtr initValue = (dynamic_pointer_cast<Expression> (scope->getVariables()->getStaticInitVal(name)))->clone(); exp = AssignmentExpressionPtr (new AssignmentExpression(exp->getLocation(), Expression::KindOfAssignmentExpression, variable, initValue, false)); (*m_exp)[i] = exp; } } }
ExpressionPtr IncludeExpression::preOptimize(AnalysisResultPtr ar) { if (ExpressionPtr rep = UnaryOpExpression::preOptimize(ar)) { return rep; } if (ar->getPhase() >= AnalysisResult::FirstPreOptimize) { if (m_include.empty()) { m_include = ar->getDependencyGraph()->add (DependencyGraph::KindOfPHPInclude, shared_from_this(), m_exp, ar->getCodeError(), m_documentRoot); m_depsSet = false; } if (!m_depsSet && !m_include.empty()) { analyzeInclude(ar, m_include); m_depsSet = true; } } return ExpressionPtr(); }
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 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); } }
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; }