void ClassStatement::onParse(AnalysisResultPtr ar) { ClassScope::KindOf kindOf = ClassScope::KindOfObjectClass; switch (m_type) { case T_CLASS: kindOf = ClassScope::KindOfObjectClass; break; case T_ABSTRACT: kindOf = ClassScope::KindOfAbstractClass; break; case T_FINAL: kindOf = ClassScope::KindOfFinalClass; break; default: ASSERT(false); } vector<string> bases; if (!m_parent.empty()) { bases.push_back(m_parent); ar->addNonFinal(m_parent); } if (m_base) m_base->getStrings(bases); StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); ClassScopePtr classScope(new ClassScope(kindOf, m_originalName, m_parent, bases, m_docComment, stmt, ar->getFileScope())); m_classScope = classScope; if (!ar->getFileScope()->addClass(ar, classScope)) { m_ignored = true; return; } ar->recordClassSource(m_name, ar->getFileScope()->getName()); if (m_stmt) { ar->pushScope(classScope); bool seenConstruct = false; for (int i = 0; i < m_stmt->getCount(); i++) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && meth->getName() == "__construct") { seenConstruct = true; break; } } for (int i = 0; i < m_stmt->getCount(); i++) { if (!seenConstruct) { MethodStatementPtr meth = dynamic_pointer_cast<MethodStatement>((*m_stmt)[i]); if (meth && classScope && meth->getName() == classScope->getName() && !meth->getModifiers()->isStatic()) { // class-name constructor classScope->setAttribute(ClassScope::classNameConstructor); } } IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParse(ar); } ar->popScope(); } }
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); } }
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()); }
int CodeGenerator::createNewId(AnalysisResultPtr ar) { FileScopePtr fs = ar->getFileScope(); if (fs) { return createNewId(fs->getName()); } return createNewId(""); }
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 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; }
void FunctionStatement::onParse(AnalysisResultPtr ar) { // note it's important to add to file scope, not a pushed FunctionContainer, // as a function may be declared inside a class's method, yet this function // is a global function, not a class method. FileScopePtr fileScope = ar->getFileScope(); if (!fileScope->addFunction(ar, onParseImpl(ar))) { m_ignored = true; return; } ar->recordFunctionSource(m_name, m_loc, fileScope->getName()); }
int CodeGenerator::createNewLocalId(AnalysisResultPtr ar) { FunctionScopePtr func = ar->getFunctionScope(); if (func) { return func->nextInlineIndex(); } FileScopePtr fs = ar->getFileScope(); if (fs) { return createNewId(fs->getName()); } return createNewId(""); }
void InterfaceStatement::onParse(AnalysisResultPtr ar) { vector<string> bases; if (m_base) m_base->getStrings(bases); StatementPtr stmt = dynamic_pointer_cast<Statement>(shared_from_this()); ClassScopePtr classScope(new ClassScope(ClassScope::KindOfInterface, m_name, "", bases, m_docComment, stmt, ar->getFileScope())); m_classScope = classScope; ar->getFileScope()->addClass(ar, classScope); ar->getDependencyGraph()->addParent(DependencyGraph::KindOfProgramUserClass, "", m_originalName, stmt); if (m_stmt) { ar->pushScope(classScope); for (int i = 0; i < m_stmt->getCount(); i++) { IParseHandlerPtr ph = dynamic_pointer_cast<IParseHandler>((*m_stmt)[i]); ph->onParse(ar); } ar->popScope(); } }
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()); } } } }
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; }
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); } }