<commit_before>//===---- lib/Jit/LLILCJit.cpp --------------------------------*- C++ -*-===//
//
// LLILC
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
// See LICENSE file in the project root for full license information.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Implementation of the main Jit entry points.
///
//===----------------------------------------------------------------------===//

#include "jitpch.h"
#include "LLILCJit.h"
#include "options.h"
#include "readerir.h"
#include "abi.h"
#include "EEMemoryManager.h"
#include "llvm/CodeGen/GCs.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include <string>

using namespace llvm;

// Get the GC-Scheme used by the runtime -- conservative/precise
// For now this is done by directly accessing environment variable.
// When CLR config support is included, update it here.
bool shouldUseConservativeGC() {
  const char *LevelCStr = getenv("COMPLUS_GCCONSERVATIVE");
  return (LevelCStr != nullptr) && (strcmp(LevelCStr, "1") == 0);
}

// Determine if GC statepoints should be inserted.
bool shouldInsertStatepoints() {
  const char *LevelCStr = getenv("COMPLUS_INSERTSTATEPOINTS");
  return (LevelCStr != nullptr) && (strcmp(LevelCStr, "1") == 0);
}

// The one and only Jit Object.
LLILCJit *LLILCJit::TheJit = nullptr;

// This is guaranteed to be called by the EE
// in single-threaded mode.
ICorJitCompiler *__stdcall getJit() {

  if (LLILCJit::TheJit == nullptr) {
    // These are one-time only operations.
    // Create the singleton jit object.
    LLILCJit::TheJit = new LLILCJit();

    // Register a signal handler, mainly so that the critical
    // section that is entered on windows when LLVM gets a fatal error
    // is properly initialized.
    sys::AddSignalHandler(&LLILCJit::signalHandler, LLILCJit::TheJit);

    // Allow LLVM to pick up options via the environment
    cl::ParseEnvironmentOptions("LLILCJit", "COMplus_altjitOptions");
  }

  return LLILCJit::TheJit;
}

// Construct the JIT instance
LLILCJit::LLILCJit() {
  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();
  InitializeNativeTargetAsmParser();
  llvm::linkStatepointExampleGC();

  ShouldInsertStatepoints = shouldInsertStatepoints();
  assert(ShouldInsertStatepoints ||
         shouldUseConservativeGC() && "Statepoints required for precise-GC");
}

#ifdef LLVM_ON_WIN32
// Windows only
BOOL WINAPI DllMain(HANDLE Instance, DWORD Reason, LPVOID Reserved) {
  if (Reason == DLL_PROCESS_ATTACH) {
    DisableThreadLibraryCalls((HINSTANCE)Instance);
  } else if (Reason == DLL_PROCESS_DETACH) {
    // TBD
  }
  return TRUE;
}
#endif // LLVM_ON_WIN32

extern "C" void __stdcall sxsJitStartup(void *CcCallbacks) {
  // nothing to do
}

void LLILCJitContext::outputDebugMethodName() {
  const size_t SizeOfBuffer = 512;
  char TempBuffer[SizeOfBuffer];
  const char *DebugClassName = nullptr;
  const char *DebugMethodName = nullptr;

  DebugMethodName = JitInfo->getMethodName(MethodInfo->ftn, &DebugClassName);
  dbgs() << format("INFO:  jitting method %s::%s using LLILCJit\n",
                   DebugClassName, DebugMethodName);
}

LLILCJitContext::LLILCJitContext(LLILCJitPerThreadState *PerThreadState)
    : State(PerThreadState), HasLoadedBitCode(false), Options(this) {
  this->Next = State->JitContext;
  State->JitContext = this;
}

LLILCJitContext::~LLILCJitContext() {
  LLILCJitContext *TopContext = State->JitContext;
  assert(this == TopContext && "Unbalanced contexts!");
  State->JitContext = TopContext->Next;
}

// This is the method invoked by the EE to Jit code.
CorJitResult LLILCJit::compileMethod(ICorJitInfo *JitInfo,
                                     CORINFO_METHOD_INFO *MethodInfo,
                                     UINT Flags, BYTE **NativeEntry,
                                     ULONG *NativeSizeOfCode) {

  // Bail if input is malformed
  if (nullptr == JitInfo || nullptr == MethodInfo || nullptr == NativeEntry ||
      nullptr == NativeSizeOfCode) {
    return CORJIT_INTERNALERROR;
  }

  // Prep main outputs
  *NativeEntry = nullptr;
  *NativeSizeOfCode = 0;

  // Set up state for this thread (if necessary)
  LLILCJitPerThreadState *PerThreadState = State.get();
  if (PerThreadState == nullptr) {
    PerThreadState = new LLILCJitPerThreadState();
    State.set(PerThreadState);
  }

  // Set up context for this Jit request
  LLILCJitContext Context = LLILCJitContext(PerThreadState);

  // Fill in context information from the CLR
  Context.JitInfo = JitInfo;
  Context.MethodInfo = MethodInfo;
  Context.Flags = Flags;
  JitInfo->getEEInfo(&Context.EEInfo);

  // Fill in context information from LLVM
  Context.LLVMContext = &PerThreadState->LLVMContext;
  std::unique_ptr<Module> M = Context.getModuleForMethod(MethodInfo);
  Context.CurrentModule = M.get();
  Context.CurrentModule->setTargetTriple(LLILC_TARGET_TRIPLE);
  Context.TheABIInfo = ABIInfo::get(*Context.CurrentModule);

  // Initialize per invocation JIT options. This should be done after the
  // rest of the Context is filled out as it has dependencies on Flags and
  // MethodInfo
  Context.Options.initialize();

  EngineBuilder Builder(std::move(M));
  std::string ErrStr;
  Builder.setErrorStr(&ErrStr);

  std::unique_ptr<RTDyldMemoryManager> MM(new EEMemoryManager(&Context));
  Builder.setMCJITMemoryManager(std::move(MM));

  TargetOptions Options;

  // Statepoint GC does not support FastIsel yet.
  Options.EnableFastISel = false;

  if (Context.Options.OptLevel != OptLevel::DEBUG_CODE) {
    Builder.setOptLevel(CodeGenOpt::Level::Default);
  } else {
    Builder.setOptLevel(CodeGenOpt::Level::None);
    Options.NoFramePointerElim = true;
  }

  Builder.setTargetOptions(Options);

  ExecutionEngine *NewEngine = Builder.create();

  if (!NewEngine) {
    errs() << "Could not create ExecutionEngine: " << ErrStr << "\n";
    return CORJIT_INTERNALERROR;
  }

  // Don't allow the EE to search for external symbols.
  NewEngine->DisableSymbolSearching();
  Context.EE = NewEngine;

  // Now jit the method.
  CorJitResult Result = CORJIT_INTERNALERROR;
  if (Context.Options.DumpLevel == VERBOSE) {
    Context.outputDebugMethodName();
  }
  bool HasMethod = this->readMethod(&Context);

  if (HasMethod) {

    if (ShouldInsertStatepoints) {
      // If using Precise GC, run the GC-Safepoint insertion
      // and lowering passes before generating code.
      legacy::PassManager Passes;
      Passes.add(createPlaceSafepointsPass());

      PassManagerBuilder PMBuilder;
      PMBuilder.OptLevel = 0;  // Set optimization level to -O0
      PMBuilder.SizeLevel = 0; // so that no additional phases are run.
      PMBuilder.populateModulePassManager(Passes);

      Passes.add(createRewriteStatepointsForGCPass(false));
      Passes.run(*Context.CurrentModule);
    }

    Context.EE->generateCodeForModule(Context.CurrentModule);

    // You need to pick up the COFFDyld changes from the MS branch of LLVM
    // or this will fail with an "Incompatible object format!" error
    // from LLVM's dynamic loader.
    uint64_t FunctionAddress =
        Context.EE->getFunctionAddress(Context.MethodName);
    *NativeEntry = (BYTE *)FunctionAddress;

    // TODO: ColdCodeSize, or separated code, is not enabled or included.
    *NativeSizeOfCode = Context.HotCodeSize + Context.ReadOnlyDataSize;

    // This is a stop-gap point to issue a default stub of GC info. This lets
    // the CLR consume our methods cleanly. (and the ETW tracing still works)
    // Down the road this will be superseded by a CLR specific
    // GCMetadataPrinter instance or similar.
    this->outputGCInfo(&Context);

    // Dump out any enabled timing info.
    TimerGroup::printAll(errs());

    // Tell the CLR that we've successfully generated code for this method.
    Result = CORJIT_OK;
  }

  // Clean up a bit
  delete Context.EE;
  Context.EE = nullptr;
  delete Context.TheABIInfo;
  Context.TheABIInfo = nullptr;

  return Result;
}

std::unique_ptr<Module>
LLILCJitContext::getModuleForMethod(CORINFO_METHOD_INFO *MethodInfo) {
  // Grab name info from the EE.
  const char *DebugClassName = nullptr;
  const char *DebugMethodName = nullptr;
  DebugMethodName = JitInfo->getMethodName(MethodInfo->ftn, &DebugClassName);

  // Stop gap name.  The full naming will likely require some more info.
  std::string ModName(DebugClassName);
  ModName.append(1, '.');
  ModName.append(DebugMethodName);

  std::unique_ptr<Module> M;
  char *BitcodePath = getenv("BITCODE_PATH");

  if (BitcodePath != nullptr) {
    SMDiagnostic Err;
    std::string Path = std::string(BitcodePath);

    // If there is a bitcode path, use the debug module name to look for an .bc
    // file. If one is found then load it directly.
    Path.append("\\");
    Path.append(ModName);
    Path.append(".bc");
    M = llvm::parseIRFile(Path, Err, *this->LLVMContext);

    if (!M) {
      // Err.print("IR Parsing failed: ", errs());
      this->HasLoadedBitCode = false;
    } else {
      std::string Message("Loaded bitcode from: ");
      Message.append(Path);
      dbgs() << Message;
      this->HasLoadedBitCode = true;
    }
  }

  if (!this->HasLoadedBitCode) {
    M = std::unique_ptr<Module>(new Module(ModName, *this->LLVMContext));
  }

  return std::move(M);
}

// Read method MSIL and construct LLVM bitcode
bool LLILCJit::readMethod(LLILCJitContext *JitContext) {
  if (JitContext->HasLoadedBitCode) {
    // This is a case where we side loaded a llvm bitcode module.
    // The module is already complete so we avoid reading entirely.
    return true;
  }

  LLVMDumpLevel DumpLevel = JitContext->Options.DumpLevel;

  LLILCJitPerThreadState *PerThreadState = State.get();
  GenIR Reader(JitContext, &PerThreadState->ClassTypeMap,
               &PerThreadState->ReverseClassTypeMap,
               &PerThreadState->BoxedTypeMap, &PerThreadState->ArrayTypeMap,
               &PerThreadState->FieldIndexMap);

  std::string FuncName = JitContext->CurrentModule->getModuleIdentifier();

  try {
    Reader.msilToIR();
  } catch (NotYetImplementedException &Nyi) {
    if (DumpLevel >= SUMMARY) {
      errs() << "Failed to read " << FuncName << '[' << Nyi.reason() << "]\n";
    }
    return false;
  }

  Function *Func = JitContext->CurrentModule->getFunction(FuncName);
  bool IsOk = !verifyFunction(*Func, &dbgs());

  assert(IsOk);

  if (IsOk) {
    if (DumpLevel >= SUMMARY) {
      errs() << "Successfully read " << FuncName << '\n';
    }
  } else {
    if (DumpLevel >= SUMMARY) {
      errs() << "Failed to read " << FuncName << "[verification error]\n";
    }
    return false;
  }

  if (DumpLevel == VERBOSE) {
    Func->dump();
  }

  JitContext->MethodName = FuncName;

  return IsOk;
}

// Stop gap function to allocate and emit stub GCInfo for the method.
// This avoids a crash in the EE.
// Assuming that this will be replaced by an override of the GCMetaData
// or similar writer in LLVM once we move to the safepoint design.
bool LLILCJit::outputGCInfo(LLILCJitContext *JitContext) {
  size_t Size = 5;
  void *GCInfoBuffer = JitContext->JitInfo->allocGCInfo(Size);

  if (GCInfoBuffer == nullptr) {
    return false;
  }

  // First word of the GCInfoBuffer should be the size of the method.
  *(uint32_t *)GCInfoBuffer = JitContext->HotCodeSize;

  // 0x8 is the end sentinel of the buffer.
  *(((char *)GCInfoBuffer) + 4) = 0x8;

  return true;
}

// Notification from the runtime that any caches should be cleaned up.
void LLILCJit::clearCache() { return; }

// Notify runtime if we have something to clean up
BOOL LLILCJit::isCacheCleanupRequired() { return FALSE; }

// Verify the JIT/EE interface identifier.
void LLILCJit::getVersionIdentifier(GUID *VersionIdentifier) {
  _ASSERTE(VersionIdentifier != nullptr);
  memcpy(VersionIdentifier, &JITEEVersionIdentifier, sizeof(GUID));
}

// Raise a fatal error.
void __cdecl LLILCJit::fatal(int Errnum, ...) {
  _ASSERTE(FAILED(Errnum));
  ULONG_PTR ExceptArg = Errnum;
  RaiseException(CORJIT_INTERNALERROR, EXCEPTION_NONCONTINUABLE, 1, &ExceptArg);
}

//  Handle an abort signal from LLVM. We don't do anything, but registering
//  this handler avoids a crash when LLVM goes down.
void LLILCJit::signalHandler(void *Cookie) {
  // do nothing
}
<commit_msg>Discard functions with landing pads<commit_after>//===---- lib/Jit/LLILCJit.cpp --------------------------------*- C++ -*-===//
//
// LLILC
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license.
// See LICENSE file in the project root for full license information.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Implementation of the main Jit entry points.
///
//===----------------------------------------------------------------------===//

#include "jitpch.h"
#include "LLILCJit.h"
#include "options.h"
#include "readerir.h"
#include "abi.h"
#include "EEMemoryManager.h"
#include "llvm/CodeGen/GCs.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include <string>

using namespace llvm;

// Get the GC-Scheme used by the runtime -- conservative/precise
// For now this is done by directly accessing environment variable.
// When CLR config support is included, update it here.
bool shouldUseConservativeGC() {
  const char *LevelCStr = getenv("COMPLUS_GCCONSERVATIVE");
  return (LevelCStr != nullptr) && (strcmp(LevelCStr, "1") == 0);
}

// Determine if GC statepoints should be inserted.
bool shouldInsertStatepoints() {
  const char *LevelCStr = getenv("COMPLUS_INSERTSTATEPOINTS");
  return (LevelCStr != nullptr) && (strcmp(LevelCStr, "1") == 0);
}

// The one and only Jit Object.
LLILCJit *LLILCJit::TheJit = nullptr;

// This is guaranteed to be called by the EE
// in single-threaded mode.
ICorJitCompiler *__stdcall getJit() {

  if (LLILCJit::TheJit == nullptr) {
    // These are one-time only operations.
    // Create the singleton jit object.
    LLILCJit::TheJit = new LLILCJit();

    // Register a signal handler, mainly so that the critical
    // section that is entered on windows when LLVM gets a fatal error
    // is properly initialized.
    sys::AddSignalHandler(&LLILCJit::signalHandler, LLILCJit::TheJit);

    // Allow LLVM to pick up options via the environment
    cl::ParseEnvironmentOptions("LLILCJit", "COMplus_altjitOptions");
  }

  return LLILCJit::TheJit;
}

// Construct the JIT instance
LLILCJit::LLILCJit() {
  InitializeNativeTarget();
  InitializeNativeTargetAsmPrinter();
  InitializeNativeTargetAsmParser();
  llvm::linkStatepointExampleGC();

  ShouldInsertStatepoints = shouldInsertStatepoints();
  assert(ShouldInsertStatepoints ||
         shouldUseConservativeGC() && "Statepoints required for precise-GC");
}

#ifdef LLVM_ON_WIN32
// Windows only
BOOL WINAPI DllMain(HANDLE Instance, DWORD Reason, LPVOID Reserved) {
  if (Reason == DLL_PROCESS_ATTACH) {
    DisableThreadLibraryCalls((HINSTANCE)Instance);
  } else if (Reason == DLL_PROCESS_DETACH) {
    // TBD
  }
  return TRUE;
}
#endif // LLVM_ON_WIN32

extern "C" void __stdcall sxsJitStartup(void *CcCallbacks) {
  // nothing to do
}

void LLILCJitContext::outputDebugMethodName() {
  const size_t SizeOfBuffer = 512;
  char TempBuffer[SizeOfBuffer];
  const char *DebugClassName = nullptr;
  const char *DebugMethodName = nullptr;

  DebugMethodName = JitInfo->getMethodName(MethodInfo->ftn, &DebugClassName);
  dbgs() << format("INFO:  jitting method %s::%s using LLILCJit\n",
                   DebugClassName, DebugMethodName);
}

LLILCJitContext::LLILCJitContext(LLILCJitPerThreadState *PerThreadState)
    : State(PerThreadState), HasLoadedBitCode(false), Options(this) {
  this->Next = State->JitContext;
  State->JitContext = this;
}

LLILCJitContext::~LLILCJitContext() {
  LLILCJitContext *TopContext = State->JitContext;
  assert(this == TopContext && "Unbalanced contexts!");
  State->JitContext = TopContext->Next;
}

// This is the method invoked by the EE to Jit code.
CorJitResult LLILCJit::compileMethod(ICorJitInfo *JitInfo,
                                     CORINFO_METHOD_INFO *MethodInfo,
                                     UINT Flags, BYTE **NativeEntry,
                                     ULONG *NativeSizeOfCode) {

  // Bail if input is malformed
  if (nullptr == JitInfo || nullptr == MethodInfo || nullptr == NativeEntry ||
      nullptr == NativeSizeOfCode) {
    return CORJIT_INTERNALERROR;
  }

  // Prep main outputs
  *NativeEntry = nullptr;
  *NativeSizeOfCode = 0;

  // Set up state for this thread (if necessary)
  LLILCJitPerThreadState *PerThreadState = State.get();
  if (PerThreadState == nullptr) {
    PerThreadState = new LLILCJitPerThreadState();
    State.set(PerThreadState);
  }

  // Set up context for this Jit request
  LLILCJitContext Context = LLILCJitContext(PerThreadState);

  // Fill in context information from the CLR
  Context.JitInfo = JitInfo;
  Context.MethodInfo = MethodInfo;
  Context.Flags = Flags;
  JitInfo->getEEInfo(&Context.EEInfo);

  // Fill in context information from LLVM
  Context.LLVMContext = &PerThreadState->LLVMContext;
  std::unique_ptr<Module> M = Context.getModuleForMethod(MethodInfo);
  Context.CurrentModule = M.get();
  Context.CurrentModule->setTargetTriple(LLILC_TARGET_TRIPLE);
  Context.TheABIInfo = ABIInfo::get(*Context.CurrentModule);

  // Initialize per invocation JIT options. This should be done after the
  // rest of the Context is filled out as it has dependencies on Flags and
  // MethodInfo
  Context.Options.initialize();

  EngineBuilder Builder(std::move(M));
  std::string ErrStr;
  Builder.setErrorStr(&ErrStr);

  std::unique_ptr<RTDyldMemoryManager> MM(new EEMemoryManager(&Context));
  Builder.setMCJITMemoryManager(std::move(MM));

  TargetOptions Options;

  // Statepoint GC does not support FastIsel yet.
  Options.EnableFastISel = false;

  if (Context.Options.OptLevel != OptLevel::DEBUG_CODE) {
    Builder.setOptLevel(CodeGenOpt::Level::Default);
  } else {
    Builder.setOptLevel(CodeGenOpt::Level::None);
    Options.NoFramePointerElim = true;
  }

  Builder.setTargetOptions(Options);

  ExecutionEngine *NewEngine = Builder.create();

  if (!NewEngine) {
    errs() << "Could not create ExecutionEngine: " << ErrStr << "\n";
    return CORJIT_INTERNALERROR;
  }

  // Don't allow the EE to search for external symbols.
  NewEngine->DisableSymbolSearching();
  Context.EE = NewEngine;

  // Now jit the method.
  CorJitResult Result = CORJIT_INTERNALERROR;
  if (Context.Options.DumpLevel == VERBOSE) {
    Context.outputDebugMethodName();
  }
  bool HasMethod = this->readMethod(&Context);

  if (HasMethod) {

    if (ShouldInsertStatepoints) {
      // If using Precise GC, run the GC-Safepoint insertion
      // and lowering passes before generating code.
      legacy::PassManager Passes;
      Passes.add(createPlaceSafepointsPass());

      PassManagerBuilder PMBuilder;
      PMBuilder.OptLevel = 0;  // Set optimization level to -O0
      PMBuilder.SizeLevel = 0; // so that no additional phases are run.
      PMBuilder.populateModulePassManager(Passes);

      Passes.add(createRewriteStatepointsForGCPass(false));
      Passes.run(*Context.CurrentModule);
    }

    Context.EE->generateCodeForModule(Context.CurrentModule);

    // You need to pick up the COFFDyld changes from the MS branch of LLVM
    // or this will fail with an "Incompatible object format!" error
    // from LLVM's dynamic loader.
    uint64_t FunctionAddress =
        Context.EE->getFunctionAddress(Context.MethodName);
    *NativeEntry = (BYTE *)FunctionAddress;

    // TODO: ColdCodeSize, or separated code, is not enabled or included.
    *NativeSizeOfCode = Context.HotCodeSize + Context.ReadOnlyDataSize;

    // This is a stop-gap point to issue a default stub of GC info. This lets
    // the CLR consume our methods cleanly. (and the ETW tracing still works)
    // Down the road this will be superseded by a CLR specific
    // GCMetadataPrinter instance or similar.
    this->outputGCInfo(&Context);

    // Dump out any enabled timing info.
    TimerGroup::printAll(errs());

    // Tell the CLR that we've successfully generated code for this method.
    Result = CORJIT_OK;
  }

  // Clean up a bit
  delete Context.EE;
  Context.EE = nullptr;
  delete Context.TheABIInfo;
  Context.TheABIInfo = nullptr;

  return Result;
}

std::unique_ptr<Module>
LLILCJitContext::getModuleForMethod(CORINFO_METHOD_INFO *MethodInfo) {
  // Grab name info from the EE.
  const char *DebugClassName = nullptr;
  const char *DebugMethodName = nullptr;
  DebugMethodName = JitInfo->getMethodName(MethodInfo->ftn, &DebugClassName);

  // Stop gap name.  The full naming will likely require some more info.
  std::string ModName(DebugClassName);
  ModName.append(1, '.');
  ModName.append(DebugMethodName);

  std::unique_ptr<Module> M;
  char *BitcodePath = getenv("BITCODE_PATH");

  if (BitcodePath != nullptr) {
    SMDiagnostic Err;
    std::string Path = std::string(BitcodePath);

    // If there is a bitcode path, use the debug module name to look for an .bc
    // file. If one is found then load it directly.
    Path.append("\\");
    Path.append(ModName);
    Path.append(".bc");
    M = llvm::parseIRFile(Path, Err, *this->LLVMContext);

    if (!M) {
      // Err.print("IR Parsing failed: ", errs());
      this->HasLoadedBitCode = false;
    } else {
      std::string Message("Loaded bitcode from: ");
      Message.append(Path);
      dbgs() << Message;
      this->HasLoadedBitCode = true;
    }
  }

  if (!this->HasLoadedBitCode) {
    M = std::unique_ptr<Module>(new Module(ModName, *this->LLVMContext));
  }

  return std::move(M);
}

// Read method MSIL and construct LLVM bitcode
bool LLILCJit::readMethod(LLILCJitContext *JitContext) {
  if (JitContext->HasLoadedBitCode) {
    // This is a case where we side loaded a llvm bitcode module.
    // The module is already complete so we avoid reading entirely.
    return true;
  }

  LLVMDumpLevel DumpLevel = JitContext->Options.DumpLevel;

  LLILCJitPerThreadState *PerThreadState = State.get();
  GenIR Reader(JitContext, &PerThreadState->ClassTypeMap,
               &PerThreadState->ReverseClassTypeMap,
               &PerThreadState->BoxedTypeMap, &PerThreadState->ArrayTypeMap,
               &PerThreadState->FieldIndexMap);

  std::string FuncName = JitContext->CurrentModule->getModuleIdentifier();

  try {
    Reader.msilToIR();
  } catch (NotYetImplementedException &Nyi) {
    if (DumpLevel >= SUMMARY) {
      errs() << "Failed to read " << FuncName << '[' << Nyi.reason() << "]\n";
    }
    return false;
  }

  Function *Func = JitContext->CurrentModule->getFunction(FuncName);
  bool IsOk = !verifyFunction(*Func, &dbgs());

  assert(IsOk);

  if (IsOk) {
    if (DumpLevel >= SUMMARY) {
      errs() << "Successfully read " << FuncName << '\n';
    }
  } else {
    if (DumpLevel >= SUMMARY) {
      errs() << "Failed to read " << FuncName << "[verification error]\n";
    }
    return false;
  }

  if (DumpLevel == VERBOSE) {
    Func->dump();
  }

  JitContext->MethodName = FuncName;

  if (IsOk) {
    for (BasicBlock &BB : *Func) {
      if (BB.getLandingPadInst() != nullptr) {
        // Don't try to push the EH code downstream (we'll currently fail on
        // the first method with EH if we do that, which prevents being able
        // to see IR for later methods)
        return false;
      }
    }
  }

  return IsOk;
}

// Stop gap function to allocate and emit stub GCInfo for the method.
// This avoids a crash in the EE.
// Assuming that this will be replaced by an override of the GCMetaData
// or similar writer in LLVM once we move to the safepoint design.
bool LLILCJit::outputGCInfo(LLILCJitContext *JitContext) {
  size_t Size = 5;
  void *GCInfoBuffer = JitContext->JitInfo->allocGCInfo(Size);

  if (GCInfoBuffer == nullptr) {
    return false;
  }

  // First word of the GCInfoBuffer should be the size of the method.
  *(uint32_t *)GCInfoBuffer = JitContext->HotCodeSize;

  // 0x8 is the end sentinel of the buffer.
  *(((char *)GCInfoBuffer) + 4) = 0x8;

  return true;
}

// Notification from the runtime that any caches should be cleaned up.
void LLILCJit::clearCache() { return; }

// Notify runtime if we have something to clean up
BOOL LLILCJit::isCacheCleanupRequired() { return FALSE; }

// Verify the JIT/EE interface identifier.
void LLILCJit::getVersionIdentifier(GUID *VersionIdentifier) {
  _ASSERTE(VersionIdentifier != nullptr);
  memcpy(VersionIdentifier, &JITEEVersionIdentifier, sizeof(GUID));
}

// Raise a fatal error.
void __cdecl LLILCJit::fatal(int Errnum, ...) {
  _ASSERTE(FAILED(Errnum));
  ULONG_PTR ExceptArg = Errnum;
  RaiseException(CORJIT_INTERNALERROR, EXCEPTION_NONCONTINUABLE, 1, &ExceptArg);
}

//  Handle an abort signal from LLVM. We don't do anything, but registering
//  this handler avoids a crash when LLVM goes down.
void LLILCJit::signalHandler(void *Cookie) {
  // do nothing
}
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <string.h>

#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/byte_buffer_reader.h"
#include "grpc/support/slice.h"

#include "byte_buffer.h"

namespace grpc {
namespace node {

using v8::Context;
using v8::Function;
using v8::Local;
using v8::Object;
using v8::Number;
using v8::Value;

grpc_byte_buffer *BufferToByteBuffer(Local<Value> buffer) {
  Nan::HandleScope scope;
  int length = ::node::Buffer::Length(buffer);
  char *data = ::node::Buffer::Data(buffer);
  gpr_slice slice = gpr_slice_malloc(length);
  memcpy(GPR_SLICE_START_PTR(slice), data, length);
  grpc_byte_buffer *byte_buffer(grpc_raw_byte_buffer_create(&slice, 1));
  gpr_slice_unref(slice);
  return byte_buffer;
}

namespace {
void delete_buffer(char *data, void *hint) { delete[] data; }
}

Local<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
  Nan::EscapableHandleScope scope;
  if (buffer == NULL) {
    return scope.Escape(Nan::Null());
  }
  grpc_byte_buffer_reader reader;
  grpc_byte_buffer_reader_init(&reader, buffer);
  gpr_slice slice = grpc_byte_buffer_reader_readall(&reader);
  size_t length = GPR_SLICE_LENGTH(slice);
  char *result = new char[length];
  memcpy(result, GPR_SLICE_START_PTR(slice), length);
  return scope.Escape(MakeFastBuffer(
      Nan::NewBuffer(result, length, delete_buffer, NULL).ToLocalChecked()));
}

Local<Value> MakeFastBuffer(Local<Value> slowBuffer) {
  Nan::EscapableHandleScope scope;
  Local<Object> globalObj = Nan::GetCurrentContext()->Global();
  Local<Function> bufferConstructor = Local<Function>::Cast(
      globalObj->Get(Nan::New("Buffer").ToLocalChecked()));
  Local<Value> consArgs[3] = {
    slowBuffer,
    Nan::New<Number>(::node::Buffer::Length(slowBuffer)),
    Nan::New<Number>(0)
  };
  Local<Object> fastBuffer = bufferConstructor->NewInstance(3, consArgs);
  return scope.Escape(fastBuffer);
}
}  // namespace node
}  // namespace grpc
<commit_msg>Remember to unref the slice<commit_after>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <string.h>

#include <node.h>
#include <nan.h>
#include "grpc/grpc.h"
#include "grpc/byte_buffer_reader.h"
#include "grpc/support/slice.h"

#include "byte_buffer.h"

namespace grpc {
namespace node {

using v8::Context;
using v8::Function;
using v8::Local;
using v8::Object;
using v8::Number;
using v8::Value;

grpc_byte_buffer *BufferToByteBuffer(Local<Value> buffer) {
  Nan::HandleScope scope;
  int length = ::node::Buffer::Length(buffer);
  char *data = ::node::Buffer::Data(buffer);
  gpr_slice slice = gpr_slice_malloc(length);
  memcpy(GPR_SLICE_START_PTR(slice), data, length);
  grpc_byte_buffer *byte_buffer(grpc_raw_byte_buffer_create(&slice, 1));
  gpr_slice_unref(slice);
  return byte_buffer;
}

namespace {
void delete_buffer(char *data, void *hint) { delete[] data; }
}

Local<Value> ByteBufferToBuffer(grpc_byte_buffer *buffer) {
  Nan::EscapableHandleScope scope;
  if (buffer == NULL) {
    return scope.Escape(Nan::Null());
  }
  grpc_byte_buffer_reader reader;
  grpc_byte_buffer_reader_init(&reader, buffer);
  gpr_slice slice = grpc_byte_buffer_reader_readall(&reader);
  size_t length = GPR_SLICE_LENGTH(slice);
  char *result = new char[length];
  memcpy(result, GPR_SLICE_START_PTR(slice), length);
  gpr_slice_unref(slice);
  return scope.Escape(MakeFastBuffer(
      Nan::NewBuffer(result, length, delete_buffer, NULL).ToLocalChecked()));
}

Local<Value> MakeFastBuffer(Local<Value> slowBuffer) {
  Nan::EscapableHandleScope scope;
  Local<Object> globalObj = Nan::GetCurrentContext()->Global();
  Local<Function> bufferConstructor = Local<Function>::Cast(
      globalObj->Get(Nan::New("Buffer").ToLocalChecked()));
  Local<Value> consArgs[3] = {
    slowBuffer,
    Nan::New<Number>(::node::Buffer::Length(slowBuffer)),
    Nan::New<Number>(0)
  };
  Local<Object> fastBuffer = bufferConstructor->NewInstance(3, consArgs);
  return scope.Escape(fastBuffer);
}
}  // namespace node
}  // namespace grpc
<|endoftext|>


<commit_before>// Copyright (c) 2011-2013, Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <fstream>
#include <list>
#include <map>
#include <sstream>
#include <string>
#include <subhook.h>
#include <amx/amx.h>
#include <amx_profiler/call_graph_writer_dot.h>
#include <amx_profiler/debug_info.h>
#include <amx_profiler/statistics_writer_html.h>
#include <amx_profiler/statistics_writer_text.h>
#include <amx_profiler/statistics_writer_json.h>
#include <amx_profiler/profiler.h>
#include "amxpath.h"
#include "configreader.h"
#include "plugin.h"
#include "pluginversion.h"

typedef void (*logprintf_t)(const char *format, ...);

extern void *pAMXFunctions;

static logprintf_t logprintf;

typedef std::map<AMX*, AMX_DEBUG> AmxToAmxDebugMap;
static AmxToAmxDebugMap old_debug_hooks;

typedef std::map<AMX*, amx_profiler::Profiler*> AmxToProfilerMap;
static AmxToProfilerMap profilers;

typedef std::map<AMX*, amx_profiler::DebugInfo*> AmxToDebugInfoMap;
static AmxToDebugInfoMap debug_infos;

// Plugin settings and their defauls.
namespace cfg {
  bool          profile_gamemode      = false;
  std::string   profile_filterscripts = "";
  std::string   profile_format        = "html";
  bool          call_graph            = false;
  std::string   call_graph_format     = "dot";
}

static void PrintException(const std::exception &e) {
  logprintf("[profiler] Error: %s", e.what());
}

namespace hooks {

SubHook amx_Exec_hook;
SubHook amx_Callback_hook;

int AMXAPI amx_Debug(AMX *amx) {
  amx_profiler::Profiler *profiler = ::profilers[amx];
  if (profiler) {
    try {
      profiler->DebugHook();
    } catch (const std::exception &e) {
      PrintException(e);
    }
  }

  AmxToAmxDebugMap::const_iterator iterator = old_debug_hooks.find(amx);
  if (iterator != old_debug_hooks.end()) {
    if (iterator->second != 0) {
      return (iterator->second)(amx);
    }
  }

  return AMX_ERR_NONE;
}

int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) {
  SubHook::ScopedRemove r(&amx_Callback_hook);
  SubHook::ScopedInstall i(&amx_Exec_hook);

  amx_profiler::Profiler *profiler = ::profilers[amx];
  if (profiler != 0) {
    try {
      return profiler->CallbackHook(index, result, params);
    } catch (const std::exception &e) {
      PrintException(e);
    }
  }

  return ::amx_Callback(amx, index, result, params);
}

int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) {
  SubHook::ScopedRemove r(&amx_Exec_hook);
  SubHook::ScopedInstall i(&amx_Callback_hook);

  amx_profiler::Profiler *profiler = ::profilers[amx];
  if (profiler != 0) {
    try {
      return profiler->ExecHook(retval, index);
    } catch (const std::exception &e) {
      PrintException(e);
    }
  }

  return ::amx_Exec(amx, retval, index);
}

} // namespace hooks

static void ToLower(std::string &s) {
  std::transform(s.begin(), s.end(), s.begin(), ::tolower);
}

static std::string GetAmxPath(AMX *amx) {
  // Has to be static to make caching work in AmxUnload().
  static AmxPathFinder finder;
  finder.AddSearchDirectory("gamemodes");
  finder.AddSearchDirectory("filterscripts");
  return finder.FindAmxPath(amx);
}

static std::string ToUnixPath(const std::string &path) {
  std::string fsPath = path;
  std::replace(fsPath.begin(), fsPath.end(), '\\', '/');
  return fsPath;
}

static bool IsGameMode(const std::string &amx_name) {
  return ToUnixPath(amx_name).find("gamemodes/") != std::string::npos;
}

static bool IsFilterScript(const std::string &amx_name) {
  return ToUnixPath(amx_name).find("filterscripts/") != std::string::npos;
}

static bool WantsProfiler(const std::string &amx_name) {
  std::string good_amx_name = ToUnixPath(amx_name);

  if (IsGameMode(good_amx_name)) {
    if (cfg::profile_gamemode) {
      return true;
    }
  } else if (IsFilterScript(good_amx_name)) {
    std::string fs_list = cfg::profile_filterscripts;
    std::stringstream fs_stream(fs_list);
    do {
      std::string fs_name;
      fs_stream >> fs_name;
      if (good_amx_name == "filterscripts/" + fs_name + ".amx"
          || good_amx_name == "filterscripts/" + fs_name) {
        return true;
      }
    } while (!fs_stream.eof());
  }

  return false;
}

template<typename Map, typename Key>
void DeleteMapEntry(Map &map, const Key &key) {
  typename Map::iterator iterator = map.find(key);
  if (iterator != map.end()) {
    delete iterator->second;
    map.erase(iterator);
  }
}

template<typename Func>
void *FunctionToVoidPtr(Func func) {
  return (void*)func;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
  return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

static void *AMXAPI amx_Align_stub(void *v) {
  return v;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
  pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
  logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];

  void **exports = reinterpret_cast<void**>(pAMXFunctions);

  exports[PLUGIN_AMX_EXPORT_Align16] = FunctionToVoidPtr(amx_Align_stub);
  exports[PLUGIN_AMX_EXPORT_Align32] = FunctionToVoidPtr(amx_Align_stub);
  exports[PLUGIN_AMX_EXPORT_Align64] = FunctionToVoidPtr(amx_Align_stub);

  hooks::amx_Exec_hook.Install(exports[PLUGIN_AMX_EXPORT_Exec],
                               FunctionToVoidPtr(hooks::amx_Exec));
  hooks::amx_Callback_hook.Install(exports[PLUGIN_AMX_EXPORT_Callback],
                                   FunctionToVoidPtr(hooks::amx_Callback));

  ConfigReader server_cfg("server.cfg");
  server_cfg.GetOption("profile_gamemode", cfg::profile_gamemode);
  server_cfg.GetOption("profile_filterscripts", cfg::profile_filterscripts);
  server_cfg.GetOption("profile_format", cfg::profile_format);
  server_cfg.GetOption("call_graph", cfg::call_graph);
  server_cfg.GetOption("call_graph_format", cfg::call_graph_format);

  logprintf("  Profiler v" PROJECT_VERSION_STRING " is OK.");

  return true;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
  std::string filename = GetAmxPath(amx);

  if (filename.empty()) {
    logprintf("[profiler] Failed to find corresponding .amx file");
    return AMX_ERR_NONE;
  }

  if (!WantsProfiler(filename)) {
    return AMX_ERR_NONE;
  }

  try {
    amx_profiler::DebugInfo *debug_info = 0;

    if (amx_profiler::HaveDebugInfo(amx)) {
      debug_info = new amx_profiler::DebugInfo(filename);
      if (debug_info->is_loaded()) {
        ::debug_infos[amx] = debug_info;
      } else {
        logprintf("[profiler] Error loading debug info: %s",
                  aux_StrError(debug_info->last_error()));
        delete debug_info;
        debug_info = 0;
      }
    }

    amx_profiler::Profiler *profiler = new amx_profiler::Profiler(amx,
                                                                  debug_info);
    profiler->set_call_graph_enabled(cfg::call_graph);

    if (debug_info != 0) {
      logprintf("[profiler] Attached profiler to '%s'", filename.c_str());
    } else {
      logprintf("[profiler] Attached profiler to '%s' (no debug info)",
                filename.c_str());
    }

    ::old_debug_hooks[amx] = amx->debug;
    amx_SetDebugHook(amx, hooks::amx_Debug);

    ::profilers[amx] = profiler;
  }
  catch (const std::exception &e) {
    PrintException(e);
  }

  return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
  try {
    amx_profiler::Profiler *profiler = ::profilers[amx];

    if (profiler != 0) {
      std::string amx_path = GetAmxPath(amx);
      std::string amx_name = std::string(amx_path, 0,
                                         amx_path.find_last_of("."));

      ToLower(cfg::profile_format);
      std::string profile_filename = amx_name + "-profile." +
                                     cfg::profile_format;
      std::ofstream profile_stream(profile_filename.c_str());

      if (profile_stream.is_open()) {
        amx_profiler::StatisticsWriter *writer = 0;

        if (cfg::profile_format == "html") {
          writer = new amx_profiler::StatisticsWriterHtml;
        } else if (cfg::profile_format == "txt" ||
                   cfg::profile_format == "text") {
          writer = new amx_profiler::StatisticsWriterText;
        } else if (cfg::profile_format == "json") {
          writer = new amx_profiler::StatisticsWriterJson;
        } else {
          logprintf("[profiler] Unrecognized profile format '%s'",
                    cfg::profile_format.c_str());
        }

        if (writer != 0) {
          logprintf("[profiler] Writing profile to '%s'",
                    profile_filename.c_str());
          writer->set_stream(&profile_stream);
          writer->set_script_name(amx_path);
          writer->set_print_date(true);
          writer->set_print_run_time(true);
          writer->Write(profiler->stats());
          delete writer;
        }

        profile_stream.close();
      } else {
        logprintf("[profiler]: Error opening file '%s'",
                  profile_filename.c_str());
      }

      if (cfg::call_graph) {
        ToLower(cfg::call_graph_format);
        std::string call_graph_filename = amx_name + "-calls." +
                                          cfg::call_graph_format;
        std::ofstream call_graph_stream(call_graph_filename.c_str());

        if (call_graph_stream.is_open()) {
          amx_profiler::CallGraphWriterDot *writer = 0;

          if (cfg::call_graph_format == "dot") {
            writer = new amx_profiler::CallGraphWriterDot;
          } else {
            logprintf("[profiler] Unrecognized call graph format '%s'",
                      cfg::call_graph_format.c_str());
          }

          if (writer != 0) {
            logprintf("[profiler] Writing call graph to '%s'",
                      call_graph_filename.c_str());
            writer->set_stream(&call_graph_stream);
            writer->set_script_name(amx_path);
            writer->set_root_node_name("SA-MP Server");
            writer->Write(profiler->call_graph());
            delete writer;
          }

          call_graph_stream.close();
        } else {
          logprintf("[profiler]: Error opening file '%s'",
                    call_graph_filename.c_str());
        }
      }
    }
  }
  catch (const std::exception &e) {
    PrintException(e);
  }

  DeleteMapEntry(::profilers, amx);
  DeleteMapEntry(::debug_infos, amx);

  return AMX_ERR_NONE;
}
<commit_msg>Wrap ALL code in exported functions in try/catch for extra safety<commit_after>// Copyright (c) 2011-2013, Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <fstream>
#include <list>
#include <map>
#include <sstream>
#include <string>
#include <subhook.h>
#include <amx/amx.h>
#include <amx_profiler/call_graph_writer_dot.h>
#include <amx_profiler/debug_info.h>
#include <amx_profiler/statistics_writer_html.h>
#include <amx_profiler/statistics_writer_text.h>
#include <amx_profiler/statistics_writer_json.h>
#include <amx_profiler/profiler.h>
#include "amxpath.h"
#include "configreader.h"
#include "plugin.h"
#include "pluginversion.h"

typedef void (*logprintf_t)(const char *format, ...);

extern void *pAMXFunctions;

static logprintf_t logprintf;

typedef std::map<AMX*, AMX_DEBUG> AmxToAmxDebugMap;
static AmxToAmxDebugMap old_debug_hooks;

typedef std::map<AMX*, amx_profiler::Profiler*> AmxToProfilerMap;
static AmxToProfilerMap profilers;

typedef std::map<AMX*, amx_profiler::DebugInfo*> AmxToDebugInfoMap;
static AmxToDebugInfoMap debug_infos;

// Plugin settings and their defauls.
namespace cfg {
  bool          profile_gamemode      = false;
  std::string   profile_filterscripts = "";
  std::string   profile_format        = "html";
  bool          call_graph            = false;
  std::string   call_graph_format     = "dot";
}

static void PrintException(const std::exception &e) {
  logprintf("[profiler] Error: %s", e.what());
}

namespace hooks {

SubHook amx_Exec_hook;
SubHook amx_Callback_hook;

int AMXAPI amx_Debug(AMX *amx) {
  amx_profiler::Profiler *profiler = ::profilers[amx];
  if (profiler) {
    try {
      profiler->DebugHook();
    } catch (const std::exception &e) {
      PrintException(e);
    }
  }

  AmxToAmxDebugMap::const_iterator iterator = old_debug_hooks.find(amx);
  if (iterator != old_debug_hooks.end()) {
    if (iterator->second != 0) {
      return (iterator->second)(amx);
    }
  }

  return AMX_ERR_NONE;
}

int AMXAPI amx_Callback(AMX *amx, cell index, cell *result, cell *params) {
  SubHook::ScopedRemove r(&amx_Callback_hook);
  SubHook::ScopedInstall i(&amx_Exec_hook);

  amx_profiler::Profiler *profiler = ::profilers[amx];
  if (profiler != 0) {
    try {
      return profiler->CallbackHook(index, result, params);
    } catch (const std::exception &e) {
      PrintException(e);
    }
  }

  return ::amx_Callback(amx, index, result, params);
}

int AMXAPI amx_Exec(AMX *amx, cell *retval, int index) {
  SubHook::ScopedRemove r(&amx_Exec_hook);
  SubHook::ScopedInstall i(&amx_Callback_hook);

  amx_profiler::Profiler *profiler = ::profilers[amx];
  if (profiler != 0) {
    try {
      return profiler->ExecHook(retval, index);
    } catch (const std::exception &e) {
      PrintException(e);
    }
  }

  return ::amx_Exec(amx, retval, index);
}

} // namespace hooks

static void ToLower(std::string &s) {
  std::transform(s.begin(), s.end(), s.begin(), ::tolower);
}

static std::string GetAmxPath(AMX *amx) {
  // Has to be static to make caching work in AmxUnload().
  static AmxPathFinder finder;
  finder.AddSearchDirectory("gamemodes");
  finder.AddSearchDirectory("filterscripts");
  return finder.FindAmxPath(amx);
}

static std::string ToUnixPath(const std::string &path) {
  std::string fsPath = path;
  std::replace(fsPath.begin(), fsPath.end(), '\\', '/');
  return fsPath;
}

static bool IsGameMode(const std::string &amx_name) {
  return ToUnixPath(amx_name).find("gamemodes/") != std::string::npos;
}

static bool IsFilterScript(const std::string &amx_name) {
  return ToUnixPath(amx_name).find("filterscripts/") != std::string::npos;
}

static bool WantsProfiler(const std::string &amx_name) {
  std::string good_amx_name = ToUnixPath(amx_name);

  if (IsGameMode(good_amx_name)) {
    if (cfg::profile_gamemode) {
      return true;
    }
  } else if (IsFilterScript(good_amx_name)) {
    std::string fs_list = cfg::profile_filterscripts;
    std::stringstream fs_stream(fs_list);
    do {
      std::string fs_name;
      fs_stream >> fs_name;
      if (good_amx_name == "filterscripts/" + fs_name + ".amx"
          || good_amx_name == "filterscripts/" + fs_name) {
        return true;
      }
    } while (!fs_stream.eof());
  }

  return false;
}

template<typename Map, typename Key>
void DeleteMapEntry(Map &map, const Key &key) {
  typename Map::iterator iterator = map.find(key);
  if (iterator != map.end()) {
    delete iterator->second;
    map.erase(iterator);
  }
}

template<typename Func>
void *FunctionToVoidPtr(Func func) {
  return (void*)func;
}

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
  return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

static void *AMXAPI amx_Align_stub(void *v) {
  return v;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
  try {
    pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];
    logprintf = (logprintf_t) ppData[PLUGIN_DATA_LOGPRINTF];

    void **exports = reinterpret_cast<void**>(pAMXFunctions);

    exports[PLUGIN_AMX_EXPORT_Align16] = FunctionToVoidPtr(amx_Align_stub);
    exports[PLUGIN_AMX_EXPORT_Align32] = FunctionToVoidPtr(amx_Align_stub);
    exports[PLUGIN_AMX_EXPORT_Align64] = FunctionToVoidPtr(amx_Align_stub);

    hooks::amx_Exec_hook.Install(exports[PLUGIN_AMX_EXPORT_Exec],
                                 FunctionToVoidPtr(hooks::amx_Exec));
    hooks::amx_Callback_hook.Install(exports[PLUGIN_AMX_EXPORT_Callback],
                                     FunctionToVoidPtr(hooks::amx_Callback));

    ConfigReader server_cfg("server.cfg");
    server_cfg.GetOption("profile_gamemode", cfg::profile_gamemode);
    server_cfg.GetOption("profile_filterscripts", cfg::profile_filterscripts);
    server_cfg.GetOption("profile_format", cfg::profile_format);
    server_cfg.GetOption("call_graph", cfg::call_graph);
    server_cfg.GetOption("call_graph_format", cfg::call_graph_format);

    logprintf("  Profiler v" PROJECT_VERSION_STRING " is OK.");
  }
  catch (std::exception &e) {
    PrintException(e);
  }

  return true;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
  try {
    std::string filename = GetAmxPath(amx);

    if (filename.empty()) {
      logprintf("[profiler] Failed to find corresponding .amx file");
      return AMX_ERR_NONE;
    }

    if (!WantsProfiler(filename)) {
      return AMX_ERR_NONE;
    }

    amx_profiler::DebugInfo *debug_info = 0;

    if (amx_profiler::HaveDebugInfo(amx)) {
      debug_info = new amx_profiler::DebugInfo(filename);
      if (debug_info->is_loaded()) {
        ::debug_infos[amx] = debug_info;
      } else {
        logprintf("[profiler] Error loading debug info: %s",
                  aux_StrError(debug_info->last_error()));
        delete debug_info;
        debug_info = 0;
      }
    }

    amx_profiler::Profiler *profiler = new amx_profiler::Profiler(amx,
                                                                  debug_info);
    profiler->set_call_graph_enabled(cfg::call_graph);

    if (debug_info != 0) {
      logprintf("[profiler] Attached profiler to '%s'", filename.c_str());
    } else {
      logprintf("[profiler] Attached profiler to '%s' (no debug info)",
                filename.c_str());
    }

    ::old_debug_hooks[amx] = amx->debug;
    amx_SetDebugHook(amx, hooks::amx_Debug);

    ::profilers[amx] = profiler;
  }
  catch (const std::exception &e) {
    PrintException(e);
  }

  return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
  try {
    amx_profiler::Profiler *profiler = ::profilers[amx];

    if (profiler != 0) {
      std::string amx_path = GetAmxPath(amx);
      std::string amx_name = std::string(amx_path, 0,
                                         amx_path.find_last_of("."));

      ToLower(cfg::profile_format);
      std::string profile_filename = amx_name + "-profile." +
                                     cfg::profile_format;
      std::ofstream profile_stream(profile_filename.c_str());

      if (profile_stream.is_open()) {
        amx_profiler::StatisticsWriter *writer = 0;

        if (cfg::profile_format == "html") {
          writer = new amx_profiler::StatisticsWriterHtml;
        } else if (cfg::profile_format == "txt" ||
                   cfg::profile_format == "text") {
          writer = new amx_profiler::StatisticsWriterText;
        } else if (cfg::profile_format == "json") {
          writer = new amx_profiler::StatisticsWriterJson;
        } else {
          logprintf("[profiler] Unrecognized profile format '%s'",
                    cfg::profile_format.c_str());
        }

        if (writer != 0) {
          logprintf("[profiler] Writing profile to '%s'",
                    profile_filename.c_str());
          writer->set_stream(&profile_stream);
          writer->set_script_name(amx_path);
          writer->set_print_date(true);
          writer->set_print_run_time(true);
          writer->Write(profiler->stats());
          delete writer;
        }

        profile_stream.close();
      } else {
        logprintf("[profiler]: Error opening file '%s'",
                  profile_filename.c_str());
      }

      if (cfg::call_graph) {
        ToLower(cfg::call_graph_format);
        std::string call_graph_filename = amx_name + "-calls." +
                                          cfg::call_graph_format;
        std::ofstream call_graph_stream(call_graph_filename.c_str());

        if (call_graph_stream.is_open()) {
          amx_profiler::CallGraphWriterDot *writer = 0;

          if (cfg::call_graph_format == "dot") {
            writer = new amx_profiler::CallGraphWriterDot;
          } else {
            logprintf("[profiler] Unrecognized call graph format '%s'",
                      cfg::call_graph_format.c_str());
          }

          if (writer != 0) {
            logprintf("[profiler] Writing call graph to '%s'",
                      call_graph_filename.c_str());
            writer->set_stream(&call_graph_stream);
            writer->set_script_name(amx_path);
            writer->set_root_node_name("SA-MP Server");
            writer->Write(profiler->call_graph());
            delete writer;
          }

          call_graph_stream.close();
        } else {
          logprintf("[profiler]: Error opening file '%s'",
                    call_graph_filename.c_str());
        }
      }
    }

    DeleteMapEntry(::profilers, amx);
    DeleteMapEntry(::debug_infos, amx);
  }
  catch (const std::exception &e) {
    PrintException(e);
  }

  return AMX_ERR_NONE;
}
<|endoftext|>


<commit_before>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD 3-Clause License within the LICENSE file.

#include <tev/HelpWindow.h>

#include <nanogui/button.h>
#include <nanogui/icons.h>
#include <nanogui/label.h>
#include <nanogui/layout.h>
#include <nanogui/opengl.h>
#include <nanogui/tabwidget.h>
#include <nanogui/window.h>

using namespace nanogui;
using namespace std;

TEV_NAMESPACE_BEGIN

#ifdef __APPLE__
string HelpWindow::COMMAND = "Cmd";
#else
string HelpWindow::COMMAND = "Ctrl";
#endif

#ifdef __APPLE__
string HelpWindow::ALT = "Opt";
#else
string HelpWindow::ALT = "Alt";
#endif

HelpWindow::HelpWindow(Widget *parent, bool supportsHdr, function<void()> closeCallback)
    : Window{parent, "Help"}, mCloseCallback{closeCallback} {

    auto closeButton = new Button{button_panel(), "", FA_TIMES};
    closeButton->set_callback(mCloseCallback);

    set_layout(new GroupLayout{});
    set_fixed_width(600);

    TabWidget* tabWidget = new TabWidget{this};

    // Keybindings tab
    {
        Widget* shortcuts = new Widget(tabWidget);
        shortcuts->set_layout(new GroupLayout{});
        tabWidget->append_tab("Keybindings", shortcuts);

        auto addRow = [](Widget* current, string keys, string desc) {
            auto row = new Widget{current};
            row->set_layout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 0, 10});
            auto descWidget = new Label{row, desc, "sans"};
            descWidget->set_fixed_width(250);
            new Label{row, keys, "sans-bold"};
        };

        new Label{shortcuts, "Image Loading", "sans-bold", 18};
        auto imageLoading = new Widget{shortcuts};
        imageLoading->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageLoading, COMMAND + "+O",                             "Open Image");
        addRow(imageLoading, COMMAND + "+S",                             "Save View as Image");
        addRow(imageLoading, COMMAND + "+R or F5",                       "Reload Image");
        addRow(imageLoading, COMMAND + "+Shift+R or " + COMMAND + "+F5", "Reload All Images");
        addRow(imageLoading, COMMAND + "+W",                             "Close Image");
        addRow(imageLoading, COMMAND + "+Shift+W",                       "Close All Images");
        addRow(imageLoading, COMMAND + "+C",                             "Copy Image or Path to Clipboard");
        addRow(imageLoading, COMMAND + "+V",                             "Paste Image from Clipboard");

        new Label{shortcuts, "Image Options", "sans-bold", 18};
        auto imageSelection = new Widget{shortcuts};
        imageSelection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageSelection, "Left Click",          "Select Hovered Image");
        addRow(imageSelection, "1…9",                 "Select N-th Image");
        addRow(imageSelection, "Down or S / Up or W", "Select Next / Previous Image");

        addRow(imageSelection, "Click & Drag (+Shift/" + COMMAND + ")", "Translate Image");
        addRow(imageSelection, "Plus / Minus / Scroll (+Shift/" + COMMAND + ")", "Zoom In / Out of Image");

        addRow(imageSelection, "F", "Fit Image to Screen");
        addRow(imageSelection, "N", "Normalize Image to [0, 1]");
        addRow(imageSelection, "R", "Reset Image Parameters");
        if (supportsHdr) {
            addRow(imageSelection, "L", "Display the image as if on an LDR screen");
        }

        addRow(imageSelection, "Shift+Right or Shift+D / Shift+Left or Shift+A", "Select Next / Previous Tonemap");

        addRow(imageSelection, "E / Shift+E", "Increase / Decrease Exposure by 0.5");
        addRow(imageSelection, "O / Shift+O", "Increase / Decrease Offset by 0.1");

        addRow(imageSelection, ALT + " (hold)", "Display raw bytes on pixels when zoomed-in");

        new Label{shortcuts, "Reference Options", "sans-bold", 18};
        auto referenceSelection = new Widget{shortcuts};
        referenceSelection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(referenceSelection, "Shift (hold)",                                "View currently selected Reference");
        addRow(referenceSelection, "Shift+Left Click or Right Click",             "Select Hovered Image as Reference");
        addRow(referenceSelection, "Shift+1…9",                                   "Select N-th Image as Reference");
        addRow(referenceSelection, "Shift+Down or Shift+S / Shift+Up or Shift+W", "Select Next / Previous Image as Reference");

        addRow(referenceSelection, "Ctrl (hold)",                                "View selected Image if Reference is selected");
        addRow(referenceSelection, "Ctrl+Right or Ctrl+D / Ctrl+Left or Ctrl+A", "Select Next / Previous Error Metric");

        new Label{shortcuts, "Channel Group Options", "sans-bold", 18};
        auto groupSelection = new Widget{shortcuts};
        groupSelection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(groupSelection, "Left Click",             "Select Hovered Channel Group");
        addRow(groupSelection, "Ctrl+1…9",               "Select N-th Channel Group");
        addRow(groupSelection, "Right or D / Left or A", "Select Next / Previous Channel Group");

        new Label{shortcuts, "Interface", "sans-bold", 18};
        auto ui = new Widget{shortcuts};
        ui->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(ui, ALT + "+Enter", "Maximize");
        addRow(ui, COMMAND + "+B", "Toggle GUI");
        addRow(ui, "H",            "Show Help (this Window)");
        addRow(ui, COMMAND + "+P", "Find Image or Channel Group");
        addRow(ui, "Q or Esc",     "Quit");
    }

    // About tab
    {
        Widget* about = new Widget(tabWidget);
        about->set_layout(new GroupLayout{});
        tabWidget->append_tab("About", about);

        auto addText = [](Widget* current, string text, string font = "sans", int fontSize = 18) {
            auto row = new Widget{current};
            row->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Middle, 0, 10});
            new Label{row, text, font, fontSize };
        };

        auto addLibrary = [](Widget* current, string name, string license, string desc) {
            auto row = new Widget{current};
            row->set_layout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 3, 30});
            auto leftColumn = new Widget{row};
            leftColumn->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Maximum});
            leftColumn->set_fixed_width(135);

            new Label{leftColumn, name, "sans-bold", 18};
            new Label{row, desc, "sans", 18};
        };

        auto addSpacer = [](Widget* current, int space) {
            auto row = new Widget{current};
            row->set_height(space);
        };

        addSpacer(about, 15);

        addText(about, "tev — The EXR Viewer", "sans-bold", 46);
        addText(about, "version " TEV_VERSION, "sans", 26);

        addSpacer(about, 50);

        addText(about, "tev was developed by Thomas Müller and is released under the BSD 3-Clause License.");
        addText(about, "It was built directly or indirectly upon the following amazing third-party libraries.");

        addSpacer(about, 30);

        addLibrary(about, "args",              "", "Single-Header Argument Parsing Library");
        addLibrary(about, "clip",              "", "Cross-Platform Clipboard Library");
        addLibrary(about, "filesystem",        "", "Lightweight Path Manipulation Library");
        addLibrary(about, "Glad",              "", "Multi-Language GL Loader-Generator");
        addLibrary(about, "GLFW",              "", "OpenGL Desktop Development Library");
        addLibrary(about, "NanoGUI",           "", "Small Widget Library for OpenGL");
        addLibrary(about, "NanoVG",            "", "Small Vector Graphics Library");
        addLibrary(about, "OpenEXR",           "", "High Dynamic-Range (HDR) Image File Format");
        addLibrary(about, "stb_image(_write)", "", "Single-Header Library for Loading and Writing Images");
        addLibrary(about, "tinyformat",        "", "Minimal Type-Safe printf() Replacement");
        addLibrary(about, "tinylogger",        "", "Minimal Pretty-Logging Library");
        addLibrary(about, "UTF8-CPP",          "", "Lightweight UTF-8 String Manipulation Library");
    }

    tabWidget->set_selected_id(0);
    tabWidget->set_callback([tabWidget] (int id) mutable {
        tabWidget->set_selected_id(id);
    });
}

bool HelpWindow::keyboard_event(int key, int scancode, int action, int modifiers) {
    if (Window::keyboard_event(key, scancode, action, modifiers)) {
        return true;
    }

    if (key == GLFW_KEY_ESCAPE) {
        mCloseCallback();
        return true;
    }

    return false;
}

TEV_NAMESPACE_END
<commit_msg>Nanogui is no longer OGL-exclusive<commit_after>// This file was developed by Thomas Müller <thomas94@gmx.net>.
// It is published under the BSD 3-Clause License within the LICENSE file.

#include <tev/HelpWindow.h>

#include <nanogui/button.h>
#include <nanogui/icons.h>
#include <nanogui/label.h>
#include <nanogui/layout.h>
#include <nanogui/opengl.h>
#include <nanogui/tabwidget.h>
#include <nanogui/window.h>

using namespace nanogui;
using namespace std;

TEV_NAMESPACE_BEGIN

#ifdef __APPLE__
string HelpWindow::COMMAND = "Cmd";
#else
string HelpWindow::COMMAND = "Ctrl";
#endif

#ifdef __APPLE__
string HelpWindow::ALT = "Opt";
#else
string HelpWindow::ALT = "Alt";
#endif

HelpWindow::HelpWindow(Widget *parent, bool supportsHdr, function<void()> closeCallback)
    : Window{parent, "Help"}, mCloseCallback{closeCallback} {

    auto closeButton = new Button{button_panel(), "", FA_TIMES};
    closeButton->set_callback(mCloseCallback);

    set_layout(new GroupLayout{});
    set_fixed_width(600);

    TabWidget* tabWidget = new TabWidget{this};

    // Keybindings tab
    {
        Widget* shortcuts = new Widget(tabWidget);
        shortcuts->set_layout(new GroupLayout{});
        tabWidget->append_tab("Keybindings", shortcuts);

        auto addRow = [](Widget* current, string keys, string desc) {
            auto row = new Widget{current};
            row->set_layout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 0, 10});
            auto descWidget = new Label{row, desc, "sans"};
            descWidget->set_fixed_width(250);
            new Label{row, keys, "sans-bold"};
        };

        new Label{shortcuts, "Image Loading", "sans-bold", 18};
        auto imageLoading = new Widget{shortcuts};
        imageLoading->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageLoading, COMMAND + "+O",                             "Open Image");
        addRow(imageLoading, COMMAND + "+S",                             "Save View as Image");
        addRow(imageLoading, COMMAND + "+R or F5",                       "Reload Image");
        addRow(imageLoading, COMMAND + "+Shift+R or " + COMMAND + "+F5", "Reload All Images");
        addRow(imageLoading, COMMAND + "+W",                             "Close Image");
        addRow(imageLoading, COMMAND + "+Shift+W",                       "Close All Images");
        addRow(imageLoading, COMMAND + "+C",                             "Copy Image or Path to Clipboard");
        addRow(imageLoading, COMMAND + "+V",                             "Paste Image from Clipboard");

        new Label{shortcuts, "Image Options", "sans-bold", 18};
        auto imageSelection = new Widget{shortcuts};
        imageSelection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(imageSelection, "Left Click",          "Select Hovered Image");
        addRow(imageSelection, "1…9",                 "Select N-th Image");
        addRow(imageSelection, "Down or S / Up or W", "Select Next / Previous Image");

        addRow(imageSelection, "Click & Drag (+Shift/" + COMMAND + ")", "Translate Image");
        addRow(imageSelection, "Plus / Minus / Scroll (+Shift/" + COMMAND + ")", "Zoom In / Out of Image");

        addRow(imageSelection, "F", "Fit Image to Screen");
        addRow(imageSelection, "N", "Normalize Image to [0, 1]");
        addRow(imageSelection, "R", "Reset Image Parameters");
        if (supportsHdr) {
            addRow(imageSelection, "L", "Display the image as if on an LDR screen");
        }

        addRow(imageSelection, "Shift+Right or Shift+D / Shift+Left or Shift+A", "Select Next / Previous Tonemap");

        addRow(imageSelection, "E / Shift+E", "Increase / Decrease Exposure by 0.5");
        addRow(imageSelection, "O / Shift+O", "Increase / Decrease Offset by 0.1");

        addRow(imageSelection, ALT + " (hold)", "Display raw bytes on pixels when zoomed-in");

        new Label{shortcuts, "Reference Options", "sans-bold", 18};
        auto referenceSelection = new Widget{shortcuts};
        referenceSelection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(referenceSelection, "Shift (hold)",                                "View currently selected Reference");
        addRow(referenceSelection, "Shift+Left Click or Right Click",             "Select Hovered Image as Reference");
        addRow(referenceSelection, "Shift+1…9",                                   "Select N-th Image as Reference");
        addRow(referenceSelection, "Shift+Down or Shift+S / Shift+Up or Shift+W", "Select Next / Previous Image as Reference");

        addRow(referenceSelection, "Ctrl (hold)",                                "View selected Image if Reference is selected");
        addRow(referenceSelection, "Ctrl+Right or Ctrl+D / Ctrl+Left or Ctrl+A", "Select Next / Previous Error Metric");

        new Label{shortcuts, "Channel Group Options", "sans-bold", 18};
        auto groupSelection = new Widget{shortcuts};
        groupSelection->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(groupSelection, "Left Click",             "Select Hovered Channel Group");
        addRow(groupSelection, "Ctrl+1…9",               "Select N-th Channel Group");
        addRow(groupSelection, "Right or D / Left or A", "Select Next / Previous Channel Group");

        new Label{shortcuts, "Interface", "sans-bold", 18};
        auto ui = new Widget{shortcuts};
        ui->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Fill, 0, 0});

        addRow(ui, ALT + "+Enter", "Maximize");
        addRow(ui, COMMAND + "+B", "Toggle GUI");
        addRow(ui, "H",            "Show Help (this Window)");
        addRow(ui, COMMAND + "+P", "Find Image or Channel Group");
        addRow(ui, "Q or Esc",     "Quit");
    }

    // About tab
    {
        Widget* about = new Widget(tabWidget);
        about->set_layout(new GroupLayout{});
        tabWidget->append_tab("About", about);

        auto addText = [](Widget* current, string text, string font = "sans", int fontSize = 18) {
            auto row = new Widget{current};
            row->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Middle, 0, 10});
            new Label{row, text, font, fontSize };
        };

        auto addLibrary = [](Widget* current, string name, string license, string desc) {
            auto row = new Widget{current};
            row->set_layout(new BoxLayout{Orientation::Horizontal, Alignment::Fill, 3, 30});
            auto leftColumn = new Widget{row};
            leftColumn->set_layout(new BoxLayout{Orientation::Vertical, Alignment::Maximum});
            leftColumn->set_fixed_width(135);

            new Label{leftColumn, name, "sans-bold", 18};
            new Label{row, desc, "sans", 18};
        };

        auto addSpacer = [](Widget* current, int space) {
            auto row = new Widget{current};
            row->set_height(space);
        };

        addSpacer(about, 15);

        addText(about, "tev — The EXR Viewer", "sans-bold", 46);
        addText(about, "version " TEV_VERSION, "sans", 26);

        addSpacer(about, 50);

        addText(about, "tev was developed by Thomas Müller and is released under the BSD 3-Clause License.");
        addText(about, "It was built directly or indirectly upon the following amazing third-party libraries.");

        addSpacer(about, 30);

        addLibrary(about, "args",              "", "Single-Header Argument Parsing Library");
        addLibrary(about, "clip",              "", "Cross-Platform Clipboard Library");
        addLibrary(about, "filesystem",        "", "Lightweight Path Manipulation Library");
        addLibrary(about, "Glad",              "", "Multi-Language GL Loader-Generator");
        addLibrary(about, "GLFW",              "", "OpenGL Desktop Development Library");
        addLibrary(about, "NanoGUI",           "", "Small GUI Library");
        addLibrary(about, "NanoVG",            "", "Small Vector Graphics Library");
        addLibrary(about, "OpenEXR",           "", "High Dynamic-Range (HDR) Image File Format");
        addLibrary(about, "stb_image(_write)", "", "Single-Header Library for Loading and Writing Images");
        addLibrary(about, "tinyformat",        "", "Minimal Type-Safe printf() Replacement");
        addLibrary(about, "tinylogger",        "", "Minimal Pretty-Logging Library");
        addLibrary(about, "UTF8-CPP",          "", "Lightweight UTF-8 String Manipulation Library");
    }

    tabWidget->set_selected_id(0);
    tabWidget->set_callback([tabWidget] (int id) mutable {
        tabWidget->set_selected_id(id);
    });
}

bool HelpWindow::keyboard_event(int key, int scancode, int action, int modifiers) {
    if (Window::keyboard_event(key, scancode, action, modifiers)) {
        return true;
    }

    if (key == GLFW_KEY_ESCAPE) {
        mCloseCallback();
        return true;
    }

    return false;
}

TEV_NAMESPACE_END
<|endoftext|>


<commit_before>//
// CMatrix/instance_method_mul.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_CMATRIX_INSTANCE_METHOD_MUL_HPP
#define EIGENJS_CMATRIX_INSTANCE_METHOD_MUL_HPP

namespace EigenJS {

EIGENJS_INSTANCE_METHOD(CMatrix, mul,
{
  NanScope();

  if (args.Length() == 1) {
    const T* const& obj = node::ObjectWrap::Unwrap<T>(args.This());
    const typename T::value_type& value = **obj;
    const typename T::value_type::Index& rows = value.rows();
    const typename T::value_type::Index& cols = value.cols();
    v8::Local<v8::Value> argv[] = {
      NanNew<v8::Integer>(rows)
    , NanNew<v8::Integer>(cols)
    };

    if (CMatrix::is_cmatrix(args[0])) {
      const CMatrix* rhs_obj =
        node::ObjectWrap::Unwrap<CMatrix>(args[0]->ToObject());
      const typename CMatrix::value_type& rhs_cmatrix = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value * rhs_cmatrix;

      NanReturnValue(instance);
    } else if (CVector::is_cvector(args[0])) {
      const CVector* rhs_obj =
          node::ObjectWrap::Unwrap<CVector>(args[0]->ToObject());
      const typename CVector::value_type& rhs_cvector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value * rhs_cvector;

      NanReturnValue(instance);
    } else if (CRowVector::is_crowvector(args[0])) {
      const CRowVector* rhs_obj =
          node::ObjectWrap::Unwrap<CRowVector>(args[0]->ToObject());
      const typename CRowVector::value_type& rhs_crowvector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value * rhs_crowvector;

      NanReturnValue(instance);
    } else if (Matrix::is_matrix(args[0])) {
      const Matrix* const& rhs_obj =
        node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
      const typename Matrix::value_type& rhs_matrix = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value *
          rhs_matrix.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (Vector::is_vector(args[0])) {
      const Vector* const& rhs_obj =
        node::ObjectWrap::Unwrap<Vector>(args[0]->ToObject());
      const typename Vector::value_type& rhs_vector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value *
          rhs_vector.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (RowVector::is_rowvector(args[0])) {
      const RowVector* const& rhs_obj =
        node::ObjectWrap::Unwrap<RowVector>(args[0]->ToObject());
      const typename RowVector::value_type& rhs_rowvector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value *
          rhs_rowvector.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (T::is_scalar(args[0])) {
      v8::Local<v8::Object> instance = T::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      T* new_obj = node::ObjectWrap::Unwrap<T>(instance);
      typename T::value_type& new_value = **new_obj;

      new_value = value * args[0]->NumberValue();

      NanReturnValue(instance);
    } else if (Complex::is_complex(args[0])) {
      const Complex* const& rhs_obj =
          node::ObjectWrap::Unwrap<Complex>(args[0]->ToObject());
      const typename Complex::value_type& rhs_complex = **rhs_obj;

      v8::Local<v8::Object> instance = T::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      T* new_obj = node::ObjectWrap::Unwrap<T>(instance);
      typename T::value_type& new_value = **new_obj;

      new_value = value * rhs_complex;

      NanReturnValue(instance);
    }
  }

  EIGENJS_THROW_ERROR_INVALID_ARGUMENT()
  NanReturnUndefined();
})

}  // namespace EigenJS

#endif  // EIGENJS_CMATRIX_INSTANCE_METHOD_MUL_HPP
<commit_msg>src: support MatrixBlock and CMatrixBlock<commit_after>//
// CMatrix/instance_method_mul.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2014 Rick Yang (rick68 at gmail dot com)
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

#ifndef EIGENJS_CMATRIX_INSTANCE_METHOD_MUL_HPP
#define EIGENJS_CMATRIX_INSTANCE_METHOD_MUL_HPP

namespace EigenJS {

EIGENJS_INSTANCE_METHOD(CMatrix, mul,
{
  NanScope();

  if (args.Length() == 1) {
    const T* const& obj = node::ObjectWrap::Unwrap<T>(args.This());
    const typename T::value_type& value = **obj;
    const typename T::value_type::Index& rows = value.rows();
    const typename T::value_type::Index& cols = value.cols();
    v8::Local<v8::Value> argv[] = {
      NanNew<v8::Integer>(rows)
    , NanNew<v8::Integer>(cols)
    };

    if (CMatrix::is_cmatrix(args[0])) {
      const CMatrix* rhs_obj =
        node::ObjectWrap::Unwrap<CMatrix>(args[0]->ToObject());
      const typename CMatrix::value_type& rhs_cmatrix = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value * rhs_cmatrix;

      NanReturnValue(instance);
    } else if (CVector::is_cvector(args[0])) {
      const CVector* rhs_obj =
          node::ObjectWrap::Unwrap<CVector>(args[0]->ToObject());
      const typename CVector::value_type& rhs_cvector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value * rhs_cvector;

      NanReturnValue(instance);
    } else if (CRowVector::is_crowvector(args[0])) {
      const CRowVector* rhs_obj =
          node::ObjectWrap::Unwrap<CRowVector>(args[0]->ToObject());
      const typename CRowVector::value_type& rhs_crowvector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value * rhs_crowvector;

      NanReturnValue(instance);
    } else if (CMatrixBlock::is_cmatrixblock(args[0])) {
      const CMatrixBlock* rhs_obj =
          node::ObjectWrap::Unwrap<CMatrixBlock>(args[0]->ToObject());
      const typename CMatrixBlock::value_type& rhs_cmatrixblock = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value;
      new_cmatrix *= rhs_cmatrixblock;

      NanReturnValue(instance);
    } else if (Matrix::is_matrix(args[0])) {
      const Matrix* const& rhs_obj =
        node::ObjectWrap::Unwrap<Matrix>(args[0]->ToObject());
      const typename Matrix::value_type& rhs_matrix = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value *
          rhs_matrix.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (Vector::is_vector(args[0])) {
      const Vector* const& rhs_obj =
        node::ObjectWrap::Unwrap<Vector>(args[0]->ToObject());
      const typename Vector::value_type& rhs_vector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value *
          rhs_vector.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (RowVector::is_rowvector(args[0])) {
      const RowVector* const& rhs_obj =
        node::ObjectWrap::Unwrap<RowVector>(args[0]->ToObject());
      const typename RowVector::value_type& rhs_rowvector = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value *
          rhs_rowvector.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (MatrixBlock::is_matrixblock(args[0])) {
      const MatrixBlock* const& rhs_obj =
        node::ObjectWrap::Unwrap<MatrixBlock>(args[0]->ToObject());
      const typename MatrixBlock::value_type& rhs_matrixblock = **rhs_obj;

      if (T::is_invalid_matrix_product(obj, rhs_obj)) {
        NanReturnUndefined();
      }

      v8::Local<v8::Object> instance = CMatrix::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      CMatrix* new_obj = node::ObjectWrap::Unwrap<CMatrix>(instance);
      typename CMatrix::value_type& new_cmatrix = **new_obj;

      new_cmatrix = value;
      new_cmatrix *=
          rhs_matrixblock.template cast<typename Complex::value_type>();

      NanReturnValue(instance);
    } else if (T::is_scalar(args[0])) {
      v8::Local<v8::Object> instance = T::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      U* new_obj = node::ObjectWrap::Unwrap<U>(instance);
      typename U::value_type& new_value = **new_obj;

      new_value = value * args[0]->NumberValue();

      NanReturnValue(instance);
    } else if (Complex::is_complex(args[0])) {
      const Complex* const& rhs_obj =
          node::ObjectWrap::Unwrap<Complex>(args[0]->ToObject());
      const typename Complex::value_type& rhs_complex = **rhs_obj;

      v8::Local<v8::Object> instance = T::new_instance(
        args
      , sizeof(argv) / sizeof(v8::Local<v8::Value>)
      , argv
      );

      U* new_obj = node::ObjectWrap::Unwrap<U>(instance);
      typename U::value_type& new_value = **new_obj;

      new_value = value * rhs_complex;

      NanReturnValue(instance);
    }
  }

  EIGENJS_THROW_ERROR_INVALID_ARGUMENT()
  NanReturnUndefined();
})

}  // namespace EigenJS

#endif  // EIGENJS_CMATRIX_INSTANCE_METHOD_MUL_HPP
<|endoftext|>


<commit_before>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2013 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#ifndef DO_GEOMETRY_ELLIPSE_HPP
#define DO_GEOMETRY_ELLIPSE_HPP

#define _USE_MATH_DEFINES
#include <cmath>
#include <DO/Core/EigenExtension.hpp>

namespace DO {

  //! Ellipse class
  class Ellipse
  {
  public:
    Ellipse() {}
    Ellipse(double radius1, double radius2, double orientation,
            const Point2d& center)
      : a_(radius1), b_(radius2), o_(orientation), c_(center) {}

    double radius1() const { return a_; }
    double radius2() const { return b_; }
    double orientation() const { return o_; }
    const Point2d& center() const { return c_; }

    double& radius1() { return a_; }
    double& radius2() { return b_; }
    double& orientation() { return o_; }
    Point2d& center() { return c_; }

    //! Get the radial vector at angle $\theta$ w.r.t. orientation $o$ of ellipse.
    Vector2d rho(double theta) const;
    //! Get point on ellipse at angle $\theta$ w.r.t. orientation $o$ of ellipse.
    Point2d operator()(double theta) const;
    /*!
      Retrieve relative orientation of point $p$ w.r.t. orientation
      $o$ of ellipse.
     */
    friend double orientation(const Point2d& p, const Ellipse& e);
    //! Polar antiderivative.
    friend inline double polarAntiderivative(const Ellipse& e, double theta)
    {
      const double y = (e.b_-e.a_)*sin(2*theta);
      const double x = (e.b_+e.a_) + (e.b_-e.a_)*cos(2*theta);
      return e.a_*e.b_*0.5*( theta - atan2(y,x) );
    }
    /*!
      This function should be used instead to compute the **positive** area
      of an ellipse sector which we define as the region bounded by:
      - the **counter-clockwise** oriented arc going **from** the endpoint
        $M(\theta0)$ **to** endpoint $M(\theta_1)$.
      - line segments connecting the center of the ellipse and the endpoints
        of the arc.

      $\theta_0$ and $\theta_1$ are required to be in the range $]\pi, \pi]$ but
      it does not matter if $\theta_0 > \theta_1$.
     */
    friend double sectorArea(const Ellipse& e, double theta0, double theta1)
    { return polarAntiderivative(e, theta1) - polarAntiderivative(e, theta0); }
    /*!
      An elliptic segment is a region bounded by an arc and the chord connecting
      the arc's endpoints.
     */
    friend double segmentArea(const Ellipse& e, double theta0, double theta1);
    //! Ellipse area.
    friend inline double area(const Ellipse& e)
    { return M_PI*e.a_*e.b_; }
    //! Shape matrix.
    friend inline Matrix2d shapeMat(const Ellipse& e)
    {
      const Eigen::Rotation2D<double> R(e.o_);
      Vector2d D( 1./(e.a_*e.a_), 1./(e.b_*e.b_) );
      return R.matrix()*D.asDiagonal()*R.matrix().transpose();
    }
    friend inline bool inside(const Point2d& p, const Ellipse& e)
    { return (p-e.c_).transpose()*shapeMat(e)*(p-e.c_) < 1.; }
    //! I/O.
    friend std::ostream& operator<<(std::ostream& os, const Ellipse& e);

  private:
    double a_, b_;
    double o_;
    Point2d c_;
  };

  //! Compute the ellipse from the conic equation
  Ellipse fromShapeMat(const Matrix2d& shapeMat, const Point2d& c);

} /* namespace DO */

#endif /* DO_GEOMETRY_ELLIPSE_HPP */<commit_msg>[Geometry] Added documentation to `inside` function for `Ellipse`.<commit_after>// ========================================================================== //
// This file is part of DO++, a basic set of libraries in C++ for computer
// vision.
//
// Copyright (C) 2013 David Ok <david.ok8@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License v. 2.0. If a copy of the MPL was not distributed with this file,
// you can obtain one at http://mozilla.org/MPL/2.0/.
// ========================================================================== //

#ifndef DO_GEOMETRY_ELLIPSE_HPP
#define DO_GEOMETRY_ELLIPSE_HPP

#define _USE_MATH_DEFINES
#include <cmath>
#include <DO/Core/EigenExtension.hpp>

namespace DO {

  //! Ellipse class
  class Ellipse
  {
  public:
    Ellipse() {}
    Ellipse(double radius1, double radius2, double orientation,
            const Point2d& center)
      : a_(radius1), b_(radius2), o_(orientation), c_(center) {}

    double radius1() const { return a_; }
    double radius2() const { return b_; }
    double orientation() const { return o_; }
    const Point2d& center() const { return c_; }

    double& radius1() { return a_; }
    double& radius2() { return b_; }
    double& orientation() { return o_; }
    Point2d& center() { return c_; }

    //! Get the radial vector at angle $\theta$ w.r.t. orientation $o$ of ellipse.
    Vector2d rho(double theta) const;
    //! Get point on ellipse at angle $\theta$ w.r.t. orientation $o$ of ellipse.
    Point2d operator()(double theta) const;
    /*!
      Retrieve relative orientation of point $p$ w.r.t. orientation
      $o$ of ellipse.
     */
    friend double orientation(const Point2d& p, const Ellipse& e);
    //! Polar antiderivative.
    friend inline double polarAntiderivative(const Ellipse& e, double theta)
    {
      const double y = (e.b_-e.a_)*sin(2*theta);
      const double x = (e.b_+e.a_) + (e.b_-e.a_)*cos(2*theta);
      return e.a_*e.b_*0.5*( theta - atan2(y,x) );
    }
    /*!
      This function should be used instead to compute the **positive** area
      of an ellipse sector which we define as the region bounded by:
      - the **counter-clockwise** oriented arc going **from** the endpoint
        $M(\theta0)$ **to** endpoint $M(\theta_1)$.
      - line segments connecting the center of the ellipse and the endpoints
        of the arc.

      $\theta_0$ and $\theta_1$ are required to be in the range $]\pi, \pi]$ but
      it does not matter if $\theta_0 > \theta_1$.
     */
    friend double sectorArea(const Ellipse& e, double theta0, double theta1)
    { return polarAntiderivative(e, theta1) - polarAntiderivative(e, theta0); }
    /*!
      An elliptic segment is a region bounded by an arc and the chord connecting
      the arc's endpoints.
     */
    friend double segmentArea(const Ellipse& e, double theta0, double theta1);
    //! Ellipse area.
    friend inline double area(const Ellipse& e)
    { return M_PI*e.a_*e.b_; }
    //! Shape matrix.
    friend inline Matrix2d shapeMat(const Ellipse& e)
    {
      const Eigen::Rotation2D<double> R(e.o_);
      Vector2d D( 1./(e.a_*e.a_), 1./(e.b_*e.b_) );
      return R.matrix()*D.asDiagonal()*R.matrix().transpose();
    }
    //! Checks if point is inside ellipse.
    friend inline bool inside(const Point2d& p, const Ellipse& e)
    { return (p-e.c_).transpose()*shapeMat(e)*(p-e.c_) < 1.; }
    //! I/O.
    friend std::ostream& operator<<(std::ostream& os, const Ellipse& e);

  private:
    double a_, b_;
    double o_;
    Point2d c_;
  };

  //! Compute the ellipse from the conic equation
  Ellipse fromShapeMat(const Matrix2d& shapeMat, const Point2d& c);

} /* namespace DO */

#endif /* DO_GEOMETRY_ELLIPSE_HPP */<|endoftext|>


<commit_before>#include "tonic.h"

void reshape(Net<float> *net, int input_size)
{
  int n_in = net->input_blobs()[0]->num();
  int c_in = net->input_blobs()[0]->channels();
  int w_in = net->input_blobs()[0]->width();
  int h_in = net->input_blobs()[0]->height();

  int n_out = net->output_blobs()[0]->num();
  int c_out = net->output_blobs()[0]->channels();
  int w_out = net->output_blobs()[0]->width();
  int h_out = net->output_blobs()[0]->height();

  // assumes C, H, W are known, only reshapes batch dim
  if(input_size/(c_in*w_in*h_in) != n_in) {
    n_in = input_size/(c_in*w_in*h_in);
    LOG(INFO) << "Reshaping input to dims: "
      << n_in << " " << c_in << " " << w_in << " " << h_in;
    //TODO: may need to add a lock
    net->input_blobs()[0]->Reshape(n_in, c_in, w_in, h_in);

    n_out = n_in;
    LOG(INFO) << "Reshaping outnput to dims: "
      << n_out << " " << c_out << " " << w_out << " " << h_out;
    //TODO: may need to add a lock
    net->output_blobs()[0]->Reshape(n_out, c_out, w_out, h_out);
  }
}
<commit_msg>typoe<commit_after>#include "tonic.h"

void reshape(Net<float> *net, int input_size)
{
  int n_in = net->input_blobs()[0]->num();
  int c_in = net->input_blobs()[0]->channels();
  int w_in = net->input_blobs()[0]->width();
  int h_in = net->input_blobs()[0]->height();

  int n_out = net->output_blobs()[0]->num();
  int c_out = net->output_blobs()[0]->channels();
  int w_out = net->output_blobs()[0]->width();
  int h_out = net->output_blobs()[0]->height();

  // assumes C, H, W are known, only reshapes batch dim
  if(input_size/(c_in*w_in*h_in) != n_in) {
    n_in = input_size/(c_in*w_in*h_in);
    LOG(INFO) << "Reshaping input to dims: "
      << n_in << " " << c_in << " " << w_in << " " << h_in;
    //TODO: may need to add a lock
    net->input_blobs()[0]->Reshape(n_in, c_in, w_in, h_in);

    n_out = n_in;
    LOG(INFO) << "Reshaping output to dims: "
      << n_out << " " << c_out << " " << w_out << " " << h_out;
    //TODO: may need to add a lock
    net->output_blobs()[0]->Reshape(n_out, c_out, w_out, h_out);
  }
}
<|endoftext|>