/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 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/statement/class_statement.h" #include "hphp/util/parser/hphp.tab.hpp" #include "hphp/compiler/expression/expression_list.h" #include "hphp/compiler/statement/statement_list.h" #include "hphp/compiler/expression/scalar_expression.h" #include "hphp/compiler/expression/parameter_expression.h" #include "hphp/compiler/analysis/class_scope.h" #include "hphp/compiler/analysis/file_scope.h" #include "hphp/compiler/analysis/function_scope.h" #include "hphp/compiler/analysis/analysis_result.h" #include "hphp/compiler/statement/method_statement.h" #include "hphp/compiler/statement/class_variable.h" #include "hphp/compiler/analysis/variable_table.h" #include "hphp/compiler/analysis/constant_table.h" #include "hphp/util/util.h" #include "hphp/compiler/statement/interface_statement.h" #include "hphp/compiler/statement/use_trait_statement.h" #include "hphp/compiler/option.h" #include #include using namespace HPHP; /////////////////////////////////////////////////////////////////////////////// // constructors/destructors ClassStatement::ClassStatement (STATEMENT_CONSTRUCTOR_PARAMETERS, int type, const string &name, const string &parent, ExpressionListPtr base, const string &docComment, StatementListPtr stmt, ExpressionListPtr attrList) : InterfaceStatement(STATEMENT_CONSTRUCTOR_PARAMETER_VALUES(ClassStatement), name, base, docComment, stmt, attrList), m_type(type), m_ignored(false) { m_parent = Util::toLower(parent); m_originalParent = parent; } StatementPtr ClassStatement::clone() { ClassStatementPtr stmt(new ClassStatement(*this)); stmt->m_stmt = Clone(m_stmt); stmt->m_base = Clone(m_base); return stmt; } /////////////////////////////////////////////////////////////////////////////// // parser functions void ClassStatement::onParse(AnalysisResultConstPtr ar, FileScopePtr fs) { 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; case T_TRAIT: kindOf = ClassScope::KindOfTrait; break; default: assert(false); } vector bases; if (!m_originalParent.empty()) { bases.push_back(m_originalParent); } if (m_base) m_base->getOriginalStrings(bases); for (auto &b : bases) { ar->parseOnDemandByClass(Util::toLower(b)); } vector attrs; if (m_attrList) { for (int i = 0; i < m_attrList->getCount(); ++i) { UserAttributePtr a = dynamic_pointer_cast((*m_attrList)[i]); attrs.push_back(a); } } StatementPtr stmt = dynamic_pointer_cast(shared_from_this()); ClassScopePtr classScope(new ClassScope(kindOf, m_originalName, m_originalParent, bases, m_docComment, stmt, attrs)); setBlockScope(classScope); if (!fs->addClass(ar, classScope)) { m_ignored = true; return; } if (Option::PersistenceHook) { classScope->setPersistent(Option::PersistenceHook(classScope, fs)); } if (m_stmt) { MethodStatementPtr constructor; // flatten continuation StatementList into MethodStatements for (int i = 0; i < m_stmt->getCount(); i++) { StatementListPtr stmts = dynamic_pointer_cast((*m_stmt)[i]); if (stmts) { m_stmt->removeElement(i); for (int j = 0; j < stmts->getCount(); j++) { m_stmt->insertElement((*stmts)[j], i + j); } } } for (int i = 0; i < m_stmt->getCount(); i++) { MethodStatementPtr meth = dynamic_pointer_cast((*m_stmt)[i]); if (meth && meth->getName() == "__construct") { constructor = meth; break; } } for (int i = 0; i < m_stmt->getCount(); i++) { if (!constructor) { MethodStatementPtr meth = dynamic_pointer_cast((*m_stmt)[i]); if (meth && meth->getName() == classScope->getName() && !classScope->isTrait()) { // class-name constructor constructor = meth; classScope->setAttribute(ClassScope::ClassNameConstructor); } } IParseHandlerPtr ph = dynamic_pointer_cast((*m_stmt)[i]); ph->onParseRecur(ar, classScope); } if (constructor && constructor->getModifiers()->isStatic()) { constructor->parseTimeFatal(Compiler::InvalidAttribute, "Constructor %s::%s() cannot be static", classScope->getOriginalName().c_str(), constructor->getOriginalName().c_str()); } } } StatementPtr ClassStatement::addClone(StatementPtr origStmt) { assert(m_stmt); StatementPtr newStmt = Clone(origStmt); MethodStatementPtr newMethStmt = dynamic_pointer_cast(newStmt); if (newMethStmt) { newMethStmt->setClassName(m_name); newMethStmt->setOriginalClassName(m_originalName); } m_stmt->addElement(newStmt); return newStmt; } /////////////////////////////////////////////////////////////////////////////// // static analysis functions string ClassStatement::getName() const { return string("Class ") + getScope()->getName(); } void ClassStatement::analyzeProgram(AnalysisResultPtr ar) { vector 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]); } checkVolatile(ar); if (m_stmt) { m_stmt->analyzeProgram(ar); } if (ar->getPhase() != AnalysisResult::AnalyzeAll) return; 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 )) || (cls->isTrait())) { Compiler::Error(Compiler::InvalidDerivation, shared_from_this(), "You are extending " + cls->getOriginalName() + " which is an interface or a trait"); } if (cls->isUserClass()) { cls->addUse(getScope(), BlockScope::UseKindParentRef); } } } } void ClassStatement::inferTypes(AnalysisResultPtr ar) { } /////////////////////////////////////////////////////////////////////////////// // code generation functions void ClassStatement::getAllParents(AnalysisResultConstPtr ar, std::vector &names) { if (!m_parent.empty()) { ClassScopePtr cls = ar->findClass(m_parent); if (cls) { if (!cls->isRedeclaring()) { cls->getAllParents(ar, names); } names.push_back(m_originalParent); } } if (m_base) { vector bases; m_base->getStrings(bases); for (unsigned int i = 0; i < bases.size(); i++) { ClassScopePtr cls = ar->findClass(bases[i]); if (cls) { cls->getAllParents(ar, names); names.push_back(cls->getOriginalName()); } } } } void ClassStatement::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) { ClassScopeRawPtr classScope = getClassScope(); if (!classScope->isUserClass()) return; if (m_type == T_TRAIT) { cg_printf("trait %s", m_originalName.c_str()); } else { switch (m_type) { case T_CLASS: break; case T_ABSTRACT: cg_printf("abstract "); break; case T_FINAL: cg_printf("final "); break; default: assert(false); } cg_printf("class %s", m_originalName.c_str()); } if (!m_parent.empty()) { cg_printf(" extends %s", m_originalParent.c_str()); } if (m_base) { cg_printf(" implements "); m_base->outputPHP(cg, ar); } cg_indentBegin(" {\n"); classScope->outputPHP(cg, ar); if (m_stmt) m_stmt->outputPHP(cg, ar); cg_indentEnd("}\n"); } bool ClassStatement::hasImpl() const { return true; }