<commit_before>//===--- SemaType.cpp - Semantic Analysis for Types -----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Chris Lattner and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file implements type-related semantic analysis.
//
//===----------------------------------------------------------------------===//

#include "Sema.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Parse/DeclSpec.h"
#include "clang/Lex/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
using namespace clang;

/// ConvertDeclSpecToType - Convert the specified declspec to the appropriate
/// type object.  This returns null on error.
static QualType ConvertDeclSpecToType(const DeclSpec &DS, ASTContext &Ctx) {
  // FIXME: Should move the logic from DeclSpec::Finish to here for validity
  // checking.

  switch (DS.getTypeSpecType()) {
  default: return QualType(); // FIXME: Handle unimp cases!
  case DeclSpec::TST_void: return Ctx.VoidTy;
  case DeclSpec::TST_char:
    if (DS.getTypeSpecSign() == DeclSpec::TSS_unspecified)
      return Ctx.CharTy;
    else if (DS.getTypeSpecSign() == DeclSpec::TSS_signed)
      return Ctx.SignedCharTy;
    else {
      assert(DS.getTypeSpecSign() == DeclSpec::TSS_unsigned &&
             "Unknown TSS value");
      return Ctx.UnsignedCharTy;
    }
  case DeclSpec::TST_unspecified:  // Unspecific typespec defaults to int.
  case DeclSpec::TST_int: {
    QualType Result;
    if (DS.getTypeSpecSign() != DeclSpec::TSS_unsigned) {
      switch (DS.getTypeSpecWidth()) {
      case DeclSpec::TSW_unspecified: Result = Ctx.IntTy; break;
      case DeclSpec::TSW_short:       Result = Ctx.ShortTy; break;
      case DeclSpec::TSW_long:        Result = Ctx.LongTy; break;
      case DeclSpec::TSW_longlong:    Result = Ctx.LongLongTy; break;
      }
    } else {
      switch (DS.getTypeSpecWidth()) {
      case DeclSpec::TSW_unspecified: Result = Ctx.UnsignedIntTy; break;
      case DeclSpec::TSW_short:       Result = Ctx.UnsignedShortTy; break;
      case DeclSpec::TSW_long:        Result = Ctx.UnsignedLongTy; break;
      case DeclSpec::TSW_longlong:    Result = Ctx.UnsignedLongLongTy; break;
      }
    }
    // Handle complex integer types.
    if (DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified)
      return Result;
    assert(DS.getTypeSpecComplex() == DeclSpec::TSC_complex &&
           "FIXME: imaginary types not supported yet!");
    return Ctx.getComplexType(Result);
  }
  case DeclSpec::TST_float:
    if (DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified)
      return Ctx.FloatTy;
    assert(DS.getTypeSpecComplex() == DeclSpec::TSC_complex &&
           "FIXME: imaginary types not supported yet!");
    return Ctx.getComplexType(Ctx.FloatTy);

  case DeclSpec::TST_double: {
    bool isLong = DS.getTypeSpecWidth() == DeclSpec::TSW_long;
    QualType T = isLong ? Ctx.LongDoubleTy : Ctx.DoubleTy;
    if (DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified)
      return T;
    assert(DS.getTypeSpecComplex() == DeclSpec::TSC_complex &&
           "FIXME: imaginary types not supported yet!");
    return Ctx.getComplexType(T);
  }
  case DeclSpec::TST_bool:         // _Bool or bool
    return Ctx.BoolTy;
  case DeclSpec::TST_decimal32:    // _Decimal32
  case DeclSpec::TST_decimal64:    // _Decimal64
  case DeclSpec::TST_decimal128:   // _Decimal128
    assert(0 && "FIXME: GNU decimal extensions not supported yet!");
  case DeclSpec::TST_enum:
  case DeclSpec::TST_union:
  case DeclSpec::TST_struct: {
    Decl *D = static_cast<Decl *>(DS.getTypeRep());
    assert(D && "Didn't get a decl for a enum/union/struct?");
    assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 &&
           DS.getTypeSpecSign() == 0 &&
           "Can't handle qualifiers on typedef names yet!");
    // TypeQuals handled by caller.
    return Ctx.getTagDeclType(cast<TagDecl>(D));
  }
  case DeclSpec::TST_typedef: {
    Decl *D = static_cast<Decl *>(DS.getTypeRep());
    assert(D && "Didn't get a decl for a typedef?");
    assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 &&
           DS.getTypeSpecSign() == 0 &&
           "Can't handle qualifiers on typedef names yet!");
    // FIXME: Adding a TST_objcInterface clause doesn't seem ideal, so
    // we have this "hack" for now...
    if (isa<ObjcInterfaceDecl>(D))
      return Ctx.getObjcInterfaceType(cast<ObjcInterfaceDecl>(D));
    // TypeQuals handled by caller.
    return Ctx.getTypedefType(cast<TypedefDecl>(D));
  }
  case DeclSpec::TST_typeofType: {
    QualType T = QualType::getFromOpaquePtr(DS.getTypeRep());
    assert(!T.isNull() && "Didn't get a type for typeof?");
    // TypeQuals handled by caller.
    return Ctx.getTypeOfType(T);
  }
  case DeclSpec::TST_typeofExpr: {
    Expr *E = static_cast<Expr *>(DS.getTypeRep());
    assert(E && "Didn't get an expression for typeof?");
    // TypeQuals handled by caller.
    return Ctx.getTypeOfExpr(E);
  }
  }
}

/// GetTypeForDeclarator - Convert the type for the specified declarator to Type
/// instances.
QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) {
  // long long is a C99 feature.
  if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x &&
      D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong)
    Diag(D.getDeclSpec().getTypeSpecWidthLoc(), diag::ext_longlong);

  QualType T = ConvertDeclSpecToType(D.getDeclSpec(), Context);

  // Apply const/volatile/restrict qualifiers to T.
  T = T.getQualifiedType(D.getDeclSpec().getTypeQualifiers());

  // Walk the DeclTypeInfo, building the recursive type as we go.  DeclTypeInfos
  // are ordered from the identifier out, which is opposite of what we want :).
  for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) {
    const DeclaratorChunk &DeclType = D.getTypeObject(e-i-1);
    switch (DeclType.Kind) {
    default: assert(0 && "Unknown decltype!");
    case DeclaratorChunk::Pointer:
      if (T->isReferenceType()) {
        // C++ 8.3.2p4: There shall be no ... pointers to references ...
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_pointer_to_reference,
             D.getIdentifier()->getName());
        D.setInvalidType(true);
        T = Context.IntTy;
      }

      // Apply the pointer typequals to the pointer object.
      T = Context.getPointerType(T).getQualifiedType(DeclType.Ptr.TypeQuals);
      break;
    case DeclaratorChunk::Reference:
      if (const ReferenceType *RT = T->getAsReferenceType()) {
        // C++ 8.3.2p4: There shall be no references to references ...
        Diag(D.getIdentifierLoc(),
             diag::err_illegal_decl_reference_to_reference,
             D.getIdentifier()->getName());
        D.setInvalidType(true);
        T = RT->getReferenceeType();
      }

      T = Context.getReferenceType(T);
      break;
    case DeclaratorChunk::Array: {
      const DeclaratorChunk::ArrayTypeInfo &ATI = DeclType.Arr;
      Expr *ArraySize = static_cast<Expr*>(ATI.NumElts);
      ArrayType::ArraySizeModifier ASM;
      if (ATI.isStar)
        ASM = ArrayType::Star;
      else if (ATI.hasStatic)
        ASM = ArrayType::Static;
      else
        ASM = ArrayType::Normal;

      // C99 6.7.5.2p1: If the element type is an incomplete or function type,
      // reject it (e.g. void ary[7], struct foo ary[7], void ary[7]())
      if (T->isIncompleteType()) {
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_incomplete_type,
             T.getAsString());
        T = Context.IntTy;
        D.setInvalidType(true);
      } else if (T->isFunctionType()) {
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_of_functions,
             D.getIdentifier()->getName());
        T = Context.getPointerType(T);
        D.setInvalidType(true);
      } else if (const ReferenceType *RT = T->getAsReferenceType()) {
        // C++ 8.3.2p4: There shall be no ... arrays of references ...
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_of_references,
             D.getIdentifier()->getName());
        T = RT->getReferenceeType();
        D.setInvalidType(true);
      } else if (const RecordType *EltTy = T->getAsRecordType()) {
        // If the element type is a struct or union that contains a variadic
        // array, reject it: C99 6.7.2.1p2.
        if (EltTy->getDecl()->hasFlexibleArrayMember()) {
          Diag(DeclType.Loc, diag::err_flexible_array_in_array,
               T.getAsString());
          T = Context.IntTy;
          D.setInvalidType(true);
        }
      }
      // C99 6.7.5.2p1: The size expression shall have integer type.
      if (ArraySize && !ArraySize->getType()->isIntegerType()) {
        Diag(ArraySize->getLocStart(), diag::err_array_size_non_int,
             ArraySize->getType().getAsString(), ArraySize->getSourceRange());
        D.setInvalidType(true);
      }
      llvm::APSInt ConstVal(32);
      // If no expression was provided, we consider it a VLA.
      if (!ArraySize || !ArraySize->isIntegerConstantExpr(ConstVal, Context))
        T = Context.getVariableArrayType(T, ArraySize, ASM, ATI.TypeQuals);
      else {
        // C99 6.7.5.2p1: If the expression is a constant expression, it shall
        // have a value greater than zero.
        if (ConstVal.isSigned()) {
          if (ConstVal.isNegative()) {
            Diag(ArraySize->getLocStart(),
                 diag::err_typecheck_negative_array_size,
                 ArraySize->getSourceRange());
            D.setInvalidType(true);
          } else if (ConstVal == 0) {
            // GCC accepts zero sized static arrays.
            Diag(ArraySize->getLocStart(), diag::ext_typecheck_zero_array_size,
                 ArraySize->getSourceRange());
          }
        }
        T = Context.getConstantArrayType(T, ConstVal, ASM, ATI.TypeQuals);
      }
      // If this is not C99, extwarn about VLA's and C99 array size modifiers.
      if (!getLangOptions().C99 &&
          (ASM != ArrayType::Normal ||
           (ArraySize && !ArraySize->isIntegerConstantExpr(Context))))
        Diag(D.getIdentifierLoc(), diag::ext_vla);
      break;
    }
    case DeclaratorChunk::Function:
      // If the function declarator has a prototype (i.e. it is not () and
      // does not have a K&R-style identifier list), then the arguments are part
      // of the type, otherwise the argument list is ().
      const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
      if (!FTI.hasPrototype) {
        // Simple void foo(), where the incoming T is the result type.
        T = Context.getFunctionTypeNoProto(T);

        // C99 6.7.5.3p3: Reject int(x,y,z) when it's not a function definition.
        if (FTI.NumArgs != 0)
          Diag(FTI.ArgInfo[0].IdentLoc, diag::err_ident_list_in_fn_declaration);

      } else {
        // Otherwise, we have a function with an argument list that is
        // potentially variadic.
        llvm::SmallVector<QualType, 16> ArgTys;

        for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) {
          QualType ArgTy = QualType::getFromOpaquePtr(FTI.ArgInfo[i].TypeInfo);
          assert(!ArgTy.isNull() && "Couldn't parse type?");

          // Look for 'void'.  void is allowed only as a single argument to a
          // function with no other parameters (C99 6.7.5.3p10).  We record
          // int(void) as a FunctionTypeProto with an empty argument list.
          if (ArgTy->isVoidType()) {
            // If this is something like 'float(int, void)', reject it.  'void'
            // is an incomplete type (C99 6.2.5p19) and function decls cannot
            // have arguments of incomplete type.
            if (FTI.NumArgs != 1 || FTI.isVariadic) {
              Diag(DeclType.Loc, diag::err_void_only_param);
              ArgTy = Context.IntTy;
              FTI.ArgInfo[i].TypeInfo = ArgTy.getAsOpaquePtr();
            } else if (FTI.ArgInfo[i].Ident) {
              // Reject, but continue to parse 'int(void abc)'.
              Diag(FTI.ArgInfo[i].IdentLoc,
                   diag::err_param_with_void_type);
              ArgTy = Context.IntTy;
              FTI.ArgInfo[i].TypeInfo = ArgTy.getAsOpaquePtr();
            } else {
              // Reject, but continue to parse 'float(const void)'.
              if (ArgTy.getQualifiers())
                Diag(DeclType.Loc, diag::err_void_param_qualified);

              // Do not add 'void' to the ArgTys list.
              break;
            }
          }

          ArgTys.push_back(ArgTy);
        }
        T = Context.getFunctionType(T, &ArgTys[0], ArgTys.size(),
                                    FTI.isVariadic);
      }
      break;
    }
  }

  return T;
}

Sema::TypeResult Sema::ParseTypeName(Scope *S, Declarator &D) {
  // C99 6.7.6: Type names have no identifier.  This is already validated by
  // the parser.
  assert(D.getIdentifier() == 0 && "Type name should have no identifier!");

  QualType T = GetTypeForDeclarator(D, S);

  assert(!T.isNull() && "GetTypeForDeclarator() returned null type");

  // In this context, we *do not* check D.getInvalidType(). If the declarator
  // type was invalid, GetTypeForDeclarator() still returns a "valid" type,
  // though it will not reflect the user specified type.
  return T.getAsOpaquePtr();
}

// Called from Parser::ParseParenDeclarator().
Sema::TypeResult Sema::ParseParamDeclaratorType(Scope *S, Declarator &D) {
  // Note: parameters have identifiers, but we don't care about them here, we
  // just want the type converted.
  QualType T = GetTypeForDeclarator(D, S);

  assert(!T.isNull() && "GetTypeForDeclarator() returned null type");

  // In this context, we *do not* check D.getInvalidType(). If the declarator
  // type was invalid, GetTypeForDeclarator() still returns a "valid" type,
  // though it will not reflect the user specified type.
  return T.getAsOpaquePtr();
}
<commit_msg><commit_after>//===--- SemaType.cpp - Semantic Analysis for Types -----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by Chris Lattner and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file implements type-related semantic analysis.
//
//===----------------------------------------------------------------------===//

#include "Sema.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/Parse/DeclSpec.h"
#include "clang/Lex/IdentifierTable.h"
#include "clang/Basic/LangOptions.h"
using namespace clang;

/// ConvertDeclSpecToType - Convert the specified declspec to the appropriate
/// type object.  This returns null on error.
static QualType ConvertDeclSpecToType(const DeclSpec &DS, ASTContext &Ctx) {
  // FIXME: Should move the logic from DeclSpec::Finish to here for validity
  // checking.

  switch (DS.getTypeSpecType()) {
  default: return QualType(); // FIXME: Handle unimp cases!
  case DeclSpec::TST_void: return Ctx.VoidTy;
  case DeclSpec::TST_char:
    if (DS.getTypeSpecSign() == DeclSpec::TSS_unspecified)
      return Ctx.CharTy;
    else if (DS.getTypeSpecSign() == DeclSpec::TSS_signed)
      return Ctx.SignedCharTy;
    else {
      assert(DS.getTypeSpecSign() == DeclSpec::TSS_unsigned &&
             "Unknown TSS value");
      return Ctx.UnsignedCharTy;
    }
  case DeclSpec::TST_unspecified:  // Unspecific typespec defaults to int.
  case DeclSpec::TST_int: {
    QualType Result;
    if (DS.getTypeSpecSign() != DeclSpec::TSS_unsigned) {
      switch (DS.getTypeSpecWidth()) {
      case DeclSpec::TSW_unspecified: Result = Ctx.IntTy; break;
      case DeclSpec::TSW_short:       Result = Ctx.ShortTy; break;
      case DeclSpec::TSW_long:        Result = Ctx.LongTy; break;
      case DeclSpec::TSW_longlong:    Result = Ctx.LongLongTy; break;
      }
    } else {
      switch (DS.getTypeSpecWidth()) {
      case DeclSpec::TSW_unspecified: Result = Ctx.UnsignedIntTy; break;
      case DeclSpec::TSW_short:       Result = Ctx.UnsignedShortTy; break;
      case DeclSpec::TSW_long:        Result = Ctx.UnsignedLongTy; break;
      case DeclSpec::TSW_longlong:    Result = Ctx.UnsignedLongLongTy; break;
      }
    }
    // Handle complex integer types.
    if (DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified)
      return Result;
    assert(DS.getTypeSpecComplex() == DeclSpec::TSC_complex &&
           "FIXME: imaginary types not supported yet!");
    return Ctx.getComplexType(Result);
  }
  case DeclSpec::TST_float:
    if (DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified)
      return Ctx.FloatTy;
    assert(DS.getTypeSpecComplex() == DeclSpec::TSC_complex &&
           "FIXME: imaginary types not supported yet!");
    return Ctx.getComplexType(Ctx.FloatTy);

  case DeclSpec::TST_double: {
    bool isLong = DS.getTypeSpecWidth() == DeclSpec::TSW_long;
    QualType T = isLong ? Ctx.LongDoubleTy : Ctx.DoubleTy;
    if (DS.getTypeSpecComplex() == DeclSpec::TSC_unspecified)
      return T;
    assert(DS.getTypeSpecComplex() == DeclSpec::TSC_complex &&
           "FIXME: imaginary types not supported yet!");
    return Ctx.getComplexType(T);
  }
  case DeclSpec::TST_bool:         // _Bool or bool
    return Ctx.BoolTy;
  case DeclSpec::TST_decimal32:    // _Decimal32
  case DeclSpec::TST_decimal64:    // _Decimal64
  case DeclSpec::TST_decimal128:   // _Decimal128
    assert(0 && "FIXME: GNU decimal extensions not supported yet!");
  case DeclSpec::TST_enum:
  case DeclSpec::TST_union:
  case DeclSpec::TST_struct: {
    Decl *D = static_cast<Decl *>(DS.getTypeRep());
    assert(D && "Didn't get a decl for a enum/union/struct?");
    assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 &&
           DS.getTypeSpecSign() == 0 &&
           "Can't handle qualifiers on typedef names yet!");
    // TypeQuals handled by caller.
    return Ctx.getTagDeclType(cast<TagDecl>(D));
  }
  case DeclSpec::TST_typedef: {
    Decl *D = static_cast<Decl *>(DS.getTypeRep());
    assert(D && "Didn't get a decl for a typedef?");
    assert(DS.getTypeSpecWidth() == 0 && DS.getTypeSpecComplex() == 0 &&
           DS.getTypeSpecSign() == 0 &&
           "Can't handle qualifiers on typedef names yet!");
    // FIXME: Adding a TST_objcInterface clause doesn't seem ideal, so
    // we have this "hack" for now...
    if (isa<ObjcInterfaceDecl>(D))
      return Ctx.getObjcInterfaceType(cast<ObjcInterfaceDecl>(D));
    // TypeQuals handled by caller.
    return Ctx.getTypedefType(cast<TypedefDecl>(D));
  }
  case DeclSpec::TST_typeofType: {
    QualType T = QualType::getFromOpaquePtr(DS.getTypeRep());
    assert(!T.isNull() && "Didn't get a type for typeof?");
    // TypeQuals handled by caller.
    return Ctx.getTypeOfType(T);
  }
  case DeclSpec::TST_typeofExpr: {
    Expr *E = static_cast<Expr *>(DS.getTypeRep());
    assert(E && "Didn't get an expression for typeof?");
    // TypeQuals handled by caller.
    return Ctx.getTypeOfExpr(E);
  }
  }
}

/// GetTypeForDeclarator - Convert the type for the specified declarator to Type
/// instances.
QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S) {
  // long long is a C99 feature.
  if (!getLangOptions().C99 && !getLangOptions().CPlusPlus0x &&
      D.getDeclSpec().getTypeSpecWidth() == DeclSpec::TSW_longlong)
    Diag(D.getDeclSpec().getTypeSpecWidthLoc(), diag::ext_longlong);

  QualType T = ConvertDeclSpecToType(D.getDeclSpec(), Context);

  // Apply const/volatile/restrict qualifiers to T.
  T = T.getQualifiedType(D.getDeclSpec().getTypeQualifiers());

  // Walk the DeclTypeInfo, building the recursive type as we go.  DeclTypeInfos
  // are ordered from the identifier out, which is opposite of what we want :).
  for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) {
    const DeclaratorChunk &DeclType = D.getTypeObject(e-i-1);
    switch (DeclType.Kind) {
    default: assert(0 && "Unknown decltype!");
    case DeclaratorChunk::Pointer:
      if (T->isReferenceType()) {
        // C++ 8.3.2p4: There shall be no ... pointers to references ...
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_pointer_to_reference,
             D.getIdentifier()->getName());
        D.setInvalidType(true);
        T = Context.IntTy;
      }

      // Apply the pointer typequals to the pointer object.
      T = Context.getPointerType(T).getQualifiedType(DeclType.Ptr.TypeQuals);
      break;
    case DeclaratorChunk::Reference:
      if (const ReferenceType *RT = T->getAsReferenceType()) {
        // C++ 8.3.2p4: There shall be no references to references ...
        Diag(D.getIdentifierLoc(),
             diag::err_illegal_decl_reference_to_reference,
             D.getIdentifier()->getName());
        D.setInvalidType(true);
        T = RT->getReferenceeType();
      }

      T = Context.getReferenceType(T);
      break;
    case DeclaratorChunk::Array: {
      const DeclaratorChunk::ArrayTypeInfo &ATI = DeclType.Arr;
      Expr *ArraySize = static_cast<Expr*>(ATI.NumElts);
      ArrayType::ArraySizeModifier ASM;
      if (ATI.isStar)
        ASM = ArrayType::Star;
      else if (ATI.hasStatic)
        ASM = ArrayType::Static;
      else
        ASM = ArrayType::Normal;

      // C99 6.7.5.2p1: If the element type is an incomplete or function type,
      // reject it (e.g. void ary[7], struct foo ary[7], void ary[7]())
      if (T->isIncompleteType()) {
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_incomplete_type,
             T.getAsString());
        T = Context.IntTy;
        D.setInvalidType(true);
      } else if (T->isFunctionType()) {
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_of_functions,
             D.getIdentifier()->getName());
        T = Context.getPointerType(T);
        D.setInvalidType(true);
      } else if (const ReferenceType *RT = T->getAsReferenceType()) {
        // C++ 8.3.2p4: There shall be no ... arrays of references ...
        Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_of_references,
             D.getIdentifier()->getName());
        T = RT->getReferenceeType();
        D.setInvalidType(true);
      } else if (const RecordType *EltTy = T->getAsRecordType()) {
        // If the element type is a struct or union that contains a variadic
        // array, reject it: C99 6.7.2.1p2.
        if (EltTy->getDecl()->hasFlexibleArrayMember()) {
          Diag(DeclType.Loc, diag::err_flexible_array_in_array,
               T.getAsString());
          T = Context.IntTy;
          D.setInvalidType(true);
        }
      }
      // C99 6.7.5.2p1: The size expression shall have integer type.
      if (ArraySize && !ArraySize->getType()->isIntegerType()) {
        Diag(ArraySize->getLocStart(), diag::err_array_size_non_int,
             ArraySize->getType().getAsString(), ArraySize->getSourceRange());
        D.setInvalidType(true);
      }
      llvm::APSInt ConstVal(32);
      // If no expression was provided, we consider it a VLA.
      if (!ArraySize || !ArraySize->isIntegerConstantExpr(ConstVal, Context))
        T = Context.getVariableArrayType(T, ArraySize, ASM, ATI.TypeQuals);
      else {
        // C99 6.7.5.2p1: If the expression is a constant expression, it shall
        // have a value greater than zero.
        if (ConstVal.isSigned()) {
          if (ConstVal.isNegative()) {
            Diag(ArraySize->getLocStart(),
                 diag::err_typecheck_negative_array_size,
                 ArraySize->getSourceRange());
            D.setInvalidType(true);
          } else if (ConstVal == 0) {
            // GCC accepts zero sized static arrays.
            Diag(ArraySize->getLocStart(), diag::ext_typecheck_zero_array_size,
                 ArraySize->getSourceRange());
          }
        }
        T = Context.getConstantArrayType(T, ConstVal, ASM, ATI.TypeQuals);
      }
      // If this is not C99, extwarn about VLA's and C99 array size modifiers.
      if (!getLangOptions().C99 &&
          (ASM != ArrayType::Normal ||
           (ArraySize && !ArraySize->isIntegerConstantExpr(Context))))
        Diag(D.getIdentifierLoc(), diag::ext_vla);
      break;
    }
    case DeclaratorChunk::Function:
      // If the function declarator has a prototype (i.e. it is not () and
      // does not have a K&R-style identifier list), then the arguments are part
      // of the type, otherwise the argument list is ().
      const DeclaratorChunk::FunctionTypeInfo &FTI = DeclType.Fun;
      if (!FTI.hasPrototype) {
        // Simple void foo(), where the incoming T is the result type.
        T = Context.getFunctionTypeNoProto(T);

        // C99 6.7.5.3p3: Reject int(x,y,z) when it's not a function definition.
        if (FTI.NumArgs != 0)
          Diag(FTI.ArgInfo[0].IdentLoc, diag::err_ident_list_in_fn_declaration);

      } else {
        // Otherwise, we have a function with an argument list that is
        // potentially variadic.
        llvm::SmallVector<QualType, 16> ArgTys;

        for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) {
          QualType ArgTy = QualType::getFromOpaquePtr(FTI.ArgInfo[i].TypeInfo);
          assert(!ArgTy.isNull() && "Couldn't parse type?");
          //
          // Perform the default function/array conversion (C99 6.7.5.3p[7,8]).
          // This matches the conversion that is done in
          // Sema::ParseParamDeclarator(). Without this conversion, the
          // argument type in the function prototype *will not* match the
          // type in ParmVarDecl (which makes the code generator unhappy).
          //
          // FIXME: We still apparently need the conversion in
          // Sema::ParseParamDeclarator(). This doesn't make any sense, since
          // it should be driving off the type being created here.
          //
          // FIXME: If a source translation tool needs to see the original type,
          // then we need to consider storing both types somewhere...
          //
          if (const ArrayType *AT = ArgTy->getAsArrayType())
            ArgTy = Context.getPointerType(AT->getElementType());
          else if (ArgTy->isFunctionType())
            ArgTy = Context.getPointerType(ArgTy);
          // Look for 'void'.  void is allowed only as a single argument to a
          // function with no other parameters (C99 6.7.5.3p10).  We record
          // int(void) as a FunctionTypeProto with an empty argument list.
          else if (ArgTy->isVoidType()) {
            // If this is something like 'float(int, void)', reject it.  'void'
            // is an incomplete type (C99 6.2.5p19) and function decls cannot
            // have arguments of incomplete type.
            if (FTI.NumArgs != 1 || FTI.isVariadic) {
              Diag(DeclType.Loc, diag::err_void_only_param);
              ArgTy = Context.IntTy;
              FTI.ArgInfo[i].TypeInfo = ArgTy.getAsOpaquePtr();
            } else if (FTI.ArgInfo[i].Ident) {
              // Reject, but continue to parse 'int(void abc)'.
              Diag(FTI.ArgInfo[i].IdentLoc,
                   diag::err_param_with_void_type);
              ArgTy = Context.IntTy;
              FTI.ArgInfo[i].TypeInfo = ArgTy.getAsOpaquePtr();
            } else {
              // Reject, but continue to parse 'float(const void)'.
              if (ArgTy.getQualifiers())
                Diag(DeclType.Loc, diag::err_void_param_qualified);

              // Do not add 'void' to the ArgTys list.
              break;
            }
          }

          ArgTys.push_back(ArgTy);
        }
        T = Context.getFunctionType(T, &ArgTys[0], ArgTys.size(),
                                    FTI.isVariadic);
      }
      break;
    }
  }

  return T;
}

Sema::TypeResult Sema::ParseTypeName(Scope *S, Declarator &D) {
  // C99 6.7.6: Type names have no identifier.  This is already validated by
  // the parser.
  assert(D.getIdentifier() == 0 && "Type name should have no identifier!");

  QualType T = GetTypeForDeclarator(D, S);

  assert(!T.isNull() && "GetTypeForDeclarator() returned null type");

  // In this context, we *do not* check D.getInvalidType(). If the declarator
  // type was invalid, GetTypeForDeclarator() still returns a "valid" type,
  // though it will not reflect the user specified type.
  return T.getAsOpaquePtr();
}

// Called from Parser::ParseParenDeclarator().
Sema::TypeResult Sema::ParseParamDeclaratorType(Scope *S, Declarator &D) {
  // Note: parameters have identifiers, but we don't care about them here, we
  // just want the type converted.
  QualType T = GetTypeForDeclarator(D, S);

  assert(!T.isNull() && "GetTypeForDeclarator() returned null type");

  // In this context, we *do not* check D.getInvalidType(). If the declarator
  // type was invalid, GetTypeForDeclarator() still returns a "valid" type,
  // though it will not reflect the user specified type.
  return T.getAsOpaquePtr();
}
<|endoftext|>


<commit_before>/**************************************************************************
 *
 * Copyright 2013 VMware, Inc.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include <limits.h> // for CHAR_MAX
#include <getopt.h>

#include <string>

#include "cli.hpp"

#include "os_string.hpp"

#include "trace_callset.hpp"
#include "trace_parser.hpp"
#include "trace_writer.hpp"

static const char *synopsis = "Stream editing of a trace.";

static void
usage(void)
{
    std::cout
        << "usage: apitrace sed [OPTIONS] TRACE_FILE...\n"
        << synopsis << "\n"
        "\n"
        "    -h, --help               Show detailed help for sed options and exit\n"
        "    -e s/SEARCH/REPLACE/     Search and replace a symbol. Use @file(<path>)\n"
        "                             to read SEARCH or REPLACE from a file.\n"
        "                             Any character (not just /) can be used as \n"
        "                             separator.\n"
        "                             XXX: Only works for enums and strings.\n"
        "    -o, --output=TRACE_FILE  Output trace file\n"
        "    --property=NAME=VALUE    Set a property\n"
        "    --calls=CALLSET          Apply search/replace only to specified calls.\n"
        "                             All other calls remain untouched.\n"
    ;
}

enum {
    PROPERTY_OPT = CHAR_MAX + 1,
    CALLS_OPT
};

const static char *
shortOptions = "ho:e:";

const static struct option
longOptions[] = {
    {"help", no_argument, 0, 'h'},
    {"output", required_argument, 0, 'o'},
    {"property", required_argument, 0, PROPERTY_OPT},
    {"calls", required_argument, 0, CALLS_OPT},
    {0, 0, 0, 0}
};

using namespace trace;

/**
 * A visitor that replaces symbol constants.
 */
class Replacer : public Visitor
{
protected:
    std::string searchString;
    std::string replaceString;

    bool isPointer = false;
    unsigned long long searchPointer = 0;
    unsigned long long replacePointer = 0;

public:
    Replacer(const std::string & _searchString, const std::string & _replaceString) :
        searchString(_searchString),
        replaceString(_replaceString)
    {
        if (searchString.length() > 2 &&
            searchString[0] == '0' &&
            searchString[1] == 'x') {
            isPointer = true;
            searchPointer = std::stoull(searchString, 0, 16);
            assert(searchPointer);
            replacePointer = std::stoull(replaceString, 0, 16);
            assert(replacePointer);
        }
    }

    virtual ~Replacer() {
    }

    void visit(Null *) override {
    }

    void visit(Bool *node) override {
    }

    void visit(SInt *node) override {
    }

    void visit(UInt *node) override {
    }

    void visit(Float *node) override {
    }

    void visit(Double *node) override {
    }

    void visit(String *node) override {
        if (!searchString.compare(node->value)) {
            size_t len = replaceString.length() + 1;
            delete [] node->value;
            char *str = new char [len];
            memcpy(str, replaceString.c_str(), len);
            node->value = str;
        }
    }

    void visit(WString *node) override {
    }

    void visit(Enum *node) override {
        const EnumValue *it = node->lookup();
        if (it) {
            if (searchString.compare(it->name) == 0) {
                const EnumSig *sig = node->sig;
                for (unsigned i = 0; i < sig->num_values; ++i) {
                    if (replaceString.compare(sig->values[i].name) == 0) {
                        node->value = sig->values[i].value;
                        break;
                    }
                }
            }
        }
    }

    void visit(Bitmask *bitmask) override {
    }

    void visit(Struct *s) override {
        for (auto memberValue : s->members) {
            _visit(memberValue);
        }
    }

    void visit(Array *array) override {
        for (auto & value : array->values) {
            _visit(value);
        }
    }

    void visit(Blob *blob) override {
    }

    void visit(Pointer *p) override {
        if (isPointer &&
            p->value == searchPointer) {
            p->value = replacePointer;
        }
    }

    void visit(Repr *r) override {
        _visit(r->humanValue);
    }

    void visit(Call *call) {
        for (auto & arg : call->args) {
            if (arg.value) {
                _visit(arg.value);
            }
        }

        if (call->ret) {
            _visit(call->ret);
        }
    }
};

typedef std::list<Replacer> Replacements;

static int
sed_trace(Replacements &replacements,
          const trace::Properties &extraProperties,
          const char *inFileName,
          std::string &outFileName,
          const trace::CallSet &calls)
{
    trace::Parser p;

    if (!p.open(inFileName)) {
        std::cerr << "error: failed to open " << inFileName << "\n";
        return 1;
    }

    /* Prepare outFileName file and writer for outFileName. */
    if (outFileName.empty()) {
        os::String base(inFileName);
        base.trimExtension();

        outFileName = std::string(base.str()) + std::string("-sed.trace");
    }

    trace::Properties properties(p.getProperties());
    for (auto & kv : extraProperties) {
        properties[kv.first] = kv.second;
    }

    trace::Writer writer;
    if (!writer.open(outFileName.c_str(), p.getVersion(), properties)) {
        std::cerr << "error: failed to create " << outFileName << "\n";
        return 1;
    }

    trace::Call *call;
    while ((call = p.parse_call())) {
        if (calls.empty() || calls.contains(*call)) {
            for (auto & replacement : replacements) {
                replacement.visit(call);
            }
        }

        writer.writeCall(call);

        delete call;
    }

    std::cerr << "Edited trace is available as " << outFileName << "\n";

    return 0;
}

/**
 * Parse a string in the format "s/SEARCH/REPLACE/".
 */
static bool
parseSubstOpt(Replacements &replacements, const char *opt)
{
    char separator;

    if (*opt++ != 's') {
        return false;
    }

    separator = *opt++;

    // Parse the search pattern
    const char *search_begin = opt;
    while (*opt != separator) {
        if (*opt == 0) {
            return false;
        }
        ++opt;
    }
    const char *search_end = opt++;

    // Parse the replace pattern
    const char *replace_begin = opt;
    while (*opt != separator) {
        if (*opt == 0) {
            return false;
        }
        ++opt;
    }
    const char *replace_end = opt++;

    if (*opt != 0) {
        return false;
    }

    std::string search(search_begin, search_end);
    std::string replace(replace_begin, replace_end);

    // If search or replace strings are taken from a file, read the file
    std::string file_subst = "@file(";

    for (int i = 0; i < 2; i++) {
        std::string *str = i ? &search : &replace;

        if (!str->compare(0, file_subst.length(), file_subst)) {
            if ((*str)[str->length()-1] != ')') {
                return false;
            }

            std::string fname = str->substr(file_subst.length());
            fname[fname.length()-1] = 0;
            FILE *f = fopen(fname.c_str(), "rt");
            if (!f) {
                std::cerr << "error: cannot open file " << fname << "\n";
                return false;
            }
            char buf[1024];
            (*str) = "";
            while (!feof(f)) {
                if (fgets(buf, 1024, f)) {
                    str->append(buf);
                }
            }
            fclose(f);
        }
    }

    replacements.push_back(Replacer(search, replace));

    return true;
}

static int
command(int argc, char *argv[])
{
    Replacements replacements;
    trace::Properties extraProperties;
    std::string outFileName;
    trace::CallSet calls;

    int opt;
    while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
        switch (opt) {
        case 'h':
            usage();
            return 0;
        case 'o':
            outFileName = optarg;
            break;
        case 'e':
            if (!parseSubstOpt(replacements, optarg)) {
                std::cerr << "error: invalid replacement pattern `" << optarg << "`\n";
                return 1;
            }
            break;
        case PROPERTY_OPT:
            {
               const char *sep = strchr(optarg, '=');
               if (sep == nullptr) {
                   std::cerr << "error: bad property `" << optarg << "`\n";
                   return 1;
               }
               std::string name(static_cast<const char *>(optarg), sep);
               std::string value(sep + 1);
               extraProperties[name] = value;
               break;
            }
        case CALLS_OPT:
            calls.merge(optarg);
            break;
        default:
            std::cerr << "error: unexpected option `" << (char)opt << "`\n";
            usage();
            return 1;
        }
    }

    if (optind >= argc) {
        std::cerr << "error: apitrace sed requires a trace file as an argument.\n";
        usage();
        return 1;
    }

    if (argc > optind + 1) {
        std::cerr << "error: extraneous arguments:";
        for (int i = optind + 1; i < argc; i++) {
            std::cerr << " " << argv[i];
        }
        std::cerr << "\n";
        usage();
        return 1;
    }

    return sed_trace(replacements, extraProperties, argv[optind], outFileName, calls);
}

const Command sed_command = {
    "sed",
    synopsis,
    usage,
    command
};
<commit_msg>cli: Allow search-replace of bitflags.<commit_after>/**************************************************************************
 *
 * Copyright 2013 VMware, Inc.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 *
 **************************************************************************/

#include <limits.h> // for CHAR_MAX
#include <getopt.h>

#include <string>

#include "cli.hpp"

#include "os_string.hpp"

#include "trace_callset.hpp"
#include "trace_parser.hpp"
#include "trace_writer.hpp"

static const char *synopsis = "Stream editing of a trace.";

static void
usage(void)
{
    std::cout
        << "usage: apitrace sed [OPTIONS] TRACE_FILE...\n"
        << synopsis << "\n"
        "\n"
        "    -h, --help               Show detailed help for sed options and exit\n"
        "    -e s/SEARCH/REPLACE/     Search and replace a symbol. Use @file(<path>)\n"
        "                             to read SEARCH or REPLACE from a file.\n"
        "                             Any character (not just /) can be used as \n"
        "                             separator.\n"
        "                             XXX: Only works for enums and strings.\n"
        "    -o, --output=TRACE_FILE  Output trace file\n"
        "    --property=NAME=VALUE    Set a property\n"
        "    --calls=CALLSET          Apply search/replace only to specified calls.\n"
        "                             All other calls remain untouched.\n"
    ;
}

enum {
    PROPERTY_OPT = CHAR_MAX + 1,
    CALLS_OPT
};

const static char *
shortOptions = "ho:e:";

const static struct option
longOptions[] = {
    {"help", no_argument, 0, 'h'},
    {"output", required_argument, 0, 'o'},
    {"property", required_argument, 0, PROPERTY_OPT},
    {"calls", required_argument, 0, CALLS_OPT},
    {0, 0, 0, 0}
};

using namespace trace;

/**
 * A visitor that replaces symbol constants.
 */
class Replacer : public Visitor
{
protected:
    std::string searchString;
    std::string replaceString;

    bool isPointer = false;
    unsigned long long searchPointer = 0;
    unsigned long long replacePointer = 0;

public:
    Replacer(const std::string & _searchString, const std::string & _replaceString) :
        searchString(_searchString),
        replaceString(_replaceString)
    {
        if (searchString.length() > 2 &&
            searchString[0] == '0' &&
            searchString[1] == 'x') {
            isPointer = true;
            searchPointer = std::stoull(searchString, 0, 16);
            assert(searchPointer);
            replacePointer = std::stoull(replaceString, 0, 16);
            assert(replacePointer);
        }
    }

    virtual ~Replacer() {
    }

    void visit(Null *) override {
    }

    void visit(Bool *node) override {
    }

    void visit(SInt *node) override {
    }

    void visit(UInt *node) override {
    }

    void visit(Float *node) override {
    }

    void visit(Double *node) override {
    }

    void visit(String *node) override {
        if (!searchString.compare(node->value)) {
            size_t len = replaceString.length() + 1;
            delete [] node->value;
            char *str = new char [len];
            memcpy(str, replaceString.c_str(), len);
            node->value = str;
        }
    }

    void visit(WString *node) override {
    }

    void visit(Enum *node) override {
        const EnumValue *it = node->lookup();
        if (it) {
            if (searchString.compare(it->name) == 0) {
                const EnumSig *sig = node->sig;
                for (unsigned i = 0; i < sig->num_values; ++i) {
                    if (replaceString.compare(sig->values[i].name) == 0) {
                        node->value = sig->values[i].value;
                        break;
                    }
                }
            }
        }
    }

    void visit(Bitmask *bitmask) override {
        const BitmaskSig *sig = bitmask->sig;
        for (const BitmaskFlag *it = sig->flags; it != sig->flags + sig->num_flags; ++it) {
            if (it->value && (bitmask->value & it->value) == it->value &&
                searchString.compare(it->name) == 0) {
                bitmask->value &= ~it->value;
                bitmask->value |= std::stoull(replaceString);
                break;
            }
        }
    }

    void visit(Struct *s) override {
        for (auto memberValue : s->members) {
            _visit(memberValue);
        }
    }

    void visit(Array *array) override {
        for (auto & value : array->values) {
            _visit(value);
        }
    }

    void visit(Blob *blob) override {
    }

    void visit(Pointer *p) override {
        if (isPointer &&
            p->value == searchPointer) {
            p->value = replacePointer;
        }
    }

    void visit(Repr *r) override {
        _visit(r->humanValue);
    }

    void visit(Call *call) {
        for (auto & arg : call->args) {
            if (arg.value) {
                _visit(arg.value);
            }
        }

        if (call->ret) {
            _visit(call->ret);
        }
    }
};

typedef std::list<Replacer> Replacements;

static int
sed_trace(Replacements &replacements,
          const trace::Properties &extraProperties,
          const char *inFileName,
          std::string &outFileName,
          const trace::CallSet &calls)
{
    trace::Parser p;

    if (!p.open(inFileName)) {
        std::cerr << "error: failed to open " << inFileName << "\n";
        return 1;
    }

    /* Prepare outFileName file and writer for outFileName. */
    if (outFileName.empty()) {
        os::String base(inFileName);
        base.trimExtension();

        outFileName = std::string(base.str()) + std::string("-sed.trace");
    }

    trace::Properties properties(p.getProperties());
    for (auto & kv : extraProperties) {
        properties[kv.first] = kv.second;
    }

    trace::Writer writer;
    if (!writer.open(outFileName.c_str(), p.getVersion(), properties)) {
        std::cerr << "error: failed to create " << outFileName << "\n";
        return 1;
    }

    trace::Call *call;
    while ((call = p.parse_call())) {
        if (calls.empty() || calls.contains(*call)) {
            for (auto & replacement : replacements) {
                replacement.visit(call);
            }
        }

        writer.writeCall(call);

        delete call;
    }

    std::cerr << "Edited trace is available as " << outFileName << "\n";

    return 0;
}

/**
 * Parse a string in the format "s/SEARCH/REPLACE/".
 */
static bool
parseSubstOpt(Replacements &replacements, const char *opt)
{
    char separator;

    if (*opt++ != 's') {
        return false;
    }

    separator = *opt++;

    // Parse the search pattern
    const char *search_begin = opt;
    while (*opt != separator) {
        if (*opt == 0) {
            return false;
        }
        ++opt;
    }
    const char *search_end = opt++;

    // Parse the replace pattern
    const char *replace_begin = opt;
    while (*opt != separator) {
        if (*opt == 0) {
            return false;
        }
        ++opt;
    }
    const char *replace_end = opt++;

    if (*opt != 0) {
        return false;
    }

    std::string search(search_begin, search_end);
    std::string replace(replace_begin, replace_end);

    // If search or replace strings are taken from a file, read the file
    std::string file_subst = "@file(";

    for (int i = 0; i < 2; i++) {
        std::string *str = i ? &search : &replace;

        if (!str->compare(0, file_subst.length(), file_subst)) {
            if ((*str)[str->length()-1] != ')') {
                return false;
            }

            std::string fname = str->substr(file_subst.length());
            fname[fname.length()-1] = 0;
            FILE *f = fopen(fname.c_str(), "rt");
            if (!f) {
                std::cerr << "error: cannot open file " << fname << "\n";
                return false;
            }
            char buf[1024];
            (*str) = "";
            while (!feof(f)) {
                if (fgets(buf, 1024, f)) {
                    str->append(buf);
                }
            }
            fclose(f);
        }
    }

    replacements.push_back(Replacer(search, replace));

    return true;
}

static int
command(int argc, char *argv[])
{
    Replacements replacements;
    trace::Properties extraProperties;
    std::string outFileName;
    trace::CallSet calls;

    int opt;
    while ((opt = getopt_long(argc, argv, shortOptions, longOptions, NULL)) != -1) {
        switch (opt) {
        case 'h':
            usage();
            return 0;
        case 'o':
            outFileName = optarg;
            break;
        case 'e':
            if (!parseSubstOpt(replacements, optarg)) {
                std::cerr << "error: invalid replacement pattern `" << optarg << "`\n";
                return 1;
            }
            break;
        case PROPERTY_OPT:
            {
               const char *sep = strchr(optarg, '=');
               if (sep == nullptr) {
                   std::cerr << "error: bad property `" << optarg << "`\n";
                   return 1;
               }
               std::string name(static_cast<const char *>(optarg), sep);
               std::string value(sep + 1);
               extraProperties[name] = value;
               break;
            }
        case CALLS_OPT:
            calls.merge(optarg);
            break;
        default:
            std::cerr << "error: unexpected option `" << (char)opt << "`\n";
            usage();
            return 1;
        }
    }

    if (optind >= argc) {
        std::cerr << "error: apitrace sed requires a trace file as an argument.\n";
        usage();
        return 1;
    }

    if (argc > optind + 1) {
        std::cerr << "error: extraneous arguments:";
        for (int i = optind + 1; i < argc; i++) {
            std::cerr << " " << argv[i];
        }
        std::cerr << "\n";
        usage();
        return 1;
    }

    return sed_trace(replacements, extraProperties, argv[optind], outFileName, calls);
}

const Command sed_command = {
    "sed",
    synopsis,
    usage,
    command
};
<|endoftext|>


<commit_before>#include <vector>
#include <fstream>
#include <iostream>
#include <cassert>
#include <cmath>
#include "RandomNumberGenerator.h"

using namespace std;
using namespace DNest3;

class Data
{
	private:
		vector<double> logw, run_id;
		vector< vector<double> > scalars;
		int N;

	public:
		Data()
		{ }

		void load()
		{
			logw.clear(); run_id.clear(); scalars.clear();

			double temp;
			fstream fin("logw.txt", ios::in);
			int k = 0;
			while(fin >> temp)
			{
				logw.push_back(temp);
				if(k == 0)
					run_id.push_back(0);
				else if(logw.back() == -1.)
					run_id.push_back(run_id.back() + 1);
				else
					run_id.push_back(run_id.back());
				k++;
			}
			fin.close();

			fin.open("scalars.txt", ios::in);
			double temp2;
			while(fin >> temp && fin >> temp2)
			{
				vector<double> vec(2);
				vec[0] = temp; vec[1] = temp2;
				scalars.push_back(vec);
			}
			fin.close();

			if(scalars.size() < logw.size())
			{
				logw.erase(logw.begin() + scalars.size(),
						logw.end());
				run_id.erase(run_id.begin() + scalars.size(),
						run_id.end());
			}
			if(logw.size() < scalars.size())
			{
				scalars.erase(scalars.begin() + logw.size(),
						scalars.end());
			}

			assert(logw.size() == scalars.size());
			assert(logw.size() == run_id.size());
			N = logw.size();
			cout<<"# Loaded "<<N<<" points."<<endl;
	}

	friend class Assignment;
};

/************************************************************************/

class Assignment
{
	private:
		const Data& data;
		vector<double> logX;

	public:
		Assignment(const Data& data)
		:data(data)
		{
		}

		void initialise()
		{
			logX.assign(data.N, 0.);

			for(int i=0; i<data.N; i++)
			{
				if(data.logw[i] == -1.)
					logX[i] = log(randomU());
				else
					logX[i] = logX[i-1] + log(randomU());
			}
		}

		int update_all()
		{
			int total = 0;
			for(int i=0; i<data.N; i++)
			{
				total += update(i);
				cout<<"."<<flush;
			}
			cout<<endl;
			return total;
		}

		// Gibbs sample one value
		int update(int i)
		{
			double proposal;

			// Range of values for proposal
			double lower = -1E300;
			double upper = 0.;

			// Lower limit -- next point in same run (if it exists)
  			if( (i != data.N) &&
					(data.run_id[i+1] == data.run_id[i]))
    				lower = logX[i+1];
			// Upper limit -- previous point in same run (if it exists)
  			if( (i != 1) &&
					(data.run_id[i-1] == data.run_id[i]))
    				upper = logX[i-1];

			// If lower limit exists, generate uniformly between limits
			if(lower != -1E300)
				proposal = lower + (upper - lower)*randomU();
			else // otherwise use exponential distribution
				proposal = upper + log(randomU());

			// Measure inconsistency
			int inconsistency_old = 0;
			int inconsistency_new = 0;

			bool outside, inside;
			for(int j=0; j<data.N; j++)
			{
				if(data.run_id[j] != data.run_id[i])
				{
					// See if distribution j is outside,
					// inside, or unknown
					outside = true;
					inside  = true;
					for(int k=0; k<2; k++)
					{
						if(data.scalars[j][k] > data.scalars[i][k])
							outside = false;
						if(data.scalars[j][k] < data.scalars[i][k])
							inside = false;
					}

					if(outside && (logX[j] < logX[i]))
						inconsistency_old++;
					if(inside  && (logX[j] > logX[i]))
						inconsistency_old++;

					if(outside && (logX[j] < proposal))
						inconsistency_new++;
					if(inside  && (logX[j] > proposal))
						inconsistency_new++;
				}
			}

			int inconsistency = inconsistency_old;
			if(inconsistency_new <= inconsistency_old)
			{
				logX[i] = proposal;
				inconsistency = inconsistency_new;
			}
			return inconsistency;
		}

		void save()
		{
			fstream fout("logX.txt", ios::out);
			for(int i=0; i<data.N; i++)
				fout<<logX[i]<<endl;
			fout.close();
		}

};

int main()
{
	RandomNumberGenerator::initialise_instance();
	RandomNumberGenerator::get_instance().set_seed(time(0));

	Data data;
	data.load();

	Assignment assignment(data);
	assignment.initialise();

	for(int i=0; i<100000000; i++)
	{
		cout<<assignment.update_all()<<endl;
		assignment.save();
	}
	return 0;
}

<commit_msg>Fixed indexing bugs<commit_after>#include <vector>
#include <fstream>
#include <iostream>
#include <cassert>
#include <cmath>
#include "RandomNumberGenerator.h"

using namespace std;
using namespace DNest3;

class Data
{
	private:
		vector<double> logw, run_id;
		vector< vector<double> > scalars;
		int N;

	public:
		Data()
		{ }

		void load()
		{
			logw.clear(); run_id.clear(); scalars.clear();

			double temp;
			fstream fin("logw.txt", ios::in);
			int k = 0;
			while(fin >> temp)
			{
				logw.push_back(temp);
				if(k == 0)
					run_id.push_back(0);
				else if(logw.back() == -1.)
					run_id.push_back(run_id.back() + 1);
				else
					run_id.push_back(run_id.back());
				k++;
			}
			fin.close();

			fin.open("scalars.txt", ios::in);
			double temp2;
			while(fin >> temp && fin >> temp2)
			{
				vector<double> vec(2);
				vec[0] = temp; vec[1] = temp2;
				scalars.push_back(vec);
			}
			fin.close();

			if(scalars.size() < logw.size())
			{
				logw.erase(logw.begin() + scalars.size(),
						logw.end());
				run_id.erase(run_id.begin() + scalars.size(),
						run_id.end());
			}
			if(logw.size() < scalars.size())
			{
				scalars.erase(scalars.begin() + logw.size(),
						scalars.end());
			}

			assert(logw.size() == scalars.size());
			assert(logw.size() == run_id.size());
			N = logw.size();
			cout<<"# Loaded "<<N<<" points."<<endl;
	}

	friend class Assignment;
};

/************************************************************************/

class Assignment
{
	private:
		const Data& data;
		vector<double> logX;

	public:
		Assignment(const Data& data)
		:data(data)
		{
		}

		void initialise()
		{
			logX.assign(data.N, 0.);

			for(int i=0; i<data.N; i++)
			{
				if(data.logw[i] == -1.)
					logX[i] = log(randomU());
				else
					logX[i] = logX[i-1] + log(randomU());
			}
		}

		int update_all()
		{
			int total = 0;
			for(int i=0; i<data.N; i++)
			{
				total += update(i);
				cout<<"."<<flush;
			}
			cout<<endl;
			return total;
		}

		// Gibbs sample one value
		int update(int i)
		{
			double proposal;

			// Range of values for proposal
			double lower = -1E300;
			double upper = 0.;

			// Lower limit -- next point in same run (if it exists)
  			if( (i != (data.N-1)) &&
					(data.run_id[i+1] == data.run_id[i]))
    				lower = logX[i+1];
			// Upper limit -- previous point in same run (if it exists)
  			if( (i != 0) &&
					(data.run_id[i-1] == data.run_id[i]))
    				upper = logX[i-1];

			// If lower limit exists, generate uniformly between limits
			if(lower != -1E300)
				proposal = lower + (upper - lower)*randomU();
			else // otherwise use exponential distribution
				proposal = upper + log(randomU());

			// Measure inconsistency
			int inconsistency_old = 0;
			int inconsistency_new = 0;

			bool outside, inside;
			for(int j=0; j<data.N; j++)
			{
				if(data.run_id[j] != data.run_id[i])
				{
					// See if distribution j is outside,
					// inside, or unknown
					outside = true;
					inside  = true;
					for(int k=0; k<2; k++)
					{
						if(data.scalars[j][k] > data.scalars[i][k])
							outside = false;
						if(data.scalars[j][k] < data.scalars[i][k])
							inside = false;
					}

					if(outside && (logX[j] < logX[i]))
						inconsistency_old++;
					if(inside  && (logX[j] > logX[i]))
						inconsistency_old++;

					if(outside && (logX[j] < proposal))
						inconsistency_new++;
					if(inside  && (logX[j] > proposal))
						inconsistency_new++;
				}
			}

			int inconsistency = inconsistency_old;
			if(inconsistency_new <= inconsistency_old)
			{
				logX[i] = proposal;
				inconsistency = inconsistency_new;
			}
			return inconsistency;
		}

		void save()
		{
			fstream fout("logX.txt", ios::out);
			for(int i=0; i<data.N; i++)
				fout<<logX[i]<<endl;
			fout.close();
		}

};

int main()
{
	RandomNumberGenerator::initialise_instance();
	RandomNumberGenerator::get_instance().set_seed(time(0));

	Data data;
	data.load();

	Assignment assignment(data);
	assignment.initialise();

	for(int i=0; i<10000000; i++)
	{
		cout<<assignment.update_all()<<endl;
		assignment.save();
	}
	return 0;
}

<|endoftext|>


<commit_before>//===- MC-X86Specific.cpp - X86-Specific code for MC ----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements X86-specific parsing, encoding and decoding stuff for
// MC.
//
//===----------------------------------------------------------------------===//

#include "AsmParser.h"
#include "llvm/MC/MCInst.h"
using namespace llvm;

/// X86Operand - Instances of this class represent one X86 machine instruction.
struct AsmParser::X86Operand {
  enum {
    Register,
    Immediate,
    Memory
  } Kind;

  union {
    struct {
      unsigned RegNo;
    } Reg;

    struct {
      // FIXME: Should be a general expression.
      int64_t Val;
    } Imm;

    struct {
      unsigned SegReg;
      int64_t Disp;     // FIXME: Should be a general expression.
      unsigned BaseReg;
      unsigned Scale;
      unsigned ScaleReg;
    } Mem;
  };

  static X86Operand CreateReg(unsigned RegNo) {
    X86Operand Res;
    Res.Kind = Register;
    Res.Reg.RegNo = RegNo;
    return Res;
  }
  static X86Operand CreateImm(int64_t Val) {
    X86Operand Res;
    Res.Kind = Immediate;
    Res.Imm.Val = Val;
    return Res;
  }
  static X86Operand CreateMem(unsigned SegReg, int64_t Disp, unsigned BaseReg,
                              unsigned Scale, unsigned ScaleReg) {
    X86Operand Res;
    Res.Kind = Memory;
    Res.Mem.SegReg   = SegReg;
    Res.Mem.Disp     = Disp;
    Res.Mem.BaseReg  = BaseReg;
    Res.Mem.Scale    = Scale;
    Res.Mem.ScaleReg = ScaleReg;
    return Res;
  }

  void AddToMCInst(MCInst &I) {
    // FIXME: Add in x86 order here.
  }
};

bool AsmParser::ParseX86Operand(X86Operand &Op) {
  switch (Lexer.getKind()) {
  default:
    return ParseX86MemOperand(Op);
  case asmtok::Register:
    // FIXME: Decode reg #.
    // FIXME: if a segment register, this could either be just the seg reg, or
    // the start of a memory operand.
    Op = X86Operand::CreateReg(123);
    Lexer.Lex(); // Eat register.
    return false;
  case asmtok::Dollar: {
    // $42 -> immediate.
    Lexer.Lex();
    int64_t Val;
    if (ParseAbsoluteExpression(Val))
      return TokError("expected integer constant");
    Op = X86Operand::CreateReg(Val);
    return false;
  case asmtok::Star:
    Lexer.Lex(); // Eat the star.

    if (Lexer.is(asmtok::Register)) {
      Op = X86Operand::CreateReg(123);
      Lexer.Lex(); // Eat register.
    } else if (ParseX86MemOperand(Op))
      return true;

    // FIXME: Note that these are 'dereferenced' so that clients know the '*' is
    // there.
    return false;
  }
  }
}

/// ParseX86MemOperand: segment: disp(basereg, indexreg, scale)
bool AsmParser::ParseX86MemOperand(X86Operand &Op) {
  // FIXME: If SegReg ':'  (e.g. %gs:), eat and remember.
  unsigned SegReg = 0;

  // We have to disambiguate a parenthesized expression "(4+5)" from the start
  // of a memory operand with a missing displacement "(%ebx)" or "(,%eax)".  The
  // only way to do this without lookahead is to eat the ( and see what is after
  // it.
  int64_t Disp = 0;
  if (Lexer.isNot(asmtok::LParen)) {
    if (ParseAbsoluteExpression(Disp)) return true;

    // After parsing the base expression we could either have a parenthesized
    // memory address or not.  If not, return now.  If so, eat the (.
    if (Lexer.isNot(asmtok::LParen)) {
      Op = X86Operand::CreateMem(SegReg, Disp, 0, 0, 0);
      return false;
    }

    // Eat the '('.
    Lexer.Lex();
  } else {
    // Okay, we have a '('.  We don't know if this is an expression or not, but
    // so we have to eat the ( to see beyond it.
    Lexer.Lex(); // Eat the '('.

    if (Lexer.is(asmtok::Register) || Lexer.is(asmtok::Comma)) {
      // Nothing to do here, fall into the code below with the '(' part of the
      // memory operand consumed.
    } else {
      // It must be an parenthesized expression, parse it now.
      if (ParseAbsoluteExpression(Disp))
        return true;

      // After parsing the base expression we could either have a parenthesized
      // memory address or not.  If not, return now.  If so, eat the (.
      if (Lexer.isNot(asmtok::LParen)) {
        Op = X86Operand::CreateMem(SegReg, Disp, 0, 0, 0);
        return false;
      }

      // Eat the '('.
      Lexer.Lex();
    }
  }

  // If we reached here, then we just ate the ( of the memory operand.  Process
  // the rest of the memory operand.
  unsigned BaseReg = 0, ScaleReg = 0, Scale = 0;

  if (Lexer.is(asmtok::Register)) {
    BaseReg = 123; // FIXME: decode reg #
    Lexer.Lex();  // eat the register.
  }

  if (Lexer.is(asmtok::Comma)) {
    Lexer.Lex(); // eat the comma.

    if (Lexer.is(asmtok::Register)) {
      ScaleReg = 123; // FIXME: decode reg #
      Lexer.Lex();  // eat the register.
      Scale = 1;      // If not specified, the scale defaults to 1.
    }

    if (Lexer.is(asmtok::Comma)) {
      Lexer.Lex(); // eat the comma.

      // If present, get and validate scale amount.
      if (Lexer.is(asmtok::IntVal)) {
        int64_t ScaleVal = Lexer.getCurIntVal();
        if (ScaleVal != 1 && ScaleVal != 2 && ScaleVal != 4 && ScaleVal != 8)
          return TokError("scale factor in address must be 1, 2, 4 or 8");
        Lexer.Lex();  // eat the scale.
        Scale = (unsigned)ScaleVal;
      }
    }
  }

  // Ok, we've eaten the memory operand, verify we have a ')' and eat it too.
  if (Lexer.isNot(asmtok::RParen))
    return TokError("unexpected token in memory operand");
  Lexer.Lex(); // Eat the ')'.

  Op = X86Operand::CreateMem(SegReg, Disp, BaseReg, Scale, ScaleReg);
  return false;
}

/// ParseX86InstOperands - Parse the operands of an X86 instruction and return
/// them as the operands of an MCInst.
bool AsmParser::ParseX86InstOperands(MCInst &Inst) {
  // If no operands are present, just return.
  if (Lexer.is(asmtok::EndOfStatement))
    return false;

  // Read the first operand.
  X86Operand Op;
  if (ParseX86Operand(Op))
    return true;
  Op.AddToMCInst(Inst);

  while (Lexer.is(asmtok::Comma)) {
    Lexer.Lex();  // Eat the comma.

    // Parse and remember the operand.
    Op = X86Operand();
    if (ParseX86Operand(Op))
      return true;
    Op.AddToMCInst(Inst);
  }
  return false;
}
<commit_msg>llvm-mc: Accept relocatable expressions when parsing displacements and immediates.<commit_after>//===- MC-X86Specific.cpp - X86-Specific code for MC ----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements X86-specific parsing, encoding and decoding stuff for
// MC.
//
//===----------------------------------------------------------------------===//

#include "AsmParser.h"
#include "llvm/MC/MCInst.h"
using namespace llvm;

/// X86Operand - Instances of this class represent one X86 machine instruction.
struct AsmParser::X86Operand {
  enum {
    Register,
    Immediate,
    Memory
  } Kind;

  union {
    struct {
      unsigned RegNo;
    } Reg;

    struct {
      MCValue Val;
    } Imm;

    struct {
      unsigned SegReg;
      MCValue Disp;
      unsigned BaseReg;
      unsigned Scale;
      unsigned ScaleReg;
    } Mem;
  };

  static X86Operand CreateReg(unsigned RegNo) {
    X86Operand Res;
    Res.Kind = Register;
    Res.Reg.RegNo = RegNo;
    return Res;
  }
  static X86Operand CreateImm(MCValue Val) {
    X86Operand Res;
    Res.Kind = Immediate;
    Res.Imm.Val = Val;
    return Res;
  }
  static X86Operand CreateMem(unsigned SegReg, MCValue Disp, unsigned BaseReg,
                              unsigned Scale, unsigned ScaleReg) {
    X86Operand Res;
    Res.Kind = Memory;
    Res.Mem.SegReg   = SegReg;
    Res.Mem.Disp     = Disp;
    Res.Mem.BaseReg  = BaseReg;
    Res.Mem.Scale    = Scale;
    Res.Mem.ScaleReg = ScaleReg;
    return Res;
  }

  void AddToMCInst(MCInst &I) {
    // FIXME: Add in x86 order here.
  }
};

bool AsmParser::ParseX86Operand(X86Operand &Op) {
  switch (Lexer.getKind()) {
  default:
    return ParseX86MemOperand(Op);
  case asmtok::Register:
    // FIXME: Decode reg #.
    // FIXME: if a segment register, this could either be just the seg reg, or
    // the start of a memory operand.
    Op = X86Operand::CreateReg(123);
    Lexer.Lex(); // Eat register.
    return false;
  case asmtok::Dollar: {
    // $42 -> immediate.
    Lexer.Lex();
    MCValue Val;
    if (ParseRelocatableExpression(Val))
      return true;
    Op = X86Operand::CreateImm(Val);
    return false;
  }
  case asmtok::Star: {
    Lexer.Lex(); // Eat the star.

    if (Lexer.is(asmtok::Register)) {
      Op = X86Operand::CreateReg(123);
      Lexer.Lex(); // Eat register.
    } else if (ParseX86MemOperand(Op))
      return true;

    // FIXME: Note that these are 'dereferenced' so that clients know the '*' is
    // there.
    return false;
  }
  }
}

/// ParseX86MemOperand: segment: disp(basereg, indexreg, scale)
bool AsmParser::ParseX86MemOperand(X86Operand &Op) {
  // FIXME: If SegReg ':'  (e.g. %gs:), eat and remember.
  unsigned SegReg = 0;

  // We have to disambiguate a parenthesized expression "(4+5)" from the start
  // of a memory operand with a missing displacement "(%ebx)" or "(,%eax)".  The
  // only way to do this without lookahead is to eat the ( and see what is after
  // it.
  MCValue Disp = MCValue::get(0, 0, 0);
  if (Lexer.isNot(asmtok::LParen)) {
    if (ParseRelocatableExpression(Disp)) return true;

    // After parsing the base expression we could either have a parenthesized
    // memory address or not.  If not, return now.  If so, eat the (.
    if (Lexer.isNot(asmtok::LParen)) {
      Op = X86Operand::CreateMem(SegReg, Disp, 0, 0, 0);
      return false;
    }

    // Eat the '('.
    Lexer.Lex();
  } else {
    // Okay, we have a '('.  We don't know if this is an expression or not, but
    // so we have to eat the ( to see beyond it.
    Lexer.Lex(); // Eat the '('.

    if (Lexer.is(asmtok::Register) || Lexer.is(asmtok::Comma)) {
      // Nothing to do here, fall into the code below with the '(' part of the
      // memory operand consumed.
    } else {
      // It must be an parenthesized expression, parse it now.
      if (ParseRelocatableExpression(Disp))
        return true;

      // After parsing the base expression we could either have a parenthesized
      // memory address or not.  If not, return now.  If so, eat the (.
      if (Lexer.isNot(asmtok::LParen)) {
        Op = X86Operand::CreateMem(SegReg, Disp, 0, 0, 0);
        return false;
      }

      // Eat the '('.
      Lexer.Lex();
    }
  }

  // If we reached here, then we just ate the ( of the memory operand.  Process
  // the rest of the memory operand.
  unsigned BaseReg = 0, ScaleReg = 0, Scale = 0;

  if (Lexer.is(asmtok::Register)) {
    BaseReg = 123; // FIXME: decode reg #
    Lexer.Lex();  // eat the register.
  }

  if (Lexer.is(asmtok::Comma)) {
    Lexer.Lex(); // eat the comma.

    if (Lexer.is(asmtok::Register)) {
      ScaleReg = 123; // FIXME: decode reg #
      Lexer.Lex();  // eat the register.
      Scale = 1;      // If not specified, the scale defaults to 1.
    }

    if (Lexer.is(asmtok::Comma)) {
      Lexer.Lex(); // eat the comma.

      // If present, get and validate scale amount.
      if (Lexer.is(asmtok::IntVal)) {
        int64_t ScaleVal = Lexer.getCurIntVal();
        if (ScaleVal != 1 && ScaleVal != 2 && ScaleVal != 4 && ScaleVal != 8)
          return TokError("scale factor in address must be 1, 2, 4 or 8");
        Lexer.Lex();  // eat the scale.
        Scale = (unsigned)ScaleVal;
      }
    }
  }

  // Ok, we've eaten the memory operand, verify we have a ')' and eat it too.
  if (Lexer.isNot(asmtok::RParen))
    return TokError("unexpected token in memory operand");
  Lexer.Lex(); // Eat the ')'.

  Op = X86Operand::CreateMem(SegReg, Disp, BaseReg, Scale, ScaleReg);
  return false;
}

/// ParseX86InstOperands - Parse the operands of an X86 instruction and return
/// them as the operands of an MCInst.
bool AsmParser::ParseX86InstOperands(MCInst &Inst) {
  // If no operands are present, just return.
  if (Lexer.is(asmtok::EndOfStatement))
    return false;

  // Read the first operand.
  X86Operand Op;
  if (ParseX86Operand(Op))
    return true;
  Op.AddToMCInst(Inst);

  while (Lexer.is(asmtok::Comma)) {
    Lexer.Lex();  // Eat the comma.

    // Parse and remember the operand.
    Op = X86Operand();
    if (ParseX86Operand(Op))
      return true;
    Op.AddToMCInst(Inst);
  }
  return false;
}
<|endoftext|>


<commit_before>#include <starmap.hpp>
#include "helpers.hpp"

#include <iterator>
#include <list>
#include <string>
#include <type_traits>
#include <vector>

#include "catch.hpp"

using iter::starmap;

namespace {
  long f(long d, int i) {
    return d * i;
  }

  int& larger_ref(int& a, int& b) {
    return a > b ? a : b;
  }

  const int& larger_const_ref(const int& a, const int& b) {
    return a > b ? a : b;
  }

  int larger(int a, int b) {
    return a > b ? a : b;
  }

  std::string g(const std::string& s, int i, char c) {
    std::stringstream ss;
    ss << s << ' ' << i << ' ' << c;
    return ss.str();
  }

  struct Callable {
    int operator()(int a, int b, int c) {
      return a + b + c;
    }

    int operator()(double a, int b) {
      return int(a + b);
    }

    int operator()(int a) {
      return a;
    }
  };
}

TEST_CASE("starmap: works with function pointer and lambda", "[starmap]") {
  using Vec = const std::vector<int>;
  const std::vector<std::pair<long, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
  Vec vc = {2l, 33l, 42l};

  std::vector<int> v;
  SECTION("with function") {
    SECTION("Normal call") {
      auto sm = starmap(f, v1);
      v.assign(std::begin(sm), std::end(sm));
    }
    SECTION("pipe") {
      auto sm = v1 | starmap(f);
      v.assign(std::begin(sm), std::end(sm));
    }
  }

  SECTION("with lambda") {
    auto sm = starmap([](long a, int b) { return a * b; }, v1);
    v.assign(std::begin(sm), std::end(sm));
  }
  REQUIRE(v == vc);
}

TEST_CASE("starmap: works with pointer to member function", "[starmap]") {
  using itertest::Point;
  std::vector<std::pair<Point, std::string>> tup = {
      {{10, 20}, "a"}, {{6, 8}, "point"}, {{3, 15}, "pos"}};
  auto sm = starmap(&Point::prefix, tup);
  std::vector<std::string> v(std::begin(sm), std::end(sm));
  const std::vector<std::string> vc = {
      "a(10, 20)", "point(6, 8)", "pos(3, 15)"};
  REQUIRE(v == vc);
}

TEST_CASE("starmap: vector of pairs const iteration", "[starmap][const]") {
  using Vec = const std::vector<int>;
  const std::vector<std::pair<double, int>> v1 = {
      {1.0, 2}, {3.0, 11}, {6.0, 7}};

  const auto sm = starmap(Callable{}, v1);
  std::vector<int> v(std::begin(sm), std::end(sm));
  Vec vc = {3, 14, 13};
  REQUIRE(v == vc);
}

TEST_CASE(
    "starmap: vector of pairs const iterators can be compared to non-const "
    "iterators",
    "[starmap][const]") {
  const std::vector<std::pair<double, int>> v1;
  auto sm = starmap(Callable{}, v1);
  const auto& csm = sm;
  (void)(std::begin(sm) == std::end(csm));
}

TEST_CASE("starmap: Works with different begin and end types", "[starmap]") {
  IntCharPairRange icr{{3, 'd'}};
  using Vec = std::vector<std::string>;
  auto sm = starmap([](int i, char c) { return std::to_string(i) + c; }, icr);
  Vec v(sm.begin(), sm.end());
  Vec vc{"0a", "1b", "2c"};
  REQUIRE(v == vc);
}

TEST_CASE("starmap: tuple of tuples const iteration", "[starmap][const]") {
  using Vec = const std::vector<int>;
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  const auto sm = starmap(Callable{}, tup);
  Vec v(std::begin(sm), std::end(sm));
}

TEST_CASE(
    "starmap: tuple of tuples const iterators can be compared to non-const "
    "iterator",
    "[starmap][const]") {
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  auto sm = starmap(Callable{}, tup);
  const auto& csm = sm;
  (void)(std::begin(sm) == std::end(csm));
  (void)(std::begin(csm) == std::end(sm));
}

TEST_CASE("starmap: list of tuples", "[starmap]") {
  using Vec = const std::vector<std::string>;
  using T = std::tuple<std::string, int, char>;
  std::list<T> li = {T{"hey", 42, 'a'}, T{"there", 3, 'b'}, T{"yall", 5, 'c'}};

  auto sm = starmap(g, li);
  Vec v(std::begin(sm), std::end(sm));
  Vec vc = {"hey 42 a", "there 3 b", "yall 5 c"};

  REQUIRE(v == vc);
}

TEST_CASE("starmap: tuple of tuples", "[starmap]") {
  using Vec = const std::vector<int>;
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  std::vector<int> v;
  SECTION("Normal call") {
    auto sm = starmap(Callable{}, tup);
    v.assign(std::begin(sm), std::end(sm));
  }
  SECTION("Pipe") {
    auto sm = tup | starmap(Callable{});
    v.assign(std::begin(sm), std::end(sm));
  }

  Vec vc = {89, 7};
  REQUIRE(v == vc);
}

TEST_CASE("starmap: tuple of pairs", "[starmap]") {
  using Vec = const std::vector<int>;
  auto p =
      std::make_pair(std::array<int, 3>{{15, 100, 2000}}, std::make_tuple(16));
  Callable c;
  auto sm = starmap(c, p);

  Vec v(std::begin(sm), std::end(sm));
  Vec vc = {2115, 16};

  REQUIRE(v == vc);
}

TEST_CASE("starmap: moves rvalues, binds to lvalues", "[starmap]") {
  itertest::BasicIterable<std::tuple<int>> bi{};
  starmap(Callable{}, bi);
  REQUIRE_FALSE(bi.was_moved_from());
  starmap(Callable{}, std::move(bi));
  REQUIRE(bi.was_moved_from());
}

TEST_CASE("starmap: iterator meets requirements", "[starmap]") {
  std::string s{};
  const std::vector<std::pair<double, int>> v1;
  auto sm = starmap([](double a, int b) { return a * b; }, v1);
  REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
}

TEST_CASE(
    "starmap:  tuple of tuples iterator meets requirements", "[starmap]") {
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  auto sm = starmap(Callable{}, tup);
  REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
}

TEST_CASE("starmap: iterator dereference type matches 'reference' type alias",
    "[starmap]") {
  std::vector<std::pair<int, int>> input;
  SECTION("with reference return type") {
    auto sm = iter::starmap(larger_ref, input);
    REQUIRE(
        std::is_same_v<decltype(*sm.begin()), decltype(sm.begin())::reference>);
  }
  SECTION("with const reference return type") {
    auto sm = iter::starmap(larger_const_ref, input);
    REQUIRE(
        std::is_same_v<decltype(*sm.begin()), decltype(sm.begin())::reference>);
  }
  SECTION("with value return type") {
    auto sm = iter::starmap(larger, input);
    REQUIRE(
        std::is_same_v<decltype(*sm.begin()), decltype(sm.begin())::reference>);
  }
}

TEST_CASE("starmap: iterator has correct 'value' type alias", "[starmap]") {
  std::vector<std::pair<int, int>> input;
  SECTION("with reference return type") {
    auto sm = iter::starmap(larger_ref, input);
    REQUIRE(std::is_same_v<int, decltype(sm.begin())::value_type>);
  }
  SECTION("with const reference return type") {
    auto sm = iter::starmap(larger_const_ref, input);
    REQUIRE(std::is_same_v<int, decltype(sm.begin())::value_type>);
  }
  SECTION("with value return type") {
    auto sm = iter::starmap(larger, input);
    REQUIRE(std::is_same_v<int, decltype(sm.begin())::value_type>);
  }
}
<commit_msg>Uses ReferenceMatchesDeref in starmap test<commit_after>#include <starmap.hpp>
#include "helpers.hpp"

#include <iterator>
#include <list>
#include <string>
#include <type_traits>
#include <vector>

#include "catch.hpp"

using iter::starmap;

namespace {
  long f(long d, int i) {
    return d * i;
  }

  int& larger_ref(int& a, int& b) {
    return a > b ? a : b;
  }

  const int& larger_const_ref(const int& a, const int& b) {
    return a > b ? a : b;
  }

  int larger(int a, int b) {
    return a > b ? a : b;
  }

  std::string g(const std::string& s, int i, char c) {
    std::stringstream ss;
    ss << s << ' ' << i << ' ' << c;
    return ss.str();
  }

  struct Callable {
    int operator()(int a, int b, int c) {
      return a + b + c;
    }

    int operator()(double a, int b) {
      return int(a + b);
    }

    int operator()(int a) {
      return a;
    }
  };
}

TEST_CASE("starmap: works with function pointer and lambda", "[starmap]") {
  using Vec = const std::vector<int>;
  const std::vector<std::pair<long, int>> v1 = {{1l, 2}, {3l, 11}, {6l, 7}};
  Vec vc = {2l, 33l, 42l};

  std::vector<int> v;
  SECTION("with function") {
    SECTION("Normal call") {
      auto sm = starmap(f, v1);
      v.assign(std::begin(sm), std::end(sm));
    }
    SECTION("pipe") {
      auto sm = v1 | starmap(f);
      v.assign(std::begin(sm), std::end(sm));
    }
  }

  SECTION("with lambda") {
    auto sm = starmap([](long a, int b) { return a * b; }, v1);
    v.assign(std::begin(sm), std::end(sm));
  }
  REQUIRE(v == vc);
}

TEST_CASE("starmap: works with pointer to member function", "[starmap]") {
  using itertest::Point;
  std::vector<std::pair<Point, std::string>> tup = {
      {{10, 20}, "a"}, {{6, 8}, "point"}, {{3, 15}, "pos"}};
  auto sm = starmap(&Point::prefix, tup);
  std::vector<std::string> v(std::begin(sm), std::end(sm));
  const std::vector<std::string> vc = {
      "a(10, 20)", "point(6, 8)", "pos(3, 15)"};
  REQUIRE(v == vc);
}

TEST_CASE("starmap: vector of pairs const iteration", "[starmap][const]") {
  using Vec = const std::vector<int>;
  const std::vector<std::pair<double, int>> v1 = {
      {1.0, 2}, {3.0, 11}, {6.0, 7}};

  const auto sm = starmap(Callable{}, v1);
  std::vector<int> v(std::begin(sm), std::end(sm));
  Vec vc = {3, 14, 13};
  REQUIRE(v == vc);
}

TEST_CASE(
    "starmap: vector of pairs const iterators can be compared to non-const "
    "iterators",
    "[starmap][const]") {
  const std::vector<std::pair<double, int>> v1;
  auto sm = starmap(Callable{}, v1);
  const auto& csm = sm;
  (void)(std::begin(sm) == std::end(csm));
}

TEST_CASE("starmap: Works with different begin and end types", "[starmap]") {
  IntCharPairRange icr{{3, 'd'}};
  using Vec = std::vector<std::string>;
  auto sm = starmap([](int i, char c) { return std::to_string(i) + c; }, icr);
  Vec v(sm.begin(), sm.end());
  Vec vc{"0a", "1b", "2c"};
  REQUIRE(v == vc);
}

TEST_CASE("starmap: tuple of tuples const iteration", "[starmap][const]") {
  using Vec = const std::vector<int>;
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  const auto sm = starmap(Callable{}, tup);
  Vec v(std::begin(sm), std::end(sm));
}

TEST_CASE(
    "starmap: tuple of tuples const iterators can be compared to non-const "
    "iterator",
    "[starmap][const]") {
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  auto sm = starmap(Callable{}, tup);
  const auto& csm = sm;
  (void)(std::begin(sm) == std::end(csm));
  (void)(std::begin(csm) == std::end(sm));
}

TEST_CASE("starmap: list of tuples", "[starmap]") {
  using Vec = const std::vector<std::string>;
  using T = std::tuple<std::string, int, char>;
  std::list<T> li = {T{"hey", 42, 'a'}, T{"there", 3, 'b'}, T{"yall", 5, 'c'}};

  auto sm = starmap(g, li);
  Vec v(std::begin(sm), std::end(sm));
  Vec vc = {"hey 42 a", "there 3 b", "yall 5 c"};

  REQUIRE(v == vc);
}

TEST_CASE("starmap: tuple of tuples", "[starmap]") {
  using Vec = const std::vector<int>;
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  std::vector<int> v;
  SECTION("Normal call") {
    auto sm = starmap(Callable{}, tup);
    v.assign(std::begin(sm), std::end(sm));
  }
  SECTION("Pipe") {
    auto sm = tup | starmap(Callable{});
    v.assign(std::begin(sm), std::end(sm));
  }

  Vec vc = {89, 7};
  REQUIRE(v == vc);
}

TEST_CASE("starmap: tuple of pairs", "[starmap]") {
  using Vec = const std::vector<int>;
  auto p =
      std::make_pair(std::array<int, 3>{{15, 100, 2000}}, std::make_tuple(16));
  Callable c;
  auto sm = starmap(c, p);

  Vec v(std::begin(sm), std::end(sm));
  Vec vc = {2115, 16};

  REQUIRE(v == vc);
}

TEST_CASE("starmap: moves rvalues, binds to lvalues", "[starmap]") {
  itertest::BasicIterable<std::tuple<int>> bi{};
  starmap(Callable{}, bi);
  REQUIRE_FALSE(bi.was_moved_from());
  starmap(Callable{}, std::move(bi));
  REQUIRE(bi.was_moved_from());
}

TEST_CASE("starmap: iterator meets requirements", "[starmap]") {
  std::string s{};
  const std::vector<std::pair<double, int>> v1;
  auto sm = starmap([](double a, int b) { return a * b; }, v1);
  REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
}

TEST_CASE(
    "starmap:  tuple of tuples iterator meets requirements", "[starmap]") {
  auto tup = std::make_tuple(std::make_tuple(10, 19, 60), std::make_tuple(7));
  auto sm = starmap(Callable{}, tup);
  REQUIRE(itertest::IsIterator<decltype(std::begin(sm))>::value);
}

TEST_CASE("starmap: iterator dereference type matches 'reference' type alias",
    "[starmap]") {
  std::vector<std::pair<int, int>> input;
  SECTION("with reference return type") {
    auto sm = iter::starmap(larger_ref, input);
    REQUIRE(itertest::ReferenceMatchesDeref<decltype(std::begin(sm))>::value);
  }
  SECTION("with const reference return type") {
    auto sm = iter::starmap(larger_const_ref, input);
    REQUIRE(itertest::ReferenceMatchesDeref<decltype(std::begin(sm))>::value);
  }
  SECTION("with value return type") {
    auto sm = iter::starmap(larger, input);
    REQUIRE(itertest::ReferenceMatchesDeref<decltype(std::begin(sm))>::value);
  }
}

TEST_CASE("starmap: iterator has correct 'value' type alias", "[starmap]") {
  std::vector<std::pair<int, int>> input;
  SECTION("with reference return type") {
    auto sm = iter::starmap(larger_ref, input);
    REQUIRE(std::is_same_v<int, decltype(sm.begin())::value_type>);
  }
  SECTION("with const reference return type") {
    auto sm = iter::starmap(larger_const_ref, input);
    REQUIRE(std::is_same_v<int, decltype(sm.begin())::value_type>);
  }
  SECTION("with value return type") {
    auto sm = iter::starmap(larger, input);
    REQUIRE(std::is_same_v<int, decltype(sm.begin())::value_type>);
  }
}
<|endoftext|>


<commit_before>/*---------------------------------------------------------------------------
+  Arab Renamer v 2.0
+  Descreption: Arab Renamer renames Arabic files to the same names but using
+  English characters like the word 'Quran', so that files could be used on
+  non-Arabic systems like Linux or on mp3 players, besides it can limit the
+  files names during renaming.
+  The program uses characters like 3, ', 6 and 7 which are popular characters
+  used to replace specific Arabic letters.
+  Copyright  2004  Kurtubba Software Limited
+  Developed by Walid Aly
+  http://fly.to/kurtubba
+  +201 215 78967
+  We welcome bug report and/or enhancement request:
+  kurtubba@gawab.com
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation.
+
+  This program 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   file : the search dialog
-------------------------------------------------------------------------------*/
#include <vcl.h>
#include "srchdlgfrm.h"
#include "mainfrm.h"
#include "getfilesthread.h"

#pragma hdrstop
//----------------------------------------------------------------------------
#pragma resource "*.dfm"
Tsrchdlg *srchdlg;
//----------------------------------------------------------------------------
__fastcall Tsrchdlg::Tsrchdlg(TComponent *Owner)
: TForm(Owner)
{
}
//----------------------------------------------------------------------------
void __fastcall Tsrchdlg::FormShow(TObject *Sender)
{
  Animate1->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall Tsrchdlg::FormHide(TObject *Sender)
{
  Animate1->Active = false;
}
//---------------------------------------------------------------------------
void __fastcall Tsrchdlg::FormKeyPress(TObject *Sender, char &Key)
{

  if(Key == VK_ESCAPE && Visible==true)
  {
    //Form1->strt->Suspend();
    Animate1->Active = false;
    if(MessageDlg( "Are you sure you want\nto abort the search?", mtConfirmation, mbAbortIgnore, 0)== mrAbort)
    {
      Hide();
      //Form1->strt->Resume();
    }
    else
    {
      //Form1->strt->Resume();
      Animate1->Active = true;
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall Tsrchdlg::FormClose(TObject *Sender, TCloseAction &Action)
{
  Action=caNone;
}
//---------------------------------------------------------------------------

<commit_msg>Update info<commit_after>/*---------------------------------------------------------------------------
+  Arab Renamer v 2.0
+  Descreption: Arab Renamer renames Arabic files to the same names but using
+  English characters like the word 'Quran', so that files could be used on
+  non-Arabic systems like Linux or on mp3 players, besides it can limit the
+  files names during renaming.
+  The program uses characters like 3, ', 6 and 7 which are popular characters
+  used to replace specific Arabic letters.
+  Copyright © 2004  Kurtubba Software Limited
+  Developed by Waleed Gadelkareem
+  http://gadelkareem.com/2006/10/13/arab-renamer-v2/
+  +201 215 78967
+  We welcome bug report and/or enhancement request:
+  gadelkareem@gmail.com
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation.
+
+  This program 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+   file : the search dialog
-------------------------------------------------------------------------------*/
#include <vcl.h>
#include "srchdlgfrm.h"
#include "mainfrm.h"
#include "getfilesthread.h"

#pragma hdrstop
//----------------------------------------------------------------------------
#pragma resource "*.dfm"
Tsrchdlg *srchdlg;
//----------------------------------------------------------------------------
__fastcall Tsrchdlg::Tsrchdlg(TComponent *Owner)
: TForm(Owner)
{
}
//----------------------------------------------------------------------------
void __fastcall Tsrchdlg::FormShow(TObject *Sender)
{
  Animate1->Active = true;
}
//---------------------------------------------------------------------------
void __fastcall Tsrchdlg::FormHide(TObject *Sender)
{
  Animate1->Active = false;
}
//---------------------------------------------------------------------------
void __fastcall Tsrchdlg::FormKeyPress(TObject *Sender, char &Key)
{

  if(Key == VK_ESCAPE && Visible==true)
  {
    //Form1->strt->Suspend();
    Animate1->Active = false;
    if(MessageDlg( "Are you sure you want\nto abort the search?", mtConfirmation, mbAbortIgnore, 0)== mrAbort)
    {
      Hide();
      //Form1->strt->Resume();
    }
    else
    {
      //Form1->strt->Resume();
      Animate1->Active = true;
    }
  }
}
//---------------------------------------------------------------------------

void __fastcall Tsrchdlg::FormClose(TObject *Sender, TCloseAction &Action)
{
  Action=caNone;
}
//---------------------------------------------------------------------------

<|endoftext|>


<commit_before>#include "test.hpp"
#include "setup_transfer.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/error_code.hpp"

#include <fstream>

using namespace libtorrent;

int test_main()
{
	int http_port = start_web_server();
	int udp_port = start_tracker();

	int prev_udp_announces = g_udp_tracker_requests;
	int prev_http_announces = g_http_tracker_requests;

	int const alert_mask = alert::all_categories
		& ~alert::progress_notification
		& ~alert::stats_notification;

	session* s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48875, 49800), "0.0.0.0", 0, alert_mask);

	session_settings sett;
	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = true;
	s->set_settings(sett);

	error_code ec;
	create_directory("./tmp1_tracker", ec);
	std::ofstream file("./tmp1_tracker/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	char tracker_url[200];
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url);

	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url);

	add_torrent_params addp;
	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp1_tracker";
	torrent_handle h = s->add_torrent(addp);

	test_sleep(2000);

	// we should have announced to the tracker by now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 1);

	delete s;

	// we should have announced the stopped event now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 2);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 2);

	// ========================================
	// test that we move on to try the next tier if the first one fails
	// ========================================

	s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(39775, 39800), "0.0.0.0", 0, alert_mask);

	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = false;
	sett.tracker_completion_timeout = 4;
	sett.tracker_receive_timeout = 2;
	s->set_settings(sett);

	create_directory("./tmp2_tracker", ec);
	file.open("./tmp2_tracker/temporary");
	t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	// this should fail
	snprintf(tracker_url, sizeof(tracker_url), "udp://www.google.com:80/announce");
	t->add_tracker(tracker_url, 0);

	// and this should fail
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.2:3/announce");
	t->add_tracker(tracker_url, 1);

	// this should be announced to
	// udp trackers are prioritized if they're on the same host as an http one
	// so this must be before the http one on 127.0.0.1
	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url, 2);

	// and this should not be announced to (since the one before it succeeded)
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url, 3);

	prev_udp_announces = g_udp_tracker_requests;
	prev_http_announces = g_http_tracker_requests;

	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp2_tracker";
	h = s->add_torrent(addp);

	for (int i = 0; i < 10; ++i)
	{
		test_sleep(1000);
		print_alerts(*s, "s");
		if (g_udp_tracker_requests == prev_udp_announces + 1) break;
	}

	test_sleep(1000);

	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces);

	delete s;

	stop_tracker();
	stop_web_server();

	return 0;
}

<commit_msg>fix unit test<commit_after>#include "test.hpp"
#include "setup_transfer.hpp"
#include "libtorrent/alert.hpp"
#include "libtorrent/session.hpp"
#include "libtorrent/error_code.hpp"

#include <fstream>

using namespace libtorrent;

int test_main()
{
	int http_port = start_web_server();
	int udp_port = start_tracker();

	int prev_udp_announces = g_udp_tracker_requests;
	int prev_http_announces = g_http_tracker_requests;

	int const alert_mask = alert::all_categories
		& ~alert::progress_notification
		& ~alert::stats_notification;

	session* s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48875, 49800), "0.0.0.0", 0, alert_mask);

	session_settings sett;
	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = true;
	s->set_settings(sett);

	error_code ec;
	create_directory("./tmp1_tracker", ec);
	std::ofstream file("./tmp1_tracker/temporary");
	boost::intrusive_ptr<torrent_info> t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	char tracker_url[200];
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url);

	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url);

	add_torrent_params addp;
	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp1_tracker";
	torrent_handle h = s->add_torrent(addp);

	test_sleep(2000);

	// we should have announced to the tracker by now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 1);

	delete s;

	// we should have announced the stopped event now
	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 2);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces + 2);

	// ========================================
	// test that we move on to try the next tier if the first one fails
	// ========================================

	s = new libtorrent::session(fingerprint("LT", 0, 1, 0, 0), std::make_pair(39775, 39800), "0.0.0.0", 0, alert_mask);

	sett.half_open_limit = 1;
	sett.announce_to_all_trackers = true;
	sett.announce_to_all_tiers = false;
	sett.tracker_completion_timeout = 2;
	sett.tracker_receive_timeout = 1;
	s->set_settings(sett);

	create_directory("./tmp2_tracker", ec);
	file.open("./tmp2_tracker/temporary");
	t = ::create_torrent(&file, 16 * 1024, 13, false);
	file.close();

	// this should fail
	snprintf(tracker_url, sizeof(tracker_url), "udp://www.google.com:80/announce");
	t->add_tracker(tracker_url, 0);

	// and this should fail
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.2:3/announce");
	t->add_tracker(tracker_url, 1);

	// this should be announced to
	// udp trackers are prioritized if they're on the same host as an http one
	// so this must be before the http one on 127.0.0.1
	snprintf(tracker_url, sizeof(tracker_url), "udp://127.0.0.1:%d/announce", udp_port);
	t->add_tracker(tracker_url, 2);

	// and this should not be announced to (since the one before it succeeded)
	snprintf(tracker_url, sizeof(tracker_url), "http://127.0.0.1:%d/announce", http_port);
	t->add_tracker(tracker_url, 3);

	prev_udp_announces = g_udp_tracker_requests;
	prev_http_announces = g_http_tracker_requests;

	addp.paused = false;
	addp.auto_managed = false;
	addp.ti = t;
	addp.save_path = "./tmp2_tracker";
	h = s->add_torrent(addp);

	for (int i = 0; i < 10; ++i)
	{
		print_alerts(*s, "s");
		test_sleep(1000);
		if (g_udp_tracker_requests == prev_udp_announces + 1) break;
	}

	test_sleep(1000);

	TEST_EQUAL(g_udp_tracker_requests, prev_udp_announces + 1);
	TEST_EQUAL(g_http_tracker_requests, prev_http_announces);

	delete s;

	stop_tracker();
	stop_web_server();

	return 0;
}

<|endoftext|>