<commit_before>//===- CIndexHigh.cpp - Higher level API functions ------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "CursorVisitor.h"
#include "CLog.h"
#include "CXCursor.h"
#include "CXSourceLocation.h"
#include "CXTranslationUnit.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Frontend/ASTUnit.h"
#include "llvm/Support/Compiler.h"

using namespace clang;
using namespace cxcursor;
using namespace cxindex;

static void getTopOverriddenMethods(CXTranslationUnit TU,
                                    const Decl *D,
                                    SmallVectorImpl<const Decl *> &Methods) {
  if (!D)
    return;
  if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
    return;

  SmallVector<CXCursor, 8> Overridden;
  cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);

  if (Overridden.empty()) {
    Methods.push_back(D->getCanonicalDecl());
    return;
  }

  for (SmallVector<CXCursor, 8>::iterator
         I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
}

namespace {

struct FindFileIdRefVisitData {
  CXTranslationUnit TU;
  FileID FID;
  const Decl *Dcl;
  int SelectorIdIdx;
  CXCursorAndRangeVisitor visitor;

  typedef SmallVector<const Decl *, 8> TopMethodsTy;
  TopMethodsTy TopMethods;

  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
                         const Decl *D, int selectorIdIdx,
                         CXCursorAndRangeVisitor visitor)
    : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
    Dcl = getCanonical(D);
    getTopOverriddenMethods(TU, Dcl, TopMethods);
  }

  ASTContext &getASTContext() const {
    return cxtu::getASTUnit(TU)->getASTContext();
  }

  /// \brief We are looking to find all semantically relevant identifiers,
  /// so the definition of "canonical" here is different than in the AST, e.g.
  ///
  /// \code
  ///   class C {
  ///     C() {}
  ///   };
  /// \endcode
  ///
  /// we consider the canonical decl of the constructor decl to be the class
  /// itself, so both 'C' can be highlighted.
  const Decl *getCanonical(const Decl *D) const {
    if (!D)
      return 0;

    D = D->getCanonicalDecl();

    if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
      if (ImplD->getClassInterface())
        return getCanonical(ImplD->getClassInterface());

    } else if (const CXXConstructorDecl *CXXCtorD =
                   dyn_cast<CXXConstructorDecl>(D)) {
      return getCanonical(CXXCtorD->getParent());
    }

    return D;
  }

  bool isHit(const Decl *D) const {
    if (!D)
      return false;

    D = getCanonical(D);
    if (D == Dcl)
      return true;

    if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
      return isOverriddingMethod(D);

    return false;
  }

private:
  bool isOverriddingMethod(const Decl *D) const {
    if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
          TopMethods.end())
      return true;

    TopMethodsTy methods;
    getTopOverriddenMethods(TU, D, methods);
    for (TopMethodsTy::iterator
           I = methods.begin(), E = methods.end(); I != E; ++I) {
      if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
            TopMethods.end())
        return true;
    }

    return false;
  }
};

} // end anonymous namespace.

/// \brief For a macro \arg Loc, returns the file spelling location and sets
/// to \arg isMacroArg whether the spelling resides inside a macro definition or
/// a macro argument.
static SourceLocation getFileSpellingLoc(SourceManager &SM,
                                         SourceLocation Loc,
                                         bool &isMacroArg) {
  assert(Loc.isMacroID());
  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
  if (SpellLoc.isMacroID())
    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);

  isMacroArg = SM.isMacroArgExpansion(Loc);
  return SpellLoc;
}

static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
                                                  CXCursor parent,
                                                  CXClientData client_data) {
  CXCursor declCursor = clang_getCursorReferenced(cursor);
  if (!clang_isDeclaration(declCursor.kind))
    return CXChildVisit_Recurse;

  const Decl *D = cxcursor::getCursorDecl(declCursor);
  if (!D)
    return CXChildVisit_Continue;

  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
  if (data->isHit(D)) {
    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);

    // We are looking for identifiers to highlight so for objc methods (and
    // not a parameter) we can only highlight the selector identifiers.
    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
      return CXChildVisit_Recurse;

    if (clang_isExpression(cursor.kind)) {
      if (cursor.kind == CXCursor_DeclRefExpr ||
          cursor.kind == CXCursor_MemberRefExpr) {
        // continue..

      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
        // continue..

      } else
        return CXChildVisit_Recurse;
    }

    SourceLocation
      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
    if (SelIdLoc.isValid())
      Loc = SelIdLoc;

    ASTContext &Ctx = data->getASTContext();
    SourceManager &SM = Ctx.getSourceManager();
    bool isInMacroDef = false;
    if (Loc.isMacroID()) {
      bool isMacroArg;
      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
      isInMacroDef = !isMacroArg;
    }

    // We are looking for identifiers in a specific file.
    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
    if (LocInfo.first != data->FID)
      return CXChildVisit_Recurse;

    if (isInMacroDef) {
      // FIXME: For a macro definition make sure that all expansions
      // of it expand to the same reference before allowing to point to it.
      return CXChildVisit_Recurse;
    }

    data->visitor.visit(data->visitor.context, cursor,
                        cxloc::translateSourceRange(Ctx, Loc));
  }
  return CXChildVisit_Recurse;
}

static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
                           const FileEntry *File,
                           CXCursorAndRangeVisitor Visitor) {
  assert(clang_isDeclaration(declCursor.kind));
  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();

  FileID FID = SM.translateFile(File);
  const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
  if (!Dcl)
    return;

  FindFileIdRefVisitData data(TU, FID, Dcl,
                              cxcursor::getSelectorIdentifierIndex(declCursor),
                              Visitor);

  if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
    clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
                        findFileIdRefVisit, &data);
    return;
  }

  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
  CursorVisitor FindIdRefsVisitor(TU,
                                  findFileIdRefVisit, &data,
                                  /*VisitPreprocessorLast=*/true,
                                  /*VisitIncludedEntities=*/false,
                                  Range,
                                  /*VisitDeclsOnly=*/true);
  FindIdRefsVisitor.visitFileRegion();
}

namespace {

struct FindFileMacroRefVisitData {
  ASTUnit &Unit;
  const FileEntry *File;
  const IdentifierInfo *Macro;
  CXCursorAndRangeVisitor visitor;

  FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
                            const IdentifierInfo *Macro,
                            CXCursorAndRangeVisitor visitor)
    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }

  ASTContext &getASTContext() const {
    return Unit.getASTContext();
  }
};

} // anonymous namespace

static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
                                                     CXCursor parent,
                                                     CXClientData client_data) {
  const IdentifierInfo *Macro = 0;
  if (cursor.kind == CXCursor_MacroDefinition)
    Macro = getCursorMacroDefinition(cursor)->getName();
  else if (cursor.kind == CXCursor_MacroExpansion)
    Macro = getCursorMacroExpansion(cursor).getName();
  if (!Macro)
    return CXChildVisit_Continue;

  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
  if (data->Macro != Macro)
    return CXChildVisit_Continue;

  SourceLocation
    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));

  ASTContext &Ctx = data->getASTContext();
  SourceManager &SM = Ctx.getSourceManager();
  bool isInMacroDef = false;
  if (Loc.isMacroID()) {
    bool isMacroArg;
    Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
    isInMacroDef = !isMacroArg;
  }

  // We are looking for identifiers in a specific file.
  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
  if (SM.getFileEntryForID(LocInfo.first) != data->File)
    return CXChildVisit_Continue;

  if (isInMacroDef) {
    // FIXME: For a macro definition make sure that all expansions
    // of it expand to the same reference before allowing to point to it.
    return CXChildVisit_Continue;
  }

  data->visitor.visit(data->visitor.context, cursor,
                      cxloc::translateSourceRange(Ctx, Loc));
  return CXChildVisit_Continue;
}

static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
                                const FileEntry *File,
                                CXCursorAndRangeVisitor Visitor) {
  if (Cursor.kind != CXCursor_MacroDefinition &&
      Cursor.kind != CXCursor_MacroExpansion)
    return;

  ASTUnit *Unit = cxtu::getASTUnit(TU);
  SourceManager &SM = Unit->getSourceManager();

  FileID FID = SM.translateFile(File);
  const IdentifierInfo *Macro = 0;
  if (Cursor.kind == CXCursor_MacroDefinition)
    Macro = getCursorMacroDefinition(Cursor)->getName();
  else
    Macro = getCursorMacroExpansion(Cursor).getName();
  if (!Macro)
    return;

  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);

  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
  CursorVisitor FindMacroRefsVisitor(TU,
                                  findFileMacroRefVisit, &data,
                                  /*VisitPreprocessorLast=*/false,
                                  /*VisitIncludedEntities=*/false,
                                  Range);
  FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
}

//===----------------------------------------------------------------------===//
// libclang public APIs.
//===----------------------------------------------------------------------===//

extern "C" {

void clang_findReferencesInFile(CXCursor cursor, CXFile file,
                                CXCursorAndRangeVisitor visitor) {
  LogRef Log = Logger::make(LLVM_FUNCTION_NAME);

  if (clang_Cursor_isNull(cursor)) {
    if (Log)
      *Log << "Null cursor";
    return;
  }
  if (cursor.kind == CXCursor_NoDeclFound) {
    if (Log)
      *Log << "Got CXCursor_NoDeclFound";
    return;
  }
  if (!file) {
    if (Log)
      *Log << "Null file";
    return;
  }
  if (!visitor.visit) {
    if (Log)
      *Log << "Null visitor";
    return;
  }

  if (Log)
    *Log << cursor << " @" << static_cast<const FileEntry *>(file);

  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
  if (!CXXUnit)
    return;

  ASTUnit::ConcurrencyCheck Check(*CXXUnit);

  if (cursor.kind == CXCursor_MacroDefinition ||
      cursor.kind == CXCursor_MacroExpansion) {
    findMacroRefsInFile(cxcursor::getCursorTU(cursor),
                        cursor,
                        static_cast<const FileEntry *>(file),
                        visitor);
    return;
  }

  // We are interested in semantics of identifiers so for C++ constructor exprs
  // prefer type references, e.g.:
  //
  //  return MyStruct();
  //
  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
  // we are actually interested in the type declaration.
  cursor = cxcursor::getTypeRefCursor(cursor);

  CXCursor refCursor = clang_getCursorReferenced(cursor);

  if (!clang_isDeclaration(refCursor.kind)) {
    if (Log)
      *Log << "cursor is not referencing a declaration";
    return;
  }

  findIdRefsInFile(cxcursor::getCursorTU(cursor),
                   refCursor,
                   static_cast<const FileEntry *>(file),
                   visitor);
}

static enum CXVisitorResult _visitCursorAndRange(void *context,
                                                 CXCursor cursor,
                                                 CXSourceRange range) {
  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
  return INVOKE_BLOCK2(block, cursor, range);
}

void clang_findReferencesInFileWithBlock(CXCursor cursor,
                                         CXFile file,
                                         CXCursorAndRangeVisitorBlock block) {
  CXCursorAndRangeVisitor visitor = { block,
                                      block ? _visitCursorAndRange : 0 };
  return clang_findReferencesInFile(cursor, file, visitor);
}

} // end: extern "C"

<commit_msg>[libclang] When calling the function of CXCursorAndRangeVisitor, don't forget to stop visitation if it returns CXVisit_Break.<commit_after>//===- CIndexHigh.cpp - Higher level API functions ------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "CursorVisitor.h"
#include "CLog.h"
#include "CXCursor.h"
#include "CXSourceLocation.h"
#include "CXTranslationUnit.h"
#include "clang/AST/DeclObjC.h"
#include "clang/Frontend/ASTUnit.h"
#include "llvm/Support/Compiler.h"

using namespace clang;
using namespace cxcursor;
using namespace cxindex;

static void getTopOverriddenMethods(CXTranslationUnit TU,
                                    const Decl *D,
                                    SmallVectorImpl<const Decl *> &Methods) {
  if (!D)
    return;
  if (!isa<ObjCMethodDecl>(D) && !isa<CXXMethodDecl>(D))
    return;

  SmallVector<CXCursor, 8> Overridden;
  cxcursor::getOverriddenCursors(cxcursor::MakeCXCursor(D, TU), Overridden);

  if (Overridden.empty()) {
    Methods.push_back(D->getCanonicalDecl());
    return;
  }

  for (SmallVector<CXCursor, 8>::iterator
         I = Overridden.begin(), E = Overridden.end(); I != E; ++I)
    getTopOverriddenMethods(TU, cxcursor::getCursorDecl(*I), Methods);
}

namespace {

struct FindFileIdRefVisitData {
  CXTranslationUnit TU;
  FileID FID;
  const Decl *Dcl;
  int SelectorIdIdx;
  CXCursorAndRangeVisitor visitor;

  typedef SmallVector<const Decl *, 8> TopMethodsTy;
  TopMethodsTy TopMethods;

  FindFileIdRefVisitData(CXTranslationUnit TU, FileID FID,
                         const Decl *D, int selectorIdIdx,
                         CXCursorAndRangeVisitor visitor)
    : TU(TU), FID(FID), SelectorIdIdx(selectorIdIdx), visitor(visitor) {
    Dcl = getCanonical(D);
    getTopOverriddenMethods(TU, Dcl, TopMethods);
  }

  ASTContext &getASTContext() const {
    return cxtu::getASTUnit(TU)->getASTContext();
  }

  /// \brief We are looking to find all semantically relevant identifiers,
  /// so the definition of "canonical" here is different than in the AST, e.g.
  ///
  /// \code
  ///   class C {
  ///     C() {}
  ///   };
  /// \endcode
  ///
  /// we consider the canonical decl of the constructor decl to be the class
  /// itself, so both 'C' can be highlighted.
  const Decl *getCanonical(const Decl *D) const {
    if (!D)
      return 0;

    D = D->getCanonicalDecl();

    if (const ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) {
      if (ImplD->getClassInterface())
        return getCanonical(ImplD->getClassInterface());

    } else if (const CXXConstructorDecl *CXXCtorD =
                   dyn_cast<CXXConstructorDecl>(D)) {
      return getCanonical(CXXCtorD->getParent());
    }

    return D;
  }

  bool isHit(const Decl *D) const {
    if (!D)
      return false;

    D = getCanonical(D);
    if (D == Dcl)
      return true;

    if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
      return isOverriddingMethod(D);

    return false;
  }

private:
  bool isOverriddingMethod(const Decl *D) const {
    if (std::find(TopMethods.begin(), TopMethods.end(), D) !=
          TopMethods.end())
      return true;

    TopMethodsTy methods;
    getTopOverriddenMethods(TU, D, methods);
    for (TopMethodsTy::iterator
           I = methods.begin(), E = methods.end(); I != E; ++I) {
      if (std::find(TopMethods.begin(), TopMethods.end(), *I) !=
            TopMethods.end())
        return true;
    }

    return false;
  }
};

} // end anonymous namespace.

/// \brief For a macro \arg Loc, returns the file spelling location and sets
/// to \arg isMacroArg whether the spelling resides inside a macro definition or
/// a macro argument.
static SourceLocation getFileSpellingLoc(SourceManager &SM,
                                         SourceLocation Loc,
                                         bool &isMacroArg) {
  assert(Loc.isMacroID());
  SourceLocation SpellLoc = SM.getImmediateSpellingLoc(Loc);
  if (SpellLoc.isMacroID())
    return getFileSpellingLoc(SM, SpellLoc, isMacroArg);

  isMacroArg = SM.isMacroArgExpansion(Loc);
  return SpellLoc;
}

static enum CXChildVisitResult findFileIdRefVisit(CXCursor cursor,
                                                  CXCursor parent,
                                                  CXClientData client_data) {
  CXCursor declCursor = clang_getCursorReferenced(cursor);
  if (!clang_isDeclaration(declCursor.kind))
    return CXChildVisit_Recurse;

  const Decl *D = cxcursor::getCursorDecl(declCursor);
  if (!D)
    return CXChildVisit_Continue;

  FindFileIdRefVisitData *data = (FindFileIdRefVisitData *)client_data;
  if (data->isHit(D)) {
    cursor = cxcursor::getSelectorIdentifierCursor(data->SelectorIdIdx, cursor);

    // We are looking for identifiers to highlight so for objc methods (and
    // not a parameter) we can only highlight the selector identifiers.
    if ((cursor.kind == CXCursor_ObjCClassMethodDecl ||
         cursor.kind == CXCursor_ObjCInstanceMethodDecl) &&
         cxcursor::getSelectorIdentifierIndex(cursor) == -1)
      return CXChildVisit_Recurse;

    if (clang_isExpression(cursor.kind)) {
      if (cursor.kind == CXCursor_DeclRefExpr ||
          cursor.kind == CXCursor_MemberRefExpr) {
        // continue..

      } else if (cursor.kind == CXCursor_ObjCMessageExpr &&
                 cxcursor::getSelectorIdentifierIndex(cursor) != -1) {
        // continue..

      } else
        return CXChildVisit_Recurse;
    }

    SourceLocation
      Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));
    SourceLocation SelIdLoc = cxcursor::getSelectorIdentifierLoc(cursor);
    if (SelIdLoc.isValid())
      Loc = SelIdLoc;

    ASTContext &Ctx = data->getASTContext();
    SourceManager &SM = Ctx.getSourceManager();
    bool isInMacroDef = false;
    if (Loc.isMacroID()) {
      bool isMacroArg;
      Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
      isInMacroDef = !isMacroArg;
    }

    // We are looking for identifiers in a specific file.
    std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
    if (LocInfo.first != data->FID)
      return CXChildVisit_Recurse;

    if (isInMacroDef) {
      // FIXME: For a macro definition make sure that all expansions
      // of it expand to the same reference before allowing to point to it.
      return CXChildVisit_Recurse;
    }

    if (data->visitor.visit(data->visitor.context, cursor,
                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
      return CXChildVisit_Break;
  }
  return CXChildVisit_Recurse;
}

static void findIdRefsInFile(CXTranslationUnit TU, CXCursor declCursor,
                           const FileEntry *File,
                           CXCursorAndRangeVisitor Visitor) {
  assert(clang_isDeclaration(declCursor.kind));
  SourceManager &SM = cxtu::getASTUnit(TU)->getSourceManager();

  FileID FID = SM.translateFile(File);
  const Decl *Dcl = cxcursor::getCursorDecl(declCursor);
  if (!Dcl)
    return;

  FindFileIdRefVisitData data(TU, FID, Dcl,
                              cxcursor::getSelectorIdentifierIndex(declCursor),
                              Visitor);

  if (const DeclContext *DC = Dcl->getParentFunctionOrMethod()) {
    clang_visitChildren(cxcursor::MakeCXCursor(cast<Decl>(DC), TU),
                        findFileIdRefVisit, &data);
    return;
  }

  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
  CursorVisitor FindIdRefsVisitor(TU,
                                  findFileIdRefVisit, &data,
                                  /*VisitPreprocessorLast=*/true,
                                  /*VisitIncludedEntities=*/false,
                                  Range,
                                  /*VisitDeclsOnly=*/true);
  FindIdRefsVisitor.visitFileRegion();
}

namespace {

struct FindFileMacroRefVisitData {
  ASTUnit &Unit;
  const FileEntry *File;
  const IdentifierInfo *Macro;
  CXCursorAndRangeVisitor visitor;

  FindFileMacroRefVisitData(ASTUnit &Unit, const FileEntry *File,
                            const IdentifierInfo *Macro,
                            CXCursorAndRangeVisitor visitor)
    : Unit(Unit), File(File), Macro(Macro), visitor(visitor) { }

  ASTContext &getASTContext() const {
    return Unit.getASTContext();
  }
};

} // anonymous namespace

static enum CXChildVisitResult findFileMacroRefVisit(CXCursor cursor,
                                                     CXCursor parent,
                                                     CXClientData client_data) {
  const IdentifierInfo *Macro = 0;
  if (cursor.kind == CXCursor_MacroDefinition)
    Macro = getCursorMacroDefinition(cursor)->getName();
  else if (cursor.kind == CXCursor_MacroExpansion)
    Macro = getCursorMacroExpansion(cursor).getName();
  if (!Macro)
    return CXChildVisit_Continue;

  FindFileMacroRefVisitData *data = (FindFileMacroRefVisitData *)client_data;
  if (data->Macro != Macro)
    return CXChildVisit_Continue;

  SourceLocation
    Loc = cxloc::translateSourceLocation(clang_getCursorLocation(cursor));

  ASTContext &Ctx = data->getASTContext();
  SourceManager &SM = Ctx.getSourceManager();
  bool isInMacroDef = false;
  if (Loc.isMacroID()) {
    bool isMacroArg;
    Loc = getFileSpellingLoc(SM, Loc, isMacroArg);
    isInMacroDef = !isMacroArg;
  }

  // We are looking for identifiers in a specific file.
  std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc);
  if (SM.getFileEntryForID(LocInfo.first) != data->File)
    return CXChildVisit_Continue;

  if (isInMacroDef) {
    // FIXME: For a macro definition make sure that all expansions
    // of it expand to the same reference before allowing to point to it.
    return CXChildVisit_Continue;
  }

  if (data->visitor.visit(data->visitor.context, cursor,
                        cxloc::translateSourceRange(Ctx, Loc)) == CXVisit_Break)
    return CXChildVisit_Break;
  return CXChildVisit_Continue;
}

static void findMacroRefsInFile(CXTranslationUnit TU, CXCursor Cursor,
                                const FileEntry *File,
                                CXCursorAndRangeVisitor Visitor) {
  if (Cursor.kind != CXCursor_MacroDefinition &&
      Cursor.kind != CXCursor_MacroExpansion)
    return;

  ASTUnit *Unit = cxtu::getASTUnit(TU);
  SourceManager &SM = Unit->getSourceManager();

  FileID FID = SM.translateFile(File);
  const IdentifierInfo *Macro = 0;
  if (Cursor.kind == CXCursor_MacroDefinition)
    Macro = getCursorMacroDefinition(Cursor)->getName();
  else
    Macro = getCursorMacroExpansion(Cursor).getName();
  if (!Macro)
    return;

  FindFileMacroRefVisitData data(*Unit, File, Macro, Visitor);

  SourceRange Range(SM.getLocForStartOfFile(FID), SM.getLocForEndOfFile(FID));
  CursorVisitor FindMacroRefsVisitor(TU,
                                  findFileMacroRefVisit, &data,
                                  /*VisitPreprocessorLast=*/false,
                                  /*VisitIncludedEntities=*/false,
                                  Range);
  FindMacroRefsVisitor.visitPreprocessedEntitiesInRegion();
}

//===----------------------------------------------------------------------===//
// libclang public APIs.
//===----------------------------------------------------------------------===//

extern "C" {

void clang_findReferencesInFile(CXCursor cursor, CXFile file,
                                CXCursorAndRangeVisitor visitor) {
  LogRef Log = Logger::make(LLVM_FUNCTION_NAME);

  if (clang_Cursor_isNull(cursor)) {
    if (Log)
      *Log << "Null cursor";
    return;
  }
  if (cursor.kind == CXCursor_NoDeclFound) {
    if (Log)
      *Log << "Got CXCursor_NoDeclFound";
    return;
  }
  if (!file) {
    if (Log)
      *Log << "Null file";
    return;
  }
  if (!visitor.visit) {
    if (Log)
      *Log << "Null visitor";
    return;
  }

  if (Log)
    *Log << cursor << " @" << static_cast<const FileEntry *>(file);

  ASTUnit *CXXUnit = cxcursor::getCursorASTUnit(cursor);
  if (!CXXUnit)
    return;

  ASTUnit::ConcurrencyCheck Check(*CXXUnit);

  if (cursor.kind == CXCursor_MacroDefinition ||
      cursor.kind == CXCursor_MacroExpansion) {
    findMacroRefsInFile(cxcursor::getCursorTU(cursor),
                        cursor,
                        static_cast<const FileEntry *>(file),
                        visitor);
    return;
  }

  // We are interested in semantics of identifiers so for C++ constructor exprs
  // prefer type references, e.g.:
  //
  //  return MyStruct();
  //
  // for 'MyStruct' we'll have a cursor pointing at the constructor decl but
  // we are actually interested in the type declaration.
  cursor = cxcursor::getTypeRefCursor(cursor);

  CXCursor refCursor = clang_getCursorReferenced(cursor);

  if (!clang_isDeclaration(refCursor.kind)) {
    if (Log)
      *Log << "cursor is not referencing a declaration";
    return;
  }

  findIdRefsInFile(cxcursor::getCursorTU(cursor),
                   refCursor,
                   static_cast<const FileEntry *>(file),
                   visitor);
}

static enum CXVisitorResult _visitCursorAndRange(void *context,
                                                 CXCursor cursor,
                                                 CXSourceRange range) {
  CXCursorAndRangeVisitorBlock block = (CXCursorAndRangeVisitorBlock)context;
  return INVOKE_BLOCK2(block, cursor, range);
}

void clang_findReferencesInFileWithBlock(CXCursor cursor,
                                         CXFile file,
                                         CXCursorAndRangeVisitorBlock block) {
  CXCursorAndRangeVisitor visitor = { block,
                                      block ? _visitCursorAndRange : 0 };
  return clang_findReferencesInFile(cursor, file, visitor);
}

} // end: extern "C"

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011, 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 "config.h"
#include "ScriptProfiler.h"

#include "BindingVisitors.h"
#include "RetainedDOMInfo.h"
#include "ScriptObject.h"
#include "V8ArrayBufferView.h"
#include "V8Binding.h"
#include "V8DOMWindow.h"
#include "V8DOMWrapper.h"
#include "V8Node.h"
#include "WebCoreMemoryInstrumentation.h"
#include "WrapperTypeInfo.h"

#include <v8-profiler.h>

#include <wtf/ThreadSpecific.h>

namespace WebCore {

typedef HashMap<String, double> ProfileNameIdleTimeMap;

void ScriptProfiler::start(ScriptState* state, const String& title)
{
    ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
    if (profileNameIdleTimeMap->contains(title))
        return;
    profileNameIdleTimeMap->add(title, 0);

    v8::HandleScope hs;
    v8::CpuProfiler::StartProfiling(v8String(title, state ? state->isolate() : v8::Isolate::GetCurrent()), true);
}

void ScriptProfiler::startForPage(Page*, const String& title)
{
    return start(0, title);
}

void ScriptProfiler::startForWorkerContext(WorkerContext*, const String& title)
{
    return start(0, title);
}

PassRefPtr<ScriptProfile> ScriptProfiler::stop(ScriptState* state, const String& title)
{
    v8::HandleScope hs;
    const v8::CpuProfile* profile = state ?
        v8::CpuProfiler::StopProfiling(v8String(title, state->isolate()), state->context()->GetSecurityToken()) :
        v8::CpuProfiler::StopProfiling(v8String(title, v8::Isolate::GetCurrent()));
    if (!profile)
        return 0;

    String profileTitle = toWebCoreString(profile->GetTitle());
    double idleTime = 0.0;
    ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
    ProfileNameIdleTimeMap::iterator profileIdleTime = profileNameIdleTimeMap->find(profileTitle);
    if (profileIdleTime != profileNameIdleTimeMap->end()) {
        idleTime = profileIdleTime->value * 1000.0;
        profileNameIdleTimeMap->remove(profileIdleTime);
    }

    return ScriptProfile::create(profile, idleTime);
}

PassRefPtr<ScriptProfile> ScriptProfiler::stopForPage(Page*, const String& title)
{
    // Use null script state to avoid filtering by context security token.
    // All functions from all iframes should be visible from Inspector UI.
    return stop(0, title);
}

PassRefPtr<ScriptProfile> ScriptProfiler::stopForWorkerContext(WorkerContext*, const String& title)
{
    return stop(0, title);
}

void ScriptProfiler::collectGarbage()
{
    v8::V8::LowMemoryNotification();
}

ScriptObject ScriptProfiler::objectByHeapObjectId(unsigned id)
{
    // As ids are unique, it doesn't matter which HeapSnapshot owns HeapGraphNode.
    // We need to find first HeapSnapshot containing a node with the specified id.
    const v8::HeapGraphNode* node = 0;
    for (int i = 0, l = v8::HeapProfiler::GetSnapshotsCount(); i < l; ++i) {
        const v8::HeapSnapshot* snapshot = v8::HeapProfiler::GetSnapshot(i);
        node = snapshot->GetNodeById(id);
        if (node)
            break;
    }
    if (!node)
        return ScriptObject();

    v8::HandleScope scope;
    v8::Handle<v8::Value> value = node->GetHeapValue();
    if (!value->IsObject())
        return ScriptObject();

    v8::Handle<v8::Object> object = value.As<v8::Object>();

    ScriptState* scriptState = ScriptState::forContext(object->CreationContext());
    return ScriptObject(scriptState, object);
}

unsigned ScriptProfiler::getHeapObjectId(const ScriptValue& value)
{
    v8::SnapshotObjectId id = v8::HeapProfiler::GetSnapshotObjectId(value.v8Value());
    return id;
}

namespace {

class ActivityControlAdapter : public v8::ActivityControl {
public:
    ActivityControlAdapter(ScriptProfiler::HeapSnapshotProgress* progress)
            : m_progress(progress), m_firstReport(true) { }
    ControlOption ReportProgressValue(int done, int total)
    {
        ControlOption result = m_progress->isCanceled() ? kAbort : kContinue;
        if (m_firstReport) {
            m_firstReport = false;
            m_progress->Start(total);
        } else
            m_progress->Worked(done);
        if (done >= total)
            m_progress->Done();
        return result;
    }
private:
    ScriptProfiler::HeapSnapshotProgress* m_progress;
    bool m_firstReport;
};

class GlobalObjectNameResolver : public v8::HeapProfiler::ObjectNameResolver {
public:
    virtual const char* GetName(v8::Handle<v8::Object> object)
    {
        if (V8DOMWrapper::isWrapperOfType(object, &V8DOMWindow::info)) {
            DOMWindow* window = V8DOMWindow::toNative(object);
            if (window) {
                CString url = window->document()->url().string().utf8();
                m_strings.append(url);
                return url.data();
            }
        }
        return 0;
    }

private:
    Vector<CString> m_strings;
};

} // namespace

// FIXME: This method should receive a ScriptState, from which we should retrieve an Isolate.
PassRefPtr<ScriptHeapSnapshot> ScriptProfiler::takeHeapSnapshot(const String& title, HeapSnapshotProgress* control)
{
    v8::HandleScope hs;
    ASSERT(control);
    ActivityControlAdapter adapter(control);
    GlobalObjectNameResolver resolver;
    const v8::HeapSnapshot* snapshot = v8::HeapProfiler::TakeSnapshot(v8String(title, v8::Isolate::GetCurrent()), v8::HeapSnapshot::kFull, &adapter, &resolver);
    return snapshot ? ScriptHeapSnapshot::create(snapshot) : 0;
}

static v8::RetainedObjectInfo* retainedDOMInfo(uint16_t classId, v8::Handle<v8::Value> wrapper)
{
    ASSERT(classId == v8DOMNodeClassId);
    if (!wrapper->IsObject())
        return 0;
    Node* node = V8Node::toNative(wrapper.As<v8::Object>());
    return node ? new RetainedDOMInfo(node) : 0;
}

void ScriptProfiler::initialize()
{
    v8::HeapProfiler::DefineWrapperClass(v8DOMNodeClassId, &retainedDOMInfo);
}

void ScriptProfiler::visitNodeWrappers(WrappedNodeVisitor* visitor)
{
    v8::HandleScope scope;

    // visitNodeWrappers() should receive a ScriptState and retrieve an Isolate
    // from the ScriptState.
    v8::Isolate* isolate = v8::Isolate::GetCurrent();

    class DOMNodeWrapperVisitor : public v8::PersistentHandleVisitor {
    public:
        DOMNodeWrapperVisitor(WrappedNodeVisitor* visitor, v8::Isolate* isolate)
            : m_visitor(visitor)
            , m_isolate(isolate)
        {
        }

        virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE
        {
            if (classId != v8DOMNodeClassId)
                return;
            UNUSED_PARAM(m_isolate);
            ASSERT(V8Node::HasInstance(value, m_isolate, worldType(m_isolate)));
            ASSERT(value->IsObject());
            v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>::Cast(value);
            m_visitor->visitNode(V8Node::toNative(wrapper));
        }

    private:
        WrappedNodeVisitor* m_visitor;
        v8::Isolate* m_isolate;
    } wrapperVisitor(visitor, isolate);

    v8::V8::VisitHandlesWithClassIds(&wrapperVisitor);
}

void ScriptProfiler::visitExternalStrings(ExternalStringVisitor* visitor)
{
    V8PerIsolateData::current()->visitExternalStrings(visitor);
}

void ScriptProfiler::visitExternalArrays(ExternalArrayVisitor* visitor)
{
    class DOMObjectWrapperVisitor : public v8::PersistentHandleVisitor {
    public:
        explicit DOMObjectWrapperVisitor(ExternalArrayVisitor* visitor)
            : m_visitor(visitor)
        {
        }

        virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE
        {
            if (classId != v8DOMObjectClassId)
                return;
            ASSERT(value->IsObject());
            v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>::Cast(value);
            if (!toWrapperTypeInfo(wrapper)->isSubclass(&V8ArrayBufferView::info))
                return;
            m_visitor->visitJSExternalArray(V8ArrayBufferView::toNative(wrapper));
        }

    private:
        ExternalArrayVisitor* m_visitor;
    } wrapperVisitor(visitor);

    v8::V8::VisitHandlesWithClassIds(&wrapperVisitor);
}

void ScriptProfiler::collectBindingMemoryInfo(MemoryInstrumentation* instrumentation)
{
    V8PerIsolateData* data = V8PerIsolateData::current();
    instrumentation->addRootObject(data);
}

size_t ScriptProfiler::profilerSnapshotsSize()
{
    return v8::HeapProfiler::GetMemorySizeUsedByProfiler();
}

ProfileNameIdleTimeMap* ScriptProfiler::currentProfileNameIdleTimeMap()
{
    AtomicallyInitializedStatic(WTF::ThreadSpecific<ProfileNameIdleTimeMap>*, map = new WTF::ThreadSpecific<ProfileNameIdleTimeMap>);
    return *map;
}

} // namespace WebCore

<commit_msg>Use isolate-based versions of CPU and Heap profilers<commit_after>/*
 * Copyright (c) 2011, 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 "config.h"
#include "ScriptProfiler.h"

#include "BindingVisitors.h"
#include "RetainedDOMInfo.h"
#include "ScriptObject.h"
#include "V8ArrayBufferView.h"
#include "V8Binding.h"
#include "V8DOMWindow.h"
#include "V8DOMWrapper.h"
#include "V8Node.h"
#include "WebCoreMemoryInstrumentation.h"
#include "WrapperTypeInfo.h"

#include <v8-profiler.h>

#include <wtf/ThreadSpecific.h>

namespace WebCore {

typedef HashMap<String, double> ProfileNameIdleTimeMap;

void ScriptProfiler::start(ScriptState* state, const String& title)
{
    ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
    if (profileNameIdleTimeMap->contains(title))
        return;
    profileNameIdleTimeMap->add(title, 0);

    v8::Isolate* isolate = state ? state->isolate() : v8::Isolate::GetCurrent();
    v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
    if (!profiler)
        return;
    v8::HandleScope handleScope(isolate);
    profiler->StartCpuProfiling(v8String(title, isolate), true);
}

void ScriptProfiler::startForPage(Page*, const String& title)
{
    return start(0, title);
}

void ScriptProfiler::startForWorkerContext(WorkerContext*, const String& title)
{
    return start(0, title);
}

PassRefPtr<ScriptProfile> ScriptProfiler::stop(ScriptState* state, const String& title)
{
    v8::Isolate* isolate = state ? state->isolate() : v8::Isolate::GetCurrent();
    v8::CpuProfiler* profiler = isolate->GetCpuProfiler();
    if (!profiler)
        return 0;
    v8::HandleScope handleScope(isolate);
    v8::Handle<v8::Value> securityToken;
    if (state)
        securityToken = state->context()->GetSecurityToken();
    const v8::CpuProfile* profile = profiler->StopCpuProfiling(v8String(title, isolate), securityToken);
    if (!profile)
        return 0;

    String profileTitle = toWebCoreString(profile->GetTitle());
    double idleTime = 0.0;
    ProfileNameIdleTimeMap* profileNameIdleTimeMap = ScriptProfiler::currentProfileNameIdleTimeMap();
    ProfileNameIdleTimeMap::iterator profileIdleTime = profileNameIdleTimeMap->find(profileTitle);
    if (profileIdleTime != profileNameIdleTimeMap->end()) {
        idleTime = profileIdleTime->value * 1000.0;
        profileNameIdleTimeMap->remove(profileIdleTime);
    }

    return ScriptProfile::create(profile, idleTime);
}

PassRefPtr<ScriptProfile> ScriptProfiler::stopForPage(Page*, const String& title)
{
    // Use null script state to avoid filtering by context security token.
    // All functions from all iframes should be visible from Inspector UI.
    return stop(0, title);
}

PassRefPtr<ScriptProfile> ScriptProfiler::stopForWorkerContext(WorkerContext*, const String& title)
{
    return stop(0, title);
}

void ScriptProfiler::collectGarbage()
{
    v8::V8::LowMemoryNotification();
}

ScriptObject ScriptProfiler::objectByHeapObjectId(unsigned id)
{
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    if (!profiler)
        return ScriptObject();
    // As ids are unique, it doesn't matter which HeapSnapshot owns HeapGraphNode.
    // We need to find first HeapSnapshot containing a node with the specified id.
    const v8::HeapGraphNode* node = 0;
    for (int i = 0, l = profiler->GetSnapshotCount(); i < l; ++i) {
        const v8::HeapSnapshot* snapshot = profiler->GetHeapSnapshot(i);
        node = snapshot->GetNodeById(id);
        if (node)
            break;
    }
    if (!node)
        return ScriptObject();

    v8::HandleScope handleScope(isolate);
    v8::Handle<v8::Value> value = node->GetHeapValue();
    if (!value->IsObject())
        return ScriptObject();

    v8::Handle<v8::Object> object = value.As<v8::Object>();

    ScriptState* scriptState = ScriptState::forContext(object->CreationContext());
    return ScriptObject(scriptState, object);
}

unsigned ScriptProfiler::getHeapObjectId(const ScriptValue& value)
{
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    v8::SnapshotObjectId id = profiler->GetObjectId(value.v8Value());
    return id;
}

namespace {

class ActivityControlAdapter : public v8::ActivityControl {
public:
    ActivityControlAdapter(ScriptProfiler::HeapSnapshotProgress* progress)
            : m_progress(progress), m_firstReport(true) { }
    ControlOption ReportProgressValue(int done, int total)
    {
        ControlOption result = m_progress->isCanceled() ? kAbort : kContinue;
        if (m_firstReport) {
            m_firstReport = false;
            m_progress->Start(total);
        } else
            m_progress->Worked(done);
        if (done >= total)
            m_progress->Done();
        return result;
    }
private:
    ScriptProfiler::HeapSnapshotProgress* m_progress;
    bool m_firstReport;
};

class GlobalObjectNameResolver : public v8::HeapProfiler::ObjectNameResolver {
public:
    virtual const char* GetName(v8::Handle<v8::Object> object)
    {
        if (V8DOMWrapper::isWrapperOfType(object, &V8DOMWindow::info)) {
            DOMWindow* window = V8DOMWindow::toNative(object);
            if (window) {
                CString url = window->document()->url().string().utf8();
                m_strings.append(url);
                return url.data();
            }
        }
        return 0;
    }

private:
    Vector<CString> m_strings;
};

} // namespace

// FIXME: This method should receive a ScriptState, from which we should retrieve an Isolate.
PassRefPtr<ScriptHeapSnapshot> ScriptProfiler::takeHeapSnapshot(const String& title, HeapSnapshotProgress* control)
{
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    if (!profiler)
        return 0;
    v8::HandleScope handleScope(isolate);
    ASSERT(control);
    ActivityControlAdapter adapter(control);
    GlobalObjectNameResolver resolver;
    const v8::HeapSnapshot* snapshot = profiler->TakeHeapSnapshot(v8String(title, isolate), &adapter, &resolver);
    return snapshot ? ScriptHeapSnapshot::create(snapshot) : 0;
}

static v8::RetainedObjectInfo* retainedDOMInfo(uint16_t classId, v8::Handle<v8::Value> wrapper)
{
    ASSERT(classId == v8DOMNodeClassId);
    if (!wrapper->IsObject())
        return 0;
    Node* node = V8Node::toNative(wrapper.As<v8::Object>());
    return node ? new RetainedDOMInfo(node) : 0;
}

void ScriptProfiler::initialize()
{
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    if (profiler)
        profiler->SetWrapperClassInfoProvider(v8DOMNodeClassId, &retainedDOMInfo);
}

void ScriptProfiler::visitNodeWrappers(WrappedNodeVisitor* visitor)
{
    // visitNodeWrappers() should receive a ScriptState and retrieve an Isolate
    // from the ScriptState.
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HandleScope handleScope(isolate);

    class DOMNodeWrapperVisitor : public v8::PersistentHandleVisitor {
    public:
        DOMNodeWrapperVisitor(WrappedNodeVisitor* visitor, v8::Isolate* isolate)
            : m_visitor(visitor)
            , m_isolate(isolate)
        {
        }

        virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE
        {
            if (classId != v8DOMNodeClassId)
                return;
            UNUSED_PARAM(m_isolate);
            ASSERT(V8Node::HasInstance(value, m_isolate, worldType(m_isolate)));
            ASSERT(value->IsObject());
            v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>::Cast(value);
            m_visitor->visitNode(V8Node::toNative(wrapper));
        }

    private:
        WrappedNodeVisitor* m_visitor;
        v8::Isolate* m_isolate;
    } wrapperVisitor(visitor, isolate);

    v8::V8::VisitHandlesWithClassIds(&wrapperVisitor);
}

void ScriptProfiler::visitExternalStrings(ExternalStringVisitor* visitor)
{
    V8PerIsolateData::current()->visitExternalStrings(visitor);
}

void ScriptProfiler::visitExternalArrays(ExternalArrayVisitor* visitor)
{
    class DOMObjectWrapperVisitor : public v8::PersistentHandleVisitor {
    public:
        explicit DOMObjectWrapperVisitor(ExternalArrayVisitor* visitor)
            : m_visitor(visitor)
        {
        }

        virtual void VisitPersistentHandle(v8::Persistent<v8::Value> value, uint16_t classId) OVERRIDE
        {
            if (classId != v8DOMObjectClassId)
                return;
            ASSERT(value->IsObject());
            v8::Persistent<v8::Object> wrapper = v8::Persistent<v8::Object>::Cast(value);
            if (!toWrapperTypeInfo(wrapper)->isSubclass(&V8ArrayBufferView::info))
                return;
            m_visitor->visitJSExternalArray(V8ArrayBufferView::toNative(wrapper));
        }

    private:
        ExternalArrayVisitor* m_visitor;
    } wrapperVisitor(visitor);

    v8::V8::VisitHandlesWithClassIds(&wrapperVisitor);
}

void ScriptProfiler::collectBindingMemoryInfo(MemoryInstrumentation* instrumentation)
{
    V8PerIsolateData* data = V8PerIsolateData::current();
    instrumentation->addRootObject(data);
}

size_t ScriptProfiler::profilerSnapshotsSize()
{
    v8::Isolate* isolate = v8::Isolate::GetCurrent();
    v8::HeapProfiler* profiler = isolate->GetHeapProfiler();
    if (!profiler)
        return 0;
    return profiler->GetProfilerMemorySize();
}

ProfileNameIdleTimeMap* ScriptProfiler::currentProfileNameIdleTimeMap()
{
    AtomicallyInitializedStatic(WTF::ThreadSpecific<ProfileNameIdleTimeMap>*, map = new WTF::ThreadSpecific<ProfileNameIdleTimeMap>);
    return *map;
}

} // namespace WebCore

<|endoftext|>


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

#include "timed_stream_fixture.h"

#include "print_processor.h"

#include "tvs/tracing.h"

#include "gtest/gtest.h"

class StreamEventSemantics : public timed_stream_fixture_b
{
  using base_type = timed_stream_fixture_b;

  using value_type = std::set<int>;
  using traits_type = tracing::timed_event_traits<value_type>;

  using stream_type = tracing::timed_stream<value_type, traits_type>;
  using printer_type = test_event_printer<int>;

  // special event writer to simplify event handling with std::set<T>
  using writer_type = tracing::timed_event_writer<int>;

protected:
  StreamEventSemantics()
    : base_type()
    , writer("writer", tracing::STREAM_CREATE)
  {
    printer.in(writer.name());
  }

  void expect_processor_output(std::string const& str)
  {
    std::stringstream actual;
    printer.print(actual);
    EXPECT_EQ(str, actual.str());
    printer.clear();
  }

  writer_type writer;
  printer_type printer;
};

TEST_F(StreamEventSemantics, PushRelEvent)
{
  // Push two events at the relative offset dur
  writer.push(0, dur);
  writer.push(10, dur);
  writer.commit();

  expect_processor_output("@1 s: { 0, 10 }\n");
}

TEST_F(StreamEventSemantics, PushAbsEvent)
{
  // Push two events to an absolute time point
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(10, abs);
  writer.commit();
  expect_processor_output("@1 s: { 0, 10 }\n");
}

TEST_F(StreamEventSemantics, PushSemantics)
{
  // Push two events at the relative offset dur
  writer.push(0, dur);
  writer.push(10, 2 * dur);
  writer.commit();
  expect_processor_output("@1 s: { 0 }\n"
                          "@2 s: { 10 }\n");
}

TEST_F(StreamEventSemantics, PartialCommit)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(10, abs / 2.0);

  // note that we use the absolute time commit interface here
  writer.commit(abs / 2.0);
  writer.commit(abs);

  // a split/merge results in decaying of the old event value to the 'right'
  // side while the duration and an empty value is created on the 'left' side of
  // a split
  std::stringstream exp;
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeSemantics)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(10, abs / 2.0);
  writer.commit();

  // a split/merge results in decaying of the old event value to the 'right'
  // side while the duration and an empty value is created on the 'left' side of
  // a split

  std::stringstream exp;
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeSemanticsTwo)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(5, abs);
  writer.push(10, abs / 2.0);
  writer.push(10, abs / 4.0);
  writer.commit();

  // we expect to have the events ordered correctly
  std::stringstream exp;
  exp << "@" << dur / 4.0 << ": { 10 }\n";
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0, 5 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeSemanticsThree)
{
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(5, abs);
  writer.push(10, abs / 2.0);
  writer.push(10, abs / 4.0);

  // now, push again over the whole 'duration'
  writer.push(10, abs);
  writer.commit();

  std::stringstream exp;
  exp << "@" << dur / 4.0 << ": { 10 }\n";
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0, 5, 10 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeCommitSemantics)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(5, abs);
  writer.push(10, abs / 2.0);
  writer.push(10, abs / 4.0);

  // now further split the stream by performing partial commits
  writer.commit(dur / 8.0);
  writer.commit(dur / 8.0);

  writer.commit(dur / 4.0);
  writer.commit(dur / 4.0);

  // the last commit should update up to the last event of the pushed events
  writer.commit();

  // we expect to have the events ordered correctly and have the splits at the
  // commit boundaries
  std::stringstream exp;
  exp << "@" << dur / 8.0 << ": { - }\n";
  exp << "@" << dur / 4.0 << ": { 10 }\n";
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur * (3.0 / 4.0)  << ": { - }\n";
  exp << "@" << dur << ": { 0, 5 }\n";

  expect_processor_output(exp.str());
}

tracing::time_type operator"" _ns(unsigned long long val)
{
#ifdef SYSX_NO_SYSTEMC
  return tracing::time_type(val * 1.0 * sysx::si::nanoseconds );
#else
  return tracing::time_type(1.0 * val, sc_core::SC_NS);
#endif
}

tracing::time_type operator"" _ms(unsigned long long val)
{
#ifdef SYSX_NO_SYSTEMC
  return tracing::time_type(val * 1.0 * sysx::si::milliseconds );
#else
  return tracing::time_type(1.0 * val, sc_core::SC_MS);
#endif
}

TEST_F(StreamEventSemantics, RoundingErrorTests)
{
  writer.push(0, 251_ns);
  writer.push(0, 46_ns);
  writer.push(0, 209_ns);
  writer.commit();
  EXPECT_EQ(writer.local_time(), 251_ns);
}

TEST_F(StreamEventSemantics, RoundingErrorTests2)
{
  writer.push(0, 52_ns);
  writer.push(0, 126_ns);
  writer.push(0, 248_ns);
  writer.push(0, 22_ns);
  writer.push(0, 26_ns);
  writer.push(0, 253_ns);

  writer.commit();
  EXPECT_EQ(writer.local_time(), 253_ns);
}
<commit_msg>tests: enable rounding error tests in release mode<commit_after>/*
 * Copyright (c) 2017-2018 OFFIS Institute for Information Technology
 *                          Oldenburg, Germany
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "timed_stream_fixture.h"

#include "print_processor.h"

#include "tvs/tracing.h"

#include "gtest/gtest.h"

class StreamEventSemantics : public timed_stream_fixture_b
{
  using base_type = timed_stream_fixture_b;

  using value_type = std::set<int>;
  using traits_type = tracing::timed_event_traits<value_type>;

  using stream_type = tracing::timed_stream<value_type, traits_type>;
  using printer_type = test_event_printer<int>;

  // special event writer to simplify event handling with std::set<T>
  using writer_type = tracing::timed_event_writer<int>;

protected:
  StreamEventSemantics()
    : base_type()
    , writer("writer", tracing::STREAM_CREATE)
  {
    printer.in(writer.name());
  }

  void expect_processor_output(std::string const& str)
  {
    std::stringstream actual;
    printer.print(actual);
    EXPECT_EQ(str, actual.str());
    printer.clear();
  }

  writer_type writer;
  printer_type printer;
};

TEST_F(StreamEventSemantics, PushRelEvent)
{
  // Push two events at the relative offset dur
  writer.push(0, dur);
  writer.push(10, dur);
  writer.commit();

  expect_processor_output("@1 s: { 0, 10 }\n");
}

TEST_F(StreamEventSemantics, PushAbsEvent)
{
  // Push two events to an absolute time point
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(10, abs);
  writer.commit();
  expect_processor_output("@1 s: { 0, 10 }\n");
}

TEST_F(StreamEventSemantics, PushSemantics)
{
  // Push two events at the relative offset dur
  writer.push(0, dur);
  writer.push(10, 2 * dur);
  writer.commit();
  expect_processor_output("@1 s: { 0 }\n"
                          "@2 s: { 10 }\n");
}

TEST_F(StreamEventSemantics, PartialCommit)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(10, abs / 2.0);

  // note that we use the absolute time commit interface here
  writer.commit(abs / 2.0);
  writer.commit(abs);

  // a split/merge results in decaying of the old event value to the 'right'
  // side while the duration and an empty value is created on the 'left' side of
  // a split
  std::stringstream exp;
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeSemantics)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(10, abs / 2.0);
  writer.commit();

  // a split/merge results in decaying of the old event value to the 'right'
  // side while the duration and an empty value is created on the 'left' side of
  // a split

  std::stringstream exp;
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeSemanticsTwo)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(5, abs);
  writer.push(10, abs / 2.0);
  writer.push(10, abs / 4.0);
  writer.commit();

  // we expect to have the events ordered correctly
  std::stringstream exp;
  exp << "@" << dur / 4.0 << ": { 10 }\n";
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0, 5 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeSemanticsThree)
{
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(5, abs);
  writer.push(10, abs / 2.0);
  writer.push(10, abs / 4.0);

  // now, push again over the whole 'duration'
  writer.push(10, abs);
  writer.commit();

  std::stringstream exp;
  exp << "@" << dur / 4.0 << ": { 10 }\n";
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur << ": { 0, 5, 10 }\n";

  expect_processor_output(exp.str());
}

TEST_F(StreamEventSemantics, SplitMergeCommitSemantics)
{
  // Push two events to an absolute time point which need to be 'merged'
  tracing::time_type abs{ dur };
  writer.push(0, abs);
  writer.push(5, abs);
  writer.push(10, abs / 2.0);
  writer.push(10, abs / 4.0);

  // now further split the stream by performing partial commits
  writer.commit(dur / 8.0);
  writer.commit(dur / 8.0);

  writer.commit(dur / 4.0);
  writer.commit(dur / 4.0);

  // the last commit should update up to the last event of the pushed events
  writer.commit();

  // we expect to have the events ordered correctly and have the splits at the
  // commit boundaries
  std::stringstream exp;
  exp << "@" << dur / 8.0 << ": { - }\n";
  exp << "@" << dur / 4.0 << ": { 10 }\n";
  exp << "@" << dur / 2.0 << ": { 10 }\n";
  exp << "@" << dur * (3.0 / 4.0)  << ": { - }\n";
  exp << "@" << dur << ": { 0, 5 }\n";

  expect_processor_output(exp.str());
}

tracing::time_type operator"" _ns(unsigned long long val)
{
#ifdef SYSX_NO_SYSTEMC
  return tracing::time_type(val * 1.0 * sysx::si::nanoseconds );
#else
  return tracing::time_type(1.0 * val, sc_core::SC_NS);
#endif
}

tracing::time_type operator"" _ms(unsigned long long val)
{
#ifdef SYSX_NO_SYSTEMC
  return tracing::time_type(val * 1.0 * sysx::si::milliseconds );
#else
  return tracing::time_type(1.0 * val, sc_core::SC_MS);
#endif
}

// only in release mode
#if defined(SYSX_NDEBUG)

TEST_F(StreamEventSemantics, RoundingErrorTests)
{
  writer.push(0, 251_ns);
  writer.push(0, 46_ns);
  writer.push(0, 209_ns);
  writer.commit();
  EXPECT_EQ(writer.local_time(), 251_ns);
}

TEST_F(StreamEventSemantics, RoundingErrorTests2)
{
  writer.push(0, 52_ns);
  writer.push(0, 126_ns);
  writer.push(0, 248_ns);
  writer.push(0, 22_ns);
  writer.push(0, 26_ns);
  writer.push(0, 253_ns);

  writer.commit();
  EXPECT_EQ(writer.local_time(), 253_ns);
}

#endif
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <qfxtester.h>
#include <QDebug>
#include <QApplication>
#include <qmlview.h>
#include <QFile>
#include <QmlComponent>
#include <QDir>
#include <QCryptographicHash>
#include <private/qabstractanimation_p.h>
#include <private/qmlgraphicsitem_p.h>

QT_BEGIN_NAMESPACE

QML_DEFINE_TYPE(Qt.VisualTest, 4,6, VisualTest, QmlGraphicsVisualTest);
QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Frame, QmlGraphicsVisualTestFrame);
QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Mouse, QmlGraphicsVisualTestMouse);
QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Key, QmlGraphicsVisualTestKey);

QmlGraphicsTester::QmlGraphicsTester(const QString &script, QmlViewer::ScriptOptions opts,
                     QmlView *parent)
: QAbstractAnimation(parent), m_script(script), m_view(parent), filterEvents(true), options(opts),
  testscript(0), hasCompleted(false), hasFailed(false)
{
    parent->viewport()->installEventFilter(this);
    parent->installEventFilter(this);
    QUnifiedTimer::instance()->setConsistentTiming(true);
    if (options & QmlViewer::Play)
        this->run();
    start();
}

QmlGraphicsTester::~QmlGraphicsTester()
{
    if (!hasFailed &&
        options & QmlViewer::Record &&
        options & QmlViewer::SaveOnExit)
        save();
}

int QmlGraphicsTester::duration() const
{
    return -1;
}

void QmlGraphicsTester::addMouseEvent(Destination dest, QMouseEvent *me)
{
    MouseEvent e(me);
    e.destination = dest;
    m_mouseEvents << e;
}

void QmlGraphicsTester::addKeyEvent(Destination dest, QKeyEvent *ke)
{
    KeyEvent e(ke);
    e.destination = dest;
    m_keyEvents << e;
}

bool QmlGraphicsTester::eventFilter(QObject *o, QEvent *e)
{
    if (!filterEvents)
        return false;

    Destination destination;
    if (o == m_view) {
        destination = View;
    } else if (o == m_view->viewport()) {
        destination = ViewPort;
    } else {
        return false;
    }

    switch (e->type()) {
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
            addKeyEvent(destination, (QKeyEvent *)e);
            return true;
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseMove:
        case QEvent::MouseButtonDblClick:
            addMouseEvent(destination, (QMouseEvent *)e);
            return true;
        default:
            break;
    }
    return false;
}

void QmlGraphicsTester::executefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QmlGraphicsTester::imagefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QmlGraphicsTester::complete()
{
    if ((options & QmlViewer::TestErrorProperty) && !hasFailed) {
        QString e = m_view->root()->property("error").toString();
        if (!e.isEmpty()) {
            qWarning() << "Test failed:" << e;
            hasFailed = true;
        }
    }
    if (options & QmlViewer::ExitOnComplete)
        QApplication::exit(hasFailed?-1:0);

    if (hasCompleted)
        return;
    hasCompleted = true;

    if (options & QmlViewer::Play)
        qWarning("Script playback complete");
}

void QmlGraphicsTester::run()
{
    QmlComponent c(m_view->engine(), m_script + QLatin1String(".qml"));

    testscript = qobject_cast<QmlGraphicsVisualTest *>(c.create());
    if (testscript) testscript->setParent(this);
    else executefailure();
    testscriptidx = 0;
}

void QmlGraphicsTester::save()
{
    QString filename = m_script + QLatin1String(".qml");
    QFileInfo filenameInfo(filename);
    QDir saveDir = filenameInfo.absoluteDir();
    saveDir.mkpath(".");

    QFile file(filename);
    file.open(QIODevice::WriteOnly);
    QTextStream ts(&file);

    ts << "import Qt.VisualTest 4.6\n\n";
    ts << "VisualTest {\n";

    int imgCount = 0;
    QList<KeyEvent> keyevents = m_savedKeyEvents;
    QList<MouseEvent> mouseevents = m_savedMouseEvents;
    for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) {
        const FrameEvent &fe = m_savedFrameEvents.at(ii);
        ts << "    Frame {\n";
        ts << "        msec: " << fe.msec << "\n";
        if (!fe.hash.isEmpty()) {
            ts << "        hash: \"" << fe.hash.toHex() << "\"\n";
        } else if (!fe.image.isNull()) {
            QString filename = filenameInfo.baseName() + "." + QString::number(imgCount) + ".png";
            fe.image.save(m_script + "." + QString::number(imgCount) + ".png");
            imgCount++;
            ts << "        image: \"" << filename << "\"\n";
        }
        ts << "    }\n";

        while (!mouseevents.isEmpty() &&
               mouseevents.first().msec == fe.msec) {
            MouseEvent me = mouseevents.takeFirst();

            ts << "    Mouse {\n";
            ts << "        type: " << me.type << "\n";
            ts << "        button: " << me.button << "\n";
            ts << "        buttons: " << me.buttons << "\n";
            ts << "        x: " << me.pos.x() << "; y: " << me.pos.y() << "\n";
            ts << "        modifiers: " << me.modifiers << "\n";
            if (me.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }

        while (!keyevents.isEmpty() &&
               keyevents.first().msec == fe.msec) {
            KeyEvent ke = keyevents.takeFirst();

            ts << "    Key {\n";
            ts << "        type: " << ke.type << "\n";
            ts << "        key: " << ke.key << "\n";
            ts << "        modifiers: " << ke.modifiers << "\n";
            ts << "        text: \"" << ke.text.toUtf8().toHex() << "\"\n";
            ts << "        autorep: " << (ke.autorep?"true":"false") << "\n";
            ts << "        count: " << ke.count << "\n";
            if (ke.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }
    }

    ts << "}\n";
    file.close();
}

void QmlGraphicsTester::updateCurrentTime(int msec)
{
    QmlGraphicsItemPrivate::setConsistentTime(msec);

    QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32);

    if (options & QmlViewer::TestImages) {
        img.fill(qRgb(255,255,255));
        QPainter p(&img);
        m_view->render(&p);
    }

    FrameEvent fe;
    fe.msec = msec;
    if (msec == 0 || !(options & QmlViewer::TestImages)) {
        // Skip first frame, skip if not doing images
    } else if (0 == (m_savedFrameEvents.count() % 60)) {
        fe.image = img;
    } else {
        QCryptographicHash hash(QCryptographicHash::Md5);
        hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height());
        fe.hash = hash.result();
    }
    m_savedFrameEvents.append(fe);

    // Deliver mouse events
    filterEvents = false;

    if (!testscript) {
        for (int ii = 0; ii < m_mouseEvents.count(); ++ii) {
            MouseEvent &me = m_mouseEvents[ii];
            me.msec = msec;
            QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers);

            if (me.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }

        for (int ii = 0; ii < m_keyEvents.count(); ++ii) {
            KeyEvent &ke = m_keyEvents[ii];
            ke.msec = msec;
            QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count);

            if (ke.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }
        m_savedMouseEvents.append(m_mouseEvents);
        m_savedKeyEvents.append(m_keyEvents);
    }

    m_mouseEvents.clear();
    m_keyEvents.clear();

    // Advance test script
    static int imgCount = 0;
    while (testscript && testscript->count() > testscriptidx) {

        QObject *event = testscript->event(testscriptidx);

        if (QmlGraphicsVisualTestFrame *frame = qobject_cast<QmlGraphicsVisualTestFrame *>(event)) {
            if (frame->msec() < msec) {
                if (options & QmlViewer::TestImages && !(options & QmlViewer::Record)) {
                    qWarning() << "QmlGraphicsTester: Extra frame.  Seen:"
                               << msec << "Expected:" << frame->msec();
                    imagefailure();
                }
            } else if (frame->msec() == msec) {
                if (!frame->hash().isEmpty() && frame->hash().toUtf8() != fe.hash.toHex()) {
                    if (options & QmlViewer::TestImages && !(options & QmlViewer::Record)) {
                        qWarning() << "QmlGraphicsTester: Mismatched frame hash.  Seen:"
                                   << fe.hash.toHex() << "Expected:"
                                   << frame->hash().toUtf8();
                        imagefailure();
                    }
                }
            } else if (frame->msec() > msec) {
                break;
            }

            if (options & QmlViewer::TestImages && !(options & QmlViewer::Record) && !frame->image().isEmpty()) {
                QImage goodImage(frame->image().toLocalFile());
                if (goodImage != img) {
                    QString reject(frame->image().toLocalFile() + ".reject.png");
                    qWarning() << "QmlGraphicsTester: Image mismatch.  Reject saved to:"
                               << reject;
                    img.save(reject);
                    imagefailure();
                }
            }
        } else if (QmlGraphicsVisualTestMouse *mouse = qobject_cast<QmlGraphicsVisualTestMouse *>(event)) {
            QPoint pos(mouse->x(), mouse->y());
            QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos;
            QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers());

            MouseEvent me(&event);
            me.msec = msec;
            if (!mouse->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                me.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                me.destination = ViewPort;
            }
            m_savedMouseEvents.append(me);
        } else if (QmlGraphicsVisualTestKey *key = qobject_cast<QmlGraphicsVisualTestKey *>(event)) {

            QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count());

            KeyEvent ke(&event);
            ke.msec = msec;
            if (!key->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                ke.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                ke.destination = ViewPort;
            }
            m_savedKeyEvents.append(ke);
        }
        testscriptidx++;
    }

    filterEvents = true;

    if (testscript && testscript->count() <= testscriptidx)
        complete();
}

QT_END_NAMESPACE
<commit_msg>Always exit on play script failure<commit_after>/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <qfxtester.h>
#include <QDebug>
#include <QApplication>
#include <qmlview.h>
#include <QFile>
#include <QmlComponent>
#include <QDir>
#include <QCryptographicHash>
#include <private/qabstractanimation_p.h>
#include <private/qmlgraphicsitem_p.h>

QT_BEGIN_NAMESPACE

QML_DEFINE_TYPE(Qt.VisualTest, 4,6, VisualTest, QmlGraphicsVisualTest);
QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Frame, QmlGraphicsVisualTestFrame);
QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Mouse, QmlGraphicsVisualTestMouse);
QML_DEFINE_TYPE(Qt.VisualTest, 4,6, Key, QmlGraphicsVisualTestKey);

QmlGraphicsTester::QmlGraphicsTester(const QString &script, QmlViewer::ScriptOptions opts,
                     QmlView *parent)
: QAbstractAnimation(parent), m_script(script), m_view(parent), filterEvents(true), options(opts),
  testscript(0), hasCompleted(false), hasFailed(false)
{
    parent->viewport()->installEventFilter(this);
    parent->installEventFilter(this);
    QUnifiedTimer::instance()->setConsistentTiming(true);
    if (options & QmlViewer::Play)
        this->run();
    start();
}

QmlGraphicsTester::~QmlGraphicsTester()
{
    if (!hasFailed &&
        options & QmlViewer::Record &&
        options & QmlViewer::SaveOnExit)
        save();
}

int QmlGraphicsTester::duration() const
{
    return -1;
}

void QmlGraphicsTester::addMouseEvent(Destination dest, QMouseEvent *me)
{
    MouseEvent e(me);
    e.destination = dest;
    m_mouseEvents << e;
}

void QmlGraphicsTester::addKeyEvent(Destination dest, QKeyEvent *ke)
{
    KeyEvent e(ke);
    e.destination = dest;
    m_keyEvents << e;
}

bool QmlGraphicsTester::eventFilter(QObject *o, QEvent *e)
{
    if (!filterEvents)
        return false;

    Destination destination;
    if (o == m_view) {
        destination = View;
    } else if (o == m_view->viewport()) {
        destination = ViewPort;
    } else {
        return false;
    }

    switch (e->type()) {
        case QEvent::KeyPress:
        case QEvent::KeyRelease:
            addKeyEvent(destination, (QKeyEvent *)e);
            return true;
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseMove:
        case QEvent::MouseButtonDblClick:
            addMouseEvent(destination, (QMouseEvent *)e);
            return true;
        default:
            break;
    }
    return false;
}

void QmlGraphicsTester::executefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QmlGraphicsTester::imagefailure()
{
    hasFailed = true;

    if (options & QmlViewer::ExitOnFailure)
        exit(-1);
}

void QmlGraphicsTester::complete()
{
    if ((options & QmlViewer::TestErrorProperty) && !hasFailed) {
        QString e = m_view->root()->property("error").toString();
        if (!e.isEmpty()) {
            qWarning() << "Test failed:" << e;
            hasFailed = true;
        }
    }
    if (options & QmlViewer::ExitOnComplete)
        QApplication::exit(hasFailed?-1:0);

    if (hasCompleted)
        return;
    hasCompleted = true;

    if (options & QmlViewer::Play)
        qWarning("Script playback complete");
}

void QmlGraphicsTester::run()
{
    QmlComponent c(m_view->engine(), m_script + QLatin1String(".qml"));

    testscript = qobject_cast<QmlGraphicsVisualTest *>(c.create());
    if (testscript) testscript->setParent(this);
    else { executefailure(); exit(-1); }
    testscriptidx = 0;
}

void QmlGraphicsTester::save()
{
    QString filename = m_script + QLatin1String(".qml");
    QFileInfo filenameInfo(filename);
    QDir saveDir = filenameInfo.absoluteDir();
    saveDir.mkpath(".");

    QFile file(filename);
    file.open(QIODevice::WriteOnly);
    QTextStream ts(&file);

    ts << "import Qt.VisualTest 4.6\n\n";
    ts << "VisualTest {\n";

    int imgCount = 0;
    QList<KeyEvent> keyevents = m_savedKeyEvents;
    QList<MouseEvent> mouseevents = m_savedMouseEvents;
    for (int ii = 0; ii < m_savedFrameEvents.count(); ++ii) {
        const FrameEvent &fe = m_savedFrameEvents.at(ii);
        ts << "    Frame {\n";
        ts << "        msec: " << fe.msec << "\n";
        if (!fe.hash.isEmpty()) {
            ts << "        hash: \"" << fe.hash.toHex() << "\"\n";
        } else if (!fe.image.isNull()) {
            QString filename = filenameInfo.baseName() + "." + QString::number(imgCount) + ".png";
            fe.image.save(m_script + "." + QString::number(imgCount) + ".png");
            imgCount++;
            ts << "        image: \"" << filename << "\"\n";
        }
        ts << "    }\n";

        while (!mouseevents.isEmpty() &&
               mouseevents.first().msec == fe.msec) {
            MouseEvent me = mouseevents.takeFirst();

            ts << "    Mouse {\n";
            ts << "        type: " << me.type << "\n";
            ts << "        button: " << me.button << "\n";
            ts << "        buttons: " << me.buttons << "\n";
            ts << "        x: " << me.pos.x() << "; y: " << me.pos.y() << "\n";
            ts << "        modifiers: " << me.modifiers << "\n";
            if (me.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }

        while (!keyevents.isEmpty() &&
               keyevents.first().msec == fe.msec) {
            KeyEvent ke = keyevents.takeFirst();

            ts << "    Key {\n";
            ts << "        type: " << ke.type << "\n";
            ts << "        key: " << ke.key << "\n";
            ts << "        modifiers: " << ke.modifiers << "\n";
            ts << "        text: \"" << ke.text.toUtf8().toHex() << "\"\n";
            ts << "        autorep: " << (ke.autorep?"true":"false") << "\n";
            ts << "        count: " << ke.count << "\n";
            if (ke.destination == ViewPort)
                ts << "        sendToViewport: true\n";
            ts << "    }\n";
        }
    }

    ts << "}\n";
    file.close();
}

void QmlGraphicsTester::updateCurrentTime(int msec)
{
    QmlGraphicsItemPrivate::setConsistentTime(msec);

    QImage img(m_view->width(), m_view->height(), QImage::Format_RGB32);

    if (options & QmlViewer::TestImages) {
        img.fill(qRgb(255,255,255));
        QPainter p(&img);
        m_view->render(&p);
    }

    FrameEvent fe;
    fe.msec = msec;
    if (msec == 0 || !(options & QmlViewer::TestImages)) {
        // Skip first frame, skip if not doing images
    } else if (0 == (m_savedFrameEvents.count() % 60)) {
        fe.image = img;
    } else {
        QCryptographicHash hash(QCryptographicHash::Md5);
        hash.addData((const char *)img.bits(), img.bytesPerLine() * img.height());
        fe.hash = hash.result();
    }
    m_savedFrameEvents.append(fe);

    // Deliver mouse events
    filterEvents = false;

    if (!testscript) {
        for (int ii = 0; ii < m_mouseEvents.count(); ++ii) {
            MouseEvent &me = m_mouseEvents[ii];
            me.msec = msec;
            QMouseEvent event(me.type, me.pos, me.button, me.buttons, me.modifiers);

            if (me.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }

        for (int ii = 0; ii < m_keyEvents.count(); ++ii) {
            KeyEvent &ke = m_keyEvents[ii];
            ke.msec = msec;
            QKeyEvent event(ke.type, ke.key, ke.modifiers, ke.text, ke.autorep, ke.count);

            if (ke.destination == View) {
                QCoreApplication::sendEvent(m_view, &event);
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
            }
        }
        m_savedMouseEvents.append(m_mouseEvents);
        m_savedKeyEvents.append(m_keyEvents);
    }

    m_mouseEvents.clear();
    m_keyEvents.clear();

    // Advance test script
    static int imgCount = 0;
    while (testscript && testscript->count() > testscriptidx) {

        QObject *event = testscript->event(testscriptidx);

        if (QmlGraphicsVisualTestFrame *frame = qobject_cast<QmlGraphicsVisualTestFrame *>(event)) {
            if (frame->msec() < msec) {
                if (options & QmlViewer::TestImages && !(options & QmlViewer::Record)) {
                    qWarning() << "QmlGraphicsTester: Extra frame.  Seen:"
                               << msec << "Expected:" << frame->msec();
                    imagefailure();
                }
            } else if (frame->msec() == msec) {
                if (!frame->hash().isEmpty() && frame->hash().toUtf8() != fe.hash.toHex()) {
                    if (options & QmlViewer::TestImages && !(options & QmlViewer::Record)) {
                        qWarning() << "QmlGraphicsTester: Mismatched frame hash.  Seen:"
                                   << fe.hash.toHex() << "Expected:"
                                   << frame->hash().toUtf8();
                        imagefailure();
                    }
                }
            } else if (frame->msec() > msec) {
                break;
            }

            if (options & QmlViewer::TestImages && !(options & QmlViewer::Record) && !frame->image().isEmpty()) {
                QImage goodImage(frame->image().toLocalFile());
                if (goodImage != img) {
                    QString reject(frame->image().toLocalFile() + ".reject.png");
                    qWarning() << "QmlGraphicsTester: Image mismatch.  Reject saved to:"
                               << reject;
                    img.save(reject);
                    imagefailure();
                }
            }
        } else if (QmlGraphicsVisualTestMouse *mouse = qobject_cast<QmlGraphicsVisualTestMouse *>(event)) {
            QPoint pos(mouse->x(), mouse->y());
            QPoint globalPos = m_view->mapToGlobal(QPoint(0, 0)) + pos;
            QMouseEvent event((QEvent::Type)mouse->type(), pos, globalPos, (Qt::MouseButton)mouse->button(), (Qt::MouseButtons)mouse->buttons(), (Qt::KeyboardModifiers)mouse->modifiers());

            MouseEvent me(&event);
            me.msec = msec;
            if (!mouse->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                me.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                me.destination = ViewPort;
            }
            m_savedMouseEvents.append(me);
        } else if (QmlGraphicsVisualTestKey *key = qobject_cast<QmlGraphicsVisualTestKey *>(event)) {

            QKeyEvent event((QEvent::Type)key->type(), key->key(), (Qt::KeyboardModifiers)key->modifiers(), QString::fromUtf8(QByteArray::fromHex(key->text().toUtf8())), key->autorep(), key->count());

            KeyEvent ke(&event);
            ke.msec = msec;
            if (!key->sendToViewport()) {
                QCoreApplication::sendEvent(m_view, &event);
                ke.destination = View;
            } else {
                QCoreApplication::sendEvent(m_view->viewport(), &event);
                ke.destination = ViewPort;
            }
            m_savedKeyEvents.append(ke);
        }
        testscriptidx++;
    }

    filterEvents = true;

    if (testscript && testscript->count() <= testscriptidx)
        complete();
}

QT_END_NAMESPACE
<|endoftext|>


<commit_before>#pragma once

#pragma warning(push)
#pragma warning(disable: 4302 4838)
#include <atlbase.h>
#include <atlfile.h>
#include <atlcoll.h>
#include <atlstr.h>
#include <atlwin.h>
#include <atlapp.h>
#include <atlctrls.h>
#include <atldlgs.h>
#pragma warning(pop)

#include "unzip.h"
#include "resource.h"
#include <shellapi.h>

static HINSTANCE hInstance;
static void ShowFailureDialog(const CString& mainInstruction, const CString& content = _T("")) {
	CTaskDialog td;
	td.SetMainInstructionText(mainInstruction);
	if (content.GetLength() != 0) td.SetContentText(content);
	td.SetWindowTitle(_T("Chocolatey Installer"));
	td.SetCommonButtons(TDCBF_CLOSE_BUTTON);
	td.SetMainIcon(TD_ERROR_ICON);
	td.ModifyFlags(0, TDF_ALLOW_DIALOG_CANCELLATION);
	td.DoModal(HWND_DESKTOP);
}

static bool ExtractZip(WORD zipResourceId, const CString& directory) {
	CResource zipResource;
	if (!zipResource.Load(_T("ZIPFILE"), zipResourceId)) return false;

	unsigned int dwSize = zipResource.GetSize();
	if (dwSize < 0x100) return false;

	HZIP zipFile = OpenZip(zipResource.Lock(), dwSize, NULL);
	SetUnzipBaseDir(zipFile, directory.GetString());

	ZRESULT zr; int index = 0; bool result = true;
	do {
		ZIPENTRY zipEntry;

		zr = GetZipItem(zipFile, index, &zipEntry);
		if (zr != ZR_MORE && zr != ZR_OK) break;

		CString targetFile = directory + "\\" + zipEntry.name;
		DeleteFile(targetFile.GetString());

		ZRESULT zr_inner = UnzipItem(zipFile, index, zipEntry.name);
		if (zr_inner != ZR_OK) {
			// Sometimes ZR_FLATE can be returned incorrectly if the file being
			// decompressed is of zero length. (UnzipItem() cannot differentiate
			// between a genuine zero-size read and an error condition.) Only fail
			// if the size decompressed size is not actually zero.
			if (zr_inner == ZR_FLATE && zipEntry.unc_size != 0) {
				result = false;
				break;
			}
		}

		index++;
	} while (zr == ZR_MORE || zr == ZR_OK);

	CloseZip(zipFile);
	zipResource.Release();

	return result;
}

#define REENTRANCY_MUTEX_NAME _T("Local\\Chocolatey Installer Reentrancy Mutex")

int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE /* hPrevInstance */, LPTSTR cmdLine, INT nCmdShow) {
	hInstance = hInst;

	HANDLE hMutex = ::OpenMutex(SYNCHRONIZE, false, REENTRANCY_MUTEX_NAME);
	bool alreadyExists = (hMutex == NULL && GetLastError() == ERROR_FILE_NOT_FOUND);

	if (!alreadyExists) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_REENTRANCY);
		CString content; content.LoadStringW(hInstance, IDS_REENTRANCY_DESC);
		ShowFailureDialog(mainInstruction, content);
		CloseHandle(hMutex);
		return 1;
	}

	hMutex = ::CreateMutex(nullptr, true, REENTRANCY_MUTEX_NAME);

	TCHAR targetDir[MAX_PATH];
	if (!::GetEnvironmentVariable(_T("TEMP"), targetDir, MAX_PATH)) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_NOTEMPDIR);
		CString content; content.LoadStringW(hInstance, IDS_NOTEMPDIR_DESC);
		ShowFailureDialog(mainInstruction, content);
		return 1;
	}

	_tcscat_s(targetDir, _T("\\ChocolateyInstaller"));
	if (!::CreateDirectory(targetDir, nullptr) && ::GetLastError() != ERROR_ALREADY_EXISTS) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_TEMPDIRWRITEFAIL);
		CString content; content.LoadStringW(hInstance, IDS_TEMPDIRWRITEFAIL_DESC);
		ShowFailureDialog(mainInstruction, content);
		return 1;
	}

	CString outputDir = targetDir;
	const WORD IDR_CHOCOLATEY_NUPKG = 1, IDR_WIZARD_ZIP = 2;
	if (!ExtractZip(IDR_CHOCOLATEY_NUPKG, outputDir + "\\choco_nupkg")) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_EXTRACTFAIL);
		ShowFailureDialog(mainInstruction, _T(""));
		return 1;
	}

	if (!ExtractZip(IDR_WIZARD_ZIP, outputDir)) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_EXTRACTFAIL);
		ShowFailureDialog(mainInstruction, _T(""));
		return 1;
	}

	CString cmd = outputDir + "\\ChocolateyInstaller.Wizard.exe";
	STARTUPINFO si; PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(STARTUPINFO);
	si.wShowWindow = SW_SHOW;
	si.dwFlags = STARTF_USESHOWWINDOW;

	if (!CreateProcess(cmd.GetString(), _T(""), nullptr, nullptr, false, 0, nullptr, outputDir.GetString(), &si, &pi)) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_EXECFAIL);
		ShowFailureDialog(mainInstruction, _T(""));
		return -1;
	}

	WaitForSingleObject(pi.hProcess, INFINITE);
	CloseHandle(hMutex);
	return 0;
}<commit_msg>Make usage of variable clearer<commit_after>#pragma once

#pragma warning(push)
#pragma warning(disable: 4302 4838)
#include <atlbase.h>
#include <atlfile.h>
#include <atlcoll.h>
#include <atlstr.h>
#include <atlwin.h>
#include <atlapp.h>
#include <atlctrls.h>
#include <atldlgs.h>
#pragma warning(pop)

#include "unzip.h"
#include "resource.h"
#include <shellapi.h>

static HINSTANCE hInstance;
static void ShowFailureDialog(const CString& mainInstruction, const CString& content = _T("")) {
	CTaskDialog td;
	td.SetMainInstructionText(mainInstruction);
	if (content.GetLength() != 0) td.SetContentText(content);
	td.SetWindowTitle(_T("Chocolatey Installer"));
	td.SetCommonButtons(TDCBF_CLOSE_BUTTON);
	td.SetMainIcon(TD_ERROR_ICON);
	td.ModifyFlags(0, TDF_ALLOW_DIALOG_CANCELLATION);
	td.DoModal(HWND_DESKTOP);
}

static bool ExtractZip(WORD zipResourceId, const CString& directory) {
	CResource zipResource;
	if (!zipResource.Load(_T("ZIPFILE"), zipResourceId)) return false;

	unsigned int dwSize = zipResource.GetSize();
	if (dwSize < 0x100) return false;

	HZIP zipFile = OpenZip(zipResource.Lock(), dwSize, NULL);
	SetUnzipBaseDir(zipFile, directory.GetString());

	ZRESULT zr; int index = 0; bool result = true;
	do {
		ZIPENTRY zipEntry;

		zr = GetZipItem(zipFile, index, &zipEntry);
		if (zr != ZR_MORE && zr != ZR_OK) break;

		CString targetFile = directory + "\\" + zipEntry.name;
		DeleteFile(targetFile.GetString());

		ZRESULT zr_inner = UnzipItem(zipFile, index, zipEntry.name);
		if (zr_inner != ZR_OK) {
			// Sometimes ZR_FLATE can be returned incorrectly if the file being
			// decompressed is of zero length. (UnzipItem() cannot differentiate
			// between a genuine zero-size read and an error condition.) Only fail
			// if the size decompressed size is not actually zero.
			if (zr_inner == ZR_FLATE && zipEntry.unc_size != 0) {
				result = false;
				break;
			}
		}

		index++;
	} while (zr == ZR_MORE || zr == ZR_OK);

	CloseZip(zipFile);
	zipResource.Release();

	return result;
}

#define REENTRANCY_MUTEX_NAME _T("Local\\Chocolatey Installer Reentrancy Mutex")

int WINAPI _tWinMain(HINSTANCE hInst, HINSTANCE /* hPrevInstance */, LPTSTR cmdLine, INT nCmdShow) {
	hInstance = hInst;

	HANDLE hMutex = ::OpenMutex(SYNCHRONIZE, false, REENTRANCY_MUTEX_NAME);
	bool alreadyExists = !(hMutex == NULL && GetLastError() == ERROR_FILE_NOT_FOUND);

	if (alreadyExists) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_REENTRANCY);
		CString content; content.LoadStringW(hInstance, IDS_REENTRANCY_DESC);
		ShowFailureDialog(mainInstruction, content);
		CloseHandle(hMutex);
		return 1;
	}

	hMutex = ::CreateMutex(nullptr, true, REENTRANCY_MUTEX_NAME);

	TCHAR targetDir[MAX_PATH];
	if (!::GetEnvironmentVariable(_T("TEMP"), targetDir, MAX_PATH)) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_NOTEMPDIR);
		CString content; content.LoadStringW(hInstance, IDS_NOTEMPDIR_DESC);
		ShowFailureDialog(mainInstruction, content);
		return 1;
	}

	_tcscat_s(targetDir, _T("\\ChocolateyInstaller"));
	if (!::CreateDirectory(targetDir, nullptr) && ::GetLastError() != ERROR_ALREADY_EXISTS) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_TEMPDIRWRITEFAIL);
		CString content; content.LoadStringW(hInstance, IDS_TEMPDIRWRITEFAIL_DESC);
		ShowFailureDialog(mainInstruction, content);
		return 1;
	}

	CString outputDir = targetDir;
	const WORD IDR_CHOCOLATEY_NUPKG = 1, IDR_WIZARD_ZIP = 2;
	if (!ExtractZip(IDR_CHOCOLATEY_NUPKG, outputDir + "\\choco_nupkg")) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_EXTRACTFAIL);
		ShowFailureDialog(mainInstruction, _T(""));
		return 1;
	}

	if (!ExtractZip(IDR_WIZARD_ZIP, outputDir)) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_EXTRACTFAIL);
		ShowFailureDialog(mainInstruction, _T(""));
		return 1;
	}

	CString cmd = outputDir + "\\ChocolateyInstaller.Wizard.exe";
	STARTUPINFO si; PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(STARTUPINFO);
	si.wShowWindow = SW_SHOW;
	si.dwFlags = STARTF_USESHOWWINDOW;

	if (!CreateProcess(cmd.GetString(), _T(""), nullptr, nullptr, false, 0, nullptr, outputDir.GetString(), &si, &pi)) {
		CString mainInstruction; mainInstruction.LoadStringW(hInstance, IDS_EXECFAIL);
		ShowFailureDialog(mainInstruction, _T(""));
		return -1;
	}

	WaitForSingleObject(pi.hProcess, INFINITE);
	CloseHandle(hMutex);
	return 0;
}<|endoftext|>