From adbbb7e1521e39cf05487fc6de5548729fd19560 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Tue, 17 Jan 2017 14:33:20 -0800 Subject: [PATCH] Added more information in abi dump. Added : 1) Base class specifiers with access, base class name, isVirtual. 2) Added access specifiers for CXXRecordDecl fields and functions. Test: header-abi-dumper -o example1.dump tests/example1.h -- clang -x c++ -I . -std=c++11. Change-Id: I3e44ee14b70600df2ae36000d8e9ace34ed39841 --- .../header-abi-dumper/src/ast_processing.cpp | 88 ++++++++++++++++--- .../header-abi-dumper/src/ast_processing.h | 17 +++- .../tools/header-checker/proto/abi_dump.proto | 13 ++- vndk/tools/header-checker/tests/example1.h | 12 +++ vndk/tools/header-checker/tests/example2.h | 18 ++++ 5 files changed, 128 insertions(+), 20 deletions(-) create mode 100644 vndk/tools/header-checker/tests/example2.h diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp index fd5d98aa9..d3dd2825e 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp @@ -1,3 +1,4 @@ + // Copyright (C) 2016 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,22 +28,38 @@ HeaderASTVisitor::HeaderASTVisitor( abi_dump::TranslationUnit *tu_ptr, clang::MangleContext *mangle_contextp, const clang::ASTContext *ast_contextp, - const clang::CompilerInstance *compiler_instance_p) + const clang::CompilerInstance *compiler_instance_p, + const std::string ¤t_file_name) : tu_ptr_(tu_ptr), mangle_contextp_(mangle_contextp), ast_contextp_(ast_contextp), - cip_(compiler_instance_p) { } + cip_(compiler_instance_p), + current_file_name_(current_file_name) { } -bool HeaderASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) { +// TODO: optimize source file initial check by preferably moving this into +// TraverseTranslationUnitDecl. +bool HeaderASTVisitor::VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) { + std::string source_file = GetDeclSourceFile(decl); + if (source_file != current_file_name_) + return true; abi_dump::RecordDecl *record_decl = tu_ptr_->add_classes(); - SetupClassFields(record_decl, decl); + if (!SetupClassFields(record_decl, decl, source_file) || + !SetupClassBases(record_decl, decl)) { + return false; + } return true; } bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) { + std::string source_file = GetDeclSourceFile(decl); + if (source_file != current_file_name_) { + return true; + } abi_dump::FunctionDecl *function_decl = tu_ptr_->add_functions(); - // FIXME: Use return value. - SetupFunction(function_decl, decl); + + if (!SetupFunction(function_decl, decl, source_file)) { + return false; + } return true; } @@ -59,7 +76,7 @@ void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { std::unique_ptr mangle_contextp( ctx.createMangleContext()); abi_dump::TranslationUnit tu; - HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_); + HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_, file_name_); v.TraverseDecl(translation_unit); std::ofstream text_output(out_dump_name_ + ".txt"); std::fstream binary_output( @@ -84,6 +101,24 @@ std::string HeaderASTVisitor::GetDeclSourceFile(const clang::NamedDecl *decl) { return file_name.str(); } +std::string HeaderASTVisitor::AccessToString(const clang::AccessSpecifier sp) { + std::string str = "none"; + switch (sp) { + case clang::AS_public: + str = "public"; + break; + case clang::AS_private: + str = "private"; + break; + case clang::AS_protected: + str = "protected"; + break; + default: + break; + } + return str; +} + std::string HeaderASTVisitor::GetMangledNameDecl(const clang::NamedDecl *decl) { std::string mangled_or_demangled_name = decl->getName(); if (mangle_contextp_->shouldMangleDeclName(decl)) { @@ -95,12 +130,13 @@ std::string HeaderASTVisitor::GetMangledNameDecl(const clang::NamedDecl *decl) { } bool HeaderASTVisitor::SetupFunction(abi_dump::FunctionDecl *functionp, - const clang::FunctionDecl *decl) { + const clang::FunctionDecl *decl, + const std::string &source_file) { // Go through all the parameters in the method and add them to the fields. // Also get the fully qualfied name and mangled name and store them. functionp->set_function_name(decl->getQualifiedNameAsString()); functionp->set_mangled_function_name(GetMangledNameDecl(decl)); - functionp->set_source_file(GetDeclSourceFile(decl)); + functionp->set_source_file(source_file); clang::QualType return_type = decl->getReturnType().getDesugaredType(*ast_contextp_); functionp->set_return_type( @@ -109,7 +145,7 @@ bool HeaderASTVisitor::SetupFunction(abi_dump::FunctionDecl *functionp, while (param_it != decl->param_end()) { abi_dump::FieldDecl *function_fieldp = functionp->add_parameters(); if (!function_fieldp) { - llvm::errs() << "Couldn't add parameter to method. Aborting"; + llvm::errs() << "Couldn't add parameter to method. Aborting\n"; return false; } function_fieldp->set_field_name((*param_it)->getName()); @@ -118,16 +154,17 @@ bool HeaderASTVisitor::SetupFunction(abi_dump::FunctionDecl *functionp, function_fieldp->set_field_type( clang::TypeName::getFullyQualifiedName(field_type, *ast_contextp_)); - param_it++; } + functionp->set_access(AccessToString(decl->getAccess())); return true; } bool HeaderASTVisitor::SetupClassFields(abi_dump::RecordDecl *classp, - const clang::RecordDecl *decl) { + const clang::CXXRecordDecl *decl, + const std::string &source_file) { classp->set_fully_qualified_name(decl->getQualifiedNameAsString()); - classp->set_source_file(GetDeclSourceFile(decl)); + classp->set_source_file(source_file); classp->set_entity_type("class"); clang::RecordDecl::field_iterator field = decl->field_begin(); while (field != decl->field_end()) { @@ -138,16 +175,39 @@ bool HeaderASTVisitor::SetupClassFields(abi_dump::RecordDecl *classp, return false; } class_fieldp->set_field_name(field->getName()); - //FIXME: This needs to change. Resolve typedef, class name, built-in etc. clang::QualType field_type = field->getType().getDesugaredType(*ast_contextp_); class_fieldp->set_field_type( clang::TypeName::getFullyQualifiedName(field_type, *ast_contextp_)); + class_fieldp->set_access(AccessToString(field->getAccess())); field++; } return true; } +bool HeaderASTVisitor::SetupClassBases(abi_dump::RecordDecl *classp, + const clang::CXXRecordDecl *decl) { + clang::CXXRecordDecl::base_class_const_iterator base_class = + decl->bases_begin(); + while (base_class != decl->bases_end()) { + abi_dump::CXXBaseSpecifier *base_specifierp = classp->add_base_specifiers(); + if (!base_specifierp) { + llvm::errs() << " Couldn't add base specifier to reference dump\n"; + return false; + } + //TODO: Make this pair into a function, used accross. + clang::QualType base_type = + base_class->getType().getDesugaredType(*ast_contextp_); + base_specifierp->set_fully_qualified_name( + clang::TypeName::getFullyQualifiedName(base_type, *ast_contextp_)); + base_specifierp->set_is_virtual(base_class->isVirtual()); + base_specifierp->set_access( + AccessToString(base_class->getAccessSpecifier())); + base_class++; + } + return true; +} + void HeaderASTPPCallbacks::MacroDefined(const clang::Token ¯o_name_tok, const clang::MacroDirective *) { assert(macro_name_tok.isAnyIdentifier()); diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h index fc1940ae4..6d986e532 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h @@ -34,21 +34,29 @@ class HeaderASTVisitor HeaderASTVisitor(abi_dump::TranslationUnit *tu_ptr, clang::MangleContext *mangle_contextp, const clang::ASTContext *ast_contextp, - const clang::CompilerInstance *compiler_instance_p); + const clang::CompilerInstance *compiler_instance_p, + const std::string ¤t_file_name); - bool VisitRecordDecl(const clang::RecordDecl *decl); + bool VisitCXXRecordDecl(const clang::CXXRecordDecl *decl); bool VisitFunctionDecl(const clang::FunctionDecl *decl); private: bool SetupFunction(abi_dump::FunctionDecl *methodp, - const clang::FunctionDecl *decl); + const clang::FunctionDecl *decl, + const std::string &source_file); bool SetupClassFields(abi_dump::RecordDecl *classp, - const clang::RecordDecl *decl); + const clang::CXXRecordDecl *decl, + const std::string &source_file); + + bool SetupClassBases(abi_dump::RecordDecl *classp, + const clang::CXXRecordDecl *decl); std::string GetDeclSourceFile(const clang::NamedDecl *decl); + std::string AccessToString(const clang::AccessSpecifier sp); + std::string GetMangledNameDecl(const clang::NamedDecl *decl); private: @@ -56,6 +64,7 @@ class HeaderASTVisitor clang::MangleContext *mangle_contextp_; const clang::ASTContext *ast_contextp_; const clang::CompilerInstance *cip_; + const std::string current_file_name_; }; class HeaderASTConsumer : public clang::ASTConsumer { diff --git a/vndk/tools/header-checker/proto/abi_dump.proto b/vndk/tools/header-checker/proto/abi_dump.proto index a18d6b60f..5e5992202 100644 --- a/vndk/tools/header-checker/proto/abi_dump.proto +++ b/vndk/tools/header-checker/proto/abi_dump.proto @@ -14,21 +14,30 @@ message FunctionDecl { repeated string template_arguments = 5 ; repeated FieldDecl parameters = 6; required string return_type = 7 [default = "VOID"]; + required string access = 8 [default = "public"]; } message FieldDecl { required string field_name = 1 [default = "NONE"]; required string field_type = 2 [default = "VOID"]; + required string access = 3 [default = "public"]; +} + +message CXXBaseSpecifier { + required string fully_qualified_name = 1 [default = "NONE"]; + required string access = 2 [default = "public"]; + required bool is_virtual = 3 [default = false]; } message RecordDecl { repeated FieldDecl fields = 2; repeated string inner_classes = 3; - repeated string base_classes = 4; + repeated CXXBaseSpecifier base_specifiers = 4; required string fully_qualified_name = 5 [default = "NONE"]; required int64 id = 6 [default = 0]; required string entity_type = 7 [default = "NONE"]; - required string source_file = 9; + required string source_file = 9 [default = "NONE"]; + required bool is_c_struct = 10 [default = false]; } message TranslationUnit { diff --git a/vndk/tools/header-checker/tests/example1.h b/vndk/tools/header-checker/tests/example1.h index 6ef0c755d..7a47e218e 100644 --- a/vndk/tools/header-checker/tests/example1.h +++ b/vndk/tools/header-checker/tests/example1.h @@ -1,6 +1,8 @@ #ifndef EXAMPLE1_H_ #define EXAMPLE1_H_ +#include "example2.h" + #if defined(__cplusplus) extern "C" { #endif @@ -13,6 +15,14 @@ struct Hello { #if defined(__cplusplus) } // extern "C" #endif +using namespace test2; +using namespace test3; +typedef float float_type; +typedef const float_type cfloat_type; +struct CPPHello : private HelloAgain, public ByeAgain { + const int cpp_foo; + cfloat_type cpp_bar; +}; template struct StackNode { @@ -47,4 +57,6 @@ public: } }; +const volatile int Global_Foo(int global_bar); + #endif // EXAMPLE1_H_ diff --git a/vndk/tools/header-checker/tests/example2.h b/vndk/tools/header-checker/tests/example2.h new file mode 100644 index 000000000..b1ead4fbb --- /dev/null +++ b/vndk/tools/header-checker/tests/example2.h @@ -0,0 +1,18 @@ +#ifndef EXAMPLE2_H_ +#define EXAMPLE2_H_ +namespace test2 { +struct HelloAgain { + int foo_again; + int bar_again; +}; +} // namespace test2 + +namespace test3 { +template +struct ByeAgain { + T foo_again; + int bar_again; +}; +} // namespace test3 + +#endif // EXAMPLE2_H_