<commit_before>//===--- libSwiftSyntaxParser.cpp - C API for Swift Syntax Parsing --------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This C API is primarily intended to serve as the Swift parsing component
// of SwiftSyntax (https://github.com/apple/swift-syntax).
//
//===----------------------------------------------------------------------===//

#include "swift-c/SyntaxParser/SwiftSyntaxParser.h"
#include "swift/AST/Module.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Parse/Parser.h"
#include "swift/Parse/SyntaxParseActions.h"
#include "swift/Syntax/Serialization/SyntaxSerialization.h"
#include "swift/Syntax/SyntaxNodes.h"
#include "swift/Subsystems.h"
#include <Block.h>

using namespace swift;
using namespace swift::syntax;
using namespace swift::byteTree;

typedef swiftparse_range_t CRange;
typedef swiftparse_client_node_t CClientNode;
typedef swiftparse_syntax_node_t CRawSyntaxNode;
typedef swiftparse_trivia_piece_t CTriviaPiece;
typedef swiftparse_syntax_kind_t CSyntaxKind;

namespace {

static unsigned getByteOffset(SourceLoc Loc, SourceManager &SM,
                              unsigned BufferID) {
  return Loc.isValid() ? SM.getLocOffsetInBuffer(Loc, BufferID) : 0;
}

static void initCRange(CRange &c_range, CharSourceRange range, SourceManager &SM,
                       unsigned BufferID) {
  if (range.isValid()) {
    c_range.offset = getByteOffset(range.getStart(), SM, BufferID);
    c_range.length = range.getByteLength();
  } else {
    c_range.offset = 0;
    c_range.length = 0;
  }
}

class SynParser {
  swiftparse_node_handler_t NodeHandler = nullptr;
  swiftparse_node_lookup_t NodeLookup = nullptr;
  swiftparse_diagnostic_handler_t DiagHandler = nullptr;

public:
  swiftparse_node_handler_t getNodeHandler() const {
    return NodeHandler;
  }

  swiftparse_node_lookup_t getNodeLookup() const {
    return NodeLookup;
  }

  swiftparse_diagnostic_handler_t getDiagnosticHandler() const {
    return DiagHandler;
  }

  void setNodeHandler(swiftparse_node_handler_t hdl) {
    auto prevBlk = NodeHandler;
    NodeHandler = Block_copy(hdl);
    Block_release(prevBlk);
  }

  void setNodeLookup(swiftparse_node_lookup_t lookupBlk) {
    auto prevBlk = NodeLookup;
    NodeLookup = Block_copy(lookupBlk);
    Block_release(prevBlk);
  }

  void setDiagnosticHandler(swiftparse_diagnostic_handler_t hdl) {
    auto prevBlk = DiagHandler;
    DiagHandler = Block_copy(hdl);
    Block_release(prevBlk);
  }

  ~SynParser() {
    setNodeHandler(nullptr);
    setNodeLookup(nullptr);
    setDiagnosticHandler(nullptr);
  }

  swiftparse_client_node_t parse(const char *source);
};

class CLibParseActions : public SyntaxParseActions {
  SynParser &SynParse;
  SourceManager &SM;
  unsigned BufferID;

public:
  CLibParseActions(SynParser &synParse, SourceManager &sm, unsigned bufID)
  : SynParse(synParse), SM(sm), BufferID(bufID) {}

private:
  swiftparse_node_handler_t getNodeHandler() const {
    return SynParse.getNodeHandler();
  }

  swiftparse_node_lookup_t getNodeLookup() const {
    return SynParse.getNodeLookup();
  }

  static void makeCTrivia(SmallVectorImpl<CTriviaPiece> &c_trivia,
                          ArrayRef<ParsedTriviaPiece> trivia) {
    for (const auto &piece : trivia) {
      CTriviaPiece c_piece;
      auto numValue =
        WrapperTypeTraits<TriviaKind>::numericValue(piece.getKind());
      c_piece.kind = numValue;
      assert(c_piece.kind == numValue && "trivia kind value is too large");
      c_piece.length = piece.getLength();
      c_trivia.push_back(c_piece);
    }
  }

  void makeCRange(CRange &c_range, CharSourceRange range) {
    return initCRange(c_range, range, SM, BufferID);
  }

  void makeCRawToken(CRawSyntaxNode &node,
                     tok kind,
                     ArrayRef<CTriviaPiece> leadingTrivia,
                     ArrayRef<CTriviaPiece> trailingTrivia,
                     CharSourceRange range) {
    node.kind = WrapperTypeTraits<SyntaxKind>::numericValue(SyntaxKind::Token);
    auto numValue = WrapperTypeTraits<swift::tok>::numericValue(kind);
    node.token_data.kind = numValue;
    assert(node.token_data.kind == numValue && "token kind value is too large");
    node.token_data.leading_trivia = leadingTrivia.data();
    node.token_data.leading_trivia_count = leadingTrivia.size();
    assert(node.token_data.leading_trivia_count == leadingTrivia.size() &&
           "leading trivia count value is too large");
    node.token_data.trailing_trivia = trailingTrivia.data();
    node.token_data.trailing_trivia_count = trailingTrivia.size();
    assert(node.token_data.trailing_trivia_count == trailingTrivia.size() &&
           "trailing trivia count value is too large");
    makeCRange(node.range, range);
    node.present = true;
  }

  OpaqueSyntaxNode recordToken(tok tokenKind,
                               ArrayRef<ParsedTriviaPiece> leadingTrivia,
                               ArrayRef<ParsedTriviaPiece> trailingTrivia,
                               CharSourceRange range) override {
    SmallVector<CTriviaPiece, 8> c_leadingTrivia, c_trailingTrivia;
    makeCTrivia(c_leadingTrivia, leadingTrivia);
    makeCTrivia(c_trailingTrivia, trailingTrivia);
    CRawSyntaxNode node;
    makeCRawToken(node, tokenKind, c_leadingTrivia, c_trailingTrivia,
                  range);
    return getNodeHandler()(&node);
  }

  OpaqueSyntaxNode recordMissingToken(tok tokenKind, SourceLoc loc) override {
    CRawSyntaxNode node;
    makeCRawToken(node, tokenKind, {}, {}, CharSourceRange{loc, 0});
    node.present = false;
    return getNodeHandler()(&node);
  }

  OpaqueSyntaxNode recordRawSyntax(SyntaxKind kind,
                                   ArrayRef<OpaqueSyntaxNode> elements,
                                   CharSourceRange range) override {
    CRawSyntaxNode node;
    auto numValue = WrapperTypeTraits<SyntaxKind>::numericValue(kind);
    node.kind = numValue;
    assert(node.kind == numValue && "syntax kind value is too large");
    node.layout_data.nodes = elements.data();
    node.layout_data.nodes_count = elements.size();
    makeCRange(node.range, range);
    node.present = true;
    return getNodeHandler()(&node);
  }

  std::pair<size_t, OpaqueSyntaxNode>
  lookupNode(size_t lexerOffset, SyntaxKind kind) override {
    auto NodeLookup = getNodeLookup();
    if (!NodeLookup) {
      return {0, nullptr};
    }
    auto numValue = WrapperTypeTraits<SyntaxKind>::numericValue(kind);
    CSyntaxKind ckind = numValue;
    assert(ckind == numValue && "syntax kind value is too large");
    auto result = NodeLookup(lexerOffset, ckind);
    return {result.length, result.node};
  }
};

static swiftparser_diagnostic_severity_t getSeverity(DiagnosticKind Kind) {
  switch (Kind) {
  case swift::DiagnosticKind::Error:
    return SWIFTPARSER_DIAGNOSTIC_SEVERITY_ERROR;
  case swift::DiagnosticKind::Warning:
    return SWIFTPARSER_DIAGNOSTIC_SEVERITY_WARNING;
  case swift::DiagnosticKind::Note:
    return SWIFTPARSER_DIAGNOSTIC_SEVERITY_NOTE;
  default:
    llvm_unreachable("unrecognized diagnostic kind.");
  }
}

struct DiagnosticDetail {
  const char* Message;
  unsigned Offset;
  std::vector<CRange> CRanges;
  swiftparser_diagnostic_severity_t Severity;
  std::vector<swiftparse_diagnostic_fixit_t> AllFixits;
};

struct SynParserDiagConsumer: public DiagnosticConsumer {
  SynParser &Parser;
  const unsigned BufferID;
  SynParserDiagConsumer(SynParser &Parser, unsigned BufferID):
    Parser(Parser), BufferID(BufferID) {}
  void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
                        DiagnosticKind Kind,
                        StringRef FormatString,
                        ArrayRef<DiagnosticArgument> FormatArgs,
                        const DiagnosticInfo &Info) override {
    assert(Kind != DiagnosticKind::Remark && "Shouldn't see this in parser.");
    // The buffer where all char* will point into.
    llvm::SmallString<256> Buffer;
    auto getCurrentText = [&]() -> const char* {
      return Buffer.data() + Buffer.size();
    };
    DiagnosticDetail Result;
    Result.Severity = getSeverity(Kind);
    Result.Offset = getByteOffset(Loc, SM, BufferID);

    // Terminate each printed text with 0 so the client-side can use char* directly.
    char NullTerm = '\0';
    {
      // Print the error message to buffer and record it.
      llvm::raw_svector_ostream OS(Buffer);
      Result.Message = getCurrentText();
      DiagnosticEngine::formatDiagnosticText(OS, FormatString, FormatArgs);
      OS << NullTerm;
    }
    for (auto R: Info.Ranges) {
      Result.CRanges.emplace_back();
      initCRange(Result.CRanges.back(), R, SM, BufferID);
    }
    for (auto Fixit: Info.FixIts) {
      Result.AllFixits.push_back({CRange(), getCurrentText()});
      initCRange(Result.AllFixits.back().range, Fixit.getRange(), SM, BufferID);
      llvm::raw_svector_ostream OS(Buffer);
      OS << Fixit.getText() << NullTerm;
    }
    Parser.getDiagnosticHandler()(static_cast<void*>(&Result));
  }
};

swiftparse_client_node_t SynParser::parse(const char *source) {
  SourceManager SM;
  unsigned bufID = SM.addNewSourceBuffer(
    llvm::MemoryBuffer::getMemBuffer(source, "syntax_parse_source"));
  LangOptions langOpts;
  langOpts.BuildSyntaxTree = true;
  langOpts.CollectParsedToken = false;
  // Disable name lookups during parsing.
  langOpts.EnableASTScopeLookup = true;

  auto parseActions =
    std::make_shared<CLibParseActions>(*this, SM, bufID);
  // We have to use SourceFileKind::Main to avoid diagnostics like
  // illegal_top_level_expr
  ParserUnit PU(SM, SourceFileKind::Main, bufID, langOpts,
                "syntax_parse_module", std::move(parseActions),
                /*SyntaxCache=*/nullptr);
  std::unique_ptr<SynParserDiagConsumer> pConsumer;
  if (DiagHandler) {
    pConsumer = llvm::make_unique<SynParserDiagConsumer>(*this, bufID);
    PU.getDiagnosticEngine().addConsumer(*pConsumer);
  }
  return PU.parse();
}
}
//===--- C API ------------------------------------------------------------===//

swiftparse_parser_t
swiftparse_parser_create(void) {
  return new SynParser();
}

void
swiftparse_parser_dispose(swiftparse_parser_t c_parser) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  delete parser;
}

void
swiftparse_parser_set_node_handler(swiftparse_parser_t c_parser,
                                   swiftparse_node_handler_t hdl) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  parser->setNodeHandler(hdl);
}

void
swiftparse_parser_set_node_lookup(swiftparse_parser_t c_parser,
                                  swiftparse_node_lookup_t lookup) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  parser->setNodeLookup(lookup);
}

swiftparse_client_node_t
swiftparse_parse_string(swiftparse_parser_t c_parser, const char *source) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  return parser->parse(source);
}

const char* swiftparse_syntax_structure_versioning_identifier(void) {
  return getSyntaxStructureVersioningIdentifier();
}

//===--------------------- C API for diagnostics -------------------------====//

void
swiftparse_parser_set_diagnostic_handler(swiftparse_parser_t c_parser,
                                         swiftparse_diagnostic_handler_t hdl) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  parser->setDiagnosticHandler(hdl);
}

const char* swiftparse_diagnostic_get_message(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->Message;
}

unsigned swiftparse_diagnostic_get_fixit_count(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->AllFixits.size();
}

swiftparse_diagnostic_fixit_t
swiftparse_diagnostic_get_fixit(swiftparser_diagnostic_t diag, unsigned idx) {
  auto allFixits = static_cast<const DiagnosticDetail*>(diag)->AllFixits;
  assert(idx < allFixits.size());
  return allFixits[idx];
}

unsigned swiftparse_diagnostic_get_range_count(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->CRanges.size();
}

swiftparse_range_t
swiftparse_diagnostic_get_range(swiftparser_diagnostic_t diag, unsigned idx) {
  return static_cast<const DiagnosticDetail*>(diag)->CRanges[idx];
}

swiftparser_diagnostic_severity_t
swiftparse_diagnostic_get_severity(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->Severity;
}

unsigned swiftparse_diagnostic_get_source_loc(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->Offset;
}
<commit_msg>SyntaxParser: avoid evaluating pound conditions in the new in-process parser.<commit_after>//===--- libSwiftSyntaxParser.cpp - C API for Swift Syntax Parsing --------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This C API is primarily intended to serve as the Swift parsing component
// of SwiftSyntax (https://github.com/apple/swift-syntax).
//
//===----------------------------------------------------------------------===//

#include "swift-c/SyntaxParser/SwiftSyntaxParser.h"
#include "swift/AST/Module.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Parse/Parser.h"
#include "swift/Parse/SyntaxParseActions.h"
#include "swift/Syntax/Serialization/SyntaxSerialization.h"
#include "swift/Syntax/SyntaxNodes.h"
#include "swift/Subsystems.h"
#include <Block.h>

using namespace swift;
using namespace swift::syntax;
using namespace swift::byteTree;

typedef swiftparse_range_t CRange;
typedef swiftparse_client_node_t CClientNode;
typedef swiftparse_syntax_node_t CRawSyntaxNode;
typedef swiftparse_trivia_piece_t CTriviaPiece;
typedef swiftparse_syntax_kind_t CSyntaxKind;

namespace {

static unsigned getByteOffset(SourceLoc Loc, SourceManager &SM,
                              unsigned BufferID) {
  return Loc.isValid() ? SM.getLocOffsetInBuffer(Loc, BufferID) : 0;
}

static void initCRange(CRange &c_range, CharSourceRange range, SourceManager &SM,
                       unsigned BufferID) {
  if (range.isValid()) {
    c_range.offset = getByteOffset(range.getStart(), SM, BufferID);
    c_range.length = range.getByteLength();
  } else {
    c_range.offset = 0;
    c_range.length = 0;
  }
}

class SynParser {
  swiftparse_node_handler_t NodeHandler = nullptr;
  swiftparse_node_lookup_t NodeLookup = nullptr;
  swiftparse_diagnostic_handler_t DiagHandler = nullptr;

public:
  swiftparse_node_handler_t getNodeHandler() const {
    return NodeHandler;
  }

  swiftparse_node_lookup_t getNodeLookup() const {
    return NodeLookup;
  }

  swiftparse_diagnostic_handler_t getDiagnosticHandler() const {
    return DiagHandler;
  }

  void setNodeHandler(swiftparse_node_handler_t hdl) {
    auto prevBlk = NodeHandler;
    NodeHandler = Block_copy(hdl);
    Block_release(prevBlk);
  }

  void setNodeLookup(swiftparse_node_lookup_t lookupBlk) {
    auto prevBlk = NodeLookup;
    NodeLookup = Block_copy(lookupBlk);
    Block_release(prevBlk);
  }

  void setDiagnosticHandler(swiftparse_diagnostic_handler_t hdl) {
    auto prevBlk = DiagHandler;
    DiagHandler = Block_copy(hdl);
    Block_release(prevBlk);
  }

  ~SynParser() {
    setNodeHandler(nullptr);
    setNodeLookup(nullptr);
    setDiagnosticHandler(nullptr);
  }

  swiftparse_client_node_t parse(const char *source);
};

class CLibParseActions : public SyntaxParseActions {
  SynParser &SynParse;
  SourceManager &SM;
  unsigned BufferID;

public:
  CLibParseActions(SynParser &synParse, SourceManager &sm, unsigned bufID)
  : SynParse(synParse), SM(sm), BufferID(bufID) {}

private:
  swiftparse_node_handler_t getNodeHandler() const {
    return SynParse.getNodeHandler();
  }

  swiftparse_node_lookup_t getNodeLookup() const {
    return SynParse.getNodeLookup();
  }

  static void makeCTrivia(SmallVectorImpl<CTriviaPiece> &c_trivia,
                          ArrayRef<ParsedTriviaPiece> trivia) {
    for (const auto &piece : trivia) {
      CTriviaPiece c_piece;
      auto numValue =
        WrapperTypeTraits<TriviaKind>::numericValue(piece.getKind());
      c_piece.kind = numValue;
      assert(c_piece.kind == numValue && "trivia kind value is too large");
      c_piece.length = piece.getLength();
      c_trivia.push_back(c_piece);
    }
  }

  void makeCRange(CRange &c_range, CharSourceRange range) {
    return initCRange(c_range, range, SM, BufferID);
  }

  void makeCRawToken(CRawSyntaxNode &node,
                     tok kind,
                     ArrayRef<CTriviaPiece> leadingTrivia,
                     ArrayRef<CTriviaPiece> trailingTrivia,
                     CharSourceRange range) {
    node.kind = WrapperTypeTraits<SyntaxKind>::numericValue(SyntaxKind::Token);
    auto numValue = WrapperTypeTraits<swift::tok>::numericValue(kind);
    node.token_data.kind = numValue;
    assert(node.token_data.kind == numValue && "token kind value is too large");
    node.token_data.leading_trivia = leadingTrivia.data();
    node.token_data.leading_trivia_count = leadingTrivia.size();
    assert(node.token_data.leading_trivia_count == leadingTrivia.size() &&
           "leading trivia count value is too large");
    node.token_data.trailing_trivia = trailingTrivia.data();
    node.token_data.trailing_trivia_count = trailingTrivia.size();
    assert(node.token_data.trailing_trivia_count == trailingTrivia.size() &&
           "trailing trivia count value is too large");
    makeCRange(node.range, range);
    node.present = true;
  }

  OpaqueSyntaxNode recordToken(tok tokenKind,
                               ArrayRef<ParsedTriviaPiece> leadingTrivia,
                               ArrayRef<ParsedTriviaPiece> trailingTrivia,
                               CharSourceRange range) override {
    SmallVector<CTriviaPiece, 8> c_leadingTrivia, c_trailingTrivia;
    makeCTrivia(c_leadingTrivia, leadingTrivia);
    makeCTrivia(c_trailingTrivia, trailingTrivia);
    CRawSyntaxNode node;
    makeCRawToken(node, tokenKind, c_leadingTrivia, c_trailingTrivia,
                  range);
    return getNodeHandler()(&node);
  }

  OpaqueSyntaxNode recordMissingToken(tok tokenKind, SourceLoc loc) override {
    CRawSyntaxNode node;
    makeCRawToken(node, tokenKind, {}, {}, CharSourceRange{loc, 0});
    node.present = false;
    return getNodeHandler()(&node);
  }

  OpaqueSyntaxNode recordRawSyntax(SyntaxKind kind,
                                   ArrayRef<OpaqueSyntaxNode> elements,
                                   CharSourceRange range) override {
    CRawSyntaxNode node;
    auto numValue = WrapperTypeTraits<SyntaxKind>::numericValue(kind);
    node.kind = numValue;
    assert(node.kind == numValue && "syntax kind value is too large");
    node.layout_data.nodes = elements.data();
    node.layout_data.nodes_count = elements.size();
    makeCRange(node.range, range);
    node.present = true;
    return getNodeHandler()(&node);
  }

  std::pair<size_t, OpaqueSyntaxNode>
  lookupNode(size_t lexerOffset, SyntaxKind kind) override {
    auto NodeLookup = getNodeLookup();
    if (!NodeLookup) {
      return {0, nullptr};
    }
    auto numValue = WrapperTypeTraits<SyntaxKind>::numericValue(kind);
    CSyntaxKind ckind = numValue;
    assert(ckind == numValue && "syntax kind value is too large");
    auto result = NodeLookup(lexerOffset, ckind);
    return {result.length, result.node};
  }
};

static swiftparser_diagnostic_severity_t getSeverity(DiagnosticKind Kind) {
  switch (Kind) {
  case swift::DiagnosticKind::Error:
    return SWIFTPARSER_DIAGNOSTIC_SEVERITY_ERROR;
  case swift::DiagnosticKind::Warning:
    return SWIFTPARSER_DIAGNOSTIC_SEVERITY_WARNING;
  case swift::DiagnosticKind::Note:
    return SWIFTPARSER_DIAGNOSTIC_SEVERITY_NOTE;
  default:
    llvm_unreachable("unrecognized diagnostic kind.");
  }
}

struct DiagnosticDetail {
  const char* Message;
  unsigned Offset;
  std::vector<CRange> CRanges;
  swiftparser_diagnostic_severity_t Severity;
  std::vector<swiftparse_diagnostic_fixit_t> AllFixits;
};

struct SynParserDiagConsumer: public DiagnosticConsumer {
  SynParser &Parser;
  const unsigned BufferID;
  SynParserDiagConsumer(SynParser &Parser, unsigned BufferID):
    Parser(Parser), BufferID(BufferID) {}
  void handleDiagnostic(SourceManager &SM, SourceLoc Loc,
                        DiagnosticKind Kind,
                        StringRef FormatString,
                        ArrayRef<DiagnosticArgument> FormatArgs,
                        const DiagnosticInfo &Info) override {
    assert(Kind != DiagnosticKind::Remark && "Shouldn't see this in parser.");
    // The buffer where all char* will point into.
    llvm::SmallString<256> Buffer;
    auto getCurrentText = [&]() -> const char* {
      return Buffer.data() + Buffer.size();
    };
    DiagnosticDetail Result;
    Result.Severity = getSeverity(Kind);
    Result.Offset = getByteOffset(Loc, SM, BufferID);

    // Terminate each printed text with 0 so the client-side can use char* directly.
    char NullTerm = '\0';
    {
      // Print the error message to buffer and record it.
      llvm::raw_svector_ostream OS(Buffer);
      Result.Message = getCurrentText();
      DiagnosticEngine::formatDiagnosticText(OS, FormatString, FormatArgs);
      OS << NullTerm;
    }
    for (auto R: Info.Ranges) {
      Result.CRanges.emplace_back();
      initCRange(Result.CRanges.back(), R, SM, BufferID);
    }
    for (auto Fixit: Info.FixIts) {
      Result.AllFixits.push_back({CRange(), getCurrentText()});
      initCRange(Result.AllFixits.back().range, Fixit.getRange(), SM, BufferID);
      llvm::raw_svector_ostream OS(Buffer);
      OS << Fixit.getText() << NullTerm;
    }
    Parser.getDiagnosticHandler()(static_cast<void*>(&Result));
  }
};

swiftparse_client_node_t SynParser::parse(const char *source) {
  SourceManager SM;
  unsigned bufID = SM.addNewSourceBuffer(
    llvm::MemoryBuffer::getMemBuffer(source, "syntax_parse_source"));
  LangOptions langOpts;
  langOpts.BuildSyntaxTree = true;
  langOpts.CollectParsedToken = false;
  // Disable name lookups during parsing.
  langOpts.EnableASTScopeLookup = true;

  auto parseActions =
    std::make_shared<CLibParseActions>(*this, SM, bufID);
  // We have to use SourceFileKind::Main to avoid diagnostics like
  // illegal_top_level_expr
  ParserUnit PU(SM, SourceFileKind::Main, bufID, langOpts,
                "syntax_parse_module", std::move(parseActions),
                /*SyntaxCache=*/nullptr);
  // Evaluating pound conditions may lead to unknown syntax.
  PU.getParser().State->PerformConditionEvaluation = false;
  std::unique_ptr<SynParserDiagConsumer> pConsumer;
  if (DiagHandler) {
    pConsumer = llvm::make_unique<SynParserDiagConsumer>(*this, bufID);
    PU.getDiagnosticEngine().addConsumer(*pConsumer);
  }
  return PU.parse();
}
}
//===--- C API ------------------------------------------------------------===//

swiftparse_parser_t
swiftparse_parser_create(void) {
  return new SynParser();
}

void
swiftparse_parser_dispose(swiftparse_parser_t c_parser) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  delete parser;
}

void
swiftparse_parser_set_node_handler(swiftparse_parser_t c_parser,
                                   swiftparse_node_handler_t hdl) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  parser->setNodeHandler(hdl);
}

void
swiftparse_parser_set_node_lookup(swiftparse_parser_t c_parser,
                                  swiftparse_node_lookup_t lookup) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  parser->setNodeLookup(lookup);
}

swiftparse_client_node_t
swiftparse_parse_string(swiftparse_parser_t c_parser, const char *source) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  return parser->parse(source);
}

const char* swiftparse_syntax_structure_versioning_identifier(void) {
  return getSyntaxStructureVersioningIdentifier();
}

//===--------------------- C API for diagnostics -------------------------====//

void
swiftparse_parser_set_diagnostic_handler(swiftparse_parser_t c_parser,
                                         swiftparse_diagnostic_handler_t hdl) {
  SynParser *parser = static_cast<SynParser*>(c_parser);
  parser->setDiagnosticHandler(hdl);
}

const char* swiftparse_diagnostic_get_message(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->Message;
}

unsigned swiftparse_diagnostic_get_fixit_count(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->AllFixits.size();
}

swiftparse_diagnostic_fixit_t
swiftparse_diagnostic_get_fixit(swiftparser_diagnostic_t diag, unsigned idx) {
  auto allFixits = static_cast<const DiagnosticDetail*>(diag)->AllFixits;
  assert(idx < allFixits.size());
  return allFixits[idx];
}

unsigned swiftparse_diagnostic_get_range_count(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->CRanges.size();
}

swiftparse_range_t
swiftparse_diagnostic_get_range(swiftparser_diagnostic_t diag, unsigned idx) {
  return static_cast<const DiagnosticDetail*>(diag)->CRanges[idx];
}

swiftparser_diagnostic_severity_t
swiftparse_diagnostic_get_severity(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->Severity;
}

unsigned swiftparse_diagnostic_get_source_loc(swiftparser_diagnostic_t diag) {
  return static_cast<const DiagnosticDetail*>(diag)->Offset;
}
<|endoftext|>


<commit_before>#include "ROOT/TDataFrame.hxx"
#include "ROOT/TSeq.hxx"
#include "TTree.h"
#include "gtest/gtest.h"

using namespace ROOT::Experimental;
using namespace ROOT::Experimental::VecOps;

struct BrVal {
   Float_t a;
   Int_t i;
};

TEST(TDFLeaves, ReadIndividualLeaves)
{
   // Taken directly from ROOT-9142
   const auto nEntries = 8;
   TTree t("t", "t");
   BrVal val;
   t.Branch("b", &val, "a/F:i/I");
   for (auto i : ROOT::TSeqI(nEntries)) {
      val.a = val.i = i;
      t.Fill();
   }

   auto res = 0;
   auto histEntries = 0;
   try {
      ROOT::Experimental::TDataFrame df(t);
      df.Histo1D("b.a")->Draw();
      auto h = df.Define("aa", [](Float_t bv) { return bv; }, {"b.a"}).Histo1D("aa");
      histEntries = h->GetEntries();
   } catch (std::runtime_error &e) {
      std::cout << e.what() << '\n';
      res = 1;
   }
   EXPECT_EQ(0, res);
   EXPECT_EQ(nEntries, histEntries);
}
<commit_msg>[TDF] Test that accessing "a.b" and "a_b" does not clash<commit_after>#include "ROOT/TDataFrame.hxx"
#include "ROOT/TSeq.hxx"
#include "TTree.h"
#include "gtest/gtest.h"

using namespace ROOT::Experimental;
using namespace ROOT::Experimental::VecOps;

struct BrVal {
   Float_t a;
   Int_t i;
};

TEST(TDFLeaves, ReadIndividualLeaves)
{
   // Taken directly from ROOT-9142
   const auto nEntries = 8;
   TTree t("t", "t");
   BrVal val;
   t.Branch("b", &val, "a/F:i/I");
   for (auto i : ROOT::TSeqI(nEntries)) {
      val.a = val.i = i;
      t.Fill();
   }

   auto res = 0;
   auto histEntries = 0;
   try {
      ROOT::Experimental::TDataFrame df(t);
      df.Histo1D<float>("b.a")->Draw();
      auto h = df.Define("aa", [](Float_t bv) { return bv; }, {"b.a"}).Histo1D<float>("aa");
      histEntries = h->GetEntries();
   } catch (std::runtime_error &e) {
      std::cout << e.what() << '\n';
      res = 1;
   }
   EXPECT_EQ(0, res);
   EXPECT_EQ(nEntries, histEntries);
}

struct S {
  int a, b;
};

TEST(TDFLeaves, LeavesWithDotNameClas)
{
   TTree t("t", "t");
   S s{40, 41};
   t.Branch("s", &s, "a/I:b/I");
   int s_a = 2;
   t.Branch("s_a", &s_a);
   t.Fill();
   t.ResetBranchAddresses();
   TDataFrame d(t);
   auto four = d.Define("res1", "s_a + s_a").Min<int>("res1");
   auto ans = d.Define("res2", "s.a + s_a").Min<int>("res2");

   EXPECT_EQ(*four, 4);
   EXPECT_EQ(*ans, 42);
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <atomic>
#include <chrono>
#include <random>
#include <string>
#include <thread>
#include <utility>

#include <TFile.h>
#include <TTree.h>
#include <TSystem.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>
#include <ROOT/TTreeProcessorMT.hxx>

#include "gtest/gtest.h"

void WriteFiles(const std::string &treename, const std::vector<std::string> &filenames)
{
   int v = 0;
   for (const auto &f : filenames) {
      TFile file(f.c_str(), "recreate");
      TTree t(treename.c_str(), treename.c_str());
      t.Branch("v", &v);
      for (auto i = 0; i < 10; ++i) {
         ++v;
         t.Fill();
      }
      t.Write();
   }
}

void WriteFileManyClusters(unsigned int nevents, const char *treename, const char *filename)
{
   int v = 0;
   TFile file(filename, "recreate");
   TTree t(treename, treename);
   t.Branch("v", &v);
   // t.SetAutoFlush(1);
   for (auto i = 0U; i < nevents; ++i) {
      t.Fill();
      t.FlushBaskets();
   }
   t.Write();
   file.Close();
}

void DeleteFiles(const std::vector<std::string> &filenames)
{
   for (const auto &f : filenames)
      gSystem->Unlink(f.c_str());
}

TEST(TreeProcessorMT, EmptyTChain)
{
   TChain c("mytree");
   auto exceptionFired(false);
   try {
      ROOT::TTreeProcessorMT proc(c);
   } catch (...) {
      exceptionFired = true;
   }
   EXPECT_TRUE(exceptionFired);
}

TEST(TreeProcessorMT, ManyFiles)
{
   const auto nFiles = 100u;
   const std::string treename = "t";
   std::vector<std::string> filenames;
   for (auto i = 0u; i < nFiles; ++i)
      filenames.emplace_back("treeprocmt_" + std::to_string(i) + ".root");

   WriteFiles(treename, filenames);

   std::atomic_int sum(0);
   std::atomic_int count(0);
   auto sumValues = [&sum, &count](TTreeReader &r) {
      TTreeReaderValue<int> v(r, "v");
      std::random_device seed;
      std::default_random_engine eng(seed());
      std::uniform_int_distribution<> rand(1, 100);
      while (r.Next()) {
         std::this_thread::sleep_for(std::chrono::nanoseconds(rand(eng)));
         sum += *v;
         ++count;
      }
   };

   // TTreeProcMT requires a vector<string_view>
   std::vector<std::string_view> fnames;
   for (const auto &f : filenames)
      fnames.emplace_back(f);

   ROOT::TTreeProcessorMT proc(fnames, treename);
   proc.Process(sumValues);

   EXPECT_EQ(count.load(), int(nFiles * 10)); // 10 entries per file
   EXPECT_EQ(sum.load(), 500500);             // sum 1..nFiles*10

   DeleteFiles(filenames);
}

TEST(TreeProcessorMT, TreeInSubDirectory)
{
   auto filename = "fileTreeInSubDirectory.root";
   auto procLambda = [](TTreeReader &r) {
      while (r.Next())
         ;
   };

   {
      TFile f(filename, "RECREATE");
      auto dir0 = f.mkdir("dir0");
      dir0->cd();
      auto dir1 = dir0->mkdir("dir1");
      dir1->cd();
      TTree t("tree", "tree");
      t.Write();
   }

   ROOT::EnableThreadSafety();

   auto fullPath = "dir0/dir1/tree";

   // With a TTree
   TFile f(filename);
   auto t = (TTree *)f.Get(fullPath);
   ROOT::TTreeProcessorMT tp(*t);
   tp.Process(procLambda);

   // With a TChain
   std::string chainElementName = filename;
   chainElementName += "/";
   chainElementName += fullPath;
   TChain chain;
   chain.Add(chainElementName.c_str());
   ROOT::TTreeProcessorMT tpc(chain);
   tpc.Process(procLambda);

   gSystem->Unlink(filename);
}

TEST(TreeProcessorMT, LimitNTasks_CheckEntries)
{
   const auto nEvents = 991;
   const auto filename = "TreeProcessorMT_LimitNTasks_CheckEntries.root";
   const auto treename = "t";
   WriteFileManyClusters(nEvents, treename, filename);
   auto nTasks = 0U;
   std::map<unsigned int, unsigned int> nEntriesCountsMap;
   std::mutex theMutex;
   auto f = [&](TTreeReader &t) {
      auto nentries = 0U;
      while (t.Next())
         nentries++;
      std::lock_guard<std::mutex> lg(theMutex);
      nTasks++;
      if (!nEntriesCountsMap.insert({nentries, 1U}).second) {
         nEntriesCountsMap[nentries]++;
      }
   };

   ROOT::DisableImplicitMT();
   ROOT::EnableImplicitMT(4);

   ROOT::TTreeProcessorMT p(filename, treename);
   p.Process(f);

   EXPECT_EQ(nTasks, 96U) << "Wrong number of tasks generated!\n";
   EXPECT_EQ(nEntriesCountsMap[10], 65U) << "Wrong number of tasks with 10 clusters each!\n";
   EXPECT_EQ(nEntriesCountsMap[11], 31U) << "Wrong number of tasks with 11 clusters each!\n";

   gSystem->Unlink(filename);
   ROOT::DisableImplicitMT();
}

void CheckClusters(std::vector<std::pair<Long64_t, Long64_t>> &clusters, Long64_t entries)
{
   using R = std::pair<Long64_t, Long64_t>;
   // sort them
   std::sort(clusters.begin(), clusters.end(), [](const R &p1, const R &p2) { return p1.first < p2.first; });
   // check each end corresponds to the next start
   const auto nClusters = clusters.size();
   for (auto i = 0u; i < nClusters - 1; ++i)
      EXPECT_EQ(clusters[i].second, clusters[i + 1].first);
   // check start and end correspond to true start and end
   EXPECT_EQ(clusters.front().first, 0LL);
   EXPECT_EQ(clusters.back().second, entries);
}

TEST(TreeProcessorMT, LimitNTasks_CheckClusters)
{
   const auto nEvents = 156;
   const auto filename = "TreeProcessorMT_LimitNTasks_CheckClusters.root";
   const auto treename = "t";
   WriteFileManyClusters(nEvents, treename, filename);

   std::mutex m;
   std::vector<std::pair<Long64_t, Long64_t>> clusters;
   auto get_clusters = [&m, &clusters](TTreeReader &t) {
      std::lock_guard<std::mutex> l(m);
      clusters.emplace_back(t.GetEntriesRange());
   };

   for (auto nThreads = 0; nThreads <= 4; ++nThreads) {
      ROOT::DisableImplicitMT();
      ROOT::EnableImplicitMT(nThreads);

      ROOT::TTreeProcessorMT p(filename, treename);
      p.Process(get_clusters);

      CheckClusters(clusters, nEvents);
      clusters.clear();
   }

   gSystem->Unlink(filename);
   ROOT::DisableImplicitMT();
}

#if !defined(_MSC_VER) || defined(R__ENABLE_BROKEN_WIN_TESTS)
TEST(TreeProcessorMT, PathName)
{
   auto fname = "root://eospublic.cern.ch//eos/root-eos/cms_opendata_2012_nanoaod/ZZTo4mu.root";
   auto f = std::unique_ptr<TFile>(TFile::Open(fname));
   auto tree = f->Get<TTree>("Events");
   ROOT::TTreeProcessorMT p(*tree);
   std::atomic<unsigned int> n(0U);
   auto func = [&n](TTreeReader &t) {
      while (t.Next())
         n++;
   };
   p.Process(func);
   EXPECT_EQ(n.load(), 1499064U) << "Wrong number of events processed!\n";
}
#endif

TEST(TreeProcessorMT, TreeWithFriendTree)
{
   std::vector<std::string> fileNames = {"TreeWithFriendTree_Tree.root", "TreeWithFriendTree_Friend.root"};
   for (auto &name : fileNames) {
      TFile f(name.c_str(), "RECREATE");
      TTree t("treeName", "treeTitle");
      t.Write();
      f.Close();
   }

   ROOT::EnableThreadSafety();
   auto procLambda = [](TTreeReader &r) {
      while (r.Next())
         ;
   };

   auto f1 = TFile::Open(fileNames[0].c_str());
   auto t1 = (TTree *)f1->Get("treeName");

   auto f2 = TFile::Open(fileNames[1].c_str());
   auto t2 = (TTree *)f2->Get("treeName");

   t1->AddFriend(t2);

   ROOT::TTreeProcessorMT tp(*t1);
   tp.Process(procLambda);

   DeleteFiles(fileNames);
}

TEST(TreeProcessorMT, ChainWithFriendChain)
{
   std::vector<std::string> fileNames = {"ChainWithFriendChain_Tree1.root", "ChainWithFriendChain_Tree2.root", "ChainWithFriendChain_Friend1.root", "ChainWithFriendChain_Friend2.root"};
   for (auto &name : fileNames) {
      TFile f(name.c_str(), "RECREATE");
      TTree t("treeName", "treeTitle");
      t.Write();
      f.Close();
   }

   ROOT::EnableThreadSafety();
   auto procLambda = [](TTreeReader &r) {
      while (r.Next())
         ;
   };

   // Version 1: Use tree name in constructor
   TChain c1("treeName");
   c1.AddFile(fileNames[0].c_str());
   c1.AddFile(fileNames[1].c_str());

   TChain c2("treeName");
   c2.AddFile(fileNames[2].c_str());
   c2.AddFile(fileNames[3].c_str());

   c1.AddFriend(&c2);

   ROOT::TTreeProcessorMT tp1(c1);
   tp1.Process(procLambda);

   // Version 2: Use tree name in AddFile
   TChain c3;
   c3.AddFile((fileNames[0] + "/treeName").c_str());
   c3.AddFile((fileNames[1] + "/treeName").c_str());

   TChain c4;
   c4.AddFile((fileNames[2] + "/treeName").c_str());
   c4.AddFile((fileNames[3] + "/treeName").c_str());

   c3.AddFriend(&c4);

   ROOT::TTreeProcessorMT tp2(c3);
   tp2.Process(procLambda);

   // Clean-up
   DeleteFiles(fileNames);
}
<commit_msg>Use `EnableImplicitMT` instead of `EnableThreadSafety` to properly init the thread pool manager<commit_after>#include <algorithm>
#include <atomic>
#include <chrono>
#include <random>
#include <string>
#include <thread>
#include <utility>

#include <TFile.h>
#include <TTree.h>
#include <TSystem.h>
#include <TTreeReader.h>
#include <TTreeReaderValue.h>
#include <ROOT/TTreeProcessorMT.hxx>

#include "gtest/gtest.h"

void WriteFiles(const std::string &treename, const std::vector<std::string> &filenames)
{
   int v = 0;
   for (const auto &f : filenames) {
      TFile file(f.c_str(), "recreate");
      TTree t(treename.c_str(), treename.c_str());
      t.Branch("v", &v);
      for (auto i = 0; i < 10; ++i) {
         ++v;
         t.Fill();
      }
      t.Write();
   }
}

void WriteFileManyClusters(unsigned int nevents, const char *treename, const char *filename)
{
   int v = 0;
   TFile file(filename, "recreate");
   TTree t(treename, treename);
   t.Branch("v", &v);
   // t.SetAutoFlush(1);
   for (auto i = 0U; i < nevents; ++i) {
      t.Fill();
      t.FlushBaskets();
   }
   t.Write();
   file.Close();
}

void DeleteFiles(const std::vector<std::string> &filenames)
{
   for (const auto &f : filenames)
      gSystem->Unlink(f.c_str());
}

TEST(TreeProcessorMT, EmptyTChain)
{
   TChain c("mytree");
   auto exceptionFired(false);
   try {
      ROOT::TTreeProcessorMT proc(c);
   } catch (...) {
      exceptionFired = true;
   }
   EXPECT_TRUE(exceptionFired);
}

TEST(TreeProcessorMT, ManyFiles)
{
   const auto nFiles = 100u;
   const std::string treename = "t";
   std::vector<std::string> filenames;
   for (auto i = 0u; i < nFiles; ++i)
      filenames.emplace_back("treeprocmt_" + std::to_string(i) + ".root");

   WriteFiles(treename, filenames);

   std::atomic_int sum(0);
   std::atomic_int count(0);
   auto sumValues = [&sum, &count](TTreeReader &r) {
      TTreeReaderValue<int> v(r, "v");
      std::random_device seed;
      std::default_random_engine eng(seed());
      std::uniform_int_distribution<> rand(1, 100);
      while (r.Next()) {
         std::this_thread::sleep_for(std::chrono::nanoseconds(rand(eng)));
         sum += *v;
         ++count;
      }
   };

   // TTreeProcMT requires a vector<string_view>
   std::vector<std::string_view> fnames;
   for (const auto &f : filenames)
      fnames.emplace_back(f);

   ROOT::TTreeProcessorMT proc(fnames, treename);
   proc.Process(sumValues);

   EXPECT_EQ(count.load(), int(nFiles * 10)); // 10 entries per file
   EXPECT_EQ(sum.load(), 500500);             // sum 1..nFiles*10

   DeleteFiles(filenames);
}

TEST(TreeProcessorMT, TreeInSubDirectory)
{
   auto filename = "fileTreeInSubDirectory.root";
   auto procLambda = [](TTreeReader &r) {
      while (r.Next())
         ;
   };

   {
      TFile f(filename, "RECREATE");
      auto dir0 = f.mkdir("dir0");
      dir0->cd();
      auto dir1 = dir0->mkdir("dir1");
      dir1->cd();
      TTree t("tree", "tree");
      t.Write();
   }

   ROOT::EnableThreadSafety();

   auto fullPath = "dir0/dir1/tree";

   // With a TTree
   TFile f(filename);
   auto t = (TTree *)f.Get(fullPath);
   ROOT::TTreeProcessorMT tp(*t);
   tp.Process(procLambda);

   // With a TChain
   std::string chainElementName = filename;
   chainElementName += "/";
   chainElementName += fullPath;
   TChain chain;
   chain.Add(chainElementName.c_str());
   ROOT::TTreeProcessorMT tpc(chain);
   tpc.Process(procLambda);

   gSystem->Unlink(filename);
}

TEST(TreeProcessorMT, LimitNTasks_CheckEntries)
{
   const auto nEvents = 991;
   const auto filename = "TreeProcessorMT_LimitNTasks_CheckEntries.root";
   const auto treename = "t";
   WriteFileManyClusters(nEvents, treename, filename);
   auto nTasks = 0U;
   std::map<unsigned int, unsigned int> nEntriesCountsMap;
   std::mutex theMutex;
   auto f = [&](TTreeReader &t) {
      auto nentries = 0U;
      while (t.Next())
         nentries++;
      std::lock_guard<std::mutex> lg(theMutex);
      nTasks++;
      if (!nEntriesCountsMap.insert({nentries, 1U}).second) {
         nEntriesCountsMap[nentries]++;
      }
   };

   ROOT::DisableImplicitMT();
   ROOT::EnableImplicitMT(4);

   ROOT::TTreeProcessorMT p(filename, treename);
   p.Process(f);

   EXPECT_EQ(nTasks, 96U) << "Wrong number of tasks generated!\n";
   EXPECT_EQ(nEntriesCountsMap[10], 65U) << "Wrong number of tasks with 10 clusters each!\n";
   EXPECT_EQ(nEntriesCountsMap[11], 31U) << "Wrong number of tasks with 11 clusters each!\n";

   gSystem->Unlink(filename);
   ROOT::DisableImplicitMT();
}

void CheckClusters(std::vector<std::pair<Long64_t, Long64_t>> &clusters, Long64_t entries)
{
   using R = std::pair<Long64_t, Long64_t>;
   // sort them
   std::sort(clusters.begin(), clusters.end(), [](const R &p1, const R &p2) { return p1.first < p2.first; });
   // check each end corresponds to the next start
   const auto nClusters = clusters.size();
   for (auto i = 0u; i < nClusters - 1; ++i)
      EXPECT_EQ(clusters[i].second, clusters[i + 1].first);
   // check start and end correspond to true start and end
   EXPECT_EQ(clusters.front().first, 0LL);
   EXPECT_EQ(clusters.back().second, entries);
}

TEST(TreeProcessorMT, LimitNTasks_CheckClusters)
{
   const auto nEvents = 156;
   const auto filename = "TreeProcessorMT_LimitNTasks_CheckClusters.root";
   const auto treename = "t";
   WriteFileManyClusters(nEvents, treename, filename);

   std::mutex m;
   std::vector<std::pair<Long64_t, Long64_t>> clusters;
   auto get_clusters = [&m, &clusters](TTreeReader &t) {
      std::lock_guard<std::mutex> l(m);
      clusters.emplace_back(t.GetEntriesRange());
   };

   for (auto nThreads = 0; nThreads <= 4; ++nThreads) {
      ROOT::DisableImplicitMT();
      ROOT::EnableImplicitMT(nThreads);

      ROOT::TTreeProcessorMT p(filename, treename);
      p.Process(get_clusters);

      CheckClusters(clusters, nEvents);
      clusters.clear();
   }

   gSystem->Unlink(filename);
   ROOT::DisableImplicitMT();
}

#if !defined(_MSC_VER) || defined(R__ENABLE_BROKEN_WIN_TESTS)
TEST(TreeProcessorMT, PathName)
{
   auto fname = "root://eospublic.cern.ch//eos/root-eos/cms_opendata_2012_nanoaod/ZZTo4mu.root";
   auto f = std::unique_ptr<TFile>(TFile::Open(fname));
   auto tree = f->Get<TTree>("Events");
   ROOT::TTreeProcessorMT p(*tree);
   std::atomic<unsigned int> n(0U);
   auto func = [&n](TTreeReader &t) {
      while (t.Next())
         n++;
   };
   p.Process(func);
   EXPECT_EQ(n.load(), 1499064U) << "Wrong number of events processed!\n";
}
#endif

TEST(TreeProcessorMT, TreeWithFriendTree)
{
   std::vector<std::string> fileNames = {"TreeWithFriendTree_Tree.root", "TreeWithFriendTree_Friend.root"};
   for (auto &name : fileNames) {
      TFile f(name.c_str(), "RECREATE");
      TTree t("treeName", "treeTitle");
      t.Write();
      f.Close();
   }

   ROOT::EnableImplicitMT(1);
   auto procLambda = [](TTreeReader &r) {
      while (r.Next())
         ;
   };

   auto f1 = TFile::Open(fileNames[0].c_str());
   auto t1 = (TTree *)f1->Get("treeName");

   auto f2 = TFile::Open(fileNames[1].c_str());
   auto t2 = (TTree *)f2->Get("treeName");

   t1->AddFriend(t2);

   ROOT::TTreeProcessorMT tp(*t1);
   tp.Process(procLambda);

   DeleteFiles(fileNames);
}

TEST(TreeProcessorMT, ChainWithFriendChain)
{
   std::vector<std::string> fileNames = {"ChainWithFriendChain_Tree1.root", "ChainWithFriendChain_Tree2.root", "ChainWithFriendChain_Friend1.root", "ChainWithFriendChain_Friend2.root"};
   for (auto &name : fileNames) {
      TFile f(name.c_str(), "RECREATE");
      TTree t("treeName", "treeTitle");
      t.Write();
      f.Close();
   }

   ROOT::EnableImplicitMT(1);
   auto procLambda = [](TTreeReader &r) {
      while (r.Next())
         ;
   };

   // Version 1: Use tree name in constructor
   TChain c1("treeName");
   c1.AddFile(fileNames[0].c_str());
   c1.AddFile(fileNames[1].c_str());

   TChain c2("treeName");
   c2.AddFile(fileNames[2].c_str());
   c2.AddFile(fileNames[3].c_str());

   c1.AddFriend(&c2);

   ROOT::TTreeProcessorMT tp1(c1);
   tp1.Process(procLambda);

   // Version 2: Use tree name in AddFile
   TChain c3;
   c3.AddFile((fileNames[0] + "/treeName").c_str());
   c3.AddFile((fileNames[1] + "/treeName").c_str());

   TChain c4;
   c4.AddFile((fileNames[2] + "/treeName").c_str());
   c4.AddFile((fileNames[3] + "/treeName").c_str());

   c3.AddFriend(&c4);

   ROOT::TTreeProcessorMT tp2(c3);
   tp2.Process(procLambda);

   // Clean-up
   DeleteFiles(fileNames);
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: canvascustomsprite.hxx,v $
 * $Revision: 1.11 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _VCLCANVAS_CANVASCUSTOMSPRITE_HXX
#define _VCLCANVAS_CANVASCUSTOMSPRITE_HXX

#include <cppuhelper/compbase4.hxx>
#include <comphelper/uno3.hxx>

#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/rendering/XCustomSprite.hpp>
#include <com/sun/star/rendering/XIntegerBitmap.hpp>
#include <com/sun/star/rendering/XPolyPolygon2D.hpp>

#include <vcl/virdev.hxx>

#include <canvas/vclwrapper.hxx>
#include <canvas/base/basemutexhelper.hxx>
#include <canvas/base/canvascustomspritebase.hxx>

#include "sprite.hxx"
#include "canvashelper.hxx"
#include "spritehelper.hxx"
#include "backbuffer.hxx"
#include "impltools.hxx"
#include "spritecanvas.hxx"
#include "repainttarget.hxx"

namespace vclcanvas
{
    typedef ::cppu::WeakComponentImplHelper4< ::com::sun::star::rendering::XCustomSprite,
                                               ::com::sun::star::rendering::XBitmapCanvas,
                                              ::com::sun::star::rendering::XIntegerBitmap,
                                                ::com::sun::star::lang::XServiceInfo >  CanvasCustomSpriteBase_Base;
    /** Mixin Sprite

        Have to mixin the Sprite interface before deriving from
        ::canvas::CanvasCustomSpriteBase, as this template should
        already implement some of those interface methods.

        The reason why this appears kinda convoluted is the fact that
        we cannot specify non-IDL types as WeakComponentImplHelperN
        template args, and furthermore, don't want to derive
        ::canvas::CanvasCustomSpriteBase directly from
        ::canvas::Sprite (because derivees of
        ::canvas::CanvasCustomSpriteBase have to explicitely forward
        the XInterface methods (e.g. via DECLARE_UNO3_AGG_DEFAULTS)
        anyway). Basically, ::canvas::CanvasCustomSpriteBase should
        remain a base class that provides implementation, not to
        enforce any specific interface on its derivees.
     */
    class CanvasCustomSpriteSpriteBase_Base : public ::canvas::BaseMutexHelper< CanvasCustomSpriteBase_Base >,
                                                 public Sprite
    {
    };

    typedef ::canvas::CanvasCustomSpriteBase< CanvasCustomSpriteSpriteBase_Base,
                                              SpriteHelper,
                                              CanvasHelper,
                                              tools::LocalGuard,
                                              ::cppu::OWeakObject >                     CanvasCustomSpriteBaseT;

    /* Definition of CanvasCustomSprite class */

    class CanvasCustomSprite : public CanvasCustomSpriteBaseT,
                               public RepaintTarget
    {
    public:
        CanvasCustomSprite( const ::com::sun::star::geometry::RealSize2D&   rSpriteSize,
                            const SpriteCanvasRef&                          rSpriteCanvas,
                            bool                                            bShowSpriteBounds );

        virtual void SAL_CALL disposing();

        // Forwarding the XComponent implementation to the
        // cppu::ImplHelper templated base
        //                                    Classname           Base doing refcount          Base implementing the XComponent interface
        //                                          |                    |                         |
        //                                          V                    V                         V
        DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( CanvasCustomSprite, CanvasCustomSpriteBase_Base, ::cppu::WeakComponentImplHelperBase );

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

        // Sprite
        virtual void redraw( OutputDevice& rOutDev,
                             bool          bBufferedUpdate ) const;
        virtual void redraw( OutputDevice&              rOutDev,
                             const ::basegfx::B2DPoint& rPos,
                             bool                       bBufferedUpdate ) const;

        // RepaintTarget
        virtual bool repaint( const GraphicObjectSharedPtr&                   rGrf,
                              const ::com::sun::star::rendering::ViewState&   viewState,
                              const ::com::sun::star::rendering::RenderState& renderState,
                              const ::Point&                                  rPt,
                              const ::Size&                                   rSz,
                              const GraphicAttr&                              rAttr ) const;

    private:
        /** MUST hold here, too, since CanvasHelper only contains a
            raw pointer (without refcounting)
        */
        SpriteCanvasRef mpSpriteCanvas;
    };
}

#endif /* _VCLCANVAS_CANVASCUSTOMSPRITE_HXX */
<commit_msg>INTEGRATION: CWS canvas05 (1.10.2); FILE MERGED 2008/04/21 07:27:52 thb 1.10.2.2: RESYNC: (1.10-1.11); FILE MERGED 2007/12/20 22:18:59 thb 1.10.2.1: #i81092# #i78888# #i78925# #i79258# #i79437# #i84784# Large canvas rework, completing various areas such as color spaces, bitmap data access, true sprite and non-sprite implementations, and upstreaming the canvas parts of rodos emf+ rendering<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: canvascustomsprite.hxx,v $
 * $Revision: 1.12 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _VCLCANVAS_CANVASCUSTOMSPRITE_HXX
#define _VCLCANVAS_CANVASCUSTOMSPRITE_HXX

#include <cppuhelper/compbase4.hxx>
#include <comphelper/uno3.hxx>

#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/rendering/XCustomSprite.hpp>
#include <com/sun/star/rendering/XIntegerBitmap.hpp>
#include <com/sun/star/rendering/XPolyPolygon2D.hpp>

#include <vcl/virdev.hxx>

#include <canvas/vclwrapper.hxx>
#include <canvas/base/basemutexhelper.hxx>
#include <canvas/base/spritesurface.hxx>
#include <canvas/base/canvascustomspritebase.hxx>

#include "sprite.hxx"
#include "canvashelper.hxx"
#include "spritehelper.hxx"
#include "backbuffer.hxx"
#include "impltools.hxx"
#include "spritecanvas.hxx"
#include "repainttarget.hxx"

namespace vclcanvas
{
    typedef ::cppu::WeakComponentImplHelper4< ::com::sun::star::rendering::XCustomSprite,
                                               ::com::sun::star::rendering::XBitmapCanvas,
                                              ::com::sun::star::rendering::XIntegerBitmap,
                                                ::com::sun::star::lang::XServiceInfo >  CanvasCustomSpriteBase_Base;
    /** Mixin Sprite

        Have to mixin the Sprite interface before deriving from
        ::canvas::CanvasCustomSpriteBase, as this template should
        already implement some of those interface methods.

        The reason why this appears kinda convoluted is the fact that
        we cannot specify non-IDL types as WeakComponentImplHelperN
        template args, and furthermore, don't want to derive
        ::canvas::CanvasCustomSpriteBase directly from
        ::canvas::Sprite (because derivees of
        ::canvas::CanvasCustomSpriteBase have to explicitely forward
        the XInterface methods (e.g. via DECLARE_UNO3_AGG_DEFAULTS)
        anyway). Basically, ::canvas::CanvasCustomSpriteBase should
        remain a base class that provides implementation, not to
        enforce any specific interface on its derivees.
     */
    class CanvasCustomSpriteSpriteBase_Base : public ::canvas::BaseMutexHelper< CanvasCustomSpriteBase_Base >,
                                                 public Sprite
    {
    };

    typedef ::canvas::CanvasCustomSpriteBase< CanvasCustomSpriteSpriteBase_Base,
                                              SpriteHelper,
                                              CanvasHelper,
                                              tools::LocalGuard,
                                              ::cppu::OWeakObject >                     CanvasCustomSpriteBaseT;

    /* Definition of CanvasCustomSprite class */

    class CanvasCustomSprite : public CanvasCustomSpriteBaseT,
                               public RepaintTarget
    {
    public:
        CanvasCustomSprite( const ::com::sun::star::geometry::RealSize2D& rSpriteSize,
                            ::com::sun::star::rendering::XGraphicDevice&  rDevice,
                            const ::canvas::SpriteSurface::Reference&     rOwningSpriteCanvas,
                            const OutDevProviderSharedPtr&                rOutDevProvider,
                            bool                                          bShowSpriteBounds );

        virtual void SAL_CALL disposing();

        // Forwarding the XComponent implementation to the
        // cppu::ImplHelper templated base
        //                                    Classname           Base doing refcount          Base implementing the XComponent interface
        //                                          |                    |                         |
        //                                          V                    V                         V
        DECLARE_UNO3_XCOMPONENT_AGG_DEFAULTS( CanvasCustomSprite, CanvasCustomSpriteBase_Base, ::cppu::WeakComponentImplHelperBase );

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

        // Sprite
        virtual void redraw( OutputDevice& rOutDev,
                             bool          bBufferedUpdate ) const;
        virtual void redraw( OutputDevice&              rOutDev,
                             const ::basegfx::B2DPoint& rPos,
                             bool                       bBufferedUpdate ) const;

        // RepaintTarget
        virtual bool repaint( const GraphicObjectSharedPtr&                   rGrf,
                              const ::com::sun::star::rendering::ViewState&   viewState,
                              const ::com::sun::star::rendering::RenderState& renderState,
                              const ::Point&                                  rPt,
                              const ::Size&                                   rSz,
                              const GraphicAttr&                              rAttr ) const;
    };
}

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


<commit_before>// Copyright 2020 Google LLC
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

// Implementation of an AEAD Service.
#include "aead_impl.h"

#include <string>
#include <utility>

#include "tink/aead.h"
#include "tink/binary_keyset_reader.h"
#include "tink/cleartext_keyset_handle.h"

namespace tink_testing_api {

using ::crypto::tink::BinaryKeysetReader;
using ::crypto::tink::CleartextKeysetHandle;

// Encrypts a message
::grpc::Status AeadImpl::CreateAead(grpc::ServerContext* context,
                                    const AeadCreationRequest* request,
                                    AeadCreationResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    response->set_err(std::string(reader_result.status().message()));
    return ::grpc::Status::OK;
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    response->set_err(std::string(handle_result.status().message()));
    return ::grpc::Status::OK;
  }
  auto aead_result = handle_result.value()->GetPrimitive<crypto::tink::Aead>();
  if (!aead_result.ok()) {
    response->set_err(std::string(aead_result.status().message()));
    return ::grpc::Status::OK;
  }
  return ::grpc::Status::OK;
}

::grpc::Status AeadImpl::Encrypt(grpc::ServerContext* context,
                                 const AeadEncryptRequest* request,
                                 AeadEncryptResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Reading Keyset failed");
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating KeysetHandle failed");
  }
  auto aead_result = handle_result.value()->GetPrimitive<crypto::tink::Aead>();
  if (!aead_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating Aead failed");
  }
  auto encrypt_result = aead_result.value()->Encrypt(
      request->plaintext(), request->associated_data());
  if (!encrypt_result.ok()) {
    response->set_err(std::string(encrypt_result.status().message()));
    return ::grpc::Status::OK;
  }
  response->set_ciphertext(encrypt_result.value());
  return ::grpc::Status::OK;
}

// Decrypts a ciphertext
::grpc::Status AeadImpl::Decrypt(grpc::ServerContext* context,
                                 const AeadDecryptRequest* request,
                                 AeadDecryptResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Reading Keyset failed");
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating KeysetHandle failed");
  }
  auto aead_result = handle_result.value()->GetPrimitive<crypto::tink::Aead>();
  if (!aead_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating Aead failed");
  }
  auto decrypt_result = aead_result.value()->Decrypt(
      request->ciphertext(), request->associated_data());
  if (!decrypt_result.ok()) {
    response->set_err(std::string(decrypt_result.status().message()));
    return ::grpc::Status::OK;
  }
  response->set_plaintext(decrypt_result.value());
  return ::grpc::Status::OK;
}

}  // namespace tink_testing_api
<commit_msg>Delete a comment I forgot to move in a previous CL -- and also another. Both are basically unnecessary.<commit_after>// Copyright 2020 Google LLC
//
// 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.
//
///////////////////////////////////////////////////////////////////////////////

// Implementation of an AEAD Service.
#include "aead_impl.h"

#include <string>
#include <utility>

#include "tink/aead.h"
#include "tink/binary_keyset_reader.h"
#include "tink/cleartext_keyset_handle.h"

namespace tink_testing_api {

using ::crypto::tink::BinaryKeysetReader;
using ::crypto::tink::CleartextKeysetHandle;

::grpc::Status AeadImpl::CreateAead(grpc::ServerContext* context,
                                    const AeadCreationRequest* request,
                                    AeadCreationResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    response->set_err(std::string(reader_result.status().message()));
    return ::grpc::Status::OK;
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    response->set_err(std::string(handle_result.status().message()));
    return ::grpc::Status::OK;
  }
  auto aead_result = handle_result.value()->GetPrimitive<crypto::tink::Aead>();
  if (!aead_result.ok()) {
    response->set_err(std::string(aead_result.status().message()));
    return ::grpc::Status::OK;
  }
  return ::grpc::Status::OK;
}

::grpc::Status AeadImpl::Encrypt(grpc::ServerContext* context,
                                 const AeadEncryptRequest* request,
                                 AeadEncryptResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Reading Keyset failed");
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating KeysetHandle failed");
  }
  auto aead_result = handle_result.value()->GetPrimitive<crypto::tink::Aead>();
  if (!aead_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating Aead failed");
  }
  auto encrypt_result = aead_result.value()->Encrypt(
      request->plaintext(), request->associated_data());
  if (!encrypt_result.ok()) {
    response->set_err(std::string(encrypt_result.status().message()));
    return ::grpc::Status::OK;
  }
  response->set_ciphertext(encrypt_result.value());
  return ::grpc::Status::OK;
}

::grpc::Status AeadImpl::Decrypt(grpc::ServerContext* context,
                                 const AeadDecryptRequest* request,
                                 AeadDecryptResponse* response) {
  auto reader_result = BinaryKeysetReader::New(request->keyset());
  if (!reader_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Reading Keyset failed");
  }
  auto handle_result =
      CleartextKeysetHandle::Read(std::move(reader_result.value()));
  if (!handle_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating KeysetHandle failed");
  }
  auto aead_result = handle_result.value()->GetPrimitive<crypto::tink::Aead>();
  if (!aead_result.ok()) {
    return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION,
                          "Creating Aead failed");
  }
  auto decrypt_result = aead_result.value()->Decrypt(
      request->ciphertext(), request->associated_data());
  if (!decrypt_result.ok()) {
    response->set_err(std::string(decrypt_result.status().message()));
    return ::grpc::Status::OK;
  }
  response->set_plaintext(decrypt_result.value());
  return ::grpc::Status::OK;
}

}  // namespace tink_testing_api
<|endoftext|>


<commit_before>#include <StdAfx.h>
#include <UI/Controls/PaneHeader/PaneHeader.h>
#include <UI/UIFunctions.h>
#include <core/utility/strings.h>
#include <core/utility/output.h>

namespace controls
{
	void PaneHeader::Initialize(_In_ CWnd* pParent, _In_opt_ HDC hdc, _In_ UINT nid)
	{
		if (pParent) m_hWndParent = pParent->m_hWnd;
		// Assign a nID to the collapse button that is IDD_COLLAPSE more than the control's nID
		m_nIDCollapse = nid + IDD_COLLAPSE;
		// TODO: We don't save our header's nID here, but we could if we wanted

		// If we need an action button, go ahead and create it
		if (m_nIDAction)
		{
			EC_B_S(m_actionButton.Create(
				strings::wstringTotstring(m_szActionButton).c_str(),
				WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
				CRect(0, 0, 0, 0),
				pParent,
				m_nIDAction));
			StyleButton(m_actionButton.m_hWnd, ui::uiButtonStyle::Unstyled);
		}

		EC_B_S(m_rightLabel.Create(
			WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE, CRect(0, 0, 0, 0), pParent, IDD_COUNTLABEL));
		ui::SubclassLabel(m_rightLabel.m_hWnd);
		StyleLabel(m_rightLabel.m_hWnd, ui::uiLabelStyle::PaneHeaderText);

		EC_B_S(Create(WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE, CRect(0, 0, 0, 0), pParent, nid));
		::SetWindowTextW(m_hWnd, m_szLabel.c_str());
		ui::SubclassLabel(m_hWnd);

		if (m_bCollapsible)
		{
			StyleLabel(m_hWnd, ui::uiLabelStyle::PaneHeaderLabel);

			EC_B_S(m_CollapseButton.Create(
				nullptr,
				WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
				CRect(0, 0, 0, 0),
				pParent,
				m_nIDCollapse));
			StyleButton(
				m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		}

		const auto sizeText = ui::GetTextExtentPoint32(hdc, m_szLabel);
		m_iLabelWidth = sizeText.cx;
		output::DebugPrint(
			output::dbgLevel::Draw,
			L"PaneHeader::Initialize m_iLabelWidth:%d \"%ws\"\n",
			m_iLabelWidth,
			m_szLabel.c_str());

		m_bInitialized = true;
	}

	// Draw our collapse button and label, if needed.
	// Draws everything to GetFixedHeight()
	void PaneHeader::DeferWindowPos(_In_ HDWP hWinPosInfo, const _In_ int x, const _In_ int y, const _In_ int width)
	{
		const auto height = GetFixedHeight();
		auto curX = x;
		const auto actionButtonWidth = m_actionButtonWidth ? m_actionButtonWidth + 2 * m_iMargin : 0;
		const auto actionButtonAndGutterWidth = actionButtonWidth ? actionButtonWidth + m_iSideMargin : 0;
		if (m_bCollapsible)
		{
			::DeferWindowPos(
				hWinPosInfo,
				m_CollapseButton.GetSafeHwnd(),
				nullptr,
				curX,
				y,
				width - actionButtonAndGutterWidth,
				height,
				SWP_NOZORDER);
			curX += m_iButtonHeight;
		}

		output::DebugPrint(
			output::dbgLevel::Draw,
			L"PaneHeader::DeferWindowPos x:%d width:%d labelpos:%d labelwidth:%d \n",
			x,
			width,
			curX,
			m_iLabelWidth);

		::DeferWindowPos(hWinPosInfo, GetSafeHwnd(), nullptr, curX, y, m_iLabelWidth, height, SWP_NOZORDER);

		if (!m_bCollapsed)
		{
			// Drop the count on top of the label we drew above
			EC_B_S(::DeferWindowPos(
				hWinPosInfo,
				m_rightLabel.GetSafeHwnd(),
				nullptr,
				x + width - m_rightLabelWidth - actionButtonAndGutterWidth,
				y,
				m_rightLabelWidth,
				height,
				SWP_NOZORDER));
		}

		if (m_nIDAction)
		{
			// Drop the action button next to the label we drew above
			EC_B_S(::DeferWindowPos(
				hWinPosInfo,
				m_actionButton.GetSafeHwnd(),
				nullptr,
				x + width - actionButtonWidth,
				y,
				actionButtonWidth,
				height,
				SWP_NOZORDER));
		}
	}

	int PaneHeader::GetMinWidth()
	{
		auto cx = m_iLabelWidth;
		if (m_bCollapsible) cx += m_iButtonHeight;
		if (m_rightLabelWidth)
		{
			cx += m_iSideMargin;
			cx += m_rightLabelWidth;
		}

		if (m_actionButtonWidth)
		{
			cx += m_iSideMargin;
			cx += m_actionButtonWidth + 2 * m_iMargin;
		}

		return cx;
	}

	void PaneHeader::SetRightLabel(const std::wstring szLabel)
	{
		if (!m_bInitialized) return;
		EC_B_S(::SetWindowTextW(m_rightLabel.m_hWnd, szLabel.c_str()));

		const auto hdc = ::GetDC(m_rightLabel.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szLabel);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_rightLabel.GetSafeHwnd(), hdc);
		m_rightLabelWidth = sizeText.cx;
	}

	void PaneHeader::SetActionButton(const std::wstring szActionButton)
	{
		// Don't bother if we never enabled the button
		if (m_nIDAction == 0) return;

		EC_B_S(::SetWindowTextW(m_actionButton.GetSafeHwnd(), szActionButton.c_str()));

		m_szActionButton = szActionButton;
		const auto hdc = ::GetDC(m_actionButton.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szActionButton);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_actionButton.GetSafeHwnd(), hdc);
		m_actionButtonWidth = sizeText.cx;
	}

	bool PaneHeader::HandleChange(UINT nID)
	{
		// Collapse buttons have a nID IDD_COLLAPSE higher than nID of the pane they toggle.
		// So if we get asked about one that matches, we can assume it's time to toggle our collapse.
		if (m_nIDCollapse == nID)
		{
			OnToggleCollapse();
			return true;
		}

		return false;
	}

	void PaneHeader::OnToggleCollapse()
	{
		m_bCollapsed = !m_bCollapsed;

		StyleButton(m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		WC_B_S(m_rightLabel.ShowWindow(m_bCollapsed ? SW_HIDE : SW_SHOW));

		// Trigger a redraw
		::PostMessage(m_hWndParent, WM_COMMAND, IDD_RECALCLAYOUT, NULL);
	}

	void PaneHeader::SetMargins(
		int iMargin,
		int iSideMargin,
		int iLabelHeight, // Height of the label
		int iButtonHeight) // Height of button
	{
		m_iMargin = iMargin;
		m_iSideMargin = iSideMargin;
		m_iLabelHeight = iLabelHeight;
		m_iButtonHeight = iButtonHeight;
	}
} // namespace controls<commit_msg>move action button to proper tab order<commit_after>#include <StdAfx.h>
#include <UI/Controls/PaneHeader/PaneHeader.h>
#include <UI/UIFunctions.h>
#include <core/utility/strings.h>
#include <core/utility/output.h>

namespace controls
{
	void PaneHeader::Initialize(_In_ CWnd* pParent, _In_opt_ HDC hdc, _In_ UINT nid)
	{
		if (pParent) m_hWndParent = pParent->m_hWnd;
		// Assign a nID to the collapse button that is IDD_COLLAPSE more than the control's nID
		m_nIDCollapse = nid + IDD_COLLAPSE;
		// TODO: We don't save our header's nID here, but we could if we wanted

		EC_B_S(m_rightLabel.Create(
			WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE, CRect(0, 0, 0, 0), pParent, IDD_COUNTLABEL));
		ui::SubclassLabel(m_rightLabel.m_hWnd);
		StyleLabel(m_rightLabel.m_hWnd, ui::uiLabelStyle::PaneHeaderText);

		EC_B_S(Create(WS_CHILD | WS_CLIPSIBLINGS | ES_READONLY | WS_VISIBLE, CRect(0, 0, 0, 0), pParent, nid));
		::SetWindowTextW(m_hWnd, m_szLabel.c_str());
		ui::SubclassLabel(m_hWnd);

		if (m_bCollapsible)
		{
			StyleLabel(m_hWnd, ui::uiLabelStyle::PaneHeaderLabel);

			EC_B_S(m_CollapseButton.Create(
				nullptr,
				WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
				CRect(0, 0, 0, 0),
				pParent,
				m_nIDCollapse));
			StyleButton(
				m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		}

		const auto sizeText = ui::GetTextExtentPoint32(hdc, m_szLabel);
		m_iLabelWidth = sizeText.cx;
		output::DebugPrint(
			output::dbgLevel::Draw,
			L"PaneHeader::Initialize m_iLabelWidth:%d \"%ws\"\n",
			m_iLabelWidth,
			m_szLabel.c_str());

		// If we need an action button, go ahead and create it
		if (m_nIDAction)
		{
			EC_B_S(m_actionButton.Create(
				strings::wstringTotstring(m_szActionButton).c_str(),
				WS_TABSTOP | WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
				CRect(0, 0, 0, 0),
				pParent,
				m_nIDAction));
			StyleButton(m_actionButton.m_hWnd, ui::uiButtonStyle::Unstyled);
		}

		m_bInitialized = true;
	}

	// Draw our collapse button and label, if needed.
	// Draws everything to GetFixedHeight()
	void PaneHeader::DeferWindowPos(_In_ HDWP hWinPosInfo, const _In_ int x, const _In_ int y, const _In_ int width)
	{
		const auto height = GetFixedHeight();
		auto curX = x;
		const auto actionButtonWidth = m_actionButtonWidth ? m_actionButtonWidth + 2 * m_iMargin : 0;
		const auto actionButtonAndGutterWidth = actionButtonWidth ? actionButtonWidth + m_iSideMargin : 0;
		if (m_bCollapsible)
		{
			::DeferWindowPos(
				hWinPosInfo,
				m_CollapseButton.GetSafeHwnd(),
				nullptr,
				curX,
				y,
				width - actionButtonAndGutterWidth,
				height,
				SWP_NOZORDER);
			curX += m_iButtonHeight;
		}

		output::DebugPrint(
			output::dbgLevel::Draw,
			L"PaneHeader::DeferWindowPos x:%d width:%d labelpos:%d labelwidth:%d \n",
			x,
			width,
			curX,
			m_iLabelWidth);

		::DeferWindowPos(hWinPosInfo, GetSafeHwnd(), nullptr, curX, y, m_iLabelWidth, height, SWP_NOZORDER);

		if (!m_bCollapsed)
		{
			// Drop the count on top of the label we drew above
			EC_B_S(::DeferWindowPos(
				hWinPosInfo,
				m_rightLabel.GetSafeHwnd(),
				nullptr,
				x + width - m_rightLabelWidth - actionButtonAndGutterWidth,
				y,
				m_rightLabelWidth,
				height,
				SWP_NOZORDER));
		}

		if (m_nIDAction)
		{
			// Drop the action button next to the label we drew above
			EC_B_S(::DeferWindowPos(
				hWinPosInfo,
				m_actionButton.GetSafeHwnd(),
				nullptr,
				x + width - actionButtonWidth,
				y,
				actionButtonWidth,
				height,
				SWP_NOZORDER));
		}
	}

	int PaneHeader::GetMinWidth()
	{
		auto cx = m_iLabelWidth;
		if (m_bCollapsible) cx += m_iButtonHeight;
		if (m_rightLabelWidth)
		{
			cx += m_iSideMargin;
			cx += m_rightLabelWidth;
		}

		if (m_actionButtonWidth)
		{
			cx += m_iSideMargin;
			cx += m_actionButtonWidth + 2 * m_iMargin;
		}

		return cx;
	}

	void PaneHeader::SetRightLabel(const std::wstring szLabel)
	{
		if (!m_bInitialized) return;
		EC_B_S(::SetWindowTextW(m_rightLabel.m_hWnd, szLabel.c_str()));

		const auto hdc = ::GetDC(m_rightLabel.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szLabel);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_rightLabel.GetSafeHwnd(), hdc);
		m_rightLabelWidth = sizeText.cx;
	}

	void PaneHeader::SetActionButton(const std::wstring szActionButton)
	{
		// Don't bother if we never enabled the button
		if (m_nIDAction == 0) return;

		EC_B_S(::SetWindowTextW(m_actionButton.GetSafeHwnd(), szActionButton.c_str()));

		m_szActionButton = szActionButton;
		const auto hdc = ::GetDC(m_actionButton.GetSafeHwnd());
		const auto hfontOld = SelectObject(hdc, ui::GetSegoeFont());
		const auto sizeText = ui::GetTextExtentPoint32(hdc, szActionButton);
		static_cast<void>(SelectObject(hdc, hfontOld));
		::ReleaseDC(m_actionButton.GetSafeHwnd(), hdc);
		m_actionButtonWidth = sizeText.cx;
	}

	bool PaneHeader::HandleChange(UINT nID)
	{
		// Collapse buttons have a nID IDD_COLLAPSE higher than nID of the pane they toggle.
		// So if we get asked about one that matches, we can assume it's time to toggle our collapse.
		if (m_nIDCollapse == nID)
		{
			OnToggleCollapse();
			return true;
		}

		return false;
	}

	void PaneHeader::OnToggleCollapse()
	{
		m_bCollapsed = !m_bCollapsed;

		StyleButton(m_CollapseButton.m_hWnd, m_bCollapsed ? ui::uiButtonStyle::UpArrow : ui::uiButtonStyle::DownArrow);
		WC_B_S(m_rightLabel.ShowWindow(m_bCollapsed ? SW_HIDE : SW_SHOW));

		// Trigger a redraw
		::PostMessage(m_hWndParent, WM_COMMAND, IDD_RECALCLAYOUT, NULL);
	}

	void PaneHeader::SetMargins(
		int iMargin,
		int iSideMargin,
		int iLabelHeight, // Height of the label
		int iButtonHeight) // Height of button
	{
		m_iMargin = iMargin;
		m_iSideMargin = iSideMargin;
		m_iLabelHeight = iLabelHeight;
		m_iButtonHeight = iButtonHeight;
	}
} // namespace controls<|endoftext|>


<commit_before>/*
 * nt loader
 *
 * Copyright 2006-2008 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>
#include <execinfo.h>
#include <getopt.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"

#include "debug.h"
#include "mem.h"
#include "object.h"
#include "objdir.h"
#include "ntcall.h"
#include "section.h"
#include "timer.h"
#include "unicode.h"
#include "fiber.h"
#include "file.h"
#include "event.h"
#include "symlink.h"

process_list_t processes;
thread_t *current;
object_t *ntdll_section;
int option_debug = 0;
ULONG KiIntSystemCall = 0;
bool forced_quit;

class default_sleeper_t : public sleeper_t
{
public:
	virtual bool check_events( bool wait );
	virtual ~default_sleeper_t() {}
};

int sleeper_t::get_int_timeout( LARGE_INTEGER& timeout )
{
	timeout.QuadPart = (timeout.QuadPart+9999)/10000;
	int t = INT_MAX;
	if (timeout.QuadPart < t)
		t = timeout.QuadPart;
	return t;
}

bool default_sleeper_t::check_events( bool wait )
{
	LARGE_INTEGER timeout;

	// check for expired timers
	bool timers_left = timeout_t::check_timers(timeout);

	// Check for a deadlock and quit.
	//  This happens if we're the only active thread,
	//  there's no more timers, and we're asked to wait.
	if (!timers_left && wait && fiber_t::last_fiber())
		return true;
	if (!wait)
		return false;

	int t = get_int_timeout( timeout );
	int r = poll( 0, 0, t );
	if (r >= 0)
		return false;
	if (errno != EINTR)
		die("poll failed %d\n", errno);
	return false;
}

default_sleeper_t default_sleeper;
sleeper_t* sleeper = &default_sleeper;

int schedule(void)
{
	/* while there's still a thread running */
	while (processes.head())
	{
		// check if any thing interesting has happened
		sleeper->check_events( false );

		// other fibers are active... schedule run them
		if (!fiber_t::last_fiber())
		{
			fiber_t::yield();
			continue;
		}

		// there's still processes but no active threads ... sleep
		if (sleeper->check_events( true ))
			break;
	}

	return 0;
}

NTSTATUS create_initial_process( thread_t **t, UNICODE_STRING& us )
{
	BYTE *pstack;
	const unsigned int stack_size = 0x100 * PAGE_SIZE;
	process_t *p = NULL;
	CONTEXT ctx;
	INITIAL_TEB init_teb;
	CLIENT_ID id;
	object_t *section = NULL;
	file_t *file = 0;
	int r;

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		return r;

	/* load the executable and ntdll */
	r = create_section( &section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	release( file );
	if (r < STATUS_SUCCESS)
		return r;

	/* create the initial process */
	r = create_process( &p, section );
	release( section );
	section = NULL;

	if (r < STATUS_SUCCESS)
		return r;

	PPEB ppeb = (PPEB) p->peb_section->get_kernel_address();
	p->create_exe_ppb( &ppeb->ProcessParameters, us );

	/* map the stack */
	pstack = NULL;
	r = p->vm->allocate_virtual_memory( &pstack, 0, stack_size, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE );
	if (r < STATUS_SUCCESS)
		return r;

	/* teb initialization data */
	memset( &init_teb, 0, sizeof init_teb );
	init_teb.StackReserved = pstack;
	init_teb.StackCommit = (BYTE*)init_teb.StackReserved + stack_size;
	init_teb.StackCommitMax = (BYTE*)init_teb.StackCommit - PAGE_SIZE;

	/* initialize the first thread's context */
	p->vm->init_context( ctx );
	ctx.Eip = (DWORD) get_entry_point( p );
	ctx.Esp = (DWORD) pstack + stack_size - 8;

	dprintf("entry point = %08lx\n", ctx.Eip);

	/* when starting nt processes, make the PEB the first arg of NtProcessStartup */
	r = p->vm->copy_to_user( (BYTE*) ctx.Esp + 4, &p->PebBaseAddress, sizeof p->PebBaseAddress );

	if (r == STATUS_SUCCESS)
		r = create_thread( t, p, &id, &ctx, &init_teb, FALSE );

	release( p );

	return r;
}

NTSTATUS init_ntdll( void )
{
	WCHAR ntdll[] = {
		'\\','?','?','\\','c',':','\\','w','i','n','n','t','\\',
		's','y','s','t','e','m','3','2','\\','n','t','d','l','l','.','d','l','l',0 };
	unicode_string_t us;
	file_t *file = 0;
	NTSTATUS r;

	us.set( ntdll );

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		die("failed to open ntdll\n");

	r = create_section( &ntdll_section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	if (r < STATUS_SUCCESS)
		die("failed to map ntdll\n");

	KiIntSystemCall = get_proc_address( ntdll_section, "KiIntSystemCall" );
	dprintf("KiIntSystemCall = %08lx\n", KiIntSystemCall);
	init_syscalls(KiIntSystemCall != 0);

	release( file );

	return r;
}

void free_ntdll( void )
{
	release( ntdll_section );
	ntdll_section = NULL;
}

void do_cleanup( void )
{
	int num_threads = 0, num_processes = 0;

	for ( process_iter_t pi(processes); pi; pi.next() )
	{
		process_t *p = pi;
		if (p->is_signalled())
			continue;
		num_processes++;
		fprintf(stderr, "process %04lx\n", p->id);
		for ( sibling_iter_t ti(p->threads); ti; ti.next() )
		{
			thread_t *t = ti;
			if (t->is_signalled())
				continue;
			fprintf(stderr, "\tthread %04lx\n", t->trace_id());
			num_threads++;
		}
	}
	if (num_threads || num_processes)
		fprintf(stderr, "%d threads %d processes left\n", num_threads, num_processes);
}

static void segv_handler(int)
{
	const int max_frames = 20;
	void *bt[max_frames];
	char **names;
	int n=0, size;
	ULONG id = 0;

	if (current)
		id = current->trace_id();

	size = backtrace(bt, max_frames);
	names = backtrace_symbols(bt, size);

	fprintf(stderr, "%04lx: caught kernel SEGV (%d frames):\n", id, size);
	for (n=0; n<size; n++)
	{
		fprintf(stderr, "%d: %s\n", n, names[n]);
	}
	exit(1);
}

bool init_skas();
bool init_tt( const char *loader_path );

void usage( void )
{
	const char usage[] =
		"Usage: %s [options] [native.exe]\n"
		"Options:\n"
		"  -d,--debug    break into debugger on exceptions\n"
		"  -h,--help     print this message\n"
		"  -q,--quiet    quiet, suppress debug messages\n"
		"  -t,--trace    trace syscall entry and exit\n"
		"  -v,--version  print version\n\n"
		"  smss.exe is started by default\n\n";
	printf( usage, PACKAGE_NAME );
	exit(0);
}

void version( void )
{
	const char version[] = "%s\n"
		"Copyright (C) 2008-2009 Mike McCormack\n"
		"Licence LGPL\n"
		"This is free software: you are free to change and redistribute it.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n\n";
	printf( version, PACKAGE_STRING );
	exit(0);
}

void parse_options(int argc, char **argv)
{
	while (1)
	{
		int option_index;
		static struct option long_options[] = {
			{"debug", 0, &option_debug, 1 },
			{"help", 0, 0, 'h' },
			{"trace", 0, &option_trace, 1 },
			{"version", 0, 0, 'v' },
			{NULL, 0, 0, 0 },
		};

		int ch = getopt_long(argc, argv, "dhtqv?", long_options, &option_index );
		if (ch == -1)
			break;

		switch (ch)
		{
		case 'd':
			option_debug = 1;
			break;
		case '?':
		case 'h':
			usage();
			break;
		case 't':
			option_trace = 1;
			break;
		case 'v':
			version();
		}
	}
}

int main(int argc, char **argv)
{
	unicode_string_t us;
	thread_t *initial_thread = NULL;
	const char *exename;

	parse_options( argc, argv );

	if (optind == argc)
	{
		// default to starting smss.exe
		exename = "\\??\\c:\\winnt\\system32\\smss.exe";
	}
	else
	{
		exename = argv[optind];
	}

	// the skas3 patch is deprecated...
	if (0) init_skas();

	// pass our path so thread tracing can find the client stub
	init_tt( argv[0] );
	if (!pcreate_address_space)
		die("no way to manage address spaces found\n");

	// enable backtraces
	signal(SIGSEGV, segv_handler);

	// initialize boottime
	SYSTEM_TIME_OF_DAY_INFORMATION dummy;
	get_system_time_of_day( dummy );

	init_registry();
	fiber_t::fibers_init();
	init_root();
	create_directory_object( (PWSTR) L"\\" );
	create_directory_object( (PWSTR) L"\\??" );
	unicode_string_t link_name, link_target;
	link_name.set( L"\\DosDevices" );
	link_target.copy( L"\\??" );
	create_symlink( link_name, link_target );
	create_directory_object( (PWSTR) L"\\Device" );
	create_directory_object( (PWSTR) L"\\Device\\MailSlot" );
	create_directory_object( (PWSTR) L"\\Security" );
	//create_directory_object( (PWSTR) L"\\DosDevices" );
	create_directory_object( (PWSTR) L"\\BaseNamedObjects" );
	create_sync_event( (PWSTR) L"\\Security\\LSA_AUTHENTICATION_INITIALIZED" );
	create_sync_event( (PWSTR) L"\\SeLsaInitEvent" );
	init_random();
	init_pipe_device();
	// XP
	create_directory_object( (PWSTR) L"\\KernelObjects" );
	create_sync_event( (PWSTR) L"\\KernelObjects\\CritSecOutOfMemoryEvent" );
	init_drives();
	init_ntdll();
	create_kthread();

	us.copy( exename );

	int r = create_initial_process( &initial_thread, us );
	if (r < STATUS_SUCCESS)
		die("create_initial_process() failed (%08x)\n", r);

	// run the main loop
	schedule();

	ntgdi_fini();
	r = initial_thread->process->ExitStatus;
	//fprintf(stderr, "process exited (%08x)\n", r);
	release( initial_thread );

	shutdown_kthread();
	do_cleanup();

	free_root();
	fiber_t::fibers_finish();
	free_registry();
	free_ntdll();

	return r;
}
<commit_msg>Correct a message<commit_after>/*
 * nt loader
 *
 * Copyright 2006-2008 Mike McCormack
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <fcntl.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/time.h>
#include <poll.h>
#include <signal.h>
#include <execinfo.h>
#include <getopt.h>

#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "windef.h"
#include "winternl.h"

#include "debug.h"
#include "mem.h"
#include "object.h"
#include "objdir.h"
#include "ntcall.h"
#include "section.h"
#include "timer.h"
#include "unicode.h"
#include "fiber.h"
#include "file.h"
#include "event.h"
#include "symlink.h"

process_list_t processes;
thread_t *current;
object_t *ntdll_section;
int option_debug = 0;
ULONG KiIntSystemCall = 0;
bool forced_quit;

class default_sleeper_t : public sleeper_t
{
public:
	virtual bool check_events( bool wait );
	virtual ~default_sleeper_t() {}
};

int sleeper_t::get_int_timeout( LARGE_INTEGER& timeout )
{
	timeout.QuadPart = (timeout.QuadPart+9999)/10000;
	int t = INT_MAX;
	if (timeout.QuadPart < t)
		t = timeout.QuadPart;
	return t;
}

bool default_sleeper_t::check_events( bool wait )
{
	LARGE_INTEGER timeout;

	// check for expired timers
	bool timers_left = timeout_t::check_timers(timeout);

	// Check for a deadlock and quit.
	//  This happens if we're the only active thread,
	//  there's no more timers, and we're asked to wait.
	if (!timers_left && wait && fiber_t::last_fiber())
		return true;
	if (!wait)
		return false;

	int t = get_int_timeout( timeout );
	int r = poll( 0, 0, t );
	if (r >= 0)
		return false;
	if (errno != EINTR)
		die("poll failed %d\n", errno);
	return false;
}

default_sleeper_t default_sleeper;
sleeper_t* sleeper = &default_sleeper;

int schedule(void)
{
	/* while there's still a thread running */
	while (processes.head())
	{
		// check if any thing interesting has happened
		sleeper->check_events( false );

		// other fibers are active... schedule run them
		if (!fiber_t::last_fiber())
		{
			fiber_t::yield();
			continue;
		}

		// there's still processes but no active threads ... sleep
		if (sleeper->check_events( true ))
			break;
	}

	return 0;
}

NTSTATUS create_initial_process( thread_t **t, UNICODE_STRING& us )
{
	BYTE *pstack;
	const unsigned int stack_size = 0x100 * PAGE_SIZE;
	process_t *p = NULL;
	CONTEXT ctx;
	INITIAL_TEB init_teb;
	CLIENT_ID id;
	object_t *section = NULL;
	file_t *file = 0;
	int r;

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		return r;

	/* load the executable and ntdll */
	r = create_section( &section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	release( file );
	if (r < STATUS_SUCCESS)
		return r;

	/* create the initial process */
	r = create_process( &p, section );
	release( section );
	section = NULL;

	if (r < STATUS_SUCCESS)
		return r;

	PPEB ppeb = (PPEB) p->peb_section->get_kernel_address();
	p->create_exe_ppb( &ppeb->ProcessParameters, us );

	/* map the stack */
	pstack = NULL;
	r = p->vm->allocate_virtual_memory( &pstack, 0, stack_size, MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE );
	if (r < STATUS_SUCCESS)
		return r;

	/* teb initialization data */
	memset( &init_teb, 0, sizeof init_teb );
	init_teb.StackReserved = pstack;
	init_teb.StackCommit = (BYTE*)init_teb.StackReserved + stack_size;
	init_teb.StackCommitMax = (BYTE*)init_teb.StackCommit - PAGE_SIZE;

	/* initialize the first thread's context */
	p->vm->init_context( ctx );
	ctx.Eip = (DWORD) get_entry_point( p );
	ctx.Esp = (DWORD) pstack + stack_size - 8;

	dprintf("entry point = %08lx\n", ctx.Eip);

	/* when starting nt processes, make the PEB the first arg of NtProcessStartup */
	r = p->vm->copy_to_user( (BYTE*) ctx.Esp + 4, &p->PebBaseAddress, sizeof p->PebBaseAddress );

	if (r == STATUS_SUCCESS)
		r = create_thread( t, p, &id, &ctx, &init_teb, FALSE );

	release( p );

	return r;
}

NTSTATUS init_ntdll( void )
{
	WCHAR ntdll[] = {
		'\\','?','?','\\','c',':','\\','w','i','n','n','t','\\',
		's','y','s','t','e','m','3','2','\\','n','t','d','l','l','.','d','l','l',0 };
	unicode_string_t us;
	file_t *file = 0;
	NTSTATUS r;

	us.set( ntdll );

	r = open_file( file, us );
	if (r < STATUS_SUCCESS)
		die("failed to open ntdll\n");

	r = create_section( &ntdll_section, file, 0, SEC_IMAGE, PAGE_EXECUTE_READWRITE );
	if (r < STATUS_SUCCESS)
		die("failed to create ntdll section\n");

	KiIntSystemCall = get_proc_address( ntdll_section, "KiIntSystemCall" );
	dprintf("KiIntSystemCall = %08lx\n", KiIntSystemCall);
	init_syscalls(KiIntSystemCall != 0);

	release( file );

	return r;
}

void free_ntdll( void )
{
	release( ntdll_section );
	ntdll_section = NULL;
}

void do_cleanup( void )
{
	int num_threads = 0, num_processes = 0;

	for ( process_iter_t pi(processes); pi; pi.next() )
	{
		process_t *p = pi;
		if (p->is_signalled())
			continue;
		num_processes++;
		fprintf(stderr, "process %04lx\n", p->id);
		for ( sibling_iter_t ti(p->threads); ti; ti.next() )
		{
			thread_t *t = ti;
			if (t->is_signalled())
				continue;
			fprintf(stderr, "\tthread %04lx\n", t->trace_id());
			num_threads++;
		}
	}
	if (num_threads || num_processes)
		fprintf(stderr, "%d threads %d processes left\n", num_threads, num_processes);
}

static void segv_handler(int)
{
	const int max_frames = 20;
	void *bt[max_frames];
	char **names;
	int n=0, size;
	ULONG id = 0;

	if (current)
		id = current->trace_id();

	size = backtrace(bt, max_frames);
	names = backtrace_symbols(bt, size);

	fprintf(stderr, "%04lx: caught kernel SEGV (%d frames):\n", id, size);
	for (n=0; n<size; n++)
	{
		fprintf(stderr, "%d: %s\n", n, names[n]);
	}
	exit(1);
}

bool init_skas();
bool init_tt( const char *loader_path );

void usage( void )
{
	const char usage[] =
		"Usage: %s [options] [native.exe]\n"
		"Options:\n"
		"  -d,--debug    break into debugger on exceptions\n"
		"  -h,--help     print this message\n"
		"  -q,--quiet    quiet, suppress debug messages\n"
		"  -t,--trace    trace syscall entry and exit\n"
		"  -v,--version  print version\n\n"
		"  smss.exe is started by default\n\n";
	printf( usage, PACKAGE_NAME );
	exit(0);
}

void version( void )
{
	const char version[] = "%s\n"
		"Copyright (C) 2008-2009 Mike McCormack\n"
		"Licence LGPL\n"
		"This is free software: you are free to change and redistribute it.\n"
		"There is NO WARRANTY, to the extent permitted by law.\n\n";
	printf( version, PACKAGE_STRING );
	exit(0);
}

void parse_options(int argc, char **argv)
{
	while (1)
	{
		int option_index;
		static struct option long_options[] = {
			{"debug", 0, &option_debug, 1 },
			{"help", 0, 0, 'h' },
			{"trace", 0, &option_trace, 1 },
			{"version", 0, 0, 'v' },
			{NULL, 0, 0, 0 },
		};

		int ch = getopt_long(argc, argv, "dhtqv?", long_options, &option_index );
		if (ch == -1)
			break;

		switch (ch)
		{
		case 'd':
			option_debug = 1;
			break;
		case '?':
		case 'h':
			usage();
			break;
		case 't':
			option_trace = 1;
			break;
		case 'v':
			version();
		}
	}
}

int main(int argc, char **argv)
{
	unicode_string_t us;
	thread_t *initial_thread = NULL;
	const char *exename;

	parse_options( argc, argv );

	if (optind == argc)
	{
		// default to starting smss.exe
		exename = "\\??\\c:\\winnt\\system32\\smss.exe";
	}
	else
	{
		exename = argv[optind];
	}

	// the skas3 patch is deprecated...
	if (0) init_skas();

	// pass our path so thread tracing can find the client stub
	init_tt( argv[0] );
	if (!pcreate_address_space)
		die("no way to manage address spaces found\n");

	// enable backtraces
	signal(SIGSEGV, segv_handler);

	// initialize boottime
	SYSTEM_TIME_OF_DAY_INFORMATION dummy;
	get_system_time_of_day( dummy );

	init_registry();
	fiber_t::fibers_init();
	init_root();
	create_directory_object( (PWSTR) L"\\" );
	create_directory_object( (PWSTR) L"\\??" );
	unicode_string_t link_name, link_target;
	link_name.set( L"\\DosDevices" );
	link_target.copy( L"\\??" );
	create_symlink( link_name, link_target );
	create_directory_object( (PWSTR) L"\\Device" );
	create_directory_object( (PWSTR) L"\\Device\\MailSlot" );
	create_directory_object( (PWSTR) L"\\Security" );
	//create_directory_object( (PWSTR) L"\\DosDevices" );
	create_directory_object( (PWSTR) L"\\BaseNamedObjects" );
	create_sync_event( (PWSTR) L"\\Security\\LSA_AUTHENTICATION_INITIALIZED" );
	create_sync_event( (PWSTR) L"\\SeLsaInitEvent" );
	init_random();
	init_pipe_device();
	// XP
	create_directory_object( (PWSTR) L"\\KernelObjects" );
	create_sync_event( (PWSTR) L"\\KernelObjects\\CritSecOutOfMemoryEvent" );
	init_drives();
	init_ntdll();
	create_kthread();

	us.copy( exename );

	int r = create_initial_process( &initial_thread, us );
	if (r < STATUS_SUCCESS)
		die("create_initial_process() failed (%08x)\n", r);

	// run the main loop
	schedule();

	ntgdi_fini();
	r = initial_thread->process->ExitStatus;
	//fprintf(stderr, "process exited (%08x)\n", r);
	release( initial_thread );

	shutdown_kthread();
	do_cleanup();

	free_root();
	fiber_t::fibers_finish();
	free_registry();
	free_ntdll();

	return r;
}
<|endoftext|>