<commit_before>/*
 * SourceIndex.cpp
 *
 * Copyright (C) 2009-19 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <core/libclang/SourceIndex.hpp>

#include <gsl/gsl>

#include <core/FilePath.hpp>
#include <core/PerformanceTimer.hpp>

#include <core/system/ProcessArgs.hpp>

#include <core/libclang/LibClang.hpp>
#include <core/libclang/UnsavedFiles.hpp>

namespace rstudio {
namespace core {
namespace libclang {

namespace {

inline unsigned applyTranslationUnitOptions(unsigned defaultOptions)
{
   // for now just reflect back the defaults
   return defaultOptions;
}

bool isHeaderExtension(const std::string& ex)
{
   return ex == ".h" || ex == ".hh" || ex == ".hpp";
}

} // anonymous namespace

bool SourceIndex::isSourceFile(const FilePath& filePath)
{
   std::string ex = filePath.extensionLowerCase();
   return  isHeaderExtension(ex) ||
           ex == ".c" || ex == ".cc" || ex == ".cpp" ||
           ex == ".m" || ex == ".mm";
}

bool SourceIndex::isSourceFile(const std::string& filename)
{
   return isSourceFile(FilePath(filename));
}

bool SourceIndex::isHeaderFile(const FilePath& filePath)
{
   return isHeaderExtension(filePath.extensionLowerCase());
}

SourceIndex::SourceIndex(CompilationDatabase compilationDB, int verbose)
{
   verbose_ = verbose;
   index_ = clang().createIndex(0, (verbose_ > 0) ? 1 : 0);
   compilationDB_ = compilationDB;
}

SourceIndex::~SourceIndex()
{
   try
   {
      // remove all
      removeAllTranslationUnits();

      // dispose the index
      if (index_ != nullptr)
         clang().disposeIndex(index_);
   }
   catch(...)
   {
   }
}

unsigned SourceIndex::getGlobalOptions() const
{
   return clang().CXIndex_getGlobalOptions(index_);
}

void SourceIndex::setGlobalOptions(unsigned options)
{
   clang().CXIndex_setGlobalOptions(index_, options);
}

void SourceIndex::removeTranslationUnit(const std::string& filename)
{
   TranslationUnits::iterator it = translationUnits_.find(filename);
   if (it != translationUnits_.end())
   {
      if (verbose_ > 0)
         std::cerr << "CLANG REMOVE INDEX: " << it->first << std::endl;
      clang().disposeTranslationUnit(it->second.tu);
      translationUnits_.erase(it->first);
   }
}

void SourceIndex::removeAllTranslationUnits()
{
   for(TranslationUnits::const_iterator it = translationUnits_.begin();
       it != translationUnits_.end(); ++it)
   {
      if (verbose_ > 0)
         std::cerr << "CLANG REMOVE INDEX: " << it->first << std::endl;

      clang().disposeTranslationUnit(it->second.tu);
   }

   translationUnits_.clear();
}

void SourceIndex::primeEditorTranslationUnit(const std::string& filename)
{
   // if we have no record of this translation unit then do a first pass
   if (translationUnits_.find(filename) == translationUnits_.end())
      getTranslationUnit(filename);
}

void SourceIndex::reprimeEditorTranslationUnit(const std::string& filename)
{
   // if we have already indexed this translation unit then re-index it
   if (translationUnits_.find(filename) != translationUnits_.end())
      getTranslationUnit(filename);
}

std::map<std::string,TranslationUnit>
                           SourceIndex::getIndexedTranslationUnits()
{
   std::map<std::string,TranslationUnit> units;
   for (TranslationUnits::value_type& t : translationUnits_)
   {
      TranslationUnit unit(t.first, t.second.tu, &unsavedFiles_);
      units.insert(std::make_pair(t.first, unit));
   }
   return units;
}

TranslationUnit SourceIndex::getTranslationUnit(const std::string& filename,
                                                bool alwaysReparse)
{
   FilePath filePath(filename);

   boost::scoped_ptr<core::PerformanceTimer> pTimer;
   if (verbose_ > 0)
   {
      std::cerr << "CLANG INDEXING: " << filePath.absolutePath() << std::endl;
      pTimer.reset(new core::PerformanceTimer(filePath.filename()));
   }

   // get the arguments and last write time for this file
   std::vector<std::string> args;
   if (compilationDB_.compileArgsForTranslationUnit)
   {
      args = compilationDB_.compileArgsForTranslationUnit(filename, true);
      if (args.empty())
         return TranslationUnit();
   }
   std::time_t lastWriteTime = filePath.lastWriteTime();

   // look it up
   TranslationUnits::iterator it = translationUnits_.find(filename);

   // check for various incremental processing scenarios
   if (it != translationUnits_.end())
   {
      // alias record
      StoredTranslationUnit& stored = it->second;

      // already up to date?
      if (!alwaysReparse &&
          (args == stored.compileArgs) &&
          (lastWriteTime == stored.lastWriteTime))
      {
         if (verbose_ > 0)
            std::cerr << "  (Index already up to date)" << std::endl;
         return TranslationUnit(filename, stored.tu, &unsavedFiles_);
      }

      // just needs reparse?
      else if (args == stored.compileArgs)
      {
         if (verbose_ > 0)
         {
            std::string reason = alwaysReparse ?
                                       "(Forced reparse)" :
                                       "(File changed on disk, reparsing)";

            std::cerr << "  " << reason << std::endl;
         }

         unsigned options = applyTranslationUnitOptions(
                                    clang().defaultReparseOptions(stored.tu));
         int ret = clang().reparseTranslationUnit(
                                stored.tu,
                                unsavedFiles().numUnsavedFiles(),
                                unsavedFiles().unsavedFilesArray(),
                                options);

         if (ret == 0)
         {
            // update last write time
            stored.lastWriteTime = lastWriteTime;

            // return it
            return TranslationUnit(filename, stored.tu, &unsavedFiles_);
         }
         else
         {
            LOG_ERROR_MESSAGE("Error re-parsing translation unit " + filename);
         }
      }
   }

   // if we got this far then there either was no existing translation
   // unit or we require a full rebuild. in all cases remove any existing
   // translation unit we have
   removeTranslationUnit(filename);

   // add verbose output if requested
   if (verbose_ >= 2)
     args.push_back("-v");

   // get the args in the fashion libclang expects (char**)
   core::system::ProcessArgs argsArray(args);

   if (verbose_ > 0)
      std::cerr << "  (Creating new index)" << std::endl;

   // create a new translation unit from the file
   unsigned options = applyTranslationUnitOptions(
                           clang().defaultEditingTranslationUnitOptions());
   CXTranslationUnit tu = clang().parseTranslationUnit(
                         index_,
                         filename.c_str(),
                         argsArray.args(),
                         gsl::narrow_cast<int>(argsArray.argCount()),
                         unsavedFiles().unsavedFilesArray(),
                         unsavedFiles().numUnsavedFiles(),
                         options);

   // save and return it if we succeeded
   if (tu != nullptr)
   {
      translationUnits_[filename] = StoredTranslationUnit(args,
                                                          lastWriteTime,
                                                          tu);

      TranslationUnit unit(filename, tu, &unsavedFiles_);
      if (verbose_ > 0)
         unit.printResourceUsage(std::cerr, false);
      return unit;
   }
   else
   {
      LOG_ERROR_MESSAGE("Error parsing translation unit " + filename);
      return TranslationUnit();
   }
}

Cursor SourceIndex::referencedCursorForFileLocation(const FileLocation &loc)
{
   // get the translation unit
   std::string filename = loc.filePath.absolutePath();
   TranslationUnit tu = getTranslationUnit(filename, true);
   if (tu.empty())
      return Cursor();

   // get the cursor
   Cursor cursor = tu.getCursor(filename, loc.line, loc.column);
   if (!cursor.isValid())
      return Cursor();

   // follow reference if we need to
   if (cursor.isReference() || cursor.isExpression())
   {
      cursor = cursor.getReferenced();
      if (!cursor.isValid())
         return Cursor();
   }

   // return the cursor that points to the definition
   return cursor;
}

} // namespace libclang
} // namespace core
} // namespace rstudio

<commit_msg>log compilation arguments used<commit_after>/*
 * SourceIndex.cpp
 *
 * Copyright (C) 2009-19 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <core/libclang/SourceIndex.hpp>

#include <gsl/gsl>

#include <core/Debug.hpp>
#include <core/FilePath.hpp>
#include <core/PerformanceTimer.hpp>

#include <core/system/ProcessArgs.hpp>

#include <core/libclang/LibClang.hpp>
#include <core/libclang/UnsavedFiles.hpp>

namespace rstudio {
namespace core {
namespace libclang {

namespace {

inline unsigned applyTranslationUnitOptions(unsigned defaultOptions)
{
   // for now just reflect back the defaults
   return defaultOptions;
}

bool isHeaderExtension(const std::string& ex)
{
   return ex == ".h" || ex == ".hh" || ex == ".hpp";
}

} // anonymous namespace

bool SourceIndex::isSourceFile(const FilePath& filePath)
{
   std::string ex = filePath.extensionLowerCase();
   return  isHeaderExtension(ex) ||
           ex == ".c" || ex == ".cc" || ex == ".cpp" ||
           ex == ".m" || ex == ".mm";
}

bool SourceIndex::isSourceFile(const std::string& filename)
{
   return isSourceFile(FilePath(filename));
}

bool SourceIndex::isHeaderFile(const FilePath& filePath)
{
   return isHeaderExtension(filePath.extensionLowerCase());
}

SourceIndex::SourceIndex(CompilationDatabase compilationDB, int verbose)
{
   verbose_ = verbose;
   index_ = clang().createIndex(0, (verbose_ > 0) ? 1 : 0);
   compilationDB_ = compilationDB;
}

SourceIndex::~SourceIndex()
{
   try
   {
      // remove all
      removeAllTranslationUnits();

      // dispose the index
      if (index_ != nullptr)
         clang().disposeIndex(index_);
   }
   catch(...)
   {
   }
}

unsigned SourceIndex::getGlobalOptions() const
{
   return clang().CXIndex_getGlobalOptions(index_);
}

void SourceIndex::setGlobalOptions(unsigned options)
{
   clang().CXIndex_setGlobalOptions(index_, options);
}

void SourceIndex::removeTranslationUnit(const std::string& filename)
{
   TranslationUnits::iterator it = translationUnits_.find(filename);
   if (it != translationUnits_.end())
   {
      if (verbose_ > 0)
         std::cerr << "CLANG REMOVE INDEX: " << it->first << std::endl;
      clang().disposeTranslationUnit(it->second.tu);
      translationUnits_.erase(it->first);
   }
}

void SourceIndex::removeAllTranslationUnits()
{
   for(TranslationUnits::const_iterator it = translationUnits_.begin();
       it != translationUnits_.end(); ++it)
   {
      if (verbose_ > 0)
         std::cerr << "CLANG REMOVE INDEX: " << it->first << std::endl;

      clang().disposeTranslationUnit(it->second.tu);
   }

   translationUnits_.clear();
}

void SourceIndex::primeEditorTranslationUnit(const std::string& filename)
{
   // if we have no record of this translation unit then do a first pass
   if (translationUnits_.find(filename) == translationUnits_.end())
      getTranslationUnit(filename);
}

void SourceIndex::reprimeEditorTranslationUnit(const std::string& filename)
{
   // if we have already indexed this translation unit then re-index it
   if (translationUnits_.find(filename) != translationUnits_.end())
      getTranslationUnit(filename);
}

std::map<std::string,TranslationUnit>
                           SourceIndex::getIndexedTranslationUnits()
{
   std::map<std::string,TranslationUnit> units;
   for (TranslationUnits::value_type& t : translationUnits_)
   {
      TranslationUnit unit(t.first, t.second.tu, &unsavedFiles_);
      units.insert(std::make_pair(t.first, unit));
   }
   return units;
}

TranslationUnit SourceIndex::getTranslationUnit(const std::string& filename,
                                                bool alwaysReparse)
{
   FilePath filePath(filename);

   boost::scoped_ptr<core::PerformanceTimer> pTimer;
   if (verbose_ > 0)
   {
      std::cerr << "CLANG INDEXING: " << filePath.absolutePath() << std::endl;
      pTimer.reset(new core::PerformanceTimer(filePath.filename()));
   }

   // get the arguments and last write time for this file
   std::vector<std::string> args;
   if (compilationDB_.compileArgsForTranslationUnit)
   {
      args = compilationDB_.compileArgsForTranslationUnit(filename, true);
      if (args.empty())
         return TranslationUnit();
   }
   std::time_t lastWriteTime = filePath.lastWriteTime();

   // look it up
   TranslationUnits::iterator it = translationUnits_.find(filename);

   // check for various incremental processing scenarios
   if (it != translationUnits_.end())
   {
      // alias record
      StoredTranslationUnit& stored = it->second;

      // already up to date?
      if (!alwaysReparse &&
          (args == stored.compileArgs) &&
          (lastWriteTime == stored.lastWriteTime))
      {
         if (verbose_ > 0)
            std::cerr << "  (Index already up to date)" << std::endl;
         return TranslationUnit(filename, stored.tu, &unsavedFiles_);
      }

      // just needs reparse?
      else if (args == stored.compileArgs)
      {
         if (verbose_ > 0)
         {
            std::string reason = alwaysReparse ?
                                       "(Forced reparse)" :
                                       "(File changed on disk, reparsing)";

            std::cerr << "  " << reason << std::endl;
         }

         unsigned options = applyTranslationUnitOptions(
                                    clang().defaultReparseOptions(stored.tu));
         int ret = clang().reparseTranslationUnit(
                                stored.tu,
                                unsavedFiles().numUnsavedFiles(),
                                unsavedFiles().unsavedFilesArray(),
                                options);

         if (ret == 0)
         {
            // update last write time
            stored.lastWriteTime = lastWriteTime;

            // return it
            return TranslationUnit(filename, stored.tu, &unsavedFiles_);
         }
         else
         {
            LOG_ERROR_MESSAGE("Error re-parsing translation unit " + filename);
         }
      }
   }

   // if we got this far then there either was no existing translation
   // unit or we require a full rebuild. in all cases remove any existing
   // translation unit we have
   removeTranslationUnit(filename);

   // add verbose output if requested
   if (verbose_ >= 2)
     args.push_back("-v");

   // report to user if requested
   if (verbose_ > 1)
   {
      std::cerr << "COMPILATION ARGUMENTS:" << std::endl;
      core::debug::print(args);
   }

   // get the args in the fashion libclang expects (char**)
   core::system::ProcessArgs argsArray(args);

   if (verbose_ > 0)
      std::cerr << "  (Creating new index)" << std::endl;

   // create a new translation unit from the file
   unsigned options = applyTranslationUnitOptions(
                           clang().defaultEditingTranslationUnitOptions());
   CXTranslationUnit tu = clang().parseTranslationUnit(
                         index_,
                         filename.c_str(),
                         argsArray.args(),
                         gsl::narrow_cast<int>(argsArray.argCount()),
                         unsavedFiles().unsavedFilesArray(),
                         unsavedFiles().numUnsavedFiles(),
                         options);

   // save and return it if we succeeded
   if (tu != nullptr)
   {
      translationUnits_[filename] = StoredTranslationUnit(args,
                                                          lastWriteTime,
                                                          tu);

      TranslationUnit unit(filename, tu, &unsavedFiles_);
      if (verbose_ > 0)
         unit.printResourceUsage(std::cerr, false);
      return unit;
   }
   else
   {
      LOG_ERROR_MESSAGE("Error parsing translation unit " + filename);
      return TranslationUnit();
   }
}

Cursor SourceIndex::referencedCursorForFileLocation(const FileLocation &loc)
{
   // get the translation unit
   std::string filename = loc.filePath.absolutePath();
   TranslationUnit tu = getTranslationUnit(filename, true);
   if (tu.empty())
      return Cursor();

   // get the cursor
   Cursor cursor = tu.getCursor(filename, loc.line, loc.column);
   if (!cursor.isValid())
      return Cursor();

   // follow reference if we need to
   if (cursor.isReference() || cursor.isExpression())
   {
      cursor = cursor.getReferenced();
      if (!cursor.isValid())
         return Cursor();
   }

   // return the cursor that points to the definition
   return cursor;
}

} // namespace libclang
} // namespace core
} // namespace rstudio

<|endoftext|>


<commit_before>#include <algorithm>
#include <stack>
#include <variant>

#include "dDNNFTreeDecompositionBuilder.h"

/* Turn a bounded-treewidth circuit c for which a tree decomposition td
 * is provided into a dNNF rooted at root, following the construction in
 * Section 5.1 of https://arxiv.org/pdf/1811.02944 */
dDNNF&& dDNNFTreeDecompositionBuilder::build() && {
  // We make the tree decomposition friendly
  td.makeFriendly(root_id);

  // We look for bags responsible for each variable
  for(bag_t i{0}; i<td.bags.size(); ++i) {
    const auto &b = td.getBag(i);
    if(td.getChildren(i).empty() && b.size()==1 && c.getGateType(*b.begin()) == BooleanGate::IN)
      responsible_bag[*b.begin()] = i;
  }

  // A friendly tree decomposition has leaf bags for every variable
  // nodes. Let's just check that to be safe.
  assert(responsible_bag.size()==c.inputs.size());

  // Create the input and negated input gates
  for(auto g: c.inputs) {
    auto gate = d.setGate(BooleanGate::IN, c.getProb(g));
    auto not_gate = d.setGate(BooleanGate::NOT);
    d.addWire(not_gate, gate);
    input_gate[g]=gate;
    negated_input_gate[g]=not_gate;
  }

  gate_vector_t<dDNNFGate> result_gates = builddDNNF();

  auto result_id = d.setGate("root", BooleanGate::OR);

  for(const auto &p: result_gates) {
    if(p.suspicious.empty() && p.valuation.find(root_id)->second) {
      d.addWire(result_id, p.id);
      break;
    }
  }

  return std::move(d);
}

constexpr bool isStrong(BooleanGate type, bool value)
{
  switch(type) {
    case BooleanGate::OR:
      return value;
    case BooleanGate::AND:
      return !value;
    case BooleanGate::IN:
      return false;
    default:
      return true;
  }
}

static bool isConnectible(const dDNNFTreeDecompositionBuilder::suspicious_t &suspicious,
                          const TreeDecomposition::Bag &b)
{
  for(const auto &g: suspicious) {
    if(b.find(g)==b.end())
      return false;
  }

  return true;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNFLeaf(
    bag_t bag)
{
  // If the bag is empty, it behaves as if it was not there
  if(td.getBag(bag).size()==0)
    return {};

  // Otherwise, since we have a friendly decomposition, we have a
  // single gate
  auto single_gate = *td.getBag(bag).begin();

  // We check if this bag is responsible for an input variable
  if(c.getGateType(single_gate)==BooleanGate::IN &&
      responsible_bag.find(single_gate)->second==bag)
  {
    // No need to create an extra gate, just point to the variable and
    // negated variable gate; no suspicious gate.
    dDNNFGate pos = { input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,true)},
      suspicious_t{}
    };
    dDNNFGate neg = { negated_input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,false)},
      suspicious_t{}
    };
    return { std::move(pos), std::move(neg) };
  } else {
    gate_vector_t<dDNNFGate> result_gates;

    // We create two TRUE gates (AND gates with no inputs)
    for(auto v: {true, false}) {
      // Optimization: we know the root is set to True, so no need to
      // construct valuations incompatible with this
      if(single_gate==root_id && !v)
        continue;

      suspicious_t suspicious;
      if(isStrong(c.getGateType(single_gate), v))
        suspicious.insert(single_gate);

      result_gates.emplace_back(
          d.setGate(BooleanGate::AND),
          valuation_t{std::make_pair(single_gate, v)},
          std::move(suspicious)
      );
    }

    return result_gates;
  }
}

bool dDNNFTreeDecompositionBuilder::isAlmostValuation(
    const valuation_t &valuation) const
{
  for(const auto &p1: valuation) {
    for(const auto &p2: valuation) {
      if(p1.first==p2.first)
        continue;
      if(!isStrong(c.getGateType(p1.first),p2.second))
        continue;

      if(circuitHasWire(p1.first,p2.first)) {
        switch(c.getGateType(p1.first)) {
          case BooleanGate::AND:
          case BooleanGate::OR:
            if(p1.second!=p2.second)
              return false;
            break;
          case BooleanGate::NOT:
            if(p1.second==p2.second)
              return false;
          default:
            ;
        }
      }
    }
  }

  return true;
}

dDNNFTreeDecompositionBuilder::suspicious_t
dDNNFTreeDecompositionBuilder::getInnocent(
    const valuation_t &valuation,
    const suspicious_t &innocent) const
{
  suspicious_t result = innocent;

  for(const auto &[g1,val]: valuation) {
    if(innocent.find(g1)!=innocent.end())
      continue;

    // We check if it is strong, if not it is innocent
    if(!isStrong(c.getGateType(g1), valuation.find(g1)->second)) {
      result.insert(g1);
      continue;
    }

    // We have a strong gate not innocented by the children bags,
    // it is only innocent if we also have in the bag an input to
    // that gate which is strong for that gate
    for(const auto &[g2, value]: valuation) {
      if(g2==g1)
        continue;

      if(circuitHasWire(g1,g2)) {
        if(isStrong(c.getGateType(g1), value)) {
          result.insert(g1);
          break;
        }
      }
    }
  }

  return result;
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::gates_to_or_t &gates_to_or)
{
  for(auto &[valuation, m]: gates_to_or) {
    o << "{";
    bool first=true;
    for(auto &[var, val]: valuation) {
      if(!first)
        o << ",";
      o << "(" << var << "," << val << ")";
      first=false;
    }
    o << "}: ";

    for(auto &[innocent, gates]: m) {
      o << "{";
      bool first=true;
      for(auto &x: innocent) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "} ";
      o << "[";
      first=true;
      for(auto &x: gates) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "] ";
    }

    o << "\n";
  }

  return o;
}

dDNNFTreeDecompositionBuilder::gates_to_or_t dDNNFTreeDecompositionBuilder::collectGatesToOr(
    bag_t bag,
    const gate_vector_t<dDNNFGate> &children_gates,
    const gates_to_or_t &partial)
{
  gates_to_or_t gates_to_or;

  for(auto g: children_gates) {
    // We check all suspicious gates are in the bag of the parent
    if(!isConnectible(g.suspicious,td.getBag(bag)))
      continue;

    // Find all valuations in partial that are compatible with this partial
    // valuation, if it exists
    auto compatibleValuation = [&g](const auto &p) {
                                 for(const auto &[var, val]: p.first) {
                                   auto it = g.valuation.find(var);
                                   if(it != g.valuation.end() && it->second != val)
                                     return false;
                                 }
                                 return true;
                               };

    for (auto it = std::find_if(partial.begin(), partial.end(), compatibleValuation);
         it != partial.end();
         it = std::find_if(std::next(it), partial.end(), compatibleValuation)) {
      auto &[matching_valuation, m] = *it;

      valuation_t valuation = matching_valuation;
      suspicious_t extra_innocent{};
      for(auto &[var, val]: g.valuation) {
         if(td.getBag(bag).find(var)!=td.getBag(bag).end()) {
           if(matching_valuation.find(var)==matching_valuation.end())
             valuation[var]=val;

           if(g.suspicious.find(var)==g.suspicious.end()) {
             extra_innocent.insert(var);
          }
        }
      }

      // We check valuation is still an almost-valuation
      if(!isAlmostValuation(valuation))
        continue;

      for(auto &[innocent, gates]: m) {
        suspicious_t new_innocent = std::move(extra_innocent);

        for(auto s: innocent)
          new_innocent.insert(s);

        new_innocent = getInnocent(valuation, new_innocent);

        if(gates.empty())
          gates_to_or[valuation][new_innocent].push_back(g.id);
        else {
          for(auto g2: gates) {
            gate_t and_gate;

            // We optimize a bit by avoiding creating an AND gate if there
            // is only one child, or if a second child is a TRUE gate
            gate_t gates_children[2];
            unsigned nb = 0;

            if(!(d.getGateType(g.id)==BooleanGate::AND &&
                  d.getWires(g.id).empty()))
              gates_children[nb++]=g.id;

            if(!(d.getGateType(g2)==BooleanGate::AND &&
                  d.getWires(g2).empty()))
              gates_children[nb++]=g2;

            if(nb==0) {
              // We have one (or two) TRUE gates; we just reuse it -- even
              // though we reuse a child gate, connections will still make
              // sense as the valuation and suspicious set been correctly
              // computed
              and_gate = g.id;
            } else if(nb==1) {
              // Only one non-TRUE gate; we reuse it. Similarly as in the
              // previous case, even though we reuse this gate, the connections
              // made from it will still take into account the valuation and
              // suspicious set
              and_gate = gates_children[0];
            } else {
              and_gate = d.setGate(BooleanGate::AND);
              for(auto x: gates_children) {
                d.addWire(and_gate, x);
              }
            }

            gates_to_or[valuation][new_innocent].push_back(and_gate);
          }
        }
      }
    }
  }

  return gates_to_or;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNF()
{
  // Unfortunately, tree decompositions can be quite deep so we need to
  // simulate recursion with a heap-based stack, to avoid exhausting the
  // actual memory stack
  struct RecursionParams
  {
    bag_t bag;
    size_t children_processed;
    gates_to_or_t gates_to_or;

    RecursionParams(bag_t b, size_t c, gates_to_or_t g) :
      bag(b), children_processed(c), gates_to_or(std::move(g)) {}

    RecursionParams(bag_t b) :
      bag(b), children_processed(0) {
        gates_to_or_t::mapped_type m;
        m[suspicious_t{}] = {};
        gates_to_or[valuation_t{}] = std::move(m);
      }
  };

  using RecursionResult = gate_vector_t<dDNNFGate>;

  std::stack<std::variant<RecursionParams,RecursionResult>> stack;
  stack.emplace(RecursionParams{td.root});

  while(!stack.empty()) {
    RecursionResult result;

    if(stack.top().index()==1) { // RecursionResult
      result = std::move(std::get<1>(stack.top()));
      stack.pop();
      if(stack.empty())
        return result;
    }

    auto [bag, children_processed, gates_to_or] = std::move(std::get<0>(stack.top()));
    stack.pop();

    if(td.getChildren(bag).empty()) {
      auto x = builddDNNFLeaf(bag);
      stack.emplace(x);
    } else {
      if(children_processed>0) {
        gates_to_or = collectGatesToOr(bag, result, gates_to_or);
      }

      if(children_processed==td.getChildren(bag).size()) {
        gate_vector_t<dDNNFGate> result_gates;

        for(auto &[valuation, m]: gates_to_or) {
          for(auto &[innocent, gates]: m) {
            gate_t result_gate;

            assert(gates.size()!=0);

            suspicious_t suspicious;
            for(auto &[var, val]: valuation)
              if(innocent.find(var)==innocent.end())
                suspicious.insert(var);

            if(gates.size()==1)
              result_gate = *gates.begin();
            else {
              result_gate = d.setGate(BooleanGate::OR);
              for(auto &g: gates) {
                d.addWire(result_gate, g);
              }
            }

            result_gates.emplace_back(result_gate, std::move(valuation), std::move(suspicious));
          }
        }

        stack.emplace(std::move(result_gates));
      } else {
        stack.emplace(RecursionParams{bag, children_processed+1, std::move(gates_to_or)});
        stack.emplace(RecursionParams{td.getChildren(bag)[children_processed]});
      }
    }
  }

  // We return from within the while loop, when we hit the last return
  // value
  assert(false);
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::dDNNFGate &g)
{
  o << g.id << "; {";
  bool first=true;
  for(const auto &p: g.valuation) {
    if(!first)
      o << ",";
    first=false;
    o << "(" << p.first << "," << p.second << ")";
  }
  o << "}; {";
  first=true;
  for(auto x: g.suspicious) {
    if(!first)
      o << ",";
    first=false;
    o << x;
  }
  o << "}";

  return o;
}

bool dDNNFTreeDecompositionBuilder::circuitHasWire(gate_t f, gate_t t) const
{
  return wiresSet.find(std::make_pair(f,t))!=wiresSet.end();
}
<commit_msg>Fix wrong std::move<commit_after>#include <algorithm>
#include <stack>
#include <variant>

#include "dDNNFTreeDecompositionBuilder.h"

/* Turn a bounded-treewidth circuit c for which a tree decomposition td
 * is provided into a dNNF rooted at root, following the construction in
 * Section 5.1 of https://arxiv.org/pdf/1811.02944 */
dDNNF&& dDNNFTreeDecompositionBuilder::build() && {
  // We make the tree decomposition friendly
  td.makeFriendly(root_id);

  // We look for bags responsible for each variable
  for(bag_t i{0}; i<td.bags.size(); ++i) {
    const auto &b = td.getBag(i);
    if(td.getChildren(i).empty() && b.size()==1 && c.getGateType(*b.begin()) == BooleanGate::IN)
      responsible_bag[*b.begin()] = i;
  }

  // A friendly tree decomposition has leaf bags for every variable
  // nodes. Let's just check that to be safe.
  assert(responsible_bag.size()==c.inputs.size());

  // Create the input and negated input gates
  for(auto g: c.inputs) {
    auto gate = d.setGate(BooleanGate::IN, c.getProb(g));
    auto not_gate = d.setGate(BooleanGate::NOT);
    d.addWire(not_gate, gate);
    input_gate[g]=gate;
    negated_input_gate[g]=not_gate;
  }

  gate_vector_t<dDNNFGate> result_gates = builddDNNF();

  auto result_id = d.setGate("root", BooleanGate::OR);

  for(const auto &p: result_gates) {
    if(p.suspicious.empty() && p.valuation.find(root_id)->second) {
      d.addWire(result_id, p.id);
      break;
    }
  }

  return std::move(d);
}

constexpr bool isStrong(BooleanGate type, bool value)
{
  switch(type) {
    case BooleanGate::OR:
      return value;
    case BooleanGate::AND:
      return !value;
    case BooleanGate::IN:
      return false;
    default:
      return true;
  }
}

static bool isConnectible(const dDNNFTreeDecompositionBuilder::suspicious_t &suspicious,
                          const TreeDecomposition::Bag &b)
{
  for(const auto &g: suspicious) {
    if(b.find(g)==b.end())
      return false;
  }

  return true;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNFLeaf(
    bag_t bag)
{
  // If the bag is empty, it behaves as if it was not there
  if(td.getBag(bag).size()==0)
    return {};

  // Otherwise, since we have a friendly decomposition, we have a
  // single gate
  auto single_gate = *td.getBag(bag).begin();

  // We check if this bag is responsible for an input variable
  if(c.getGateType(single_gate)==BooleanGate::IN &&
      responsible_bag.find(single_gate)->second==bag)
  {
    // No need to create an extra gate, just point to the variable and
    // negated variable gate; no suspicious gate.
    dDNNFGate pos = { input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,true)},
      suspicious_t{}
    };
    dDNNFGate neg = { negated_input_gate.find(single_gate)->second,
      {std::make_pair(single_gate,false)},
      suspicious_t{}
    };
    return { std::move(pos), std::move(neg) };
  } else {
    gate_vector_t<dDNNFGate> result_gates;

    // We create two TRUE gates (AND gates with no inputs)
    for(auto v: {true, false}) {
      // Optimization: we know the root is set to True, so no need to
      // construct valuations incompatible with this
      if(single_gate==root_id && !v)
        continue;

      suspicious_t suspicious;
      if(isStrong(c.getGateType(single_gate), v))
        suspicious.insert(single_gate);

      result_gates.emplace_back(
          d.setGate(BooleanGate::AND),
          valuation_t{std::make_pair(single_gate, v)},
          std::move(suspicious)
      );
    }

    return result_gates;
  }
}

bool dDNNFTreeDecompositionBuilder::isAlmostValuation(
    const valuation_t &valuation) const
{
  for(const auto &p1: valuation) {
    for(const auto &p2: valuation) {
      if(p1.first==p2.first)
        continue;
      if(!isStrong(c.getGateType(p1.first),p2.second))
        continue;

      if(circuitHasWire(p1.first,p2.first)) {
        switch(c.getGateType(p1.first)) {
          case BooleanGate::AND:
          case BooleanGate::OR:
            if(p1.second!=p2.second)
              return false;
            break;
          case BooleanGate::NOT:
            if(p1.second==p2.second)
              return false;
          default:
            ;
        }
      }
    }
  }

  return true;
}

dDNNFTreeDecompositionBuilder::suspicious_t
dDNNFTreeDecompositionBuilder::getInnocent(
    const valuation_t &valuation,
    const suspicious_t &innocent) const
{
  suspicious_t result = innocent;

  for(const auto &[g1,val]: valuation) {
    if(innocent.find(g1)!=innocent.end())
      continue;

    // We check if it is strong, if not it is innocent
    if(!isStrong(c.getGateType(g1), valuation.find(g1)->second)) {
      result.insert(g1);
      continue;
    }

    // We have a strong gate not innocented by the children bags,
    // it is only innocent if we also have in the bag an input to
    // that gate which is strong for that gate
    for(const auto &[g2, value]: valuation) {
      if(g2==g1)
        continue;

      if(circuitHasWire(g1,g2)) {
        if(isStrong(c.getGateType(g1), value)) {
          result.insert(g1);
          break;
        }
      }
    }
  }

  return result;
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::gates_to_or_t &gates_to_or)
{
  for(auto &[valuation, m]: gates_to_or) {
    o << "{";
    bool first=true;
    for(auto &[var, val]: valuation) {
      if(!first)
        o << ",";
      o << "(" << var << "," << val << ")";
      first=false;
    }
    o << "}: ";

    for(auto &[innocent, gates]: m) {
      o << "{";
      bool first=true;
      for(auto &x: innocent) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "} ";
      o << "[";
      first=true;
      for(auto &x: gates) {
        if(!first)
          o << ",";
        o << x;
        first=false;
      }
      o << "] ";
    }

    o << "\n";
  }

  return o;
}

dDNNFTreeDecompositionBuilder::gates_to_or_t dDNNFTreeDecompositionBuilder::collectGatesToOr(
    bag_t bag,
    const gate_vector_t<dDNNFGate> &children_gates,
    const gates_to_or_t &partial)
{
  gates_to_or_t gates_to_or;

  for(auto g: children_gates) {
    // We check all suspicious gates are in the bag of the parent
    if(!isConnectible(g.suspicious,td.getBag(bag)))
      continue;

    // Find all valuations in partial that are compatible with this partial
    // valuation, if it exists
    auto compatibleValuation = [&g](const auto &p) {
                                 for(const auto &[var, val]: p.first) {
                                   auto it = g.valuation.find(var);
                                   if(it != g.valuation.end() && it->second != val)
                                     return false;
                                 }
                                 return true;
                               };

    for (auto it = std::find_if(partial.begin(), partial.end(), compatibleValuation);
         it != partial.end();
         it = std::find_if(std::next(it), partial.end(), compatibleValuation)) {
      auto &[matching_valuation, m] = *it;

      valuation_t valuation = matching_valuation;
      suspicious_t extra_innocent{};
      for(auto &[var, val]: g.valuation) {
         if(td.getBag(bag).find(var)!=td.getBag(bag).end()) {
           if(matching_valuation.find(var)==matching_valuation.end())
             valuation[var]=val;

           if(g.suspicious.find(var)==g.suspicious.end()) {
             extra_innocent.insert(var);
          }
        }
      }

      // We check valuation is still an almost-valuation
      if(!isAlmostValuation(valuation))
        continue;

      for(auto &[innocent, gates]: m) {
        suspicious_t new_innocent = extra_innocent;

        for(auto s: innocent)
          new_innocent.insert(s);

        new_innocent = getInnocent(valuation, new_innocent);

        if(gates.empty())
          gates_to_or[valuation][new_innocent].push_back(g.id);
        else {
          for(auto g2: gates) {
            gate_t and_gate;

            // We optimize a bit by avoiding creating an AND gate if there
            // is only one child, or if a second child is a TRUE gate
            gate_t gates_children[2];
            unsigned nb = 0;

            if(!(d.getGateType(g.id)==BooleanGate::AND &&
                  d.getWires(g.id).empty()))
              gates_children[nb++]=g.id;

            if(!(d.getGateType(g2)==BooleanGate::AND &&
                  d.getWires(g2).empty()))
              gates_children[nb++]=g2;

            if(nb==0) {
              // We have one (or two) TRUE gates; we just reuse it -- even
              // though we reuse a child gate, connections will still make
              // sense as the valuation and suspicious set been correctly
              // computed
              and_gate = g.id;
            } else if(nb==1) {
              // Only one non-TRUE gate; we reuse it. Similarly as in the
              // previous case, even though we reuse this gate, the connections
              // made from it will still take into account the valuation and
              // suspicious set
              and_gate = gates_children[0];
            } else {
              and_gate = d.setGate(BooleanGate::AND);
              for(auto x: gates_children) {
                d.addWire(and_gate, x);
              }
            }

            gates_to_or[valuation][new_innocent].push_back(and_gate);
          }
        }
      }
    }
  }

  return gates_to_or;
}

dDNNFTreeDecompositionBuilder::gate_vector_t<dDNNFTreeDecompositionBuilder::dDNNFGate> dDNNFTreeDecompositionBuilder::builddDNNF()
{
  // Unfortunately, tree decompositions can be quite deep so we need to
  // simulate recursion with a heap-based stack, to avoid exhausting the
  // actual memory stack
  struct RecursionParams
  {
    bag_t bag;
    size_t children_processed;
    gates_to_or_t gates_to_or;

    RecursionParams(bag_t b, size_t c, gates_to_or_t g) :
      bag(b), children_processed(c), gates_to_or(std::move(g)) {}

    RecursionParams(bag_t b) :
      bag(b), children_processed(0) {
        gates_to_or_t::mapped_type m;
        m[suspicious_t{}] = {};
        gates_to_or[valuation_t{}] = std::move(m);
      }
  };

  using RecursionResult = gate_vector_t<dDNNFGate>;

  std::stack<std::variant<RecursionParams,RecursionResult>> stack;
  stack.emplace(RecursionParams{td.root});

  while(!stack.empty()) {
    RecursionResult result;

    if(stack.top().index()==1) { // RecursionResult
      result = std::move(std::get<1>(stack.top()));
      stack.pop();
      if(stack.empty())
        return result;
    }

    auto [bag, children_processed, gates_to_or] = std::move(std::get<0>(stack.top()));
    stack.pop();

    if(td.getChildren(bag).empty()) {
      auto x = builddDNNFLeaf(bag);
      stack.emplace(x);
    } else {
      if(children_processed>0) {
        gates_to_or = collectGatesToOr(bag, result, gates_to_or);
      }

      if(children_processed==td.getChildren(bag).size()) {
        gate_vector_t<dDNNFGate> result_gates;

        for(auto &[valuation, m]: gates_to_or) {
          for(auto &[innocent, gates]: m) {
            gate_t result_gate;

            assert(gates.size()!=0);

            suspicious_t suspicious;
            for(auto &[var, val]: valuation)
              if(innocent.find(var)==innocent.end())
                suspicious.insert(var);

            if(gates.size()==1)
              result_gate = *gates.begin();
            else {
              result_gate = d.setGate(BooleanGate::OR);
              for(auto &g: gates) {
                d.addWire(result_gate, g);
              }
            }

            result_gates.emplace_back(result_gate, std::move(valuation), std::move(suspicious));
          }
        }

        stack.emplace(std::move(result_gates));
      } else {
        stack.emplace(RecursionParams{bag, children_processed+1, std::move(gates_to_or)});
        stack.emplace(RecursionParams{td.getChildren(bag)[children_processed]});
      }
    }
  }

  // We return from within the while loop, when we hit the last return
  // value
  assert(false);
}

std::ostream &operator<<(std::ostream &o, const dDNNFTreeDecompositionBuilder::dDNNFGate &g)
{
  o << g.id << "; {";
  bool first=true;
  for(const auto &p: g.valuation) {
    if(!first)
      o << ",";
    first=false;
    o << "(" << p.first << "," << p.second << ")";
  }
  o << "}; {";
  first=true;
  for(auto x: g.suspicious) {
    if(!first)
      o << ",";
    first=false;
    o << x;
  }
  o << "}";

  return o;
}

bool dDNNFTreeDecompositionBuilder::circuitHasWire(gate_t f, gate_t t) const
{
  return wiresSet.find(std::make_pair(f,t))!=wiresSet.end();
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: unomodel.hxx,v $
 *
 *  $Revision: 1.12 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 15:01:21 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef UNOMODEL_HXX
#define UNOMODEL_HXX

#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSET_HPP_
#include <com/sun/star/beans/XPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XMULTIPROPERTYSET_HPP_
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYSTATE_HPP_
#include <com/sun/star/beans/XPropertyState.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
#include <com/sun/star/lang/XServiceInfo.hpp>
#endif
#ifndef _COM_SUN_STAR_VIEW_XRENDERABLE_HPP_
#include <com/sun/star/view/XRenderable.hpp>
#endif
#ifndef _SFX_SFXBASEMODEL_HXX_
#include <sfx2/sfxbasemodel.hxx>
#endif
#ifndef _COMPHELPER_PROPERTYSETHELPER_HXX_
#include <comphelper/propertysethelper.hxx>
#endif

class SmFormat;

//-----------------------------------------------------------------------------
class SmModel : public SfxBaseModel,
                public comphelper::PropertySetHelper,
                public com::sun::star::lang::XServiceInfo,
                public com::sun::star::view::XRenderable
{
protected:
    virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const ::com::sun::star::uno::Any* pValues )
        throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::beans::PropertyVetoException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::WrappedTargetException );
    virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, ::com::sun::star::uno::Any* pValue )
        throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException );
public:
    SmModel( SfxObjectShell *pObjSh = 0 );
    virtual ~SmModel() throw ();

    //XInterface
    virtual     ::com::sun::star::uno::Any SAL_CALL queryInterface( const ::com::sun::star::uno::Type& aType ) throw(::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL acquire(  ) throw();
    virtual void SAL_CALL release(  ) throw();

    //XTypeProvider
    virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Type > SAL_CALL getTypes(  ) throw(::com::sun::star::uno::RuntimeException);

    static const ::com::sun::star::uno::Sequence< sal_Int8 > & getUnoTunnelId();

    //XUnoTunnel
    virtual sal_Int64 SAL_CALL getSomething( const ::com::sun::star::uno::Sequence< sal_Int8 >& aIdentifier ) throw(::com::sun::star::uno::RuntimeException);

    //XRenderable
    virtual sal_Int32 SAL_CALL getRendererCount( const ::com::sun::star::uno::Any& rSelection, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rxOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
    virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > SAL_CALL getRenderer( sal_Int32 nRenderer, const ::com::sun::star::uno::Any& rSelection, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rxOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL render( sal_Int32 nRenderer, const ::com::sun::star::uno::Any& rSelection, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rxOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);

    //XServiceInfo
    virtual rtl::OUString SAL_CALL getImplementationName(void)
        throw( ::com::sun::star::uno::RuntimeException );
    virtual BOOL SAL_CALL supportsService(const rtl::OUString& ServiceName)
            throw( ::com::sun::star::uno::RuntimeException );
    virtual ::com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames(void)
            throw( ::com::sun::star::uno::RuntimeException );

    virtual void SAL_CALL setParent( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& xParent )
            throw( ::com::sun::star::lang::NoSupportException, ::com::sun::star::uno::RuntimeException );

    static ::com::sun::star::uno::Sequence< rtl::OUString > getSupportedServiceNames_Static();
    static ::rtl::OUString getImplementationName_Static();
};

#endif
<commit_msg>INTEGRATION: CWS changefileheader (1.12.278); FILE MERGED 2008/04/01 15:41:55 thb 1.12.278.3: #i85898# Stripping all external header guards 2008/04/01 12:41:38 thb 1.12.278.2: #i85898# Stripping all external header guards 2008/03/31 16:29:34 rt 1.12.278.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: unomodel.hxx,v $
 * $Revision: 1.13 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef UNOMODEL_HXX
#define UNOMODEL_HXX

#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XMultiPropertySet.hpp>
#include <com/sun/star/beans/XPropertyState.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/view/XRenderable.hpp>
#include <sfx2/sfxbasemodel.hxx>
#include <comphelper/propertysethelper.hxx>

class SmFormat;

//-----------------------------------------------------------------------------
class SmModel : public SfxBaseModel,
                public comphelper::PropertySetHelper,
                public com::sun::star::lang::XServiceInfo,
                public com::sun::star::view::XRenderable
{
protected:
    virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const ::com::sun::star::uno::Any* pValues )
        throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::beans::PropertyVetoException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::WrappedTargetException );
    virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, ::com::sun::star::uno::Any* pValue )
        throw(::com::sun::star::beans::UnknownPropertyException, ::com::sun::star::lang::WrappedTargetException );
public:
    SmModel( SfxObjectShell *pObjSh = 0 );
    virtual ~SmModel() throw ();

    //XInterface
    virtual     ::com::sun::star::uno::Any SAL_CALL queryInterface( const ::com::sun::star::uno::Type& aType ) throw(::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL acquire(  ) throw();
    virtual void SAL_CALL release(  ) throw();

    //XTypeProvider
    virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Type > SAL_CALL getTypes(  ) throw(::com::sun::star::uno::RuntimeException);

    static const ::com::sun::star::uno::Sequence< sal_Int8 > & getUnoTunnelId();

    //XUnoTunnel
    virtual sal_Int64 SAL_CALL getSomething( const ::com::sun::star::uno::Sequence< sal_Int8 >& aIdentifier ) throw(::com::sun::star::uno::RuntimeException);

    //XRenderable
    virtual sal_Int32 SAL_CALL getRendererCount( const ::com::sun::star::uno::Any& rSelection, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rxOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
    virtual ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue > SAL_CALL getRenderer( sal_Int32 nRenderer, const ::com::sun::star::uno::Any& rSelection, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rxOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);
    virtual void SAL_CALL render( sal_Int32 nRenderer, const ::com::sun::star::uno::Any& rSelection, const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >& rxOptions ) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException);

    //XServiceInfo
    virtual rtl::OUString SAL_CALL getImplementationName(void)
        throw( ::com::sun::star::uno::RuntimeException );
    virtual BOOL SAL_CALL supportsService(const rtl::OUString& ServiceName)
            throw( ::com::sun::star::uno::RuntimeException );
    virtual ::com::sun::star::uno::Sequence< rtl::OUString > SAL_CALL getSupportedServiceNames(void)
            throw( ::com::sun::star::uno::RuntimeException );

    virtual void SAL_CALL setParent( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >& xParent )
            throw( ::com::sun::star::lang::NoSupportException, ::com::sun::star::uno::RuntimeException );

    static ::com::sun::star::uno::Sequence< rtl::OUString > getSupportedServiceNames_Static();
    static ::rtl::OUString getImplementationName_Static();
};

#endif
<|endoftext|>


<commit_before><commit_msg>coverity#705726: fix memory leak<commit_after><|endoftext|>


<commit_before>#include <mpi.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <fj_tool/fapp.h>
#include <fjcoll.h>

#include "3d-mhd.h"

int T_MAX;
int T_MONITOR;
int mpi_my_rank;

const double PI = 3.141592653589793;

Formura_Navigator navi;

float frand() {
  return rand() / float(RAND_MAX);
}

double wctime() {
  struct timeval tv;
  gettimeofday(&tv,NULL);
  return (double)tv.tv_sec + (double)tv.tv_usec*1e-6;
}

void init() {
  for(int ix = navi.lower_x; ix < navi.upper_x; ++ix) {
    for(int iy = navi.lower_y; iy < navi.upper_y; ++iy) {
      for(int iz = navi.lower_z; iz < navi.upper_z; ++iz) {
        double x = (navi.offset_x + ix)/(double)NX;
        double y = (navi.offset_y + iy)/(double)NY;
        double z = (navi.offset_z + iz)/(double)NZ;
        dens[ix][iy][iz] = 0.1*sin(2*PI*z);
        vx[ix][iy][iz]   = -sin(2 * PI * y);
        vy[ix][iy][iz]   =  sin(2 * PI * x);
        vz[ix][iy][iz]   = 0;
        Bx[ix][iy][iz]   = -sin(2 * PI * y);
        By[ix][iy][iz]   =  sin(4 * PI * x);
        Bz[ix][iy][iz]   = 0;
        s[ix][iy][iz]    = 0;
        Psi[ix][iy][iz]  = 0;
      }
    }
  }
}

void write_monitor() {
  printf("t = %d\n", navi.time_step);
  char fn[256];
  sprintf(fn, "out/dump-%06d-%d.txt", navi.time_step, mpi_my_rank);
  FILE *fp = fopen(fn,"wb");
  int global_position[3];
  global_position[0] = navi.offset_x + navi.lower_x;
  global_position[1] = navi.offset_y + navi.lower_y;
  global_position[2] = navi.offset_z + navi.lower_z;
  fwrite(global_position, sizeof(int), 3, fp);

  int z_size = navi.upper_z - navi.lower_z;
  for(int x = navi.lower_x; x < navi.upper_x; ++x) {
    for(int y = navi.lower_y; y < navi.upper_y; ++y) {
      fwrite(dens[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    }
  }
  fclose(fp);
}

int main (int argc, char **argv)
{
  srand(time(NULL));
  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_my_rank);
  Formura_Init(&navi, MPI_COMM_WORLD);

  if (argc <= 1) {
    T_MAX=100;
  }else{
    sscanf(argv[1], "%d",  &T_MAX);
  }
  if (argc <= 2) {
    T_MONITOR=100;
  }else{
    sscanf(argv[2], "%d",  &T_MONITOR);
  }

  init();

  double t_begin, t_end;

  for(;;){
    double t = wctime();
    if(navi.time_step % T_MONITOR == 0 || navi.time_step <= 3 * T_MONITOR ) {
      printf("%d step @ %lf sec\n", navi.time_step, t-t_begin);
    }
    if(navi.time_step % T_MONITOR == 0) {
      write_monitor(navi);
    }

    if (navi.time_step >= T_MAX) break;
    if (navi.time_step == 0) {
      t_begin = wctime();
      start_collection("main");
    }

    Formura_Forward(&navi); // navi.time_step increases

    if (navi.time_step >= T_MAX) {
      t_end = wctime();
      stop_collection("main");
    }
  }
  printf("wct = %lf sec\n",t_end - t_begin);

  MPI_Barrier(MPI_COMM_WORLD);
  MPI_Finalize();

}
<commit_msg>Support for binary output.<commit_after>#include <mpi.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <fj_tool/fapp.h>
#include <fjcoll.h>

#include "3d-mhd.h"

int T_MAX;
int T_MONITOR;
int mpi_my_rank;

const double PI = 3.141592653589793;

Formura_Navigator navi;

float frand() {
  return rand() / float(RAND_MAX);
}

double wctime() {
  struct timeval tv;
  gettimeofday(&tv,NULL);
  return (double)tv.tv_sec + (double)tv.tv_usec*1e-6;
}

void init() {
  for(int ix = navi.lower_x; ix < navi.upper_x; ++ix) {
    for(int iy = navi.lower_y; iy < navi.upper_y; ++iy) {
      for(int iz = navi.lower_z; iz < navi.upper_z; ++iz) {
        double x = (navi.offset_x + ix)/(double)NX;
        double y = (navi.offset_y + iy)/(double)NY;
        double z = (navi.offset_z + iz)/(double)NZ;
        dens[ix][iy][iz] = 0.1*sin(2*PI*z);
        vx[ix][iy][iz]   = -sin(2 * PI * y);
        vy[ix][iy][iz]   =  sin(2 * PI * x);
        vz[ix][iy][iz]   = 0;
        Bx[ix][iy][iz]   = -sin(2 * PI * y);
        By[ix][iy][iz]   =  sin(4 * PI * x);
        Bz[ix][iy][iz]   = 0;
        s[ix][iy][iz]    = 0;
        Psi[ix][iy][iz]  = 0;
      }
    }
  }
}

void write_monitor() {
  printf("t = %d\n", navi.time_step);
  char fn[256];
  sprintf(fn, "out/dump-%06d-%d.txt", navi.time_step, mpi_my_rank);
  FILE *fp = fopen(fn,"wb");
  int global_position[6];
  global_position[0] = navi.offset_x + navi.lower_x;
  global_position[1] = navi.offset_y + navi.lower_y;
  global_position[2] = navi.offset_z + navi.lower_z;
  global_position[3] = navi.upper_x - navi.lower_x;
  global_position[4] = navi.upper_y - navi.lower_y;
  global_position[5] = navi.upper_z - navi.lower_z;
  fwrite(global_position, sizeof(int), 6, fp);

  int x_size = navi.upper_x - navi.lower_x;
  int y_size = navi.upper_y - navi.lower_y;
  int z_size = navi.upper_z - navi.lower_z;
  {
    const int y=navi.lower_y + y_size/2;
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(dens[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(vx[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(vy[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(vz[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(Bx[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(By[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(Bz[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int x = navi.lower_x; x < navi.upper_x; ++x) fwrite(s[x][y]+navi.lower_z, sizeof(double),z_size, fp);
  }
  {
    const int x=navi.lower_x + x_size/2;
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(dens[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(vx[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(vy[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(vz[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(Bx[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(By[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(Bz[x][y]+navi.lower_z, sizeof(double),z_size, fp);
    for(int y = navi.lower_y; y < navi.upper_y; ++y) fwrite(s[x][y]+navi.lower_z, sizeof(double),z_size, fp);
  }
  fclose(fp);
}

int main (int argc, char **argv)
{
  srand(time(NULL));
  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &mpi_my_rank);
  Formura_Init(&navi, MPI_COMM_WORLD);

  if (argc <= 1) {
    T_MAX=100;
  }else{
    sscanf(argv[1], "%d",  &T_MAX);
  }
  if (argc <= 2) {
    T_MONITOR=100;
  }else{
    sscanf(argv[2], "%d",  &T_MONITOR);
  }

  init();

  double t_begin, t_end;

  for(;;){
    double t = wctime();
    if(navi.time_step % T_MONITOR == 0 || navi.time_step <= 3 * T_MONITOR ) {
      printf("%d step @ %lf sec\n", navi.time_step, t-t_begin);
    }
    if(navi.time_step % T_MONITOR == 0) {
      write_monitor();
    }

    if (navi.time_step >= T_MAX) break;
    if (navi.time_step == 0) {
      t_begin = wctime();
      start_collection("main");
    }

    Formura_Forward(&navi); // navi.time_step increases

    if (navi.time_step >= T_MAX) {
      t_end = wctime();
      stop_collection("main");
    }
  }
  printf("wct = %lf sec\n",t_end - t_begin);

  MPI_Barrier(MPI_COMM_WORLD);
  MPI_Finalize();

}
<|endoftext|>


<commit_before><commit_msg>remove redundant draw call<commit_after><|endoftext|>


<commit_before>#include <cassert>
#include <iostream>
#include <string>
#include <unistd.h>
#include <thread>
#include <cstring>

#include <map>
#include <vector>

#include "libplatform/libplatform.h"
#include "uv.h"
#include "node.h"

#define TOBY_VERSION_MAJOR 0
#define TOBY_VERSION_MINOR 1
#define TOBY_VERSION_PATCH 1

#define TOBY_VERSION  NODE_STRINGIFY(TOBY_VERSION_MAJOR) "." \
                      NODE_STRINGIFY(TOBY_VERSION_MINOR) "." \
                      NODE_STRINGIFY(TOBY_VERSION_PATCH)

namespace toby {

using namespace std;
using namespace node;
using namespace v8;

typedef void  (*TobyOnloadCallback)(void*);
typedef char* (*TobyHostCallCallback)(const char*, const char*);

extern "C" char* tobyJSCompile(const char* source);
extern "C" char* tobyJSCall(const char* name, const char* value);
extern "C" bool tobyJSEmit(const char* name, const char* value);
extern "C" void tobyInit(const char* processName,
                         const char* userScript,
                         TobyOnloadCallback _tobyOnLoad,
                         TobyHostCallCallback _tobyHostCall);
extern "C" void _tobyRegister();

class ArrayBufferAllocator : public ArrayBuffer::Allocator {
 public:
  virtual void* Allocate(size_t length) {
    void* data = AllocateUninitialized(length);
    return data == NULL ? data : memset(data, 0, length);
  }
  virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
  virtual void Free(void* data, size_t) { free(data); }
};

static uv_loop_t* loop;
static Isolate* isolate_;

TobyOnloadCallback tobyOnLoad;
TobyHostCallCallback tobyHostCall;

// FIXME : vardic arguments? multiple listeners?
using Callback = std::map<std::string, Persistent<Function>>;
static Callback *eventListeners;

static Local<Value> GetValue(Isolate* isolate, Local<Context> context,
                             Local<Object> object, const char* property) {
  Local<String> v8_str =
      String::NewFromUtf8(isolate, property, NewStringType::kNormal)
          .ToLocalChecked();
  return object->Get(context, v8_str).ToLocalChecked();
}

static Local<Value> Stringify(Isolate* isolate, Local<Context> context,
                             Local<Value> object) {
  auto global = context->Global();

  Local<Value> result;
  auto JSON = GetValue(isolate, context, global, "JSON");
  auto stringify = GetValue(isolate, context, JSON.As<Object>(), "stringify");

  std::vector<Local<Value>> argv;
  argv.push_back(object);

  auto method = stringify.As<Function>();
  result = MakeCallback(isolate, global, method, argv.size(), argv.data());

  return result;
}

char* tobyJSCompile(const char* source) {
  Isolate* isolate = static_cast<Isolate*>(isolate_);
  Local<Value> result;

  HandleScope handle_scope(isolate);
  TryCatch try_catch(isolate);

  Local<Context> context(isolate->GetCurrentContext());

  Local<String> script = String::NewFromUtf8(isolate, source);
  Local<Script> compiled_script;
  if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
    String::Utf8Value error(try_catch.Exception());
    return NULL;
  }

  if (!compiled_script->Run(context).ToLocal(&result)) {
    String::Utf8Value error(try_catch.Exception());
    return NULL;
  }

  result = Stringify(isolate, context, result);
  String::Utf8Value ret(result);

  char* data = new char[ret.length()];
  strcpy(data, *ret);
  return data;
}

char* tobyJSCall(const char* name, const char* value) {
  Isolate* isolate = static_cast<Isolate*>(isolate_);

  auto context = isolate->GetCurrentContext();
  auto global = context->Global();

  Local<Value> result;
  auto func = GetValue(isolate, context, global, name);

  if (func->IsFunction()) {
    std::vector<Local<Value>> argv;
    Local<Value> argument = String::NewFromUtf8(isolate, value);
    argv.push_back(argument);

    auto method = func.As<Function>();
    result = MakeCallback(isolate,
      isolate->GetCurrentContext()->Global(), method,
      argv.size(), argv.data());
  }
  else {
    result = Undefined(isolate);
    return NULL;
  }

  result = Stringify(isolate, context, result);
  String::Utf8Value ret(result);

  char* data = new char[ret.length()];
  strcpy(data, *ret);
  return data;
}

static void HostCallMethod(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  Local<Value> result;

  auto context = isolate->GetCurrentContext();
  auto global = context->Global();

  {
    // FIXME : better way to serialize/deserialize?
    result = Stringify(isolate, context, args[1]);

    String::Utf8Value key(args[0]);
    const char* c_key = *key;

    String::Utf8Value value(result);
    const char* c_value = *value;

    char* ret = tobyHostCall(c_key, c_value);
    result = String::NewFromUtf8(isolate, ret, NewStringType::kNormal).ToLocalChecked();
  }

  args.GetReturnValue().Set(result);
}

// /node/test/addons/async-hello-world/binding.cc
struct async_req {
  uv_work_t req;
  std::string name;
  std::string value;
  Isolate* isolate;
  // Persistent<Function> callback;
};

static void DoAsync(uv_work_t* r) {
  async_req* req = reinterpret_cast<async_req*>(r->data);
  // printf("DoAsync\n");
}

static void AfterAsync(uv_work_t* r, int status) {
  // FIXME : check the node.js is still alive

  // printf("AfterAsync\n");
  async_req* req = reinterpret_cast<async_req*>(r->data);

  if (eventListeners->count(req->name) == 0) {
    delete req;
    return;
  }

  Isolate* isolate = req->isolate;
  auto context = isolate->GetCurrentContext();

  HandleScope scope(isolate);

  std::vector<Local<Value>> argv;
  Local<Value> argument = String::NewFromUtf8(isolate, req->value.c_str());  //value.c_str()
  argv.push_back(argument);

  TryCatch try_catch(isolate);
  Local<Value> result;

  Local<Function> callback = Local<Function>::New(isolate, eventListeners->at(req->name));
  result = callback->Call(context->Global(), argv.size(), argv.data());

  // // cleanup
  // req->callback.Reset();
  delete req;

  if (try_catch.HasCaught()) {
    FatalException(isolate, try_catch);
  }
}

bool tobyJSEmit(const char* name, const char* value) {
  async_req* req = new async_req;
  req->req.data = req;
  req->isolate = isolate_; // FIXME : ...

  req->name = std::string(name);  // FIXME : use pointer
  req->value = std::string(value);

  uv_queue_work(loop,
                &req->req,
                DoAsync,
                AfterAsync);
  return true;
}

static void OnMethod(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  //Local<Value> result = Undefined(isolate);

  auto context = isolate->GetCurrentContext();
  auto global = context->Global();

  if (args[1]->IsFunction()) {
    String::Utf8Value name(args[0]);

    Local<Function> callback = Local<Function>::Cast(args[1]);
    (*eventListeners)[std::string(*name)].Reset(isolate, callback);

    // FIXME : remove it in removeListener()
    //eventListeners[name].Reset();
  }
}

static void _tobyInit(Isolate* isolate) {
  // dummy event. do not end the loop.
  // FIXME : use uv_async_init!!
  const char* source = "const toby = process.binding('toby');"
                       "(function(){setInterval(function(){},1000)})();";

  Local<Value> result;
  HandleScope handle_scope(isolate);
  TryCatch try_catch(isolate);

  Local<Context> context(isolate->GetCurrentContext());

  Local<String> script = String::NewFromUtf8(isolate, source);
  Local<Script> compiled_script;
  if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
    String::Utf8Value error(try_catch.Exception());
    // printf("%s", *error);
    return;
  }

  if (!compiled_script->Run(context).ToLocal(&result)) {
    String::Utf8Value error(try_catch.Exception());
    // printf("%s", *error);
    return;
  }

  // result = Stringify(isolate, context, result);
  // String::Utf8Value ret(result);
}

static void atExitCB(void* arg) {
  Isolate* isolate = static_cast<Isolate*>(arg);
  HandleScope handle_scope(isolate);
  Local<Object> obj = Object::New(isolate);
  assert(!obj.IsEmpty());  // Assert VM is still alive.
  assert(obj->IsObject());
}

static void init(Local<Object> exports) {
  AtExit(atExitCB, exports->GetIsolate());

  NODE_SET_METHOD(exports, "hostCall", HostCallMethod);
  NODE_SET_METHOD(exports, "on", OnMethod);

  exports->DefineOwnProperty(
    exports->GetIsolate()->GetCurrentContext(),
    String::NewFromUtf8(exports->GetIsolate(), "version"),
    String::NewFromUtf8(exports->GetIsolate(), TOBY_VERSION),
    v8::ReadOnly).FromJust();

  // call the host's OnLoad()
  tobyOnLoad(isolate_);
}

NODE_MODULE_CONTEXT_AWARE_BUILTIN(toby, init)

static void _node(const char* processName, const char* userScript) {
  // argv memory should be adjacent.
  // libuv/src/unix/proctitle.c
  int _argc = 3;
  char* _argv[_argc];

  // FIXME : leave empty string in evalScript(-e "")
  // there's a bug in OSX(such as, "bad option: -e?")
  char evalScript[] = "";
  char nodeOptions[] = "-e";

  int size = 0;
  size += strlen(processName) + 1;
  size += strlen(nodeOptions) + 1;
  size += strlen(evalScript) + 1;

  char* buf = new char[size];
  int i = 0;

  _argv[0] = buf+i;
  strncpy(buf+i, processName, strlen(processName));
  i += strlen(processName);
  buf[++i] = '\0';

  _argv[1] = buf+i;
  strncpy(buf+i, nodeOptions, strlen(nodeOptions));
  i += strlen(nodeOptions);
  buf[++i] = '\0';

  _argv[2] = buf+i;
  strncpy(buf+i, evalScript, strlen(evalScript));
  i += strlen(evalScript);
  buf[++i] = '\0';

  {
    using namespace std;
    using namespace node;
    using namespace v8;
    using namespace toby;

    static Platform* platform_;
    platform_ = platform::CreateDefaultPlatform();  //v8_default_thread_pool_size = 4;
    V8::InitializePlatform(platform_);
    V8::Initialize();

    Isolate::CreateParams params;
    params.array_buffer_allocator = new ArrayBufferAllocator();

    isolate_ = Isolate::New(params);
    // // FIXME : check isolate is null or not
    // if (isolate == nullptr)
    //   return 12;  // Signal internal error.

    Locker locker(isolate_);
    Isolate::Scope isolate_scope(isolate_);
    HandleScope handle_scope(isolate_);
    static Local<Context> context = Context::New(isolate_);
    Context::Scope context_scope(context);

    int exec_argc;
    const char** exec_argv;
    Init(&_argc, const_cast<const char**>(_argv), &exec_argc, &exec_argv);

    static Environment* env = CreateEnvironment(
        isolate_, loop, context, _argc, _argv, exec_argc, exec_argv);

    LoadEnvironment(env);

    /* inject the toby script / user script here */
    toby::_tobyInit(isolate_);
    {
      // 'node/lib/internal/bootstrap_node.js' is lazily loaded.
      std::string source = "";
      source += "process.nextTick(function() {";
      source += userScript;
      source += "});";

      Isolate* isolate = static_cast<Isolate*>(isolate_);
      Local<Value> result;

      HandleScope handle_scope(isolate);
      TryCatch try_catch(isolate);

      Local<Context> context(isolate->GetCurrentContext());

      Local<String> script = String::NewFromUtf8(isolate, source.c_str());
      Local<Script> compiled_script;
      if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
        String::Utf8Value error(try_catch.Exception());
        fprintf(stderr, "Fatal Error in userScript\n%s", *error);
        fflush(stderr);
        return ;
      }

      if (!compiled_script->Run(context).ToLocal(&result)) {
        String::Utf8Value error(try_catch.Exception());
        fprintf(stderr, "Fatal Error in userScript\n%s", *error);
        fflush(stderr);
        return ;
      }
    }
    /* */

    bool more;
    do {
      platform::PumpMessageLoop(platform_, isolate_);
      more = uv_run(loop, UV_RUN_ONCE);
      if (more == false) {
        platform::PumpMessageLoop(platform_, isolate_);
        EmitBeforeExit(env);

        more = uv_loop_alive(loop);
        if (uv_run(loop, UV_RUN_NOWAIT) != 0)
          more = true;
      }
    } while (more == true);

    // FIXME : not reached
    // due to 'setInterval(function(){},1000)' in _tobyInit
    const int exit_code = EmitExit(env);
    RunAtExit(env);
    //return exit_code;
  }
}

void _tobyRegister() {
  // FIXME : need to find out another way
  //         more info on http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
  // workaround patch for freepascal. un-comment below line.
  // the toby module won't be registered due to over-writting '__libc_csu_init'
  // in freepascal/rtl/linux/x86_64/cprt0.as
  //   movq __libc_csu_init@GOTPCREL(%rip), %rcx
  toby::_register_toby();
}

void tobyInit(const char* processName,
              const char* userScript,
              TobyOnloadCallback _tobyOnLoad,
              TobyHostCallCallback _tobyHostCall) {
  // initialized the eventListeners map
  eventListeners = new Callback;

  // set the default event loop.
  loop = uv_default_loop();

  toby::tobyOnLoad = _tobyOnLoad;
  toby::tobyHostCall = _tobyHostCall;

  // start the node.js in a thread
  std::thread n(_node, processName, userScript);
  n.detach();
}

}  // namespace toby<commit_msg>remove un-necessary header file <unistd.h><commit_after>#include <cassert>
#include <iostream>
#include <string>
#include <thread>
#include <cstring>

#include <map>
#include <vector>

#include "libplatform/libplatform.h"
#include "uv.h"
#include "node.h"

#define TOBY_VERSION_MAJOR 0
#define TOBY_VERSION_MINOR 1
#define TOBY_VERSION_PATCH 1

#define TOBY_VERSION  NODE_STRINGIFY(TOBY_VERSION_MAJOR) "." \
                      NODE_STRINGIFY(TOBY_VERSION_MINOR) "." \
                      NODE_STRINGIFY(TOBY_VERSION_PATCH)

namespace toby {

using namespace std;
using namespace node;
using namespace v8;

typedef void  (*TobyOnloadCallback)(void*);
typedef char* (*TobyHostCallCallback)(const char*, const char*);

extern "C" char* tobyJSCompile(const char* source);
extern "C" char* tobyJSCall(const char* name, const char* value);
extern "C" bool tobyJSEmit(const char* name, const char* value);
extern "C" void tobyInit(const char* processName,
                         const char* userScript,
                         TobyOnloadCallback _tobyOnLoad,
                         TobyHostCallCallback _tobyHostCall);
extern "C" void _tobyRegister();

class ArrayBufferAllocator : public ArrayBuffer::Allocator {
 public:
  virtual void* Allocate(size_t length) {
    void* data = AllocateUninitialized(length);
    return data == NULL ? data : memset(data, 0, length);
  }
  virtual void* AllocateUninitialized(size_t length) { return malloc(length); }
  virtual void Free(void* data, size_t) { free(data); }
};

static uv_loop_t* loop;
static Isolate* isolate_;

TobyOnloadCallback tobyOnLoad;
TobyHostCallCallback tobyHostCall;

// FIXME : vardic arguments? multiple listeners?
using Callback = std::map<std::string, Persistent<Function>>;
static Callback *eventListeners;

static Local<Value> GetValue(Isolate* isolate, Local<Context> context,
                             Local<Object> object, const char* property) {
  Local<String> v8_str =
      String::NewFromUtf8(isolate, property, NewStringType::kNormal)
          .ToLocalChecked();
  return object->Get(context, v8_str).ToLocalChecked();
}

static Local<Value> Stringify(Isolate* isolate, Local<Context> context,
                             Local<Value> object) {
  auto global = context->Global();

  Local<Value> result;
  auto JSON = GetValue(isolate, context, global, "JSON");
  auto stringify = GetValue(isolate, context, JSON.As<Object>(), "stringify");

  std::vector<Local<Value>> argv;
  argv.push_back(object);

  auto method = stringify.As<Function>();
  result = MakeCallback(isolate, global, method, argv.size(), argv.data());

  return result;
}

char* tobyJSCompile(const char* source) {
  Isolate* isolate = static_cast<Isolate*>(isolate_);
  Local<Value> result;

  HandleScope handle_scope(isolate);
  TryCatch try_catch(isolate);

  Local<Context> context(isolate->GetCurrentContext());

  Local<String> script = String::NewFromUtf8(isolate, source);
  Local<Script> compiled_script;
  if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
    String::Utf8Value error(try_catch.Exception());
    return NULL;
  }

  if (!compiled_script->Run(context).ToLocal(&result)) {
    String::Utf8Value error(try_catch.Exception());
    return NULL;
  }

  result = Stringify(isolate, context, result);
  String::Utf8Value ret(result);

  char* data = new char[ret.length()];
  strcpy(data, *ret);
  return data;
}

char* tobyJSCall(const char* name, const char* value) {
  Isolate* isolate = static_cast<Isolate*>(isolate_);

  auto context = isolate->GetCurrentContext();
  auto global = context->Global();

  Local<Value> result;
  auto func = GetValue(isolate, context, global, name);

  if (func->IsFunction()) {
    std::vector<Local<Value>> argv;
    Local<Value> argument = String::NewFromUtf8(isolate, value);
    argv.push_back(argument);

    auto method = func.As<Function>();
    result = MakeCallback(isolate,
      isolate->GetCurrentContext()->Global(), method,
      argv.size(), argv.data());
  }
  else {
    result = Undefined(isolate);
    return NULL;
  }

  result = Stringify(isolate, context, result);
  String::Utf8Value ret(result);

  char* data = new char[ret.length()];
  strcpy(data, *ret);
  return data;
}

static void HostCallMethod(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  Local<Value> result;

  auto context = isolate->GetCurrentContext();
  auto global = context->Global();

  {
    // FIXME : better way to serialize/deserialize?
    result = Stringify(isolate, context, args[1]);

    String::Utf8Value key(args[0]);
    const char* c_key = *key;

    String::Utf8Value value(result);
    const char* c_value = *value;

    char* ret = tobyHostCall(c_key, c_value);
    result = String::NewFromUtf8(isolate, ret, NewStringType::kNormal).ToLocalChecked();
  }

  args.GetReturnValue().Set(result);
}

// /node/test/addons/async-hello-world/binding.cc
struct async_req {
  uv_work_t req;
  std::string name;
  std::string value;
  Isolate* isolate;
  // Persistent<Function> callback;
};

static void DoAsync(uv_work_t* r) {
  async_req* req = reinterpret_cast<async_req*>(r->data);
  // printf("DoAsync\n");
}

static void AfterAsync(uv_work_t* r, int status) {
  // FIXME : check the node.js is still alive

  // printf("AfterAsync\n");
  async_req* req = reinterpret_cast<async_req*>(r->data);

  if (eventListeners->count(req->name) == 0) {
    delete req;
    return;
  }

  Isolate* isolate = req->isolate;
  auto context = isolate->GetCurrentContext();

  HandleScope scope(isolate);

  std::vector<Local<Value>> argv;
  Local<Value> argument = String::NewFromUtf8(isolate, req->value.c_str());  //value.c_str()
  argv.push_back(argument);

  TryCatch try_catch(isolate);
  Local<Value> result;

  Local<Function> callback = Local<Function>::New(isolate, eventListeners->at(req->name));
  result = callback->Call(context->Global(), argv.size(), argv.data());

  // // cleanup
  // req->callback.Reset();
  delete req;

  if (try_catch.HasCaught()) {
    FatalException(isolate, try_catch);
  }
}

bool tobyJSEmit(const char* name, const char* value) {
  async_req* req = new async_req;
  req->req.data = req;
  req->isolate = isolate_; // FIXME : ...

  req->name = std::string(name);  // FIXME : use pointer
  req->value = std::string(value);

  uv_queue_work(loop,
                &req->req,
                DoAsync,
                AfterAsync);
  return true;
}

static void OnMethod(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  HandleScope scope(isolate);
  //Local<Value> result = Undefined(isolate);

  auto context = isolate->GetCurrentContext();
  auto global = context->Global();

  if (args[1]->IsFunction()) {
    String::Utf8Value name(args[0]);

    Local<Function> callback = Local<Function>::Cast(args[1]);
    (*eventListeners)[std::string(*name)].Reset(isolate, callback);

    // FIXME : remove it in removeListener()
    //eventListeners[name].Reset();
  }
}

static void _tobyInit(Isolate* isolate) {
  // dummy event. do not end the loop.
  // FIXME : use uv_async_init!!
  const char* source = "const toby = process.binding('toby');"
                       "(function(){setInterval(function(){},1000)})();";

  Local<Value> result;
  HandleScope handle_scope(isolate);
  TryCatch try_catch(isolate);

  Local<Context> context(isolate->GetCurrentContext());

  Local<String> script = String::NewFromUtf8(isolate, source);
  Local<Script> compiled_script;
  if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
    String::Utf8Value error(try_catch.Exception());
    // printf("%s", *error);
    return;
  }

  if (!compiled_script->Run(context).ToLocal(&result)) {
    String::Utf8Value error(try_catch.Exception());
    // printf("%s", *error);
    return;
  }

  // result = Stringify(isolate, context, result);
  // String::Utf8Value ret(result);
}

static void atExitCB(void* arg) {
  Isolate* isolate = static_cast<Isolate*>(arg);
  HandleScope handle_scope(isolate);
  Local<Object> obj = Object::New(isolate);
  assert(!obj.IsEmpty());  // Assert VM is still alive.
  assert(obj->IsObject());
}

static void init(Local<Object> exports) {
  AtExit(atExitCB, exports->GetIsolate());

  NODE_SET_METHOD(exports, "hostCall", HostCallMethod);
  NODE_SET_METHOD(exports, "on", OnMethod);

  exports->DefineOwnProperty(
    exports->GetIsolate()->GetCurrentContext(),
    String::NewFromUtf8(exports->GetIsolate(), "version"),
    String::NewFromUtf8(exports->GetIsolate(), TOBY_VERSION),
    v8::ReadOnly).FromJust();

  // call the host's OnLoad()
  tobyOnLoad(isolate_);
}

NODE_MODULE_CONTEXT_AWARE_BUILTIN(toby, init)

static void _node(const char* processName, const char* userScript) {
  // argv memory should be adjacent.
  // libuv/src/unix/proctitle.c
  int _argc = 3;
  char* _argv[_argc];

  // FIXME : leave empty string in evalScript(-e "")
  // there's a bug in OSX(such as, "bad option: -e?")
  char evalScript[] = "";
  char nodeOptions[] = "-e";

  int size = 0;
  size += strlen(processName) + 1;
  size += strlen(nodeOptions) + 1;
  size += strlen(evalScript) + 1;

  char* buf = new char[size];
  int i = 0;

  _argv[0] = buf+i;
  strncpy(buf+i, processName, strlen(processName));
  i += strlen(processName);
  buf[++i] = '\0';

  _argv[1] = buf+i;
  strncpy(buf+i, nodeOptions, strlen(nodeOptions));
  i += strlen(nodeOptions);
  buf[++i] = '\0';

  _argv[2] = buf+i;
  strncpy(buf+i, evalScript, strlen(evalScript));
  i += strlen(evalScript);
  buf[++i] = '\0';

  {
    using namespace std;
    using namespace node;
    using namespace v8;
    using namespace toby;

    static Platform* platform_;
    platform_ = platform::CreateDefaultPlatform();  //v8_default_thread_pool_size = 4;
    V8::InitializePlatform(platform_);
    V8::Initialize();

    Isolate::CreateParams params;
    params.array_buffer_allocator = new ArrayBufferAllocator();

    isolate_ = Isolate::New(params);
    // // FIXME : check isolate is null or not
    // if (isolate == nullptr)
    //   return 12;  // Signal internal error.

    Locker locker(isolate_);
    Isolate::Scope isolate_scope(isolate_);
    HandleScope handle_scope(isolate_);
    static Local<Context> context = Context::New(isolate_);
    Context::Scope context_scope(context);

    int exec_argc;
    const char** exec_argv;
    Init(&_argc, const_cast<const char**>(_argv), &exec_argc, &exec_argv);

    static Environment* env = CreateEnvironment(
        isolate_, loop, context, _argc, _argv, exec_argc, exec_argv);

    LoadEnvironment(env);

    /* inject the toby script / user script here */
    toby::_tobyInit(isolate_);
    {
      // 'node/lib/internal/bootstrap_node.js' is lazily loaded.
      std::string source = "";
      source += "process.nextTick(function() {";
      source += userScript;
      source += "});";

      Isolate* isolate = static_cast<Isolate*>(isolate_);
      Local<Value> result;

      HandleScope handle_scope(isolate);
      TryCatch try_catch(isolate);

      Local<Context> context(isolate->GetCurrentContext());

      Local<String> script = String::NewFromUtf8(isolate, source.c_str());
      Local<Script> compiled_script;
      if (!Script::Compile(context, script).ToLocal(&compiled_script)) {
        String::Utf8Value error(try_catch.Exception());
        fprintf(stderr, "Fatal Error in userScript\n%s", *error);
        fflush(stderr);
        return ;
      }

      if (!compiled_script->Run(context).ToLocal(&result)) {
        String::Utf8Value error(try_catch.Exception());
        fprintf(stderr, "Fatal Error in userScript\n%s", *error);
        fflush(stderr);
        return ;
      }
    }
    /* */

    bool more;
    do {
      platform::PumpMessageLoop(platform_, isolate_);
      more = uv_run(loop, UV_RUN_ONCE);
      if (more == false) {
        platform::PumpMessageLoop(platform_, isolate_);
        EmitBeforeExit(env);

        more = uv_loop_alive(loop);
        if (uv_run(loop, UV_RUN_NOWAIT) != 0)
          more = true;
      }
    } while (more == true);

    // FIXME : not reached
    // due to 'setInterval(function(){},1000)' in _tobyInit
    const int exit_code = EmitExit(env);
    RunAtExit(env);
    //return exit_code;
  }
}

void _tobyRegister() {
  // FIXME : need to find out another way
  //         more info on http://dbp-consulting.com/tutorials/debugging/linuxProgramStartup.html
  // workaround patch for freepascal. un-comment below line.
  // the toby module won't be registered due to over-writting '__libc_csu_init'
  // in freepascal/rtl/linux/x86_64/cprt0.as
  //   movq __libc_csu_init@GOTPCREL(%rip), %rcx
  toby::_register_toby();
}

void tobyInit(const char* processName,
              const char* userScript,
              TobyOnloadCallback _tobyOnLoad,
              TobyHostCallCallback _tobyHostCall) {
  // initialized the eventListeners map
  eventListeners = new Callback;

  // set the default event loop.
  loop = uv_default_loop();

  toby::tobyOnLoad = _tobyOnLoad;
  toby::tobyHostCall = _tobyHostCall;

  // start the node.js in a thread
  std::thread n(_node, processName, userScript);
  n.detach();
}

}  // namespace toby<|endoftext|>