<commit_before>//===- lib/Runtimes/Tracer/WrapCstdio_h.cpp -------------------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "Tracer.hpp"

#include "seec/RuntimeErrors/FormatSelects.hpp"
#include "seec/Runtimes/MangleFunction.h"
#include "seec/Trace/DetectCalls.hpp"
#include "seec/Trace/ScanFormatSpecifiers.hpp"
#include "seec/Trace/TraceThreadListener.hpp"
#include "seec/Trace/TraceThreadMemCheck.hpp"
#include "seec/Util/ScopeExit.hpp"

#include "llvm/Support/CallSite.h"

#include <cctype>
#include <cstdio>

extern "C" {

//===----------------------------------------------------------------------===//
// scanf, fscanf, sscanf
//===----------------------------------------------------------------------===//

/// \brief Attempt to match a sequence of literal characters.
///
bool matchNonConversionCharacters(int &CharactersRead,
                                  FILE *Stream,
                                  char const *Start,
                                  char const *End = nullptr)
{
  while(*Start && (End == nullptr || Start < End)) {
    if (std::isspace(*Start)) {
      // Match all whitespace characters.
      int ReadChar;

      while ((ReadChar = std::fgetc(Stream)) != EOF) {
        if (!std::isspace(ReadChar)) {
          std::ungetc(ReadChar, Stream);
          break;
        }

        ++CharactersRead;
      }
    }
    else {
      // Match a single character.
      int ReadChar = std::fgetc(Stream);
      if (ReadChar == EOF)
        return false;

      if (ReadChar != *Start) {
        std::ungetc(ReadChar, Stream);
        return false;
      }

      ++CharactersRead;
    }

    ++Start;
  }

  return true;
}

// ParseInt

// ParseFloat

// ParseString

// ParseCharacterSet

static int
checkStreamScan(seec::runtime_errors::format_selects::CStdFunction FSFunction,
                unsigned VarArgsStartIndex,
                FILE *Stream,
                char const *Format)
{
  using namespace seec::trace;

  auto &ThreadEnv = getThreadEnvironment();
  auto &Listener = ThreadEnv.getThreadListener();
  auto Instruction = ThreadEnv.getInstruction();
  auto InstructionIndex = ThreadEnv.getInstructionIndex();
  auto Call = llvm::CallSite(Instruction);
  assert(Call && "expected call or invoke instruction.");

  // Interace with the thread listener's notification system.
  Listener.enterNotification();
  auto DoExit = seec::scopeExit([&](){ Listener.exitPostNotification(); });

  // Use a VarArgList to access our arguments.
  detect_calls::VarArgList<TraceThreadListener>
    VarArgs{Listener, Call, VarArgsStartIndex};

  // Lock IO streams and global memory.
  Listener.acquireGlobalMemoryWriteLock();
  auto StreamsAccessor = Listener.getProcessListener().getStreamsAccessor();

  // Use a CIOChecker to help check memory.
  CIOChecker Checker{Listener,
                     InstructionIndex,
                     FSFunction,
                     StreamsAccessor.getObject()};

  // Check that the stream is valid.
  if (Stream == stdin || Stream == stdout || Stream == stderr) {
    Checker.checkStandardStreamIsValid(Stream);
  }
  else {
    Checker.checkStreamIsValid(0, Stream);
  }

  // Check and perform the (f)scanf.
  auto FormatSize = Checker.checkCStringRead(VarArgsStartIndex - 1, Format);
  if (!FormatSize)
    return 0;

  int Result = 0;
  int NumCharsRead = 0;
  unsigned NextArg = 0;
  char const *NextChar = Format;

  while (true) {
    auto Conversion = ScanConversionSpecifier::readNextFrom(NextChar);
    if (!Conversion.Start) {
      // Attempt to match and consume remaining characters.
      matchNonConversionCharacters(NumCharsRead, Stream, NextChar);
      break;
    }

    // Attempt to match and consume [NextChar, Conversion.Start).
    if (!matchNonConversionCharacters(NumCharsRead, Stream, NextChar,
                                      Conversion.Start))
      break;

    auto const StartIndex = Conversion.Start - Format;

    // Ensure that the conversion specifier was parsed correctly.
    if (!Conversion.End) {
      Listener.handleRunError(
        seec::runtime_errors::createRunError
          <seec::runtime_errors::RunErrorType::FormatSpecifierParse>
          (FSFunction, VarArgsStartIndex - 1, StartIndex),
        RunErrorSeverity::Fatal,
        InstructionIndex);
      return false;
    }

    auto const EndIndex = Conversion.End - Format;

    // If a precision was specified, ensure that precision is allowed.
    if (Conversion.SuppressAssignment
        && !Conversion.allowedSuppressAssignment()) {
      Listener.handleRunError(
        seec::runtime_errors::createRunError
          <seec::runtime_errors::RunErrorType::FormatSpecifierSuppressionDenied>
          (FSFunction, VarArgsStartIndex - 1, StartIndex, EndIndex),
        RunErrorSeverity::Fatal,
        InstructionIndex);
      return false;
    }

    // Check that the argument type matches the expected type. Don't check that
    // the argument exists here, because some conversion specifiers don't
    // require an argument (i.e. %%), so we check if it exists when needed, in
    // the isArgumentTypeOK() implementation.
    if (!Conversion.isArgumentTypeOK(VarArgs, NextArg)) {
      Listener.handleRunError(
        seec::runtime_errors::createRunError
          <seec::runtime_errors::RunErrorType::FormatSpecifierArgType>
          (FSFunction,
           VarArgsStartIndex - 1,
           StartIndex,
           EndIndex,
           asCFormatLengthModifier(Conversion.Length),
           VarArgs.offset() + NextArg),
        RunErrorSeverity::Fatal,
        InstructionIndex);
      return false;
    }

    // If the argument type is a pointer, check that the destination is
    // writable. The conversion for strings (and sets) is a special case.
    if (Conversion.Conversion == ScanConversionSpecifier::Specifier::s
        || Conversion.Conversion == ScanConversionSpecifier::Specifier::set) {
      if (NextArg < VarArgs.size()) {
        // Check that the field width is specified.
        if (!Conversion.WidthSpecified) {
          Listener.handleRunError(
            seec::runtime_errors::createRunError
              <seec::runtime_errors::RunErrorType::FormatSpecifierWidthMissing>
              (FSFunction,
               VarArgsStartIndex - 1,
               StartIndex,
               EndIndex),
            RunErrorSeverity::Fatal,
            InstructionIndex);
          return false;
        }

        // Check that the destination is writable and has sufficient space.
        auto MaybePointeeArea = Conversion.getArgumentPointee(VarArgs, NextArg);
        if (!MaybePointeeArea.assigned()) {
          llvm_unreachable("unassigned pointee for string conversion.");
          return false;
        }

        // TODO: This width may not be correct for wchar_t *.
        if (!Checker.checkMemoryExistsAndAccessibleForParameter(
                VarArgs.offset() + NextArg,
                MaybePointeeArea.get<0>().address(),
                Conversion.Width,
                seec::runtime_errors::format_selects::MemoryAccess::Write))
          return false;
      }
    }
    else {
      auto MaybePointeeArea = Conversion.getArgumentPointee(VarArgs, NextArg);
      if (MaybePointeeArea.assigned()) {
        auto Area = MaybePointeeArea.get<0>();
        Checker.checkMemoryExistsAndAccessibleForParameter(
          VarArgs.offset() + NextArg,
          Area.address(),
          Area.length(),
          seec::runtime_errors::format_selects::MemoryAccess::Write);
      }
    }

    bool ConversionSuccessful = true;

    switch (Conversion.Conversion) {
      case ScanConversionSpecifier::Specifier::none:
        llvm_unreachable("encountered scan conversion specifier \"none\"");
        break;

      case ScanConversionSpecifier::Specifier::percent:
        if (!matchNonConversionCharacters(NumCharsRead,
                                          Stream,
                                          Conversion.End - 1,
                                          Conversion.End)) {
          ConversionSuccessful = false;
        }
        break;

      case ScanConversionSpecifier::Specifier::c:
        // Read a single char.
        if (Conversion.Length == LengthModifier::none) {
          if (Conversion.SuppressAssignment) {
            if (std::fscanf(Stream, "%*c") == EOF)
              ConversionSuccessful = false;
          }
          else {
            auto Ptr = VarArgs.getAs<char *>(NextArg).get<0>();
            if (std::fscanf(Stream, "%c", Ptr) == 1)
              ++Result;
            else
              ConversionSuccessful = false;
          }
        }
        else if (Conversion.Length == LengthModifier::l) {
          if (Conversion.SuppressAssignment) {
            if (std::fscanf(Stream, "&*lc") == EOF)
              ConversionSuccessful = false;
          }
          else {
            auto Ptr = VarArgs.getAs<wchar_t *>(NextArg).get<0>();
            if (std::fscanf(Stream, "%lc", Ptr) == 1)
              ++Result;
            else
              ConversionSuccessful = false;
          }
        }
        else {
          ConversionSuccessful = false;
        }
        break;

      case ScanConversionSpecifier::Specifier::s:
        // TODO: Read string.
        break;

      case ScanConversionSpecifier::Specifier::set:
        // TODO: Read set.
        break;

      case ScanConversionSpecifier::Specifier::u:
        // TODO: Read unsigned integer.
        break;
      case ScanConversionSpecifier::Specifier::d:
      case ScanConversionSpecifier::Specifier::i:
      case ScanConversionSpecifier::Specifier::o:
      case ScanConversionSpecifier::Specifier::x:
        // TODO: Read integer.
        break;

      case ScanConversionSpecifier::Specifier::n:
        if (!Conversion.SuppressAssignment) {
          ConversionSuccessful
            = Conversion.assignPointee(VarArgs, NextArg, NumCharsRead);
        }
        break;

      case ScanConversionSpecifier::Specifier::a:
      case ScanConversionSpecifier::Specifier::A:
      case ScanConversionSpecifier::Specifier::e:
      case ScanConversionSpecifier::Specifier::E:
      case ScanConversionSpecifier::Specifier::f:
      case ScanConversionSpecifier::Specifier::F:
      case ScanConversionSpecifier::Specifier::g:
      case ScanConversionSpecifier::Specifier::G:
        // TODO: Read float.
        break;

      case ScanConversionSpecifier::Specifier::p:
        // Read pointer.
        if (Conversion.SuppressAssignment) {
          if (std::fscanf(Stream, "%*p") == EOF)
            ConversionSuccessful = false;
        }
        else {
          auto Ptr = VarArgs.getAs<void **>(NextArg).get<0>();
          if (std::fscanf(Stream, "%p", Ptr) == 1)
            ++Result;
          else
            ConversionSuccessful = false;
        }
        break;
    }

    if (!ConversionSuccessful)
      break;

    // Move to the next argument (unless this conversion specifier doesn't
    // consume an argument).
    if (Conversion.Conversion != ScanConversionSpecifier::Specifier::percent
        && Conversion.SuppressAssignment == false) {
      ++NextArg;
    }

    // The next position to search from should be the first character following
    // this conversion specifier.
    NextChar = Conversion.End;
  }

  // Record the produced value.
  Listener.notifyValue(InstructionIndex, Instruction, unsigned(Result));

  if (NextChar == Format && (std::feof(Stream) || std::ferror(Stream)))
    return EOF;

  return Result;
}

int
SEEC_MANGLE_FUNCTION(scanf)
(char const *Format, ...)
{
  return checkStreamScan
            (seec::runtime_errors::format_selects::CStdFunction::scanf,
             1,
             stdin,
             Format);
}

int
SEEC_MANGLE_FUNCTION(fscanf)
(FILE *Stream, char const *Format, ...)
{
  return checkStreamScan
            (seec::runtime_errors::format_selects::CStdFunction::fscanf,
             2,
             Stream,
             Format);
}

int
SEEC_MANGLE_FUNCTION(sscanf)
(char const *Buffer, char const *Format, ...)
{
  auto &ThreadEnv = seec::trace::getThreadEnvironment();
  auto &Listener = ThreadEnv.getThreadListener();
  auto Call = llvm::CallSite(ThreadEnv.getInstruction());
  assert(Call && "expected call or invoke instruction.");

  // TODO: Check and do.

  // Do.
  llvm_unreachable("sscanf: not implemented.");

  // Record.

  // Return.
  return 0;
}

} // extern "C"
<commit_msg>Don't attempt assignment when there are insufficient variadic arguments for checkStreamScan().<commit_after>//===- lib/Runtimes/Tracer/WrapCstdio_h.cpp -------------------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "Tracer.hpp"

#include "seec/RuntimeErrors/FormatSelects.hpp"
#include "seec/Runtimes/MangleFunction.h"
#include "seec/Trace/DetectCalls.hpp"
#include "seec/Trace/ScanFormatSpecifiers.hpp"
#include "seec/Trace/TraceThreadListener.hpp"
#include "seec/Trace/TraceThreadMemCheck.hpp"
#include "seec/Util/ScopeExit.hpp"

#include "llvm/Support/CallSite.h"

#include <cctype>
#include <cstdio>

extern "C" {

//===----------------------------------------------------------------------===//
// scanf, fscanf, sscanf
//===----------------------------------------------------------------------===//

/// \brief Attempt to match a sequence of literal characters.
///
bool matchNonConversionCharacters(int &CharactersRead,
                                  FILE *Stream,
                                  char const *Start,
                                  char const *End = nullptr)
{
  while(*Start && (End == nullptr || Start < End)) {
    if (std::isspace(*Start)) {
      // Match all whitespace characters.
      int ReadChar;

      while ((ReadChar = std::fgetc(Stream)) != EOF) {
        if (!std::isspace(ReadChar)) {
          std::ungetc(ReadChar, Stream);
          break;
        }

        ++CharactersRead;
      }
    }
    else {
      // Match a single character.
      int ReadChar = std::fgetc(Stream);
      if (ReadChar == EOF)
        return false;

      if (ReadChar != *Start) {
        std::ungetc(ReadChar, Stream);
        return false;
      }

      ++CharactersRead;
    }

    ++Start;
  }

  return true;
}

// ParseInt

// ParseFloat

// ParseString

// ParseCharacterSet

static int
checkStreamScan(seec::runtime_errors::format_selects::CStdFunction FSFunction,
                unsigned VarArgsStartIndex,
                FILE *Stream,
                char const *Format)
{
  using namespace seec::trace;

  auto &ThreadEnv = getThreadEnvironment();
  auto &Listener = ThreadEnv.getThreadListener();
  auto Instruction = ThreadEnv.getInstruction();
  auto InstructionIndex = ThreadEnv.getInstructionIndex();
  auto Call = llvm::CallSite(Instruction);
  assert(Call && "expected call or invoke instruction.");

  // Interace with the thread listener's notification system.
  Listener.enterNotification();
  auto DoExit = seec::scopeExit([&](){ Listener.exitPostNotification(); });

  // Use a VarArgList to access our arguments.
  detect_calls::VarArgList<TraceThreadListener>
    VarArgs{Listener, Call, VarArgsStartIndex};

  // Lock IO streams and global memory.
  Listener.acquireGlobalMemoryWriteLock();
  auto StreamsAccessor = Listener.getProcessListener().getStreamsAccessor();

  // Use a CIOChecker to help check memory.
  CIOChecker Checker{Listener,
                     InstructionIndex,
                     FSFunction,
                     StreamsAccessor.getObject()};

  // Check that the stream is valid.
  if (Stream == stdin || Stream == stdout || Stream == stderr) {
    Checker.checkStandardStreamIsValid(Stream);
  }
  else {
    Checker.checkStreamIsValid(0, Stream);
  }

  // Check and perform the (f)scanf.
  auto FormatSize = Checker.checkCStringRead(VarArgsStartIndex - 1, Format);
  if (!FormatSize)
    return 0;

  int Result = 0;
  int NumCharsRead = 0;
  unsigned NextArg = 0;
  char const *NextChar = Format;

  while (true) {
    auto Conversion = ScanConversionSpecifier::readNextFrom(NextChar);
    if (!Conversion.Start) {
      // Attempt to match and consume remaining characters.
      matchNonConversionCharacters(NumCharsRead, Stream, NextChar);
      break;
    }

    // Attempt to match and consume [NextChar, Conversion.Start).
    if (!matchNonConversionCharacters(NumCharsRead, Stream, NextChar,
                                      Conversion.Start))
      break;

    auto const StartIndex = Conversion.Start - Format;

    // Ensure that the conversion specifier was parsed correctly.
    if (!Conversion.End) {
      Listener.handleRunError(
        seec::runtime_errors::createRunError
          <seec::runtime_errors::RunErrorType::FormatSpecifierParse>
          (FSFunction, VarArgsStartIndex - 1, StartIndex),
        RunErrorSeverity::Fatal,
        InstructionIndex);
      return Result;
    }

    auto const EndIndex = Conversion.End - Format;

    // If assignment was suppressed, ensure that suppressing assignment is OK.
    if (Conversion.SuppressAssignment) {
      if (!Conversion.allowedSuppressAssignment()) {
        Listener.handleRunError(
          seec::runtime_errors::createRunError
          <seec::runtime_errors::RunErrorType::FormatSpecifierSuppressionDenied>
            (FSFunction, VarArgsStartIndex - 1, StartIndex, EndIndex),
          RunErrorSeverity::Fatal,
          InstructionIndex);
        return Result;
      }
    }
    else {
      // Check that the argument type matches the expected type. Don't check
      // that the argument exists here, because some conversion specifiers don't
      // require an argument (i.e. %%), so we check if it exists when needed, in
      // the isArgumentTypeOK() implementation.
      if (!Conversion.isArgumentTypeOK(VarArgs, NextArg)) {
        Listener.handleRunError(
          seec::runtime_errors::createRunError
            <seec::runtime_errors::RunErrorType::FormatSpecifierArgType>
            (FSFunction,
             VarArgsStartIndex - 1,
             StartIndex,
             EndIndex,
             asCFormatLengthModifier(Conversion.Length),
             VarArgs.offset() + NextArg),
          RunErrorSeverity::Fatal,
          InstructionIndex);
        return Result;
      }

      // If the argument type is a pointer, check that the destination is
      // writable. The conversion for strings (and sets) is a special case.
      if (Conversion.Conversion == ScanConversionSpecifier::Specifier::s
          || Conversion.Conversion == ScanConversionSpecifier::Specifier::set) {
        if (NextArg < VarArgs.size()) {
          // Check that the field width is specified.
          if (!Conversion.WidthSpecified) {
            Listener.handleRunError(
              seec::runtime_errors::createRunError
                <seec::runtime_errors::RunErrorType::FormatSpecifierWidthMissing>
                (FSFunction,
                 VarArgsStartIndex - 1,
                 StartIndex,
                 EndIndex),
              RunErrorSeverity::Warning,
              InstructionIndex);
          }
          else {
            // Check that the destination is writable and has sufficient space
            // for the field width specified by the programmer.
            auto MaybeArea = Conversion.getArgumentPointee(VarArgs, NextArg);

            if (!Checker.checkMemoryExistsAndAccessibleForParameter(
                    VarArgs.offset() + NextArg,
                    MaybeArea.get<0>().address(),
                    Conversion.Width,
                    seec::runtime_errors::format_selects::MemoryAccess::Write))
              return false;
          }
        }
      }
      else {
        auto MaybePointeeArea = Conversion.getArgumentPointee(VarArgs, NextArg);
        if (MaybePointeeArea.assigned()) {
          auto Area = MaybePointeeArea.get<0>();
          Checker.checkMemoryExistsAndAccessibleForParameter(
            VarArgs.offset() + NextArg,
            Area.address(),
            Area.length(),
            seec::runtime_errors::format_selects::MemoryAccess::Write);
        }
      }
    }

    bool ConversionSuccessful = true;

    switch (Conversion.Conversion) {
      case ScanConversionSpecifier::Specifier::none:
        llvm_unreachable("encountered scan conversion specifier \"none\"");
        break;

      case ScanConversionSpecifier::Specifier::percent:
        if (!matchNonConversionCharacters(NumCharsRead,
                                          Stream,
                                          Conversion.End - 1,
                                          Conversion.End)) {
          ConversionSuccessful = false;
        }
        break;

      case ScanConversionSpecifier::Specifier::c:
        // Read a single char.
        if (Conversion.Length == LengthModifier::none) {
          if (Conversion.SuppressAssignment || NextArg >= VarArgs.size()) {
            if (std::fscanf(Stream, "%*c") == EOF)
              ConversionSuccessful = false;
          }
          else {
            auto Ptr = VarArgs.getAs<char *>(NextArg).get<0>();
            if (std::fscanf(Stream, "%c", Ptr) == 1)
              ++Result;
            else
              ConversionSuccessful = false;
          }
        }
        else if (Conversion.Length == LengthModifier::l) {
          if (Conversion.SuppressAssignment || NextArg >= VarArgs.size()) {
            if (std::fscanf(Stream, "&*lc") == EOF)
              ConversionSuccessful = false;
          }
          else {
            auto Ptr = VarArgs.getAs<wchar_t *>(NextArg).get<0>();
            if (std::fscanf(Stream, "%lc", Ptr) == 1)
              ++Result;
            else
              ConversionSuccessful = false;
          }
        }
        else {
          ConversionSuccessful = false;
        }
        break;

      case ScanConversionSpecifier::Specifier::s:
        // TODO: Read string.
        break;

      case ScanConversionSpecifier::Specifier::set:
        // TODO: Read set.
        break;

      case ScanConversionSpecifier::Specifier::u:
        // TODO: Read unsigned integer.
        break;
      case ScanConversionSpecifier::Specifier::d:
      case ScanConversionSpecifier::Specifier::i:
      case ScanConversionSpecifier::Specifier::o:
      case ScanConversionSpecifier::Specifier::x:
        // TODO: Read integer.
        break;

      case ScanConversionSpecifier::Specifier::n:
        if (!Conversion.SuppressAssignment) {
          ConversionSuccessful
            = Conversion.assignPointee(VarArgs, NextArg, NumCharsRead);
        }
        break;

      case ScanConversionSpecifier::Specifier::a:
      case ScanConversionSpecifier::Specifier::A:
      case ScanConversionSpecifier::Specifier::e:
      case ScanConversionSpecifier::Specifier::E:
      case ScanConversionSpecifier::Specifier::f:
      case ScanConversionSpecifier::Specifier::F:
      case ScanConversionSpecifier::Specifier::g:
      case ScanConversionSpecifier::Specifier::G:
        // TODO: Read float.
        break;

      case ScanConversionSpecifier::Specifier::p:
        // Read pointer.
        if (Conversion.SuppressAssignment || NextArg >= VarArgs.size()) {
          if (std::fscanf(Stream, "%*p") == EOF)
            ConversionSuccessful = false;
        }
        else {
          auto Ptr = VarArgs.getAs<void **>(NextArg).get<0>();
          if (std::fscanf(Stream, "%p", Ptr) == 1)
            ++Result;
          else
            ConversionSuccessful = false;
        }
        break;
    }

    if (!ConversionSuccessful)
      break;

    // Move to the next argument (unless this conversion specifier doesn't
    // consume an argument).
    if (Conversion.Conversion != ScanConversionSpecifier::Specifier::percent
        && Conversion.SuppressAssignment == false) {
      ++NextArg;
    }

    // The next position to search from should be the first character following
    // this conversion specifier.
    NextChar = Conversion.End;
  }

  // Ensure that we got exactly the right number of arguments.
  if (NextArg > VarArgs.size()) {
    Listener.handleRunError(createRunError<RunErrorType::VarArgsInsufficient>
                                          (FSFunction,
                                           NextArg,
                                           VarArgs.size()),
                            RunErrorSeverity::Fatal,
                            InstructionIndex);
  }
  else if (NextArg < VarArgs.size()) {
    Listener.handleRunError(createRunError<RunErrorType::VarArgsSuperfluous>
                                          (FSFunction,
                                           NextArg,
                                           VarArgs.size()),
                            RunErrorSeverity::Warning,
                            InstructionIndex);
  }

  // Record the produced value.
  Listener.notifyValue(InstructionIndex, Instruction, unsigned(Result));

  if (NextChar == Format && (std::feof(Stream) || std::ferror(Stream)))
    return EOF;

  return Result;
}

int
SEEC_MANGLE_FUNCTION(scanf)
(char const *Format, ...)
{
  return checkStreamScan
            (seec::runtime_errors::format_selects::CStdFunction::scanf,
             1,
             stdin,
             Format);
}

int
SEEC_MANGLE_FUNCTION(fscanf)
(FILE *Stream, char const *Format, ...)
{
  return checkStreamScan
            (seec::runtime_errors::format_selects::CStdFunction::fscanf,
             2,
             Stream,
             Format);
}

int
SEEC_MANGLE_FUNCTION(sscanf)
(char const *Buffer, char const *Format, ...)
{
  auto &ThreadEnv = seec::trace::getThreadEnvironment();
  auto &Listener = ThreadEnv.getThreadListener();
  auto Call = llvm::CallSite(ThreadEnv.getInstruction());
  assert(Call && "expected call or invoke instruction.");

  // TODO: Check and do.

  // Do.
  llvm_unreachable("sscanf: not implemented.");

  // Record.

  // Return.
  return 0;
}

} // extern "C"
<|endoftext|>


<commit_before>//===- ConstantMerge.cpp - Merge duplicate global constants -----------------=//
//
// This file defines the interface to a pass that merges duplicate global
// constants together into a single constant that is shared.  This is useful
// because some passes (ie TraceValues) insert a lot of string constants into
// the program, regardless of whether or not an existing string is available.
//
// Algorithm: ConstantMerge is designed to build up a map of available constants
// and eliminate duplicates when it is initialized.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/IPO.h"
#include "llvm/Module.h"
#include "llvm/Constants.h"
#include "llvm/Pass.h"
#include "Support/Statistic.h"

namespace {
  struct ConstantMerge : public Pass {
    // run - For this pass, process all of the globals in the module,
    // eliminating duplicate constants.
    //
    bool run(Module &M);

  private:
    void replaceUsesOfWith(GlobalVariable *Old, GlobalVariable *New);
    void replaceConstantWith(Constant *Old, Constant *New);
  };

  Statistic<> NumMerged("constmerge", "Number of global constants merged");
  RegisterOpt<ConstantMerge> X("constmerge","Merge Duplicate Global Constants");
}

Pass *createConstantMergePass() { return new ConstantMerge(); }

bool ConstantMerge::run(Module &M) {
  std::map<Constant*, GlobalVariable*> CMap;
  bool MadeChanges = false;

  for (Module::giterator GV = M.gbegin(), E = M.gend(); GV != E; ++GV)
    if (GV->isConstant()) {  // Only process constants
      assert(GV->hasInitializer() && "Globals constants must have inits!");
      Constant *Init = GV->getInitializer();

      // Check to see if the initializer is already known...
      std::map<Constant*, GlobalVariable*>::iterator I = CMap.find(Init);

      if (I == CMap.end()) {    // Nope, add it to the map
        CMap.insert(I, std::make_pair(Init, GV));
      } else {                  // Yup, this is a duplicate!
        // Make all uses of the duplicate constant use the cannonical version...
        replaceUsesOfWith(GV, I->second);

        // Delete the global value from the module... and back up iterator to
        // not skip the next global...
        GV = --M.getGlobalList().erase(GV);

        ++NumMerged;
        MadeChanges = true;
      }
    }

  return MadeChanges;
}

/// replaceUsesOfWith - Replace all uses of Old with New.  For instructions,
/// this is a really simple matter of replacing the reference to Old with a
/// reference to New.  For constants references, however, we must carefully
/// build replacement constants to substitute in.
///
void ConstantMerge::replaceUsesOfWith(GlobalVariable *Old, GlobalVariable *New){
  while (!Old->use_empty()) {
    User *U = Old->use_back();
    if (ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(U))
      replaceConstantWith(CPR, ConstantPointerRef::get(New));
    else
      U->replaceUsesOfWith(Old, New);
  }
}

/// replaceWith - Ok, so we have a constant 'Old' and we want to replace it with
/// 'New'.  To do this, we have to recursively go through the uses of Old,
/// replacing them with new things.  The problem is that if a constant uses Old,
/// then we need to replace the uses of the constant with uses of the equivalent
/// constant that uses New instead.
///
void ConstantMerge::replaceConstantWith(Constant *Old, Constant *New) {
  while (!Old->use_empty()) {
    User *U = Old->use_back();

    if (Constant *C = dyn_cast<Constant>(U)) {
      Constant *Replacement = 0;

      // Depending on the type of constant, build a suitable replacement...
      if (ConstantExpr *CE = dyn_cast<ConstantExpr>(C)) {
        if (CE->getOpcode() == Instruction::GetElementPtr) {
          std::vector<Constant*> Indices;
          Constant *Pointer = cast<Constant>(CE->getOperand(0));
          Indices.reserve(CE->getNumOperands()-1);
          if (Pointer == Old) Pointer = New;

          for (unsigned i = 1, e = CE->getNumOperands(); i != e; ++i) {
            Constant *Val = cast<Constant>(CE->getOperand(i));
            if (Val == Old) Val = New;
            Indices.push_back(Val);
          }
          Replacement = ConstantExpr::getGetElementPtr(Pointer, Indices);
        } else if (CE->getOpcode() == Instruction::Cast) {
          assert(CE->getOperand(0) == Old && "Cast only has one use!");
          Replacement = ConstantExpr::getCast(New, CE->getType());
        } else if (CE->getNumOperands() == 2) {
          Constant *C1 = cast<Constant>(CE->getOperand(0));
          Constant *C2 = cast<Constant>(CE->getOperand(1));
          if (C1 == Old) C1 = New;
          if (C2 == Old) C2 = New;
          Replacement = ConstantExpr::get(CE->getOpcode(), C1, C2);
        } else {
          assert(0 && "Unknown ConstantExpr type!");
        }

      } else if (ConstantArray *CA = dyn_cast<ConstantArray>(C)) {
        std::vector<Constant*> Values;
        Values.reserve(CA->getValues().size());
        for (unsigned i = 0, e = CA->getValues().size(); i != e; ++i) {
          Constant *Val = cast<Constant>(CA->getValues()[i]);
          if (Val == Old) Val = New;
          Values.push_back(Val);
        }

        Replacement = ConstantArray::get(CA->getType(), Values);
      } else if (ConstantStruct *CS = dyn_cast<ConstantStruct>(C)) {
        std::vector<Constant*> Values;
        Values.reserve(CS->getValues().size());

        for (unsigned i = 0, e = CS->getValues().size(); i != e; ++i) {
          Constant *Val = cast<Constant>(CS->getValues()[i]);
          if (Val == Old) Val = New;
          Values.push_back(Val);
        }

        Replacement = ConstantStruct::get(CS->getType(), Values);
      } else {
        assert(0 && "Unexpected/unknown constant type!");
      }

      // Now that we have a suitable replacement, recursively eliminate C.
      replaceConstantWith(C, Replacement);

    } else {
      // If it is not a constant, we can simply replace uses of Old with New.
      U->replaceUsesOfWith(Old, New);
    }

  }

  // No-one refers to this old dead constant now, destroy it!
  Old->destroyConstant();
}
<commit_msg>  - Dramatically simplify the ConstantMerge code now that     Value::replaceAllUsesWith works with constants correctly.<commit_after>//===- ConstantMerge.cpp - Merge duplicate global constants -----------------=//
//
// This file defines the interface to a pass that merges duplicate global
// constants together into a single constant that is shared.  This is useful
// because some passes (ie TraceValues) insert a lot of string constants into
// the program, regardless of whether or not an existing string is available.
//
// Algorithm: ConstantMerge is designed to build up a map of available constants
// and eliminate duplicates when it is initialized.
//
//===----------------------------------------------------------------------===//

#include "llvm/Transforms/IPO.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "Support/Statistic.h"

namespace {
  Statistic<> NumMerged("constmerge", "Number of global constants merged");

  struct ConstantMerge : public Pass {
    // run - For this pass, process all of the globals in the module,
    // eliminating duplicate constants.
    //
    bool run(Module &M);
  };

  RegisterOpt<ConstantMerge> X("constmerge","Merge Duplicate Global Constants");
}

Pass *createConstantMergePass() { return new ConstantMerge(); }

bool ConstantMerge::run(Module &M) {
  std::map<Constant*, GlobalVariable*> CMap;
  bool MadeChanges = false;

  for (Module::giterator GV = M.gbegin(), E = M.gend(); GV != E; ++GV)
    if (GV->isConstant()) {  // Only process constants
      assert(GV->hasInitializer() && "Globals constants must have inits!");
      Constant *Init = GV->getInitializer();

      // Check to see if the initializer is already known...
      std::map<Constant*, GlobalVariable*>::iterator I = CMap.find(Init);

      if (I == CMap.end()) {    // Nope, add it to the map
        CMap.insert(I, std::make_pair(Init, GV));
      } else {                  // Yup, this is a duplicate!
        // Make all uses of the duplicate constant use the cannonical version...
        GV->replaceAllUsesWith(I->second);

        // Delete the global value from the module... and back up iterator to
        // not skip the next global...
        GV = --M.getGlobalList().erase(GV);

        ++NumMerged;
        MadeChanges = true;
      }
    }

  return MadeChanges;
}
<|endoftext|>


<commit_before>// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/base/bits.h"
#include "src/base/macros.h"
#include "testing/gtest-support.h"

#ifdef DEBUG
#define DISABLE_IN_RELEASE(Name) Name
#else
#define DISABLE_IN_RELEASE(Name) DISABLED_##Name
#endif

namespace v8 {
namespace base {
namespace bits {

TEST(Bits, CountPopulation32) {
  EXPECT_EQ(0u, CountPopulation32(0));
  EXPECT_EQ(1u, CountPopulation32(1));
  EXPECT_EQ(8u, CountPopulation32(0x11111111));
  EXPECT_EQ(16u, CountPopulation32(0xf0f0f0f0));
  EXPECT_EQ(24u, CountPopulation32(0xfff0f0ff));
  EXPECT_EQ(32u, CountPopulation32(0xffffffff));
}

TEST(Bits, CountLeadingZeros32) {
  EXPECT_EQ(32u, CountLeadingZeros32(0));
  EXPECT_EQ(31u, CountLeadingZeros32(1));
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(31u - shift, CountLeadingZeros32(1u << shift));
  }
  EXPECT_EQ(4u, CountLeadingZeros32(0x0f0f0f0f));
}

TEST(Bits, CountTrailingZeros32) {
  EXPECT_EQ(32u, CountTrailingZeros32(0));
  EXPECT_EQ(31u, CountTrailingZeros32(0x80000000));
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(shift, CountTrailingZeros32(1u << shift));
  }
  EXPECT_EQ(4u, CountTrailingZeros32(0xf0f0f0f0));
}

TEST(Bits, IsPowerOfTwo32) {
  EXPECT_FALSE(IsPowerOfTwo32(0U));
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_TRUE(IsPowerOfTwo32(1U << shift));
    EXPECT_FALSE(IsPowerOfTwo32((1U << shift) + 5U));
    EXPECT_FALSE(IsPowerOfTwo32(~(1U << shift)));
  }
  TRACED_FORRANGE(uint32_t, shift, 2, 31) {
    EXPECT_FALSE(IsPowerOfTwo32((1U << shift) - 1U));
  }
  EXPECT_FALSE(IsPowerOfTwo32(0xffffffff));
}

TEST(Bits, IsPowerOfTwo64) {
  EXPECT_FALSE(IsPowerOfTwo64(0U));
  TRACED_FORRANGE(uint32_t, shift, 0, 63) {
    EXPECT_TRUE(IsPowerOfTwo64(V8_UINT64_C(1) << shift));
    EXPECT_FALSE(IsPowerOfTwo64((V8_UINT64_C(1) << shift) + 5U));
    EXPECT_FALSE(IsPowerOfTwo64(~(V8_UINT64_C(1) << shift)));
  }
  TRACED_FORRANGE(uint32_t, shift, 2, 63) {
    EXPECT_FALSE(IsPowerOfTwo64((V8_UINT64_C(1) << shift) - 1U));
  }
  EXPECT_FALSE(IsPowerOfTwo64(V8_UINT64_C(0xffffffffffffffff)));
}

TEST(Bits, RoundUpToPowerOfTwo32) {
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(1u << shift, RoundUpToPowerOfTwo32(1u << shift));
  }
  EXPECT_EQ(0u, RoundUpToPowerOfTwo32(0));
  EXPECT_EQ(4u, RoundUpToPowerOfTwo32(3));
  EXPECT_EQ(0x80000000u, RoundUpToPowerOfTwo32(0x7fffffffu));
}

TEST(BitsDeathTest, DISABLE_IN_RELEASE(RoundUpToPowerOfTwo32)) {
  ASSERT_DEATH({ RoundUpToPowerOfTwo32(0x80000001u); }, "0x80000000");
}

TEST(Bits, RoundDownToPowerOfTwo32) {
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(1u << shift, RoundDownToPowerOfTwo32(1u << shift));
  }
  EXPECT_EQ(0u, RoundDownToPowerOfTwo32(0));
  EXPECT_EQ(4u, RoundDownToPowerOfTwo32(5));
  EXPECT_EQ(0x80000000u, RoundDownToPowerOfTwo32(0x80000001u));
}

TEST(Bits, RotateRight32) {
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(0u, RotateRight32(0u, shift));
  }
  EXPECT_EQ(1u, RotateRight32(1, 0));
  EXPECT_EQ(1u, RotateRight32(2, 1));
  EXPECT_EQ(0x80000000u, RotateRight32(1, 1));
}

TEST(Bits, RotateRight64) {
  TRACED_FORRANGE(uint64_t, shift, 0, 63) {
    EXPECT_EQ(0u, RotateRight64(0u, shift));
  }
  EXPECT_EQ(1u, RotateRight64(1, 0));
  EXPECT_EQ(1u, RotateRight64(2, 1));
  EXPECT_EQ(V8_UINT64_C(0x8000000000000000), RotateRight64(1, 1));
}

}  // namespace bits
}  // namespace base
}  // namespace v8
<commit_msg>Disable unit test because of NaCL breakage after r23617.<commit_after>// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/base/bits.h"
#include "src/base/macros.h"
#include "testing/gtest-support.h"

#ifdef DEBUG
#define DISABLE_IN_RELEASE(Name) Name
#else
#define DISABLE_IN_RELEASE(Name) DISABLED_##Name
#endif

namespace v8 {
namespace base {
namespace bits {

TEST(Bits, CountPopulation32) {
  EXPECT_EQ(0u, CountPopulation32(0));
  EXPECT_EQ(1u, CountPopulation32(1));
  EXPECT_EQ(8u, CountPopulation32(0x11111111));
  EXPECT_EQ(16u, CountPopulation32(0xf0f0f0f0));
  EXPECT_EQ(24u, CountPopulation32(0xfff0f0ff));
  EXPECT_EQ(32u, CountPopulation32(0xffffffff));
}

TEST(Bits, CountLeadingZeros32) {
  EXPECT_EQ(32u, CountLeadingZeros32(0));
  EXPECT_EQ(31u, CountLeadingZeros32(1));
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(31u - shift, CountLeadingZeros32(1u << shift));
  }
  EXPECT_EQ(4u, CountLeadingZeros32(0x0f0f0f0f));
}

TEST(Bits, CountTrailingZeros32) {
  EXPECT_EQ(32u, CountTrailingZeros32(0));
  EXPECT_EQ(31u, CountTrailingZeros32(0x80000000));
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(shift, CountTrailingZeros32(1u << shift));
  }
  EXPECT_EQ(4u, CountTrailingZeros32(0xf0f0f0f0));
}

TEST(Bits, IsPowerOfTwo32) {
  EXPECT_FALSE(IsPowerOfTwo32(0U));
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_TRUE(IsPowerOfTwo32(1U << shift));
    EXPECT_FALSE(IsPowerOfTwo32((1U << shift) + 5U));
    EXPECT_FALSE(IsPowerOfTwo32(~(1U << shift)));
  }
  TRACED_FORRANGE(uint32_t, shift, 2, 31) {
    EXPECT_FALSE(IsPowerOfTwo32((1U << shift) - 1U));
  }
  EXPECT_FALSE(IsPowerOfTwo32(0xffffffff));
}

TEST(Bits, IsPowerOfTwo64) {
  EXPECT_FALSE(IsPowerOfTwo64(0U));
  TRACED_FORRANGE(uint32_t, shift, 0, 63) {
    EXPECT_TRUE(IsPowerOfTwo64(V8_UINT64_C(1) << shift));
    EXPECT_FALSE(IsPowerOfTwo64((V8_UINT64_C(1) << shift) + 5U));
    EXPECT_FALSE(IsPowerOfTwo64(~(V8_UINT64_C(1) << shift)));
  }
  TRACED_FORRANGE(uint32_t, shift, 2, 63) {
    EXPECT_FALSE(IsPowerOfTwo64((V8_UINT64_C(1) << shift) - 1U));
  }
  EXPECT_FALSE(IsPowerOfTwo64(V8_UINT64_C(0xffffffffffffffff)));
}

TEST(Bits, RoundUpToPowerOfTwo32) {
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(1u << shift, RoundUpToPowerOfTwo32(1u << shift));
  }
  EXPECT_EQ(0u, RoundUpToPowerOfTwo32(0));
  EXPECT_EQ(4u, RoundUpToPowerOfTwo32(3));
  EXPECT_EQ(0x80000000u, RoundUpToPowerOfTwo32(0x7fffffffu));
}

#if 0
// TODO(bmeurer): Fix this on NaCL builds.
TEST(BitsDeathTest, DISABLE_IN_RELEASE(RoundUpToPowerOfTwo32)) {
  ASSERT_DEATH({ RoundUpToPowerOfTwo32(0x80000001u); }, "0x80000000");
}
#endif

TEST(Bits, RoundDownToPowerOfTwo32) {
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(1u << shift, RoundDownToPowerOfTwo32(1u << shift));
  }
  EXPECT_EQ(0u, RoundDownToPowerOfTwo32(0));
  EXPECT_EQ(4u, RoundDownToPowerOfTwo32(5));
  EXPECT_EQ(0x80000000u, RoundDownToPowerOfTwo32(0x80000001u));
}

TEST(Bits, RotateRight32) {
  TRACED_FORRANGE(uint32_t, shift, 0, 31) {
    EXPECT_EQ(0u, RotateRight32(0u, shift));
  }
  EXPECT_EQ(1u, RotateRight32(1, 0));
  EXPECT_EQ(1u, RotateRight32(2, 1));
  EXPECT_EQ(0x80000000u, RotateRight32(1, 1));
}

TEST(Bits, RotateRight64) {
  TRACED_FORRANGE(uint64_t, shift, 0, 63) {
    EXPECT_EQ(0u, RotateRight64(0u, shift));
  }
  EXPECT_EQ(1u, RotateRight64(1, 0));
  EXPECT_EQ(1u, RotateRight64(2, 1));
  EXPECT_EQ(V8_UINT64_C(0x8000000000000000), RotateRight64(1, 1));
}

}  // namespace bits
}  // namespace base
}  // namespace v8
<|endoftext|>


<commit_before>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/tbmcsa_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <threads/atomic_incumbent.hh>
#include <threads/queue.hh>
#include <graph/degree_sort.hh>

#include <algorithm>
#include <list>
#include <functional>
#include <vector>
#include <thread>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct QueueItem
    {
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
        unsigned cn;
        std::vector<int> position;
    };

    /**
     * We've possibly found a new best. Update best_anywhere and our local
     * result, and do any necessary printing.
     */
    template <unsigned size_>
    auto found_possible_new_best(const FixedBitGraph<size_> & graph, const std::vector<int> & o,
            const FixedBitSet<size_> & c, int c_popcount,
            const MaxCliqueParams & params, MaxCliqueResult & result, AtomicIncumbent & best_anywhere,
            const std::vector<int> & position) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size, position);
        }
    }

    /**
     * Bound function.
     */
    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    auto waited_long_enough(
            const MaxCliqueParams & params,
            std::chrono::time_point<std::chrono::steady_clock> & last_donation_time) -> bool
    {
        auto now = std::chrono::steady_clock::now();
        auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_donation_time);
        if (delta.count() > params.donation_wait) {
            last_donation_time = now;
            return true;
        }
        else
            return false;
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                      // vertex ordering
            Queue<QueueItem<size_> > * const maybe_queue,    // not null if we're populating: enqueue here
            Queue<QueueItem<size_> > * const donation_queue, // not null if we're donating: donate here
            std::chrono::time_point<std::chrono::steady_clock> & last_donation_time,
            FixedBitSet<size_> & c,                          // current candidate clique
            FixedBitSet<size_> & p,                          // potential additions
            MaxCliqueResult & result,
            const MaxCliqueParams & params,
            AtomicIncumbent & best_anywhere,
            std::vector<int> & position) -> void
    {
        ++result.nodes;

        auto c_popcount = c.popcount();

        // get our coloured vertices
        std::array<unsigned, size_ * bits_per_word> p_order, colours;

        // use override on initial order and bounds, if appropriate
        if (order_ == MaxCliqueOrder::Manual && 0 == c_popcount) {
            for (int i = 0 ; i < graph.size() ; ++i) {
                p_order[i] = i;
                colours[i] = params.initial_order_and_bounds.at(i).second;
            }
        }
        else
            colourise<size_>(graph, p, p_order, colours);

        bool chose_to_donate = false;

        // for each v in p... (v comes later)
        for (int n = p.popcount() - 1 ; n >= 0 ; --n) {
            ++position.back();

            // bound, timeout or early exit?
            if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
                return;

            auto v = p_order[n];

            // consider taking v
            c.set(v);
            ++c_popcount;

            // filter p to contain vertices adjacent to v
            FixedBitSet<size_> new_p = p;
            new_p = p;
            graph.intersect_with_row(v, new_p);

            if (new_p.empty()) {
                found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere, position);
            }
            else
            {
                // do we enqueue or recurse?
                bool should_expand = true;

                if (maybe_queue && c_popcount == params.split_depth) {
                    auto new_position = position;
                    new_position.push_back(0);
                    maybe_queue->enqueue_blocking(QueueItem<size_>{ c, std::move(new_p), colours[n], std::move(new_position) }, params.n_threads);
                    should_expand = false;
                }
                else if (new_p.popcount() < params.min_donate_size) {
                    chose_to_donate = false;
                }
                else if (donation_queue && (chose_to_donate || donation_queue->want_donations())
                        && (0 == params.donation_wait || waited_long_enough(params, last_donation_time))) {
                    auto new_position = position;
                    new_position.push_back(0);
                    donation_queue->enqueue(QueueItem<size_>{ c, std::move(new_p), colours[n], std::move(new_position) });
                    should_expand = false;
                    chose_to_donate = true;
                    ++result.donations;
                }

                if (should_expand) {
                    position.push_back(0);
                    expand<order_, size_>(graph, o, maybe_queue, donation_queue, last_donation_time, c, new_p, result, params, best_anywhere, position);
                    position.pop_back();
                }
            }

            // now consider not taking v
            c.unset(v);
            p.unset(v);
            --c_popcount;
        }
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto max_clique(const FixedBitGraph<size_> & graph, const std::vector<int> & o, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        Queue<QueueItem<size_> > queue{ params.n_threads, params.work_donation, params.donate_when_empty }; // work queue

        MaxCliqueResult result; // global result
        std::mutex result_mutex;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::list<std::thread> threads; // populating thread, and workers

        /* populate */
        threads.push_back(std::thread([&] {
                    MaxCliqueResult tr; // local result

                    FixedBitSet<size_> tc; // local candidate clique
                    tc.resize(graph.size());

                    FixedBitSet<size_> tp; // local potential additions
                    tp.resize(graph.size());
                    tp.set_all();

                    std::vector<int> position;
                    position.reserve(graph.size());
                    position.push_back(0);

                    auto last_donation_time = std::chrono::steady_clock::now();

                    // populate!
                    expand<order_, size_>(graph, o, &queue, nullptr, last_donation_time, tc, tp, result, params, best_anywhere, position);

                    // merge results
                    queue.initial_producer_done();
                    std::unique_lock<std::mutex> guard(result_mutex);
                    result.merge(tr);
                    }));

        /* workers */
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {
                        auto start_time = std::chrono::steady_clock::now(); // local start time
                        auto last_donation_time = std::chrono::steady_clock::now();

                        MaxCliqueResult tr; // local result

                        while (true) {
                            // get some work to do
                            QueueItem<size_> args;
                            if (! queue.dequeue_blocking(args))
                                break;

                            // re-evaluate the bound against our new best
                            if (args.cn <= best_anywhere.get())
                                continue;

                            // do some work
                            expand<order_, size_>(graph, o, nullptr, params.work_donation ? &queue : nullptr, last_donation_time,
                                    args.c, args.p, tr, params, best_anywhere, args.position);

                            // keep track of top nodes done
                            if (! params.abort.load())
                                ++tr.top_nodes_done;
                        }

                        auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                        // merge results
                        {
                            std::unique_lock<std::mutex> guard(result_mutex);
                            result.merge(tr);
                            result.times.push_back(overall_time);
                        }
                        }));
        }

        // wait until they're done, and clean up threads
        for (auto & t : threads)
            t.join();

        return result;
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto tbmcsa(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        std::vector<int> o(graph.size()); // vertex ordering
        std::iota(o.begin(), o.end(), 0);

        switch (order_) {
            case MaxCliqueOrder::Degree:
                degree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::Manual:
                for (int i = 0 ; i < graph.size() ; ++i)
                    o.at(i) = params.initial_order_and_bounds.at(i).first;
                break;
        }

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        // go!
        return max_clique<order_>(bit_graph, o, params);
    }
}

template <MaxCliqueOrder order_>
auto parasols::tbmcsa_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 256, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return tbmcsa<order_, 1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return tbmcsa<order_, 2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return tbmcsa<order_, 4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return tbmcsa<order_, 8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return tbmcsa<order_, 16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return tbmcsa<order_, 32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return tbmcsa<order_, 64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return tbmcsa<order_, 128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return tbmcsa<order_, 256>(graph, params);
    else
        throw GraphTooBig();
}

template auto parasols::tbmcsa_max_clique<MaxCliqueOrder::Degree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::tbmcsa_max_clique<MaxCliqueOrder::Manual>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;

<commit_msg>Logic fix<commit_after>/* vim: set sw=4 sts=4 et foldmethod=syntax : */

#include <max_clique/tbmcsa_max_clique.hh>
#include <max_clique/colourise.hh>
#include <max_clique/print_incumbent.hh>
#include <threads/atomic_incumbent.hh>
#include <threads/queue.hh>
#include <graph/degree_sort.hh>

#include <algorithm>
#include <list>
#include <functional>
#include <vector>
#include <thread>

using namespace parasols;

namespace
{
    template <unsigned size_>
    struct QueueItem
    {
        FixedBitSet<size_> c;
        FixedBitSet<size_> p;
        unsigned cn;
        std::vector<int> position;
    };

    /**
     * We've possibly found a new best. Update best_anywhere and our local
     * result, and do any necessary printing.
     */
    template <unsigned size_>
    auto found_possible_new_best(const FixedBitGraph<size_> & graph, const std::vector<int> & o,
            const FixedBitSet<size_> & c, int c_popcount,
            const MaxCliqueParams & params, MaxCliqueResult & result, AtomicIncumbent & best_anywhere,
            const std::vector<int> & position) -> void
    {
        if (best_anywhere.update(c_popcount)) {
            result.size = c_popcount;
            result.members.clear();
            for (int i = 0 ; i < graph.size() ; ++i)
                if (c.test(i))
                    result.members.insert(o[i]);
            print_incumbent(params, result.size, position);
        }
    }

    /**
     * Bound function.
     */
    auto bound(unsigned c_popcount, unsigned cn, const MaxCliqueParams & params, AtomicIncumbent & best_anywhere) -> bool
    {
        unsigned best_anywhere_value = best_anywhere.get();
        return (c_popcount + cn <= best_anywhere_value || best_anywhere_value >= params.stop_after_finding);
    }

    auto waited_long_enough(
            const MaxCliqueParams & params,
            std::chrono::time_point<std::chrono::steady_clock> & last_donation_time) -> bool
    {
        if (0 == params.donation_wait)
            return true;

        auto now = std::chrono::steady_clock::now();
        auto delta = std::chrono::duration_cast<std::chrono::milliseconds>(now - last_donation_time);
        if (delta.count() > params.donation_wait) {
            last_donation_time = now;
            return true;
        }
        else
            return false;
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto expand(
            const FixedBitGraph<size_> & graph,
            const std::vector<int> & o,                      // vertex ordering
            Queue<QueueItem<size_> > * const maybe_queue,    // not null if we're populating: enqueue here
            Queue<QueueItem<size_> > * const donation_queue, // not null if we're donating: donate here
            std::chrono::time_point<std::chrono::steady_clock> & last_donation_time,
            FixedBitSet<size_> & c,                          // current candidate clique
            FixedBitSet<size_> & p,                          // potential additions
            MaxCliqueResult & result,
            const MaxCliqueParams & params,
            AtomicIncumbent & best_anywhere,
            std::vector<int> & position) -> void
    {
        ++result.nodes;

        auto c_popcount = c.popcount();

        // get our coloured vertices
        std::array<unsigned, size_ * bits_per_word> p_order, colours;

        // use override on initial order and bounds, if appropriate
        if (order_ == MaxCliqueOrder::Manual && 0 == c_popcount) {
            for (int i = 0 ; i < graph.size() ; ++i) {
                p_order[i] = i;
                colours[i] = params.initial_order_and_bounds.at(i).second;
            }
        }
        else
            colourise<size_>(graph, p, p_order, colours);

        bool chose_to_donate = false;

        // for each v in p... (v comes later)
        for (int n = p.popcount() - 1 ; n >= 0 ; --n) {
            ++position.back();

            // bound, timeout or early exit?
            if (bound(c_popcount, colours[n], params, best_anywhere) || params.abort.load())
                return;

            auto v = p_order[n];

            // consider taking v
            c.set(v);
            ++c_popcount;

            // filter p to contain vertices adjacent to v
            FixedBitSet<size_> new_p = p;
            new_p = p;
            graph.intersect_with_row(v, new_p);

            if (new_p.empty()) {
                found_possible_new_best(graph, o, c, c_popcount, params, result, best_anywhere, position);
            }
            else
            {
                // do we enqueue or recurse?
                bool should_expand = true;

                if (maybe_queue && c_popcount == params.split_depth) {
                    auto new_position = position;
                    new_position.push_back(0);
                    maybe_queue->enqueue_blocking(QueueItem<size_>{ c, std::move(new_p), colours[n], std::move(new_position) }, params.n_threads);
                    should_expand = false;
                }
                else if (new_p.popcount() < params.min_donate_size) {
                    chose_to_donate = false;
                }
                else if (donation_queue && (chose_to_donate ||
                            (donation_queue->want_donations() && waited_long_enough(params, last_donation_time)))) {
                    auto new_position = position;
                    new_position.push_back(0);
                    donation_queue->enqueue(QueueItem<size_>{ c, std::move(new_p), colours[n], std::move(new_position) });
                    should_expand = false;
                    chose_to_donate = true;
                    ++result.donations;
                }

                if (should_expand) {
                    position.push_back(0);
                    expand<order_, size_>(graph, o, maybe_queue, donation_queue, last_donation_time, c, new_p, result, params, best_anywhere, position);
                    position.pop_back();
                }
            }

            // now consider not taking v
            c.unset(v);
            p.unset(v);
            --c_popcount;
        }
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto max_clique(const FixedBitGraph<size_> & graph, const std::vector<int> & o, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        Queue<QueueItem<size_> > queue{ params.n_threads, params.work_donation, params.donate_when_empty }; // work queue

        MaxCliqueResult result; // global result
        std::mutex result_mutex;

        AtomicIncumbent best_anywhere; // global incumbent
        best_anywhere.update(params.initial_bound);

        std::list<std::thread> threads; // populating thread, and workers

        /* populate */
        threads.push_back(std::thread([&] {
                    MaxCliqueResult tr; // local result

                    FixedBitSet<size_> tc; // local candidate clique
                    tc.resize(graph.size());

                    FixedBitSet<size_> tp; // local potential additions
                    tp.resize(graph.size());
                    tp.set_all();

                    std::vector<int> position;
                    position.reserve(graph.size());
                    position.push_back(0);

                    auto last_donation_time = std::chrono::steady_clock::now();

                    // populate!
                    expand<order_, size_>(graph, o, &queue, nullptr, last_donation_time, tc, tp, result, params, best_anywhere, position);

                    // merge results
                    queue.initial_producer_done();
                    std::unique_lock<std::mutex> guard(result_mutex);
                    result.merge(tr);
                    }));

        /* workers */
        for (unsigned i = 0 ; i < params.n_threads ; ++i) {
            threads.push_back(std::thread([&, i] {
                        auto start_time = std::chrono::steady_clock::now(); // local start time
                        auto last_donation_time = std::chrono::steady_clock::now();

                        MaxCliqueResult tr; // local result

                        while (true) {
                            // get some work to do
                            QueueItem<size_> args;
                            if (! queue.dequeue_blocking(args))
                                break;

                            // re-evaluate the bound against our new best
                            if (args.cn <= best_anywhere.get())
                                continue;

                            // do some work
                            expand<order_, size_>(graph, o, nullptr, params.work_donation ? &queue : nullptr, last_donation_time,
                                    args.c, args.p, tr, params, best_anywhere, args.position);

                            // keep track of top nodes done
                            if (! params.abort.load())
                                ++tr.top_nodes_done;
                        }

                        auto overall_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start_time);

                        // merge results
                        {
                            std::unique_lock<std::mutex> guard(result_mutex);
                            result.merge(tr);
                            result.times.push_back(overall_time);
                        }
                        }));
        }

        // wait until they're done, and clean up threads
        for (auto & t : threads)
            t.join();

        return result;
    }

    template <MaxCliqueOrder order_, unsigned size_>
    auto tbmcsa(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
    {
        std::vector<int> o(graph.size()); // vertex ordering
        std::iota(o.begin(), o.end(), 0);

        switch (order_) {
            case MaxCliqueOrder::Degree:
                degree_sort(graph, o, false);
                break;
            case MaxCliqueOrder::Manual:
                for (int i = 0 ; i < graph.size() ; ++i)
                    o.at(i) = params.initial_order_and_bounds.at(i).first;
                break;
        }

        // re-encode graph as a bit graph
        FixedBitGraph<size_> bit_graph;
        bit_graph.resize(graph.size());

        for (int i = 0 ; i < graph.size() ; ++i)
            for (int j = 0 ; j < graph.size() ; ++j)
                if (graph.adjacent(o[i], o[j]))
                    bit_graph.add_edge(i, j);

        // go!
        return max_clique<order_>(bit_graph, o, params);
    }
}

template <MaxCliqueOrder order_>
auto parasols::tbmcsa_max_clique(const Graph & graph, const MaxCliqueParams & params) -> MaxCliqueResult
{
    /* This is pretty horrible: in order to avoid dynamic allocation, select
     * the appropriate specialisation for our graph's size. */
    static_assert(max_graph_words == 256, "Need to update here if max_graph_size is changed.");
    if (graph.size() < bits_per_word)
        return tbmcsa<order_, 1>(graph, params);
    else if (graph.size() < 2 * bits_per_word)
        return tbmcsa<order_, 2>(graph, params);
    else if (graph.size() < 4 * bits_per_word)
        return tbmcsa<order_, 4>(graph, params);
    else if (graph.size() < 8 * bits_per_word)
        return tbmcsa<order_, 8>(graph, params);
    else if (graph.size() < 16 * bits_per_word)
        return tbmcsa<order_, 16>(graph, params);
    else if (graph.size() < 32 * bits_per_word)
        return tbmcsa<order_, 32>(graph, params);
    else if (graph.size() < 64 * bits_per_word)
        return tbmcsa<order_, 64>(graph, params);
    else if (graph.size() < 128 * bits_per_word)
        return tbmcsa<order_, 128>(graph, params);
    else if (graph.size() < 256 * bits_per_word)
        return tbmcsa<order_, 256>(graph, params);
    else
        throw GraphTooBig();
}

template auto parasols::tbmcsa_max_clique<MaxCliqueOrder::Degree>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;
template auto parasols::tbmcsa_max_clique<MaxCliqueOrder::Manual>(const Graph &, const MaxCliqueParams &) -> MaxCliqueResult;

<|endoftext|>


<commit_before>//
// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EGLDebugTest.cpp:
//   Tests of EGL_KHR_debug extension

#include <gtest/gtest.h>

#include <EGL/egl.h>
#include <EGL/eglext.h>

#include "test_utils/ANGLETest.h"
#include "test_utils/angle_test_configs.h"

namespace angle
{
class EGLDebugTest : public ANGLETest
{
  protected:
    bool hasExtension() const { return eglClientExtensionEnabled("EGL_KHR_debug"); }

    static void EGLAPIENTRY StubCallback(EGLenum error,
                                         const char *command,
                                         EGLint messageType,
                                         EGLLabelKHR threadLabel,
                                         EGLLabelKHR objectLabel,
                                         const char *message)
    {
    }

    static void EGLAPIENTRY CheckBadBindAPIError(EGLenum error,
                                                 const char *command,
                                                 EGLint messageType,
                                                 EGLLabelKHR threadLabel,
                                                 EGLLabelKHR objectLabel,
                                                 const char *message)
    {
        EXPECT_STREQ("eglBindAPI", command);
        ASSERT_EGLENUM_EQ(EGL_BAD_PARAMETER, error);
        EXPECT_STREQ("Thread", static_cast<const char *>(threadLabel));
    }

    static EGLDEBUGPROCKHR EGLAttribToDebugCallback(EGLAttrib attrib)
    {
        return reinterpret_cast<EGLDEBUGPROCKHR>(static_cast<uintptr_t>(attrib));
    }

    static EGLAttrib DebugCallbackToEGLAttrib(EGLDEBUGPROCKHR callback)
    {
        return static_cast<EGLAttrib>(reinterpret_cast<intptr_t>(callback));
    }
};

// Test that the extension is always available (it is implemented in ANGLE's frontend).
TEST_P(EGLDebugTest, ExtensionAlwaysAvailable)
{
    ASSERT_TRUE(hasExtension());
}

// Check that the default message filters and callbacks are correct
TEST_P(EGLDebugTest, DefaultParameters)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(nullptr, nullptr));

    EGLAttrib result = 0;

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_ERROR_KHR, &result));
    EXPECT_EGL_TRUE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_WARN_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_INFO_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_CALLBACK_KHR, &result));
    EXPECT_EQ(nullptr, EGLAttribToDebugCallback(result));
}

// Check that the message control and callback parameters can be set and then queried back
TEST_P(EGLDebugTest, SetMessageControl)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EGLAttrib controls[] = {
        EGL_DEBUG_MSG_CRITICAL_KHR, EGL_FALSE,
        // EGL_DEBUG_MSG_ERROR_KHR left unset
        EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE, EGL_DEBUG_MSG_INFO_KHR, EGL_FALSE, EGL_NONE, EGL_NONE,
    };

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(&StubCallback, controls));

    EGLAttrib result = 0;

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_CRITICAL_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_ERROR_KHR, &result));
    EXPECT_EGL_TRUE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_WARN_KHR, &result));
    EXPECT_EGL_TRUE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_INFO_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_CALLBACK_KHR, &result));
    EXPECT_EQ(DebugCallbackToEGLAttrib(&StubCallback), result);
}

// Set a thread label and then trigger a callback to verify the callback parameters are correct
TEST_P(EGLDebugTest, CorrectCallbackParameters)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(nullptr, nullptr));

    EXPECT_EQ(EGL_SUCCESS, eglLabelObjectKHR(EGL_NO_DISPLAY, EGL_OBJECT_THREAD_KHR, nullptr,
                                             const_cast<char *>("Thread")));

    // Enable all messages
    EGLAttrib controls[] = {
        EGL_DEBUG_MSG_CRITICAL_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_ERROR_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_WARN_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_INFO_KHR,
        EGL_TRUE,
        EGL_NONE,
        EGL_NONE,
    };

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS),
              eglDebugMessageControlKHR(&CheckBadBindAPIError, controls));

    // Generate an error and trigger the callback
    EXPECT_EGL_FALSE(eglBindAPI(0xBADDBADD));
}

// Test that labels can be set and that errors are generated if the wrong object type is used
TEST_P(EGLDebugTest, SetLabel)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(nullptr, nullptr));

    // Display display and object must be equal when setting a display label
    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS),
              eglLabelObjectKHR(getEGLWindow()->getDisplay(), EGL_OBJECT_DISPLAY_KHR,
                                getEGLWindow()->getDisplay(), const_cast<char *>("Display")));
    EXPECT_NE(static_cast<EGLint>(EGL_SUCCESS),
              eglLabelObjectKHR(nullptr, EGL_OBJECT_DISPLAY_KHR, getEGLWindow()->getDisplay(),
                                const_cast<char *>("Display")));

    //  Set a surface label
    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS),
              eglLabelObjectKHR(getEGLWindow()->getDisplay(), EGL_OBJECT_SURFACE_KHR,
                                getEGLWindow()->getSurface(), const_cast<char *>("Surface")));
    EXPECT_EGL_ERROR(EGL_SUCCESS);

    // Provide a surface but use an image label type
    EXPECT_EQ(static_cast<EGLint>(EGL_BAD_PARAMETER),
              eglLabelObjectKHR(getEGLWindow()->getDisplay(), EGL_OBJECT_IMAGE_KHR,
                                getEGLWindow()->getSurface(), const_cast<char *>("Image")));
    EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}

ANGLE_INSTANTIATE_TEST(EGLDebugTest,
                       ES2_D3D9(),
                       ES2_D3D11(),
                       ES3_D3D11(),
                       ES2_OPENGL(),
                       ES3_OPENGL());

}  // namespace angle
<commit_msg>Clean up state in EGLDebugTest.<commit_after>//
// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// EGLDebugTest.cpp:
//   Tests of EGL_KHR_debug extension

#include <gtest/gtest.h>

#include <EGL/egl.h>
#include <EGL/eglext.h>

#include "test_utils/ANGLETest.h"
#include "test_utils/angle_test_configs.h"

namespace angle
{
class EGLDebugTest : public ANGLETest
{
  protected:
    void TearDown() override
    {
        eglDebugMessageControlKHR(nullptr, nullptr);
    }

    bool hasExtension() const { return eglClientExtensionEnabled("EGL_KHR_debug"); }

    static void EGLAPIENTRY StubCallback(EGLenum error,
                                         const char *command,
                                         EGLint messageType,
                                         EGLLabelKHR threadLabel,
                                         EGLLabelKHR objectLabel,
                                         const char *message)
    {
    }

    static void EGLAPIENTRY CheckBadBindAPIError(EGLenum error,
                                                 const char *command,
                                                 EGLint messageType,
                                                 EGLLabelKHR threadLabel,
                                                 EGLLabelKHR objectLabel,
                                                 const char *message)
    {
        EXPECT_STREQ("eglBindAPI", command);
        ASSERT_EGLENUM_EQ(EGL_BAD_PARAMETER, error);
        EXPECT_STREQ("Thread", static_cast<const char *>(threadLabel));
    }

    static EGLDEBUGPROCKHR EGLAttribToDebugCallback(EGLAttrib attrib)
    {
        return reinterpret_cast<EGLDEBUGPROCKHR>(static_cast<uintptr_t>(attrib));
    }

    static EGLAttrib DebugCallbackToEGLAttrib(EGLDEBUGPROCKHR callback)
    {
        return static_cast<EGLAttrib>(reinterpret_cast<intptr_t>(callback));
    }
};

// Test that the extension is always available (it is implemented in ANGLE's frontend).
TEST_P(EGLDebugTest, ExtensionAlwaysAvailable)
{
    ASSERT_TRUE(hasExtension());
}

// Check that the default message filters and callbacks are correct
TEST_P(EGLDebugTest, DefaultParameters)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(nullptr, nullptr));

    EGLAttrib result = 0;

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_ERROR_KHR, &result));
    EXPECT_EGL_TRUE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_WARN_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_INFO_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_CALLBACK_KHR, &result));
    EXPECT_EQ(nullptr, EGLAttribToDebugCallback(result));
}

// Check that the message control and callback parameters can be set and then queried back
TEST_P(EGLDebugTest, SetMessageControl)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EGLAttrib controls[] = {
        EGL_DEBUG_MSG_CRITICAL_KHR, EGL_FALSE,
        // EGL_DEBUG_MSG_ERROR_KHR left unset
        EGL_DEBUG_MSG_WARN_KHR, EGL_TRUE, EGL_DEBUG_MSG_INFO_KHR, EGL_FALSE, EGL_NONE, EGL_NONE,
    };

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(&StubCallback, controls));

    EGLAttrib result = 0;

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_CRITICAL_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_ERROR_KHR, &result));
    EXPECT_EGL_TRUE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_WARN_KHR, &result));
    EXPECT_EGL_TRUE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_MSG_INFO_KHR, &result));
    EXPECT_EGL_FALSE(result);

    EXPECT_EGL_TRUE(eglQueryDebugKHR(EGL_DEBUG_CALLBACK_KHR, &result));
    EXPECT_EQ(DebugCallbackToEGLAttrib(&StubCallback), result);
}

// Set a thread label and then trigger a callback to verify the callback parameters are correct
TEST_P(EGLDebugTest, CorrectCallbackParameters)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(nullptr, nullptr));

    EXPECT_EQ(EGL_SUCCESS, eglLabelObjectKHR(EGL_NO_DISPLAY, EGL_OBJECT_THREAD_KHR, nullptr,
                                             const_cast<char *>("Thread")));

    // Enable all messages
    EGLAttrib controls[] = {
        EGL_DEBUG_MSG_CRITICAL_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_ERROR_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_WARN_KHR,
        EGL_TRUE,
        EGL_DEBUG_MSG_INFO_KHR,
        EGL_TRUE,
        EGL_NONE,
        EGL_NONE,
    };

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS),
              eglDebugMessageControlKHR(&CheckBadBindAPIError, controls));

    // Generate an error and trigger the callback
    EXPECT_EGL_FALSE(eglBindAPI(0xBADDBADD));
}

// Test that labels can be set and that errors are generated if the wrong object type is used
TEST_P(EGLDebugTest, SetLabel)
{
    ANGLE_SKIP_TEST_IF(!hasExtension());

    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS), eglDebugMessageControlKHR(nullptr, nullptr));

    // Display display and object must be equal when setting a display label
    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS),
              eglLabelObjectKHR(getEGLWindow()->getDisplay(), EGL_OBJECT_DISPLAY_KHR,
                                getEGLWindow()->getDisplay(), const_cast<char *>("Display")));
    EXPECT_NE(static_cast<EGLint>(EGL_SUCCESS),
              eglLabelObjectKHR(nullptr, EGL_OBJECT_DISPLAY_KHR, getEGLWindow()->getDisplay(),
                                const_cast<char *>("Display")));

    //  Set a surface label
    EXPECT_EQ(static_cast<EGLint>(EGL_SUCCESS),
              eglLabelObjectKHR(getEGLWindow()->getDisplay(), EGL_OBJECT_SURFACE_KHR,
                                getEGLWindow()->getSurface(), const_cast<char *>("Surface")));
    EXPECT_EGL_ERROR(EGL_SUCCESS);

    // Provide a surface but use an image label type
    EXPECT_EQ(static_cast<EGLint>(EGL_BAD_PARAMETER),
              eglLabelObjectKHR(getEGLWindow()->getDisplay(), EGL_OBJECT_IMAGE_KHR,
                                getEGLWindow()->getSurface(), const_cast<char *>("Image")));
    EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
}

ANGLE_INSTANTIATE_TEST(EGLDebugTest,
                       ES2_D3D9(),
                       ES2_D3D11(),
                       ES3_D3D11(),
                       ES2_OPENGL(),
                       ES3_OPENGL());

}  // namespace angle
<|endoftext|>


<commit_before>// Floating Temple
// Copyright 2015 Derek S. Snyder
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "python/local_object_impl.h"

#include "third_party/Python-3.4.2/Include/Python.h"

#include <cstddef>
#include <string>
#include <vector>

#include "base/escape.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "include/c++/value.h"
#include "python/dict_local_object.h"
#include "python/false_local_object.h"
#include "python/get_serialized_object_type.h"
#include "python/interpreter_impl.h"
#include "python/list_local_object.h"
#include "python/long_local_object.h"
#include "python/method_context.h"
#include "python/none_local_object.h"
#include "python/proto/serialization.pb.h"
#include "python/python_gil_lock.h"
#include "python/thread_substitution.h"
#include "python/true_local_object.h"

using std::size_t;
using std::string;
using std::vector;

namespace floating_temple {
namespace python {

LocalObjectImpl::LocalObjectImpl(PyObject* py_object)
    : py_object_(CHECK_NOTNULL(py_object)) {
}

LocalObjectImpl::~LocalObjectImpl() {
  PythonGilLock lock;
  Py_DECREF(py_object_);
}

size_t LocalObjectImpl::Serialize(void* buffer, size_t buffer_size,
                                  SerializationContext* context) const {
  ObjectProto object_proto;
  PopulateObjectProto(&object_proto, context);

  const size_t byte_size = static_cast<size_t>(object_proto.ByteSize());
  if (byte_size <= buffer_size) {
    object_proto.SerializeWithCachedSizesToArray(static_cast<uint8*>(buffer));
  }

  return byte_size;
}

void LocalObjectImpl::InvokeMethod(Thread* thread,
                                   PeerObject* peer_object,
                                   const string& method_name,
                                   const vector<Value>& parameters,
                                   Value* return_value) {
  CHECK(thread != nullptr);
  CHECK(peer_object != nullptr);

  VLOG(3) << "Invoke method on local object: " << method_name;

  MethodContext method_context;
  ThreadSubstitution thread_substitution(InterpreterImpl::instance(), thread);

  PythonGilLock lock;

  // TODO(dss): Fail gracefully if the peer passes the wrong number of
  // parameters, or the wrong types of parameters.

  // TODO(dss): Fail gracefully if a remote peer sends an invalid method name.
  CHECK(this->InvokeTypeSpecificMethod(peer_object, method_name, parameters,
                                       &method_context, return_value))
      << "Unexpected method name \"" << CEscape(method_name) << "\"";
}

// static
LocalObjectImpl* LocalObjectImpl::Deserialize(const void* buffer,
                                              size_t buffer_size,
                                              DeserializationContext* context) {
  CHECK(buffer != nullptr);

  ObjectProto object_proto;
  CHECK(object_proto.ParseFromArray(buffer, buffer_size));

  const ObjectProto::Type object_type = GetSerializedObjectType(object_proto);

  switch (object_type) {
    case ObjectProto::PY_NONE:
      return new NoneLocalObject();

    case ObjectProto::LONG:
      return LongLocalObject::ParseLongProto(object_proto.long_object());

    case ObjectProto::FALSE:
      return new FalseLocalObject();

    case ObjectProto::TRUE:
      return new TrueLocalObject();

    case ObjectProto::LIST:
      return ListLocalObject::ParseListProto(object_proto.list_object(),
                                             context);

    case ObjectProto::DICT:
      return DictLocalObject::ParseDictProto(object_proto.dict_object(),
                                             context);

    case ObjectProto::FLOAT:
    case ObjectProto::COMPLEX:
    case ObjectProto::BYTES:
    case ObjectProto::BYTE_ARRAY:
    case ObjectProto::UNICODE:
    case ObjectProto::TUPLE:
    case ObjectProto::SET:
    case ObjectProto::FROZEN_SET:
      LOG(FATAL) << "Not yet implemented";
      return nullptr;

    default:
      LOG(FATAL) << "Unexpected object type: " << static_cast<int>(object_type);
  }

  return nullptr;
}

}  // namespace python
}  // namespace floating_temple
<commit_msg>Modify LocalObjectImpl::InvokeMethod to call the standard low-level methods on Python objects.<commit_after>// Floating Temple
// Copyright 2015 Derek S. Snyder
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "python/local_object_impl.h"

#include "third_party/Python-3.4.2/Include/Python.h"

#include <cstddef>
#include <string>
#include <vector>

#include "base/escape.h"
#include "base/integral_types.h"
#include "base/logging.h"
#include "include/c++/value.h"
#include "python/dict_local_object.h"
#include "python/false_local_object.h"
#include "python/get_serialized_object_type.h"
#include "python/interpreter_impl.h"
#include "python/list_local_object.h"
#include "python/long_local_object.h"
#include "python/make_value.h"
#include "python/method_context.h"
#include "python/none_local_object.h"
#include "python/proto/serialization.pb.h"
#include "python/python_gil_lock.h"
#include "python/thread_substitution.h"
#include "python/true_local_object.h"

using std::size_t;
using std::string;
using std::vector;

namespace floating_temple {
namespace python {

LocalObjectImpl::LocalObjectImpl(PyObject* py_object)
    : py_object_(CHECK_NOTNULL(py_object)) {
}

LocalObjectImpl::~LocalObjectImpl() {
  PythonGilLock lock;
  Py_DECREF(py_object_);
}

size_t LocalObjectImpl::Serialize(void* buffer, size_t buffer_size,
                                  SerializationContext* context) const {
  ObjectProto object_proto;
  PopulateObjectProto(&object_proto, context);

  const size_t byte_size = static_cast<size_t>(object_proto.ByteSize());
  if (byte_size <= buffer_size) {
    object_proto.SerializeWithCachedSizesToArray(static_cast<uint8*>(buffer));
  }

  return byte_size;
}

#define CALL_TP_METHOD0(method) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->method != nullptr); \
        CHECK_EQ(parameters.size(), 0u); \
        MakeReturnValue((*object_type->method)(py_object_), return_value); \
        return; \
      } \
    } while (false)

#define CALL_TP_METHOD1(method, param_type1) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->method != nullptr); \
        CHECK_EQ(parameters.size(), 1u); \
        MakeReturnValue( \
            (*object_type->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_TP_METHOD2(method, param_type1, param_type2) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->method != nullptr); \
        CHECK_EQ(parameters.size(), 2u); \
        MakeReturnValue( \
            (*object_type->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context), \
                ExtractValue<param_type2>(parameters[1], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_NB_METHOD0(method) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_number != nullptr); \
        CHECK(object_type->tp_as_number->method != nullptr); \
        CHECK_EQ(parameters.size(), 0u); \
        MakeReturnValue((*object_type->tp_as_number->method)(py_object_), \
                        return_value); \
        return; \
      } \
    } while (false)

#define CALL_NB_METHOD1(method, param_type1) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_number != nullptr); \
        CHECK(object_type->tp_as_number->method != nullptr); \
        CHECK_EQ(parameters.size(), 1u); \
        MakeReturnValue( \
            (*object_type->tp_as_number->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_NB_METHOD2(method, param_type1, param_type2) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_number != nullptr); \
        CHECK(object_type->tp_as_number->method != nullptr); \
        CHECK_EQ(parameters.size(), 2u); \
        MakeReturnValue( \
            (*object_type->tp_as_number->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context), \
                ExtractValue<param_type2>(parameters[1], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_SQ_METHOD0(method) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_sequence != nullptr); \
        CHECK(object_type->tp_as_sequence->method != nullptr); \
        CHECK_EQ(parameters.size(), 0u); \
        MakeReturnValue((*object_type->tp_as_sequence->method)(py_object_), \
                        return_value); \
        return; \
      } \
    } while (false)

#define CALL_SQ_METHOD1(method, param_type1) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_sequence != nullptr); \
        CHECK(object_type->tp_as_sequence->method != nullptr); \
        CHECK_EQ(parameters.size(), 1u); \
        MakeReturnValue( \
            (*object_type->tp_as_sequence->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_SQ_METHOD2(method, param_type1, param_type2) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_sequence != nullptr); \
        CHECK(object_type->tp_as_sequence->method != nullptr); \
        CHECK_EQ(parameters.size(), 2u); \
        MakeReturnValue( \
            (*object_type->tp_as_sequence->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context), \
                ExtractValue<param_type2>(parameters[1], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_MP_METHOD0(method) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_mapping != nullptr); \
        CHECK(object_type->tp_as_mapping->method != nullptr); \
        CHECK_EQ(parameters.size(), 0u); \
        MakeReturnValue((*object_type->tp_as_mapping->method)(py_object_), \
                        return_value); \
        return; \
      } \
    } while (false)

#define CALL_MP_METHOD1(method, param_type1) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_mapping != nullptr); \
        CHECK(object_type->tp_as_mapping->method != nullptr); \
        CHECK_EQ(parameters.size(), 1u); \
        MakeReturnValue( \
            (*object_type->tp_as_mapping->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

#define CALL_MP_METHOD2(method, param_type1, param_type2) \
    do { \
      if (method_name == #method) { \
        CHECK(object_type->tp_as_mapping != nullptr); \
        CHECK(object_type->tp_as_mapping->method != nullptr); \
        CHECK_EQ(parameters.size(), 2u); \
        MakeReturnValue( \
            (*object_type->tp_as_mapping->method)( \
                py_object_, \
                ExtractValue<param_type1>(parameters[0], &method_context), \
                ExtractValue<param_type2>(parameters[1], &method_context)), \
            return_value); \
        return; \
      } \
    } while (false)

void LocalObjectImpl::InvokeMethod(Thread* thread,
                                   PeerObject* peer_object,
                                   const string& method_name,
                                   const vector<Value>& parameters,
                                   Value* return_value) {
  CHECK(thread != nullptr);
  CHECK(peer_object != nullptr);

  VLOG(3) << "Invoke method on local object: " << method_name;

  MethodContext method_context;
  ThreadSubstitution thread_substitution(InterpreterImpl::instance(), thread);

  PythonGilLock lock;

  const PyTypeObject* const object_type = Py_TYPE(py_object_);
  CHECK(object_type != nullptr);

  // TODO(dss): Consider using binary search instead of linear search to find
  // the method given its name.

  // TODO(dss): Fail gracefully if the peer passes the wrong number of
  // parameters, or the wrong types of parameters.

  CALL_TP_METHOD1(tp_getattr, char*);
  CALL_TP_METHOD2(tp_setattr, char*, PyObject*);
  CALL_TP_METHOD0(tp_repr);
  CALL_TP_METHOD0(tp_hash);
  CALL_TP_METHOD2(tp_call, PyObject*, PyObject*);
  CALL_TP_METHOD0(tp_str);
  CALL_TP_METHOD1(tp_getattro, PyObject*);
  CALL_TP_METHOD2(tp_setattro, PyObject*, PyObject*);
  CALL_TP_METHOD2(tp_richcompare, PyObject*, int);
  CALL_TP_METHOD0(tp_iter);
  CALL_TP_METHOD0(tp_iternext);
  CALL_TP_METHOD2(tp_descr_get, PyObject*, PyObject*);
  CALL_TP_METHOD2(tp_descr_set, PyObject*, PyObject*);
  CALL_TP_METHOD2(tp_init, PyObject*, PyObject*);

  CALL_NB_METHOD1(nb_add, PyObject*);
  CALL_NB_METHOD1(nb_subtract, PyObject*);
  CALL_NB_METHOD1(nb_multiply, PyObject*);
  CALL_NB_METHOD1(nb_remainder, PyObject*);
  CALL_NB_METHOD1(nb_divmod, PyObject*);
  CALL_NB_METHOD2(nb_power, PyObject*, PyObject*);
  CALL_NB_METHOD0(nb_negative);
  CALL_NB_METHOD0(nb_positive);
  CALL_NB_METHOD0(nb_absolute);
  CALL_NB_METHOD0(nb_bool);
  CALL_NB_METHOD0(nb_invert);
  CALL_NB_METHOD1(nb_lshift, PyObject*);
  CALL_NB_METHOD1(nb_rshift, PyObject*);
  CALL_NB_METHOD1(nb_and, PyObject*);
  CALL_NB_METHOD1(nb_xor, PyObject*);
  CALL_NB_METHOD1(nb_or, PyObject*);
  CALL_NB_METHOD0(nb_int);
  CALL_NB_METHOD0(nb_float);
  CALL_NB_METHOD1(nb_inplace_add, PyObject*);
  CALL_NB_METHOD1(nb_inplace_subtract, PyObject*);
  CALL_NB_METHOD1(nb_inplace_multiply, PyObject*);
  CALL_NB_METHOD1(nb_inplace_remainder, PyObject*);
  CALL_NB_METHOD2(nb_inplace_power, PyObject*, PyObject*);
  CALL_NB_METHOD1(nb_inplace_lshift, PyObject*);
  CALL_NB_METHOD1(nb_inplace_rshift, PyObject*);
  CALL_NB_METHOD1(nb_inplace_and, PyObject*);
  CALL_NB_METHOD1(nb_inplace_xor, PyObject*);
  CALL_NB_METHOD1(nb_inplace_or, PyObject*);
  CALL_NB_METHOD1(nb_floor_divide, PyObject*);
  CALL_NB_METHOD1(nb_true_divide, PyObject*);
  CALL_NB_METHOD1(nb_inplace_floor_divide, PyObject*);
  CALL_NB_METHOD1(nb_inplace_true_divide, PyObject*);
  CALL_NB_METHOD0(nb_index);

  CALL_SQ_METHOD0(sq_length);
  CALL_SQ_METHOD1(sq_concat, PyObject*);
  CALL_SQ_METHOD1(sq_repeat, Py_ssize_t);
  CALL_SQ_METHOD1(sq_item, Py_ssize_t);
  CALL_SQ_METHOD2(sq_ass_item, Py_ssize_t, PyObject*);
  CALL_SQ_METHOD1(sq_contains, PyObject*);
  CALL_SQ_METHOD1(sq_inplace_concat, PyObject*);
  CALL_SQ_METHOD1(sq_inplace_repeat, Py_ssize_t);

  CALL_MP_METHOD0(mp_length);
  CALL_MP_METHOD1(mp_subscript, PyObject*);
  CALL_MP_METHOD2(mp_ass_subscript, PyObject*, PyObject*);

  // TODO(dss): Fail gracefully if a remote peer sends an invalid method name.
  CHECK(this->InvokeTypeSpecificMethod(peer_object, method_name, parameters,
                                       &method_context, return_value))
      << "Unexpected method name \"" << CEscape(method_name) << "\"";
}

#undef CALL_TP_METHOD0
#undef CALL_TP_METHOD1
#undef CALL_TP_METHOD2
#undef CALL_NB_METHOD0
#undef CALL_NB_METHOD1
#undef CALL_NB_METHOD2
#undef CALL_SQ_METHOD0
#undef CALL_SQ_METHOD1
#undef CALL_SQ_METHOD2
#undef CALL_MP_METHOD0
#undef CALL_MP_METHOD1
#undef CALL_MP_METHOD2

// static
LocalObjectImpl* LocalObjectImpl::Deserialize(const void* buffer,
                                              size_t buffer_size,
                                              DeserializationContext* context) {
  CHECK(buffer != nullptr);

  ObjectProto object_proto;
  CHECK(object_proto.ParseFromArray(buffer, buffer_size));

  const ObjectProto::Type object_type = GetSerializedObjectType(object_proto);

  switch (object_type) {
    case ObjectProto::PY_NONE:
      return new NoneLocalObject();

    case ObjectProto::LONG:
      return LongLocalObject::ParseLongProto(object_proto.long_object());

    case ObjectProto::FALSE:
      return new FalseLocalObject();

    case ObjectProto::TRUE:
      return new TrueLocalObject();

    case ObjectProto::LIST:
      return ListLocalObject::ParseListProto(object_proto.list_object(),
                                             context);

    case ObjectProto::DICT:
      return DictLocalObject::ParseDictProto(object_proto.dict_object(),
                                             context);

    case ObjectProto::FLOAT:
    case ObjectProto::COMPLEX:
    case ObjectProto::BYTES:
    case ObjectProto::BYTE_ARRAY:
    case ObjectProto::UNICODE:
    case ObjectProto::TUPLE:
    case ObjectProto::SET:
    case ObjectProto::FROZEN_SET:
      LOG(FATAL) << "Not yet implemented";
      return nullptr;

    default:
      LOG(FATAL) << "Unexpected object type: " << static_cast<int>(object_type);
  }

  return nullptr;
}

}  // namespace python
}  // namespace floating_temple
<|endoftext|>