blob: a87d064dd47d99fe16474dec756cb90072ff75e3 [file] [log] [blame] [edit]
// 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 provides a wrapper for CXXRecordDecl that accumulates GC related
// information about a class. Accumulated information is memoized and the info
// objects are stored in a RecordCache.
#ifndef TOOLS_BLINK_GC_PLUGIN_RECORD_INFO_H_
#define TOOLS_BLINK_GC_PLUGIN_RECORD_INFO_H_
#include <map>
#include <vector>
#include "Edge.h"
#include "clang/AST/AST.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/Frontend/CompilerInstance.h"
class RecordCache;
// A potentially tracable and/or lifetime affecting point in the object graph.
class GraphPoint {
public:
GraphPoint() : traced_(false) {}
virtual ~GraphPoint() {}
void MarkTraced() { traced_ = true; }
bool IsProperlyTraced() { return traced_ || !NeedsTracing().IsNeeded(); }
bool IsInproperlyTraced() { return traced_ && NeedsTracing().IsIllegal(); }
virtual const TracingStatus NeedsTracing() = 0;
private:
bool traced_;
};
class BasePoint : public GraphPoint {
public:
BasePoint(const clang::CXXBaseSpecifier& spec,
RecordInfo* info,
const TracingStatus& status)
: spec_(spec), info_(info), status_(status) {}
const TracingStatus NeedsTracing() override { return status_; }
const clang::CXXBaseSpecifier& spec() { return spec_; }
RecordInfo* info() { return info_; }
private:
const clang::CXXBaseSpecifier& spec_;
RecordInfo* info_;
TracingStatus status_;
};
class FieldPoint : public GraphPoint {
public:
FieldPoint(clang::FieldDecl* field, Edge* edge)
: field_(field), edge_(edge) {}
const TracingStatus NeedsTracing() override {
return edge_->NeedsTracing(Edge::kRecursive);
}
clang::FieldDecl* field() { return field_; }
Edge* edge() { return edge_; }
private:
clang::FieldDecl* field_;
Edge* edge_;
friend class RecordCache;
void deleteEdge() { delete edge_; }
};
// Wrapper class to lazily collect information about a C++ record.
class RecordInfo {
public:
typedef std::vector<std::pair<clang::CXXRecordDecl*, BasePoint>> Bases;
struct FieldDeclCmp {
bool operator()(clang::FieldDecl* a, clang::FieldDecl *b) const {
return a->getBeginLoc() < b->getBeginLoc();
}
};
typedef std::map<clang::FieldDecl*, FieldPoint, FieldDeclCmp> Fields;
typedef std::vector<const clang::Type*> TemplateArgs;
~RecordInfo();
clang::CXXRecordDecl* record() const { return record_; }
const std::string& name() const { return name_; }
Fields& GetFields();
Bases& GetBases();
const clang::CXXBaseSpecifier* GetDirectGCBase();
clang::CXXMethodDecl* GetTraceMethod();
clang::CXXMethodDecl* GetTraceWrappersMethod();
clang::CXXMethodDecl* GetTraceDispatchMethod();
clang::CXXMethodDecl* GetFinalizeDispatchMethod();
bool GetTemplateArgs(size_t count, TemplateArgs* output_args);
bool IsHeapAllocatedCollection();
bool IsGCDerived();
bool IsGCDirectlyDerived();
bool IsGCAllocated();
bool IsGCMixin();
bool IsStackAllocated();
bool IsNewDisallowed();
bool HasDefinition();
clang::CXXMethodDecl* DeclaresNewOperator();
bool RequiresTraceMethod();
bool NeedsFinalization();
bool DeclaresLocalTraceMethod();
TracingStatus NeedsTracing(Edge::NeedsTracingOption);
clang::CXXMethodDecl* InheritsNonVirtualTrace();
bool IsConsideredAbstract();
static clang::CXXRecordDecl* GetDependentTemplatedDecl(const clang::Type&);
private:
RecordInfo(clang::CXXRecordDecl* record, RecordCache* cache);
void walkBases();
Fields* CollectFields();
Bases* CollectBases();
void DetermineTracingMethods();
bool InheritsTrace();
Edge* CreateEdge(const clang::Type* type);
Edge* CreateEdgeFromOriginalType(const clang::Type* type);
bool HasOptionalFinalizer();
bool HasTypeAlias(std::string marker_name) const;
bool GetTemplateArgsInternal(
const llvm::ArrayRef<clang::TemplateArgument>& args,
size_t count,
TemplateArgs* output_args);
RecordCache* cache_;
clang::CXXRecordDecl* record_;
const std::string name_;
TracingStatus fields_need_tracing_;
Bases* bases_ = nullptr;
Fields* fields_ = nullptr;
enum CachedBool { kFalse = 0, kTrue = 1, kNotComputed = 2 };
CachedBool is_stack_allocated_ = kNotComputed;
CachedBool does_need_finalization_ = kNotComputed;
CachedBool is_declaring_local_trace_ = kNotComputed;
bool determined_new_operator_ = false;
clang::CXXMethodDecl* new_operator_ = nullptr;
bool determined_trace_methods_ = false;
clang::CXXMethodDecl* trace_method_ = nullptr;
clang::CXXMethodDecl* trace_dispatch_method_ = nullptr;
clang::CXXMethodDecl* finalize_dispatch_method_ = nullptr;
bool is_gc_derived_ = false;
std::vector<std::string> gc_base_names_;
const clang::CXXBaseSpecifier* directly_derived_gc_base_ = nullptr;
friend class RecordCache;
};
class RecordCache {
public:
RecordCache(clang::CompilerInstance& instance)
: instance_(instance)
{
}
RecordInfo* Lookup(clang::CXXRecordDecl* record);
RecordInfo* Lookup(const clang::CXXRecordDecl* record) {
return Lookup(const_cast<clang::CXXRecordDecl*>(record));
}
RecordInfo* Lookup(clang::DeclContext* decl) {
return Lookup(clang::dyn_cast<clang::CXXRecordDecl>(decl));
}
RecordInfo* Lookup(const clang::Type* type) {
return Lookup(type->getAsCXXRecordDecl());
}
RecordInfo* Lookup(const clang::QualType& type) {
return Lookup(type.getTypePtr());
}
~RecordCache() {
for (Cache::iterator it = cache_.begin(); it != cache_.end(); ++it) {
if (!it->second.fields_)
continue;
for (RecordInfo::Fields::iterator fit = it->second.fields_->begin();
fit != it->second.fields_->end();
++fit) {
fit->second.deleteEdge();
}
}
}
clang::CompilerInstance& instance() const { return instance_; }
private:
clang::CompilerInstance& instance_;
typedef std::map<clang::CXXRecordDecl*, RecordInfo> Cache;
Cache cache_;
};
#endif // TOOLS_BLINK_GC_PLUGIN_RECORD_INFO_H_