Arquivos
hhvm/hphp/compiler/analysis/class_scope.cpp
T
Paul Tarjan 4da410ab58 Rename closures and generators
Closures and generators are really hard to reason about in backtraces.

For generators, I took the exact name of the method and put ##$continuation##. I put it at the end so it reads exactly like a method name since in WWW we use them almost the same as the method itself.

For closures, I named the class ##Closue$Class::method#num## where num is an optional autoincrementing number.

I decided to stop prefixing the methods with ##0## which is breaking the reflection tests.

This basically redoes D782498 and D750608 but it works in repo mode.
2013-05-20 13:52:30 -07:00

1455 linhas
50 KiB
C++

/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#include "hphp/compiler/analysis/class_scope.h"
#include "hphp/compiler/analysis/analysis_result.h"
#include "hphp/compiler/analysis/code_error.h"
#include "hphp/compiler/analysis/constant_table.h"
#include "hphp/compiler/analysis/file_scope.h"
#include "hphp/compiler/analysis/function_scope.h"
#include "hphp/compiler/analysis/variable_table.h"
#include "hphp/compiler/construct.h"
#include "hphp/compiler/expression/class_constant_expression.h"
#include "hphp/compiler/expression/closure_expression.h"
#include "hphp/compiler/expression/constant_expression.h"
#include "hphp/compiler/expression/scalar_expression.h"
#include "hphp/compiler/expression/unary_op_expression.h"
#include "hphp/compiler/expression/simple_function_call.h"
#include "hphp/compiler/option.h"
#include "hphp/compiler/parser/parser.h"
#include "hphp/compiler/statement/interface_statement.h"
#include "hphp/compiler/statement/function_statement.h"
#include "hphp/compiler/statement/method_statement.h"
#include "hphp/compiler/statement/statement_list.h"
#include "hphp/runtime/base/builtin_functions.h"
#include "hphp/runtime/base/class_info.h"
#include "hphp/compiler/statement/class_variable.h"
#include "hphp/compiler/statement/class_constant.h"
#include "hphp/compiler/statement/use_trait_statement.h"
#include "hphp/compiler/statement/trait_prec_statement.h"
#include "hphp/compiler/statement/trait_alias_statement.h"
#include "hphp/runtime/base/zend/zend_string.h"
#include "hphp/util/util.h"
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
using namespace HPHP;
using std::map;
///////////////////////////////////////////////////////////////////////////////
ClassScope::ClassScope(KindOf kindOf, const std::string &name,
const std::string &parent,
const vector<string> &bases,
const std::string &docComment, StatementPtr stmt,
const std::vector<UserAttributePtr> &attrs)
: BlockScope(name, docComment, stmt, BlockScope::ClassScope),
m_parent(parent), m_bases(bases), m_attribute(0), m_redeclaring(-1),
m_kindOf(kindOf), m_derivesFromRedeclaring(FromNormal),
m_traitStatus(NOT_FLATTENED), m_volatile(false),
m_persistent(false), m_derivedByDynamic(false),
m_sep(false), m_needsCppCtor(false), m_needsInit(true), m_knownBases(0) {
m_dynamic = Option::IsDynamicClass(m_name);
// dynamic class is also volatile
m_volatile = Option::AllVolatile || m_dynamic;
for (unsigned i = 0; i < attrs.size(); ++i) {
if (m_userAttributes.find(attrs[i]->getName()) != m_userAttributes.end()) {
attrs[i]->parseTimeFatal(Compiler::DeclaredAttributeTwice,
"Redeclared attribute %s",
attrs[i]->getName().c_str());
}
m_userAttributes[attrs[i]->getName()] = attrs[i]->getExp();
}
assert(m_parent.empty() || (!m_bases.empty() && m_bases[0] == m_parent));
}
// System
ClassScope::ClassScope(AnalysisResultPtr ar,
const std::string &name, const std::string &parent,
const std::vector<std::string> &bases,
const FunctionScopePtrVec &methods)
: BlockScope(name, "", StatementPtr(), BlockScope::ClassScope),
m_parent(parent), m_bases(bases),
m_attribute(0), m_redeclaring(-1),
m_kindOf(KindOfObjectClass), m_derivesFromRedeclaring(FromNormal),
m_traitStatus(NOT_FLATTENED), m_dynamic(false),
m_volatile(false), m_persistent(false),
m_derivedByDynamic(false), m_sep(false), m_needsCppCtor(false),
m_needsInit(true), m_knownBases(0) {
BOOST_FOREACH(FunctionScopePtr f, methods) {
if (f->getName() == "__construct") setAttribute(HasConstructor);
else if (f->getName() == "__destruct") setAttribute(HasDestructor);
else if (f->getName() == "__get") setAttribute(HasUnknownPropGetter);
else if (f->getName() == "__set") setAttribute(HasUnknownPropSetter);
else if (f->getName() == "__call") setAttribute(HasUnknownMethodHandler);
else if (f->getName() == "__callstatic") {
setAttribute(HasUnknownStaticMethodHandler);
} else if (f->getName() == "__isset") setAttribute(HasUnknownPropTester);
else if (f->getName() == "__unset") setAttribute(HasPropUnsetter);
else if (f->getName() == "__invoke") setAttribute(HasInvokeMethod);
addFunction(ar, f);
}
setAttribute(Extension);
setAttribute(System);
assert(m_parent.empty() || (!m_bases.empty() && m_bases[0] == m_parent));
}
const std::string &ClassScope::getOriginalName() const {
if (m_stmt) {
return dynamic_pointer_cast<InterfaceStatement>(m_stmt)->
getOriginalName();
}
return m_originalName;
}
// like getId(), but without the label formatting
std::string ClassScope::getDocName() const {
string name = getOriginalName();
if (m_redeclaring < 0) {
return name;
}
return name + Option::IdPrefix +
boost::lexical_cast<std::string>(m_redeclaring);
}
std::string ClassScope::getId() const {
string name = CodeGenerator::FormatLabel(getOriginalName());
if (m_redeclaring < 0) {
return name;
}
return name + Option::IdPrefix +
boost::lexical_cast<std::string>(m_redeclaring);
}
bool ClassScope::NeedStaticArray(ClassScopePtr cls, FunctionScopePtr func) {
return cls && cls->getAttribute(NotFinal) && !func->isPrivate();
}
///////////////////////////////////////////////////////////////////////////////
void ClassScope::derivedMagicMethods(ClassScopePtr super) {
super->setAttribute(NotFinal);
if (derivedByDynamic()) {
super->m_derivedByDynamic = true;
}
if (m_attribute & (HasUnknownPropGetter|
MayHaveUnknownPropGetter|
InheritsUnknownPropGetter)) {
super->setAttribute(MayHaveUnknownPropGetter);
}
if (m_attribute & (HasUnknownPropSetter|
MayHaveUnknownPropSetter|
InheritsUnknownPropSetter)) {
super->setAttribute(MayHaveUnknownPropSetter);
}
if (m_attribute & (HasUnknownPropTester|
MayHaveUnknownPropTester|
InheritsUnknownPropTester)) {
super->setAttribute(MayHaveUnknownPropTester);
}
if (m_attribute & (HasPropUnsetter|
MayHavePropUnsetter|
InheritsPropUnsetter)) {
super->setAttribute(MayHavePropUnsetter);
}
if (m_attribute & (HasUnknownMethodHandler|
MayHaveUnknownMethodHandler|
InheritsUnknownMethodHandler)) {
super->setAttribute(MayHaveUnknownMethodHandler);
}
if (m_attribute & (HasUnknownStaticMethodHandler|
MayHaveUnknownStaticMethodHandler|
InheritsUnknownStaticMethodHandler)) {
super->setAttribute(MayHaveUnknownStaticMethodHandler);
}
if (m_attribute & (HasInvokeMethod|
MayHaveInvokeMethod|
InheritsInvokeMethod)) {
super->setAttribute(MayHaveInvokeMethod);
}
if (m_attribute & (HasArrayAccess|
MayHaveArrayAccess|
InheritsArrayAccess)) {
super->setAttribute(MayHaveArrayAccess);
}
}
void ClassScope::inheritedMagicMethods(ClassScopePtr super) {
if (super->m_attribute & UsesUnknownTrait) {
setAttribute(UsesUnknownTrait);
}
if (super->m_attribute &
(HasUnknownPropGetter|InheritsUnknownPropGetter)) {
setAttribute(InheritsUnknownPropGetter);
}
if (super->m_attribute & (HasUnknownPropSetter|InheritsUnknownPropSetter)) {
setAttribute(InheritsUnknownPropSetter);
}
if (super->m_attribute & (HasUnknownPropTester|InheritsUnknownPropTester)) {
setAttribute(InheritsUnknownPropTester);
}
if (super->m_attribute & (HasPropUnsetter|InheritsPropUnsetter)) {
setAttribute(InheritsPropUnsetter);
}
if (super->m_attribute &
(HasUnknownMethodHandler|InheritsUnknownMethodHandler)) {
setAttribute(InheritsUnknownMethodHandler);
}
if (super->m_attribute &
(HasUnknownStaticMethodHandler|InheritsUnknownStaticMethodHandler)) {
setAttribute(InheritsUnknownStaticMethodHandler);
}
if (super->m_attribute & (HasInvokeMethod|InheritsInvokeMethod)) {
setAttribute(InheritsInvokeMethod);
}
if (super->m_attribute & (HasArrayAccess|InheritsArrayAccess)) {
setAttribute(InheritsArrayAccess);
}
}
bool ClassScope::implementsArrayAccess() {
return
getAttribute(MayHaveArrayAccess) |
getAttribute(HasArrayAccess) |
getAttribute(InheritsArrayAccess);
}
bool ClassScope::implementsAccessor(int prop) {
if (m_attribute & prop) return true;
if (prop & MayHaveUnknownPropGetter) {
prop |= HasUnknownPropGetter | InheritsUnknownPropGetter;
}
if (prop & MayHaveUnknownPropSetter) {
prop |= HasUnknownPropSetter | InheritsUnknownPropSetter;
}
if (prop & MayHaveUnknownPropTester) {
prop |= HasUnknownPropTester | InheritsUnknownPropTester;
}
if (prop & MayHavePropUnsetter) {
prop |= HasPropUnsetter | InheritsPropUnsetter;
}
return m_attribute & prop;
}
void ClassScope::checkDerivation(AnalysisResultPtr ar, hphp_string_iset &seen) {
seen.insert(m_name);
hphp_string_iset bases;
for (int i = m_bases.size() - 1; i >= 0; i--) {
const string &base = m_bases[i];
if (seen.find(base) != seen.end() || bases.find(base) != bases.end()) {
Compiler::Error(
Compiler::InvalidDerivation,
m_stmt,
"The class hierarchy contains a circular reference involving " + base);
if (i == 0 && !m_parent.empty()) {
assert(base == m_parent);
m_parent.clear();
}
m_bases.erase(m_bases.begin() + i);
continue;
}
bases.insert(base);
ClassScopePtrVec parents = ar->findClasses(Util::toLower(base));
for (unsigned int j = 0; j < parents.size(); j++) {
parents[j]->checkDerivation(ar, seen);
}
}
seen.erase(m_name);
}
void ClassScope::collectMethods(AnalysisResultPtr ar,
StringToFunctionScopePtrMap &funcs,
bool collectPrivate /* = true */,
bool forInvoke /* = false */) {
// add all functions this class has
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
const FunctionScopePtr &fs = *iter;
if (!collectPrivate && fs->isPrivate()) continue;
FunctionScopePtr &func = funcs[fs->getName()];
if (!func) {
func = fs;
} else {
func->setVirtual();
fs->setVirtual();
fs->setHasOverride();
if (fs->isFinal()) {
std::string s__MockClass = "__MockClass";
ClassScopePtr derivedClass = func->getContainingClass();
if (derivedClass->m_userAttributes.find(s__MockClass) ==
derivedClass->m_userAttributes.end()) {
Compiler::Error(Compiler::InvalidOverride,
fs->getStmt(), func->getStmt());
}
}
}
}
int n = forInvoke ? m_parent.empty() ? 0 : 1 : m_bases.size();
// walk up
for (int i = 0; i < n; i++) {
const string &base = m_bases[i];
ClassScopePtr super = ar->findClass(base);
if (super) {
if (super->isRedeclaring()) {
if (forInvoke) continue;
const ClassScopePtrVec &classes = ar->findRedeclaredClasses(base);
StringToFunctionScopePtrMap pristine(funcs);
BOOST_FOREACH(ClassScopePtr cls, classes) {
cls->m_derivedByDynamic = true;
StringToFunctionScopePtrMap cur(pristine);
derivedMagicMethods(cls);
cls->collectMethods(ar, cur, false, forInvoke);
inheritedMagicMethods(cls);
funcs.insert(cur.begin(), cur.end());
cls->getVariables()->
forceVariants(ar, VariableTable::AnyNonPrivateVars);
}
if (base == m_parent) {
m_derivesFromRedeclaring = DirectFromRedeclared;
getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars,
false);
getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
} else if (isInterface()) {
m_derivesFromRedeclaring = DirectFromRedeclared;
}
setVolatile();
} else {
derivedMagicMethods(super);
super->collectMethods(ar, funcs, false, forInvoke);
inheritedMagicMethods(super);
if (super->derivesFromRedeclaring()) {
if (base == m_parent) {
m_derivesFromRedeclaring = IndirectFromRedeclared;
getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
} else if (isInterface()) {
m_derivesFromRedeclaring = IndirectFromRedeclared;
}
setVolatile();
} else if (super->isVolatile()) {
setVolatile();
}
}
} else {
Compiler::Error(Compiler::UnknownBaseClass, m_stmt, base);
if (base == m_parent) {
ar->declareUnknownClass(m_parent);
m_derivesFromRedeclaring = DirectFromRedeclared;
getVariables()->setAttribute(VariableTable::NeedGlobalPointer);
getVariables()->forceVariants(ar, VariableTable::AnyNonPrivateVars);
setVolatile();
} else {
if (isInterface()) {
m_derivesFromRedeclaring = DirectFromRedeclared;
}
m_bases.erase(m_bases.begin() + i);
n--;
i--;
}
}
}
}
void ClassScope::importTraitProperties(AnalysisResultPtr ar) {
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
if (!tCls) continue;
ClassStatementPtr tStmt =
dynamic_pointer_cast<ClassStatement>(tCls->getStmt());
StatementListPtr tStmts = tStmt->getStmts();
if (!tStmts) continue;
for (int s = 0; s < tStmts->getCount(); s++) {
ClassVariablePtr prop =
dynamic_pointer_cast<ClassVariable>((*tStmts)[s]);
if (prop) {
ClassVariablePtr cloneProp = dynamic_pointer_cast<ClassVariable>(
dynamic_pointer_cast<ClassStatement>(m_stmt)->addClone(prop));
cloneProp->resetScope(shared_from_this(), true);
cloneProp->addTraitPropsToScope(ar,
dynamic_pointer_cast<ClassScope>(shared_from_this()));
}
}
}
}
MethodStatementPtr
ClassScope::importTraitMethod(const TraitMethod& traitMethod,
AnalysisResultPtr ar,
string methName,
GeneratorRenameMap& genRenameMap,
const std::map<string, MethodStatementPtr>&
importedTraitMethods) {
MethodStatementPtr meth = traitMethod.m_method;
string origMethName = traitMethod.m_originalName;
ModifierExpressionPtr modifiers = traitMethod.m_modifiers;
if (meth->getOrigGeneratorFunc()) {
const string &name = meth->getOrigGeneratorFunc()->getName();
if (!importedTraitMethods.count(name)) {
// Dont import the generator, if the origGenerator wasnt imported
// this happens when a generator in the trait is hidden by a non-generator
// method in the importing class.
return MethodStatementPtr();
}
}
MethodStatementPtr cloneMeth = dynamic_pointer_cast<MethodStatement>(
dynamic_pointer_cast<ClassStatement>(m_stmt)->addClone(meth));
cloneMeth->setName(methName);
cloneMeth->setOriginalName(origMethName);
// Note: keep previous modifiers if none specified when importing the trait
if (modifiers && modifiers->getCount()) {
cloneMeth->setModifiers(modifiers);
}
FunctionScopePtr funcScope = meth->getFunctionScope();
// Trait method typehints, self and parent, need to be converted
ClassScopePtr cScope = dynamic_pointer_cast<ClassScope>(shared_from_this());
cloneMeth->fixupSelfAndParentTypehints( cScope );
// Generator methods need to be renamed, otherwise code gen produces multiple
// continuation classes with the same name
if (funcScope->isGenerator()) {
const string& newName = getNewGeneratorName(funcScope, genRenameMap);
methName = origMethName = newName;
cloneMeth->setName(newName);
cloneMeth->setOriginalName(newName);
}
FunctionScopePtr cloneFuncScope
(new HPHP::FunctionScope(funcScope, ar, methName, origMethName, cloneMeth,
cloneMeth->getModifiers()));
cloneMeth->resetScope(cloneFuncScope, true);
cloneFuncScope->setOuterScope(shared_from_this());
informClosuresAboutScopeClone(cloneMeth, cloneFuncScope, ar);
cloneMeth->addTraitMethodToScope(ar,
dynamic_pointer_cast<ClassScope>(shared_from_this()));
return cloneMeth;
}
void ClassScope::informClosuresAboutScopeClone(
ConstructPtr root,
FunctionScopePtr outerScope,
AnalysisResultPtr ar) {
if (!root) {
return;
}
for (int i = 0; i < root->getKidCount(); i++) {
ConstructPtr cons = root->getNthKid(i);
ClosureExpressionPtr closure =
dynamic_pointer_cast<ClosureExpression>(cons);
if (!closure) {
informClosuresAboutScopeClone(cons, outerScope, ar);
continue;
}
FunctionStatementPtr func = closure->getClosureFunction();
HPHP::FunctionScopePtr funcScope = func->getFunctionScope();
assert(funcScope->isClosure());
funcScope->addClonedTraitOuterScope(outerScope);
// Don't need to recurse
}
}
void ClassScope::addImportTraitMethod(const TraitMethod &traitMethod,
const string &methName) {
m_importMethToTraitMap[methName].push_back(traitMethod);
}
void
ClassScope::setImportTraitMethodModifiers(const string &methName,
ClassScopePtr traitCls,
ModifierExpressionPtr modifiers) {
TraitMethodList &methList = m_importMethToTraitMap[methName];
for (TraitMethodList::iterator iter = methList.begin();
iter != methList.end(); iter++) {
if (iter->m_trait == traitCls) {
iter->m_modifiers = modifiers;
return;
}
}
}
MethodStatementPtr
ClassScope::findTraitMethod(AnalysisResultPtr ar,
ClassScopePtr trait,
const string &methodName,
std::set<ClassScopePtr> &visitedTraits) {
if (visitedTraits.find(trait) != visitedTraits.end()) {
return MethodStatementPtr();
}
visitedTraits.insert(trait);
ClassStatementPtr tStmt =
dynamic_pointer_cast<ClassStatement>(trait->getStmt());
StatementListPtr tStmts = tStmt->getStmts();
// Look in the current trait
for (int s = 0; s < tStmts->getCount(); s++) {
MethodStatementPtr meth =
dynamic_pointer_cast<MethodStatement>((*tStmts)[s]);
if (meth) { // Handle methods
if (meth->getName() == methodName) {
return meth;
}
}
}
// Look into children traits
for (int s = 0; s < tStmts->getCount(); s++) {
UseTraitStatementPtr useTraitStmt =
dynamic_pointer_cast<UseTraitStatement>((*tStmts)[s]);
if (useTraitStmt) {
vector<string> usedTraits;
useTraitStmt->getUsedTraitNames(usedTraits);
for (unsigned i = 0; i < usedTraits.size(); i++) {
MethodStatementPtr foundMethod =
findTraitMethod(ar, ar->findClass(usedTraits[i]), methodName,
visitedTraits);
if (foundMethod) return foundMethod;
}
}
}
return MethodStatementPtr(); // not found
}
void ClassScope::findTraitMethodsToImport(AnalysisResultPtr ar,
ClassScopePtr trait) {
ClassStatementPtr tStmt =
dynamic_pointer_cast<ClassStatement>(trait->getStmt());
StatementListPtr tStmts = tStmt->getStmts();
if (!tStmts) return;
for (int s = 0; s < tStmts->getCount(); s++) {
MethodStatementPtr meth =
dynamic_pointer_cast<MethodStatement>((*tStmts)[s]);
if (meth) {
TraitMethod traitMethod(trait, meth, ModifierExpressionPtr(),
MethodStatementPtr());
addImportTraitMethod(traitMethod, meth->getName());
}
}
}
void ClassScope::applyTraitPrecRule(TraitPrecStatementPtr stmt) {
const string methodName = Util::toLower(stmt->getMethodName());
const string selectedTraitName = Util::toLower(stmt->getTraitName());
std::set<string> otherTraitNames;
stmt->getOtherTraitNames(otherTraitNames);
map<string,TraitMethodList>::iterator methIter =
m_importMethToTraitMap.find(methodName);
if (methIter == m_importMethToTraitMap.end()) {
Compiler::Error(Compiler::UnknownObjectMethod, stmt);
return;
}
bool foundSelectedTrait = false;
TraitMethodList &methList = methIter->second;
for (TraitMethodList::iterator nextTraitIter = methList.begin();
nextTraitIter != methList.end(); ) {
TraitMethodList::iterator traitIter = nextTraitIter++;
string availTraitName = traitIter->m_trait->getName();
if (availTraitName == selectedTraitName) {
foundSelectedTrait = true;
} else {
if (otherTraitNames.find(availTraitName) != otherTraitNames.end()) {
otherTraitNames.erase(availTraitName);
methList.erase(traitIter);
}
}
}
// Report error if didn't find the selected trait
if (!foundSelectedTrait) {
Compiler::Error(Compiler::UnknownTrait, stmt);
}
// Sanity checking: otherTraitNames should be empty now
if (otherTraitNames.size()) {
Compiler::Error(Compiler::UnknownTrait, stmt);
}
}
bool ClassScope::hasMethod(const string &methodName) const {
return m_functions.find(methodName) != m_functions.end();
}
ClassScopePtr
ClassScope::findSingleTraitWithMethod(AnalysisResultPtr ar,
const string &methodName) const {
ClassScopePtr trait = ClassScopePtr();
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
if (!tCls) continue;
if (tCls->hasMethod(methodName)) {
if (trait) { // more than one trait contains method
return ClassScopePtr();
}
trait = tCls;
}
}
return trait;
}
void ClassScope::addTraitAlias(TraitAliasStatementPtr aliasStmt) {
const string &traitName = aliasStmt->getTraitName();
const string &origMethName = aliasStmt->getMethodName();
const string &newMethName = aliasStmt->getNewMethodName();
string origName = traitName.empty() ? "(null)" : traitName;
origName += "::" + origMethName;
m_traitAliases.push_back(std::pair<string, string>(newMethName, origName));
}
void ClassScope::applyTraitAliasRule(AnalysisResultPtr ar,
TraitAliasStatementPtr stmt) {
const string traitName = Util::toLower(stmt->getTraitName());
const string origMethName = Util::toLower(stmt->getMethodName());
const string newMethName = Util::toLower(stmt->getNewMethodName());
// Get the trait's "class"
ClassScopePtr traitCls;
if (traitName.empty()) {
traitCls = findSingleTraitWithMethod(ar, origMethName);
} else {
traitCls = ar->findClass(traitName);
}
if (!traitCls || !(traitCls->isTrait())) {
Compiler::Error(Compiler::UnknownTrait, stmt);
return;
}
// Keep record of alias rule
addTraitAlias(stmt);
// Get the method
std::set<ClassScopePtr> visitedTraits;
MethodStatementPtr methStmt = findTraitMethod(ar, traitCls, origMethName,
visitedTraits);
if (!methStmt) {
Compiler::Error(Compiler::UnknownTraitMethod, stmt);
return;
}
if (origMethName == newMethName) {
setImportTraitMethodModifiers(origMethName, traitCls, stmt->getModifiers());
}
else {
// Insert renamed entry into the set of methods to be imported
TraitMethod traitMethod(traitCls, methStmt, stmt->getModifiers(), stmt,
stmt->getNewMethodName());
addImportTraitMethod(traitMethod, newMethName);
}
}
void ClassScope::applyTraitRules(AnalysisResultPtr ar) {
ClassStatementPtr classStmt = dynamic_pointer_cast<ClassStatement>(getStmt());
assert(classStmt);
StatementListPtr stmts = classStmt->getStmts();
if (!stmts) return;
for (int s = 0; s < stmts->getCount(); s++) {
StatementPtr stmt = (*stmts)[s];
UseTraitStatementPtr useStmt =
dynamic_pointer_cast<UseTraitStatement>(stmt);
if (!useStmt) continue;
StatementListPtr rules = useStmt->getStmts();
for (int r = 0; r < rules->getCount(); r++) {
StatementPtr rule = (*rules)[r];
TraitPrecStatementPtr precStmt =
dynamic_pointer_cast<TraitPrecStatement>(rule);
if (precStmt) {
applyTraitPrecRule(precStmt);
} else {
TraitAliasStatementPtr aliasStmt =
dynamic_pointer_cast<TraitAliasStatement>(rule);
assert(aliasStmt);
applyTraitAliasRule(ar, aliasStmt);
}
}
}
}
// This method removes trait abstract methods that are either:
// 1) implemented by other traits
// 2) duplicate
void ClassScope::removeSpareTraitAbstractMethods(AnalysisResultPtr ar) {
for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin();
iter != m_importMethToTraitMap.end(); iter++) {
TraitMethodList& tMethList = iter->second;
bool hasNonAbstractMeth = false;
unsigned countAbstractMeths = 0;
for (TraitMethodList::const_iterator traitMethIter = tMethList.begin();
traitMethIter != tMethList.end(); traitMethIter++) {
ModifierExpressionPtr modifiers = traitMethIter->m_modifiers ?
traitMethIter->m_modifiers : traitMethIter->m_method->getModifiers();
if (!(modifiers->isAbstract())) {
hasNonAbstractMeth = true;
} else {
countAbstractMeths++;
}
}
if (hasNonAbstractMeth || countAbstractMeths > 1) {
// Erase spare abstract declarations
bool firstAbstractMeth = true;
for (TraitMethodList::iterator nextTraitIter = tMethList.begin();
nextTraitIter != tMethList.end(); ) {
TraitMethodList::iterator traitIter = nextTraitIter++;
ModifierExpressionPtr modifiers = traitIter->m_modifiers ?
traitIter->m_modifiers : traitIter->m_method->getModifiers();
if (modifiers->isAbstract()) {
if (hasNonAbstractMeth || !firstAbstractMeth) {
tMethList.erase(traitIter);
}
firstAbstractMeth = false;
}
}
}
}
}
const string& ClassScope::getNewGeneratorName(
FunctionScopePtr genFuncScope, GeneratorRenameMap &genRenameMap) {
assert(genFuncScope->isGenerator());
const string& oldName = genFuncScope->getName();
GeneratorRenameMap::iterator mapIt = genRenameMap.find(oldName);
if (mapIt != genRenameMap.end()) {
return mapIt->second;
}
string newName = ParserBase::newContinuationName(
oldName + "_" + lexical_cast<string>(genFuncScope->getNewID())
);
genRenameMap[oldName] = newName;
return genRenameMap[oldName];
}
void
ClassScope::renameCreateContinuationCalls(AnalysisResultPtr ar,
ConstructPtr c,
ImportedMethodMap &importedMethods) {
if (!c) return;
SimpleFunctionCallPtr funcCall = dynamic_pointer_cast<SimpleFunctionCall>(c);
if (funcCall && funcCall->getName() == "hphp_create_continuation") {
ExpressionListPtr params = funcCall->getParams();
assert(params->getCount() >= 2);
const string &oldClassName =
dynamic_pointer_cast<ScalarExpression>((*params)[0])->getString();
ClassScopePtr oldClassScope = ar->findClass(oldClassName);
if (!oldClassScope || !oldClassScope->isTrait()) return;
const string &oldGenName =
dynamic_pointer_cast<ScalarExpression>((*params)[1])->getString();
MethodStatementPtr origGenStmt = importedMethods[Util::toLower(oldGenName)];
assert(origGenStmt);
const string &newGenName = origGenStmt->getOriginalName();
ExpressionPtr newGenExpr = funcCall->makeScalarExpression(ar, newGenName);
ExpressionPtr newClsExpr = funcCall->makeScalarExpression(ar, getName());
(*params)[0] = newClsExpr;
(*params)[1] = newGenExpr;
funcCall->analyzeProgram(ar);
return;
}
for (int i=0; i < c->getKidCount(); i++) {
renameCreateContinuationCalls(ar, c->getNthKid(i), importedMethods);
}
}
void ClassScope::relinkGeneratorMethods(
AnalysisResultPtr ar,
ImportedMethodMap &importedMethods) {
for (ImportedMethodMap::const_iterator methIt =
importedMethods.begin(); methIt != importedMethods.end(); methIt++) {
MethodStatementPtr newMeth = methIt->second;
// Skip non-generator methods
if (!newMeth) continue;
if (newMeth->getOrigGeneratorFunc()) {
// Get corresponding original generator method in the current class
const string& origGenName = newMeth->getOrigGeneratorFunc()->getName();
MethodStatementPtr origGenStmt = importedMethods[origGenName];
assert(origGenStmt);
// It must be an orig gen func already, we're just updating to point
// to the corresponding method cloned from the trait
assert(origGenStmt->getGeneratorFunc());
newMeth->setOrigGeneratorFunc(origGenStmt);
origGenStmt->setGeneratorFunc(newMeth);
}
// OrigGenerator methods need to have their hphp_create_continuation calls
// patched to the new generator name.
if (newMeth->getGeneratorFunc()) {
renameCreateContinuationCalls(ar, newMeth, importedMethods);
}
}
}
void ClassScope::importUsedTraits(AnalysisResultPtr ar) {
if (m_traitStatus == FLATTENED) return;
if (m_traitStatus == BEING_FLATTENED) {
Compiler::Error(Compiler::CyclicDependentTraits, getStmt());
return;
}
if (m_usedTraitNames.size() == 0) {
m_traitStatus = FLATTENED;
return;
}
m_traitStatus = BEING_FLATTENED;
// First, make sure that parent classes have their traits imported
if (!m_parent.empty()) {
ClassScopePtr parent = ar->findClass(m_parent);
if (parent) {
parent->importUsedTraits(ar);
}
}
// Find trait methods to be imported
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
if (!tCls || !(tCls->isTrait())) {
setAttribute(UsesUnknownTrait);
Compiler::Error(Compiler::UnknownTrait, getStmt());
continue;
}
// First, make sure the used trait is flattened
tCls->importUsedTraits(ar);
findTraitMethodsToImport(ar, tCls);
}
// Apply rules
applyTraitRules(ar);
// Remove trait abstract methods provided by other traits and duplicates
removeSpareTraitAbstractMethods(ar);
// Apply precedence of current class over used traits
for (MethodToTraitListMap::iterator iter = m_importMethToTraitMap.begin();
iter != m_importMethToTraitMap.end(); ) {
MethodToTraitListMap::iterator thisiter = iter;
iter++;
if (findFunction(ar, thisiter->first, 0, 0) != FunctionScopePtr()) {
m_importMethToTraitMap.erase(thisiter);
}
}
std::map<string, MethodStatementPtr> importedTraitMethods;
std::vector<std::pair<string,const TraitMethod*> > importedTraitsWithOrigName;
GeneratorRenameMap genRenameMap;
// Actually import the methods
for (MethodToTraitListMap::const_iterator
iter = m_importMethToTraitMap.begin();
iter != m_importMethToTraitMap.end(); iter++) {
// The rules may rule out a method from all traits.
// In this case, simply don't import the method.
if (iter->second.size() == 0) {
continue;
}
// Consistency checking: each name must only refer to one imported method
if (iter->second.size() > 1) {
Compiler::Error(Compiler::MethodInMultipleTraits, getStmt());
} else {
TraitMethodList::const_iterator traitMethIter = iter->second.begin();
if ((traitMethIter->m_modifiers ? traitMethIter->m_modifiers :
traitMethIter->m_method->getModifiers())->isAbstract()) {
// Skip abstract methods, if method already exists in the class
if (findFunction(ar, iter->first, true) ||
importedTraitMethods.count(iter->first)) {
continue;
}
}
if (traitMethIter->m_modifiers &&
traitMethIter->m_modifiers->isStatic()) {
Compiler::Error(Compiler::InvalidAccessModifier,
traitMethIter->m_modifiers);
continue;
}
string sourceName = traitMethIter->m_ruleStmt ?
Util::toLower(((TraitAliasStatement*)traitMethIter->m_ruleStmt.get())->
getMethodName()) : iter->first;
importedTraitMethods[sourceName] = MethodStatementPtr();
importedTraitsWithOrigName.push_back(
make_pair(sourceName, &*traitMethIter));
}
}
for (unsigned i = 0; i < importedTraitsWithOrigName.size(); i++) {
const string &sourceName = importedTraitsWithOrigName[i].first;
const TraitMethod *traitMethod = importedTraitsWithOrigName[i].second;
MethodStatementPtr newMeth = importTraitMethod(
*traitMethod, ar, Util::toLower(traitMethod->m_originalName),
genRenameMap, importedTraitMethods);
if (newMeth) {
importedTraitMethods[sourceName] = newMeth;
}
}
// Relink generator and origGenerator methods
relinkGeneratorMethods(ar, importedTraitMethods);
// Import trait properties
importTraitProperties(ar);
m_traitStatus = FLATTENED;
}
bool ClassScope::usesTrait(const string &traitName) const {
for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
if (traitName == m_usedTraitNames[i]) {
return true;
}
}
return false;
}
bool ClassScope::needsInvokeParent(AnalysisResultConstPtr ar,
bool considerSelf /* = true */) {
// check all functions this class has
if (considerSelf) {
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
if ((*iter)->isPrivate()) return true;
}
}
// walk up
if (!m_parent.empty()) {
ClassScopePtr super = ar->findClass(m_parent);
return !super || super->isRedeclaring() || super->needsInvokeParent(ar);
}
return false;
}
bool ClassScope::derivesDirectlyFrom(const std::string &base) const {
BOOST_FOREACH(std::string base_i, m_bases) {
if (strcasecmp(base_i.c_str(), base.c_str()) == 0) return true;
}
return false;
}
bool ClassScope::derivesFrom(AnalysisResultConstPtr ar,
const std::string &base,
bool strict, bool def) const {
if (derivesDirectlyFrom(base)) return true;
BOOST_FOREACH(std::string base_i, m_bases) {
ClassScopePtr cl = ar->findClass(base_i);
if (cl) {
if (strict && cl->isRedeclaring()) {
if (def) return true;
continue;
}
if (cl->derivesFrom(ar, base, strict, def)) return true;
}
}
return false;
}
ClassScopePtr ClassScope::FindCommonParent(AnalysisResultConstPtr ar,
const std::string &cn1,
const std::string &cn2) {
ClassScopePtr cls1 = ar->findClass(cn1);
ClassScopePtr cls2 = ar->findClass(cn2);
if (!cls1 || !cls2) return ClassScopePtr();
if (cls1->getName() == cls2->getName()) return cls1;
if (cls1->derivesFrom(ar, cn2, true, false)) return cls2;
if (cls2->derivesFrom(ar, cn1, true, false)) return cls1;
// walk up the class hierarchy.
BOOST_FOREACH(const std::string &base1, cls1->m_bases) {
BOOST_FOREACH(const std::string &base2, cls2->m_bases) {
ClassScopePtr parent = FindCommonParent(ar, base1, base2);
if (parent) return parent;
}
}
return ClassScopePtr();
}
void ClassScope::setVolatile() {
if (!m_volatile) {
m_volatile = true;
Lock lock(s_depsMutex);
const BlockScopeRawPtrFlagsVec &orderedUsers = getOrderedUsers();
for (BlockScopeRawPtrFlagsVec::const_iterator it = orderedUsers.begin(),
end = orderedUsers.end(); it != end; ++it) {
BlockScopeRawPtrFlagsVec::value_type pf = *it;
if (pf->second & UseKindParentRef) {
BlockScopeRawPtr scope = pf->first;
if (scope->is(BlockScope::ClassScope)) {
((HPHP::ClassScope*)scope.get())->setVolatile();
}
}
}
}
}
FunctionScopePtr ClassScope::findFunction(AnalysisResultConstPtr ar,
const std::string &name,
bool recursive,
bool exclIntfBase /* = false */) {
assert(Util::toLower(name) == name);
StringToFunctionScopePtrMap::const_iterator iter;
iter = m_functions.find(name);
if (iter != m_functions.end()) {
assert(iter->second);
return iter->second;
}
// walk up
if (recursive) {
int s = m_bases.size();
for (int i = 0; i < s; i++) {
const string &base = m_bases[i];
ClassScopePtr super = ar->findClass(base);
if (!super) continue;
if (exclIntfBase && super->isInterface()) break;
if (super->isRedeclaring()) {
if (base == m_parent) {
m_derivesFromRedeclaring = DirectFromRedeclared;
break;
}
continue;
}
FunctionScopePtr func =
super->findFunction(ar, name, true, exclIntfBase);
if (func) return func;
}
}
if (!Option::AllDynamic &&
derivesFromRedeclaring() == DirectFromRedeclared) {
setDynamic(ar, name);
}
return FunctionScopePtr();
}
FunctionScopePtr ClassScope::findConstructor(AnalysisResultConstPtr ar,
bool recursive) {
StringToFunctionScopePtrMap::const_iterator iter;
string name;
if (classNameCtor()) {
name = getName();
} else {
name = "__construct";
}
iter = m_functions.find(name);
if (iter != m_functions.end()) {
assert(iter->second);
return iter->second;
}
// walk up
if (recursive && derivesFromRedeclaring() != DirectFromRedeclared) {
ClassScopePtr super = ar->findClass(m_parent);
if (super) {
FunctionScopePtr func = super->findConstructor(ar, true);
if (func) return func;
}
}
if (!Option::AllDynamic &&
derivesFromRedeclaring() == DirectFromRedeclared) {
setDynamic(ar, name);
}
return FunctionScopePtr();
}
void ClassScope::setStaticDynamic(AnalysisResultConstPtr ar) {
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
FunctionScopePtr fs = *iter;
if (fs->isStatic()) fs->setDynamic();
}
if (!m_parent.empty()) {
if (derivesFromRedeclaring() == DirectFromRedeclared) {
const ClassScopePtrVec &parents = ar->findRedeclaredClasses(m_parent);
BOOST_FOREACH(ClassScopePtr cl, parents) {
cl->setStaticDynamic(ar);
}
} else {
ClassScopePtr parent = ar->findClass(m_parent);
if (parent) {
parent->setStaticDynamic(ar);
}
}
}
}
void ClassScope::setDynamic(AnalysisResultConstPtr ar,
const std::string &name) {
StringToFunctionScopePtrMap::const_iterator iter =
m_functions.find(name);
if (iter != m_functions.end()) {
FunctionScopePtr fs = iter->second;
fs->setDynamic();
} else if (!m_parent.empty()) {
if (derivesFromRedeclaring() == DirectFromRedeclared) {
const ClassScopePtrVec &parents = ar->findRedeclaredClasses(m_parent);
BOOST_FOREACH(ClassScopePtr cl, parents) {
cl->setDynamic(ar, name);
}
} else {
ClassScopePtr parent = ar->findClass(m_parent);
if (parent) {
parent->setDynamic(ar, name);
}
}
}
}
void ClassScope::setSystem() {
setAttribute(ClassScope::System);
m_volatile = m_dynamic = false;
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
(*iter)->setSystem();
}
}
bool ClassScope::needLazyStaticInitializer() {
return getVariables()->getAttribute(VariableTable::ContainsDynamicStatic) ||
getConstants()->hasDynamic();
}
bool ClassScope::hasConst(const string &name) const {
const Symbol *sym = m_constants->getSymbol(name);
assert(!sym || sym->isPresent());
return sym;
}
Symbol *ClassScope::findProperty(ClassScopePtr &cls,
const string &name,
AnalysisResultConstPtr ar) {
return getVariables()->findProperty(cls, name, ar);
}
TypePtr ClassScope::checkProperty(BlockScopeRawPtr context,
Symbol *sym, TypePtr type,
bool coerce, AnalysisResultConstPtr ar) {
return getVariables()->checkProperty(context, sym, type, coerce, ar);
}
TypePtr ClassScope::checkConst(BlockScopeRawPtr context,
const std::string &name, TypePtr type,
bool coerce, AnalysisResultConstPtr ar,
ConstructPtr construct,
const std::vector<std::string> &bases,
BlockScope *&defScope) {
defScope = nullptr;
return getConstants()->check(context, name, type, coerce,
ar, construct, m_bases, defScope);
}
void ClassScope::getAllParents(AnalysisResultConstPtr ar,
std::vector<std::string> &names) {
if (m_stmt) {
if (isInterface()) {
boost::dynamic_pointer_cast<InterfaceStatement>
(m_stmt)->getAllParents(ar, names);
} else {
boost::dynamic_pointer_cast<ClassStatement>
(m_stmt)->getAllParents(ar, names);
}
} else {
for (unsigned i = 0; i < m_bases.size(); i++) {
const string &base = m_bases[i];
names.push_back(base);
if (ClassScopePtr cls = ar->findClass(base)) {
if (!cls->isRedeclaring()) {
cls->getAllParents(ar, names);
}
}
}
}
}
void ClassScope::getInterfaces(AnalysisResultConstPtr ar,
std::vector<std::string> &names,
bool recursive /* = true */) const {
ClassScope *self = const_cast<ClassScope*>(this);
if (recursive && !m_parent.empty()) {
ClassScopePtr cls(ar->findClass(m_parent));
if (cls && cls->isRedeclaring()) {
cls = self->findExactClass(cls);
}
if (cls) cls->getInterfaces(ar, names, true);
}
if (!m_bases.empty()) {
vector<string>::const_iterator begin =
m_parent.empty() ? m_bases.begin() : m_bases.begin() + 1;
for (vector<string>::const_iterator it = begin;
it != m_bases.end(); ++it) {
ClassScopePtr cls(ar->findClass(*it));
if (cls && cls->isRedeclaring()) {
cls = self->findExactClass(cls);
}
if (cls) names.push_back(cls->getDocName());
else names.push_back(*it);
if (cls && recursive) {
cls->getInterfaces(ar, names, true);
}
}
}
}
ClassScopePtr ClassScope::getParentScope(AnalysisResultConstPtr ar) const {
if (m_parent.empty()) return ClassScopePtr();
return ar->findClass(m_parent);
}
void ClassScope::serialize(JSON::CodeError::OutputStream &out) const {
JSON::CodeError::MapStream ms(out);
std::map<string, int> propMap;
std::set<string> names;
m_variables->getNames(names);
BOOST_FOREACH(string name, names) {
int pm = 0;
if (m_variables->isPublic(name)) pm |= ClassScope::Public;
else if (m_variables->isPrivate(name)) pm |= ClassScope::Private;
else if (m_variables->isProtected(name)) pm |= ClassScope::Protected;
if (m_variables->isStatic(name)) pm |= ClassScope::Static;
propMap[name] = pm;
}
names.clear();
vector<string> cnames;
m_constants->getSymbols(cnames);
// What's a mod again?
ms.add("attributes", m_attribute)
.add("kind", m_kindOf)
.add("parent", m_parent)
.add("bases", m_bases)
.add("properties", propMap)
.add("functions", m_functions);
ms.add("consts");
JSON::CodeError::MapStream cs(out);
BOOST_FOREACH(string cname, cnames) {
TypePtr type = m_constants->getType(cname);
if (!type) {
cs.add(cname, -1);
} else if (type->isSpecificObject()) {
cs.add(cname, type->getName());
} else {
cs.add(cname, type->getKindOf());
}
}
cs.done();
ms.done();
}
static inline string GetDocName(AnalysisResultPtr ar,
BlockScopeRawPtr scope,
const string &name) {
ClassScopePtr c(ar->findClass(name));
if (c && c->isRedeclaring()) {
ClassScopePtr exact(scope->findExactClass(c));
return exact ?
exact->getDocName() :
c->getOriginalName(); // if we can't tell which redec class,
// then don't use the redec name
}
// TODO: pick a better way of signaling unknown?
return c ? c->getDocName() : "UnknownClass";
}
class GetDocNameFunctor {
public:
GetDocNameFunctor(AnalysisResultPtr ar, BlockScopeRawPtr scope) :
m_ar(ar), m_scope(scope) {}
inline string operator()(const string &name) const {
return GetDocName(m_ar, m_scope, name);
}
private:
AnalysisResultPtr m_ar;
BlockScopeRawPtr m_scope;
};
void ClassScope::serialize(JSON::DocTarget::OutputStream &out) const {
// TODO(stephentu): fix this hack
ClassScopeRawPtr self(const_cast<ClassScope*>(this));
JSON::DocTarget::MapStream ms(out);
ms.add("name", getDocName());
ms.add("line", getStmt() ? getStmt()->getLocation()->line0 : 0);
ms.add("docs", m_docComment);
ms.add("parent");
if (m_parent.empty()) {
out << JSON::Null;
} else {
out << GetDocName(out.analysisResult(), self, m_parent);
}
vector<string> ifaces;
getInterfaces(out.analysisResult(), ifaces, true);
vector<string> origIfaces;
origIfaces.resize(ifaces.size());
transform(ifaces.begin(), ifaces.end(), origIfaces.begin(),
GetDocNameFunctor(out.analysisResult(), self));
ms.add("interfaces", origIfaces);
int mods = 0;
// TODO: you should really only get one of these, we should assert this
if (m_kindOf == KindOfAbstractClass) mods |= ClassInfo::IsAbstract;
if (m_kindOf == KindOfFinalClass) mods |= ClassInfo::IsFinal;
if (m_kindOf == KindOfInterface) mods |= ClassInfo::IsInterface;
if (m_kindOf == KindOfTrait) mods |= ClassInfo::IsTrait;
ms.add("modifiers", mods);
FunctionScopePtrVec funcs;
getFunctionsFlattened(0, funcs);
ms.add("methods", funcs);
vector<Symbol*> rawSymbols;
getVariables()->getSymbols(rawSymbols, true);
vector<SymClassVarWrapper> wrappedSymbols;
for (vector<Symbol*>::iterator it = rawSymbols.begin();
it != rawSymbols.end(); ++it) {
wrappedSymbols.push_back(SymClassVarWrapper(*it));
}
ms.add("properties", wrappedSymbols);
// TODO: constants
ms.done();
}
bool ClassScope::hasProperty(const string &name) const {
const Symbol *sym = m_variables->getSymbol(name);
assert(!sym || sym->isPresent());
return sym;
}
void ClassScope::setRedeclaring(AnalysisResultConstPtr ar, int redecId) {
if (isTrait()) {
Compiler::Error(Compiler::RedeclaredTrait, m_stmt);
}
m_redeclaring = redecId;
setVolatile(); // redeclared class is also volatile
for (FunctionScopePtrVec::const_iterator iter =
m_functionsVec.begin(); iter != m_functionsVec.end(); ++iter) {
(*iter)->setDynamic();
}
}
ClassScopePtr ClassScope::getRootParent(AnalysisResultConstPtr ar,
const std::string &methodName) {
ClassScopePtr root = dynamic_pointer_cast<ClassScope>(shared_from_this());
for (ClassScopePtr cls = getParentScope(ar); cls;
cls = cls->getParentScope(ar)) {
if (methodName.empty() ||
cls->m_functions.find(methodName) != cls->m_functions.end()) {
root = cls;
}
}
return root;
}
void ClassScope::getRootParents(AnalysisResultConstPtr ar,
const std::string &methodName,
ClassScopePtrVec &roots,
ClassScopePtr curClass) {
ClassScopePtr root = dynamic_pointer_cast<ClassScope>(shared_from_this());
if (m_parent.empty()) {
roots.push_back(curClass);
} else {
ClassScopePtrVec parents = ar->findRedeclaredClasses(m_parent);
for (unsigned int i = 0; i < parents.size(); i++) {
ClassScopePtr cls = parents[i];
if (methodName.empty() ||
cls->m_functions.find(methodName) != cls->m_functions.end()) {
curClass = cls;
}
cls->getRootParents(ar, methodName, roots, curClass);
}
}
}
bool ClassScope::addFunction(AnalysisResultConstPtr ar,
FunctionScopePtr funcScope) {
FunctionScopePtr &func = m_functions[funcScope->getName()];
if (func) {
func->getStmt()->parseTimeFatal(Compiler::DeclaredMethodTwice,
"Redeclared method %s::%s",
getOriginalName().c_str(),
func->getOriginalName().c_str());
}
func = funcScope;
m_functionsVec.push_back(funcScope);
return true;
}
bool ClassScope::canSkipCreateMethod(AnalysisResultConstPtr ar) const {
// create() is not necessary if
// 1) not inheriting from any class
// 2) no constructor defined (__construct or class name)
// 3) no init() defined
if (derivesFromRedeclaring() ||
getAttribute(HasConstructor) ||
getAttribute(ClassNameConstructor) ||
needsInitMethod()) {
return false;
}
if (!m_parent.empty()) {
ClassScopePtr parent = getParentScope(ar);
if (parent) return parent->canSkipCreateMethod(ar);
}
return true;
}