<commit_before>#include "ImportMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace import_tidy;

namespace clang {
namespace ast_matchers {
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCMessageExpr> message;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCInterfaceDecl> interface;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCMethodDecl> method;
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCProtocolExpr> protocol;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ImportDecl> import;

  AST_MATCHER(ObjCInterfaceDecl, isImplementationInMainFile) {
    if (!Node.isThisDeclarationADefinition())
      return false;

    if (auto *ImpDecl = Node.getImplementation()) {
      auto &SourceManager = Finder->getASTContext().getSourceManager();
      return SourceManager.isInMainFile(SourceManager.getExpansionLoc(ImpDecl->getLocStart()));
    } else {
      return false;
    }
  }

  AST_MATCHER(ObjCInterfaceDecl, isForwardDeclare) {
    return !Node.hasDefinition();
  }

  AST_MATCHER(Decl, isNotInSystemHeader) {
    auto Loc = Node.getLocation();
    if (!Loc.isValid())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    return !SM.isInSystemHeader(Loc);
  }

  AST_MATCHER(ObjCMethodDecl, isDefinedInHeaderOrMainFile) {
    auto *ID = Node.getClassInterface();
    if (!ID || !ID->getImplementation())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    if (SM.isInMainFile(SM.getExpansionLoc(Node.getLocStart())))
      return true;

    auto ImpLoc = SM.getExpansionLoc(ID->getImplementation()->getLocStart());
    return SM.isInMainFile(ImpLoc);
  }

} // end namespace ast_matchers
} // end namespace clang

namespace {

  static bool haveReplacementForFile(Replacements &Replacements, StringRef Path) {
    auto Found = std::find_if(Replacements.begin(), Replacements.end(),
                              [Path](const Replacement &R) {
                                return R.getFilePath() == Path;
                              });
    return Found != Replacements.end();
  }

  unsigned RangeEnd(const Range &R) {
    return R.getOffset() + R.getLength();
  }

  static std::vector<Range> collapsedRanges(const std::vector<Range> &Ranges) {
    std::vector<Range> sorted(Ranges.begin(), Ranges.end());
    if (Ranges.size() == 0)
      return sorted;

    std::sort(sorted.begin(), sorted.end(), [](Range &lhs, Range &rhs) {
      return lhs.getOffset() < rhs.getOffset();
    });

    for (auto I = sorted.begin(); I < sorted.end() - 1; I++) {
      auto FirstRange = *I;
      auto NextRange = *(I + 1);

      if (RangeEnd(FirstRange) >= NextRange.getOffset()) {
        *I = Range(FirstRange.getOffset(),
                   RangeEnd(NextRange) - FirstRange.getOffset());
        sorted.erase(I + 1);
      }
    }

    return sorted;
  }

  class ImportCallbacks : public clang::PPCallbacks {
  public:
    ImportCallbacks(const SourceManager &SM, ImportMatcher &M) :
      SM(SM), Matcher(M) { };

    void InclusionDirective(SourceLocation HashLoc,
                            const Token &IncludeTok,
                            StringRef FileName,
                            bool IsAngled,
                            CharSourceRange FilenameRange,
                            const FileEntry *File,
                            StringRef SearchPath,
                            StringRef RelativePath,
                            const Module *Imported) override {
      if (!SM.isInSystemHeader(HashLoc)) {
        Matcher.removeImport(HashLoc, SM);
      }
    }
  private:
    const SourceManager &SM;
    ImportMatcher &Matcher;
  };

} // end anonymous namespace

namespace import_tidy {
  static const StringRef nodeKey = "key";

  bool FileCallbacks::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
    llvm::outs() << "Compiling " << Filename << "\n";
    auto &SM = CI.getSourceManager();
    auto &PP = CI.getPreprocessor();
    PP.addPPCallbacks(std::unique_ptr<ImportCallbacks>(new ImportCallbacks(SM, Matcher)));
    SourceMgr = &SM;
    Matcher.setSysroot(CI.getHeaderSearchOpts().Sysroot);

    return true;
  }

  void FileCallbacks::handleEndSource() {
    Matcher.flush(*SourceMgr);
  }

  void CallExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *FD = Result.Nodes.getNodeAs<CallExpr>(nodeKey)->getDirectCallee()) {
      auto &SM = *Result.SourceManager;
      if (SM.isInMainFile(FD->getLocStart())) {
        Matcher.addImport(SM.getMainFileID(), FD, SM);
      }
    }
  }

  void CastExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *CE = Result.Nodes.getNodeAs<CastExpr>(nodeKey)) {
      auto &SM = *Result.SourceManager;

      if (auto *PT = CE->getType()->getAs<ObjCObjectPointerType>()) {
        if (auto *ID = PT->getInterfaceDecl()) {
          Matcher.addImport(SM.getMainFileID(), ID, SM);
        }
        for (auto i = PT->qual_begin(); i != PT->qual_end(); i++) {
          Matcher.addImport(SM.getMainFileID(), *i, SM);
        }
      } else if (auto *DRE = dyn_cast<DeclRefExpr>(CE->getSubExpr())) {
        if (auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
          Matcher.addImport(SM.getMainFileID(), ECD, SM);
        }
      }
    }
  }

  void InterfaceCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *ID = Result.Nodes.getNodeAs<ObjCInterfaceDecl>(nodeKey)) {
      auto &SM = *Result.SourceManager;
      auto InFile = SM.getFileID(ID->getLocation());

      // import superclasses
      if (auto *SC = ID->getSuperClass()) {
        Matcher.addImport(InFile, SC, SM);
      }

      // import this file, it is a header
      if (InFile != SM.getMainFileID()) {
        Matcher.addImport(SM.getMainFileID(), ID, SM);
      }

      // import all protocol definitions
      for (auto *P : ID->protocols()) {
        Matcher.addImport(InFile, P, SM);
      }

      // import all protocol definitions of categories, most likely
      // class extensions in the implementation
      for (auto *Category : ID->visible_categories()) {
        auto CategoryFile = SM.getFileID(Category->getLocation());

        for (auto *Protocol : Category->protocols()) {
          Matcher.addImport(CategoryFile, Protocol, SM);
        }
      }

      // import any categories extending this class or its superclasses
      // that were included from this file
      auto *D = ID;
      do {
        for (auto *Cat : D->visible_categories()) {
          for (auto *Ctx : Cat->noload_decls()) {
            auto Loc = Ctx->getLocStart();
            auto IncludedIn = SM.getFileID(SM.getIncludeLoc(SM.getFileID(Loc)));
            if (IncludedIn == InFile) {
              Matcher.addImport(InFile, Ctx, SM);
            }
          }
        }
      } while ((D = D->getSuperClass()));
    }
  }

  void MessageExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *E = Result.Nodes.getNodeAs<ObjCMessageExpr>(nodeKey)) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), E->getMethodDecl(), SM);

      if (auto *ID = E->getReceiverInterface()) {
        Matcher.addImport(SM.getMainFileID(), ID, SM);
      } else if (auto *Ptr = E->getReceiverType()->getAs<ObjCObjectPointerType>()) {
        for (auto i = Ptr->qual_begin(); i != Ptr->qual_end(); i++) {
          Matcher.addImport(SM.getMainFileID(), *i, SM);
        }
      }
    }
  }

  void MethodCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *M = Result.Nodes.getNodeAs<ObjCMethodDecl>(nodeKey)) {
      auto FID = Result.SourceManager->getFileID(M->getLocation());
      addType(FID, M->getReturnType(), *Result.SourceManager);

      for (auto i = M->param_begin(); i != M->param_end(); i++) {
        addType(FID, (*i)->getType(), *Result.SourceManager);
      }
    }
  }

  void MethodCallback::addType(const FileID InFile, QualType T, const SourceManager &SM) {
    bool isMainFile = SM.getMainFileID() == InFile;

    if (auto *PT = T->getAs<ObjCObjectPointerType>()) {
      // import or forward declare the class type
      if (auto *ID = PT->getInterfaceDecl()) {
        auto Filename = SM.getFilename(ID->getLocation());
        bool isSystemDecl = Filename.startswith(Matcher.getSysroot());
        Matcher.addImport(InFile, ID, SM, !isSystemDecl && !isMainFile);
      }

      // import or forward declare any protocols being conformed to
      for (auto i = PT->qual_begin(); i != PT->qual_end(); i++) {
        auto Filename = SM.getFilename((*i)->getLocation());
        bool isSystemDecl = Filename.startswith(Matcher.getSysroot());
        Matcher.addImport(InFile, *i, SM, !isSystemDecl && !isMainFile);
      }
    } else if (auto *TD = T->getAs<TypedefType>()) {
      // any typedefs need to be imported
      if (auto *TypeDecl = TD->getDecl()) {
        Matcher.addImport(InFile, TypeDecl, SM);
      }
    }
  }

  void ProtocolCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *PD = Result.Nodes.getNodeAs<ObjCProtocolExpr>(nodeKey)->getProtocol()) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), PD, SM);
    }
  }

  void StripCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *D = Result.Nodes.getNodeAs<Decl>(nodeKey)) {
      Matcher.removeImport(D->getLocStart(), *Result.SourceManager);
    }
  }

  std::unique_ptr<FrontendActionFactory>
  ImportMatcher::getActionFactory(MatchFinder& Finder) {
    auto CallMatcher = callExpr(isExpansionInMainFile()).bind(nodeKey);
    auto CastMatcher = castExpr(isExpansionInMainFile()).bind(nodeKey);
    auto InterfaceMatcher = interface(isImplementationInMainFile()).bind(nodeKey);
    auto ForwardDeclareMatcher = interface(isNotInSystemHeader(), isForwardDeclare()).bind(nodeKey);
    auto ImportMatcher = import(isNotInSystemHeader()).bind(nodeKey);
    auto MsgMatcher = message(isExpansionInMainFile()).bind(nodeKey);
    auto MtdMatcher = method(isDefinedInHeaderOrMainFile()).bind(nodeKey);
    auto ProtoMatcher = protocol(isExpansionInMainFile()).bind(nodeKey);
    Finder.addMatcher(CallMatcher, &CallCallback);
    Finder.addMatcher(CastMatcher, &CastCallback);
    Finder.addMatcher(InterfaceMatcher, &InterfaceCallback);
    Finder.addMatcher(ForwardDeclareMatcher, &StripCallback);
    Finder.addMatcher(ImportMatcher, &StripCallback);
    Finder.addMatcher(MsgMatcher, &MsgCallback);
    Finder.addMatcher(MtdMatcher, &MtdCallback);
    Finder.addMatcher(ProtoMatcher, &ProtoCallback);

    return newFrontendActionFactory(&Finder, &FileCallbacks);
  }

  void ImportMatcher::addImport(const FileID InFile,
                                const Decl *D,
                                const SourceManager &SM,
                                bool isForwardDeclare) {
    // only allow file locations
    if (SM.getFilename(D->getLocStart()).size() == 0)
      return;

    // don't include files in themselves
    if (!isForwardDeclare && SM.getFileID(D->getLocStart()) == InFile)
      return;

    ImportMap[InFile].push_back(Import(SM, D, isForwardDeclare));
  }

  void ImportMatcher::removeImport(const SourceLocation Loc, const SourceManager &SM) {
    auto fid = SM.getFileID(Loc);
    auto *fileStart = SM.getBuffer(fid)->getBufferStart();
    unsigned start = SM.getFileOffset(Loc);
    unsigned length = strchr(fileStart + start, '\n') - fileStart - start + 1;

    ImportRanges[fid].push_back(Range(start, length));
  }

  void ImportMatcher::flush(const SourceManager &SM) {
    auto &out = llvm::outs();

    for (auto &Pair : ImportMap) {
      std::string import;
      llvm::raw_string_ostream ImportStr(import);
      auto Imports = sortedUniqueImports(Pair.second);
      for (auto *Import : Imports) {
        ImportStr << *Import << '\n';
      }

      auto Fid = Pair.first;
      auto StartLoc = SM.getLocForStartOfFile(Fid);
      auto Path = SM.getFilename(StartLoc);
      if (haveReplacementForFile(Replacements, Path))
        continue;

      auto ReplacementRanges = collapsedRanges(ImportRanges[Fid]);
      if (ReplacementRanges.size() == 0) {
        // make sure there is at least a line of whitespace after the new imports
        ImportStr << '\n';
      }

      for (auto I = ReplacementRanges.cbegin(); I != ReplacementRanges.cend(); I++) {
        auto Text = I == ReplacementRanges.cbegin() ? ImportStr.str() : "";
        auto Start = StartLoc.getLocWithOffset(I->getOffset());

        Replacements.insert(Replacement(SM, Start, I->getLength(), Text));
      }

      out << "File: " << SM.getFilename(StartLoc) << "\n";
      out << ImportStr.str() << "\n";
    }
    ImportMap.clear();
    ImportRanges.clear();
  }

  void ImportMatcher::printLibraryCounts(llvm::raw_ostream &OS) {
//    using ImpPair = std::pair<Import, unsigned>;
//    std::vector<ImpPair> counts(LibraryImportCount.begin(), LibraryImportCount.end());
//    std::sort(counts.begin(), counts.end(), [](const ImpPair &L, const ImpPair &R) {
//      return L.second < R.second;
//    });
//
//    OS << "\n\n";
//    OS << "--------------------------------" << "\n";
//    OS << "Libraries sorted by import count" << "\n";
//    OS << "--------------------------------" << "\n";
//    for (auto I = counts.rbegin(); I != counts.rend(); I++) {
//      OS << I->first << " : " << I->second << " times\n";
//    }
  }

} // end namespace import_tidy
<commit_msg>correctly handle overlapping import ranges, don't strip implicit @imports<commit_after>#include "ImportMatchers.h"
#include "clang/Basic/SourceManager.h"
#include "clang/ASTMatchers/ASTMatchersInternal.h"
#include "clang/AST/ExprObjC.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>

using namespace clang;
using namespace clang::ast_matchers;
using namespace clang::tooling;
using namespace import_tidy;

namespace clang {
namespace ast_matchers {
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCMessageExpr> message;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCInterfaceDecl> interface;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ObjCMethodDecl> method;
  static const internal::VariadicDynCastAllOfMatcher<Stmt, ObjCProtocolExpr> protocol;
  static const internal::VariadicDynCastAllOfMatcher<Decl, ImportDecl> import;

  AST_MATCHER(ObjCInterfaceDecl, isImplementationInMainFile) {
    if (!Node.isThisDeclarationADefinition())
      return false;

    if (auto *ImpDecl = Node.getImplementation()) {
      auto &SourceManager = Finder->getASTContext().getSourceManager();
      return SourceManager.isInMainFile(SourceManager.getExpansionLoc(ImpDecl->getLocStart()));
    } else {
      return false;
    }
  }

  AST_MATCHER(ObjCInterfaceDecl, isForwardDeclare) {
    return !Node.hasDefinition();
  }

  AST_MATCHER(Decl, isNotInSystemHeader) {
    auto Loc = Node.getLocation();
    if (!Loc.isValid())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    return !SM.isInSystemHeader(Loc);
  }

  AST_MATCHER(ObjCMethodDecl, isDefinedInHeaderOrMainFile) {
    auto *ID = Node.getClassInterface();
    if (!ID || !ID->getImplementation())
      return false;

    auto &SM = Finder->getASTContext().getSourceManager();
    if (SM.isInMainFile(SM.getExpansionLoc(Node.getLocStart())))
      return true;

    auto ImpLoc = SM.getExpansionLoc(ID->getImplementation()->getLocStart());
    return SM.isInMainFile(ImpLoc);
  }

} // end namespace ast_matchers
} // end namespace clang

namespace {

  static bool haveReplacementForFile(Replacements &Replacements, StringRef Path) {
    auto Found = std::find_if(Replacements.begin(), Replacements.end(),
                              [Path](const Replacement &R) {
                                return R.getFilePath() == Path;
                              });
    return Found != Replacements.end();
  }

  unsigned RangeEnd(const Range &R) {
    return R.getOffset() + R.getLength();
  }

  static std::vector<Range> collapsedRanges(const std::vector<Range> &Ranges) {
    std::vector<Range> sorted(Ranges.begin(), Ranges.end());
    if (Ranges.size() == 0)
      return sorted;

    std::sort(sorted.begin(), sorted.end(), [](Range &lhs, Range &rhs) {
      return lhs.getOffset() < rhs.getOffset();
    });

    auto I = sorted.begin();
    while (I < sorted.end() - 1) {
      if (RangeEnd(*I) >= (*(I + 1)).getOffset()) {
        *I = Range((*I).getOffset(), RangeEnd(*(I+1)) - (*I).getOffset());
        sorted.erase(I + 1);
      } else {
        I++;
      }
    }

    return sorted;
  }

  class ImportCallbacks : public clang::PPCallbacks {
  public:
    ImportCallbacks(const SourceManager &SM, ImportMatcher &M) :
      SM(SM), Matcher(M) { };

    void InclusionDirective(SourceLocation HashLoc,
                            const Token &IncludeTok,
                            StringRef FileName,
                            bool IsAngled,
                            CharSourceRange FilenameRange,
                            const FileEntry *File,
                            StringRef SearchPath,
                            StringRef RelativePath,
                            const Module *Imported) override {
      if (!SM.isInSystemHeader(HashLoc)) {
        Matcher.removeImport(HashLoc, SM);
      }
    }
  private:
    const SourceManager &SM;
    ImportMatcher &Matcher;
  };

} // end anonymous namespace

namespace import_tidy {
  static const StringRef nodeKey = "key";

  bool FileCallbacks::handleBeginSource(CompilerInstance &CI, StringRef Filename) {
    llvm::outs() << "Compiling " << Filename << "\n";
    auto &SM = CI.getSourceManager();
    auto &PP = CI.getPreprocessor();
    PP.addPPCallbacks(std::unique_ptr<ImportCallbacks>(new ImportCallbacks(SM, Matcher)));
    SourceMgr = &SM;
    Matcher.setSysroot(CI.getHeaderSearchOpts().Sysroot);

    return true;
  }

  void FileCallbacks::handleEndSource() {
    Matcher.flush(*SourceMgr);
  }

  void CallExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *FD = Result.Nodes.getNodeAs<CallExpr>(nodeKey)->getDirectCallee()) {
      auto &SM = *Result.SourceManager;
      if (SM.isInMainFile(FD->getLocStart())) {
        Matcher.addImport(SM.getMainFileID(), FD, SM);
      }
    }
  }

  void CastExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *CE = Result.Nodes.getNodeAs<CastExpr>(nodeKey)) {
      auto &SM = *Result.SourceManager;

      if (auto *PT = CE->getType()->getAs<ObjCObjectPointerType>()) {
        if (auto *ID = PT->getInterfaceDecl()) {
          Matcher.addImport(SM.getMainFileID(), ID, SM);
        }
        for (auto i = PT->qual_begin(); i != PT->qual_end(); i++) {
          Matcher.addImport(SM.getMainFileID(), *i, SM);
        }
      } else if (auto *DRE = dyn_cast<DeclRefExpr>(CE->getSubExpr())) {
        if (auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) {
          Matcher.addImport(SM.getMainFileID(), ECD, SM);
        }
      }
    }
  }

  void InterfaceCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *ID = Result.Nodes.getNodeAs<ObjCInterfaceDecl>(nodeKey)) {
      auto &SM = *Result.SourceManager;
      auto InFile = SM.getFileID(ID->getLocation());

      // import superclasses
      if (auto *SC = ID->getSuperClass()) {
        Matcher.addImport(InFile, SC, SM);
      }

      // import this file, it is a header
      if (InFile != SM.getMainFileID()) {
        Matcher.addImport(SM.getMainFileID(), ID, SM);
      }

      // import all protocol definitions
      for (auto *P : ID->protocols()) {
        Matcher.addImport(InFile, P, SM);
      }

      // import all protocol definitions of categories, most likely
      // class extensions in the implementation
      for (auto *Category : ID->visible_categories()) {
        auto CategoryFile = SM.getFileID(Category->getLocation());

        for (auto *Protocol : Category->protocols()) {
          Matcher.addImport(CategoryFile, Protocol, SM);
        }
      }

      // import any categories extending this class or its superclasses
      // that were included from this file
      auto *D = ID;
      do {
        for (auto *Cat : D->visible_categories()) {
          for (auto *Ctx : Cat->noload_decls()) {
            auto Loc = Ctx->getLocStart();
            auto IncludedIn = SM.getFileID(SM.getIncludeLoc(SM.getFileID(Loc)));
            if (IncludedIn == InFile) {
              Matcher.addImport(InFile, Ctx, SM);
            }
          }
        }
      } while ((D = D->getSuperClass()));
    }
  }

  void MessageExprCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *E = Result.Nodes.getNodeAs<ObjCMessageExpr>(nodeKey)) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), E->getMethodDecl(), SM);

      if (auto *ID = E->getReceiverInterface()) {
        Matcher.addImport(SM.getMainFileID(), ID, SM);
      } else if (auto *Ptr = E->getReceiverType()->getAs<ObjCObjectPointerType>()) {
        for (auto i = Ptr->qual_begin(); i != Ptr->qual_end(); i++) {
          Matcher.addImport(SM.getMainFileID(), *i, SM);
        }
      }
    }
  }

  void MethodCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *M = Result.Nodes.getNodeAs<ObjCMethodDecl>(nodeKey)) {
      auto FID = Result.SourceManager->getFileID(M->getLocation());
      addType(FID, M->getReturnType(), *Result.SourceManager);

      for (auto i = M->param_begin(); i != M->param_end(); i++) {
        addType(FID, (*i)->getType(), *Result.SourceManager);
      }
    }
  }

  void MethodCallback::addType(const FileID InFile, QualType T, const SourceManager &SM) {
    bool isMainFile = SM.getMainFileID() == InFile;

    if (auto *PT = T->getAs<ObjCObjectPointerType>()) {
      // import or forward declare the class type
      if (auto *ID = PT->getInterfaceDecl()) {
        auto Filename = SM.getFilename(ID->getLocation());
        bool isSystemDecl = Filename.startswith(Matcher.getSysroot());
        Matcher.addImport(InFile, ID, SM, !isSystemDecl && !isMainFile);
      }

      // import or forward declare any protocols being conformed to
      for (auto i = PT->qual_begin(); i != PT->qual_end(); i++) {
        auto Filename = SM.getFilename((*i)->getLocation());
        bool isSystemDecl = Filename.startswith(Matcher.getSysroot());
        Matcher.addImport(InFile, *i, SM, !isSystemDecl && !isMainFile);
      }
    } else if (auto *TD = T->getAs<TypedefType>()) {
      // any typedefs need to be imported
      if (auto *TypeDecl = TD->getDecl()) {
        Matcher.addImport(InFile, TypeDecl, SM);
      }
    }
  }

  void ProtocolCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *PD = Result.Nodes.getNodeAs<ObjCProtocolExpr>(nodeKey)->getProtocol()) {
      auto &SM = *Result.SourceManager;
      Matcher.addImport(SM.getMainFileID(), PD, SM);
    }
  }

  void StripCallback::run(const MatchFinder::MatchResult &Result) {
    if (auto *D = Result.Nodes.getNodeAs<Decl>(nodeKey)) {
      // implicit imports will already be stripped by the preprocessor callbacks
      if (!D->isImplicit())
        Matcher.removeImport(D->getLocStart(), *Result.SourceManager);
    }
  }

  std::unique_ptr<FrontendActionFactory>
  ImportMatcher::getActionFactory(MatchFinder& Finder) {
    auto CallMatcher = callExpr(isExpansionInMainFile()).bind(nodeKey);
    auto CastMatcher = castExpr(isExpansionInMainFile()).bind(nodeKey);
    auto InterfaceMatcher = interface(isImplementationInMainFile()).bind(nodeKey);
    auto ForwardDeclareMatcher = interface(isNotInSystemHeader(), isForwardDeclare()).bind(nodeKey);
    auto ImportMatcher = import(isNotInSystemHeader()).bind(nodeKey);
    auto MsgMatcher = message(isExpansionInMainFile()).bind(nodeKey);
    auto MtdMatcher = method(isDefinedInHeaderOrMainFile()).bind(nodeKey);
    auto ProtoMatcher = protocol(isExpansionInMainFile()).bind(nodeKey);
    Finder.addMatcher(CallMatcher, &CallCallback);
    Finder.addMatcher(CastMatcher, &CastCallback);
    Finder.addMatcher(InterfaceMatcher, &InterfaceCallback);
    Finder.addMatcher(ForwardDeclareMatcher, &StripCallback);
    Finder.addMatcher(ImportMatcher, &StripCallback);
    Finder.addMatcher(MsgMatcher, &MsgCallback);
    Finder.addMatcher(MtdMatcher, &MtdCallback);
    Finder.addMatcher(ProtoMatcher, &ProtoCallback);

    return newFrontendActionFactory(&Finder, &FileCallbacks);
  }

  void ImportMatcher::addImport(const FileID InFile,
                                const Decl *D,
                                const SourceManager &SM,
                                bool isForwardDeclare) {
    // only allow file locations
    if (SM.getFilename(D->getLocStart()).size() == 0)
      return;

    // don't include files in themselves
    if (!isForwardDeclare && SM.getFileID(D->getLocStart()) == InFile)
      return;

    ImportMap[InFile].push_back(Import(SM, D, isForwardDeclare));
  }

  void ImportMatcher::removeImport(const SourceLocation Loc, const SourceManager &SM) {
    auto fid = SM.getFileID(Loc);
    auto *fileStart = SM.getBuffer(fid)->getBufferStart();
    unsigned start = SM.getFileOffset(Loc);
    unsigned length = strchr(fileStart + start, '\n') - fileStart - start + 1;

    ImportRanges[fid].push_back(Range(start, length));
  }

  void ImportMatcher::flush(const SourceManager &SM) {
    auto &out = llvm::outs();

    for (auto &Pair : ImportMap) {
      std::string import;
      llvm::raw_string_ostream ImportStr(import);
      auto Imports = sortedUniqueImports(Pair.second);
      for (auto *Import : Imports) {
        ImportStr << *Import << '\n';
      }

      auto Fid = Pair.first;
      auto StartLoc = SM.getLocForStartOfFile(Fid);
      auto Path = SM.getFilename(StartLoc);
      if (haveReplacementForFile(Replacements, Path))
        continue;

      auto ReplacementRanges = collapsedRanges(ImportRanges[Fid]);
      if (ReplacementRanges.size() == 0) {
        // make sure there is at least a line of whitespace after the new imports
        ImportStr << '\n';
      }

      for (auto I = ReplacementRanges.cbegin(); I != ReplacementRanges.cend(); I++) {
        auto Text = I == ReplacementRanges.cbegin() ? ImportStr.str() : "";
        auto Start = StartLoc.getLocWithOffset(I->getOffset());

        Replacements.insert(Replacement(SM, Start, I->getLength(), Text));
      }

      out << "File: " << SM.getFilename(StartLoc) << "\n";
      out << ImportStr.str() << "\n";
    }
    ImportMap.clear();
    ImportRanges.clear();
  }

  void ImportMatcher::printLibraryCounts(llvm::raw_ostream &OS) {
//    using ImpPair = std::pair<Import, unsigned>;
//    std::vector<ImpPair> counts(LibraryImportCount.begin(), LibraryImportCount.end());
//    std::sort(counts.begin(), counts.end(), [](const ImpPair &L, const ImpPair &R) {
//      return L.second < R.second;
//    });
//
//    OS << "\n\n";
//    OS << "--------------------------------" << "\n";
//    OS << "Libraries sorted by import count" << "\n";
//    OS << "--------------------------------" << "\n";
//    for (auto I = counts.rbegin(); I != counts.rend(); I++) {
//      OS << I->first << " : " << I->second << " times\n";
//    }
  }

} // end namespace import_tidy
<|endoftext|>


<commit_before>/******************************************************************************
 Copyright 2016 Allied Telesis Labs Ltd. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
       this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <buildsys.h>

/* This is the full name of the file to be downloaded */
std::string DownloadFetch::full_name()
{
	const char *fname = NULL;

	if(strlen(this->filename.c_str())) {
		fname = this->filename.c_str();
	} else {
		fname = strrchr(this->fetch_uri.c_str(), '/');
		fname++;
	}

	return std::string(fname);
}

/* This is the final name, without any compressed extension */
std::string DownloadFetch::final_name()
{
	std::string ret = this->full_name();

	if(decompress) {
		char *fname = strdup(ret.c_str());
		char *ext = strrchr(fname, '.');
		if(ext != NULL)
			ext[0] = '\0';
		ret = std::string(fname);
		free(fname);
	}

	return ret;
}

bool DownloadFetch::fetch(BuildDir * d)
{
	int res = mkdir("dl", 0700);
	if((res < 0) && (errno != EEXIST)) {
		throw CustomException("Error: Creating download directory");
	}

	bool get = false;

	std::string fullname = this->full_name();
	std::string fname = this->final_name();

	{
		char *fpath = NULL;
		asprintf(&fpath, "%s/dl/%s", WORLD->getWorkingDir()->c_str(),
			 fname.c_str());
		FILE *f = fopen(fpath, "r");
		if(f == NULL) {
			get = true;
		} else {
			fclose(f);
		}
		free(fpath);
	}

	if(get) {
		bool localCacheHit = false;
		//Attempt to get file from local tarball cache if one is configured.
		if(WORLD->haveTarballCache()) {
			PackageCmd *pc = new PackageCmd("dl", "wget");
			char *url = NULL;
			asprintf(&url, "%s/%s", WORLD->tarballCache().c_str(),
				 fname.c_str());
			pc->addArg(url);
			pc->addArgFmt("-O%s", fullname.c_str());
			free(url);
			localCacheHit = pc->Run(this->P);
			delete pc;
		}
		//If we didn't get the file from the local cache, look upstream.
		if(!localCacheHit) {
			PackageCmd *pc = new PackageCmd("dl", "wget");
			pc->addArg(this->fetch_uri.c_str());
			pc->addArgFmt("-O%s", fullname.c_str());
			if(!pc->Run(this->P))
				throw CustomException("Failed to fetch file");
			delete pc;
		}
		if(decompress) {
			// We want to run a command on this file
			char *cmd = NULL;
			char *filename = strdup(fname.c_str());
			char *ext = strrchr(filename, '.');
			if(ext == NULL) {
				log(P->getName().c_str(),
				    "Could not guess decompression based on extension: %s\n",
				    fname.c_str());
			}

			if(strcmp(ext, ".bz2") == 0) {
				asprintf(&cmd, "bunzip2 -d dl/%s", fullname.c_str());
			} else if(strcmp(ext, ".gz") == 0) {
				asprintf(&cmd, "gunzip -d dl/%s", fullname.c_str());
			}
			system(cmd);
			free(cmd);
			free(filename);
		}
	}

	return true;
}

std::string DownloadFetch::HASH()
{
	char *fpath = NULL;
	asprintf(&fpath, "%s/dl/%s", WORLD->getWorkingDir()->c_str(),
		 this->final_name().c_str());
	char *hash = hash_file(fpath);
	free(fpath);
	std::string ret = std::string(hash);
	free(hash);
	return ret;
}

bool LinkFetch::fetch(BuildDir * d)
{
	char *location = strdup(this->fetch_uri.c_str());
	PackageCmd *pc = new PackageCmd(d->getPath(), "ln");
	pc->addArg("-sf");
	char *l = P->relative_fetch_path(location);
	pc->addArg(l);
	pc->addArg(".");
	if(!pc->Run(this->P)) {
		// An error occured, try remove the file, then relink
		PackageCmd *rmpc = new PackageCmd(d->getPath(), "rm");
		rmpc->addArg("-fr");
		char *l2 = strrchr(l, '/');
		if(l2[1] == '\0') {
			l2[0] = '\0';
			l2 = strrchr(l, '/');
		}
		l2++;
		rmpc->addArg(l2);
		if(!rmpc->Run(this->P))
			throw
			    CustomException
			    ("Failed to ln (symbolically), could not remove target first");
		if(!pc->Run(this->P))
			throw
			    CustomException
			    ("Failed to ln (symbolically), even after removing target first");
		delete rmpc;
	}
	free(l);
	delete pc;
	free(location);
	return true;
}

std::string LinkFetch::HASH()
{
	return std::string("");
}

std::string LinkFetch::relative_path()
{
	char *path = strdup(this->fetch_uri.c_str());
	const char *dname = strrchr(path, '/');
	if(dname) {
		dname++;
	} else {
		dname = path;
	}

	std::string ret = std::string(dname);

	free(path);
	return ret;
}

bool CopyFetch::fetch(BuildDir * d)
{
	char *location = strdup(this->fetch_uri.c_str());
	PackageCmd *pc = new PackageCmd(d->getPath(), "cp");
	pc->addArg("-dpRuf");
	char *l = P->absolute_fetch_path(location);
	pc->addArg(l);
	pc->addArg(".");
	free(l);
	if(!pc->Run(this->P))
		throw CustomException("Failed to copy (recursively)");
	delete pc;
	log(P, "Copied data in, considering code updated");
	P->setCodeUpdated();
	free(location);
	return true;
}

std::string CopyFetch::HASH()
{
	return std::string("");
}

std::string CopyFetch::relative_path()
{
	char *path = strdup(this->fetch_uri.c_str());
	const char *dname = strrchr(path, '/');
	if(dname) {
		dname++;
	} else {
		dname = path;
	}

	std::string ret = std::string(dname);

	free(path);
	return ret;
}

bool Fetch::add(FetchUnit * fu)
{
	FetchUnit **t = this->FUs;
	this->FU_count++;
	this->FUs = (FetchUnit **) realloc(t, sizeof(FetchUnit *) * this->FU_count);
	if(this->FUs == NULL) {
		this->FUs = t;
		this->FU_count--;
		return false;
	}
	this->FUs[this->FU_count - 1] = fu;
	return true;
}
<commit_msg>fetch: download, get hash from package digest if available<commit_after>/******************************************************************************
 Copyright 2016 Allied Telesis Labs Ltd. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice,
       this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in the
       documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <buildsys.h>

/* This is the full name of the file to be downloaded */
std::string DownloadFetch::full_name()
{
	const char *fname = NULL;

	if(strlen(this->filename.c_str())) {
		fname = this->filename.c_str();
	} else {
		fname = strrchr(this->fetch_uri.c_str(), '/');
		fname++;
	}

	return std::string(fname);
}

/* This is the final name, without any compressed extension */
std::string DownloadFetch::final_name()
{
	std::string ret = this->full_name();

	if(decompress) {
		char *fname = strdup(ret.c_str());
		char *ext = strrchr(fname, '.');
		if(ext != NULL)
			ext[0] = '\0';
		ret = std::string(fname);
		free(fname);
	}

	return ret;
}

bool DownloadFetch::fetch(BuildDir * d)
{
	int res = mkdir("dl", 0700);
	if((res < 0) && (errno != EEXIST)) {
		throw CustomException("Error: Creating download directory");
	}

	bool get = false;

	std::string fullname = this->full_name();
	std::string fname = this->final_name();

	{
		char *fpath = NULL;
		asprintf(&fpath, "%s/dl/%s", WORLD->getWorkingDir()->c_str(),
			 fname.c_str());
		FILE *f = fopen(fpath, "r");
		if(f == NULL) {
			get = true;
		} else {
			fclose(f);
		}
		free(fpath);
	}

	if(get) {
		bool localCacheHit = false;
		//Attempt to get file from local tarball cache if one is configured.
		if(WORLD->haveTarballCache()) {
			PackageCmd *pc = new PackageCmd("dl", "wget");
			char *url = NULL;
			asprintf(&url, "%s/%s", WORLD->tarballCache().c_str(),
				 fname.c_str());
			pc->addArg(url);
			pc->addArgFmt("-O%s", fullname.c_str());
			free(url);
			localCacheHit = pc->Run(this->P);
			delete pc;
		}
		//If we didn't get the file from the local cache, look upstream.
		if(!localCacheHit) {
			PackageCmd *pc = new PackageCmd("dl", "wget");
			pc->addArg(this->fetch_uri.c_str());
			pc->addArgFmt("-O%s", fullname.c_str());
			if(!pc->Run(this->P))
				throw CustomException("Failed to fetch file");
			delete pc;
		}
		if(decompress) {
			// We want to run a command on this file
			char *cmd = NULL;
			char *filename = strdup(fname.c_str());
			char *ext = strrchr(filename, '.');
			if(ext == NULL) {
				log(P->getName().c_str(),
				    "Could not guess decompression based on extension: %s\n",
				    fname.c_str());
			}

			if(strcmp(ext, ".bz2") == 0) {
				asprintf(&cmd, "bunzip2 -d dl/%s", fullname.c_str());
			} else if(strcmp(ext, ".gz") == 0) {
				asprintf(&cmd, "gunzip -d dl/%s", fullname.c_str());
			}
			system(cmd);
			free(cmd);
			free(filename);
		}
	}

	return true;
}

std::string DownloadFetch::HASH()
{
	std::string ret;
	/* Check if the package contains pre-computed hashes */
	char *hash = P->getFileHash(this->final_name().c_str());
	/* Otherwise fetch and calculate the hash */
	if(!hash) {
		this->fetch(NULL);
		char *fpath = NULL;
		asprintf(&fpath, "%s/dl/%s", WORLD->getWorkingDir()->c_str(),
			 this->final_name().c_str());
		hash = hash_file(fpath);
		free(fpath);
	}
	ret = std::string(hash);
	free(hash);
	return ret;
}

bool LinkFetch::fetch(BuildDir * d)
{
	char *location = strdup(this->fetch_uri.c_str());
	PackageCmd *pc = new PackageCmd(d->getPath(), "ln");
	pc->addArg("-sf");
	char *l = P->relative_fetch_path(location);
	pc->addArg(l);
	pc->addArg(".");
	if(!pc->Run(this->P)) {
		// An error occured, try remove the file, then relink
		PackageCmd *rmpc = new PackageCmd(d->getPath(), "rm");
		rmpc->addArg("-fr");
		char *l2 = strrchr(l, '/');
		if(l2[1] == '\0') {
			l2[0] = '\0';
			l2 = strrchr(l, '/');
		}
		l2++;
		rmpc->addArg(l2);
		if(!rmpc->Run(this->P))
			throw
			    CustomException
			    ("Failed to ln (symbolically), could not remove target first");
		if(!pc->Run(this->P))
			throw
			    CustomException
			    ("Failed to ln (symbolically), even after removing target first");
		delete rmpc;
	}
	free(l);
	delete pc;
	free(location);
	return true;
}

std::string LinkFetch::HASH()
{
	return std::string("");
}

std::string LinkFetch::relative_path()
{
	char *path = strdup(this->fetch_uri.c_str());
	const char *dname = strrchr(path, '/');
	if(dname) {
		dname++;
	} else {
		dname = path;
	}

	std::string ret = std::string(dname);

	free(path);
	return ret;
}

bool CopyFetch::fetch(BuildDir * d)
{
	char *location = strdup(this->fetch_uri.c_str());
	PackageCmd *pc = new PackageCmd(d->getPath(), "cp");
	pc->addArg("-dpRuf");
	char *l = P->absolute_fetch_path(location);
	pc->addArg(l);
	pc->addArg(".");
	free(l);
	if(!pc->Run(this->P))
		throw CustomException("Failed to copy (recursively)");
	delete pc;
	log(P, "Copied data in, considering code updated");
	P->setCodeUpdated();
	free(location);
	return true;
}

std::string CopyFetch::HASH()
{
	return std::string("");
}

std::string CopyFetch::relative_path()
{
	char *path = strdup(this->fetch_uri.c_str());
	const char *dname = strrchr(path, '/');
	if(dname) {
		dname++;
	} else {
		dname = path;
	}

	std::string ret = std::string(dname);

	free(path);
	return ret;
}

bool Fetch::add(FetchUnit * fu)
{
	FetchUnit **t = this->FUs;
	this->FU_count++;
	this->FUs = (FetchUnit **) realloc(t, sizeof(FetchUnit *) * this->FU_count);
	if(this->FUs == NULL) {
		this->FUs = t;
		this->FU_count--;
		return false;
	}
	this->FUs[this->FU_count - 1] = fu;
	return true;
}
<|endoftext|>


<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: baseModel.C,v 1.15 2002/12/16 16:10:09 amoll Exp $

#include <BALL/MOLVIEW/FUNCTOR/baseModel.h>

using namespace std;

namespace BALL
{

	namespace MOLVIEW
	{

		BaseModelProcessor::BaseModelProcessor()
			throw()
			:	UnaryProcessor<Composite>(),
				ExtendedPropertyManager(),
				find_geometric_object_(),
				color_calculator_(const_cast<ColorCalculator*>((ColorCalculator*)&RTTI::getDefault<ColorCalculator>())),
				model_connector_(const_cast<BaseModelConnector*>((BaseModelConnector*)&RTTI::getDefault<BaseModelConnector>()))
		{
			clear_();
		}

		BaseModelProcessor::BaseModelProcessor
			(const BaseModelProcessor& base_model_processor, bool deep)
			throw()
			:	UnaryProcessor<Composite>(),
				ExtendedPropertyManager(base_model_processor, deep),
				find_geometric_object_(base_model_processor.find_geometric_object_, deep),
				color_calculator_(base_model_processor.color_calculator_),
				model_connector_(base_model_processor.model_connector_)
		{
		}

		BaseModelProcessor::~BaseModelProcessor()
			throw()
		{
			#ifdef BALL_VIEW_DEBUG
				cout << "Destructing object " << (void *)this
					<< " of class " << RTTI::getName<BaseModelProcessor>() << endl;
			#endif

			destroy();
		}

		void BaseModelProcessor::clear()
			throw()
		{
			ExtendedPropertyManager::clear();
			find_geometric_object_.clear();
			color_calculator_ = const_cast<ColorCalculator*>((ColorCalculator*)&RTTI::getDefault<ColorCalculator>());
			model_connector_ = const_cast<BaseModelConnector*>((BaseModelConnector*)&RTTI::getDefault<BaseModelConnector>());

			clear_();
		}

		void BaseModelProcessor::destroy()
			throw()
		{
		}

		void BaseModelProcessor::set
			(const BaseModelProcessor& base_model_processor, bool deep)
			throw()
		{
			clear_();

			ExtendedPropertyManager::set(base_model_processor);
			find_geometric_object_.set(base_model_processor.find_geometric_object_, deep);
			color_calculator_ = base_model_processor.color_calculator_;
			model_connector_ = base_model_processor.model_connector_;
		}

		const BaseModelProcessor& BaseModelProcessor::operator =
			(const BaseModelProcessor& base_model_processor)
			throw()
		{
			set(base_model_processor);

			return *this;
		}

		void BaseModelProcessor::get
			(BaseModelProcessor& base_model_processor, bool deep) const
			throw()
		{
			base_model_processor.set(*this, deep);
		}

		void BaseModelProcessor::swap
			(BaseModelProcessor& base_model_processor)
			throw()
		{
			ExtendedPropertyManager::swap(base_model_processor);

			find_geometric_object_.swap(base_model_processor.find_geometric_object_);

			ColorCalculator *color_calculator = color_calculator_;
			color_calculator_ = base_model_processor.color_calculator_;
			base_model_processor.color_calculator_ = color_calculator;

			BaseModelConnector *model_connector = model_connector_;
			model_connector_ = base_model_processor.model_connector_;
			base_model_processor.model_connector_ = model_connector;
		}

		bool BaseModelProcessor::start()
		{
			if (hasProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC) == true)
			{
				getSearcher_().setProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
				getSearcher_().clearProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
				model_connector_->getGeometricObjectSearcher().setProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
				model_connector_->getGeometricObjectSearcher().clearProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
			}
			else // default
			{
				getSearcher_().setProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
				getSearcher_().clearProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
				model_connector_->getGeometricObjectSearcher().setProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
				model_connector_->getGeometricObjectSearcher().clearProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
			}

			getModelConnector()->registerColorCalculator(*getColorCalculator());

			return true;
		}

		bool BaseModelProcessor::finish()
		{
			return true;
		}

		Processor::Result BaseModelProcessor::operator()(Composite & /* composite */)
		{
			return Processor::CONTINUE;
		}

		bool BaseModelProcessor::isValid() const
			throw()
		{
			return (ExtendedPropertyManager::isValid()
							&& model_connector_->isValid()
							&& find_geometric_object_.isValid());
		}

		void BaseModelProcessor::dump(ostream& s, Size depth) const
			throw()
		{
			BALL_DUMP_STREAM_PREFIX(s);

			BALL_DUMP_DEPTH(s, depth);
			BALL_DUMP_HEADER(s, this, this);

			BALL_DUMP_DEPTH(s, depth);
			s << "Properties: -------------------------------" << endl;

			dump_(s, depth + 1);

			BALL_DUMP_DEPTH(s, depth);
			s << "-----------------------------------------------------" << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "default colorCalculator registered: "
					 << ((color_calculator_ ==
								(const_cast<ColorCalculator*>((const ColorCalculator *)(&RTTI::getDefault<ColorCalculator>())))) ? "yes" : "no")
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "user colorCalculator registered: "
					 << ((color_calculator_ !=
								(const_cast<ColorCalculator*>((const ColorCalculator *)(&RTTI::getDefault<ColorCalculator>())))) ? "yes" : "no")
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "generate static model: "
					 << ((hasProperty(GeometricObject::PROPERTY__OBJECT_STATIC)) ? "yes" : "no")
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "generate dynamic model: "
					 << ((hasProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC)) ? "yes" : "no")
					 << endl;

			BALL_DUMP_STREAM_SUFFIX(s);
		}

		void BaseModelProcessor::dump_
			(ostream& s, Size depth) const
		{
			BALL_DUMP_STREAM_PREFIX(s);

			BALL_DUMP_DEPTH(s, depth);
			s << "transparent object: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__OBJECT_TRANSPARENT)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "           visible: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__OBJECT_VISIBLE)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "            opened: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__OBJECT_OPENED)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "        dmode dots: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_MODE_DOTS)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "   dmode wireframe: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_MODE_WIREFRAME)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "       dmode solid: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_MODE_SOLID)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "    dprecision low: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_PRECISION_LOW)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << " dprecision medium: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_PRECISION_MEDIUM)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "   dprecision high: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_PRECISION_HIGH)
					 << endl;

			BALL_DUMP_STREAM_SUFFIX(s);
		}

  	bool BaseModelProcessor::isModel_(GeometricObject& geometric_object)
			throw()
    {
			if (geometric_object.hasProperty(PROPERTY__MODEL_BALL_AND_STICK))
			{
				return true;
			}
			else if (geometric_object.hasProperty(PROPERTY__MODEL_DOTS))
			{
				return true;
			}
			else if (geometric_object.hasProperty(PROPERTY__MODEL_LINES))
			{
				return true;
			}
			else if (geometric_object.hasProperty(PROPERTY__MODEL_VDW))
			{
				return true;
			}

			return false;
    }

	  void BaseModelProcessor::removeGeometricObjects_(Composite& composite, bool only_models)
			throw()
    {
			// get geometric objects
			composite.applyChild(getSearcher_());

			// if geometric objects found, delete them
			if (getSearcher_().geometricObjectsFound())
			{
				// get geometric objects
				List<VIEW::GeometricObject*>::Iterator it = getSearcher_().getGeometricObjects().begin();

				for(; it != getSearcher_().getGeometricObjects().end(); ++it)
				{
					if (only_models)
					{
						if (!isModel_(**it))
						{
							continue;
						}
					}

					Composite *composite_ptr = RTTI::castTo<Composite>(**it);

					// remove geometric object from composite
					if(composite_ptr != 0)
					{
						// delete geometric object
						delete composite_ptr;
					}
				}
			}
   }

		void BaseModelProcessor::clear_()
			throw()
		{
			setProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
			setProperty(GeometricObject::PROPERTY__OBJECT_OPAQUE);
			setProperty(GeometricObject::PROPERTY__OBJECT_VISIBLE);
			setProperty(GeometricObject::PROPERTY__OBJECT_CLOSED);
			setProperty(GeometricObject::PROPERTY__DRAWING_MODE_SOLID);
			setProperty(GeometricObject::PROPERTY__DRAWING_PRECISION_HIGH);
		}

#		ifdef BALL_NO_INLINE_FUNCTIONS
#			include <BALL/MOLVIEW/FUNCTOR/baseModel.iC>
#		endif

	} // namespace MOLVIEW

} // namespace BALL
<commit_msg>cosmetic change<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: baseModel.C,v 1.16 2002/12/17 16:09:58 amoll Exp $

#include <BALL/MOLVIEW/FUNCTOR/baseModel.h>

using namespace std;

namespace BALL
{

	namespace MOLVIEW
	{

		BaseModelProcessor::BaseModelProcessor()
			throw()
			:	UnaryProcessor<Composite>(),
				ExtendedPropertyManager(),
				find_geometric_object_(),
				color_calculator_(const_cast<ColorCalculator*>((ColorCalculator*)&RTTI::getDefault<ColorCalculator>())),
				model_connector_(const_cast<BaseModelConnector*>((BaseModelConnector*)&RTTI::getDefault<BaseModelConnector>()))
		{
			clear_();
		}

		BaseModelProcessor::BaseModelProcessor
			(const BaseModelProcessor& base_model_processor, bool deep)
			throw()
			:	UnaryProcessor<Composite>(),
				ExtendedPropertyManager(base_model_processor, deep),
				find_geometric_object_(base_model_processor.find_geometric_object_, deep),
				color_calculator_(base_model_processor.color_calculator_),
				model_connector_(base_model_processor.model_connector_)
		{
		}

		BaseModelProcessor::~BaseModelProcessor()
			throw()
		{
			#ifdef BALL_VIEW_DEBUG
				cout << "Destructing object " << (void *)this
					<< " of class " << RTTI::getName<BaseModelProcessor>() << endl;
			#endif

			destroy();
		}

		void BaseModelProcessor::clear()
			throw()
		{
			ExtendedPropertyManager::clear();
			find_geometric_object_.clear();
			color_calculator_ = const_cast<ColorCalculator*>((ColorCalculator*)&RTTI::getDefault<ColorCalculator>());
			model_connector_ = const_cast<BaseModelConnector*>((BaseModelConnector*)&RTTI::getDefault<BaseModelConnector>());

			clear_();
		}

		void BaseModelProcessor::destroy()
			throw()
		{
		}

		void BaseModelProcessor::set
			(const BaseModelProcessor& base_model_processor, bool deep)
			throw()
		{
			clear_();

			ExtendedPropertyManager::set(base_model_processor);
			find_geometric_object_.set(base_model_processor.find_geometric_object_, deep);
			color_calculator_ = base_model_processor.color_calculator_;
			model_connector_ = base_model_processor.model_connector_;
		}

		const BaseModelProcessor& BaseModelProcessor::operator =
			(const BaseModelProcessor& base_model_processor)
			throw()
		{
			set(base_model_processor);

			return *this;
		}

		void BaseModelProcessor::get
			(BaseModelProcessor& base_model_processor, bool deep) const
			throw()
		{
			base_model_processor.set(*this, deep);
		}

		void BaseModelProcessor::swap
			(BaseModelProcessor& base_model_processor)
			throw()
		{
			ExtendedPropertyManager::swap(base_model_processor);

			find_geometric_object_.swap(base_model_processor.find_geometric_object_);

			ColorCalculator *color_calculator = color_calculator_;
			color_calculator_ = base_model_processor.color_calculator_;
			base_model_processor.color_calculator_ = color_calculator;

			BaseModelConnector *model_connector = model_connector_;
			model_connector_ = base_model_processor.model_connector_;
			base_model_processor.model_connector_ = model_connector;
		}

		bool BaseModelProcessor::start()
		{
			if (hasProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC))
			{
				getSearcher_().setProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
				getSearcher_().clearProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
				model_connector_->getGeometricObjectSearcher().setProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
				model_connector_->getGeometricObjectSearcher().clearProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
			}
			else // default
			{
				getSearcher_().setProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
				getSearcher_().clearProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
				model_connector_->getGeometricObjectSearcher().setProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
				model_connector_->getGeometricObjectSearcher().clearProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC);
			}

			getModelConnector()->registerColorCalculator(*getColorCalculator());

			return true;
		}

		bool BaseModelProcessor::finish()
		{
			return true;
		}

		Processor::Result BaseModelProcessor::operator()(Composite & /* composite */)
		{
			return Processor::CONTINUE;
		}

		bool BaseModelProcessor::isValid() const
			throw()
		{
			return (ExtendedPropertyManager::isValid()
							&& model_connector_->isValid()
							&& find_geometric_object_.isValid());
		}

		void BaseModelProcessor::dump(ostream& s, Size depth) const
			throw()
		{
			BALL_DUMP_STREAM_PREFIX(s);

			BALL_DUMP_DEPTH(s, depth);
			BALL_DUMP_HEADER(s, this, this);

			BALL_DUMP_DEPTH(s, depth);
			s << "Properties: -------------------------------" << endl;

			dump_(s, depth + 1);

			BALL_DUMP_DEPTH(s, depth);
			s << "-----------------------------------------------------" << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "default colorCalculator registered: "
					 << ((color_calculator_ ==
								(const_cast<ColorCalculator*>((const ColorCalculator *)(&RTTI::getDefault<ColorCalculator>())))) ? "yes" : "no")
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "user colorCalculator registered: "
					 << ((color_calculator_ !=
								(const_cast<ColorCalculator*>((const ColorCalculator *)(&RTTI::getDefault<ColorCalculator>())))) ? "yes" : "no")
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "generate static model: "
					 << ((hasProperty(GeometricObject::PROPERTY__OBJECT_STATIC)) ? "yes" : "no")
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "generate dynamic model: "
					 << ((hasProperty(GeometricObject::PROPERTY__OBJECT_DYNAMIC)) ? "yes" : "no")
					 << endl;

			BALL_DUMP_STREAM_SUFFIX(s);
		}

		void BaseModelProcessor::dump_
			(ostream& s, Size depth) const
		{
			BALL_DUMP_STREAM_PREFIX(s);

			BALL_DUMP_DEPTH(s, depth);
			s << "transparent object: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__OBJECT_TRANSPARENT)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "           visible: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__OBJECT_VISIBLE)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "            opened: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__OBJECT_OPENED)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "        dmode dots: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_MODE_DOTS)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "   dmode wireframe: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_MODE_WIREFRAME)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "       dmode solid: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_MODE_SOLID)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "    dprecision low: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_PRECISION_LOW)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << " dprecision medium: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_PRECISION_MEDIUM)
					 << endl;

			BALL_DUMP_DEPTH(s, depth);
			s << "   dprecision high: "
					 << BALL_MOLVIEW_PRINT_PROPERTY
											(GeometricObject::PROPERTY__DRAWING_PRECISION_HIGH)
					 << endl;

			BALL_DUMP_STREAM_SUFFIX(s);
		}

  	bool BaseModelProcessor::isModel_(GeometricObject& geometric_object)
			throw()
    {
			if (geometric_object.hasProperty(PROPERTY__MODEL_BALL_AND_STICK))
			{
				return true;
			}
			else if (geometric_object.hasProperty(PROPERTY__MODEL_DOTS))
			{
				return true;
			}
			else if (geometric_object.hasProperty(PROPERTY__MODEL_LINES))
			{
				return true;
			}
			else if (geometric_object.hasProperty(PROPERTY__MODEL_VDW))
			{
				return true;
			}

			return false;
    }

	  void BaseModelProcessor::removeGeometricObjects_(Composite& composite, bool only_models)
			throw()
    {
			// get geometric objects
			composite.applyChild(getSearcher_());

			// if geometric objects found, delete them
			if (getSearcher_().geometricObjectsFound())
			{
				// get geometric objects
				List<VIEW::GeometricObject*>::Iterator it = getSearcher_().getGeometricObjects().begin();

				for(; it != getSearcher_().getGeometricObjects().end(); ++it)
				{
					if (only_models)
					{
						if (!isModel_(**it))
						{
							continue;
						}
					}

					Composite *composite_ptr = RTTI::castTo<Composite>(**it);

					// remove geometric object from composite
					if(composite_ptr != 0)
					{
						// delete geometric object
						delete composite_ptr;
					}
				}
			}
   }

		void BaseModelProcessor::clear_()
			throw()
		{
			setProperty(GeometricObject::PROPERTY__OBJECT_STATIC);
			setProperty(GeometricObject::PROPERTY__OBJECT_OPAQUE);
			setProperty(GeometricObject::PROPERTY__OBJECT_VISIBLE);
			setProperty(GeometricObject::PROPERTY__OBJECT_CLOSED);
			setProperty(GeometricObject::PROPERTY__DRAWING_MODE_SOLID);
			setProperty(GeometricObject::PROPERTY__DRAWING_PRECISION_HIGH);
		}

#		ifdef BALL_NO_INLINE_FUNCTIONS
#			include <BALL/MOLVIEW/FUNCTOR/baseModel.iC>
#		endif

	} // namespace MOLVIEW

} // namespace BALL
<|endoftext|>


<commit_before>/*
 * Copyright(c) Records For Living, Inc. 2004-2011.  All rights reserved
 */
#include	"Stroika/Foundation/StroikaPreComp.h"

#include	<iostream>
#include	<sstream>

#include	"Stroika/Foundation/Debug/Assertions.h"
#include	"Stroika/Foundation/Debug/Trace.h"
#include	"Stroika/Foundation/Execution/Sleep.h"
#include	"Stroika/Foundation/Time/Date.h"
#include	"Stroika/Foundation/Time/DateTime.h"
#include	"Stroika/Foundation/Time/Duration.h"
#include	"Stroika/Foundation/Time/Realtime.h"

#include	"../TestHarness/TestHarness.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Time;

using	Stroika::Foundation::Debug::TraceContextBumper;

namespace	{

	void	Test_1_TestTickCountGrowsMonotonically_ ()
		{
			DurationSecondsType	start	=	Time::GetTickCount ();
			Execution::Sleep (0.1);
			VerifyTestResult (start <= Time::GetTickCount ());
		}

}

namespace	{

	void	Test_2_TestTimeOfDay_ ()
		{
			{
				TimeOfDay	t;
				VerifyTestResult (t.empty ());
				TimeOfDay	t2 (2);
				VerifyTestResult (t < t2);
				VerifyTestResult (t.GetAsSecondsCount () == 0);
				VerifyTestResult (not t2.empty ());
				VerifyTestResult (t.Format ().empty ());
				VerifyTestResult (not t2.Format ().empty ());
				VerifyTestResult (t2.GetHours () == 0);
				VerifyTestResult (t2.GetMinutes () == 0);
				VerifyTestResult (t2.GetSeconds () == 2);
			}
			{
				TimeOfDay	t2 (5 * 60 * 60 + 3 * 60 + 49);
				VerifyTestResult (t2.GetHours () == 5);
				VerifyTestResult (t2.GetMinutes () == 3);
				VerifyTestResult (t2.GetSeconds () == 49);
			}
			{
				TimeOfDay	t2 (25 * 60 * 60);
				VerifyTestResult (t2.GetHours () == 23);
				VerifyTestResult (t2.GetMinutes () == 59);
				VerifyTestResult (t2.GetSeconds () == 59);
				VerifyTestResult (t2 == TimeOfDay::kMax);
			}
			{
				VerifyTestResult (TimeOfDay::Parse (L"3pm", locale::classic ()).GetAsSecondsCount () == 15 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3am", locale::classic ()).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3:00", locale::classic ()).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"16:00", locale::classic ()).GetAsSecondsCount () == 16 * 60 * 60);
			}
			{
				// Not sure these should ALWAYS work in any locale. Probably not. But any locale I'd test in??? Maybe... Good for starters anyhow...
				//		-- LGP 2011-10-08
				VerifyTestResult (TimeOfDay::Parse (L"3pm", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 15 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3am", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3:00", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"16:00", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 16 * 60 * 60);
			}

			{
				#if		qPlatform_Windows
					const	LCID	kUS_ENGLISH_LOCALE	=	MAKELCID (MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
				#endif
				TimeOfDay	threePM	=	TimeOfDay::Parse (L"3pm", locale::classic ());
				#if		qPlatform_Windows
				VerifyTestResult (threePM.Format (kUS_ENGLISH_LOCALE) == L"3 PM");
				#endif
				//VerifyTestResult (threePM.Format (locale::classic ()) == L"3 PM");
				VerifyTestResult (threePM.Format (locale::classic ()) == L"15:00:00");	// UGH!!!
			}

		}

}

namespace	{
	void	VERIFY_ROUNDTRIP_XML_ (const Date& d)
		{
			VerifyTestResult (Date::Parse (d.Format (Date::eXML_PF), Date::eXML_PF) == d);
		}

	void	Test_3_TestDate_ ()
		{
			{
				Date	d (Year (1903), eApril, DayOfMonth (4));
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1903-04-04");
				VERIFY_ROUNDTRIP_XML_ (d);
				d.AddDays (4);
				VERIFY_ROUNDTRIP_XML_ (d);
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1903-04-08");
				d.AddDays (-4);
				VERIFY_ROUNDTRIP_XML_ (d);
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1903-04-04");
			}
			{
				Date	d	=	Date::Parse (L"09/14/1752", locale::classic ());
				VerifyTestResult (not d.empty ());
				VerifyTestResult (d == Date::kMin);
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1752-09-14");	// xml cuz otherwise we get confusion over locale - COULD use hardwired US locale at some point?
			}
			{
				Date	d;
				VerifyTestResult (d.empty ());
				VerifyTestResult (d < DateTime::GetToday ());
				VerifyTestResult (DateTime::GetToday () > d);
			}
			{
				Date	d	=	Date::kMin;
				VerifyTestResult (not d.empty ());
				VerifyTestResult (d < DateTime::Now ().GetDate ());
				VerifyTestResult (not (DateTime::Now ().GetDate () < d));
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1752-09-14");	// xml cuz otherwise we get confusion over locale - COULD use hardwired US locale at some point?
			}
			#if		qPlatform_Windows
			{
				wstring	testCase	=	L"6/1/2005";
				VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			{
				wstring	testCase	=	L"4/20/1964";
				VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			{
				wstring	testCase	=	L"7/4/1776";
				VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			{
				wstring	testCase	=	L"7/4/2076";
				//	TODO:
				//		Fails - debug soon -- LGP 2011-10-08
				//VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			#endif
		}

}

namespace	{

	void	Test_4_TestDateTime_ ()
		{
			{
				DateTime	d	=	Date (Year (1903), eApril, DayOfMonth (4));
				VerifyTestResult (d.Format (DateTime::eXML_PF) == L"1903-04-04");
			}
			{
				DateTime	d;
				VerifyTestResult (d.empty ());
				VerifyTestResult (d < DateTime::Now ());
				VerifyTestResult (DateTime::Now () > d);
			}
			{
				DateTime	d	=	DateTime::kMin;
				VerifyTestResult (not d.empty ());
				VerifyTestResult (d < DateTime::Now ());
				VerifyTestResult (DateTime::Now () > d);
				d = DateTime (d.GetDate (), d.GetTimeOfDay (), DateTime::eUTC_TZ);	// so that compare works - cuz we dont know timezone we'll run test with...
				VerifyTestResult (d.Format (DateTime::eXML_PF) == L"1752-09-14T00:00:00Z");	// xml cuz otherwise we get confusion over locale - COULD use hardwired US locale at some point?
			}
			#if		qPlatform_Windows
			{
				const	LCID	kUS_ENGLISH_LOCALE	=	MAKELCID (MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
				wstring	testCase	=	L"2010-01-01";
				//TODO: FIX SO THIS WORKS... (or come up with better test)
				//VerifyTestResult (DateTime::Parse (testCase, kUS_ENGLISH_LOCALE) == DateTime::Parse (testCase, locale::classic ()));
			}
			#endif
		}

}

namespace	{

	void	Test_5_DateTimeTimeT_ ()
		{
			{
				DateTime	d	=	Date (Year (2000), eApril, DayOfMonth (20));
				VerifyTestResult (d.As<time_t> () == 956188800);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3pm", locale ()));
				VerifyTestResult (d.As<time_t> () == 802278000);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3pm", TimeOfDay::eCurrentLocale_PF));
				VerifyTestResult (d.As<time_t> () == 802278000);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3am", TimeOfDay::eCurrentLocale_PF));
				VerifyTestResult (d.As<time_t> () == 802234800);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3:00", TimeOfDay::eCurrentLocale_PF));
				VerifyTestResult (d.As<time_t> () == 802234800);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				const	time_t	kTEST	=	802234800;
				DateTime	d	=	DateTime (kTEST);
				VerifyTestResult (d.As<time_t> () == kTEST);	// source - http://www.onlineconversion.com/unix_time.htm
			}
		}

}

namespace	{

	void	Test_6_Duration_ ()
		{
			const	int	kSecondsPerDay		=	TimeOfDay::kMaxSecondsPerDay;
			{
				const	Duration	k30Days		=	Duration (L"P30D");
				VerifyTestResult (k30Days.As<time_t> () == 30 * kSecondsPerDay);
			}
			{
				const	Duration	k6Months		=	Duration (L"P6M");
				VerifyTestResult (k6Months.As<time_t> () == 6 * 30 * kSecondsPerDay);
			}
			{
				const	Duration	kP1Y		=	Duration (L"P1Y");
				VerifyTestResult (kP1Y.As<time_t> () == 365 * kSecondsPerDay);
			}
			{
				const	Duration	kP2Y		=	Duration (L"P2Y");
				VerifyTestResult (kP2Y.As<time_t> () == 2 * 365 * kSecondsPerDay);
				VerifyTestResult (Duration (2 * 365 * kSecondsPerDay).As<wstring> () == L"P2Y");
			}
			{
				const	Duration	kHalfMinute		=	Duration (L"PT0.5M");
				VerifyTestResult (kHalfMinute.As<time_t> () == 30);
			}
			{
				const	Duration	kD		=	Duration (L"PT0.1S");
				VerifyTestResult (kD.As<time_t> () == 0);
				VerifyTestResult (kD.As<double> () == 0.1);
			}
			{
				const	Duration	kHalfMinute		=	Duration (L"PT0.5M");
				VerifyTestResult (kHalfMinute.PrettyPrint () == L"30 seconds");
			}
			{
				const	Duration	k3MS		=	Duration (L"PT0.003S");
				VerifyTestResult (k3MS.PrettyPrint () == L"3 milliseconds");
			}
			{
				const	Duration	kD		=	Duration (L"PT1.003S");
				VerifyTestResult (kD.PrettyPrint () == L"1 second, 3 milliseconds");
			}
			{
				const	Duration	kD		=	Duration (L"PT0.000045S");
				VerifyTestResult (kD.PrettyPrint () == L"45 microseconds");
			}
		}

}

namespace	{

	void	DoRegressionTests_ ()
		{
			Test_1_TestTickCountGrowsMonotonically_ ();
			Test_2_TestTimeOfDay_ ();
			Test_3_TestDate_ ();
			Test_4_TestDateTime_ ();
			Test_5_DateTimeTimeT_ ();
			Test_6_Duration_ ();
		}
}

#if qOnlyOneMain
extern  int TestDateAndTime ()
#else
int main (int argc, const char* argv[])
#endif
{
	Stroika::TestHarness::Setup ();
	Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
	return EXIT_SUCCESS;
}

<commit_msg>more automated tests for Duration stuff in time test suite<commit_after>/*
 * Copyright(c) Records For Living, Inc. 2004-2011.  All rights reserved
 */
#include	"Stroika/Foundation/StroikaPreComp.h"

#include	<iostream>
#include	<sstream>

#include	"Stroika/Foundation/Debug/Assertions.h"
#include	"Stroika/Foundation/Debug/Trace.h"
#include	"Stroika/Foundation/Execution/Sleep.h"
#include	"Stroika/Foundation/Time/Date.h"
#include	"Stroika/Foundation/Time/DateTime.h"
#include	"Stroika/Foundation/Time/Duration.h"
#include	"Stroika/Foundation/Time/Realtime.h"

#include	"../TestHarness/TestHarness.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Time;

using	Stroika::Foundation::Debug::TraceContextBumper;

namespace	{

	void	Test_1_TestTickCountGrowsMonotonically_ ()
		{
			DurationSecondsType	start	=	Time::GetTickCount ();
			Execution::Sleep (0.1);
			VerifyTestResult (start <= Time::GetTickCount ());
		}

}

namespace	{

	void	Test_2_TestTimeOfDay_ ()
		{
			{
				TimeOfDay	t;
				VerifyTestResult (t.empty ());
				TimeOfDay	t2 (2);
				VerifyTestResult (t < t2);
				VerifyTestResult (t.GetAsSecondsCount () == 0);
				VerifyTestResult (not t2.empty ());
				VerifyTestResult (t.Format ().empty ());
				VerifyTestResult (not t2.Format ().empty ());
				VerifyTestResult (t2.GetHours () == 0);
				VerifyTestResult (t2.GetMinutes () == 0);
				VerifyTestResult (t2.GetSeconds () == 2);
			}
			{
				TimeOfDay	t2 (5 * 60 * 60 + 3 * 60 + 49);
				VerifyTestResult (t2.GetHours () == 5);
				VerifyTestResult (t2.GetMinutes () == 3);
				VerifyTestResult (t2.GetSeconds () == 49);
			}
			{
				TimeOfDay	t2 (25 * 60 * 60);
				VerifyTestResult (t2.GetHours () == 23);
				VerifyTestResult (t2.GetMinutes () == 59);
				VerifyTestResult (t2.GetSeconds () == 59);
				VerifyTestResult (t2 == TimeOfDay::kMax);
			}
			{
				VerifyTestResult (TimeOfDay::Parse (L"3pm", locale::classic ()).GetAsSecondsCount () == 15 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3am", locale::classic ()).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3:00", locale::classic ()).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"16:00", locale::classic ()).GetAsSecondsCount () == 16 * 60 * 60);
			}
			{
				// Not sure these should ALWAYS work in any locale. Probably not. But any locale I'd test in??? Maybe... Good for starters anyhow...
				//		-- LGP 2011-10-08
				VerifyTestResult (TimeOfDay::Parse (L"3pm", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 15 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3am", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"3:00", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 3 * 60 * 60);
				VerifyTestResult (TimeOfDay::Parse (L"16:00", TimeOfDay::eCurrentLocale_PF).GetAsSecondsCount () == 16 * 60 * 60);
			}

			{
				#if		qPlatform_Windows
					const	LCID	kUS_ENGLISH_LOCALE	=	MAKELCID (MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
				#endif
				TimeOfDay	threePM	=	TimeOfDay::Parse (L"3pm", locale::classic ());
				#if		qPlatform_Windows
				VerifyTestResult (threePM.Format (kUS_ENGLISH_LOCALE) == L"3 PM");
				#endif
				//VerifyTestResult (threePM.Format (locale::classic ()) == L"3 PM");
				VerifyTestResult (threePM.Format (locale::classic ()) == L"15:00:00");	// UGH!!!
			}

		}

}

namespace	{
	void	VERIFY_ROUNDTRIP_XML_ (const Date& d)
		{
			VerifyTestResult (Date::Parse (d.Format (Date::eXML_PF), Date::eXML_PF) == d);
		}

	void	Test_3_TestDate_ ()
		{
			{
				Date	d (Year (1903), eApril, DayOfMonth (4));
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1903-04-04");
				VERIFY_ROUNDTRIP_XML_ (d);
				d.AddDays (4);
				VERIFY_ROUNDTRIP_XML_ (d);
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1903-04-08");
				d.AddDays (-4);
				VERIFY_ROUNDTRIP_XML_ (d);
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1903-04-04");
			}
			{
				Date	d	=	Date::Parse (L"09/14/1752", locale::classic ());
				VerifyTestResult (not d.empty ());
				VerifyTestResult (d == Date::kMin);
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1752-09-14");	// xml cuz otherwise we get confusion over locale - COULD use hardwired US locale at some point?
			}
			{
				Date	d;
				VerifyTestResult (d.empty ());
				VerifyTestResult (d < DateTime::GetToday ());
				VerifyTestResult (DateTime::GetToday () > d);
			}
			{
				Date	d	=	Date::kMin;
				VerifyTestResult (not d.empty ());
				VerifyTestResult (d < DateTime::Now ().GetDate ());
				VerifyTestResult (not (DateTime::Now ().GetDate () < d));
				VerifyTestResult (d.Format (Date::eXML_PF) == L"1752-09-14");	// xml cuz otherwise we get confusion over locale - COULD use hardwired US locale at some point?
			}
			#if		qPlatform_Windows
			{
				wstring	testCase	=	L"6/1/2005";
				VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			{
				wstring	testCase	=	L"4/20/1964";
				VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			{
				wstring	testCase	=	L"7/4/1776";
				VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			{
				wstring	testCase	=	L"7/4/2076";
				//	TODO:
				//		Fails - debug soon -- LGP 2011-10-08
				//VerifyTestResult (Date::Parse (testCase, LOCALE_USER_DEFAULT) == Date::Parse (testCase, locale::classic ()));
			}
			#endif
		}

}

namespace	{

	void	Test_4_TestDateTime_ ()
		{
			{
				DateTime	d	=	Date (Year (1903), eApril, DayOfMonth (4));
				VerifyTestResult (d.Format (DateTime::eXML_PF) == L"1903-04-04");
			}
			{
				DateTime	d;
				VerifyTestResult (d.empty ());
				VerifyTestResult (d < DateTime::Now ());
				VerifyTestResult (DateTime::Now () > d);
			}
			{
				DateTime	d	=	DateTime::kMin;
				VerifyTestResult (not d.empty ());
				VerifyTestResult (d < DateTime::Now ());
				VerifyTestResult (DateTime::Now () > d);
				d = DateTime (d.GetDate (), d.GetTimeOfDay (), DateTime::eUTC_TZ);	// so that compare works - cuz we dont know timezone we'll run test with...
				VerifyTestResult (d.Format (DateTime::eXML_PF) == L"1752-09-14T00:00:00Z");	// xml cuz otherwise we get confusion over locale - COULD use hardwired US locale at some point?
			}
			#if		qPlatform_Windows
			{
				const	LCID	kUS_ENGLISH_LOCALE	=	MAKELCID (MAKELANGID (LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT);
				wstring	testCase	=	L"2010-01-01";
				//TODO: FIX SO THIS WORKS... (or come up with better test)
				//VerifyTestResult (DateTime::Parse (testCase, kUS_ENGLISH_LOCALE) == DateTime::Parse (testCase, locale::classic ()));
			}
			#endif
		}

}

namespace	{

	void	Test_5_DateTimeTimeT_ ()
		{
			{
				DateTime	d	=	Date (Year (2000), eApril, DayOfMonth (20));
				VerifyTestResult (d.As<time_t> () == 956188800);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3pm", locale ()));
				VerifyTestResult (d.As<time_t> () == 802278000);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3pm", TimeOfDay::eCurrentLocale_PF));
				VerifyTestResult (d.As<time_t> () == 802278000);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3am", TimeOfDay::eCurrentLocale_PF));
				VerifyTestResult (d.As<time_t> () == 802234800);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				DateTime	d	=	DateTime (Date (Year (1995), eJune, DayOfMonth (4)), TimeOfDay::Parse (L"3:00", TimeOfDay::eCurrentLocale_PF));
				VerifyTestResult (d.As<time_t> () == 802234800);	// source - http://www.onlineconversion.com/unix_time.htm
			}
			{
				const	time_t	kTEST	=	802234800;
				DateTime	d	=	DateTime (kTEST);
				VerifyTestResult (d.As<time_t> () == kTEST);	// source - http://www.onlineconversion.com/unix_time.htm
			}
		}

}

namespace	{

	void	Test_6_Duration_ ()
		{
			const	int	kSecondsPerDay		=	TimeOfDay::kMaxSecondsPerDay;
			{
				const	Duration	k30Days		=	Duration (L"P30D");
				VerifyTestResult (k30Days.As<time_t> () == 30 * kSecondsPerDay);
			}
			{
				const	Duration	k6Months		=	Duration (L"P6M");
				VerifyTestResult (k6Months.As<time_t> () == 6 * 30 * kSecondsPerDay);
			}
			{
				const	Duration	kP1Y		=	Duration (L"P1Y");
				VerifyTestResult (kP1Y.As<time_t> () == 365 * kSecondsPerDay);
			}
			{
				const	Duration	kP2Y		=	Duration (L"P2Y");
				VerifyTestResult (kP2Y.As<time_t> () == 2 * 365 * kSecondsPerDay);
				VerifyTestResult (Duration (2 * 365 * kSecondsPerDay).As<wstring> () == L"P2Y");
			}
			{
				const	Duration	kHalfMinute		=	Duration (L"PT0.5M");
				VerifyTestResult (kHalfMinute.As<time_t> () == 30);
			}
			{
				const	Duration	kD		=	Duration (L"PT0.1S");
				VerifyTestResult (kD.As<time_t> () == 0);
				VerifyTestResult (kD.As<double> () == 0.1);
			}
			{
				const	Duration	kHalfMinute		=	Duration (L"PT0.5M");
				VerifyTestResult (kHalfMinute.PrettyPrint () == L"30 seconds");
			}
			{
				const	Duration	k3MS		=	Duration (L"PT0.003S");
				VerifyTestResult (k3MS.PrettyPrint () == L"3 milliseconds");
			}
			{
				const	Duration	kD		=	Duration (L"PT1.003S");
				VerifyTestResult (kD.PrettyPrint () == L"1 second, 3 milliseconds");
			}
			{
				const	Duration	kD		=	Duration (L"PT0.000045S");
				VerifyTestResult (kD.PrettyPrint () == L"45 microseconds");
			}
			VerifyTestResult (Duration (L"P30S").As<time_t> () == 30);
			VerifyTestResult (Duration (L"PT30S").As<time_t> () == 30);
			VerifyTestResult (Duration (60).As<wstring> () == L"PT1M");
			VerifyTestResult (Duration (L"-PT1H1S").As<time_t> () == -3601);

			for (time_t i = -45; i < 60 * 3 * 60 + 99; ++i) {
				VerifyTestResult (Duration (Duration (i).As<wstring> ()).As<time_t> () == i);
			}
			for (time_t i = 60 * 60 * 24 * 365 - 40; i < 3 * 60 * 60 * 24 * 365; i += 263) {
				VerifyTestResult (Duration (Duration (i).As<wstring> ()).As<time_t> () == i);
			}
		}

}

namespace	{

	void	DoRegressionTests_ ()
		{
			Test_1_TestTickCountGrowsMonotonically_ ();
			Test_2_TestTimeOfDay_ ();
			Test_3_TestDate_ ();
			Test_4_TestDateTime_ ();
			Test_5_DateTimeTimeT_ ();
			Test_6_Duration_ ();
		}
}

#if qOnlyOneMain
extern  int TestDateAndTime ()
#else
int main (int argc, const char* argv[])
#endif
{
	Stroika::TestHarness::Setup ();
	Stroika::TestHarness::PrintPassOrFail (DoRegressionTests_);
	return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2020  The Symbiflow Authors
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include "propagation.h"
#include <cassert>

USING_YOSYS_NAMESPACE

void Propagation::PropagateThroughBuffers(Buffer buffer) {
    for (auto& clock : Clocks::GetClocks(design_)) {
	auto& clock_wire = clock.second;
#ifdef SDC_DEBUG
	log("Clock wire %s\n", Clock::WireName(clock_wire).c_str());
#endif
	auto buf_wires =
	    FindSinkWiresForCellType(clock_wire, buffer.type, buffer.output);
	int path_delay(0);
	for (auto wire : buf_wires) {
#ifdef SDC_DEBUG
	    log("%s wire: %s\n", buffer.type.c_str(),
	        RTLIL::id2cstr(wire->name));
#endif
	    path_delay += buffer.delay;
	    Clock::Add(wire, Clock::Period(clock_wire),
	               Clock::RisingEdge(clock_wire) + path_delay,
	               Clock::FallingEdge(clock_wire) + path_delay, Clock::PROPAGATED);
	}
    }
}

std::vector<RTLIL::Wire*> Propagation::FindSinkWiresForCellType(
    RTLIL::Wire* driver_wire, const std::string& cell_type,
    const std::string& cell_port) {
    std::vector<RTLIL::Wire*> wires;
    if (!driver_wire) {
	return wires;
    }
    auto cell = FindSinkCellOfType(driver_wire, cell_type);
    RTLIL::Wire* wire = FindSinkWireOnPort(cell, cell_port);
    if (wire) {
	wires.push_back(wire);
	auto further_wires =
	    FindSinkWiresForCellType(wire, cell_type, cell_port);
	std::copy(further_wires.begin(), further_wires.end(),
	          std::back_inserter(wires));
    }
    return wires;
}

RTLIL::Cell* Propagation::FindSinkCellOfType(RTLIL::Wire* wire,
                                             const std::string& type) {
    RTLIL::Cell* sink_cell = NULL;
    if (!wire) {
	return sink_cell;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/w:" + wire->name.str();
    pass_->extra_args(std::vector<std::string>{base_selection, "%co:+" + type,
                                               base_selection, "%d"},
                      0, design_);
    auto selected_cells = top_module->selected_cells();
    // FIXME Handle more than one sink
    assert(selected_cells.size() <= 1);
    if (selected_cells.size() > 0) {
	sink_cell = selected_cells.at(0);
#ifdef SDC_DEBUG
	log("Found sink cell: %s\n",
	    RTLIL::unescape_id(sink_cell->name).c_str());
#endif
    }
    return sink_cell;
}

RTLIL::Cell* Propagation::FindSinkCellOnPort(RTLIL::Wire* wire,
                                             const std::string& port) {
    RTLIL::Cell* sink_cell = NULL;
    if (!wire) {
	return sink_cell;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/w:" + wire->name.str();
    pass_->extra_args(
        std::vector<std::string>{base_selection, "%co:+[" + port + "]",
                                 base_selection, "%d"},
        0, design_);
    auto selected_cells = top_module->selected_cells();
    // FIXME Handle more than one sink
    assert(selected_cells.size() <= 1);
    if (selected_cells.size() > 0) {
	sink_cell = selected_cells.at(0);
#ifdef SDC_DEBUG
	log("Found sink cell: %s\n",
	    RTLIL::unescape_id(sink_cell->name).c_str());
#endif
    }
    return sink_cell;
}

bool Propagation::WireHasSinkCell(RTLIL::Wire* wire) {
    if (!wire) {
	return false;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/w:" + wire->name.str();
    pass_->extra_args(
        std::vector<std::string>{base_selection, "%co:*",
                                 base_selection, "%d"},
        0, design_);
    auto selected_cells = top_module->selected_cells();
    return selected_cells.size() > 0;
}

RTLIL::Wire* Propagation::FindSinkWireOnPort(RTLIL::Cell* cell,
                                             const std::string& port_name) {
    RTLIL::Wire* sink_wire = NULL;
    if (!cell) {
	return sink_wire;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/c:" + cell->name.str();
    pass_->extra_args(
        std::vector<std::string>{base_selection, "%co:+[" + port_name + "]",
                                 base_selection, "%d"},
        0, design_);
    auto selected_wires = top_module->selected_wires();
    // FIXME Handle more than one sink
    assert(selected_wires.size() <= 1);
    if (selected_wires.size() > 0) {
	sink_wire = selected_wires.at(0);
#ifdef SDC_DEBUG
	log("Found sink wire: %s\n",
	    RTLIL::unescape_id(sink_wire->name).c_str());
#endif
    }
    return sink_wire;
}

void NaturalPropagation::Run() {
#ifdef SDC_DEBUG
    log("Start natural clock propagation\n");
#endif
    for (auto& clock : Clocks::GetClocks(design_)) {
	auto& clock_wire = clock.second;
#ifdef SDC_DEBUG
	log("Processing clock %s\n", RTLIL::id2cstr(clock_wire->name));
#endif
	auto aliases = FindAliasWires(clock_wire);
	Clock::Add(Clock::WireName(clock_wire), aliases,
	           Clock::Period(clock_wire), Clock::RisingEdge(clock_wire),
	           Clock::FallingEdge(clock_wire), Clock::PROPAGATED);
    }
#ifdef SDC_DEBUG
    log("Finish natural clock propagation\n\n");
#endif
}

std::vector<RTLIL::Wire*> NaturalPropagation::FindAliasWires(
    RTLIL::Wire* wire) {
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::vector<RTLIL::Wire*> alias_wires;
    pass_->extra_args(
        std::vector<std::string>{
            top_module->name.str() + "/w:" + wire->name.str(), "%a"},
        0, design_);
    for (auto module : design_->selected_modules()) {
	for (auto wire : module->selected_wires()) {
	    alias_wires.push_back(wire);
	}
    }
    return alias_wires;
}

void BufferPropagation::Run() {
#ifdef SDC_DEBUG
    log("Start buffer clock propagation\n");
    log("IBUF pass\n");
#endif
    PropagateThroughBuffers(IBuf());
#ifdef SDC_DEBUG
    log("BUFG pass\n");
#endif
    PropagateThroughBuffers(Bufg());
#ifdef SDC_DEBUG
    log("Finish buffer clock propagation\n\n");
#endif
}

void ClockDividerPropagation::Run() {
#ifdef SDC_DEBUG
    log("Start clock divider clock propagation\n");
#endif
    PropagateThroughClockDividers(Pll());
    PropagateThroughBuffers(Bufg());
#ifdef SDC_DEBUG
    log("Finish clock divider clock propagation\n\n");
#endif
}

void ClockDividerPropagation::PropagateThroughClockDividers(
    ClockDivider divider) {
    for (auto& clock : Clocks::GetClocks(design_)) {
	auto& clock_wire = clock.second;
#ifdef SDC_DEBUG
	log("Processing clock %s\n", Clock::WireName(clock_wire).c_str());
#endif
	PropagateClocksForCellType(clock_wire, divider.type);
    }
}

void ClockDividerPropagation::PropagateClocksForCellType(
    RTLIL::Wire* driver_wire, const std::string& cell_type) {
    if (cell_type == "PLLE2_ADV") {
	RTLIL::Cell* cell = NULL;
	for (auto input : Pll::inputs) {
	    cell = FindSinkCellOnPort(driver_wire, input);
	    if (cell and RTLIL::unescape_id(cell->type) == cell_type) {
		break;
	    }
	}
	if (!cell) {
	    return;
	}
	Pll pll(cell, Clock::Period(driver_wire),
	        Clock::RisingEdge(driver_wire));
	for (auto output : Pll::outputs) {
	    RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
	    // Don't add clocks on dangling wires
	    if (wire && WireHasSinkCell(wire)) {
		float clkout_period(pll.clkout_period.at(output));
		float clkout_rising_edge(pll.clkout_rising_edge.at(output));
		float clkout_falling_edge(pll.clkout_falling_edge.at(output));
		Clock::Add(wire, clkout_period, clkout_rising_edge,
		           clkout_falling_edge, Clock::GENERATED);
	    }
	}
    }
}

<commit_msg>SDC: Add TODO comment to remove dangling wire check<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2020  The Symbiflow Authors
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include "propagation.h"
#include <cassert>

USING_YOSYS_NAMESPACE

void Propagation::PropagateThroughBuffers(Buffer buffer) {
    for (auto& clock : Clocks::GetClocks(design_)) {
	auto& clock_wire = clock.second;
#ifdef SDC_DEBUG
	log("Clock wire %s\n", Clock::WireName(clock_wire).c_str());
#endif
	auto buf_wires =
	    FindSinkWiresForCellType(clock_wire, buffer.type, buffer.output);
	int path_delay(0);
	for (auto wire : buf_wires) {
#ifdef SDC_DEBUG
	    log("%s wire: %s\n", buffer.type.c_str(),
	        RTLIL::id2cstr(wire->name));
#endif
	    path_delay += buffer.delay;
	    Clock::Add(wire, Clock::Period(clock_wire),
	               Clock::RisingEdge(clock_wire) + path_delay,
	               Clock::FallingEdge(clock_wire) + path_delay, Clock::PROPAGATED);
	}
    }
}

std::vector<RTLIL::Wire*> Propagation::FindSinkWiresForCellType(
    RTLIL::Wire* driver_wire, const std::string& cell_type,
    const std::string& cell_port) {
    std::vector<RTLIL::Wire*> wires;
    if (!driver_wire) {
	return wires;
    }
    auto cell = FindSinkCellOfType(driver_wire, cell_type);
    RTLIL::Wire* wire = FindSinkWireOnPort(cell, cell_port);
    if (wire) {
	wires.push_back(wire);
	auto further_wires =
	    FindSinkWiresForCellType(wire, cell_type, cell_port);
	std::copy(further_wires.begin(), further_wires.end(),
	          std::back_inserter(wires));
    }
    return wires;
}

RTLIL::Cell* Propagation::FindSinkCellOfType(RTLIL::Wire* wire,
                                             const std::string& type) {
    RTLIL::Cell* sink_cell = NULL;
    if (!wire) {
	return sink_cell;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/w:" + wire->name.str();
    pass_->extra_args(std::vector<std::string>{base_selection, "%co:+" + type,
                                               base_selection, "%d"},
                      0, design_);
    auto selected_cells = top_module->selected_cells();
    // FIXME Handle more than one sink
    assert(selected_cells.size() <= 1);
    if (selected_cells.size() > 0) {
	sink_cell = selected_cells.at(0);
#ifdef SDC_DEBUG
	log("Found sink cell: %s\n",
	    RTLIL::unescape_id(sink_cell->name).c_str());
#endif
    }
    return sink_cell;
}

RTLIL::Cell* Propagation::FindSinkCellOnPort(RTLIL::Wire* wire,
                                             const std::string& port) {
    RTLIL::Cell* sink_cell = NULL;
    if (!wire) {
	return sink_cell;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/w:" + wire->name.str();
    pass_->extra_args(
        std::vector<std::string>{base_selection, "%co:+[" + port + "]",
                                 base_selection, "%d"},
        0, design_);
    auto selected_cells = top_module->selected_cells();
    // FIXME Handle more than one sink
    assert(selected_cells.size() <= 1);
    if (selected_cells.size() > 0) {
	sink_cell = selected_cells.at(0);
#ifdef SDC_DEBUG
	log("Found sink cell: %s\n",
	    RTLIL::unescape_id(sink_cell->name).c_str());
#endif
    }
    return sink_cell;
}

bool Propagation::WireHasSinkCell(RTLIL::Wire* wire) {
    if (!wire) {
	return false;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/w:" + wire->name.str();
    pass_->extra_args(
        std::vector<std::string>{base_selection, "%co:*",
                                 base_selection, "%d"},
        0, design_);
    auto selected_cells = top_module->selected_cells();
    return selected_cells.size() > 0;
}

RTLIL::Wire* Propagation::FindSinkWireOnPort(RTLIL::Cell* cell,
                                             const std::string& port_name) {
    RTLIL::Wire* sink_wire = NULL;
    if (!cell) {
	return sink_wire;
    }
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::string base_selection =
        top_module->name.str() + "/c:" + cell->name.str();
    pass_->extra_args(
        std::vector<std::string>{base_selection, "%co:+[" + port_name + "]",
                                 base_selection, "%d"},
        0, design_);
    auto selected_wires = top_module->selected_wires();
    // FIXME Handle more than one sink
    assert(selected_wires.size() <= 1);
    if (selected_wires.size() > 0) {
	sink_wire = selected_wires.at(0);
#ifdef SDC_DEBUG
	log("Found sink wire: %s\n",
	    RTLIL::unescape_id(sink_wire->name).c_str());
#endif
    }
    return sink_wire;
}

void NaturalPropagation::Run() {
#ifdef SDC_DEBUG
    log("Start natural clock propagation\n");
#endif
    for (auto& clock : Clocks::GetClocks(design_)) {
	auto& clock_wire = clock.second;
#ifdef SDC_DEBUG
	log("Processing clock %s\n", RTLIL::id2cstr(clock_wire->name));
#endif
	auto aliases = FindAliasWires(clock_wire);
	Clock::Add(Clock::WireName(clock_wire), aliases,
	           Clock::Period(clock_wire), Clock::RisingEdge(clock_wire),
	           Clock::FallingEdge(clock_wire), Clock::PROPAGATED);
    }
#ifdef SDC_DEBUG
    log("Finish natural clock propagation\n\n");
#endif
}

std::vector<RTLIL::Wire*> NaturalPropagation::FindAliasWires(
    RTLIL::Wire* wire) {
    RTLIL::Module* top_module = design_->top_module();
    assert(top_module);
    std::vector<RTLIL::Wire*> alias_wires;
    pass_->extra_args(
        std::vector<std::string>{
            top_module->name.str() + "/w:" + wire->name.str(), "%a"},
        0, design_);
    for (auto module : design_->selected_modules()) {
	for (auto wire : module->selected_wires()) {
	    alias_wires.push_back(wire);
	}
    }
    return alias_wires;
}

void BufferPropagation::Run() {
#ifdef SDC_DEBUG
    log("Start buffer clock propagation\n");
    log("IBUF pass\n");
#endif
    PropagateThroughBuffers(IBuf());
#ifdef SDC_DEBUG
    log("BUFG pass\n");
#endif
    PropagateThroughBuffers(Bufg());
#ifdef SDC_DEBUG
    log("Finish buffer clock propagation\n\n");
#endif
}

void ClockDividerPropagation::Run() {
#ifdef SDC_DEBUG
    log("Start clock divider clock propagation\n");
#endif
    PropagateThroughClockDividers(Pll());
    PropagateThroughBuffers(Bufg());
#ifdef SDC_DEBUG
    log("Finish clock divider clock propagation\n\n");
#endif
}

void ClockDividerPropagation::PropagateThroughClockDividers(
    ClockDivider divider) {
    for (auto& clock : Clocks::GetClocks(design_)) {
	auto& clock_wire = clock.second;
#ifdef SDC_DEBUG
	log("Processing clock %s\n", Clock::WireName(clock_wire).c_str());
#endif
	PropagateClocksForCellType(clock_wire, divider.type);
    }
}

void ClockDividerPropagation::PropagateClocksForCellType(
    RTLIL::Wire* driver_wire, const std::string& cell_type) {
    if (cell_type == "PLLE2_ADV") {
	RTLIL::Cell* cell = NULL;
	for (auto input : Pll::inputs) {
	    cell = FindSinkCellOnPort(driver_wire, input);
	    if (cell and RTLIL::unescape_id(cell->type) == cell_type) {
		break;
	    }
	}
	if (!cell) {
	    return;
	}
	Pll pll(cell, Clock::Period(driver_wire),
	        Clock::RisingEdge(driver_wire));
	for (auto output : Pll::outputs) {
	    RTLIL::Wire* wire = FindSinkWireOnPort(cell, output);
	    // Don't add clocks on dangling wires
	    // TODO Remove the workaround with the WireHasSinkCell check once the following issue is fixed:
	    // https://github.com/SymbiFlow/yosys-symbiflow-plugins/issues/59
	    if (wire && WireHasSinkCell(wire)) {
		float clkout_period(pll.clkout_period.at(output));
		float clkout_rising_edge(pll.clkout_rising_edge.at(output));
		float clkout_falling_edge(pll.clkout_falling_edge.at(output));
		Clock::Add(wire, clkout_period, clkout_rising_edge,
		           clkout_falling_edge, Clock::GENERATED);
	    }
	}
    }
}

<|endoftext|>