<commit_before>// @(#)root/thread:$Id$
// Authors: Enric Tejedor, Enrico Guiraud CERN  05/06/2018

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class ROOT::TTreeProcessorMT
    \ingroup Parallelism
    \brief A class to process the entries of a TTree in parallel.

By means of its Process method, ROOT::TTreeProcessorMT provides a way to process the
entries of a TTree in parallel. When invoking TTreeProcessor::Process, the user
passes a function whose only parameter is a TTreeReader. The function iterates
on a subrange of entries by using that TTreeReader.

The implementation of ROOT::TTreeProcessorMT parallelizes the processing of the subranges,
each corresponding to a cluster in the TTree. This is possible thanks to the use
of a ROOT::TThreadedObject, so that each thread works with its own TFile and TTree
objects.
*/

#include "TROOT.h"
#include "ROOT/TTreeProcessorMT.hxx"
#include "ROOT/TThreadExecutor.hxx"

using namespace ROOT;

namespace ROOT {
namespace Internal {
////////////////////////////////////////////////////////////////////////
/// Return a vector of cluster boundaries for the given tree and files.
ClustersAndEntries
MakeClusters(const std::string &treeName, const std::vector<std::string> &fileNames)
{
   // Note that as a side-effect of opening all files that are going to be used in the
   // analysis once, all necessary streamers will be loaded into memory.
   TDirectory::TContext c;
   std::vector<std::vector<EntryCluster>> clustersPerFile;
   std::vector<Long64_t> entriesPerFile;
   const auto nFileNames = fileNames.size();
   Long64_t offset = 0ll;
   for (auto i = 0u; i < nFileNames; ++i) {
      std::unique_ptr<TFile> f(TFile::Open(fileNames[i].c_str())); // need TFile::Open to load plugins if need be
      if (!f || f->IsZombie()) {
         Error("TTreeProcessorMT::Process",
               "An error occurred while opening file %s: skipping it.",
               fileNames[i].c_str());
         clustersPerFile.emplace_back(std::vector<EntryCluster>());
         entriesPerFile.emplace_back(0ULL);
         continue;
      }
      TTree *t = nullptr; // not a leak, t will be deleted by f
      f->GetObject(treeName.c_str(), t);

      if (!t) {
         Error("TTreeProcessorMT::Process",
               "An error occurred while getting tree %s from file %s: skipping this file.",
               treeName.c_str(), fileNames[i].c_str());
         clustersPerFile.emplace_back(std::vector<EntryCluster>());
         entriesPerFile.emplace_back(0ULL);
         continue;
      }

      auto clusterIter = t->GetClusterIterator(0);
      Long64_t start = 0ll, end = 0ll;
      const Long64_t entries = t->GetEntries();
      // Iterate over the clusters in the current file
      std::vector<EntryCluster> clusters;
      while ((start = clusterIter()) < entries) {
         end = clusterIter.GetNextEntry();
         // Add the current file's offset to start and end to make them (chain) global
         clusters.emplace_back(EntryCluster{start + offset, end + offset});
      }
      offset += entries;
      clustersPerFile.emplace_back(std::move(clusters));
      entriesPerFile.emplace_back(entries);
   }

   return std::make_pair(std::move(clustersPerFile), std::move(entriesPerFile));
}

////////////////////////////////////////////////////////////////////////
/// Return a vector containing the number of entries of each file of each friend TChain
std::vector<std::vector<Long64_t>> GetFriendEntries(const std::vector<std::pair<std::string, std::string>> &friendNames,
                                                    const std::vector<std::vector<std::string>> &friendFileNames)
{
   std::vector<std::vector<Long64_t>> friendEntries;
   const auto nFriends = friendNames.size();
   for (auto i = 0u; i < nFriends; ++i) {
      std::vector<Long64_t> nEntries;
      const auto &thisFriendName = friendNames[i].first;
      const auto &thisFriendFiles = friendFileNames[i];
      for (const auto &fname : thisFriendFiles) {
         std::unique_ptr<TFile> f(TFile::Open(fname.c_str()));
         TTree *t = nullptr; // owned by TFile
         f->GetObject(thisFriendName.c_str(), t);
         nEntries.emplace_back(t->GetEntries());
      }
      friendEntries.emplace_back(std::move(nEntries));
   }

   return friendEntries;
}
}
}

////////////////////////////////////////////////////////////////////////////////
/// Get and store the names, aliases and file names of the friends of the tree.
/// \param[in] tree The main tree whose friends to
///
/// Note that "friends of friends" and circular references in the lists of friends are not supported.
Internal::FriendInfo TTreeProcessorMT::GetFriendInfo(TTree &tree)
{
   std::vector<Internal::NameAlias> friendNames;
   std::vector<std::vector<std::string>> friendFileNames;

   const auto friends = tree.GetListOfFriends();
   if (!friends)
      return Internal::FriendInfo();

   for (auto fr : *friends) {
      const auto frTree = static_cast<TFriendElement *>(fr)->GetTree();

      // Check if friend tree has an alias
      const auto realName = frTree->GetName();
      const auto alias = tree.GetFriendAlias(frTree);
      if (alias) {
         friendNames.emplace_back(std::make_pair(realName, std::string(alias)));
      } else {
         friendNames.emplace_back(std::make_pair(realName, ""));
      }

      // Store the file names of the friend tree
      friendFileNames.emplace_back();
      auto &fileNames = friendFileNames.back();
      const bool isChain = tree.IsA() == TChain::Class();
      if (isChain) {
         const auto frChain = static_cast<TChain *>(frTree);
         for (auto f : *(frChain->GetListOfFiles())) {
            fileNames.emplace_back(f->GetTitle());
         }
      } else {
         const auto f = frTree->GetCurrentFile();
         if (!f)
            throw std::runtime_error("Friend trees with no associated file are not supported.");
         fileNames.emplace_back(f->GetName());
      }
   }

   return Internal::FriendInfo{std::move(friendNames), std::move(friendFileNames)};
}

////////////////////////////////////////////////////////////////////////////////
/// Retrieve the name of the first TTree in the first input file, else throw.
std::string TTreeProcessorMT::FindTreeName()
{
   std::string treeName;

   if (fFileNames.empty())
      throw std::runtime_error("Empty list of files and no tree name provided");

   ::TDirectory::TContext ctxt(gDirectory);
   std::unique_ptr<TFile> f(TFile::Open(fFileNames[0].c_str()));
   TIter next(f->GetListOfKeys());
   while (TKey *key = (TKey *)next()) {
      const char *className = key->GetClassName();
      if (strcmp(className, "TTree") == 0) {
         treeName = key->GetName();
         break;
      }
   }
   if (treeName.empty())
      throw std::runtime_error("Cannot find any tree in file " + fFileNames[0]);

   return treeName;
}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a file name.
/// \param[in] filename Name of the file containing the tree to process.
/// \param[in] treename Name of the tree to process. If not provided,
///                     the implementation will automatically search for a
///                     tree in the file.
TTreeProcessorMT::TTreeProcessorMT(std::string_view filename, std::string_view treename)
   : fFileNames({std::string(filename)}), fTreeName(treename.empty() ? FindTreeName() : treename), fFriendInfo() {}

std::vector<std::string> CheckAndConvert(const std::vector<std::string_view> & views)
{
   if (views.empty())
      throw std::runtime_error("The provided list of file names is empty");

   std::vector<std::string> strings;
   strings.reserve(views.size());
   for (const auto &v : views)
      strings.emplace_back(v);
   return strings;
}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a collection of file names.
/// \param[in] filenames Collection of the names of the files containing the tree to process.
/// \param[in] treename Name of the tree to process. If not provided,
///                     the implementation will automatically search for a
///                     tree in the collection of files.
TTreeProcessorMT::TTreeProcessorMT(const std::vector<std::string_view> &filenames, std::string_view treename)
   : fFileNames(CheckAndConvert(filenames)), fTreeName(treename.empty() ? FindTreeName() : treename), fFriendInfo() {}

std::vector<std::string> GetFilesFromTree(TTree &tree)
{
   std::vector<std::string> filenames;

   const bool isChain = tree.IsA() == TChain::Class();
   if (isChain) {
      TObjArray *filelist = static_cast<TChain &>(tree).GetListOfFiles();
      const auto nFiles = filelist->GetEntries();
      if (nFiles == 0)
         throw std::runtime_error("The provided chain of files is empty");
      filenames.reserve(nFiles);
      for (auto f : *filelist)
         filenames.emplace_back(f->GetTitle());
   } else {
      TFile *f = tree.GetCurrentFile();
      if (!f) {
         const auto msg = "The specified TTree is not linked to any file, in-memory-only trees are not supported.";
         throw std::runtime_error(msg);
      }

      filenames.emplace_back(f->GetName());
   }

   return filenames;
}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a TTree and a TEntryList.
/// \param[in] tree Tree or chain of files containing the tree to process.
/// \param[in] entries List of entry numbers to process.
TTreeProcessorMT::TTreeProcessorMT(TTree &tree, const TEntryList &entries)
   : fFileNames(GetFilesFromTree(tree)), fTreeName(tree.GetName()), fEntryList(entries),
     fFriendInfo(GetFriendInfo(tree)) {}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a TTree.
/// \param[in] tree Tree or chain of files containing the tree to process.
TTreeProcessorMT::TTreeProcessorMT(TTree &tree) : TTreeProcessorMT(tree, TEntryList()) {}

//////////////////////////////////////////////////////////////////////////////
/// Process the entries of a TTree in parallel. The user-provided function
/// receives a TTreeReader which can be used to iterate on a subrange of
/// entries
/// ~~~{.cpp}
/// TTreeProcessorMT::Process([](TTreeReader& readerSubRange) {
///                            // Select branches to read
///                            while (readerSubRange.next()) {
///                                // Use content of current entry
///                            }
///                         });
/// ~~~
/// The user needs to be aware that each of the subranges can potentially
/// be processed in parallel. This means that the code of the user function
/// should be thread safe.
///
/// \param[in] func User-defined function that processes a subrange of entries
void TTreeProcessorMT::Process(std::function<void(TTreeReader &)> func)
{
   const std::vector<Internal::NameAlias> &friendNames = fFriendInfo.fFriendNames;
   const std::vector<std::vector<std::string>> &friendFileNames = fFriendInfo.fFriendFileNames;

   // If an entry list or friend trees are present, we need to generate clusters with global entry numbers,
   // so we do it here for all files.
   const bool hasFriends = !friendNames.empty();
   const bool hasEntryList = fEntryList.GetN() > 0;
   const bool shouldRetrieveAllClusters = hasFriends || hasEntryList;
   const auto clustersAndEntries =
      shouldRetrieveAllClusters ? Internal::MakeClusters(fTreeName, fFileNames) : Internal::ClustersAndEntries{};
   const auto &clusters = clustersAndEntries.first;
   const auto &entries = clustersAndEntries.second;

   // Retrieve number of entries for each file for each friend tree
   const auto friendEntries =
      hasFriends ? Internal::GetFriendEntries(friendNames, friendFileNames) : std::vector<std::vector<Long64_t>>{};

   TThreadExecutor pool;
   // Parent task, spawns tasks that process each of the entry clusters for each input file
   using Internal::EntryCluster;
   auto processFile = [&](std::size_t fileIdx) {

      // If cluster information is already present, build TChains with all input files and use global entry numbers
      // Otherwise get cluster information only for the file we need to process and use local entry numbers
      const bool shouldUseGlobalEntries = hasFriends || hasEntryList;
      // theseFiles contains either all files or just the single file to process
      const auto &theseFiles = shouldUseGlobalEntries ? fFileNames : std::vector<std::string>({fFileNames[fileIdx]});
      // Evaluate clusters (with local entry numbers) and number of entries for this file, if needed
      const auto theseClustersAndEntries =
         shouldUseGlobalEntries ? Internal::ClustersAndEntries{} : Internal::MakeClusters(fTreeName, theseFiles);

      // All clusters for the file to process, either with global or local entry numbers
      const auto &thisFileClusters = shouldUseGlobalEntries ? clusters[fileIdx] : theseClustersAndEntries.first[0];

      // Either all number of entries or just the ones for this file
      const auto &theseEntries =
         shouldUseGlobalEntries ? entries : std::vector<Long64_t>({theseClustersAndEntries.second[0]});

      auto processCluster = [&](const Internal::EntryCluster &c) {
         // This task will operate with the tree that contains start
         treeView->PushTaskFirstEntry(c.start);

         std::unique_ptr<TTreeReader> reader;
         std::unique_ptr<TEntryList> elist;
         std::tie(reader, elist) = treeView->GetTreeReader(c.start, c.end, fTreeName, theseFiles, fFriendInfo,
                                                           fEntryList, theseEntries, friendEntries);
         func(*reader);

         // In case of task interleaving, we need to load here the tree of the parent task
         treeView->PopTaskFirstEntry();
      };

      pool.Foreach(processCluster, thisFileClusters);
   };

   std::vector<std::size_t> fileIdxs(fFileNames.size());
   std::iota(fileIdxs.begin(), fileIdxs.end(), 0u);

   // Enable this IMT use case (activate its locks)
   Internal::TParTreeProcessingRAII ptpRAII;

   pool.Foreach(processFile, fileIdxs);
}
<commit_msg>[TreePMT] Small performance improvement and code modernisation<commit_after>// @(#)root/thread:$Id$
// Authors: Enric Tejedor, Enrico Guiraud CERN  05/06/2018

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

/** \class ROOT::TTreeProcessorMT
    \ingroup Parallelism
    \brief A class to process the entries of a TTree in parallel.

By means of its Process method, ROOT::TTreeProcessorMT provides a way to process the
entries of a TTree in parallel. When invoking TTreeProcessor::Process, the user
passes a function whose only parameter is a TTreeReader. The function iterates
on a subrange of entries by using that TTreeReader.

The implementation of ROOT::TTreeProcessorMT parallelizes the processing of the subranges,
each corresponding to a cluster in the TTree. This is possible thanks to the use
of a ROOT::TThreadedObject, so that each thread works with its own TFile and TTree
objects.
*/

#include "TROOT.h"
#include "ROOT/TTreeProcessorMT.hxx"
#include "ROOT/TThreadExecutor.hxx"

using namespace ROOT;

namespace ROOT {
namespace Internal {
////////////////////////////////////////////////////////////////////////
/// Return a vector of cluster boundaries for the given tree and files.
ClustersAndEntries
MakeClusters(const std::string &treeName, const std::vector<std::string> &fileNames)
{
   // Note that as a side-effect of opening all files that are going to be used in the
   // analysis once, all necessary streamers will be loaded into memory.
   TDirectory::TContext c;
   const auto nFileNames = fileNames.size();
   std::vector<std::vector<EntryCluster>> clustersPerFile; clustersPerFile.reserve(nFileNames);
   std::vector<Long64_t> entriesPerFile; entriesPerFile.reserve(nFileNames);
   Long64_t offset = 0ll;
   for (const auto &fileName : fileNames) {
      auto fileNameC = fileName.c_str();
      std::unique_ptr<TFile> f(TFile::Open(fileNameC)); // need TFile::Open to load plugins if need be
      if (!f || f->IsZombie()) {
         Error("TTreeProcessorMT::Process",
               "An error occurred while opening file %s: skipping it.",
               fileNameC);
         clustersPerFile.emplace_back(std::vector<EntryCluster>());
         entriesPerFile.emplace_back(0ULL);
         continue;
      }
      TTree *t = nullptr; // not a leak, t will be deleted by f
      f->GetObject(treeName.c_str(), t);

      if (!t) {
         Error("TTreeProcessorMT::Process",
               "An error occurred while getting tree %s from file %s: skipping this file.",
               treeName.c_str(), fileNameC);
         clustersPerFile.emplace_back(std::vector<EntryCluster>());
         entriesPerFile.emplace_back(0ULL);
         continue;
      }

      auto clusterIter = t->GetClusterIterator(0);
      Long64_t start = 0ll, end = 0ll;
      const Long64_t entries = t->GetEntries();
      // Iterate over the clusters in the current file
      std::vector<EntryCluster> clusters;
      while ((start = clusterIter()) < entries) {
         end = clusterIter.GetNextEntry();
         // Add the current file's offset to start and end to make them (chain) global
         clusters.emplace_back(EntryCluster{start + offset, end + offset});
      }
      offset += entries;
      clustersPerFile.emplace_back(std::move(clusters));
      entriesPerFile.emplace_back(entries);
   }

   return std::make_pair(std::move(clustersPerFile), std::move(entriesPerFile));
}

////////////////////////////////////////////////////////////////////////
/// Return a vector containing the number of entries of each file of each friend TChain
std::vector<std::vector<Long64_t>> GetFriendEntries(const std::vector<std::pair<std::string, std::string>> &friendNames,
                                                    const std::vector<std::vector<std::string>> &friendFileNames)
{
   std::vector<std::vector<Long64_t>> friendEntries;
   const auto nFriends = friendNames.size();
   for (auto i = 0u; i < nFriends; ++i) {
      std::vector<Long64_t> nEntries;
      const auto &thisFriendName = friendNames[i].first;
      const auto &thisFriendFiles = friendFileNames[i];
      for (const auto &fname : thisFriendFiles) {
         std::unique_ptr<TFile> f(TFile::Open(fname.c_str()));
         TTree *t = nullptr; // owned by TFile
         f->GetObject(thisFriendName.c_str(), t);
         nEntries.emplace_back(t->GetEntries());
      }
      friendEntries.emplace_back(std::move(nEntries));
   }

   return friendEntries;
}
}
}

////////////////////////////////////////////////////////////////////////////////
/// Get and store the names, aliases and file names of the friends of the tree.
/// \param[in] tree The main tree whose friends to
///
/// Note that "friends of friends" and circular references in the lists of friends are not supported.
Internal::FriendInfo TTreeProcessorMT::GetFriendInfo(TTree &tree)
{
   std::vector<Internal::NameAlias> friendNames;
   std::vector<std::vector<std::string>> friendFileNames;

   const auto friends = tree.GetListOfFriends();
   if (!friends)
      return Internal::FriendInfo();

   for (auto fr : *friends) {
      const auto frTree = static_cast<TFriendElement *>(fr)->GetTree();

      // Check if friend tree has an alias
      const auto realName = frTree->GetName();
      const auto alias = tree.GetFriendAlias(frTree);
      if (alias) {
         friendNames.emplace_back(std::make_pair(realName, std::string(alias)));
      } else {
         friendNames.emplace_back(std::make_pair(realName, ""));
      }

      // Store the file names of the friend tree
      friendFileNames.emplace_back();
      auto &fileNames = friendFileNames.back();
      const bool isChain = tree.IsA() == TChain::Class();
      if (isChain) {
         const auto frChain = static_cast<TChain *>(frTree);
         for (auto f : *(frChain->GetListOfFiles())) {
            fileNames.emplace_back(f->GetTitle());
         }
      } else {
         const auto f = frTree->GetCurrentFile();
         if (!f)
            throw std::runtime_error("Friend trees with no associated file are not supported.");
         fileNames.emplace_back(f->GetName());
      }
   }

   return Internal::FriendInfo{std::move(friendNames), std::move(friendFileNames)};
}

////////////////////////////////////////////////////////////////////////////////
/// Retrieve the name of the first TTree in the first input file, else throw.
std::string TTreeProcessorMT::FindTreeName()
{
   std::string treeName;

   if (fFileNames.empty())
      throw std::runtime_error("Empty list of files and no tree name provided");

   ::TDirectory::TContext ctxt(gDirectory);
   std::unique_ptr<TFile> f(TFile::Open(fFileNames[0].c_str()));
   TIter next(f->GetListOfKeys());
   while (TKey *key = (TKey *)next()) {
      const char *className = key->GetClassName();
      if (strcmp(className, "TTree") == 0) {
         treeName = key->GetName();
         break;
      }
   }
   if (treeName.empty())
      throw std::runtime_error("Cannot find any tree in file " + fFileNames[0]);

   return treeName;
}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a file name.
/// \param[in] filename Name of the file containing the tree to process.
/// \param[in] treename Name of the tree to process. If not provided,
///                     the implementation will automatically search for a
///                     tree in the file.
TTreeProcessorMT::TTreeProcessorMT(std::string_view filename, std::string_view treename)
   : fFileNames({std::string(filename)}), fTreeName(treename.empty() ? FindTreeName() : treename), fFriendInfo() {}

std::vector<std::string> CheckAndConvert(const std::vector<std::string_view> & views)
{
   if (views.empty())
      throw std::runtime_error("The provided list of file names is empty");

   std::vector<std::string> strings;
   strings.reserve(views.size());
   for (const auto &v : views)
      strings.emplace_back(v);
   return strings;
}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a collection of file names.
/// \param[in] filenames Collection of the names of the files containing the tree to process.
/// \param[in] treename Name of the tree to process. If not provided,
///                     the implementation will automatically search for a
///                     tree in the collection of files.
TTreeProcessorMT::TTreeProcessorMT(const std::vector<std::string_view> &filenames, std::string_view treename)
   : fFileNames(CheckAndConvert(filenames)), fTreeName(treename.empty() ? FindTreeName() : treename), fFriendInfo() {}

std::vector<std::string> GetFilesFromTree(TTree &tree)
{
   std::vector<std::string> filenames;

   const bool isChain = tree.IsA() == TChain::Class();
   if (isChain) {
      TObjArray *filelist = static_cast<TChain &>(tree).GetListOfFiles();
      const auto nFiles = filelist->GetEntries();
      if (nFiles == 0)
         throw std::runtime_error("The provided chain of files is empty");
      filenames.reserve(nFiles);
      for (auto f : *filelist)
         filenames.emplace_back(f->GetTitle());
   } else {
      TFile *f = tree.GetCurrentFile();
      if (!f) {
         const auto msg = "The specified TTree is not linked to any file, in-memory-only trees are not supported.";
         throw std::runtime_error(msg);
      }

      filenames.emplace_back(f->GetName());
   }

   return filenames;
}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a TTree and a TEntryList.
/// \param[in] tree Tree or chain of files containing the tree to process.
/// \param[in] entries List of entry numbers to process.
TTreeProcessorMT::TTreeProcessorMT(TTree &tree, const TEntryList &entries)
   : fFileNames(GetFilesFromTree(tree)), fTreeName(tree.GetName()), fEntryList(entries),
     fFriendInfo(GetFriendInfo(tree)) {}

////////////////////////////////////////////////////////////////////////
/// Constructor based on a TTree.
/// \param[in] tree Tree or chain of files containing the tree to process.
TTreeProcessorMT::TTreeProcessorMT(TTree &tree) : TTreeProcessorMT(tree, TEntryList()) {}

//////////////////////////////////////////////////////////////////////////////
/// Process the entries of a TTree in parallel. The user-provided function
/// receives a TTreeReader which can be used to iterate on a subrange of
/// entries
/// ~~~{.cpp}
/// TTreeProcessorMT::Process([](TTreeReader& readerSubRange) {
///                            // Select branches to read
///                            while (readerSubRange.next()) {
///                                // Use content of current entry
///                            }
///                         });
/// ~~~
/// The user needs to be aware that each of the subranges can potentially
/// be processed in parallel. This means that the code of the user function
/// should be thread safe.
///
/// \param[in] func User-defined function that processes a subrange of entries
void TTreeProcessorMT::Process(std::function<void(TTreeReader &)> func)
{
   const std::vector<Internal::NameAlias> &friendNames = fFriendInfo.fFriendNames;
   const std::vector<std::vector<std::string>> &friendFileNames = fFriendInfo.fFriendFileNames;

   // If an entry list or friend trees are present, we need to generate clusters with global entry numbers,
   // so we do it here for all files.
   const bool hasFriends = !friendNames.empty();
   const bool hasEntryList = fEntryList.GetN() > 0;
   const bool shouldRetrieveAllClusters = hasFriends || hasEntryList;
   const auto clustersAndEntries =
      shouldRetrieveAllClusters ? Internal::MakeClusters(fTreeName, fFileNames) : Internal::ClustersAndEntries{};
   const auto &clusters = clustersAndEntries.first;
   const auto &entries = clustersAndEntries.second;

   // Retrieve number of entries for each file for each friend tree
   const auto friendEntries =
      hasFriends ? Internal::GetFriendEntries(friendNames, friendFileNames) : std::vector<std::vector<Long64_t>>{};

   TThreadExecutor pool;
   // Parent task, spawns tasks that process each of the entry clusters for each input file
   using Internal::EntryCluster;
   auto processFile = [&](std::size_t fileIdx) {

      // If cluster information is already present, build TChains with all input files and use global entry numbers
      // Otherwise get cluster information only for the file we need to process and use local entry numbers
      const bool shouldUseGlobalEntries = hasFriends || hasEntryList;
      // theseFiles contains either all files or just the single file to process
      const auto &theseFiles = shouldUseGlobalEntries ? fFileNames : std::vector<std::string>({fFileNames[fileIdx]});
      // Evaluate clusters (with local entry numbers) and number of entries for this file, if needed
      const auto theseClustersAndEntries =
         shouldUseGlobalEntries ? Internal::ClustersAndEntries{} : Internal::MakeClusters(fTreeName, theseFiles);

      // All clusters for the file to process, either with global or local entry numbers
      const auto &thisFileClusters = shouldUseGlobalEntries ? clusters[fileIdx] : theseClustersAndEntries.first[0];

      // Either all number of entries or just the ones for this file
      const auto &theseEntries =
         shouldUseGlobalEntries ? entries : std::vector<Long64_t>({theseClustersAndEntries.second[0]});

      auto processCluster = [&](const Internal::EntryCluster &c) {
         // This task will operate with the tree that contains start
         treeView->PushTaskFirstEntry(c.start);

         std::unique_ptr<TTreeReader> reader;
         std::unique_ptr<TEntryList> elist;
         std::tie(reader, elist) = treeView->GetTreeReader(c.start, c.end, fTreeName, theseFiles, fFriendInfo,
                                                           fEntryList, theseEntries, friendEntries);
         func(*reader);

         // In case of task interleaving, we need to load here the tree of the parent task
         treeView->PopTaskFirstEntry();
      };

      pool.Foreach(processCluster, thisFileClusters);
   };

   std::vector<std::size_t> fileIdxs(fFileNames.size());
   std::iota(fileIdxs.begin(), fileIdxs.end(), 0u);

   // Enable this IMT use case (activate its locks)
   Internal::TParTreeProcessingRAII ptpRAII;

   pool.Foreach(processFile, fileIdxs);
}
<|endoftext|>


<commit_before>void tofanal (Int_t evNumber=0)
{
/////////////////////////////////////////////////////////////////////////
//   This macro is a small example of a ROOT macro
//   illustrating how to read the output of GALICE
//   and fill some histograms.
//
//     Root > .L anal.C   //this loads the macro in memory
//     Root > anal();     //by default process first event
//     Root > anal(2);    //process third event
//Begin_Html
/*

*/
//End_Html
/////////////////////////////////////////////////////////////////////////

// Dynamically link some shared libs
   if (gClassTable->GetID("AliRun") < 0) {
     gROOT->LoadMacro("loadlibs.C");
     loadlibs();
   } else {
      delete gAlice;
      gAlice = 0;
   }

// Connect the Root Galice file containing Geometry, Kine and Hits
   TFile *file = (TFile*)gROOT->GetListOfFiles()->FindObject("galice.root");
   if (!file) file = new TFile("galice.root");

// Get AliRun object from file or create it if not on file
   if (!gAlice) {
      gAlice = (AliRun*)file->Get("gAlice");
      if (gAlice) printf("AliRun object found on file\n");
      if (!gAlice) gAlice = new AliRun("gAlice","TOF test program");
   }

// Import the Kine and Hits Trees for the event evNumber in the file
   gAlice->GetEvent(evNumber);
   Float_t x,y,z,mass,e;
   Int_t nbytes = 0;
   Int_t j,hit,ipart;
   Int_t nhits;
   Float_t tof;
   TParticle *particle;

// Get pointers to Alice detectors and Hits containers
   AliDetector *TOF  = gAlice->GetDetector("TOF");
   TClonesArray *Particles = gAlice->Particles();

   Int_t ntracks    = gAlice->TreeH()->GetEntries();

   // Create histograms
   TH1F *hTOF = new TH1F("TOF","Time-of-flight distribution",100,0,10e-8);
   TH1F *hTOFprim = new TH1F("TOFprim","Time-of-flight distribution of primaries",100,0,10e-8);
// Start loop on tracks in the hits containers
   for (Int_t track=0; track<ntracks;track++) {
     if(TOF) {
     // ======>Histogram TOF
       for(AliTOFhit* tofHit=(AliTOFhit*)TOF->FirstHit(track); tofHit; tofHit=(AliTOFhit*)TOF->NextHit()) {
         tof = tofHit->fTof;
         hTOF->Fill(tof);
         ipart    = tofHit->fTrack;
         particle = (TParticle*)Particles->UncheckedAt(ipart);
         if (particle->GetFirstMother() < 0) hTOFprim->Fill(tof);
       }
     }
   }

//Create a canvas, set the view range, show histograms
   TCanvas *c1 = new TCanvas("c1","Alice TOF hits",400,10,600,700);
   c1->Divide(1,2);
   c1->cd(1);
   gPad->SetFillColor(33);
   hTOF->SetFillColor(42);
   hTOF->Draw();
   //   hSectors->Fit("pol1");
   c1->cd(2);
   gPad->SetFillColor(33);
   hTOFprim->SetFillColor(42);
   hTOFprim->Draw();
   //   hTOFprim->Draw("same");
   c1->Print("tofanal.ps");
   delete c1;
   c1 = 0;
}
<commit_msg>TOF example macro updated<commit_after>void tofanal (Int_t evNumber=0)
{
/////////////////////////////////////////////////////////////////////////
//   This macro is a small example of a ROOT macro
//   illustrating how to read the output of GALICE
//   and fill some histograms.
//
//     Root > .L anal.C   //this loads the macro in memory
//     Root > anal();     //by default process first event
//     Root > anal(2);    //process third event
//Begin_Html
/*

*/
//End_Html
/////////////////////////////////////////////////////////////////////////

// Dynamically link some shared libs
   if (gClassTable->GetID("AliRun") < 0) {
     gROOT->LoadMacro("loadlibs.C");
     loadlibs();
   } else {
      delete gAlice;
      gAlice = 0;
   }

// Connect the Root Galice file containing Geometry, Kine and Hits
   TFile *file = (TFile*)gROOT->GetListOfFiles()->FindObject("galice.root");
   if (!file) file = new TFile("galice.root");

// Get AliRun object from file or create it if not on file
   if (!gAlice) {
      gAlice = (AliRun*)file->Get("gAlice");
      if (gAlice) printf("AliRun object found on file\n");
      if (!gAlice) gAlice = new AliRun("gAlice","TOF test program");
   }

// Import the Kine and Hits Trees for the event evNumber in the file
   gAlice->GetEvent(evNumber);
   Float_t x,y,z,mass,e;
   Int_t nbytes = 0;
   Int_t j,hit,ipart;
   Int_t nhits;
   Float_t tof;
   TParticle *particle;

// Get pointers to Alice detectors and Hits containers
   AliDetector *TOF  = gAlice->GetDetector("TOF");

   Int_t ntracks    = gAlice->TreeH()->GetEntries();

   // Create histograms
   TH1F *hTOF = new TH1F("TOF","Time-of-flight distribution",100,0,10e-8);
   TH1F *hTOFprim = new TH1F("TOFprim","Time-of-flight distribution of primaries",100,0,10e-8);
// Start loop on tracks in the hits containers
   for (Int_t track=0; track<ntracks;track++) {
     if(TOF) {
     // ======>Histogram TOF
       for(AliTOFhit* tofHit=(AliTOFhit*)TOF->FirstHit(track); tofHit; tofHit=(AliTOFhit*)TOF->NextHit()) {
         tof = tofHit->GetTof();
         hTOF->Fill(tof);
         ipart    = tofHit->GetTrack();
         particle = (TParticle*)gAlice->Particle(ipart);
         if (particle->GetFirstMother() < 0) hTOFprim->Fill(tof);
       }
     }
   }

//Create a canvas, set the view range, show histograms
   TCanvas *c1 = new TCanvas("c1","Alice TOF hits",400,10,600,700);
   c1->Divide(1,2);
   c1->cd(1);
   gPad->SetFillColor(33);
   hTOF->SetFillColor(42);
   hTOF->Draw();
   //   hSectors->Fit("pol1");
   c1->cd(2);
   gPad->SetFillColor(33);
   hTOFprim->SetFillColor(42);
   hTOFprim->Draw();
   //   hTOFprim->Draw("same");
   c1->Print("tofanal.ps");
   delete c1;
   c1 = 0;
}
<|endoftext|>


<commit_before>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 *
 * adios2_f2c_adios.cpp
 *
 *  Created on: Nov 8, 2017
 *      Author: William F Godoy godoywf@ornl.gov
 */

#include "adios2_f2c_adios.h"

#include <stdexcept>

#ifdef ADIOS2_HAVE_MPI_F
void FC_GLOBAL(adios2_init_f2c,
               ADIOS2_INIT_F2C)(adios2_ADIOS **adios, MPI_Fint *comm,
                                const int *debug_mode, int *ierr)
{
    FC_GLOBAL(adios2_init_config_f2c, ADIOS2_INIT_CONFIG_F2C)
    (adios, "", comm, debug_mode, ierr);
}

void FC_GLOBAL(adios2_init_config_f2c,
               ADIOS2_INIT_CONFIG_F2C)(adios2_ADIOS **adios,
                                       const char *config_file, MPI_Fint *comm,
                                       const int *debug_mode, int *ierr)
{
    *ierr = 0;
    try
    {
        *adios = adios2_init_config_glue(
            config_file, MPI_Comm_f2c(*comm),
            static_cast<adios2_debug_mode>(*debug_mode), "Fortran");
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}
#else
void FC_GLOBAL(adios2_init_f2c, ADIOS2_INIT_F2C)(adios2_ADIOS **adios,
                                                 const int *debug_mode,
                                                 int *ierr)
{
    adios2_init_config_f2c_(adios, "", debug_mode, ierr);
}

void FC_GLOBAL(adios2_init_config_f2c,
               ADIOS2_INIT_CONFIG_F2C)(adios2_ADIOS **adios,
                                       const char *config_file,
                                       const int *debug_mode, int *ierr)
{
    *ierr = 0;
    try
    {
        *adios = adios2_init_config_nompi_glue(
            config_file, static_cast<adios2_debug_mode>(*debug_mode),
            "Fortran");
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}
#endif

void FC_GLOBAL(adios2_declare_io_f2c,
               ADIOS2_DECLARE_IO_F2C)(adios2_IO **io, adios2_ADIOS **adios,
                                      const char *io_name, int *ierr)
{
    *ierr = 0;

    try
    {
        *io = adios2_declare_io(*adios, io_name);
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}

void FC_GLOBAL(adios2_finalize_f2c, ADIOS2_FINALIZE_F2C)(adios2_ADIOS **adios,
                                                         int *ierr)
{
    *ierr = 0;
    try
    {
        adios2_finalize(*adios);
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}
<commit_msg>Fix for issue #295<commit_after>/*
 * Distributed under the OSI-approved Apache License, Version 2.0.  See
 * accompanying file Copyright.txt for details.
 *
 * adios2_f2c_adios.cpp
 *
 *  Created on: Nov 8, 2017
 *      Author: William F Godoy godoywf@ornl.gov
 */

#include "adios2_f2c_adios.h"

#include <stdexcept>

#ifdef ADIOS2_HAVE_MPI_F
void FC_GLOBAL(adios2_init_f2c,
               ADIOS2_INIT_F2C)(adios2_ADIOS **adios, MPI_Fint *comm,
                                const int *debug_mode, int *ierr)
{
    FC_GLOBAL(adios2_init_config_f2c, ADIOS2_INIT_CONFIG_F2C)
    (adios, "", comm, debug_mode, ierr);
}

void FC_GLOBAL(adios2_init_config_f2c,
               ADIOS2_INIT_CONFIG_F2C)(adios2_ADIOS **adios,
                                       const char *config_file, MPI_Fint *comm,
                                       const int *debug_mode, int *ierr)
{
    *ierr = 0;
    try
    {
        *adios = adios2_init_config_glue(
            config_file, MPI_Comm_f2c(*comm),
            static_cast<adios2_debug_mode>(*debug_mode), "Fortran");
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}
#else
void FC_GLOBAL(adios2_init_f2c, ADIOS2_INIT_F2C)(adios2_ADIOS **adios,
                                                 const int *debug_mode,
                                                 int *ierr)
{
    FC_GLOBAL(adios2_init_config_f2c, ADIOS2_INIT_CONFIG_F2C)
    (adios, "", debug_mode, ierr);
}

void FC_GLOBAL(adios2_init_config_f2c,
               ADIOS2_INIT_CONFIG_F2C)(adios2_ADIOS **adios,
                                       const char *config_file,
                                       const int *debug_mode, int *ierr)
{
    *ierr = 0;
    try
    {
        *adios = adios2_init_config_nompi_glue(
            config_file, static_cast<adios2_debug_mode>(*debug_mode),
            "Fortran");
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}
#endif

void FC_GLOBAL(adios2_declare_io_f2c,
               ADIOS2_DECLARE_IO_F2C)(adios2_IO **io, adios2_ADIOS **adios,
                                      const char *io_name, int *ierr)
{
    *ierr = 0;

    try
    {
        *io = adios2_declare_io(*adios, io_name);
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}

void FC_GLOBAL(adios2_finalize_f2c, ADIOS2_FINALIZE_F2C)(adios2_ADIOS **adios,
                                                         int *ierr)
{
    *ierr = 0;
    try
    {
        adios2_finalize(*adios);
    }
    catch (std::exception &e)
    {
        *ierr = 1;
    }
}
<|endoftext|>


<commit_before>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2015 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * 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.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** 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 HOLDER 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 "EnvisionAstConsumer.h"

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QStringList>

#include <clang/AST/DeclCXX.h>

#include "EnvisionPPCallbacks.h"
#include "TypeUtilities.h"

EnvisionAstConsumer::EnvisionAstConsumer(clang::CompilerInstance& ci, QString currentFile, APIData& outData)
	: compilerInstance_{ci}, outData_{outData}
{
	currentFile.replace(".cpp", ".h");
	currentFile_ = currentFile.toStdString();
	currentClassName_ = currentFile.split(QDir::separator()).last();
	currentClassName_ = currentClassName_.split(".").first();
}

void EnvisionAstConsumer::Initialize(clang::ASTContext&)
{
	compilerInstance_.getPreprocessor().addPPCallbacks(
				std::make_unique<EnvisionPPCallbacks>(compilerInstance_.getSourceManager(),
																  currentFile_, attributes_, privateAttributes_));
	compilerInstance_.getPreprocessor().enableIncrementalProcessing();
}

void EnvisionAstConsumer::HandleTagDeclDefinition(clang::TagDecl* tagDecl)
{
	// We only care about stuff in the header file of the current translation unit source file.
	auto &sourceManager = compilerInstance_.getSourceManager();
	auto sourceLocation = tagDecl->getSourceRange().getBegin();
	if (sourceManager.getFilename(sourceLocation) != currentFile_) return;

	if (auto classDecl = llvm::dyn_cast<clang::CXXRecordDecl>(tagDecl))
		HandleClassDecl(classDecl);
	else if (auto enumDecl = llvm::dyn_cast<clang::EnumDecl>(tagDecl))
		HandleEnumDecl(enumDecl);
	else
		qDebug() << "Uknown Decl";
}

void EnvisionAstConsumer::HandleEnumDecl(clang::EnumDecl* enumDecl)
{
	QString enumName = QString::fromStdString(enumDecl->getNameAsString());
	QString fullEnumName = enumName;
	auto context = enumDecl->getDeclContext();
	if (auto recordDecl = llvm::dyn_cast<clang::CXXRecordDecl>(context))
	{
		QString recordName = QString::fromStdString(recordDecl->getNameAsString());
		fullEnumName.prepend("::").prepend(recordName);

		auto recordContext = recordDecl->getDeclContext();
		if (auto namespaceDecl = llvm::dyn_cast<clang::NamespaceDecl>(recordContext))
		{
			QString namespaceName = QString::fromStdString(namespaceDecl->getNameAsString());
			fullEnumName.prepend("::").prepend(namespaceName);
		}
	}
	EnumData eData(enumName);
	eData.qualifiedName_ = fullEnumName;

	for (auto enumConstant : enumDecl->enumerators())
	{
		QString constantName = QString::fromStdString(enumConstant->getNameAsString());
		eData.values_.append({constantName, QString("%1::%2").arg(eData.qualifiedName_, constantName)});
	}

	processedEnums_ << eData;
}

void EnvisionAstConsumer::HandleClassDecl(clang::CXXRecordDecl* classDecl)
{
	auto context = classDecl->getDeclContext();
	if (context->isNamespace())
	{
		auto namespaceDecl = llvm::dyn_cast<clang::NamespaceDecl>(context);
		auto namespaceName = QString::fromStdString(namespaceDecl->getNameAsString());
		auto className = QString::fromStdString(classDecl->getNameAsString());
		if (namespaceName == outData_.namespaceName_ && className == currentClassName_)
		{
			QStringList bases = baseClasses(classDecl);
			if (!allowedBases_.contains(bases[0])) return; // We only consider classes which have a base that we allow.

			ClassData cData(className);
			cData.qualifiedName_ = QString("%1::%2").arg(namespaceName, className);
			for (auto base : classDecl->bases())
			{
				auto baseString = TypeUtilities::typePtrToString(base.getType().getTypePtr());
				if (!baseString.isEmpty())
				{
					if (!baseString.contains("::"))
						cData.baseClasses_ << QString("%1::%2").arg(namespaceName, baseString);
					else
						cData.baseClasses_ << baseString;
				}
			}
			QSet<QString> seenMethods;
			QSet<QString> possibleAttributeSetters;
			for (auto method : classDecl->methods())
			{
				QString methodName = QString::fromStdString(method->getNameAsString());
				if (seenMethods.contains(methodName)) continue;

				auto it = attributes_.find(methodName);
				if (it != attributes_.end())
				{
					cData.attributes_.append(attribute(it.key(), it.value(), cData.qualifiedName_, method));
					seenMethods << it.key() << it.value();
				}
				else
				{
					// ignore private attributes:
					auto privateIt = privateAttributes_.find(methodName);
					if (privateIt != privateAttributes_.end())
					{
						seenMethods << methodName << privateIt.value();
					}
					else if (methodName.startsWith("set"))
					{
						possibleAttributeSetters << methodName;
					}
				}
			}
			// check if we have some more attributes which don't have an attribute macro:
			for (auto method : classDecl->methods())
			{
				QString methodName = QString::fromStdString(method->getNameAsString());
				if (seenMethods.contains(methodName)) continue;

				if (possibleAttributeSetters.contains(methodName)) continue;
				for (QString setterName : possibleAttributeSetters)
				{
					QString possibleGetterName = setterName.mid(4); // drop set and first letter
					possibleGetterName.prepend(setterName[3].toLower());
					if (methodName == possibleGetterName)
					{
						// Found another attribute:
						cData.attributes_.append(attribute(methodName, setterName, cData.qualifiedName_, method));
						seenMethods << setterName << methodName;
						break;
					}
				}
			}
			// Add enums
			cData.enums_ << processedEnums_;
			processedEnums_.clear();
			// Add class to api structure
			outData_.addIncludeFile(QString::fromStdString(currentFile_));
			outData_.insertClassData(cData, bases);
		}
	}
}

ClassAttribute EnvisionAstConsumer::attribute(const QString& attributeName, const QString& attributeSetterName,
													const QString& qualifiedClassName, const clang::CXXMethodDecl* method)
{
	QString getter = QString("&%1::%2").arg(qualifiedClassName, attributeName);
	auto returnTypePtr = method->getReturnType().getTypePtr();
	if (returnTypePtr->isReferenceType())
	{
		auto refType = llvm::dyn_cast<clang::ReferenceType>(returnTypePtr);
		auto pointeeType = refType->getPointeeType();
		if (pointeeType.isConstQualified())
		{
			QString pointeeTypeString = TypeUtilities::typePtrToString(pointeeType.getTypePtr());
			QString newGetter = QString("make_function((const %1& (%2::*)())%3,"
												 " return_value_policy<copy_const_reference>())")
					.arg(pointeeTypeString, qualifiedClassName, getter);
			getter = newGetter;
		}
	}
	else if (returnTypePtr->isPointerType())
	{
		getter = QString("make_function(%1, return_internal_reference<>())").arg(getter);
	}
	QString setter = QString("&%1::%2").arg(qualifiedClassName, attributeSetterName);
	return {attributeName, getter, setter};
}

QStringList EnvisionAstConsumer::baseClasses(clang::CXXRecordDecl* classDecl)
{
	QStringList result {};
	for (auto base : classDecl->bases())
	{
		result << baseClasses(base.getType()->getAsCXXRecordDecl());
	}
	result << QString::fromStdString(classDecl->getQualifiedNameAsString());
	result.removeAll("Core::Reflect");
	return result;
}
<commit_msg>Don't wrap private stuff<commit_after>/***********************************************************************************************************************
**
** Copyright (c) 2011, 2015 ETH Zurich
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
** following conditions are met:
**
**    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
**      disclaimer.
**    * 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.
**    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
**      derived from this software without specific prior written permission.
**
**
** 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 HOLDER 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 "EnvisionAstConsumer.h"

#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QStringList>

#include <clang/AST/DeclCXX.h>

#include "EnvisionPPCallbacks.h"
#include "TypeUtilities.h"

EnvisionAstConsumer::EnvisionAstConsumer(clang::CompilerInstance& ci, QString currentFile, APIData& outData)
	: compilerInstance_{ci}, outData_{outData}
{
	currentFile.replace(".cpp", ".h");
	currentFile_ = currentFile.toStdString();
	currentClassName_ = currentFile.split(QDir::separator()).last();
	currentClassName_ = currentClassName_.split(".").first();
}

void EnvisionAstConsumer::Initialize(clang::ASTContext&)
{
	compilerInstance_.getPreprocessor().addPPCallbacks(
				std::make_unique<EnvisionPPCallbacks>(compilerInstance_.getSourceManager(),
																  currentFile_, attributes_, privateAttributes_));
	compilerInstance_.getPreprocessor().enableIncrementalProcessing();
}

void EnvisionAstConsumer::HandleTagDeclDefinition(clang::TagDecl* tagDecl)
{
	// We only care about stuff in the header file of the current translation unit source file.
	auto &sourceManager = compilerInstance_.getSourceManager();
	auto sourceLocation = tagDecl->getSourceRange().getBegin();
	if (sourceManager.getFilename(sourceLocation) != currentFile_) return;

	if (auto classDecl = llvm::dyn_cast<clang::CXXRecordDecl>(tagDecl))
		HandleClassDecl(classDecl);
	else if (auto enumDecl = llvm::dyn_cast<clang::EnumDecl>(tagDecl))
		HandleEnumDecl(enumDecl);
	else
		qDebug() << "Uknown Decl";
}

void EnvisionAstConsumer::HandleEnumDecl(clang::EnumDecl* enumDecl)
{
	// Ignore private enums:
	if (enumDecl->getAccess() == clang::AccessSpecifier::AS_private) return;

	QString enumName = QString::fromStdString(enumDecl->getNameAsString());
	QString fullEnumName = enumName;
	auto context = enumDecl->getDeclContext();
	if (auto recordDecl = llvm::dyn_cast<clang::CXXRecordDecl>(context))
	{
		QString recordName = QString::fromStdString(recordDecl->getNameAsString());
		fullEnumName.prepend("::").prepend(recordName);

		auto recordContext = recordDecl->getDeclContext();
		if (auto namespaceDecl = llvm::dyn_cast<clang::NamespaceDecl>(recordContext))
		{
			QString namespaceName = QString::fromStdString(namespaceDecl->getNameAsString());
			fullEnumName.prepend("::").prepend(namespaceName);
		}
	}
	EnumData eData(enumName);
	eData.qualifiedName_ = fullEnumName;

	for (auto enumConstant : enumDecl->enumerators())
	{
		QString constantName = QString::fromStdString(enumConstant->getNameAsString());
		eData.values_.append({constantName, QString("%1::%2").arg(eData.qualifiedName_, constantName)});
	}

	processedEnums_ << eData;
}

void EnvisionAstConsumer::HandleClassDecl(clang::CXXRecordDecl* classDecl)
{
	auto context = classDecl->getDeclContext();
	if (context->isNamespace())
	{
		auto namespaceDecl = llvm::dyn_cast<clang::NamespaceDecl>(context);
		auto namespaceName = QString::fromStdString(namespaceDecl->getNameAsString());
		auto className = QString::fromStdString(classDecl->getNameAsString());
		if (namespaceName == outData_.namespaceName_ && className == currentClassName_)
		{
			QStringList bases = baseClasses(classDecl);
			if (!allowedBases_.contains(bases[0])) return; // We only consider classes which have a base that we allow.

			ClassData cData(className);
			cData.qualifiedName_ = QString("%1::%2").arg(namespaceName, className);
			for (auto base : classDecl->bases())
			{
				auto baseString = TypeUtilities::typePtrToString(base.getType().getTypePtr());
				if (!baseString.isEmpty())
				{
					if (!baseString.contains("::"))
						cData.baseClasses_ << QString("%1::%2").arg(namespaceName, baseString);
					else
						cData.baseClasses_ << baseString;
				}
			}
			QSet<QString> seenMethods;
			QSet<QString> possibleAttributeSetters;
			for (auto method : classDecl->methods())
			{
				QString methodName = QString::fromStdString(method->getNameAsString());
				if (seenMethods.contains(methodName)) continue;

				auto it = attributes_.find(methodName);
				if (it != attributes_.end())
				{
					cData.attributes_.append(attribute(it.key(), it.value(), cData.qualifiedName_, method));
					seenMethods << it.key() << it.value();
				}
				else
				{
					// ignore private attributes:
					auto privateIt = privateAttributes_.find(methodName);
					if (privateIt != privateAttributes_.end())
					{
						seenMethods << methodName << privateIt.value();
					}
					else if (methodName.startsWith("set"))
					{
						possibleAttributeSetters << methodName;
					}
				}
			}
			// check if we have some more attributes which don't have an attribute macro:
			for (auto method : classDecl->methods())
			{
				// Ignore private methods:
				if (method->getAccess() == clang::AccessSpecifier::AS_private) continue;

				QString methodName = QString::fromStdString(method->getNameAsString());
				if (seenMethods.contains(methodName)) continue;

				if (possibleAttributeSetters.contains(methodName)) continue;
				for (QString setterName : possibleAttributeSetters)
				{
					QString possibleGetterName = setterName.mid(4); // drop set and first letter
					possibleGetterName.prepend(setterName[3].toLower());
					if (methodName == possibleGetterName)
					{
						// Found another attribute:
						cData.attributes_.append(attribute(methodName, setterName, cData.qualifiedName_, method));
						seenMethods << setterName << methodName;
						break;
					}
				}
			}
			// Add enums
			cData.enums_ << processedEnums_;
			processedEnums_.clear();
			// Add class to api structure
			outData_.addIncludeFile(QString::fromStdString(currentFile_));
			outData_.insertClassData(cData, bases);
		}
	}
}

ClassAttribute EnvisionAstConsumer::attribute(const QString& attributeName, const QString& attributeSetterName,
													const QString& qualifiedClassName, const clang::CXXMethodDecl* method)
{
	QString getter = QString("&%1::%2").arg(qualifiedClassName, attributeName);
	auto returnTypePtr = method->getReturnType().getTypePtr();
	if (returnTypePtr->isReferenceType())
	{
		auto refType = llvm::dyn_cast<clang::ReferenceType>(returnTypePtr);
		auto pointeeType = refType->getPointeeType();
		if (pointeeType.isConstQualified())
		{
			QString pointeeTypeString = TypeUtilities::typePtrToString(pointeeType.getTypePtr());
			QString newGetter = QString("make_function((const %1& (%2::*)())%3,"
												 " return_value_policy<copy_const_reference>())")
					.arg(pointeeTypeString, qualifiedClassName, getter);
			getter = newGetter;
		}
	}
	else if (returnTypePtr->isPointerType())
	{
		getter = QString("make_function(%1, return_internal_reference<>())").arg(getter);
	}
	QString setter = QString("&%1::%2").arg(qualifiedClassName, attributeSetterName);
	return {attributeName, getter, setter};
}

QStringList EnvisionAstConsumer::baseClasses(clang::CXXRecordDecl* classDecl)
{
	QStringList result {};
	for (auto base : classDecl->bases())
	{
		result << baseClasses(base.getType()->getAsCXXRecordDecl());
	}
	result << QString::fromStdString(classDecl->getQualifiedNameAsString());
	result.removeAll("Core::Reflect");
	return result;
}
<|endoftext|>


<commit_before>#ifdef USE_WEBSOCKETS
#include <QWebSocket>
#include <QWebSocketServer>

#include "deconz/dbg_trace.h"
#include "websocket_server.h"

#define MAX_ATTEMPS 50
#define WS_PORT 20877

/*! Constructor.
 */
WebSocketServer::WebSocketServer(QObject *parent) :
    QObject(parent)
{
    srv = new QWebSocketServer("deconz", QWebSocketServer::NonSecureMode, this);

    quint16 p = WS_PORT;

    while (!srv->listen(QHostAddress::AnyIPv4, p))
    {
        if (p == 0)
        {
            DBG_Printf(DBG_ERROR, "giveup starting websocket server on port %u. error: %s\n", qPrintable(srv->errorString()));
            break;
        }

        DBG_Printf(DBG_ERROR, "failed to start websocket server on port %u. error: %s\n", qPrintable(srv->errorString()));

        if (p < (WS_PORT  + MAX_ATTEMPS))
        { p++; }
        else
        { p = 0; }
    }

    if (srv->isListening())
    {
        DBG_Printf(DBG_INFO, "started websocket server at port %u\n", srv->serverPort());
        connect(srv, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
    }
}

/*! Returns the websocket server port.
    \return the active server port, or 0 if not active
 */
quint16 WebSocketServer::port() const
{
    return srv->isListening() ? srv->serverPort() : 0;
}

/*! Handler for new client connections.
 */
void WebSocketServer::onNewConnection()
{
    while (srv->hasPendingConnections())
    {
        QWebSocket *sock = srv->nextPendingConnection();
        clients.push_back(sock);
    }
}

/*! Broadcasts a message to all connected clients.
    \param msg the message as JSON string
 */
void WebSocketServer::broadcastTextMessage(const QString &msg)
{
    for (size_t i = 0; i < clients.size(); i++)
    {
        QWebSocket *sock = clients[i];

        if (sock->state() == QAbstractSocket::ConnectedState)
        {
            sock->sendTextMessage(msg);
        }
        else
        {
            DBG_Printf(DBG_INFO, "Remove websocket %s:%u\n", qPrintable(sock->peerAddress().toString()), sock->peerPort());
            sock->deleteLater();
            clients[i] = clients.back();
            clients.pop_back();
        }
    }
}
#else // no websockets
  WebSocketServer::WebSocketServer(QObject *parent) :
      QObject(parent)
  { }
  void WebSocketServer::onNewConnection() { }
  void WebSocketServer::broadcastTextMessage(const QString &) { }
  quint16 WebSocketServer::port() const {  return 0; }
#endif
<commit_msg>Try to use proxy friendly ports for websocket server, use random port as fallback<commit_after>#ifdef USE_WEBSOCKETS
#include <QWebSocket>
#include <QWebSocketServer>

#include "deconz/dbg_trace.h"
#include "websocket_server.h"

/*! Constructor.
 */
WebSocketServer::WebSocketServer(QObject *parent) :
    QObject(parent)
{
    srv = new QWebSocketServer("deconz", QWebSocketServer::NonSecureMode, this);

    quint16 p = 0;
    quint16 ports[] = { 443, 8080, 8088, 20877, 0 }; // start with proxy frinedly ports first, use random port as fallback

    while (!srv->listen(QHostAddress::AnyIPv4, ports[p]))
    {
        if (ports[p] == 0)
        {
            DBG_Printf(DBG_ERROR, "giveup starting websocket server on port %u. error: %s\n", ports[p], qPrintable(srv->errorString()));
            break;
        }

        DBG_Printf(DBG_ERROR, "failed to start websocket server on port %u. error: %s\n", ports[p], qPrintable(srv->errorString()));
        p++;
    }

    if (srv->isListening())
    {
        DBG_Printf(DBG_INFO, "started websocket server at port %u\n", srv->serverPort());
        connect(srv, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
    }
}

/*! Returns the websocket server port.
    \return the active server port, or 0 if not active
 */
quint16 WebSocketServer::port() const
{
    return srv->isListening() ? srv->serverPort() : 0;
}

/*! Handler for new client connections.
 */
void WebSocketServer::onNewConnection()
{
    while (srv->hasPendingConnections())
    {
        QWebSocket *sock = srv->nextPendingConnection();
        clients.push_back(sock);
    }
}

/*! Broadcasts a message to all connected clients.
    \param msg the message as JSON string
 */
void WebSocketServer::broadcastTextMessage(const QString &msg)
{
    for (size_t i = 0; i < clients.size(); i++)
    {
        QWebSocket *sock = clients[i];

        if (sock->state() == QAbstractSocket::ConnectedState)
        {
            sock->sendTextMessage(msg);
        }
        else
        {
            DBG_Printf(DBG_INFO, "Remove websocket %s:%u\n", qPrintable(sock->peerAddress().toString()), sock->peerPort());
            sock->deleteLater();
            clients[i] = clients.back();
            clients.pop_back();
        }
    }
}
#else // no websockets
  WebSocketServer::WebSocketServer(QObject *parent) :
      QObject(parent)
  { }
  void WebSocketServer::onNewConnection() { }
  void WebSocketServer::broadcastTextMessage(const QString &) { }
  quint16 WebSocketServer::port() const {  return 0; }
#endif
<|endoftext|>


<commit_before><commit_msg>New unit test case for unwanted date detection on pivot table labels.<commit_after><|endoftext|>


<commit_before><commit_msg>-Werror=unused-variable<commit_after><|endoftext|>


<commit_before><commit_msg>New unit test for pivot table's date output handling.<commit_after><|endoftext|>


<commit_before><commit_msg>unit test for string conversion feature, related fdo#37132 fdo#74622<commit_after><|endoftext|>


<commit_before>/* @doc \file Object to generate a file following AMELIF format.

   Copyright (c) 2007-2010
   @author Olivier Stasse
   JRL-Japan, CNRS/AIST

   All rights reserved.

   Please refers to file License.txt for details on the license.

*/

#include <string>
#include <iostream>
#include <fstream>
#include <dynamicsJRLJapan/dynamicsJRLJapanFactory.h>
#include "CommonTools.h"

using namespace std;
using namespace dynamicsJRLJapan;

void DisplayDynamicRobotInformation(CjrlDynamicRobot *aDynamicRobot,
				    ostream &tcout)
{
  std::vector<CjrlJoint *> aVec = aDynamicRobot->jointVector();
  int r = aVec.size();
  tcout << "Number of joints :" << r << endl;

}

int main(int argc, char *argv[])
{
  string aSpecificitiesFileName;
  string aPath;
  string aName;
  string aMapFromJointToRank;

  ofstream tcout("output.txt",ofstream::out);

  if (argc!=5)
    {
      aPath="./";
      aName="sample.wrl";
      aSpecificitiesFileName = "sampleSpecificities.xml";
      aMapFromJointToRank = "sampleLinkJointRank.xml";
    }
  else
    {
      aSpecificitiesFileName = argv[3];
      aPath=argv[1];
      aName=argv[2];
      aMapFromJointToRank=argv[4];
    }
  dynamicsJRLJapan::ObjectFactory aRobotDynamicsObjectConstructor;

  CjrlHumanoidDynamicRobot * aHDR = aRobotDynamicsObjectConstructor.createHumanoidDynamicRobot();

  if (aHDR==0)
    {
      cerr<< "Dynamic cast on HDR failed " << endl;
      exit(-1);
    }
  string RobotFileName = aPath+aName;
  dynamicsJRLJapan::parseOpenHRPVRMLFile(*aHDR,RobotFileName,aMapFromJointToRank,aSpecificitiesFileName);

  // Display tree of the joints.
  CjrlJoint* rootJoint = aHDR->rootJoint();

  int NbOfDofs = aHDR->numberDof();
  tcout << "NbOfDofs :" << NbOfDofs << std::endl;
  if (NbOfDofs==0)
    {
      cerr << "Empty Robot..."<< endl;
      return -1;
    }
  MAL_VECTOR_DIM(aCurrentConf,double,NbOfDofs);
  int lindex=0;
  for(int i=0;i<6;i++)
    aCurrentConf[lindex++] = 0.0;

  for(int i=0;i<(NbOfDofs-6 < 40 ? NbOfDofs-6 : 40) ;i++)
    aCurrentConf[lindex++] = 0.0;

  aHDR->currentConfiguration(aCurrentConf);

  MAL_VECTOR_DIM(aCurrentVel,double,NbOfDofs);
  lindex=0;
  for(int i=0;i<NbOfDofs;i++)
    aCurrentVel[lindex++] = 0.0;

  MAL_S3_VECTOR(ZMPval,double);

  aHDR->currentVelocity(aCurrentVel);
  //  aHDR->setComputeZMP(true);
  string inProperty="ComputeZMP"; string aValue="true";
  aHDR->setProperty(inProperty,aValue);
  aHDR->computeForwardKinematics();
  ZMPval = aHDR->zeroMomentumPoint();
  tcout << "First value of ZMP : "
	<< ZMPval(0) << " "
	<< ZMPval(1) << " "
	<< ZMPval(2) << endl;
  MAL_S3_VECTOR(poscom,double);
  poscom = aHDR->positionCenterOfMass();
  tcout << "Should be equal to the CoM: "
	<< poscom(0) << " "
	<< poscom(1) << " "
	<< poscom(2) << endl;

  matrixNxP InertiaMatrix;
  aHDR->computeInertiaMatrix();
  InertiaMatrix = aHDR->inertiaMatrix();

  tcout << "InertiaMatrix("
       << MAL_MATRIX_NB_ROWS(InertiaMatrix)<< ","
       << MAL_MATRIX_NB_COLS(InertiaMatrix)<< ")"<< endl;

  tcout << InertiaMatrix << endl;

  ofstream aof;
  aof.open("InertiaMatrix.dat");
  for(unsigned int i=0;i<MAL_MATRIX_NB_ROWS(InertiaMatrix);i++)
    {
      for(unsigned int j=0;j<MAL_MATRIX_NB_COLS(InertiaMatrix);j++)
       {
         aof << InertiaMatrix(i,j) << " ";
       }
      aof << endl;
    }
  aof.close();

  std::vector<CjrlJoint *> aVec = aHDR->jointVector();

  // Get the Jacobian of the right ankle.
  CjrlJoint  * aJoint = aHDR->rightAnkle();
  aJoint->computeJacobianJointWrtConfig();

  MAL_MATRIX(,double) aJ;
//  DisplayMatrix(aJ);
  tcout << "****************************" << endl;
  rootJoint->computeJacobianJointWrtConfig();
  aJ = rootJoint->jacobianJointWrtConfig();

  tcout << "Rank of Root: " << rootJoint->rankInConfiguration() << endl;

  //  DisplayMatrix(aJ);

  aJoint = aHDR->waist();
  tcout << "****************************" << endl;
  aHDR->computeJacobianCenterOfMass();
  tcout << "Value of the CoM's Jacobian:" << endl
       << aHDR->jacobianCenterOfMass() << endl;
  tcout << "****************************" << endl;
  RecursiveDisplayOfJoints(rootJoint,tcout,10);

  tcout << "****************************" << endl;

  // Test rank of the hands.
  tcout << "Rank of the right hand "<< endl;
  tcout << aHDR->rightWrist()->rankInConfiguration() << endl;
  CjrlHand *rightHand = aHDR->rightHand();
  string empty("");
  DisplayHand(rightHand,empty,tcout);

  tcout << "Rank of the left hand "<< endl;
  tcout << aHDR->leftWrist()->rankInConfiguration() << endl;
  CjrlHand *leftHand = aHDR->leftHand();
  DisplayHand(leftHand,empty,tcout);

  // Test rank of the feet.
  tcout << "Rank of the right foot "<< endl;
  tcout << aHDR->rightFoot()->associatedAnkle()->rankInConfiguration() << endl;
  CjrlFoot *rightFoot = aHDR->rightFoot();
  DisplayFoot(rightFoot,empty,tcout);

  tcout << "Rank of the left foot "<< endl;
  tcout << aHDR->leftFoot()->associatedAnkle()->rankInConfiguration() << endl;
  CjrlFoot *leftFoot = aHDR->leftFoot();
  DisplayFoot(leftFoot,empty,tcout);

  MAL_VECTOR_FILL(aCurrentVel,0.0);
  MAL_VECTOR_DIM(aCurrentAcc,double,NbOfDofs);
  MAL_VECTOR_FILL(aCurrentAcc,0.0);

  // This is mandatory for this implementation of computeForwardKinematics
  // to compute the derivative of the momentum.
  {
    string inProperty[4]={"TimeStep","ComputeAcceleration",
			  "ComputeBackwardDynamics", "ComputeZMP"};
    string inValue[4]={"0.005","true","true","true"};
    for(unsigned int i=0;i<4;i++)
      aHDR->setProperty(inProperty[i],inValue[i]);

  }
  for(int i=0;i<4;i++)
    {
      aHDR->currentVelocity(aCurrentVel);
      aHDR->currentAcceleration(aCurrentAcc);
      aHDR->computeForwardKinematics();
      ZMPval = aHDR->zeroMomentumPoint();
      tcout << i << "-th value of ZMP : "
	    << ZMPval(0) << " "
	    << ZMPval(1) << " "
	    << ZMPval(2) << endl;
      poscom = aHDR->positionCenterOfMass();
      tcout << "Should be equal to the CoM: "
	    << poscom(0) << " "
	    << poscom(1) << " "
	    << poscom(2) << endl;
    }

  // Check the information on actuated joints.
  std::vector<CjrlJoint *> ActuatedJoints = aHDR->getActuatedJoints();

  tcout << "Size of actuated Joints:" << ActuatedJoints.size() << endl;
  for(unsigned int i=0;i<ActuatedJoints.size();i++)
    tcout << "Rank of actuated joints ("<<i<< ") in configuration :"
	  << ActuatedJoints[i]->rankInConfiguration() << endl;

  tcout << "Humanoid mass:" << aHDR->mass() << endl;

  DisplayForces(aHDR,empty,tcout);
  DisplayTorques(aHDR,empty, tcout);

  tcout.close();
  // ASCII Comparison between the generated output and the reference one
  // given in argument.
  if (argc==2)
    {
      char OurFileName[120]="output.txt";
      if (CompareTwoFiles(argv[1],OurFileName))
	{
	  delete aHDR;
	  return 0;
	}
    }
  delete aHDR;
  return -1;
}
<commit_msg>Add new tests of methods not covered yet + report file.<commit_after>/* @doc \file Object to generate a file following AMELIF format.

   Copyright (c) 2007-2010
   @author Olivier Stasse
   JRL-Japan, CNRS/AIST

   All rights reserved.

   Please refers to file License.txt for details on the license.

*/

#include <string>
#include <iostream>
#include <fstream>
#include <dynamicsJRLJapan/dynamicsJRLJapanFactory.h>
#include "CommonTools.h"

using namespace std;
using namespace dynamicsJRLJapan;

void DisplayDynamicRobotInformation(CjrlDynamicRobot *aDynamicRobot,
				    ostream &tcout)
{
  std::vector<CjrlJoint *> aVec = aDynamicRobot->jointVector();
  int r = aVec.size();
  tcout << "Number of joints :" << r << endl;

}

int main(int argc, char *argv[])
{
  string aSpecificitiesFileName;
  string aPath;
  string aName;
  string aMapFromJointToRank;

  ofstream tcout("output.txt",ofstream::out);

  if (argc!=5)
    {
      aPath="./";
      aName="sample.wrl";
      aSpecificitiesFileName = "sampleSpecificities.xml";
      aMapFromJointToRank = "sampleLinkJointRank.xml";
    }
  else
    {
      aSpecificitiesFileName = argv[3];
      aPath=argv[1];
      aName=argv[2];
      aMapFromJointToRank=argv[4];
    }
  dynamicsJRLJapan::ObjectFactory aRobotDynamicsObjectConstructor;

  CjrlHumanoidDynamicRobot * aHDR = aRobotDynamicsObjectConstructor.createHumanoidDynamicRobot();

  if (aHDR==0)
    {
      cerr<< "Dynamic cast on HDR failed " << endl;
      exit(-1);
    }
  string RobotFileName = aPath+aName;
  dynamicsJRLJapan::parseOpenHRPVRMLFile(*aHDR,RobotFileName,aMapFromJointToRank,aSpecificitiesFileName);

  // Display tree of the joints.
  CjrlJoint* rootJoint = aHDR->rootJoint();

  int NbOfDofs = aHDR->numberDof();
  tcout << "NbOfDofs :" << NbOfDofs << std::endl;
  if (NbOfDofs==0)
    {
      cerr << "Empty Robot..."<< endl;
      return -1;
    }
  MAL_VECTOR_DIM(aCurrentConf,double,NbOfDofs);
  int lindex=0;
  for(int i=0;i<6;i++)
    aCurrentConf[lindex++] = 0.0;

  for(int i=0;i<(NbOfDofs-6 < 40 ? NbOfDofs-6 : 40) ;i++)
    aCurrentConf[lindex++] = 0.0;

  aHDR->currentConfiguration(aCurrentConf);

  MAL_VECTOR_DIM(aCurrentVel,double,NbOfDofs);
  lindex=0;
  for(int i=0;i<NbOfDofs;i++)
    aCurrentVel[lindex++] = 0.0;

  MAL_S3_VECTOR(ZMPval,double);

  aHDR->currentVelocity(aCurrentVel);
  //  aHDR->setComputeZMP(true);
  string inProperty="ComputeZMP"; string aValue="true";
  aHDR->setProperty(inProperty,aValue);
  aHDR->computeForwardKinematics();
  ZMPval = aHDR->zeroMomentumPoint();
  tcout << "First value of ZMP : "
	<< ZMPval(0) << " "
	<< ZMPval(1) << " "
	<< ZMPval(2) << endl;
  MAL_S3_VECTOR(poscom,double);
  poscom = aHDR->positionCenterOfMass();
  tcout << "Should be equal to the CoM: "
	<< poscom(0) << " "
	<< poscom(1) << " "
	<< poscom(2) << endl;

  matrixNxP InertiaMatrix;
  aHDR->computeInertiaMatrix();
  InertiaMatrix = aHDR->inertiaMatrix();

  tcout << "InertiaMatrix("
       << MAL_MATRIX_NB_ROWS(InertiaMatrix)<< ","
       << MAL_MATRIX_NB_COLS(InertiaMatrix)<< ")"<< endl;

  tcout << InertiaMatrix << endl;

  ofstream aof;
  aof.open("InertiaMatrix.dat");
  for(unsigned int i=0;i<MAL_MATRIX_NB_ROWS(InertiaMatrix);i++)
    {
      for(unsigned int j=0;j<MAL_MATRIX_NB_COLS(InertiaMatrix);j++)
       {
         aof << InertiaMatrix(i,j) << " ";
       }
      aof << endl;
    }
  aof.close();

  std::vector<CjrlJoint *> aVec = aHDR->jointVector();

  // Get the Jacobian of the right ankle.
  CjrlJoint  * aJoint = aHDR->rightAnkle();
  aJoint->computeJacobianJointWrtConfig();

  tcout << "Jacobian of the right ankle." << endl;
  MAL_MATRIX(,double) aJ;
  aJ = aJoint->jacobianJointWrtConfig();
  DisplayMatrix(aJ,tcout);

  // Get the articular Jacobian from the right ankle to the right wrist.
  vector3d origin; origin(0) = 0.0; origin(1) = 0.0; origin(2) = 0.0;
  aHDR->getJacobian(*aHDR->rightAnkle(),
		    *aHDR->rightWrist(),
		    origin,
		    aJ);
  tcout << "Jacobian from the right ankle to the right wrist. " << endl;
  DisplayMatrix(aJ,tcout);

  MAL_MATRIX_RESIZE(aJ,3, MAL_MATRIX_NB_COLS(aJ));
  // Get the linear part of the articular Jacobian from the right ankle to the right wrist.
  aHDR->getPositionJacobian(*aHDR->rightAnkle(),
			    *aHDR->rightWrist(),
			    origin,
			    aJ);
  tcout << "Jacobian from the right ankle to the right wrist. " << endl;
  DisplayMatrix(aJ,tcout);

  // Get the angular part of the articular Jacobian from the right ankle to the right wrist.
  aHDR->getOrientationJacobian(*aHDR->rightAnkle(),
			       *aHDR->rightWrist(),
			       aJ);

  tcout << "Jacobian from the right ankle to the right wrist. " << endl;
  DisplayMatrix(aJ,tcout);

  tcout << "****************************" << endl;
  rootJoint->computeJacobianJointWrtConfig();
  aJ = rootJoint->jacobianJointWrtConfig();

  tcout << "Rank of Root: " << rootJoint->rankInConfiguration() << endl;

  //  DisplayMatrix(aJ);

  aJoint = aHDR->waist();
  tcout << "****************************" << endl;
  aHDR->computeJacobianCenterOfMass();
  tcout << "Value of the CoM's Jacobian:" << endl
       << aHDR->jacobianCenterOfMass() << endl;
  tcout << "****************************" << endl;
  RecursiveDisplayOfJoints(rootJoint,tcout,10);

  tcout << "****************************" << endl;

  // Test rank of the hands.
  tcout << "Rank of the right hand "<< endl;
  tcout << aHDR->rightWrist()->rankInConfiguration() << endl;
  CjrlHand *rightHand = aHDR->rightHand();
  string empty("");
  DisplayHand(rightHand,empty,tcout);

  tcout << "Rank of the left hand "<< endl;
  tcout << aHDR->leftWrist()->rankInConfiguration() << endl;
  CjrlHand *leftHand = aHDR->leftHand();
  DisplayHand(leftHand,empty,tcout);

  // Test rank of the feet.
  tcout << "Rank of the right foot "<< endl;
  tcout << aHDR->rightFoot()->associatedAnkle()->rankInConfiguration() << endl;
  CjrlFoot *rightFoot = aHDR->rightFoot();
  DisplayFoot(rightFoot,empty,tcout);

  tcout << "Rank of the left foot "<< endl;
  tcout << aHDR->leftFoot()->associatedAnkle()->rankInConfiguration() << endl;
  CjrlFoot *leftFoot = aHDR->leftFoot();
  DisplayFoot(leftFoot,empty,tcout);

  MAL_VECTOR_FILL(aCurrentVel,0.0);
  MAL_VECTOR_DIM(aCurrentAcc,double,NbOfDofs);
  MAL_VECTOR_FILL(aCurrentAcc,0.0);

  // This is mandatory for this implementation of computeForwardKinematics
  // to compute the derivative of the momentum.
  {
    string inProperty[4]={"TimeStep","ComputeAcceleration",
			  "ComputeBackwardDynamics", "ComputeZMP"};
    string inValue[4]={"0.005","true","true","true"};
    for(unsigned int i=0;i<4;i++)
      aHDR->setProperty(inProperty[i],inValue[i]);

  }
  for(int i=0;i<4;i++)
    {
      aHDR->currentVelocity(aCurrentVel);
      aHDR->currentAcceleration(aCurrentAcc);
      aHDR->computeForwardKinematics();
      ZMPval = aHDR->zeroMomentumPoint();
      tcout << i << "-th value of ZMP : "
	    << ZMPval(0) << " "
	    << ZMPval(1) << " "
	    << ZMPval(2) << endl;
      poscom = aHDR->positionCenterOfMass();
      tcout << "Should be equal to the CoM: "
	    << poscom(0) << " "
	    << poscom(1) << " "
	    << poscom(2) << endl;
    }

  // Check the information on actuated joints.
  std::vector<CjrlJoint *> ActuatedJoints = aHDR->getActuatedJoints();

  tcout << "Size of actuated Joints:" << ActuatedJoints.size() << endl;
  for(unsigned int i=0;i<ActuatedJoints.size();i++)
    tcout << "Rank of actuated joints ("<<i<< ") in configuration :"
	  << ActuatedJoints[i]->rankInConfiguration() << endl;

  tcout << "Humanoid mass:" << aHDR->mass() << endl;

  DisplayForces(aHDR,empty,tcout);
  DisplayTorques(aHDR,empty, tcout);

  tcout.close();
  // ASCII Comparison between the generated output and the reference one
  // given in argument.
  if (argc==2)
    {
      char OurFileName[120]="output.txt";
      char ReportFileName[120]="reportthdr.txt";
      if (CompareTwoFiles(argv[1],
			  OurFileName,
			  ReportFileName))
	{
	  delete aHDR;
	  return 0;
	}
    }
  delete aHDR;
  return -1;
}
<|endoftext|>


<commit_before>//===- DebugIR.cpp - Unit tests for the DebugIR pass ----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// The tests in this file verify the DebugIR pass that generates debug metadata
// for LLVM IR.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/Triple.h"
#include "../lib/Transforms/Instrumentation/DebugIR.h"
#include "llvm/Config/config.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Transforms/Instrumentation.h"

// These tests do not depend on MCJIT, but we use the TrivialModuleBuilder
// helper class to construct some trivial Modules.
#include "../unittests/ExecutionEngine/MCJIT/MCJITTestBase.h"

#include <string>

#include "gtest/gtest.h"

#if defined(LLVM_ON_WIN32)
#include <direct.h>
#define getcwd_impl _getcwd
#elif defined (HAVE_GETCWD)
#include <unistd.h>
#define getcwd_impl getcwd
#endif // LLVM_ON_WIN32

using namespace llvm;
using namespace std;

namespace {

/// Insert a mock CUDescriptor with the specified producer
void insertCUDescriptor(Module *M, StringRef File, StringRef Dir,
                        StringRef Producer) {
  DIBuilder B(*M);
  B.createCompileUnit(dwarf::DW_LANG_C99, File, Dir, Producer, false, "", 0);
  B.finalize();
}

/// Attempts to remove file at Path and returns true if it existed, or false if
/// it did not.
bool removeIfExists(StringRef Path) {
  // This is an approximation, on error we don't know in general if the file
  // existed or not.
  llvm::error_code EC = sys::fs::remove(Path, false);
  return EC != llvm::errc::no_such_file_or_directory;
}

char * current_dir() {
#if defined(LLVM_ON_WIN32) || defined(HAVE_GETCWD)
  // calling getcwd (or _getcwd() on windows) with a null buffer makes it
  // allocate a sufficiently sized buffer to store the current working dir.
  return getcwd_impl(0, 0);
#else
  return 0;
#endif
}

class TestDebugIR : public ::testing::Test, public TrivialModuleBuilder {
protected:
  TestDebugIR()
      : TrivialModuleBuilder(sys::getProcessTriple())
      , cwd(current_dir()) {}

  ~TestDebugIR() { free(cwd); }

  /// Returns a concatenated path string consisting of Dir and Filename
  string getPath(const string &Dir, const string &Filename) {
    SmallVector<char, 8> Path;
    sys::path::append(Path, Dir, Filename);
    Path.resize(Dir.size() + Filename.size() + 2);
    Path[Dir.size() + Filename.size() + 1] = '\0';
    return string(Path.data());
  }

  LLVMContext Context;
  char *cwd;
  std::unique_ptr<Module> M;
  std::unique_ptr<DebugIR> D;
};

// Test empty named Module that is not supposed to be output to disk.
TEST_F(TestDebugIR, EmptyNamedModuleNoWrite) {
  string Dir = "MadeUpDirectory";
  string File = "empty_module.ll";
  string Path(getPath(Dir, File));

  M.reset(createEmptyModule(Path));

  // constructing DebugIR with no args should not result in any file generated.
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));
  D->runOnModule(*M);

  // verify DebugIR did not generate a file
  ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path;
}

// Test a non-empty unnamed module that is output to an autogenerated file name.
TEST_F(TestDebugIR, NonEmptyUnnamedModuleWriteToAutogeneratedFile) {
  M.reset(createEmptyModule());
  insertAddFunction(M.get());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true)));

  string Path;
  D->runOnModule(*M, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test not specifying a name in the module -- DebugIR should generate a name
// and write the file contents.
TEST_F(TestDebugIR, EmptyModuleWriteAnonymousFile) {
  M.reset(createEmptyModule());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(false, false)));

  string Path;
  D->runOnModule(*M, Path);

  // verify DebugIR generated a file and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

#ifdef HAVE_GETCWD // These tests require get_current_dir_name()

// Test empty named Module that is to be output to path specified at Module
// construction.
TEST_F(TestDebugIR, EmptyNamedModuleWriteFile) {
  string Filename("NamedFile1");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true)));

  string Path;
  D->runOnModule(*M, Path);

  // verify DebugIR was able to correctly parse the file name from module ID
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test an empty unnamed module generates an output file whose path is specified
// at DebugIR construction.
TEST_F(TestDebugIR, EmptyUnnamedModuleWriteNamedFile) {
  string Filename("NamedFile2");

  M.reset(createEmptyModule());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(
      false, false, StringRef(cwd), StringRef(Filename))));
  string Path;
  D->runOnModule(*M, Path);

  string ExpectedPath(getPath(cwd, Filename));
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test an empty named module generates an output file at the path specified
// during DebugIR construction.
TEST_F(TestDebugIR, EmptyNamedModuleWriteNamedFile) {
  string Filename("NamedFile3");

  string UnexpectedPath(getPath(cwd, "UnexpectedFilename"));
  M.reset(createEmptyModule(UnexpectedPath));

  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(
      false, false, StringRef(cwd), StringRef(Filename))));
  string Path;
  D->runOnModule(*M, Path);

  string ExpectedPath(getPath(cwd, Filename));
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;

  // verify DebugIR did not generate a file at the path specified at Module
  // construction.
  ASSERT_FALSE(removeIfExists(UnexpectedPath)) << "Unexpected file " << Path;
}

// Test a non-empty named module that is not supposed to be output to disk
TEST_F(TestDebugIR, NonEmptyNamedModuleNoWrite) {
  string Filename("NamedFile4");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  insertAddFunction(M.get());

  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));

  string Path;
  D->runOnModule(*M, Path);
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR did not generate a file
  ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path;
}

// Test a non-empty named module that is output to disk.
TEST_F(TestDebugIR, NonEmptyNamedModuleWriteFile) {
  string Filename("NamedFile5");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  insertAddFunction(M.get());

  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true)));

  string Path;
  D->runOnModule(*M, Path);
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test a non-empty unnamed module is output to a path specified at DebugIR
// construction.
TEST_F(TestDebugIR, NonEmptyUnnamedModuleWriteToNamedFile) {
  string Filename("NamedFile6");

  M.reset(createEmptyModule());
  insertAddFunction(M.get());

  D.reset(static_cast<DebugIR *>(
      llvm::createDebugIRPass(true, true, cwd, Filename)));
  string Path;
  D->runOnModule(*M, Path);

  string ExpectedPath(getPath(cwd, Filename));
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test that information inside existing debug metadata is retained
TEST_F(TestDebugIR, ExistingMetadataRetained) {
  string Filename("NamedFile7");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  insertAddFunction(M.get());

  StringRef Producer("TestProducer");
  insertCUDescriptor(M.get(), Filename, cwd, Producer);

  DebugInfoFinder Finder;
  Finder.processModule(*M);
  ASSERT_EQ((unsigned)1, Finder.compile_unit_count());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));

  string Path;
  D->runOnModule(*M, Path);
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR did not generate a file
  ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path;

  DICompileUnit CU(*Finder.compile_unit_begin());

  // Verify original CU information is retained
  ASSERT_EQ(Filename, CU.getFilename());
  ASSERT_EQ(cwd, CU.getDirectory());
  ASSERT_EQ(Producer, CU.getProducer());
}

#endif // HAVE_GETCWD

#ifdef GTEST_HAS_DEATH_TEST

// Test a non-empty unnamed module that is not supposed to be output to disk
// NOTE: this test is expected to die with LLVM_ERROR, and such depends on
// google test's "death test" mode.
TEST_F(TestDebugIR, NonEmptyUnnamedModuleNoWrite) {
  M.reset(createEmptyModule(StringRef()));
  insertAddFunction(M.get());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));

  // No name in module or on DebugIR construction ==> DebugIR should assert
  EXPECT_DEATH(D->runOnModule(*M),
               "DebugIR unable to determine file name in input.");
}

#endif // GTEST_HAS_DEATH_TEST
}
<commit_msg>DebugIRTests: Fixup for r204130.<commit_after>//===- DebugIR.cpp - Unit tests for the DebugIR pass ----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// The tests in this file verify the DebugIR pass that generates debug metadata
// for LLVM IR.
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/Triple.h"
#include "../lib/Transforms/Instrumentation/DebugIR.h"
#include "llvm/Config/config.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Transforms/Instrumentation.h"

// These tests do not depend on MCJIT, but we use the TrivialModuleBuilder
// helper class to construct some trivial Modules.
#include "../unittests/ExecutionEngine/MCJIT/MCJITTestBase.h"

#include <string>

#include "gtest/gtest.h"

#if defined(LLVM_ON_WIN32)
#include <direct.h>
#define getcwd_impl _getcwd
#elif defined (HAVE_GETCWD)
#include <unistd.h>
#define getcwd_impl getcwd
#endif // LLVM_ON_WIN32

using namespace llvm;
using namespace std;

namespace {

/// Insert a mock CUDescriptor with the specified producer
void insertCUDescriptor(Module *M, StringRef File, StringRef Dir,
                        StringRef Producer) {
  DIBuilder B(*M);
  B.createCompileUnit(dwarf::DW_LANG_C99, File, Dir, Producer, false, "", 0);
  B.finalize();
}

/// Attempts to remove file at Path and returns true if it existed, or false if
/// it did not.
bool removeIfExists(StringRef Path) {
  // This is an approximation, on error we don't know in general if the file
  // existed or not.
  llvm::error_code EC = sys::fs::remove(Path, false);
  return EC != llvm::errc::no_such_file_or_directory;
}

char * current_dir() {
#if defined(LLVM_ON_WIN32) || defined(HAVE_GETCWD)
  // calling getcwd (or _getcwd() on windows) with a null buffer makes it
  // allocate a sufficiently sized buffer to store the current working dir.
  return getcwd_impl(0, 0);
#else
  return 0;
#endif
}

class TestDebugIR : public ::testing::Test, public TrivialModuleBuilder {
protected:
  TestDebugIR()
      : TrivialModuleBuilder(sys::getProcessTriple())
      , cwd(current_dir()) {}

  ~TestDebugIR() { free(cwd); }

  /// Returns a concatenated path string consisting of Dir and Filename
  string getPath(const string &Dir, const string &Filename) {
    SmallVector<char, 8> Path;
    sys::path::append(Path, Dir, Filename);
    Path.resize(Dir.size() + Filename.size() + 2);
    Path[Dir.size() + Filename.size() + 1] = '\0';
    return string(Path.data());
  }

  LLVMContext Context;
  char *cwd;
  std::unique_ptr<Module> M;
  std::unique_ptr<DebugIR> D;
};

// Test empty named Module that is not supposed to be output to disk.
TEST_F(TestDebugIR, EmptyNamedModuleNoWrite) {
  string Dir = "MadeUpDirectory";
  string File = "empty_module.ll";
  string Path(getPath(Dir, File));

  M.reset(createEmptyModule(Path));

  // constructing DebugIR with no args should not result in any file generated.
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));
  D->runOnModule(*M);

  // verify DebugIR did not generate a file
  ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path;
}

// Test a non-empty unnamed module that is output to an autogenerated file name.
TEST_F(TestDebugIR, NonEmptyUnnamedModuleWriteToAutogeneratedFile) {
  M.reset(createEmptyModule());
  insertAddFunction(M.get());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true)));

  string Path;
  D->runOnModule(*M, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test not specifying a name in the module -- DebugIR should generate a name
// and write the file contents.
TEST_F(TestDebugIR, EmptyModuleWriteAnonymousFile) {
  M.reset(createEmptyModule());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(false, false)));

  string Path;
  D->runOnModule(*M, Path);

  // verify DebugIR generated a file and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

#ifdef HAVE_GETCWD // These tests require get_current_dir_name()

// Test empty named Module that is to be output to path specified at Module
// construction.
TEST_F(TestDebugIR, EmptyNamedModuleWriteFile) {
  string Filename("NamedFile1");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true)));

  string Path;
  D->runOnModule(*M, Path);

  // verify DebugIR was able to correctly parse the file name from module ID
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test an empty unnamed module generates an output file whose path is specified
// at DebugIR construction.
TEST_F(TestDebugIR, EmptyUnnamedModuleWriteNamedFile) {
  string Filename("NamedFile2");

  M.reset(createEmptyModule());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(
      false, false, StringRef(cwd), StringRef(Filename))));
  string Path;
  D->runOnModule(*M, Path);

  string ExpectedPath(getPath(cwd, Filename));
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test an empty named module generates an output file at the path specified
// during DebugIR construction.
TEST_F(TestDebugIR, EmptyNamedModuleWriteNamedFile) {
  string Filename("NamedFile3");

  string UnexpectedPath(getPath(cwd, "UnexpectedFilename"));
  M.reset(createEmptyModule(UnexpectedPath));

  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(
      false, false, StringRef(cwd), StringRef(Filename))));
  string Path;
  D->runOnModule(*M, Path);

  string ExpectedPath(getPath(cwd, Filename));
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;

  // verify DebugIR did not generate a file at the path specified at Module
  // construction.
  ASSERT_FALSE(removeIfExists(UnexpectedPath)) << "Unexpected file " << Path;
}

// Test a non-empty named module that is not supposed to be output to disk
TEST_F(TestDebugIR, NonEmptyNamedModuleNoWrite) {
  string Filename("NamedFile4");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  insertAddFunction(M.get());

  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));

  string Path;
  D->runOnModule(*M, Path);
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR did not generate a file
  ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path;
}

// Test a non-empty named module that is output to disk.
TEST_F(TestDebugIR, NonEmptyNamedModuleWriteFile) {
  string Filename("NamedFile5");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  insertAddFunction(M.get());

  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass(true, true)));

  string Path;
  D->runOnModule(*M, Path);
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test a non-empty unnamed module is output to a path specified at DebugIR
// construction.
TEST_F(TestDebugIR, NonEmptyUnnamedModuleWriteToNamedFile) {
  string Filename("NamedFile6");

  M.reset(createEmptyModule());
  insertAddFunction(M.get());

  D.reset(static_cast<DebugIR *>(
      llvm::createDebugIRPass(true, true, cwd, Filename)));
  string Path;
  D->runOnModule(*M, Path);

  string ExpectedPath(getPath(cwd, Filename));
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR generated a file, and clean it up
  ASSERT_TRUE(removeIfExists(Path)) << "Missing expected file at " << Path;
}

// Test that information inside existing debug metadata is retained
TEST_F(TestDebugIR, ExistingMetadataRetained) {
  string Filename("NamedFile7");
  string ExpectedPath(getPath(cwd, Filename));

  M.reset(createEmptyModule(ExpectedPath));
  insertAddFunction(M.get());

  StringRef Producer("TestProducer");
  insertCUDescriptor(M.get(), Filename, cwd, Producer);

  DebugInfoFinder Finder;
  Finder.processModule(*M);
  ASSERT_EQ((unsigned)1, Finder.compile_unit_count());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));

  string Path;
  D->runOnModule(*M, Path);
  ASSERT_EQ(ExpectedPath, Path);

  // verify DebugIR did not generate a file
  ASSERT_FALSE(removeIfExists(Path)) << "Unexpected file " << Path;

  DICompileUnit CU(*Finder.compile_units().begin());

  // Verify original CU information is retained
  ASSERT_EQ(Filename, CU.getFilename());
  ASSERT_EQ(cwd, CU.getDirectory());
  ASSERT_EQ(Producer, CU.getProducer());
}

#endif // HAVE_GETCWD

#ifdef GTEST_HAS_DEATH_TEST

// Test a non-empty unnamed module that is not supposed to be output to disk
// NOTE: this test is expected to die with LLVM_ERROR, and such depends on
// google test's "death test" mode.
TEST_F(TestDebugIR, NonEmptyUnnamedModuleNoWrite) {
  M.reset(createEmptyModule(StringRef()));
  insertAddFunction(M.get());
  D.reset(static_cast<DebugIR *>(llvm::createDebugIRPass()));

  // No name in module or on DebugIR construction ==> DebugIR should assert
  EXPECT_DEATH(D->runOnModule(*M),
               "DebugIR unable to determine file name in input.");
}

#endif // GTEST_HAS_DEATH_TEST
}
<|endoftext|>