tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Utils.h (15813B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef Utils_h__
      6 #define Utils_h__
      7 
      8 #include "CustomAttributes.h"
      9 #include "ThirdPartyPaths.h"
     10 #include "ThreadAllows.h"
     11 #include "plugin.h"
     12 
     13 #if CLANG_VERSION_FULL >= 1300
     14 // Starting with clang-13 some functions from StringRef have been renamed
     15 #define compare_lower compare_insensitive
     16 #endif
     17 
     18 inline StringRef getFilename(const SourceManager &SM, SourceLocation Loc) {
     19  // We use the presumed location to handle #line directives and such, so the
     20  // plugin is friendly to icecc / sccache users.
     21  auto PL = SM.getPresumedLoc(Loc);
     22  if (PL.isValid()) {
     23    return StringRef(PL.getFilename());
     24  }
     25  return SM.getFilename(Loc);
     26 }
     27 
     28 // Check if the given expression contains an assignment expression.
     29 // This can either take the form of a Binary Operator or a
     30 // Overloaded Operator Call.
     31 inline bool hasSideEffectAssignment(const Expr *Expression) {
     32  if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) {
     33    auto BinOp = OpCallExpr->getOperator();
     34    if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) {
     35      return true;
     36    }
     37  } else if (auto BinOpExpr = dyn_cast_or_null<BinaryOperator>(Expression)) {
     38    if (BinOpExpr->isAssignmentOp()) {
     39      return true;
     40    }
     41  }
     42 
     43  // Recurse to children.
     44  for (const Stmt *SubStmt : Expression->children()) {
     45    auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt);
     46    if (ChildExpr && hasSideEffectAssignment(ChildExpr)) {
     47      return true;
     48    }
     49  }
     50 
     51  return false;
     52 }
     53 
     54 template <class T>
     55 inline bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) {
     56  auto &SourceManager = AC.getSourceManager();
     57  auto ExpansionLoc = SourceManager.getExpansionLoc(D.getBeginLoc());
     58  if (ExpansionLoc.isInvalid()) {
     59    return false;
     60  }
     61  return SourceManager.isInSystemHeader(ExpansionLoc);
     62 }
     63 
     64 template <typename T> inline StringRef getNameChecked(const T &D) {
     65  return D->getIdentifier() ? D->getName() : "";
     66 }
     67 
     68 /// A cached data of whether classes are refcounted or not.
     69 typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>>
     70    RefCountedMap;
     71 extern RefCountedMap RefCountedClasses;
     72 
     73 inline bool classHasAddRefRelease(const CXXRecordDecl *D) {
     74  const RefCountedMap::iterator &It = RefCountedClasses.find(D);
     75  if (It != RefCountedClasses.end()) {
     76    return It->second.second;
     77  }
     78 
     79  bool SeenAddRef = false;
     80  bool SeenRelease = false;
     81  for (CXXRecordDecl::method_iterator Method = D->method_begin();
     82       Method != D->method_end(); ++Method) {
     83    const auto &Name = getNameChecked(Method);
     84    if (Name == "AddRef") {
     85      SeenAddRef = true;
     86    } else if (Name == "Release") {
     87      SeenRelease = true;
     88    }
     89  }
     90  RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease);
     91  return SeenAddRef && SeenRelease;
     92 }
     93 
     94 inline bool isClassRefCounted(QualType T);
     95 
     96 inline bool isClassRefCounted(const CXXRecordDecl *D) {
     97  // Normalize so that D points to the definition if it exists.
     98  if (!D->hasDefinition())
     99    return false;
    100  D = D->getDefinition();
    101  // Base class: anyone with AddRef/Release is obviously a refcounted class.
    102  if (classHasAddRefRelease(D))
    103    return true;
    104 
    105  // Look through all base cases to figure out if the parent is a refcounted
    106  // class.
    107  for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin();
    108       Base != D->bases_end(); ++Base) {
    109    bool Super = isClassRefCounted(Base->getType());
    110    if (Super) {
    111      return true;
    112    }
    113  }
    114 
    115  return false;
    116 }
    117 
    118 inline bool isClassRefCounted(QualType T) {
    119  while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
    120    T = ArrTy->getElementType();
    121  CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
    122  return Clazz ? isClassRefCounted(Clazz) : false;
    123 }
    124 
    125 inline const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) {
    126  for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end();
    127       Field != E; ++Field) {
    128    if (getNameChecked(Field) == "mRefCnt") {
    129      return *Field;
    130    }
    131  }
    132  return 0;
    133 }
    134 
    135 inline bool typeHasVTable(QualType T) {
    136  while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
    137    T = ArrTy->getElementType();
    138  CXXRecordDecl *Offender = T->getAsCXXRecordDecl();
    139  return Offender && Offender->hasDefinition() && Offender->isDynamicClass();
    140 }
    141 
    142 inline StringRef getDeclarationNamespace(const Decl *Declaration) {
    143  const DeclContext *DC =
    144      Declaration->getDeclContext()->getEnclosingNamespaceContext();
    145  const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC);
    146  if (!ND) {
    147    return "";
    148  }
    149 
    150  while (const DeclContext *ParentDC = ND->getParent()) {
    151    if (!isa<NamespaceDecl>(ParentDC)) {
    152      break;
    153    }
    154    ND = cast<NamespaceDecl>(ParentDC);
    155  }
    156 
    157  const auto &Name = ND->getName();
    158  return Name;
    159 }
    160 
    161 inline bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) {
    162  StringRef Name = getDeclarationNamespace(Declaration);
    163  if (Name == "") {
    164    return false;
    165  }
    166 
    167  return Name == "std" ||               // standard C++ lib
    168         Name == "__gnu_cxx" ||         // gnu C++ lib
    169         Name == "boost" ||             // boost
    170         Name == "webrtc" ||            // upstream webrtc
    171         Name == "rtc" ||               // upstream webrtc 'base' package
    172 #if CLANG_VERSION_MAJOR >= 16
    173         Name.starts_with("icu_") ||    // icu
    174 #else
    175         Name.startswith("icu_") ||     // icu
    176 #endif
    177         Name == "google" ||            // protobuf
    178         Name == "google_breakpad" ||   // breakpad
    179         Name == "soundtouch" ||        // libsoundtouch
    180         Name == "stagefright" ||       // libstagefright
    181         Name == "MacFileUtilities" ||  // MacFileUtilities
    182         Name == "dwarf2reader" ||      // dwarf2reader
    183         Name == "arm_ex_to_module" ||  // arm_ex_to_module
    184         Name == "testing" ||           // gtest
    185         Name == "Json" ||              // jsoncpp
    186         Name == "rlbox" ||             // rlbox
    187         Name == "v8";                  // irregexp
    188 }
    189 
    190 inline bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) {
    191  StringRef Name = getDeclarationNamespace(Declaration);
    192  if (Name == "") {
    193    return false;
    194  }
    195 
    196  return Name == "std" ||             // standard C++ lib
    197         Name == "__gnu_cxx" ||       // gnu C++ lib
    198         Name == "google_breakpad" || // breakpad
    199         Name == "webrtc" ||          // libwebrtc
    200         Name == "testing" ||         // gtest
    201         Name == "rlbox";             // rlbox
    202 }
    203 
    204 inline bool isIgnoredPathForImplicitConversion(const Decl *Declaration) {
    205  Declaration = Declaration->getCanonicalDecl();
    206  SourceLocation Loc = Declaration->getLocation();
    207  const SourceManager &SM = Declaration->getASTContext().getSourceManager();
    208  SmallString<1024> FileName = getFilename(SM, Loc);
    209  llvm::sys::fs::make_absolute(FileName);
    210  llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
    211                                    End = llvm::sys::path::rend(FileName);
    212  for (; Begin != End; ++Begin) {
    213    if (Begin->compare_lower(StringRef("graphite2")) == 0) {
    214      return true;
    215    }
    216    if (Begin->compare_lower(StringRef("chromium")) == 0) {
    217      // Ignore security/sandbox/chromium but not ipc/chromium.
    218      ++Begin;
    219      return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0;
    220    }
    221  }
    222  return false;
    223 }
    224 
    225 inline bool isIgnoredPathForSprintfLiteral(const CallExpr *Call,
    226                                           const SourceManager &SM) {
    227  SourceLocation Loc = Call->getBeginLoc();
    228  SmallString<1024> FileName = getFilename(SM, Loc);
    229  llvm::sys::fs::make_absolute(FileName);
    230  llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName),
    231                                    End = llvm::sys::path::rend(FileName);
    232  for (; Begin != End; ++Begin) {
    233    if (Begin->compare_lower(StringRef("angle")) == 0 ||
    234        Begin->compare_lower(StringRef("chromium")) == 0 ||
    235        Begin->compare_lower(StringRef("crashreporter")) == 0 ||
    236        Begin->compare_lower(StringRef("google-breakpad")) == 0 ||
    237        Begin->compare_lower(StringRef("gflags")) == 0 ||
    238        Begin->compare_lower(StringRef("harfbuzz")) == 0 ||
    239        Begin->compare_lower(StringRef("icu")) == 0 ||
    240        Begin->compare_lower(StringRef("jsoncpp")) == 0 ||
    241        Begin->compare_lower(StringRef("libstagefright")) == 0 ||
    242        Begin->compare_lower(StringRef("transport")) == 0 ||
    243        Begin->compare_lower(StringRef("protobuf")) == 0 ||
    244        Begin->compare_lower(StringRef("skia")) == 0 ||
    245        Begin->compare_lower(StringRef("sfntly")) == 0 ||
    246        // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof
    247        Begin->compare_lower(StringRef("testing")) == 0) {
    248      return true;
    249    }
    250    if (Begin->compare_lower(StringRef("webrtc")) == 0) {
    251      // Ignore trunk/webrtc, but not media/webrtc
    252      ++Begin;
    253      return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0;
    254    }
    255  }
    256  return false;
    257 }
    258 
    259 inline bool isInterestingDeclForImplicitConversion(const Decl *Declaration) {
    260  return !isInIgnoredNamespaceForImplicitConversion(Declaration) &&
    261         !isIgnoredPathForImplicitConversion(Declaration);
    262 }
    263 
    264 inline bool isIgnoredExprForMustUse(const Expr *E) {
    265  if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) {
    266    switch (OpCall->getOperator()) {
    267    case OO_Equal:
    268    case OO_PlusEqual:
    269    case OO_MinusEqual:
    270    case OO_StarEqual:
    271    case OO_SlashEqual:
    272    case OO_PercentEqual:
    273    case OO_CaretEqual:
    274    case OO_AmpEqual:
    275    case OO_PipeEqual:
    276    case OO_LessLessEqual:
    277    case OO_GreaterGreaterEqual:
    278      return true;
    279    default:
    280      return false;
    281    }
    282  }
    283 
    284  if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) {
    285    return Op->isAssignmentOp();
    286  }
    287 
    288  return false;
    289 }
    290 
    291 inline bool typeIsRefPtr(QualType Q) {
    292  CXXRecordDecl *D = Q->getAsCXXRecordDecl();
    293  if (!D || !D->getIdentifier()) {
    294    return false;
    295  }
    296 
    297  StringRef name = D->getName();
    298  if (name == "RefPtr" || name == "nsCOMPtr") {
    299    return true;
    300  }
    301  return false;
    302 }
    303 
    304 // The method defined in clang for ignoring implicit nodes doesn't work with
    305 // some AST trees. To get around this, we define our own implementation of
    306 // IgnoreTrivials.
    307 inline const Stmt *MaybeSkipOneTrivial(const Stmt *s) {
    308  if (!s) {
    309    return nullptr;
    310  }
    311  if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) {
    312    return ewc->getSubExpr();
    313  }
    314  if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) {
    315    // With clang 10 and up `getTemporary` has been replaced with the more
    316    // versatile `getSubExpr`.
    317 #if CLANG_VERSION_FULL >= 1000
    318    return mte->getSubExpr();
    319 #else
    320    return mte->GetTemporaryExpr();
    321 #endif
    322  }
    323  if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) {
    324    return bte->getSubExpr();
    325  }
    326  if (auto *ce = dyn_cast<CastExpr>(s)) {
    327    s = ce->getSubExpr();
    328  }
    329  if (auto *pe = dyn_cast<ParenExpr>(s)) {
    330    s = pe->getSubExpr();
    331  }
    332  // Not a trivial.
    333  return s;
    334 }
    335 
    336 inline const Stmt *IgnoreTrivials(const Stmt *s) {
    337  while (true) {
    338    const Stmt *newS = MaybeSkipOneTrivial(s);
    339    if (newS == s) {
    340      return newS;
    341    }
    342    s = newS;
    343  }
    344 
    345  // Unreachable
    346  return nullptr;
    347 }
    348 
    349 inline const Expr *IgnoreTrivials(const Expr *e) {
    350  return cast_or_null<Expr>(IgnoreTrivials(static_cast<const Stmt *>(e)));
    351 }
    352 
    353 // Returns the input if the input is not a trivial.
    354 inline const Expr *MaybeSkipOneTrivial(const Expr *e) {
    355  return cast_or_null<Expr>(MaybeSkipOneTrivial(static_cast<const Stmt *>(e)));
    356 }
    357 
    358 const FieldDecl *getBaseRefCntMember(QualType T);
    359 
    360 inline const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) {
    361  const FieldDecl *RefCntMember = getClassRefCntMember(D);
    362  if (RefCntMember && isClassRefCounted(D)) {
    363    return RefCntMember;
    364  }
    365 
    366  for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(),
    367                                                E = D->bases_end();
    368       Base != E; ++Base) {
    369    RefCntMember = getBaseRefCntMember(Base->getType());
    370    if (RefCntMember) {
    371      return RefCntMember;
    372    }
    373  }
    374  return 0;
    375 }
    376 
    377 inline const FieldDecl *getBaseRefCntMember(QualType T) {
    378  while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe())
    379    T = ArrTy->getElementType();
    380  CXXRecordDecl *Clazz = T->getAsCXXRecordDecl();
    381  return Clazz ? getBaseRefCntMember(Clazz) : 0;
    382 }
    383 
    384 inline bool isPlacementNew(const CXXNewExpr *Expression) {
    385  // Regular new expressions aren't placement new
    386  if (Expression->getNumPlacementArgs() == 0)
    387    return false;
    388  const FunctionDecl *Declaration = Expression->getOperatorNew();
    389  if (Declaration && hasCustomAttribute<moz_heap_allocator>(Declaration)) {
    390    return false;
    391  }
    392  return true;
    393 }
    394 
    395 extern DenseMap<StringRef, bool> InThirdPartyPathCache;
    396 
    397 inline bool inThirdPartyPath(SourceLocation Loc, const SourceManager &SM) {
    398  StringRef OriginalFileName = getFilename(SM, Loc);
    399  auto pair = InThirdPartyPathCache.find(OriginalFileName);
    400  if (pair != InThirdPartyPathCache.end()) {
    401    return pair->second;
    402  }
    403 
    404  SmallString<1024> FileName = OriginalFileName;
    405  llvm::sys::fs::make_absolute(FileName);
    406 
    407  for (uint32_t i = 0; i < MOZ_THIRD_PARTY_PATHS_COUNT; ++i) {
    408    auto PathB = sys::path::begin(FileName);
    409    auto PathE = sys::path::end(FileName);
    410 
    411    auto ThirdPartyB = sys::path::begin(MOZ_THIRD_PARTY_PATHS[i]);
    412    auto ThirdPartyE = sys::path::end(MOZ_THIRD_PARTY_PATHS[i]);
    413 
    414    for (; PathB != PathE; ++PathB) {
    415      // Perform an inner loop to compare path segments, checking if the current
    416      // segment is the start of the current third party path.
    417      auto IPathB = PathB;
    418      auto IThirdPartyB = ThirdPartyB;
    419      for (; IPathB != PathE && IThirdPartyB != ThirdPartyE;
    420           ++IPathB, ++IThirdPartyB) {
    421        if (IPathB->compare_lower(*IThirdPartyB) != 0) {
    422          break;
    423        }
    424      }
    425 
    426      // We found a match!
    427      if (IThirdPartyB == ThirdPartyE) {
    428        InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, true));
    429        return true;
    430      }
    431    }
    432  }
    433 
    434  InThirdPartyPathCache.insert(std::make_pair(OriginalFileName, false));
    435  return false;
    436 }
    437 
    438 inline bool inThirdPartyPath(const Decl *D, ASTContext *context) {
    439  D = D->getCanonicalDecl();
    440  SourceLocation Loc = D->getLocation();
    441  const SourceManager &SM = context->getSourceManager();
    442 
    443  return inThirdPartyPath(Loc, SM);
    444 }
    445 
    446 inline CXXRecordDecl *getNonTemplateSpecializedCXXRecordDecl(QualType Q) {
    447  auto *D = Q->getAsCXXRecordDecl();
    448 
    449  if (!D) {
    450    auto TemplateQ = Q->getAs<TemplateSpecializationType>();
    451    if (!TemplateQ) {
    452      return nullptr;
    453    }
    454 
    455    auto TemplateDecl = TemplateQ->getTemplateName().getAsTemplateDecl();
    456    if (!TemplateDecl) {
    457      return nullptr;
    458    }
    459 
    460    D = dyn_cast_or_null<CXXRecordDecl>(TemplateDecl->getTemplatedDecl());
    461    if (!D) {
    462      return nullptr;
    463    }
    464  }
    465 
    466  return D;
    467 }
    468 
    469 inline bool inThirdPartyPath(const Decl *D) {
    470  return inThirdPartyPath(D, &D->getASTContext());
    471 }
    472 
    473 inline bool inThirdPartyPath(const Stmt *S, ASTContext *context) {
    474  SourceLocation Loc = S->getBeginLoc();
    475  const SourceManager &SM = context->getSourceManager();
    476  auto ExpansionLoc = SM.getExpansionLoc(Loc);
    477  if (ExpansionLoc.isInvalid()) {
    478    return inThirdPartyPath(Loc, SM);
    479  }
    480  return inThirdPartyPath(ExpansionLoc, SM);
    481 }
    482 
    483 /// Polyfill for CXXOperatorCallExpr::isInfixBinaryOp()
    484 inline bool isInfixBinaryOp(const CXXOperatorCallExpr *OpCall) {
    485 #if CLANG_VERSION_FULL >= 400
    486  return OpCall->isInfixBinaryOp();
    487 #else
    488  // Taken from clang source.
    489  if (OpCall->getNumArgs() != 2)
    490    return false;
    491 
    492  switch (OpCall->getOperator()) {
    493  case OO_Call:
    494  case OO_Subscript:
    495    return false;
    496  default:
    497    return true;
    498  }
    499 #endif
    500 }
    501 
    502 #undef compare_lower
    503 #endif