Exemplo n.º 1
0
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);
  }

  ClassScopePtr clsScope = getClassScope();

  // Check that every trait stmt is either a method, class_var, or trait_use
  if (clsScope->isTrait()) {
    StatementListPtr stmts = getStmts();
    if (stmts) {
      for (int s = 0; s < stmts->getCount(); s++) {
        StatementPtr stmt = (*stmts)[s];
        if(!dynamic_pointer_cast<UseTraitStatement>(stmt) &&
           !dynamic_pointer_cast<MethodStatement>(stmt) &&
           !dynamic_pointer_cast<ClassVariable>(stmt)) {
          Compiler::Error(Compiler::InvalidTraitStatement, stmt);
        }
      }
    }
  }

  if (ar->getPhase() != AnalysisResult::AnalyzeAll) return;

  clsScope->importUsedTraits(ar);

  ar->recordClassSource(m_name, m_loc, getFileScope()->getName());
  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(),
                        cls->getOriginalName());
      }
      if (cls->isUserClass()) {
        cls->addUse(getScope(), BlockScope::UseKindParentRef);
      }
    }
  }
}
Exemplo n.º 2
0
void ClassScope::importUsedTraits(AnalysisResultPtr ar) {
  // Trait flattening is supposed to happen only when we have awareness of the
  // whole program.
  assert(Option::WholeProgram);

  if (m_traitStatus == FLATTENED) return;
  if (m_traitStatus == BEING_FLATTENED) {
    getStmt()->analysisTimeFatal(
      Compiler::CyclicDependentTraits,
      "Cyclic dependency between traits involving %s",
      getScopeName().c_str()
    );
    return;
  }
  if (m_usedTraitNames.size() == 0) {
    m_traitStatus = FLATTENED;
    return;
  }
  m_traitStatus = BEING_FLATTENED;

  m_numDeclMethods = m_functionsVec.size();

  // 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);
    }
  }

  TMIData tmid;

  if (isTrait()) {
    for (auto const& req : getClassRequiredExtends()) {
      ClassScopePtr rCls = ar->findClass(req);
      if (!rCls || rCls->isFinal() || rCls->isInterface()) {
        getStmt()->analysisTimeFatal(
          Compiler::InvalidDerivation,
          Strings::TRAIT_BAD_REQ_EXTENDS,
          m_scopeName.c_str(),
          req.c_str(),
          req.c_str()
        );
      }
    }
    for (auto const& req : getClassRequiredImplements()) {
      ClassScopePtr rCls = ar->findClass(req);
      if (!rCls || !(rCls->isInterface())) {
        getStmt()->analysisTimeFatal(
          Compiler::InvalidDerivation,
          Strings::TRAIT_BAD_REQ_IMPLEMENTS,
          m_scopeName.c_str(),
          req.c_str(),
          req.c_str()
        );
      }
    }
  }

  // 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); // XXX: is this useful ... for anything?
      getStmt()->analysisTimeFatal(
        Compiler::UnknownTrait,
        Strings::TRAITS_UNKNOWN_TRAIT,
        m_usedTraitNames[i].c_str()
      );
    }

    // First, make sure the used trait is flattened.
    tCls->importUsedTraits(ar);

    findTraitMethodsToImport(ar, tCls, tmid);

    // Import any interfaces implemented.
    tCls->getInterfaces(ar, m_bases, /* recursive */ false);

    importClassRequirements(ar, tCls);
  }

  // Apply rules.
  applyTraitRules(tmid);

  // Remove methods declared on the current class from the trait import list;
  // the class methods take precedence.
  for (auto const& methName : tmid.methodNames()) {
    if (findFunction(ar, methName, false /* recursive */,
                     false /* exclIntfBase */)) {
      // This does not affect the methodNames() vector.
      tmid.erase(methName);
    }
  }

  auto traitMethods = tmid.finish(this);

  std::map<string, MethodStatementPtr, stdltistr> importedTraitMethods;
  std::vector<std::pair<string,const TraitMethod*>> importedTraitsWithOrigName;

  // Actually import the methods.
  for (auto const& mdata : traitMethods) {
    if ((mdata.tm.modifiers ? mdata.tm.modifiers
                            : mdata.tm.method->getModifiers()
        )->isAbstract()) {
      // Skip abstract methods, if the method already exists in the class.
      if (findFunction(ar, mdata.name, true) ||
          importedTraitMethods.count(mdata.name)) {
        continue;
      }
    }

    auto sourceName = mdata.tm.ruleStmt
      ? ((TraitAliasStatement*)mdata.tm.ruleStmt.get())->getMethodName()
      : mdata.name;

    importedTraitMethods[sourceName] = MethodStatementPtr();
    importedTraitsWithOrigName.push_back(
      std::make_pair(sourceName, &mdata.tm));
  }

  // Make sure there won't be 2 constructors after importing
  auto traitConstruct = importedTraitMethods.count("__construct");
  auto traitName = importedTraitMethods.count(getScopeName());
  auto classConstruct = m_functions.count("__construct");
  auto className = m_functions.count(getScopeName());
  if ((traitConstruct && traitName) ||
      (traitConstruct && className) ||
      (classConstruct && traitName)) {
    getStmt()->analysisTimeFatal(
      Compiler::InvalidDerivation,
      "%s has colliding constructor definitions coming from traits",
      getScopeName().c_str()
    );
  }

  for (auto const& traitPair : importedTraitsWithOrigName) {
    auto traitMethod = traitPair.second;

    MethodStatementPtr newMeth = importTraitMethod(
      *traitMethod,
      ar,
      traitMethod->originalName
    );
  }

  // Import trait properties
  importTraitProperties(ar);

  m_traitStatus = FLATTENED;
}
Exemplo n.º 3
0
void ClassScope::importUsedTraits(AnalysisResultPtr ar) {
  // Trait flattening is supposed to happen only when we have awareness of
  // the whole program.
  assert(Option::WholeProgram);

  if (m_traitStatus == FLATTENED) return;
  if (m_traitStatus == BEING_FLATTENED) {
    getStmt()->analysisTimeFatal(
      Compiler::CyclicDependentTraits,
      "Cyclic dependency between traits involving %s",
      getOriginalName().c_str()
    );
    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);
    }
  }

  if (isTrait()) {
    for (auto const& req : getTraitRequiredExtends()) {
      ClassScopePtr rCls = ar->findClass(req);
      if (!rCls || rCls->isFinal() || rCls->isInterface()) {
        getStmt()->analysisTimeFatal(
          Compiler::InvalidDerivation,
          Strings::TRAIT_BAD_REQ_EXTENDS,
          m_originalName.c_str(),
          req.c_str(),
          req.c_str()
        );
      }
    }
    for (auto const& req : getTraitRequiredImplements()) {
      ClassScopePtr rCls = ar->findClass(req);
      if (!rCls || !(rCls->isInterface())) {
        getStmt()->analysisTimeFatal(
          Compiler::InvalidDerivation,
          Strings::TRAIT_BAD_REQ_IMPLEMENTS,
          m_originalName.c_str(),
          req.c_str(),
          req.c_str()
        );
      }
    }
  }

  // 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); // XXX: is this useful ... for anything?
      getStmt()->analysisTimeFatal(
        Compiler::UnknownTrait,
        Strings::TRAITS_UNKNOWN_TRAIT,
        m_usedTraitNames[i].c_str()
      );
    }
    // First, make sure the used trait is flattened
    tCls->importUsedTraits(ar);

    findTraitMethodsToImport(ar, tCls);

    // Import any interfaces implemented
    tCls->getInterfaces(ar, m_bases, false);
  }
  for (unsigned i = 0; i < m_usedTraitNames.size(); i++) {
    // Requirements must be checked in a separate loop because the
    // interfaces required by one trait may be implemented by another trait
    // whose "use" appears later in the class' scope
    ClassScopePtr tCls = ar->findClass(m_usedTraitNames[i]);
    importTraitRequirements(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;

  // 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) {
      getStmt()->analysisTimeFatal(
        Compiler::MethodInMultipleTraits,
        Strings::METHOD_IN_MULTIPLE_TRAITS,
        iter->first.c_str()
      );
    } 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;
        }
      }
      string sourceName = traitMethIter->m_ruleStmt ?
        toLower(((TraitAliasStatement*)traitMethIter->m_ruleStmt.get())->
                      getMethodName()) : iter->first;
      importedTraitMethods[sourceName] = MethodStatementPtr();
      importedTraitsWithOrigName.push_back(
        make_pair(sourceName, &*traitMethIter));
    }
  }

  // Make sure there won't be 2 constructors after importing
  auto traitConstruct = importedTraitMethods.count("__construct");
  auto traitName = importedTraitMethods.count(getName());
  auto classConstruct = m_functions.count("__construct");
  auto className = m_functions.count(getName());
  if ((traitConstruct && traitName) ||
      (traitConstruct && className) ||
      (classConstruct && traitName)) {
    getStmt()->analysisTimeFatal(
      Compiler::InvalidDerivation,
      "%s has colliding constructor definitions coming from traits",
      getOriginalName().c_str()
    );
  }

  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, toLower(traitMethod->m_originalName),
      importedTraitMethods);
    if (newMeth) {
      importedTraitMethods[sourceName] = newMeth;
    }
  }

  // Import trait properties
  importTraitProperties(ar);

  m_traitStatus = FLATTENED;
}