<commit_before>//===--- ParseDecl.cpp - Swift Language Parser for #if directives -- ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// Conditional Compilation Block Parsing and AST Building
//
//===----------------------------------------------------------------------===//

#include "swift/Parse/Parser.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/Version.h"
#include "swift/Parse/Lexer.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"

using namespace swift;

/// Parse and populate a list of #if/#elseif/#else/#endif clauses.
/// Delegate callback function to parse elements in the blocks.
template <typename ElemTy, unsigned N>
static ParserStatus parseIfConfig(
    Parser &P, SmallVectorImpl<IfConfigClause<ElemTy>> &Clauses,
    SourceLoc &EndLoc, bool HadMissingEnd,
    llvm::function_ref<void(SmallVectorImpl<ElemTy> &, bool)> parseElements) {
  Parser::StructureMarkerRAII ParsingDecl(
      P, P.Tok.getLoc(), Parser::StructureMarkerKind::IfConfig);

  bool foundActive = false;
  ConditionalCompilationExprState ConfigState;
  while (1) {
    bool isElse = P.Tok.is(tok::pound_else);
    SourceLoc ClauseLoc = P.consumeToken();
    Expr *Condition = nullptr;

    // Parse and evaluate the directive.
    if (isElse) {
      ConfigState.setConditionActive(!foundActive);
    } else {
      llvm::SaveAndRestore<bool> S(P.InPoundIfEnvironment, true);
      ParserResult<Expr> Result = P.parseExprSequence(diag::expected_expr,
                                                      /*isBasic*/true,
                                                      /*isForDirective*/true);
      if (Result.isNull())
        return makeParserError();

      Condition = Result.get();

      // Evaluate the condition, to validate it.
      ConfigState = P.classifyConditionalCompilationExpr(Condition, P.Context,
                                                         P.Diags);
      if (foundActive)
        ConfigState.setConditionActive(false);
    }

    foundActive |= ConfigState.isConditionActive();

    if (!P.Tok.isAtStartOfLine() && P.Tok.isNot(tok::eof)) {
      P.diagnose(P.Tok.getLoc(),
                 diag::extra_tokens_conditional_compilation_directive);
    }

    // Parse elements
    SmallVector<ElemTy, N> Elements;
    if (ConfigState.shouldParse()) {
      parseElements(Elements, ConfigState.isConditionActive());
    } else {
      DiagnosticTransaction DT(P.Diags);
      P.skipUntilConditionalBlockClose();
      DT.abort();
    }

    Clauses.push_back(IfConfigClause<ElemTy>(ClauseLoc, Condition,
                                             P.Context.AllocateCopy(Elements),
                                             ConfigState.isConditionActive()));

    if (P.Tok.isNot(tok::pound_elseif, tok::pound_else))
      break;

    if (isElse)
      P.diagnose(P.Tok, diag::expected_close_after_else_directive);
  }

  HadMissingEnd = P.parseEndIfDirective(EndLoc);
  return makeParserSuccess();
}

ParserResult<IfConfigDecl> Parser::parseDeclIfConfig(ParseDeclOptions Flags) {
  SmallVector<IfConfigClause<Decl *>, 4> Clauses;
  SourceLoc EndLoc;
  bool HadMissingEnd = false;
  auto Status = parseIfConfig<Decl *, 8>(
      *this, Clauses, EndLoc, HadMissingEnd,
      [&](SmallVectorImpl<Decl *> &Decls, bool IsActive) {
    Optional<Scope> scope;
    if (!IsActive)
      scope.emplace(this, getScopeInfo().getCurrentScope()->getKind(),
                    /*inactiveConfigBlock=*/true);

    ParserStatus Status;
    bool PreviousHadSemi = true;
    while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif,
                      tok::eof)) {
      if (Tok.is(tok::r_brace)) {
        diagnose(Tok.getLoc(),
                  diag::unexpected_rbrace_in_conditional_compilation_block);
        // If we see '}', following declarations don't look like belong to
        // the current decl context; skip them.
        skipUntilConditionalBlockClose();
        break;
      }
      Status |= parseDeclItem(PreviousHadSemi, Flags,
                              [&](Decl *D) {Decls.push_back(D);});
    }
  });
  if (Status.isError())
    return makeParserErrorResult<IfConfigDecl>();

  IfConfigDecl *ICD = new (Context) IfConfigDecl(CurDeclContext,
                                                 Context.AllocateCopy(Clauses),
                                                 EndLoc, HadMissingEnd);
  return makeParserResult(ICD);
}

ParserResult<Stmt> Parser::parseStmtIfConfig(BraceItemListKind Kind) {
  SmallVector<IfConfigClause<ASTNode>, 4> Clauses;
  SourceLoc EndLoc;
  bool HadMissingEnd = false;
  auto Status = parseIfConfig<ASTNode, 16>(
      *this, Clauses, EndLoc, HadMissingEnd,
      [&](SmallVectorImpl<ASTNode> &Elements, bool IsActive) {
    parseBraceItems(Elements, Kind, IsActive
                      ? BraceItemListKind::ActiveConditionalBlock
                      : BraceItemListKind::InactiveConditionalBlock);
  });
  if (Status.isError())
    return makeParserErrorResult<Stmt>();

  auto *ICS = new (Context) IfConfigStmt(Context.AllocateCopy(Clauses),
                                         EndLoc, HadMissingEnd);
  return makeParserResult(ICS);
}

// Evaluate a subset of expression types suitable for build configuration
// conditional expressions.  The accepted expression types are:
//  - The magic constants "true" and "false".
//  - Named decl ref expressions ("FOO")
//  - Parenthesized expressions ("(FOO)")
//  - Binary "&&" or "||" operations applied to other build configuration
//    conditional expressions
//  - Unary "!" expressions applied to other build configuration conditional
//    expressions
//  - Single-argument call expressions, where the function being invoked is a
//    supported target configuration (currently "os", "arch", and
//    "_compiler_version"), and whose argument is a named decl ref expression
ConditionalCompilationExprState
Parser::classifyConditionalCompilationExpr(Expr *condition,
                                           ASTContext &Context,
                                           DiagnosticEngine &D) {
  assert(condition && "Cannot classify a NULL condition expression!");

  // Evaluate a ParenExpr.
  if (auto *PE = dyn_cast<ParenExpr>(condition))
    return classifyConditionalCompilationExpr(PE->getSubExpr(), Context, D);

  // Evaluate a "&&" or "||" expression.
  if (auto *SE = dyn_cast<SequenceExpr>(condition)) {
    // Check for '&&' or '||' as the expression type.
    if (SE->getNumElements() < 3) {
      D.diagnose(SE->getLoc(),
                 diag::unsupported_conditional_compilation_binary_expression);
      return ConditionalCompilationExprState::error();
    }
    // Before type checking, chains of binary expressions will not be fully
    // parsed, so associativity has not yet been encoded in the subtree.
    auto elements = SE->getElements();
    auto numElements = SE->getNumElements();
    size_t iOperator = 1;
    size_t iOperand = 2;

    auto result = classifyConditionalCompilationExpr(elements[0], Context, D);

    while (iOperand < numElements) {

      if (auto *UDREOp = dyn_cast<UnresolvedDeclRefExpr>(elements[iOperator])) {
        auto name = UDREOp->getName().getBaseName().str();

        if (name.equals("||") || name.equals("&&")) {
          auto rhs = classifyConditionalCompilationExpr(elements[iOperand],
                                                        Context, D);

          if (name.equals("||")) {
            result = result || rhs;
            if (result.isConditionActive())
              break;
          }

          if (name.equals("&&")) {
            result = result && rhs;
            if (!result.isConditionActive())
              break;
          }
        } else {
          D.diagnose(
              SE->getLoc(),
              diag::unsupported_conditional_compilation_binary_expression);
          return ConditionalCompilationExprState::error();
        }
      } else {
        // Swift3 didn't have this branch. the operator and the RHS are
        // silently ignored.
        if (!Context.isSwiftVersion3()) {
          D.diagnose(
              elements[iOperator]->getLoc(),
              diag::unsupported_conditional_compilation_expression_type);
          return ConditionalCompilationExprState::error();
        } else {
          SourceRange ignoredRange(elements[iOperator]->getLoc(),
                                   elements[iOperand]->getEndLoc());
          D.diagnose(
              elements[iOperator]->getLoc(),
              diag::swift3_unsupported_conditional_compilation_expression_type)
            .highlight(ignoredRange);
        }
      }

      iOperator += 2;
      iOperand += 2;
    }

    return result;
  }

  // Evaluate a named reference expression.
  if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(condition)) {
    auto name = UDRE->getName().getBaseName().str();
    return {Context.LangOpts.isCustomConditionalCompilationFlagSet(name),
      ConditionalCompilationExprKind::DeclRef};
  }

  // Evaluate a Boolean literal.
  if (auto *boolLit = dyn_cast<BooleanLiteralExpr>(condition)) {
    return {boolLit->getValue(), ConditionalCompilationExprKind::Boolean};
  }

  // Evaluate a negation (unary "!") expression.
  if (auto *PUE = dyn_cast<PrefixUnaryExpr>(condition)) {
    // If the PUE is not a negation expression, return false
    auto name =
    cast<UnresolvedDeclRefExpr>(PUE->getFn())->getName().getBaseName().str();
    if (name != "!") {
      D.diagnose(PUE->getLoc(),
                 diag::unsupported_conditional_compilation_unary_expression);
      return ConditionalCompilationExprState::error();
    }

    return !classifyConditionalCompilationExpr(PUE->getArg(), Context, D);
  }

  // Evaluate a target config call expression.
  if (auto *CE = dyn_cast<CallExpr>(condition)) {
    // look up target config, and compare value
    auto fnNameExpr = dyn_cast<UnresolvedDeclRefExpr>(CE->getFn());

    // Get the arg, which should be in a paren expression.
    if (!fnNameExpr) {
      D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_expression);
      return ConditionalCompilationExprState::error();
    }

    auto fnName = fnNameExpr->getName().getBaseName().str();

    auto *PE = dyn_cast<ParenExpr>(CE->getArg());
    if (!PE) {
      auto diag = D.diagnose(CE->getLoc(),
                             diag::platform_condition_expected_one_argument);
      return ConditionalCompilationExprState::error();
    }

    if (!fnName.equals("arch") && !fnName.equals("os") &&
        !fnName.equals("_endian") &&
        !fnName.equals("_runtime") &&
        !fnName.equals("swift") &&
        !fnName.equals("_compiler_version")) {
      D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_expression);
      return ConditionalCompilationExprState::error();
    }

    if (fnName.equals("_compiler_version")) {
      if (auto SLE = dyn_cast<StringLiteralExpr>(PE->getSubExpr())) {
        if (SLE->getValue().empty()) {
          D.diagnose(CE->getLoc(), diag::empty_version_string);
          return ConditionalCompilationExprState::error();
        }
        auto versionRequirement =
        version::Version::parseCompilerVersionString(SLE->getValue(),
                                                     SLE->getLoc(),
                                                     &D);
        auto thisVersion = version::Version::getCurrentCompilerVersion();
        auto VersionNewEnough = thisVersion >= versionRequirement;
        return {VersionNewEnough,
          ConditionalCompilationExprKind::CompilerVersion};
      } else {
        D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_argument,
                 "string literal");
        return ConditionalCompilationExprState::error();
      }
    } else if (fnName.equals("swift")) {
      auto PUE = dyn_cast<PrefixUnaryExpr>(PE->getSubExpr());
      if (!PUE) {
        D.diagnose(PE->getSubExpr()->getLoc(),
                   diag::unsupported_platform_condition_argument,
                   "a unary comparison, such as '>=2.2'");
        return ConditionalCompilationExprState::error();
      }

      auto prefix = dyn_cast<UnresolvedDeclRefExpr>(PUE->getFn());
      auto versionArg = PUE->getArg();
      auto versionStartLoc = versionArg->getStartLoc();
      auto endLoc = Lexer::getLocForEndOfToken(Context.SourceMgr,
                                               versionArg->getSourceRange().End);
      CharSourceRange versionCharRange(Context.SourceMgr, versionStartLoc,
                                       endLoc);
      auto versionString = Context.SourceMgr.extractText(versionCharRange);

      auto versionRequirement =
        version::Version::parseVersionString(versionString,
                                             versionStartLoc,
                                             &D);

      if (!versionRequirement.hasValue())
        return ConditionalCompilationExprState::error();

      auto thisVersion = Context.LangOpts.EffectiveLanguageVersion;

      if (!prefix->getName().getBaseName().str().equals(">=")) {
        D.diagnose(PUE->getFn()->getLoc(),
                   diag::unexpected_version_comparison_operator)
          .fixItReplace(PUE->getFn()->getLoc(), ">=");
        return ConditionalCompilationExprState::error();
      }

      auto VersionNewEnough = thisVersion >= versionRequirement.getValue();
      return {VersionNewEnough,
        ConditionalCompilationExprKind::LanguageVersion};
    } else {
      if (auto UDRE = dyn_cast<UnresolvedDeclRefExpr>(PE->getSubExpr())) {
        // The sub expression should be an UnresolvedDeclRefExpr (we won't
        // tolerate extra parens).
        auto argumentIdent = UDRE->getName().getBaseName();
        auto argument = argumentIdent.str();
        PlatformConditionKind Kind =
          llvm::StringSwitch<PlatformConditionKind>(fnName)
          .Case("os", PlatformConditionKind::OS)
          .Case("arch", PlatformConditionKind::Arch)
          .Case("_endian", PlatformConditionKind::Endianness)
          .Case("_runtime", PlatformConditionKind::Runtime);

        // FIXME: Perform the replacement macOS -> OSX elsewhere.
        if (Kind == PlatformConditionKind::OS && argument == "macOS")
          argument = "OSX";

        std::vector<StringRef> suggestions;
        if (!LangOptions::checkPlatformConditionSupported(Kind, argument,
                                                          suggestions)) {
          if (Kind == PlatformConditionKind::Runtime) {
            // Error for _runtime()
            D.diagnose(UDRE->getLoc(),
                       diag::unsupported_platform_runtime_condition_argument);
            return ConditionalCompilationExprState::error();
          }
          StringRef DiagName;
          switch (Kind) {
          case PlatformConditionKind::OS:
            DiagName = "operating system"; break;
          case PlatformConditionKind::Arch:
            DiagName = "architecture"; break;
          case PlatformConditionKind::Endianness:
            DiagName = "endianness"; break;
          case PlatformConditionKind::Runtime:
            llvm_unreachable("handled above");
          }
          D.diagnose(UDRE->getLoc(), diag::unknown_platform_condition_argument,
                     DiagName, fnName);
          for (const StringRef &suggestion : suggestions) {
            D.diagnose(UDRE->getLoc(), diag::note_typo_candidate, suggestion)
              .fixItReplace(UDRE->getSourceRange(), suggestion);
          }
        }

        auto target = Context.LangOpts.getPlatformConditionValue(Kind);
        return {target == argument, ConditionalCompilationExprKind::DeclRef};
      } else {
        D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_argument,
                   "identifier");
        return ConditionalCompilationExprState::error();
      }
    }
  }

  // "#if 0" isn't valid, but it is common, so recognize it and handle it
  // with a fixit elegantly.
  if (auto *IL = dyn_cast<IntegerLiteralExpr>(condition))
    if (IL->getDigitsText() == "0" || IL->getDigitsText() == "1") {
      StringRef replacement = IL->getDigitsText() == "0" ? "false" :"true";
      D.diagnose(IL->getLoc(), diag::unsupported_conditional_compilation_integer,
                 IL->getDigitsText(), replacement)
       .fixItReplace(IL->getLoc(), replacement);
      return {IL->getDigitsText() == "1",
        ConditionalCompilationExprKind::Integer};
    }

  // If we've gotten here, it's an unsupported expression type.
  D.diagnose(condition->getLoc(),
             diag::unsupported_conditional_compilation_expression_type);
  return ConditionalCompilationExprState::error();
}
<commit_msg>[gardening] Fix incorrect file name in header<commit_after>//===--- ParseIfConfig.cpp - Swift Language Parser for #if directives -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// Conditional Compilation Block Parsing and AST Building
//
//===----------------------------------------------------------------------===//

#include "swift/Parse/Parser.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/Version.h"
#include "swift/Parse/Lexer.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/SaveAndRestore.h"

using namespace swift;

/// Parse and populate a list of #if/#elseif/#else/#endif clauses.
/// Delegate callback function to parse elements in the blocks.
template <typename ElemTy, unsigned N>
static ParserStatus parseIfConfig(
    Parser &P, SmallVectorImpl<IfConfigClause<ElemTy>> &Clauses,
    SourceLoc &EndLoc, bool HadMissingEnd,
    llvm::function_ref<void(SmallVectorImpl<ElemTy> &, bool)> parseElements) {
  Parser::StructureMarkerRAII ParsingDecl(
      P, P.Tok.getLoc(), Parser::StructureMarkerKind::IfConfig);

  bool foundActive = false;
  ConditionalCompilationExprState ConfigState;
  while (1) {
    bool isElse = P.Tok.is(tok::pound_else);
    SourceLoc ClauseLoc = P.consumeToken();
    Expr *Condition = nullptr;

    // Parse and evaluate the directive.
    if (isElse) {
      ConfigState.setConditionActive(!foundActive);
    } else {
      llvm::SaveAndRestore<bool> S(P.InPoundIfEnvironment, true);
      ParserResult<Expr> Result = P.parseExprSequence(diag::expected_expr,
                                                      /*isBasic*/true,
                                                      /*isForDirective*/true);
      if (Result.isNull())
        return makeParserError();

      Condition = Result.get();

      // Evaluate the condition, to validate it.
      ConfigState = P.classifyConditionalCompilationExpr(Condition, P.Context,
                                                         P.Diags);
      if (foundActive)
        ConfigState.setConditionActive(false);
    }

    foundActive |= ConfigState.isConditionActive();

    if (!P.Tok.isAtStartOfLine() && P.Tok.isNot(tok::eof)) {
      P.diagnose(P.Tok.getLoc(),
                 diag::extra_tokens_conditional_compilation_directive);
    }

    // Parse elements
    SmallVector<ElemTy, N> Elements;
    if (ConfigState.shouldParse()) {
      parseElements(Elements, ConfigState.isConditionActive());
    } else {
      DiagnosticTransaction DT(P.Diags);
      P.skipUntilConditionalBlockClose();
      DT.abort();
    }

    Clauses.push_back(IfConfigClause<ElemTy>(ClauseLoc, Condition,
                                             P.Context.AllocateCopy(Elements),
                                             ConfigState.isConditionActive()));

    if (P.Tok.isNot(tok::pound_elseif, tok::pound_else))
      break;

    if (isElse)
      P.diagnose(P.Tok, diag::expected_close_after_else_directive);
  }

  HadMissingEnd = P.parseEndIfDirective(EndLoc);
  return makeParserSuccess();
}

ParserResult<IfConfigDecl> Parser::parseDeclIfConfig(ParseDeclOptions Flags) {
  SmallVector<IfConfigClause<Decl *>, 4> Clauses;
  SourceLoc EndLoc;
  bool HadMissingEnd = false;
  auto Status = parseIfConfig<Decl *, 8>(
      *this, Clauses, EndLoc, HadMissingEnd,
      [&](SmallVectorImpl<Decl *> &Decls, bool IsActive) {
    Optional<Scope> scope;
    if (!IsActive)
      scope.emplace(this, getScopeInfo().getCurrentScope()->getKind(),
                    /*inactiveConfigBlock=*/true);

    ParserStatus Status;
    bool PreviousHadSemi = true;
    while (Tok.isNot(tok::pound_else, tok::pound_endif, tok::pound_elseif,
                      tok::eof)) {
      if (Tok.is(tok::r_brace)) {
        diagnose(Tok.getLoc(),
                  diag::unexpected_rbrace_in_conditional_compilation_block);
        // If we see '}', following declarations don't look like belong to
        // the current decl context; skip them.
        skipUntilConditionalBlockClose();
        break;
      }
      Status |= parseDeclItem(PreviousHadSemi, Flags,
                              [&](Decl *D) {Decls.push_back(D);});
    }
  });
  if (Status.isError())
    return makeParserErrorResult<IfConfigDecl>();

  IfConfigDecl *ICD = new (Context) IfConfigDecl(CurDeclContext,
                                                 Context.AllocateCopy(Clauses),
                                                 EndLoc, HadMissingEnd);
  return makeParserResult(ICD);
}

ParserResult<Stmt> Parser::parseStmtIfConfig(BraceItemListKind Kind) {
  SmallVector<IfConfigClause<ASTNode>, 4> Clauses;
  SourceLoc EndLoc;
  bool HadMissingEnd = false;
  auto Status = parseIfConfig<ASTNode, 16>(
      *this, Clauses, EndLoc, HadMissingEnd,
      [&](SmallVectorImpl<ASTNode> &Elements, bool IsActive) {
    parseBraceItems(Elements, Kind, IsActive
                      ? BraceItemListKind::ActiveConditionalBlock
                      : BraceItemListKind::InactiveConditionalBlock);
  });
  if (Status.isError())
    return makeParserErrorResult<Stmt>();

  auto *ICS = new (Context) IfConfigStmt(Context.AllocateCopy(Clauses),
                                         EndLoc, HadMissingEnd);
  return makeParserResult(ICS);
}

// Evaluate a subset of expression types suitable for build configuration
// conditional expressions.  The accepted expression types are:
//  - The magic constants "true" and "false".
//  - Named decl ref expressions ("FOO")
//  - Parenthesized expressions ("(FOO)")
//  - Binary "&&" or "||" operations applied to other build configuration
//    conditional expressions
//  - Unary "!" expressions applied to other build configuration conditional
//    expressions
//  - Single-argument call expressions, where the function being invoked is a
//    supported target configuration (currently "os", "arch", and
//    "_compiler_version"), and whose argument is a named decl ref expression
ConditionalCompilationExprState
Parser::classifyConditionalCompilationExpr(Expr *condition,
                                           ASTContext &Context,
                                           DiagnosticEngine &D) {
  assert(condition && "Cannot classify a NULL condition expression!");

  // Evaluate a ParenExpr.
  if (auto *PE = dyn_cast<ParenExpr>(condition))
    return classifyConditionalCompilationExpr(PE->getSubExpr(), Context, D);

  // Evaluate a "&&" or "||" expression.
  if (auto *SE = dyn_cast<SequenceExpr>(condition)) {
    // Check for '&&' or '||' as the expression type.
    if (SE->getNumElements() < 3) {
      D.diagnose(SE->getLoc(),
                 diag::unsupported_conditional_compilation_binary_expression);
      return ConditionalCompilationExprState::error();
    }
    // Before type checking, chains of binary expressions will not be fully
    // parsed, so associativity has not yet been encoded in the subtree.
    auto elements = SE->getElements();
    auto numElements = SE->getNumElements();
    size_t iOperator = 1;
    size_t iOperand = 2;

    auto result = classifyConditionalCompilationExpr(elements[0], Context, D);

    while (iOperand < numElements) {

      if (auto *UDREOp = dyn_cast<UnresolvedDeclRefExpr>(elements[iOperator])) {
        auto name = UDREOp->getName().getBaseName().str();

        if (name.equals("||") || name.equals("&&")) {
          auto rhs = classifyConditionalCompilationExpr(elements[iOperand],
                                                        Context, D);

          if (name.equals("||")) {
            result = result || rhs;
            if (result.isConditionActive())
              break;
          }

          if (name.equals("&&")) {
            result = result && rhs;
            if (!result.isConditionActive())
              break;
          }
        } else {
          D.diagnose(
              SE->getLoc(),
              diag::unsupported_conditional_compilation_binary_expression);
          return ConditionalCompilationExprState::error();
        }
      } else {
        // Swift3 didn't have this branch. the operator and the RHS are
        // silently ignored.
        if (!Context.isSwiftVersion3()) {
          D.diagnose(
              elements[iOperator]->getLoc(),
              diag::unsupported_conditional_compilation_expression_type);
          return ConditionalCompilationExprState::error();
        } else {
          SourceRange ignoredRange(elements[iOperator]->getLoc(),
                                   elements[iOperand]->getEndLoc());
          D.diagnose(
              elements[iOperator]->getLoc(),
              diag::swift3_unsupported_conditional_compilation_expression_type)
            .highlight(ignoredRange);
        }
      }

      iOperator += 2;
      iOperand += 2;
    }

    return result;
  }

  // Evaluate a named reference expression.
  if (auto *UDRE = dyn_cast<UnresolvedDeclRefExpr>(condition)) {
    auto name = UDRE->getName().getBaseName().str();
    return {Context.LangOpts.isCustomConditionalCompilationFlagSet(name),
      ConditionalCompilationExprKind::DeclRef};
  }

  // Evaluate a Boolean literal.
  if (auto *boolLit = dyn_cast<BooleanLiteralExpr>(condition)) {
    return {boolLit->getValue(), ConditionalCompilationExprKind::Boolean};
  }

  // Evaluate a negation (unary "!") expression.
  if (auto *PUE = dyn_cast<PrefixUnaryExpr>(condition)) {
    // If the PUE is not a negation expression, return false
    auto name =
    cast<UnresolvedDeclRefExpr>(PUE->getFn())->getName().getBaseName().str();
    if (name != "!") {
      D.diagnose(PUE->getLoc(),
                 diag::unsupported_conditional_compilation_unary_expression);
      return ConditionalCompilationExprState::error();
    }

    return !classifyConditionalCompilationExpr(PUE->getArg(), Context, D);
  }

  // Evaluate a target config call expression.
  if (auto *CE = dyn_cast<CallExpr>(condition)) {
    // look up target config, and compare value
    auto fnNameExpr = dyn_cast<UnresolvedDeclRefExpr>(CE->getFn());

    // Get the arg, which should be in a paren expression.
    if (!fnNameExpr) {
      D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_expression);
      return ConditionalCompilationExprState::error();
    }

    auto fnName = fnNameExpr->getName().getBaseName().str();

    auto *PE = dyn_cast<ParenExpr>(CE->getArg());
    if (!PE) {
      auto diag = D.diagnose(CE->getLoc(),
                             diag::platform_condition_expected_one_argument);
      return ConditionalCompilationExprState::error();
    }

    if (!fnName.equals("arch") && !fnName.equals("os") &&
        !fnName.equals("_endian") &&
        !fnName.equals("_runtime") &&
        !fnName.equals("swift") &&
        !fnName.equals("_compiler_version")) {
      D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_expression);
      return ConditionalCompilationExprState::error();
    }

    if (fnName.equals("_compiler_version")) {
      if (auto SLE = dyn_cast<StringLiteralExpr>(PE->getSubExpr())) {
        if (SLE->getValue().empty()) {
          D.diagnose(CE->getLoc(), diag::empty_version_string);
          return ConditionalCompilationExprState::error();
        }
        auto versionRequirement =
        version::Version::parseCompilerVersionString(SLE->getValue(),
                                                     SLE->getLoc(),
                                                     &D);
        auto thisVersion = version::Version::getCurrentCompilerVersion();
        auto VersionNewEnough = thisVersion >= versionRequirement;
        return {VersionNewEnough,
          ConditionalCompilationExprKind::CompilerVersion};
      } else {
        D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_argument,
                 "string literal");
        return ConditionalCompilationExprState::error();
      }
    } else if (fnName.equals("swift")) {
      auto PUE = dyn_cast<PrefixUnaryExpr>(PE->getSubExpr());
      if (!PUE) {
        D.diagnose(PE->getSubExpr()->getLoc(),
                   diag::unsupported_platform_condition_argument,
                   "a unary comparison, such as '>=2.2'");
        return ConditionalCompilationExprState::error();
      }

      auto prefix = dyn_cast<UnresolvedDeclRefExpr>(PUE->getFn());
      auto versionArg = PUE->getArg();
      auto versionStartLoc = versionArg->getStartLoc();
      auto endLoc = Lexer::getLocForEndOfToken(Context.SourceMgr,
                                               versionArg->getSourceRange().End);
      CharSourceRange versionCharRange(Context.SourceMgr, versionStartLoc,
                                       endLoc);
      auto versionString = Context.SourceMgr.extractText(versionCharRange);

      auto versionRequirement =
        version::Version::parseVersionString(versionString,
                                             versionStartLoc,
                                             &D);

      if (!versionRequirement.hasValue())
        return ConditionalCompilationExprState::error();

      auto thisVersion = Context.LangOpts.EffectiveLanguageVersion;

      if (!prefix->getName().getBaseName().str().equals(">=")) {
        D.diagnose(PUE->getFn()->getLoc(),
                   diag::unexpected_version_comparison_operator)
          .fixItReplace(PUE->getFn()->getLoc(), ">=");
        return ConditionalCompilationExprState::error();
      }

      auto VersionNewEnough = thisVersion >= versionRequirement.getValue();
      return {VersionNewEnough,
        ConditionalCompilationExprKind::LanguageVersion};
    } else {
      if (auto UDRE = dyn_cast<UnresolvedDeclRefExpr>(PE->getSubExpr())) {
        // The sub expression should be an UnresolvedDeclRefExpr (we won't
        // tolerate extra parens).
        auto argumentIdent = UDRE->getName().getBaseName();
        auto argument = argumentIdent.str();
        PlatformConditionKind Kind =
          llvm::StringSwitch<PlatformConditionKind>(fnName)
          .Case("os", PlatformConditionKind::OS)
          .Case("arch", PlatformConditionKind::Arch)
          .Case("_endian", PlatformConditionKind::Endianness)
          .Case("_runtime", PlatformConditionKind::Runtime);

        // FIXME: Perform the replacement macOS -> OSX elsewhere.
        if (Kind == PlatformConditionKind::OS && argument == "macOS")
          argument = "OSX";

        std::vector<StringRef> suggestions;
        if (!LangOptions::checkPlatformConditionSupported(Kind, argument,
                                                          suggestions)) {
          if (Kind == PlatformConditionKind::Runtime) {
            // Error for _runtime()
            D.diagnose(UDRE->getLoc(),
                       diag::unsupported_platform_runtime_condition_argument);
            return ConditionalCompilationExprState::error();
          }
          StringRef DiagName;
          switch (Kind) {
          case PlatformConditionKind::OS:
            DiagName = "operating system"; break;
          case PlatformConditionKind::Arch:
            DiagName = "architecture"; break;
          case PlatformConditionKind::Endianness:
            DiagName = "endianness"; break;
          case PlatformConditionKind::Runtime:
            llvm_unreachable("handled above");
          }
          D.diagnose(UDRE->getLoc(), diag::unknown_platform_condition_argument,
                     DiagName, fnName);
          for (const StringRef &suggestion : suggestions) {
            D.diagnose(UDRE->getLoc(), diag::note_typo_candidate, suggestion)
              .fixItReplace(UDRE->getSourceRange(), suggestion);
          }
        }

        auto target = Context.LangOpts.getPlatformConditionValue(Kind);
        return {target == argument, ConditionalCompilationExprKind::DeclRef};
      } else {
        D.diagnose(CE->getLoc(), diag::unsupported_platform_condition_argument,
                   "identifier");
        return ConditionalCompilationExprState::error();
      }
    }
  }

  // "#if 0" isn't valid, but it is common, so recognize it and handle it
  // with a fixit elegantly.
  if (auto *IL = dyn_cast<IntegerLiteralExpr>(condition))
    if (IL->getDigitsText() == "0" || IL->getDigitsText() == "1") {
      StringRef replacement = IL->getDigitsText() == "0" ? "false" :"true";
      D.diagnose(IL->getLoc(), diag::unsupported_conditional_compilation_integer,
                 IL->getDigitsText(), replacement)
       .fixItReplace(IL->getLoc(), replacement);
      return {IL->getDigitsText() == "1",
        ConditionalCompilationExprKind::Integer};
    }

  // If we've gotten here, it's an unsupported expression type.
  D.diagnose(condition->getLoc(),
             diag::unsupported_conditional_compilation_expression_type);
  return ConditionalCompilationExprState::error();
}
<|endoftext|>


<commit_before>//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the HTMLRewriter clas, which is used to translate the
//  text of a source file into prettified HTML.
//
//===----------------------------------------------------------------------===//

#include "clang/Rewrite/Rewriter.h"
#include "clang/Rewrite/HTMLRewrite.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/MemoryBuffer.h"
#include <sstream>

using namespace clang;

void html::EscapeText(Rewriter& R, unsigned FileID, bool EscapeSpaces) {

  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
  const char* C = Buf->getBufferStart();
  const char* FileEnd = Buf->getBufferEnd();

  assert (C <= FileEnd);

  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {

    switch (*C) {
      default: break;

      case ' ':
        if (EscapeSpaces) {
          SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
          R.ReplaceText(Loc, 1, " ", 6);
        }
        break;

      case '\t': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, "    ", 6*4);
        break;
      }

      case '<': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, "<", 4);
        break;
      }

      case '>': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, ">", 4);
        break;
      }

      case '&': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, "&", 5);
        break;
      }
    }
  }
}

std::string html::EscapeText(const std::string& s, bool EscapeSpaces) {

  unsigned len = s.size();
  std::ostringstream os;

  for (unsigned i = 0 ; i < len; ++i) {

    char c = s[i];

    switch (c) {
      default:
        os << c; break;

      case ' ':
        if (EscapeSpaces) os << " ";
        else os << ' ';
        break;

        case '\t': for (unsigned i = 0; i < 4; ++i) os << " "; break;
        case '<': os << "<"; break;
        case '>': os << ">"; break;
        case '&': os << "&"; break;
    }
  }

  return os.str();
}

static void AddLineNumber(Rewriter& R, unsigned LineNo,
                          SourceLocation B, SourceLocation E) {

  std::ostringstream os;
  os << "
<td class=\"num\" id=\"LN" << LineNo << "\">"
     << LineNo << "	";

  if (B == E) { // Handle empty lines.
    os << "
";
    R.InsertStrBefore(B, os.str());
  }
  else {
    R.InsertStrBefore(B, os.str());
    R.InsertCStrBefore(E, "
");
  }
}

void html::AddLineNumbers(Rewriter& R, unsigned FileID) {

  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
  const char* FileBeg = Buf->getBufferStart();
  const char* FileEnd = Buf->getBufferEnd();
  const char* C = FileBeg;

  assert (C <= FileEnd);

  unsigned LineNo = 0;
  unsigned FilePos = 0;

  while (C != FileEnd) {

    ++LineNo;
    unsigned LineStartPos = FilePos;
    unsigned LineEndPos = FileEnd - FileBeg;

    assert (FilePos <= LineEndPos);
    assert (C < FileEnd);

    // Scan until the newline (or end-of-file).

    while (C != FileEnd) {
      char c = *C;
      ++C;

      if (c == '\n') {
        LineEndPos = FilePos++;
        break;
      }

      ++FilePos;
    }

    AddLineNumber(R, LineNo,
                  SourceLocation::getFileLoc(FileID, LineStartPos),
                  SourceLocation::getFileLoc(FileID, LineEndPos));
  }

  // Add one big div tag that surrounds all of the code.

  R.InsertCStrBefore(SourceLocation::getFileLoc(FileID, 0),
                     "
\n");

  R.InsertCStrAfter(SourceLocation::getFileLoc(FileID, FileEnd - FileBeg),
                    "
");
}

void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, unsigned FileID) {

  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
  const char* FileStart = Buf->getBufferStart();
  const char* FileEnd = Buf->getBufferEnd();

  SourceLocation StartLoc = SourceLocation::getFileLoc(FileID, 0);
  SourceLocation EndLoc = SourceLocation::getFileLoc(FileID, FileEnd-FileStart);

  // Generate header

  {
    std::ostringstream os;

    os << "\n\n"
       << "\n\n";

    R.InsertStrBefore(StartLoc, os.str());
  }

  // Generate footer

  {
    std::ostringstream os;

    os << "\n";
    R.InsertStrAfter(EndLoc, os.str());
  }
}

<commit_msg>When substituting tabs during HTMLification, only add " " when we are "escaping" spaces.<commit_after>//== HTMLRewrite.cpp - Translate source code into prettified HTML --*- C++ -*-//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the HTMLRewriter clas, which is used to translate the
//  text of a source file into prettified HTML.
//
//===----------------------------------------------------------------------===//

#include "clang/Rewrite/Rewriter.h"
#include "clang/Rewrite/HTMLRewrite.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/MemoryBuffer.h"
#include <sstream>

using namespace clang;

void html::EscapeText(Rewriter& R, unsigned FileID, bool EscapeSpaces) {

  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
  const char* C = Buf->getBufferStart();
  const char* FileEnd = Buf->getBufferEnd();

  assert (C <= FileEnd);

  for (unsigned FilePos = 0; C != FileEnd ; ++C, ++FilePos) {

    switch (*C) {
      default: break;

      case ' ':
        if (EscapeSpaces) {
          SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
          R.ReplaceText(Loc, 1, " ", 6);
        }
        break;

      case '\t': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);

        if (EscapeSpaces)
          R.ReplaceText(Loc, 1, "    ", 6*4);
        else
          R.ReplaceText(Loc, 1, "    ", 4);

        break;
      }

      case '<': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, "<", 4);
        break;
      }

      case '>': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, ">", 4);
        break;
      }

      case '&': {
        SourceLocation Loc = SourceLocation::getFileLoc(FileID, FilePos);
        R.ReplaceText(Loc, 1, "&", 5);
        break;
      }
    }
  }
}

std::string html::EscapeText(const std::string& s, bool EscapeSpaces) {

  unsigned len = s.size();
  std::ostringstream os;

  for (unsigned i = 0 ; i < len; ++i) {

    char c = s[i];

    switch (c) {
      default:
        os << c; break;

      case ' ':
        if (EscapeSpaces) os << " ";
        else os << ' ';
        break;

        case '\t': for (unsigned i = 0; i < 4; ++i) os << " "; break;
        case '<': os << "<"; break;
        case '>': os << ">"; break;
        case '&': os << "&"; break;
    }
  }

  return os.str();
}

static void AddLineNumber(Rewriter& R, unsigned LineNo,
                          SourceLocation B, SourceLocation E) {

  std::ostringstream os;
  os << "
<td class=\"num\" id=\"LN" << LineNo << "\">"
     << LineNo << "	";

  if (B == E) { // Handle empty lines.
    os << "
";
    R.InsertStrBefore(B, os.str());
  }
  else {
    R.InsertStrBefore(B, os.str());
    R.InsertCStrBefore(E, "
");
  }
}

void html::AddLineNumbers(Rewriter& R, unsigned FileID) {

  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
  const char* FileBeg = Buf->getBufferStart();
  const char* FileEnd = Buf->getBufferEnd();
  const char* C = FileBeg;

  assert (C <= FileEnd);

  unsigned LineNo = 0;
  unsigned FilePos = 0;

  while (C != FileEnd) {

    ++LineNo;
    unsigned LineStartPos = FilePos;
    unsigned LineEndPos = FileEnd - FileBeg;

    assert (FilePos <= LineEndPos);
    assert (C < FileEnd);

    // Scan until the newline (or end-of-file).

    while (C != FileEnd) {
      char c = *C;
      ++C;

      if (c == '\n') {
        LineEndPos = FilePos++;
        break;
      }

      ++FilePos;
    }

    AddLineNumber(R, LineNo,
                  SourceLocation::getFileLoc(FileID, LineStartPos),
                  SourceLocation::getFileLoc(FileID, LineEndPos));
  }

  // Add one big div tag that surrounds all of the code.

  R.InsertCStrBefore(SourceLocation::getFileLoc(FileID, 0),
                     "
\n");

  R.InsertCStrAfter(SourceLocation::getFileLoc(FileID, FileEnd - FileBeg),
                    "
");
}

void html::AddHeaderFooterInternalBuiltinCSS(Rewriter& R, unsigned FileID) {

  const llvm::MemoryBuffer *Buf = R.getSourceMgr().getBuffer(FileID);
  const char* FileStart = Buf->getBufferStart();
  const char* FileEnd = Buf->getBufferEnd();

  SourceLocation StartLoc = SourceLocation::getFileLoc(FileID, 0);
  SourceLocation EndLoc = SourceLocation::getFileLoc(FileID, FileEnd-FileStart);

  // Generate header

  {
    std::ostringstream os;

    os << "\n\n"
       << "\n\n";

    R.InsertStrBefore(StartLoc, os.str());
  }

  // Generate footer

  {
    std::ostringstream os;

    os << "\n";
    R.InsertStrAfter(EndLoc, os.str());
  }
}

<|endoftext|>


<commit_before>//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains functions used to do a variety of low-level, often
// system-specific, tasks.
//
//===----------------------------------------------------------------------===//

#include "Support/SystemUtils.h"
#include "Config/sys/types.h"
#include "Config/sys/stat.h"
#include "Config/fcntl.h"
#include "Config/sys/wait.h"
#include "Config/unistd.h"
#include "Config/config.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <cerrno>
using namespace llvm;

/// isExecutableFile - This function returns true if the filename specified
/// exists and is executable.
///
bool llvm::isExecutableFile(const std::string &ExeFileName) {
  struct stat Buf;
  if (stat(ExeFileName.c_str(), &Buf))
    return false;  // Must not be executable!

  if (!(Buf.st_mode & S_IFREG))
    return false;                    // Not a regular file?

  if (Buf.st_uid == getuid())        // Owner of file?
    return Buf.st_mode & S_IXUSR;
  else if (Buf.st_gid == getgid())   // In group of file?
    return Buf.st_mode & S_IXGRP;
  else                               // Unrelated to file?
    return Buf.st_mode & S_IXOTH;
}

/// isStandardOutAConsole - Return true if we can tell that the standard output
/// stream goes to a terminal window or console.
bool llvm::isStandardOutAConsole() {
#if HAVE_ISATTY
  return isatty(1);
#endif
  // If we don't have isatty, just return false.
  return false;
}

/// FindExecutable - Find a named executable, giving the argv[0] of program
/// being executed. This allows us to find another LLVM tool if it is built
/// into the same directory, but that directory is neither the current
/// directory, nor in the PATH.  If the executable cannot be found, return an
/// empty string.
///
std::string llvm::FindExecutable(const std::string &ExeName,
                                 const std::string &ProgramPath) {
  // First check the directory that bugpoint is in.  We can do this if
  // BugPointPath contains at least one / character, indicating that it is a
  // relative path to bugpoint itself.
  //
  std::string Result = ProgramPath;
  while (!Result.empty() && Result[Result.size()-1] != '/')
    Result.erase(Result.size()-1, 1);

  if (!Result.empty()) {
    Result += ExeName;
    if (isExecutableFile(Result)) return Result; // Found it?
  }

  // Okay, if the path to the program didn't tell us anything, try using the
  // PATH environment variable.
  const char *PathStr = getenv("PATH");
  if (PathStr == 0) return "";

  // Now we have a colon separated list of directories to search... try them...
  unsigned PathLen = strlen(PathStr);
  while (PathLen) {
    // Find the first colon...
    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');

    // Check to see if this first directory contains the executable...
    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
    if (isExecutableFile(FilePath))
      return FilePath;                    // Found the executable!

    // Nope it wasn't in this directory, check the next range!
    PathLen -= Colon-PathStr;
    PathStr = Colon;
    while (*PathStr == ':') {   // Advance past colons
      PathStr++;
      PathLen--;
    }
  }

  // If we fell out, we ran out of directories in PATH to search, return failure
  return "";
}

static void RedirectFD(const std::string &File, int FD) {
  if (File.empty()) return;  // Noop

  // Open the file
  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
  if (InFD == -1) {
    std::cerr << "Error opening file '" << File << "' for "
              << (FD == 0 ? "input" : "output") << "!\n";
    exit(1);
  }

  dup2(InFD, FD);   // Install it as the requested FD
  close(InFD);      // Close the original FD
}

/// RunProgramWithTimeout - This function executes the specified program, with
/// the specified null-terminated argument array, with the stdin/out/err fd's
/// redirected, with a timeout specified on the command line.  This terminates
/// the calling program if there is an error executing the specified program.
/// It returns the return value of the program, or -1 if a timeout is detected.
///
int llvm::RunProgramWithTimeout(const std::string &ProgramPath,
                                const char **Args,
                                const std::string &StdInFile,
                                const std::string &StdOutFile,
                                const std::string &StdErrFile) {
  // FIXME: install sigalarm handler here for timeout...

  int Child = fork();
  switch (Child) {
  case -1:
    std::cerr << "ERROR forking!\n";
    exit(1);
  case 0:               // Child
    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
    RedirectFD(StdOutFile, 1);
    if (StdOutFile != StdErrFile)
      RedirectFD(StdErrFile, 2);
    else
      dup2(1, 2);

    execv(ProgramPath.c_str(), (char *const *)Args);
    std::cerr << "Error executing program: '" << ProgramPath;
    for (; *Args; ++Args)
      std::cerr << " " << *Args;
    std::cerr << "'\n";
    exit(1);

  default: break;
  }

  // Make sure all output has been written while waiting
  std::cout << std::flush;

  int Status;
  if (wait(&Status) != Child) {
    if (errno == EINTR) {
      static bool FirstTimeout = true;
      if (FirstTimeout) {
        std::cout <<
 "*** Program execution timed out!  This mechanism is designed to handle\n"
 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
 "    can be used to change the timeout threshold or disable it completely\n"
 "    (with -timeout=0).  This message is only displayed once.\n";
        FirstTimeout = false;
      }
      return -1;   // Timeout detected
    }

    std::cerr << "Error waiting for child process!\n";
    exit(1);
  }
  return Status;
}

//
// Function: ExecWait ()
//
// Description:
//  This function executes a program with the specified arguments and
//  environment.  It then waits for the progarm to termiante and then returns
//  to the caller.
//
// Inputs:
//  argv - The arguments to the program as an array of C strings.  The first
//         argument should be the name of the program to execute, and the
//         last argument should be a pointer to NULL.
//
//  envp - The environment passes to the program as an array of C strings in
//         the form of "name=value" pairs.  The last element should be a
//         pointer to NULL.
//
// Outputs:
//  None.
//
// Return value:
//  0 - No errors.
//  1 - The program could not be executed.
//  1 - The program returned a non-zero exit status.
//  1 - The program terminated abnormally.
//
// Notes:
//  The program will inherit the stdin, stdout, and stderr file descriptors
//  as well as other various configuration settings (umask).
//
//  This function should not print anything to stdout/stderr on its own.  It is
//  a generic library function.  The caller or executed program should report
//  errors in the way it sees fit.
//
//  This function does not use $PATH to find programs.
//
int llvm::ExecWait(const char * const old_argv[],
                   const char * const old_envp[]) {
  // Child process ID
  register int child;

  // Status from child process when it exits
  int status;

  //
  // Create local versions of the parameters that can be passed into execve()
  // without creating const problems.
  //
  char ** const argv = (char ** const) old_argv;
  char ** const envp = (char ** const) old_envp;

  //
  // Create a child process.
  //
  switch (child=fork())
  {
    //
    // An error occured:  Return to the caller.
    //
    case -1:
      return 1;
      break;

    //
    // Child process: Execute the program.
    //
    case 0:
      execve (argv[0], argv, envp);

      //
      // If the execve() failed, we should exit and let the parent pick up
      // our non-zero exit status.
      //
      exit (1);
      break;

    //
    // Parent process: Break out of the switch to do our processing.
    //
    default:
      break;
  }

  //
  // Parent process: Wait for the child process to termiante.
  //
  if ((wait (&status)) == -1)
  {
    return 1;
  }

  //
  // If the program exited normally with a zero exit status, return success!
  //
  if (WIFEXITED (status) && (WEXITSTATUS(status) == 0))
  {
    return 0;
  }

  //
  // Otherwise, return failure.
  //
  return 1;
}
<commit_msg>Changes to make libSupport build on systems that don't have the wait syscall.<commit_after>//===- SystemUtils.h - Utilities to do low-level system stuff --*- C++ -*--===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains functions used to do a variety of low-level, often
// system-specific, tasks.
//
//===----------------------------------------------------------------------===//

#include "Support/SystemUtils.h"
#include "Config/sys/types.h"
#include "Config/sys/stat.h"
#include "Config/fcntl.h"
#include "Config/sys/wait.h"
#include "Config/unistd.h"
#include "Config/config.h"
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <cerrno>
using namespace llvm;

/// isExecutableFile - This function returns true if the filename specified
/// exists and is executable.
///
bool llvm::isExecutableFile(const std::string &ExeFileName) {
  struct stat Buf;
  if (stat(ExeFileName.c_str(), &Buf))
    return false;  // Must not be executable!

  if (!(Buf.st_mode & S_IFREG))
    return false;                    // Not a regular file?

  if (Buf.st_uid == getuid())        // Owner of file?
    return Buf.st_mode & S_IXUSR;
  else if (Buf.st_gid == getgid())   // In group of file?
    return Buf.st_mode & S_IXGRP;
  else                               // Unrelated to file?
    return Buf.st_mode & S_IXOTH;
}

/// isStandardOutAConsole - Return true if we can tell that the standard output
/// stream goes to a terminal window or console.
bool llvm::isStandardOutAConsole() {
#if HAVE_ISATTY
  return isatty(1);
#endif
  // If we don't have isatty, just return false.
  return false;
}

/// FindExecutable - Find a named executable, giving the argv[0] of program
/// being executed. This allows us to find another LLVM tool if it is built
/// into the same directory, but that directory is neither the current
/// directory, nor in the PATH.  If the executable cannot be found, return an
/// empty string.
///
std::string llvm::FindExecutable(const std::string &ExeName,
                                 const std::string &ProgramPath) {
  // First check the directory that bugpoint is in.  We can do this if
  // BugPointPath contains at least one / character, indicating that it is a
  // relative path to bugpoint itself.
  //
  std::string Result = ProgramPath;
  while (!Result.empty() && Result[Result.size()-1] != '/')
    Result.erase(Result.size()-1, 1);

  if (!Result.empty()) {
    Result += ExeName;
    if (isExecutableFile(Result)) return Result; // Found it?
  }

  // Okay, if the path to the program didn't tell us anything, try using the
  // PATH environment variable.
  const char *PathStr = getenv("PATH");
  if (PathStr == 0) return "";

  // Now we have a colon separated list of directories to search... try them...
  unsigned PathLen = strlen(PathStr);
  while (PathLen) {
    // Find the first colon...
    const char *Colon = std::find(PathStr, PathStr+PathLen, ':');

    // Check to see if this first directory contains the executable...
    std::string FilePath = std::string(PathStr, Colon) + '/' + ExeName;
    if (isExecutableFile(FilePath))
      return FilePath;                    // Found the executable!

    // Nope it wasn't in this directory, check the next range!
    PathLen -= Colon-PathStr;
    PathStr = Colon;
    while (*PathStr == ':') {   // Advance past colons
      PathStr++;
      PathLen--;
    }
  }

  // If we fell out, we ran out of directories in PATH to search, return failure
  return "";
}

static void RedirectFD(const std::string &File, int FD) {
  if (File.empty()) return;  // Noop

  // Open the file
  int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666);
  if (InFD == -1) {
    std::cerr << "Error opening file '" << File << "' for "
              << (FD == 0 ? "input" : "output") << "!\n";
    exit(1);
  }

  dup2(InFD, FD);   // Install it as the requested FD
  close(InFD);      // Close the original FD
}

/// RunProgramWithTimeout - This function executes the specified program, with
/// the specified null-terminated argument array, with the stdin/out/err fd's
/// redirected, with a timeout specified on the command line.  This terminates
/// the calling program if there is an error executing the specified program.
/// It returns the return value of the program, or -1 if a timeout is detected.
///
int llvm::RunProgramWithTimeout(const std::string &ProgramPath,
                                const char **Args,
                                const std::string &StdInFile,
                                const std::string &StdOutFile,
                                const std::string &StdErrFile) {
  // FIXME: install sigalarm handler here for timeout...

#ifdef HAVE_SYS_WAIT_H
  int Child = fork();
  switch (Child) {
  case -1:
    std::cerr << "ERROR forking!\n";
    exit(1);
  case 0:               // Child
    RedirectFD(StdInFile, 0);      // Redirect file descriptors...
    RedirectFD(StdOutFile, 1);
    if (StdOutFile != StdErrFile)
      RedirectFD(StdErrFile, 2);
    else
      dup2(1, 2);

    execv(ProgramPath.c_str(), (char *const *)Args);
    std::cerr << "Error executing program: '" << ProgramPath;
    for (; *Args; ++Args)
      std::cerr << " " << *Args;
    std::cerr << "'\n";
    exit(1);

  default: break;
  }

  // Make sure all output has been written while waiting
  std::cout << std::flush;

  int Status;
  if (wait(&Status) != Child) {
    if (errno == EINTR) {
      static bool FirstTimeout = true;
      if (FirstTimeout) {
        std::cout <<
 "*** Program execution timed out!  This mechanism is designed to handle\n"
 "    programs stuck in infinite loops gracefully.  The -timeout option\n"
 "    can be used to change the timeout threshold or disable it completely\n"
 "    (with -timeout=0).  This message is only displayed once.\n";
        FirstTimeout = false;
      }
      return -1;   // Timeout detected
    }

    std::cerr << "Error waiting for child process!\n";
    exit(1);
  }
  return Status;

#else
  std::cerr << "RunProgramWithTimeout not implemented on this platform!\n";
  return -1;
#endif
}

//
// Function: ExecWait ()
//
// Description:
//  This function executes a program with the specified arguments and
//  environment.  It then waits for the progarm to termiante and then returns
//  to the caller.
//
// Inputs:
//  argv - The arguments to the program as an array of C strings.  The first
//         argument should be the name of the program to execute, and the
//         last argument should be a pointer to NULL.
//
//  envp - The environment passes to the program as an array of C strings in
//         the form of "name=value" pairs.  The last element should be a
//         pointer to NULL.
//
// Outputs:
//  None.
//
// Return value:
//  0 - No errors.
//  1 - The program could not be executed.
//  1 - The program returned a non-zero exit status.
//  1 - The program terminated abnormally.
//
// Notes:
//  The program will inherit the stdin, stdout, and stderr file descriptors
//  as well as other various configuration settings (umask).
//
//  This function should not print anything to stdout/stderr on its own.  It is
//  a generic library function.  The caller or executed program should report
//  errors in the way it sees fit.
//
//  This function does not use $PATH to find programs.
//
int llvm::ExecWait(const char * const old_argv[],
                   const char * const old_envp[]) {
#ifdef HAVE_SYS_WAIT_H
  //
  // Create local versions of the parameters that can be passed into execve()
  // without creating const problems.
  //
  char ** const argv = (char ** const) old_argv;
  char ** const envp = (char ** const) old_envp;

  // Create a child process.
  switch (fork()) {
    // An error occured:  Return to the caller.
    case -1:
      return 1;
      break;

    // Child process: Execute the program.
    case 0:
      execve (argv[0], argv, envp);
      // If the execve() failed, we should exit and let the parent pick up
      // our non-zero exit status.
      exit (1);

    // Parent process: Break out of the switch to do our processing.
    default:
      break;
  }

  // Parent process: Wait for the child process to termiante.
  int status;
  if ((wait (&status)) == -1)
    return 1;

  // If the program exited normally with a zero exit status, return success!
  if (WIFEXITED (status) && (WEXITSTATUS(status) == 0))
    return 0;
#else
  std::cerr << "llvm::ExecWait not implemented on this platform!\n";
#endif

  // Otherwise, return failure.
  return 1;
}
<|endoftext|>


<commit_before>//===-- asan_malloc_win.cc ------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific malloc interception.
//===----------------------------------------------------------------------===//

#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
#include "interception/interception.h"

#include <stddef.h>

using namespace __asan;  // NOLINT

// MT: Simply defining functions with the same signature in *.obj
// files overrides the standard functions in the CRT.
// MD: Memory allocation functions are defined in the CRT .dll,
// so we have to intercept them before they are called for the first time.

#if ASAN_DYNAMIC
# define ALLOCATION_FUNCTION_ATTRIBUTE
#else
# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
#endif

extern "C" {
ALLOCATION_FUNCTION_ATTRIBUTE
void free(void *ptr) {
  GET_STACK_TRACE_FREE;
  return asan_free(ptr, &stack, FROM_MALLOC);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void _free_dbg(void *ptr, int) {
  free(ptr);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void _free_base(void *ptr) {
  free(ptr);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *malloc(size_t size) {
  GET_STACK_TRACE_MALLOC;
  return asan_malloc(size, &stack);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_malloc_base(size_t size) {
  return malloc(size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_malloc_dbg(size_t size, int, const char *, int) {
  return malloc(size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *calloc(size_t nmemb, size_t size) {
  GET_STACK_TRACE_MALLOC;
  return asan_calloc(nmemb, size, &stack);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_base(size_t nmemb, size_t size) {
  return calloc(nmemb, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
  return calloc(nmemb, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
  return calloc(nmemb, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *realloc(void *ptr, size_t size) {
  GET_STACK_TRACE_MALLOC;
  return asan_realloc(ptr, size, &stack);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_realloc_dbg(void *ptr, size_t size, int) {
  UNREACHABLE("_realloc_dbg should not exist!");
  return 0;
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_realloc_base(void *ptr, size_t size) {
  return realloc(ptr, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_recalloc(void *p, size_t n, size_t elem_size) {
  if (!p)
    return calloc(n, elem_size);
  const size_t size = n * elem_size;
  if (elem_size != 0 && size / elem_size != n)
    return 0;
  return realloc(p, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_recalloc_base(void *p, size_t n, size_t elem_size) {
  return _recalloc(p, n, elem_size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
size_t _msize(void *ptr) {
  GET_CURRENT_PC_BP_SP;
  (void)sp;
  return asan_malloc_usable_size(ptr, pc, bp);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand(void *memblock, size_t size) {
  // _expand is used in realloc-like functions to resize the buffer if possible.
  // We don't want memory to stand still while resizing buffers, so return 0.
  return 0;
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand_dbg(void *memblock, size_t size) {
  return _expand(memblock, size);
}

// TODO(timurrrr): Might want to add support for _aligned_* allocation
// functions to detect a bit more bugs.  Those functions seem to wrap malloc().

int _CrtDbgReport(int, const char*, int,
                  const char*, const char*, ...) {
  ShowStatsAndAbort();
}

int _CrtDbgReportW(int reportType, const wchar_t*, int,
                   const wchar_t*, const wchar_t*, ...) {
  ShowStatsAndAbort();
}

int _CrtSetReportMode(int, int) {
  return 0;
}
}  // extern "C"

INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
                   SIZE_T dwBytes) {
  GET_STACK_TRACE_MALLOC;
  void *p = asan_malloc(dwBytes, &stack);
  // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
  // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
  // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
  if (dwFlags == HEAP_ZERO_MEMORY)
    internal_memset(p, 0, asan_mz_size(p));
  else
    CHECK(dwFlags == 0 && "unsupported heap flags");
  return p;
}

INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
  CHECK(dwFlags == 0 && "unsupported heap flags");
  GET_STACK_TRACE_FREE;
  asan_free(lpMem, &stack, FROM_MALLOC);
  return true;
}

INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
                   LPVOID lpMem, SIZE_T dwBytes) {
  GET_STACK_TRACE_MALLOC;
  // Realloc should never reallocate in place.
  if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
    return nullptr;
  CHECK(dwFlags == 0 && "unsupported heap flags");
  return asan_realloc(lpMem, dwBytes, &stack);
}

INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
                   LPCVOID lpMem) {
  CHECK(dwFlags == 0 && "unsupported heap flags");
  GET_CURRENT_PC_BP_SP;
  (void)sp;
  return asan_malloc_usable_size(lpMem, pc, bp);
}

namespace __asan {

static void TryToOverrideFunction(const char *fname, uptr new_func) {
  // Failure here is not fatal. The CRT may not be present, and different CRT
  // versions use different symbols.
  if (!__interception::OverrideFunction(fname, new_func))
    VPrintf(2, "Failed to override function %s\n", fname);
}

void ReplaceSystemMalloc() {
#if defined(ASAN_DYNAMIC)
  TryToOverrideFunction("free", (uptr)free);
  TryToOverrideFunction("_free_base", (uptr)free);
  TryToOverrideFunction("malloc", (uptr)malloc);
  TryToOverrideFunction("_malloc_base", (uptr)malloc);
  TryToOverrideFunction("_malloc_crt", (uptr)malloc);
  TryToOverrideFunction("calloc", (uptr)calloc);
  TryToOverrideFunction("_calloc_base", (uptr)calloc);
  TryToOverrideFunction("_calloc_crt", (uptr)calloc);
  TryToOverrideFunction("realloc", (uptr)realloc);
  TryToOverrideFunction("_realloc_base", (uptr)realloc);
  TryToOverrideFunction("_realloc_crt", (uptr)realloc);
  TryToOverrideFunction("_recalloc", (uptr)_recalloc);
  TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
  TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
  TryToOverrideFunction("_msize", (uptr)_msize);
  TryToOverrideFunction("_expand", (uptr)_expand);
  TryToOverrideFunction("_expand_base", (uptr)_expand);

  // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
  // enable cross-module inlining. This means our _malloc_base hook won't catch
  // all CRT allocations. This code here patches the import table of
  // ucrtbase.dll so that all attempts to use the lower-level win32 heap
  // allocation API will be directed to ASan's heap. We don't currently
  // intercept all calls to HeapAlloc. If we did, we would have to check on
  // HeapFree whether the pointer came from ASan of from the system.
#define INTERCEPT_UCRT_FUNCTION(func)                                         \
  if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll",                           \
                                    "api-ms-win-core-heap-l1-1-0.dll", func)) \
    VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
  INTERCEPT_UCRT_FUNCTION(HeapAlloc);
  INTERCEPT_UCRT_FUNCTION(HeapFree);
  INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
  INTERCEPT_UCRT_FUNCTION(HeapSize);
#undef INTERCEPT_UCRT_FUNCTION
#endif
}
}  // namespace __asan

#endif  // _WIN32
<commit_msg>[ASan] [Windows] Avoid including windows.h in asan_malloc_win.cc<commit_after>//===-- asan_malloc_win.cc ------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of AddressSanitizer, an address sanity checker.
//
// Windows-specific malloc interception.
//===----------------------------------------------------------------------===//

#include "sanitizer_common/sanitizer_platform.h"
#if SANITIZER_WINDOWS
// Intentionally not including windows.h here, to avoid the risk of
// pulling in conflicting declarations of these functions. (With mingw-w64,
// there's a risk of windows.h pulling in stdint.h.)
typedef int BOOL;
typedef void *HANDLE;
typedef const void *LPCVOID;
typedef void *LPVOID;

#define HEAP_ZERO_MEMORY           0x00000008
#define HEAP_REALLOC_IN_PLACE_ONLY 0x00000010

#include "asan_allocator.h"
#include "asan_interceptors.h"
#include "asan_internal.h"
#include "asan_stack.h"
#include "interception/interception.h"

#include <stddef.h>

using namespace __asan;  // NOLINT

// MT: Simply defining functions with the same signature in *.obj
// files overrides the standard functions in the CRT.
// MD: Memory allocation functions are defined in the CRT .dll,
// so we have to intercept them before they are called for the first time.

#if ASAN_DYNAMIC
# define ALLOCATION_FUNCTION_ATTRIBUTE
#else
# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
#endif

extern "C" {
ALLOCATION_FUNCTION_ATTRIBUTE
void free(void *ptr) {
  GET_STACK_TRACE_FREE;
  return asan_free(ptr, &stack, FROM_MALLOC);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void _free_dbg(void *ptr, int) {
  free(ptr);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void _free_base(void *ptr) {
  free(ptr);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *malloc(size_t size) {
  GET_STACK_TRACE_MALLOC;
  return asan_malloc(size, &stack);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_malloc_base(size_t size) {
  return malloc(size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_malloc_dbg(size_t size, int, const char *, int) {
  return malloc(size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *calloc(size_t nmemb, size_t size) {
  GET_STACK_TRACE_MALLOC;
  return asan_calloc(nmemb, size, &stack);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_base(size_t nmemb, size_t size) {
  return calloc(nmemb, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_dbg(size_t nmemb, size_t size, int, const char *, int) {
  return calloc(nmemb, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_calloc_impl(size_t nmemb, size_t size, int *errno_tmp) {
  return calloc(nmemb, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *realloc(void *ptr, size_t size) {
  GET_STACK_TRACE_MALLOC;
  return asan_realloc(ptr, size, &stack);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_realloc_dbg(void *ptr, size_t size, int) {
  UNREACHABLE("_realloc_dbg should not exist!");
  return 0;
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_realloc_base(void *ptr, size_t size) {
  return realloc(ptr, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_recalloc(void *p, size_t n, size_t elem_size) {
  if (!p)
    return calloc(n, elem_size);
  const size_t size = n * elem_size;
  if (elem_size != 0 && size / elem_size != n)
    return 0;
  return realloc(p, size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_recalloc_base(void *p, size_t n, size_t elem_size) {
  return _recalloc(p, n, elem_size);
}

ALLOCATION_FUNCTION_ATTRIBUTE
size_t _msize(void *ptr) {
  GET_CURRENT_PC_BP_SP;
  (void)sp;
  return asan_malloc_usable_size(ptr, pc, bp);
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand(void *memblock, size_t size) {
  // _expand is used in realloc-like functions to resize the buffer if possible.
  // We don't want memory to stand still while resizing buffers, so return 0.
  return 0;
}

ALLOCATION_FUNCTION_ATTRIBUTE
void *_expand_dbg(void *memblock, size_t size) {
  return _expand(memblock, size);
}

// TODO(timurrrr): Might want to add support for _aligned_* allocation
// functions to detect a bit more bugs.  Those functions seem to wrap malloc().

int _CrtDbgReport(int, const char*, int,
                  const char*, const char*, ...) {
  ShowStatsAndAbort();
}

int _CrtDbgReportW(int reportType, const wchar_t*, int,
                   const wchar_t*, const wchar_t*, ...) {
  ShowStatsAndAbort();
}

int _CrtSetReportMode(int, int) {
  return 0;
}
}  // extern "C"

INTERCEPTOR_WINAPI(LPVOID, HeapAlloc, HANDLE hHeap, DWORD dwFlags,
                   SIZE_T dwBytes) {
  GET_STACK_TRACE_MALLOC;
  void *p = asan_malloc(dwBytes, &stack);
  // Reading MSDN suggests that the *entire* usable allocation is zeroed out.
  // Otherwise it is difficult to HeapReAlloc with HEAP_ZERO_MEMORY.
  // https://blogs.msdn.microsoft.com/oldnewthing/20120316-00/?p=8083
  if (dwFlags == HEAP_ZERO_MEMORY)
    internal_memset(p, 0, asan_mz_size(p));
  else
    CHECK(dwFlags == 0 && "unsupported heap flags");
  return p;
}

INTERCEPTOR_WINAPI(BOOL, HeapFree, HANDLE hHeap, DWORD dwFlags, LPVOID lpMem) {
  CHECK(dwFlags == 0 && "unsupported heap flags");
  GET_STACK_TRACE_FREE;
  asan_free(lpMem, &stack, FROM_MALLOC);
  return true;
}

INTERCEPTOR_WINAPI(LPVOID, HeapReAlloc, HANDLE hHeap, DWORD dwFlags,
                   LPVOID lpMem, SIZE_T dwBytes) {
  GET_STACK_TRACE_MALLOC;
  // Realloc should never reallocate in place.
  if (dwFlags & HEAP_REALLOC_IN_PLACE_ONLY)
    return nullptr;
  CHECK(dwFlags == 0 && "unsupported heap flags");
  return asan_realloc(lpMem, dwBytes, &stack);
}

INTERCEPTOR_WINAPI(SIZE_T, HeapSize, HANDLE hHeap, DWORD dwFlags,
                   LPCVOID lpMem) {
  CHECK(dwFlags == 0 && "unsupported heap flags");
  GET_CURRENT_PC_BP_SP;
  (void)sp;
  return asan_malloc_usable_size(lpMem, pc, bp);
}

namespace __asan {

static void TryToOverrideFunction(const char *fname, uptr new_func) {
  // Failure here is not fatal. The CRT may not be present, and different CRT
  // versions use different symbols.
  if (!__interception::OverrideFunction(fname, new_func))
    VPrintf(2, "Failed to override function %s\n", fname);
}

void ReplaceSystemMalloc() {
#if defined(ASAN_DYNAMIC)
  TryToOverrideFunction("free", (uptr)free);
  TryToOverrideFunction("_free_base", (uptr)free);
  TryToOverrideFunction("malloc", (uptr)malloc);
  TryToOverrideFunction("_malloc_base", (uptr)malloc);
  TryToOverrideFunction("_malloc_crt", (uptr)malloc);
  TryToOverrideFunction("calloc", (uptr)calloc);
  TryToOverrideFunction("_calloc_base", (uptr)calloc);
  TryToOverrideFunction("_calloc_crt", (uptr)calloc);
  TryToOverrideFunction("realloc", (uptr)realloc);
  TryToOverrideFunction("_realloc_base", (uptr)realloc);
  TryToOverrideFunction("_realloc_crt", (uptr)realloc);
  TryToOverrideFunction("_recalloc", (uptr)_recalloc);
  TryToOverrideFunction("_recalloc_base", (uptr)_recalloc);
  TryToOverrideFunction("_recalloc_crt", (uptr)_recalloc);
  TryToOverrideFunction("_msize", (uptr)_msize);
  TryToOverrideFunction("_expand", (uptr)_expand);
  TryToOverrideFunction("_expand_base", (uptr)_expand);

  // Recent versions of ucrtbase.dll appear to be built with PGO and LTCG, which
  // enable cross-module inlining. This means our _malloc_base hook won't catch
  // all CRT allocations. This code here patches the import table of
  // ucrtbase.dll so that all attempts to use the lower-level win32 heap
  // allocation API will be directed to ASan's heap. We don't currently
  // intercept all calls to HeapAlloc. If we did, we would have to check on
  // HeapFree whether the pointer came from ASan of from the system.
#define INTERCEPT_UCRT_FUNCTION(func)                                         \
  if (!INTERCEPT_FUNCTION_DLLIMPORT("ucrtbase.dll",                           \
                                    "api-ms-win-core-heap-l1-1-0.dll", func)) \
    VPrintf(2, "Failed to intercept ucrtbase.dll import %s\n", #func);
  INTERCEPT_UCRT_FUNCTION(HeapAlloc);
  INTERCEPT_UCRT_FUNCTION(HeapFree);
  INTERCEPT_UCRT_FUNCTION(HeapReAlloc);
  INTERCEPT_UCRT_FUNCTION(HeapSize);
#undef INTERCEPT_UCRT_FUNCTION
#endif
}
}  // namespace __asan

#endif  // _WIN32
<|endoftext|>


<commit_before>#include "condor_common.h"
#include "classad.h"

static char *_FileName_ = __FILE__;

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	attributeStr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, char *attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = strnewp( attrname );
	expr = tree;
	absolute = absolut;
}

AttributeReference::
~AttributeReference()
{
	if( attributeStr ) delete []( attributeStr );
	if( expr ) delete expr;
}

AttributeReference *AttributeReference::
Copy( )
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) return NULL;

	if (attributeStr) newTree->attributeStr = strnewp( attributeStr );
	newTree->attributeName = attributeName;

	if( expr && ( newTree->expr=expr->Copy( ) ) == NULL ) {
		delete []( newTree->attributeStr );
		delete newTree;
		return NULL;
	}

	newTree->nodeKind = nodeKind;
	newTree->absolute = absolute;

	return newTree;
}

void AttributeReference::
_SetParentScope( ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

bool AttributeReference::
ToSink (Sink &s)
{
	if( ( absolute 	&& !s.SendToSink( (void*)".", 1 ) )	 || ( expr
			&& ( !expr->ToSink( s ) || !s.SendToSink( (void*) ".", 1 ) ) ) ||
		( attributeStr &&
			!s.SendToSink((void*)attributeStr,strlen(attributeStr))))
				return false;

	return true;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val)
{
	ExprTree	*tree, *dummy;
	ClassAd		*curAd;
	bool		rval;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Evaluate( state , val );
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig )
{
	ExprTree	*tree;
	ClassAd		*curAd;
	bool		rval;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state , tree , sig , true ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Evaluate( state , val );
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, OpKind*)
{
	if( absolute ) {
		ntree = Copy();
		return( ntree != NULL );
	}

	if( !Evaluate( state, val ) ) {
		return false;
	}

	if( val.IsClassAdValue() || val.IsListValue() || val.IsUndefinedValue() ) {
		ntree = Copy();
		return( ntree != NULL );
	} else {
		ntree = NULL;
	}
	return true;
}

int AttributeReference::
FindExpr( EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig )
{
	ClassAd 	*current=NULL;
	ExprTree	*sigXpr = NULL;
	Value		val;
	bool		rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sigXpr):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			sig = sigXpr;
			return( EVAL_UNDEF );
		}
		if( !val.IsClassAdValue( current ) ) {
			if( wantSig ) {
				if(!(sig=new AttributeReference(sigXpr,attributeStr,absolute))){
					return( EVAL_FAIL );
				}
			}
			return( EVAL_ERROR );
		}
	}

	if( wantSig ) {
		sig = new AttributeReference( sigXpr, attributeStr, absolute );
	}

	// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

void AttributeReference::
SetReference (ExprTree *tree, char *attrStr, bool absolut)
{
	if (expr) delete expr;
	if (attributeStr) delete [] (attributeStr);

	expr 		= tree;
	absolute 	= absolut;
	attributeStr= attrStr ? strnewp(attrStr) : 0;
}
<commit_msg>Copy parent scope correctly in Copy() method.<commit_after>#include "condor_common.h"
#include "classad.h"

static char *_FileName_ = __FILE__;

AttributeReference::
AttributeReference()
{
	nodeKind = ATTRREF_NODE;
	expr = NULL;
	attributeStr = NULL;
	absolute = false;
}

// a private ctor for use in significant expr identification
AttributeReference::
AttributeReference( ExprTree *tree, char *attrname, bool absolut )
{
	nodeKind = ATTRREF_NODE;
	attributeStr = strnewp( attrname );
	expr = tree;
	absolute = absolut;
}

AttributeReference::
~AttributeReference()
{
	if( attributeStr ) delete []( attributeStr );
	if( expr ) delete expr;
}

AttributeReference *AttributeReference::
Copy( )
{
	AttributeReference *newTree = new AttributeReference ();
	if (newTree == 0) return NULL;

	if (attributeStr) newTree->attributeStr = strnewp( attributeStr );
	newTree->attributeName = attributeName;

	if( expr && ( newTree->expr=expr->Copy( ) ) == NULL ) {
		delete []( newTree->attributeStr );
		delete newTree;
		return NULL;
	}

	newTree->nodeKind = nodeKind;
	newTree->parentScope = parentScope;
	newTree->absolute = absolute;

	return newTree;
}

void AttributeReference::
_SetParentScope( ClassAd *parent )
{
	if( expr ) expr->SetParentScope( parent );
}

bool AttributeReference::
ToSink (Sink &s)
{
	if( ( absolute 	&& !s.SendToSink( (void*)".", 1 ) )	 || ( expr
			&& ( !expr->ToSink( s ) || !s.SendToSink( (void*) ".", 1 ) ) ) ||
		( attributeStr &&
			!s.SendToSink((void*)attributeStr,strlen(attributeStr))))
				return false;

	return true;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val)
{
	ExprTree	*tree, *dummy;
	ClassAd		*curAd;
	bool		rval;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state, tree, dummy, false ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Evaluate( state , val );
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Evaluate (EvalState &state, Value &val, ExprTree *&sig )
{
	ExprTree	*tree;
	ClassAd		*curAd;
	bool		rval;

	// find the expression and the evalstate
	curAd = state.curAd;
	switch( FindExpr( state , tree , sig , true ) ) {
		case EVAL_FAIL:
			return false;

		case EVAL_ERROR:
			val.SetErrorValue();
			state.curAd = curAd;
			return true;

		case EVAL_UNDEF:
			val.SetUndefinedValue();
			state.curAd = curAd;
			return true;

		case EVAL_OK:
			rval = tree->Evaluate( state , val );
			state.curAd = curAd;
			return rval;

		default:  EXCEPT( "ClassAd:  Should not reach here" );
	}
	return false;
}

bool AttributeReference::
_Flatten( EvalState &state, Value &val, ExprTree*&ntree, OpKind*)
{
	if( absolute ) {
		ntree = Copy();
		return( ntree != NULL );
	}

	if( !Evaluate( state, val ) ) {
		return false;
	}

	if( val.IsClassAdValue() || val.IsListValue() || val.IsUndefinedValue() ) {
		ntree = Copy();
		return( ntree != NULL );
	} else {
		ntree = NULL;
	}
	return true;
}

int AttributeReference::
FindExpr( EvalState &state, ExprTree *&tree, ExprTree *&sig, bool wantSig )
{
	ClassAd 	*current=NULL;
	ExprTree	*sigXpr = NULL;
	Value		val;
	bool		rval;

	sig = NULL;

	// establish starting point for search
	if( expr == NULL ) {
		// "attr" and ".attr"
		current = absolute ? state.rootAd : state.curAd;
	} else {
		// "expr.attr"
		rval=wantSig?expr->Evaluate(state,val,sigXpr):expr->Evaluate(state,val);
		if( !rval ) {
			return( EVAL_FAIL );
		}

		if( val.IsUndefinedValue( ) ) {
			sig = sigXpr;
			return( EVAL_UNDEF );
		}
		if( !val.IsClassAdValue( current ) ) {
			if( wantSig ) {
				if(!(sig=new AttributeReference(sigXpr,attributeStr,absolute))){
					return( EVAL_FAIL );
				}
			}
			return( EVAL_ERROR );
		}
	}

	if( wantSig ) {
		sig = new AttributeReference( sigXpr, attributeStr, absolute );
	}

	// lookup with scope; this may side-affect state
	return( current->LookupInScope( attributeStr, tree, state ) );
}

void AttributeReference::
SetReference (ExprTree *tree, char *attrStr, bool absolut)
{
	if (expr) delete expr;
	if (attributeStr) delete [] (attributeStr);

	expr 		= tree;
	absolute 	= absolut;
	attributeStr= attrStr ? strnewp(attrStr) : 0;
}
<|endoftext|>