// Copyright (C) 2016 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dumper/ast_processing.h"
#include "dumper/abi_wrappers.h"
#include "repr/ir_dumper.h"
#include <clang/Lex/Token.h>
#include <clang/AST/QualTypeNames.h>
#include <clang/Index/CodegenNameGenerator.h>
#include <fstream>
#include <iostream>
#include <string>
namespace header_checker {
namespace dumper {
HeaderASTVisitor::HeaderASTVisitor(
const HeaderCheckerOptions &options, clang::MangleContext *mangle_contextp,
clang::ASTContext *ast_contextp,
const clang::CompilerInstance *compiler_instance_p,
const clang::Decl *tu_decl, repr::ModuleIR *module,
ASTCaches *ast_caches)
: options_(options), mangle_contextp_(mangle_contextp),
ast_contextp_(ast_contextp), cip_(compiler_instance_p), tu_decl_(tu_decl),
module_(module), ast_caches_(ast_caches) {}
bool HeaderASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) {
// Avoid segmentation fault in getASTRecordLayout.
if (decl->isInvalidDecl()) {
return true;
}
// Skip forward declarations, dependent records. Also skip anonymous records
// as they will be traversed through record fields.
if (!decl->isThisDeclarationADefinition() ||
decl->getTypeForDecl()->isDependentType() ||
decl->isAnonymousStructOrUnion() ||
!decl->hasNameForLinkage() ||
!decl->isExternallyVisible()) {
return true;
}
RecordDeclWrapper record_decl_wrapper(
mangle_contextp_, ast_contextp_, cip_, decl, module_, ast_caches_);
return record_decl_wrapper.GetRecordDecl();
}
bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) {
if (!decl->isThisDeclarationADefinition() ||
decl->getTypeForDecl()->isDependentType() ||
!decl->hasNameForLinkage()) {
return true;
}
EnumDeclWrapper enum_decl_wrapper(
mangle_contextp_, ast_contextp_, cip_, decl, module_, ast_caches_);
return enum_decl_wrapper.GetEnumDecl();
}
static bool MutateFunctionWithLinkageName(const repr::FunctionIR *function,
repr::ModuleIR *module,
std::string &linkage_name) {
auto added_function = std::make_unique<repr::FunctionIR>();
*added_function = *function;
added_function->SetLinkerSetKey(linkage_name);
return module->AddLinkableMessage(*added_function);
}
static bool AddMangledFunctions(const repr::FunctionIR *function,
repr:: ModuleIR *module,
std::vector<std::string> &manglings) {
for (auto &&mangling : manglings) {
if (!MutateFunctionWithLinkageName(function, module, mangling)) {
return false;
}
}
return true;
}
bool HeaderASTVisitor::ShouldSkipFunctionDecl(const clang::FunctionDecl *decl) {
if (!decl->getDefinition()) {
if (!options_.dump_function_declarations_ ||
options_.source_file_ != ABIWrapper::GetDeclSourceFile(decl, cip_)) {
return true;
}
}
// Skip explicitly deleted functions such as `Foo operator=(Foo) = delete;`.
if (decl->isDeleted()) {
return true;
}
if (decl->getLinkageAndVisibility().getLinkage() !=
clang::Linkage::ExternalLinkage) {
return true;
}
if (const clang::CXXMethodDecl *method_decl =
llvm::dyn_cast<clang::CXXMethodDecl>(decl)) {
const clang::CXXRecordDecl *record_decl = method_decl->getParent();
// Avoid segmentation fault in getThunkInfo in getAllManglings.
if (method_decl->isVirtual() && record_decl->isInvalidDecl()) {
return true;
}
if (record_decl->getTypeForDecl()->isDependentType()) {
return true;
}
}
clang::FunctionDecl::TemplatedKind tkind = decl->getTemplatedKind();
switch (tkind) {
case clang::FunctionDecl::TK_NonTemplate:
case clang::FunctionDecl::TK_FunctionTemplateSpecialization:
case clang::FunctionDecl::TK_MemberSpecialization:
return false;
default:
return true;
}
}
bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) {
if (ShouldSkipFunctionDecl(decl)) {
return true;
}
FunctionDeclWrapper function_decl_wrapper(
mangle_contextp_, ast_contextp_, cip_, decl, module_, ast_caches_);
auto function_wrapper = function_decl_wrapper.GetFunctionDecl();
// Destructors and Constructors can have more than 1 symbol generated from the
// same Decl.
clang::index::CodegenNameGenerator cg(*ast_contextp_);
std::vector<std::string> manglings = cg.getAllManglings(decl);
if (!manglings.empty()) {
return AddMangledFunctions(function_wrapper.get(), module_, manglings);
}
std::string linkage_name =
ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_);
return MutateFunctionWithLinkageName(function_wrapper.get(), module_,
linkage_name);
}
bool HeaderASTVisitor::VisitVarDecl(const clang::VarDecl *decl) {
if (!decl->hasGlobalStorage() ||
decl->getType().getTypePtr()->isDependentType()) {
// Non global / static variable declarations don't need to be dumped.
return true;
}
GlobalVarDeclWrapper global_var_decl_wrapper(
mangle_contextp_, ast_contextp_, cip_, decl, module_, ast_caches_);
return global_var_decl_wrapper.GetGlobalVarDecl();
}
static bool AreHeadersExported(const std::set<std::string> &exported_headers) {
return !exported_headers.empty();
}
// We don't need to recurse into Declarations which are not exported.
bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) {
if (!decl) {
return true;
}
std::string source_file = ABIWrapper::GetDeclSourceFile(decl, cip_);
ast_caches_->decl_to_source_file_cache_.insert(
std::make_pair(decl, source_file));
// If no exported headers are specified we assume the whole AST is exported.
const auto &exported_headers = options_.exported_headers_;
if ((decl != tu_decl_) && AreHeadersExported(exported_headers) &&
(exported_headers.find(source_file) == exported_headers.end())) {
return true;
}
// If at all we're looking at the source file's AST decl node, it should be a
// function decl node.
if ((decl != tu_decl_) &&
(source_file == ast_caches_->translation_unit_source_) &&
!decl->isFunctionOrFunctionTemplate()) {
return true;
}
return RecursiveASTVisitor<HeaderASTVisitor>::TraverseDecl(decl);
}
HeaderASTConsumer::HeaderASTConsumer(
clang::CompilerInstance *compiler_instancep, HeaderCheckerOptions &options)
: cip_(compiler_instancep), options_(options) {}
void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
clang::PrintingPolicy policy(ctx.getPrintingPolicy());
// Suppress 'struct' keyword for C source files while getting QualType string
// names to avoid inconsistency between C and C++ (for C++ files, this is true
// by default)
policy.SuppressTagKeyword = true;
ctx.setPrintingPolicy(policy);
clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl();
std::unique_ptr<clang::MangleContext> mangle_contextp(
ctx.createMangleContext());
const std::string &translation_unit_source =
ABIWrapper::GetDeclSourceFile(translation_unit, cip_);
ASTCaches ast_caches(translation_unit_source);
if (!options_.exported_headers_.empty()) {
options_.exported_headers_.insert(translation_unit_source);
}
std::unique_ptr<repr::ModuleIR> module(
new repr::ModuleIR(nullptr /*FIXME*/));
HeaderASTVisitor v(options_, mangle_contextp.get(), &ctx, cip_,
translation_unit, module.get(), &ast_caches);
if (!v.TraverseDecl(translation_unit)) {
llvm::errs() << "ABI extraction failed\n";
::exit(1);
}
std::unique_ptr<repr::IRDumper> ir_dumper =
repr::IRDumper::CreateIRDumper(options_.text_format_,
options_.dump_name_);
if (!ir_dumper->Dump(*module)) {
llvm::errs() << "Serialization failed\n";
::exit(1);
}
}
} // dumper
} // header_checker