chromium/tools/clang/blink_gc_plugin/Config.h

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file defines the names used by GC infrastructure.

// TODO: Restructure the name determination to use fully qualified names (ala,
// blink::Foo) so that the plugin can be enabled for all of chromium. Doing so
// would allow us to catch errors with structures outside of blink that might
// have unsafe pointers to GC allocated blink structures.

#ifndef TOOLS_BLINK_GC_PLUGIN_CONFIG_H_
#define TOOLS_BLINK_GC_PLUGIN_CONFIG_H_

#include <cassert>

#include "RecordInfo.h"
#include "clang/AST/AST.h"
#include "clang/AST/Attr.h"

extern const char kNewOperatorName[];
extern const char kCreateName[];
extern const char kTraceName[];
extern const char kFinalizeName[];
extern const char kTraceAfterDispatchName[];
extern const char kRegisterWeakMembersName[];
extern const char kHeapAllocatorName[];
extern const char kTraceIfNeededName[];
extern const char kVisitorDispatcherName[];
extern const char kVisitorVarName[];
extern const char kConstIteratorName[];
extern const char kIteratorName[];
extern const char kConstReverseIteratorName[];
extern const char kReverseIteratorName[];

class Config {
 private:
  // Checks that the namespace matches the expected namespace and that the type
  // takes at least |expected_minimum_arg_count| template arguments. If both
  // requirements are fulfilled, populates |args| with the first
  // |expected_minimum_arg_count| template arguments. Verifying only the minimum
  // expected argument keeps the plugin resistant to changes in the type
  // definitions (to some extent)
  static bool VerifyNamespaceAndArgCount(std::string expected_ns_name,
                                         int expected_minimum_arg_count,
                                         llvm::StringRef ns_name,
                                         RecordInfo* info,
                                         RecordInfo::TemplateArgs* args) {
    return (ns_name == expected_ns_name) &&
           info->GetTemplateArgs(expected_minimum_arg_count, args);
  }

 public:
  static bool IsMember(llvm::StringRef name,
                       llvm::StringRef ns_name,
                       RecordInfo* info,
                       RecordInfo::TemplateArgs* args) {
    if (name == "BasicMember") {
      if (!VerifyNamespaceAndArgCount("cppgc", 2, ns_name, info, args))
        return false;
      return (*args)[1]->getAsRecordDecl()->getName() == "StrongMemberTag";
    }
    return false;
  }

  static bool IsWeakMember(llvm::StringRef name,
                           llvm::StringRef ns_name,
                           RecordInfo* info,
                           RecordInfo::TemplateArgs* args) {
    if (name == "BasicMember") {
      if (!VerifyNamespaceAndArgCount("cppgc", 2, ns_name, info, args))
        return false;
      return (*args)[1]->getAsRecordDecl()->getName() == "WeakMemberTag";
    }
    return false;
  }

  static bool IsPersistent(llvm::StringRef name,
                           llvm::StringRef ns_name,
                           RecordInfo* info,
                           RecordInfo::TemplateArgs* args) {
    if (name == "BasicPersistent") {
      return VerifyNamespaceAndArgCount("cppgc", 1, ns_name, info, args);
    }
    return false;
  }

  static bool IsCrossThreadPersistent(llvm::StringRef name,
                                      llvm::StringRef ns_name,
                                      RecordInfo* info,
                                      RecordInfo::TemplateArgs* args) {
    if (name == "BasicCrossThreadPersistent") {
      return VerifyNamespaceAndArgCount("cppgc", 1, ns_name, info, args);
    }
    return false;
  }

  static bool IsRefPtr(llvm::StringRef name) { return name == "scoped_refptr"; }

  static bool IsWeakPtr(llvm::StringRef name) { return name == "WeakPtr"; }

  static bool IsRefOrWeakPtr(llvm::StringRef name) {
    return IsRefPtr(name) || IsWeakPtr(name);
  }

  static bool IsUniquePtr(llvm::StringRef name) {
    return name == "unique_ptr";
  }

  static bool IsTraceWrapperV8Reference(llvm::StringRef name,
                                        llvm::StringRef ns_name,
                                        RecordInfo* info,
                                        RecordInfo::TemplateArgs* args) {
    return name == "TracedReference" &&
           VerifyNamespaceAndArgCount("v8", 1, ns_name, info, args);
  }

  static bool IsWTFCollection(llvm::StringRef name) {
    return name == "Vector" ||
           name == "Deque" ||
           name == "HashSet" ||
           name == "LinkedHashSet" ||
           name == "HashCountedSet" ||
           name == "HashMap";
  }

  static bool IsSTDCollection(llvm::StringRef name) {
    return name == "vector" || name == "map" || name == "unordered_map" ||
           name == "set" || name == "unordered_set" || name == "array" ||
           name == "optional" || name == "variant";
  }

  static bool IsGCCollection(llvm::StringRef name) {
    return name == "HeapVector" || name == "HeapDeque" ||
           name == "HeapHashSet" || name == "HeapLinkedHashSet" ||
           name == "HeapHashCountedSet" || name == "HeapHashMap" ||
           name == "HeapLinkedStack";
  }

  static bool IsHashMap(llvm::StringRef name) {
    return name == "HashMap" || name == "HeapHashMap" || name == "map" ||
           name == "unordered_map";
  }

  // Assumes name is a valid collection name.
  static size_t CollectionDimension(llvm::StringRef name) {
    // In case we're dealing with a variant, we want to collect the whole
    // parameter pack.
    if (name == "variant") {
      return 0;
    }
    return (IsHashMap(name) || name == "pair") ? 2 : 1;
  }

  static bool IsRefCountedBase(llvm::StringRef name) {
    return name == "RefCounted" ||
           name == "ThreadSafeRefCounted";
  }

  static bool IsGCSimpleBase(llvm::StringRef name) {
    return name == "GarbageCollected";
  }

  static bool IsGCMixinBase(llvm::StringRef name) {
    return name == "GarbageCollectedMixin";
  }

  static bool IsGCBase(llvm::StringRef name) {
    return IsGCSimpleBase(name) || IsGCMixinBase(name);
  }

  static bool IsIterator(llvm::StringRef name) {
    return name == kIteratorName || name == kConstIteratorName ||
           name == kReverseIteratorName || name == kConstReverseIteratorName;
  }

  // Returns true of the base classes that do not need a vtable entry for trace
  // because they cannot possibly initiate a GC during construction.
  static bool IsSafePolymorphicBase(llvm::StringRef name) {
    return IsGCBase(name) || IsRefCountedBase(name);
  }

  static bool IsAnnotated(const clang::Decl* decl, const std::string& anno) {
    clang::AnnotateAttr* attr = decl->getAttr<clang::AnnotateAttr>();
    return attr && (attr->getAnnotation() == anno);
  }

  static bool IsIgnoreAnnotated(const clang::Decl* decl) {
    return IsAnnotated(decl, "blink_gc_plugin_ignore");
  }

  static bool IsVisitor(llvm::StringRef name) { return name == "Visitor"; }

  static bool IsVisitorPtrType(const clang::QualType& formal_type) {
    if (!formal_type->isPointerType())
      return false;

    clang::CXXRecordDecl* pointee_type =
        formal_type->getPointeeType()->getAsCXXRecordDecl();
    if (!pointee_type)
      return false;

    if (!IsVisitor(pointee_type->getName()))
      return false;

    return true;
  }

  static bool IsVisitorDispatcherType(const clang::QualType& formal_type) {
    if (const clang::SubstTemplateTypeParmType* subst_type =
            clang::dyn_cast<clang::SubstTemplateTypeParmType>(
                formal_type.getTypePtr())) {
      if (IsVisitorPtrType(subst_type->getReplacementType())) {
        // VisitorDispatcher template parameter substituted to Visitor*.
        return true;
      }
    } else if (const clang::TemplateTypeParmType* parm_type =
                   clang::dyn_cast<clang::TemplateTypeParmType>(
                       formal_type.getTypePtr())) {
      if (parm_type->getDecl()->getName() == kVisitorDispatcherName) {
        // Unresolved, but its parameter name is VisitorDispatcher.
        return true;
      }
    }

    return IsVisitorPtrType(formal_type);
  }

  enum TraceMethodType {
    NOT_TRACE_METHOD,
    TRACE_METHOD,
    TRACE_AFTER_DISPATCH_METHOD,
  };

  static TraceMethodType GetTraceMethodType(const clang::FunctionDecl* method) {
    if (method->getNumParams() != 1)
      return NOT_TRACE_METHOD;

    const std::string& name = method->getNameAsString();
    if (name != kTraceName && name != kTraceAfterDispatchName)
      return NOT_TRACE_METHOD;

    const clang::QualType& formal_type = method->getParamDecl(0)->getType();
    if (!IsVisitorPtrType(formal_type)) {
      return NOT_TRACE_METHOD;
    }

    if (name == kTraceName)
      return TRACE_METHOD;
    if (name == kTraceAfterDispatchName)
      return TRACE_AFTER_DISPATCH_METHOD;

    assert(false && "Should not reach here");
    return NOT_TRACE_METHOD;
  }

  static bool IsTraceMethod(const clang::FunctionDecl* method) {
    return GetTraceMethodType(method) != NOT_TRACE_METHOD;
  }

  static bool IsTraceWrappersMethod(const clang::FunctionDecl* method);

  static bool StartsWith(const std::string& str, const std::string& prefix) {
    if (prefix.size() > str.size())
      return false;
    return str.compare(0, prefix.size(), prefix) == 0;
  }

  static bool EndsWith(const std::string& str, const std::string& suffix) {
    if (suffix.size() > str.size())
      return false;
    return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
  }

  // Test if a template specialization is an instantiation.
  static bool IsTemplateInstantiation(clang::CXXRecordDecl* record);
};

#endif  // TOOLS_BLINK_GC_PLUGIN_CONFIG_H_