text: /*
     +----------------------------------------------------------------------+
     | 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/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 <sstream>
  #include <algorithm>
  
  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<string> 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<UserAttributePtr> attrs;
    if (m_attrList) {
      for (int i = 0; i < m_attrList->getCount(); ++i) {
        UserAttributePtr a =
          dynamic_pointer_cast<UserAttribute>((*m_attrList)[i]);
        attrs.push_back(a);
      }
    }
  
    StatementPtr stmt = dynamic_pointer_cast<Statement>(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<StatementList>((*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<MethodStatement>((*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<MethodStatement>((*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<IParseHandler>((*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<MethodStatement>(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<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]);
    }
  
    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<std::string> &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<string> 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;
  }
  