Introduce recursive dumping/diffing of types.
1) Types are now quantifed into one of the following:
a) RecordType - structures/ unions/ classes.
b) EnumType - enumerated types.
c) QualifiedType - types with qualifiers intact.
d) PointerType - pointer to a type.
e) LvalueReferenceType
f) RvalueReferenceType
g) ArrayType
2) ABI diffing now diffs based on types directly / indirectly reachable
by function signatures / global variable types by default. Also added an option
to header-abi-diff:
-check-all-apis : Include changes to all APIs exported via exported
headers whether directly / indirectly used by function signatures /
global variable types or not.
3) Introduced an internal representation of abi information. This
enables us to switch formats of abi-dumps / abi-diffs more easily
without changing the AST parsing code. For example: In the future, if we
wanted to add options to view the abi-dumps in html / xml we could just
add classes which extended IRDumper and implement dumping methods.
4) The linked ABI dump of a shared library also includes a list of the
exported functions and objects collected from the .dynsym table. This makes it
possible to avoid false reports of ABI breakages when functions/ global
variables are moved to assembly files.
Test: abi-dumping and linking: make -j64 from ToT
abi_diffing: change parameter types directly, indirectly.
Ran tests/test.py with soong running the built tool, all tests passed.
Bug: 62060883
Bug: 63865902
Change-Id: Id9ef757165a1669049fde20bda4147d5074cc191
This commit is contained in:
@@ -24,11 +24,14 @@ cc_defaults {
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-std=c++11",
|
||||
"-DGOOGLE_PROTOBUF_NO_RTTI",
|
||||
"-UNDEBUG"
|
||||
],
|
||||
|
||||
cppflags: [
|
||||
"-std=c++14",
|
||||
],
|
||||
|
||||
target: {
|
||||
windows: {
|
||||
enabled: false
|
||||
@@ -40,8 +43,8 @@ cc_defaults {
|
||||
name: "header-checker-lib-defaults",
|
||||
|
||||
static_libs: [
|
||||
"libclangTooling",
|
||||
"libclangToolingCore",
|
||||
"libclangTooling",
|
||||
"libclangFrontendTool",
|
||||
"libclangFrontend",
|
||||
"libclangDriver",
|
||||
@@ -134,8 +137,8 @@ cc_binary_host {
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libheader-checker-proto",
|
||||
"libheader-abi-util",
|
||||
"libheader-checker-proto",
|
||||
],
|
||||
|
||||
target: {
|
||||
@@ -269,8 +272,14 @@ cc_library_static {
|
||||
"libLLVMMCParser",
|
||||
"libLLVMCore",
|
||||
"libLLVMSupport",
|
||||
"libheader-checker-proto",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libprotobuf-cpp-full",
|
||||
],
|
||||
|
||||
|
||||
cflags: [
|
||||
"-Wcast-qual",
|
||||
"-Wno-long-long",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
## Usage
|
||||
header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I
|
||||
<export-include-dir-2>.. -- <cflags>
|
||||
For options : header-abi-dumper --help
|
||||
|
||||
# VNDK Header Abi Linker
|
||||
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
## Usage
|
||||
header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ...
|
||||
For options : header-abi-linker --help
|
||||
|
||||
# VNDK Header Abi Diff
|
||||
|
||||
@@ -24,10 +26,13 @@
|
||||
abi's exposed by the two dumps.
|
||||
|
||||
# Return Value
|
||||
1: InCompatible
|
||||
0: Compatible or Compatible Extension.
|
||||
0: Compatible
|
||||
1: Changes to APIs unreferenced by symbols in the .dynsym table
|
||||
4: Compatible Extension
|
||||
8: Incompatible
|
||||
|
||||
|
||||
## Usage
|
||||
header-abi-diff -old <old-abi-dump> -new <new-abi-dump> -o <report>
|
||||
For options : header-abi-diff --help
|
||||
|
||||
|
||||
@@ -14,169 +14,262 @@
|
||||
|
||||
#include "abi_diff.h"
|
||||
|
||||
#include <header_abi_util.h>
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
CompatibilityStatus HeaderAbiDiff::GenerateCompatibilityReport() {
|
||||
abi_dump::TranslationUnit old_tu;
|
||||
abi_dump::TranslationUnit new_tu;
|
||||
std::ifstream old_input(old_dump_);
|
||||
std::ifstream new_input(new_dump_);
|
||||
google::protobuf::io::IstreamInputStream text_iso(&old_input);
|
||||
google::protobuf::io::IstreamInputStream text_isn(&new_input);
|
||||
|
||||
if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) ||
|
||||
!google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) {
|
||||
llvm::errs() << "Failed to generate compatibility report\n";
|
||||
abi_util::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() {
|
||||
using abi_util::TextFormatToIRReader;
|
||||
std::unique_ptr<abi_util::TextFormatToIRReader> old_reader =
|
||||
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", old_dump_);
|
||||
std::unique_ptr<abi_util::TextFormatToIRReader> new_reader =
|
||||
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", new_dump_);
|
||||
if (!old_reader || !new_reader || !old_reader->ReadDump() ||
|
||||
!new_reader->ReadDump()) {
|
||||
llvm::errs() << "Could not create Text Format readers\n";
|
||||
::exit(1);
|
||||
}
|
||||
return CompareTUs(old_tu, new_tu);
|
||||
std::unique_ptr<abi_util::IRDiffDumper> ir_diff_dumper =
|
||||
abi_util::IRDiffDumper::CreateIRDiffDumper("protobuf", cr_);
|
||||
abi_util::CompatibilityStatusIR status =
|
||||
CompareTUs(old_reader.get(), new_reader.get(), ir_diff_dumper.get());
|
||||
if (!ir_diff_dumper->Dump()) {
|
||||
llvm::errs() << "Could not dump diff report\n";
|
||||
::exit(1);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
CompatibilityStatus HeaderAbiDiff::CompareTUs(
|
||||
const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu) {
|
||||
std::unique_ptr<abi_diff::TranslationUnitDiff> diff_tu(
|
||||
new abi_diff::TranslationUnitDiff);
|
||||
CompatibilityStatus record_status = Collect<abi_dump::RecordDecl>(
|
||||
diff_tu->mutable_records_added(), diff_tu->mutable_records_removed(),
|
||||
diff_tu->mutable_records_diff(), old_tu.records(), new_tu.records(),
|
||||
ignored_symbols_);
|
||||
template <typename F>
|
||||
static void AddTypesToMap(std::map<std::string, const abi_util::TypeIR *> *dst,
|
||||
const abi_util::TextFormatToIRReader *tu, F func) {
|
||||
AddToMap(dst, tu->GetRecordTypes(), func);
|
||||
AddToMap(dst, tu->GetEnumTypes(), func);
|
||||
AddToMap(dst, tu->GetPointerTypes(), func);
|
||||
AddToMap(dst, tu->GetBuiltinTypes(), func);
|
||||
AddToMap(dst, tu->GetArrayTypes(), func);
|
||||
AddToMap(dst, tu->GetLvalueReferenceTypes(), func);
|
||||
AddToMap(dst, tu->GetRvalueReferenceTypes(), func);
|
||||
AddToMap(dst, tu->GetQualifiedTypes(), func);
|
||||
}
|
||||
|
||||
CompatibilityStatus function_status = Collect<abi_dump::FunctionDecl>(
|
||||
diff_tu->mutable_functions_added(), diff_tu->mutable_functions_removed(),
|
||||
diff_tu->mutable_functions_diff(), old_tu.functions(),
|
||||
new_tu.functions(), ignored_symbols_);
|
||||
abi_util::CompatibilityStatusIR HeaderAbiDiff::CompareTUs(
|
||||
const abi_util::TextFormatToIRReader *old_tu,
|
||||
const abi_util::TextFormatToIRReader *new_tu,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||
// Collect all old and new types in maps, so that we can refer to them by
|
||||
// type name / linker_set_key later.
|
||||
std::map<std::string, const abi_util::TypeIR *> old_types;
|
||||
std::map<std::string, const abi_util::TypeIR *> new_types;
|
||||
AddTypesToMap(&old_types, old_tu,
|
||||
[](const abi_util::TypeIR *e) {return e->GetLinkerSetKey();});
|
||||
AddTypesToMap(&new_types, new_tu,
|
||||
[](const abi_util::TypeIR *e) {return e->GetLinkerSetKey();});
|
||||
|
||||
CompatibilityStatus enum_status = Collect<abi_dump::EnumDecl>(
|
||||
diff_tu->mutable_enums_added(), diff_tu->mutable_enums_removed(),
|
||||
diff_tu->mutable_enums_diff(), old_tu.enums(), new_tu.enums(),
|
||||
ignored_symbols_);
|
||||
|
||||
CompatibilityStatus global_var_status = Collect<abi_dump::GlobalVarDecl>(
|
||||
diff_tu->mutable_global_vars_added(),
|
||||
diff_tu->mutable_global_vars_removed(),
|
||||
diff_tu->mutable_global_vars_diff(), old_tu.global_vars(),
|
||||
new_tu.global_vars(), ignored_symbols_);
|
||||
|
||||
CompatibilityStatus combined_status =
|
||||
record_status | function_status | enum_status | global_var_status;
|
||||
|
||||
if (combined_status & CompatibilityStatus::INCOMPATIBLE) {
|
||||
combined_status = CompatibilityStatus::INCOMPATIBLE;
|
||||
} else if (combined_status & CompatibilityStatus::EXTENSION) {
|
||||
combined_status = CompatibilityStatus::EXTENSION;
|
||||
} else {
|
||||
combined_status = CompatibilityStatus::COMPATIBLE;
|
||||
}
|
||||
diff_tu->set_compatibility_status(combined_status);
|
||||
diff_tu->set_lib_name(lib_name_);
|
||||
diff_tu->set_arch(arch_);
|
||||
std::ofstream text_output(cr_);
|
||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||
|
||||
if(!google::protobuf::TextFormat::Print(*diff_tu, &text_os)) {
|
||||
llvm::errs() << "Unable to dump report\n";
|
||||
// Collect fills in added, removed ,unsafe and safe function diffs.
|
||||
if (!CollectDynsymExportables(old_tu->GetFunctions(), new_tu->GetFunctions(),
|
||||
old_tu->GetElfFunctions(),
|
||||
new_tu->GetElfFunctions(),
|
||||
old_types, new_types,
|
||||
ir_diff_dumper) ||
|
||||
!CollectDynsymExportables(old_tu->GetGlobalVariables(),
|
||||
new_tu->GetGlobalVariables(),
|
||||
old_tu->GetElfObjects(),
|
||||
new_tu->GetElfObjects(),
|
||||
old_types, new_types,
|
||||
ir_diff_dumper)) {
|
||||
llvm::errs() << "Unable to collect dynsym exportables\n";
|
||||
::exit(1);
|
||||
}
|
||||
|
||||
// By the time this call is reached, all referenced types have been diffed.
|
||||
// So all additional calls on ir_diff_dumper get DiffKind::Unreferenced.
|
||||
if (check_all_apis_ && !CollectUserDefinedTypes(old_tu, new_tu, old_types,
|
||||
new_types, ir_diff_dumper)) {
|
||||
llvm::errs() << "Unable to collect user defined types\n";
|
||||
::exit(1);
|
||||
}
|
||||
|
||||
abi_util::CompatibilityStatusIR combined_status =
|
||||
ir_diff_dumper->GetCompatibilityStatusIR();
|
||||
|
||||
ir_diff_dumper->AddLibNameIR(lib_name_);
|
||||
ir_diff_dumper->AddArchIR(arch_);
|
||||
ir_diff_dumper->AddCompatibilityStatusIR(combined_status);
|
||||
return combined_status;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
abi_diff::CompatibilityStatus HeaderAbiDiff::Collect(
|
||||
google::protobuf::RepeatedPtrField<T> *elements_added,
|
||||
google::protobuf::RepeatedPtrField<T> *elements_removed,
|
||||
google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
|
||||
const google::protobuf::RepeatedPtrField<T> &old_srcs,
|
||||
const google::protobuf::RepeatedPtrField<T> &new_srcs,
|
||||
const std::set<std::string> &ignored_symbols) {
|
||||
assert(elements_added != nullptr);
|
||||
assert(elements_removed != nullptr);
|
||||
assert(elements_diff != nullptr);
|
||||
bool HeaderAbiDiff::CollectUserDefinedTypes(
|
||||
const abi_util::TextFormatToIRReader *old_tu,
|
||||
const abi_util::TextFormatToIRReader *new_tu,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||
return CollectUserDefinedTypesInternal(
|
||||
old_tu->GetRecordTypes(), new_tu->GetRecordTypes(), old_types_map,
|
||||
new_types_map, ir_diff_dumper) &&
|
||||
CollectUserDefinedTypesInternal(old_tu->GetEnumTypes(),
|
||||
new_tu->GetEnumTypes(), old_types_map,
|
||||
new_types_map, ir_diff_dumper);
|
||||
}
|
||||
|
||||
std::map<std::string, const T*> old_elements_map;
|
||||
std::map<std::string, const T*> new_elements_map;
|
||||
AddToMap(&old_elements_map, old_srcs);
|
||||
AddToMap(&new_elements_map, new_srcs);
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
|
||||
const std::vector<T> &old_ud_types,
|
||||
const std::vector<T> &new_ud_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||
// No elf information for records and enums.
|
||||
std::map<std::string, const T *> old_ud_types_map;
|
||||
std::map<std::string, const T *> new_ud_types_map;
|
||||
|
||||
if (!PopulateRemovedElements(elements_removed, old_elements_map,
|
||||
new_elements_map, ignored_symbols) ||
|
||||
!PopulateRemovedElements(elements_added, new_elements_map,
|
||||
old_elements_map, ignored_symbols) ||
|
||||
!PopulateCommonElements(elements_diff, old_elements_map,
|
||||
new_elements_map, ignored_symbols)) {
|
||||
abi_util::AddToMap(&old_ud_types_map, old_ud_types,
|
||||
[](const T *e)
|
||||
{ return e->GetLinkerSetKey();});
|
||||
|
||||
abi_util::AddToMap(&new_ud_types_map, new_ud_types,
|
||||
[](const T *e)
|
||||
{ return e->GetLinkerSetKey();});
|
||||
|
||||
return Collect(old_ud_types_map, new_ud_types_map, nullptr, nullptr,
|
||||
ir_diff_dumper) &&
|
||||
PopulateCommonElements(old_ud_types_map, new_ud_types_map, old_types_map,
|
||||
new_types_map, ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::Unreferenced);
|
||||
}
|
||||
|
||||
template <typename T, typename ElfSymbolType>
|
||||
bool HeaderAbiDiff::CollectDynsymExportables(
|
||||
const std::vector<T> &old_exportables,
|
||||
const std::vector<T> &new_exportables,
|
||||
const std::vector<ElfSymbolType> &old_elf_symbols,
|
||||
const std::vector<ElfSymbolType> &new_elf_symbols,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||
std::map<std::string, const T *> old_exportables_map;
|
||||
std::map<std::string, const T *> new_exportables_map;
|
||||
std::map<std::string, const abi_util::ElfSymbolIR *> old_elf_symbol_map;
|
||||
std::map<std::string, const abi_util::ElfSymbolIR *> new_elf_symbol_map;
|
||||
|
||||
abi_util::AddToMap(&old_exportables_map, old_exportables,
|
||||
[](const T *e)
|
||||
{ return e->GetLinkerSetKey();});
|
||||
abi_util::AddToMap(&new_exportables_map, new_exportables,
|
||||
[](const T *e)
|
||||
{ return e->GetLinkerSetKey();});
|
||||
abi_util::AddToMap(
|
||||
&old_elf_symbol_map, old_elf_symbols,
|
||||
[](const ElfSymbolType *symbol) { return symbol->GetName();});
|
||||
abi_util::AddToMap(
|
||||
&new_elf_symbol_map, new_elf_symbols,
|
||||
[](const ElfSymbolType *symbol) { return symbol->GetName();});
|
||||
|
||||
if (!Collect(old_exportables_map,
|
||||
new_exportables_map, &old_elf_symbol_map, &new_elf_symbol_map,
|
||||
ir_diff_dumper) ||
|
||||
!CollectElfSymbols(old_elf_symbol_map, new_elf_symbol_map,
|
||||
ir_diff_dumper) ||
|
||||
!PopulateCommonElements(old_exportables_map, new_exportables_map,
|
||||
old_types_map, new_types_map, ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::Referenced)) {
|
||||
llvm::errs() << "Diffing dynsym exportables failed\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Collect added and removed Elements. The elf set is needed since some symbols
|
||||
// might not have meta-data about them collected through the AST. For eg: if a
|
||||
// function Foo is defined in an assembly file on target A, but in a c/c++ file
|
||||
// on target B, foo does not have meta-data surrounding it when building target
|
||||
// A, this does not mean it is not in the ABI + API of the library.
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::Collect(
|
||||
const std::map<std::string, const T*> &old_elements_map,
|
||||
const std::map<std::string, const T*> &new_elements_map,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *old_elf_map,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *new_elf_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||
if (!PopulateRemovedElements(
|
||||
old_elements_map, new_elements_map, new_elf_map, ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::Removed) ||
|
||||
!PopulateRemovedElements(new_elements_map, old_elements_map, old_elf_map,
|
||||
ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind::Added)) {
|
||||
llvm::errs() << "Populating functions in report failed\n";
|
||||
::exit(1);
|
||||
return false;
|
||||
}
|
||||
if (elements_diff->size() || elements_removed->size()) {
|
||||
return CompatibilityStatus::INCOMPATIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HeaderAbiDiff::CollectElfSymbols(
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> &old_symbols,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> &new_symbols,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||
std::vector<const abi_util::ElfSymbolIR *> removed_elements =
|
||||
abi_util::FindRemovedElements(old_symbols, new_symbols);
|
||||
|
||||
std::vector<const abi_util::ElfSymbolIR *> added_elements =
|
||||
abi_util::FindRemovedElements(new_symbols, old_symbols);
|
||||
|
||||
return PopulateElfElements(removed_elements, ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind::Removed) &&
|
||||
PopulateElfElements(added_elements, ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind::Added);
|
||||
}
|
||||
|
||||
bool HeaderAbiDiff::PopulateElfElements(
|
||||
std::vector<const abi_util::ElfSymbolIR *> &elf_elements,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
for (auto &&elf_element : elf_elements) {
|
||||
if (!ir_diff_dumper->AddElfSymbolMessageIR(elf_element, diff_kind)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (elements_added->size()) {
|
||||
return CompatibilityStatus::EXTENSION;
|
||||
}
|
||||
return CompatibilityStatus::COMPATIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::PopulateRemovedElements(
|
||||
google::protobuf::RepeatedPtrField<T> *dst,
|
||||
const std::map<std::string, const T*> &old_elements_map,
|
||||
const std::map<std::string, const T*> &new_elements_map,
|
||||
const std::set<std::string> &ignored_symbols) {
|
||||
|
||||
std::vector<const T *> removed_elements;
|
||||
for (auto &&map_element : old_elements_map) {
|
||||
const T *element = map_element.second;
|
||||
auto new_element =
|
||||
new_elements_map.find(element->basic_abi().linker_set_key());
|
||||
if (new_element == new_elements_map.end()) {
|
||||
removed_elements.emplace_back(element);
|
||||
}
|
||||
}
|
||||
if (!DumpLoneElements(dst, removed_elements, ignored_symbols)) {
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *elf_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::vector<const T *> removed_elements =
|
||||
abi_util::FindRemovedElements(old_elements_map, new_elements_map);
|
||||
if (!DumpLoneElements(removed_elements, elf_map, ir_diff_dumper, diff_kind)) {
|
||||
llvm::errs() << "Dumping added / removed element to report failed\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::PopulateCommonElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map,
|
||||
const std::set<std::string> &ignored_symbols) {
|
||||
std::vector<std::pair<const T *, const T *>> common_elements;
|
||||
typename std::map<std::string, const T *>::const_iterator old_element =
|
||||
old_elements_map.begin();
|
||||
typename std::map<std::string, const T *>::const_iterator new_element =
|
||||
new_elements_map.begin();
|
||||
while (old_element != old_elements_map.end() &&
|
||||
new_element != new_elements_map.end()) {
|
||||
if (old_element->first == new_element->first) {
|
||||
common_elements.emplace_back(std::make_pair(
|
||||
old_element->second, new_element->second));
|
||||
old_element++;
|
||||
new_element++;
|
||||
continue;
|
||||
}
|
||||
if (old_element->first < new_element->first) {
|
||||
old_element++;
|
||||
} else {
|
||||
new_element++;
|
||||
}
|
||||
}
|
||||
if (!DumpDiffElements(dst, common_elements, ignored_symbols)) {
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::vector<std::pair<const T *, const T *>> common_elements =
|
||||
abi_util::FindCommonElements(old_elements_map, new_elements_map);
|
||||
if (!DumpDiffElements(common_elements, old_types, new_types,
|
||||
ir_diff_dumper, diff_kind)) {
|
||||
llvm::errs() << "Dumping difference in common element to report failed\n";
|
||||
return false;
|
||||
}
|
||||
@@ -185,48 +278,62 @@ bool HeaderAbiDiff::PopulateCommonElements(
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::DumpLoneElements(
|
||||
google::protobuf::RepeatedPtrField<T> *dst,
|
||||
std::vector<const T *> &elements,
|
||||
const std::set<std::string> &ignored_symbols) {
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *elf_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
// If the record / enum has source file information, skip it.
|
||||
std::smatch source_file_match;
|
||||
std::regex source_file_regex(" at ");
|
||||
for (auto &&element : elements) {
|
||||
if (abi_diff_wrappers::IgnoreSymbol<T>(element, ignored_symbols)) {
|
||||
if (abi_diff_wrappers::IgnoreSymbol<T>(
|
||||
element, ignored_symbols_,
|
||||
[](const T *e) {return e->GetLinkerSetKey();})) {
|
||||
continue;
|
||||
}
|
||||
T *added_element = dst->Add();
|
||||
if (!added_element) {
|
||||
llvm::errs() << "Adding element diff failed\n";
|
||||
// The element does exist in the .dynsym table, we do not have meta-data
|
||||
// surrounding the element.
|
||||
const std::string &element_linker_set_key = element->GetLinkerSetKey();
|
||||
if ((elf_map != nullptr) &&
|
||||
(elf_map->find(element_linker_set_key) != elf_map->end())) {
|
||||
continue;
|
||||
}
|
||||
if (std::regex_search(element_linker_set_key, source_file_match,
|
||||
source_file_regex)) {
|
||||
continue;
|
||||
}
|
||||
if (!ir_diff_dumper->AddLinkableMessageIR(element, diff_kind)) {
|
||||
llvm::errs() << "Couldn't dump added /removed element\n";
|
||||
return false;
|
||||
}
|
||||
*added_element = *element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
|
||||
template <typename T>
|
||||
bool HeaderAbiDiff::DumpDiffElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
std::vector<std::pair<const T *,const T *>> &pairs,
|
||||
const std::set<std::string> &ignored_symbols) {
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
for (auto &&pair : pairs) {
|
||||
const T *old_element = pair.first;
|
||||
const T *new_element = pair.second;
|
||||
// Not having inheritance from protobuf messages makes this
|
||||
// restrictive code.
|
||||
if (abi_diff_wrappers::IgnoreSymbol<T>(old_element, ignored_symbols)) {
|
||||
|
||||
if (abi_diff_wrappers::IgnoreSymbol<T>(
|
||||
old_element, ignored_symbols_,
|
||||
[](const T *e) {return e->GetLinkerSetKey();})) {
|
||||
continue;
|
||||
}
|
||||
abi_diff_wrappers::DiffWrapper<T, TDiff> diff_wrapper(old_element,
|
||||
new_element);
|
||||
std::unique_ptr<TDiff> decl_diff_ptr = diff_wrapper.Get();
|
||||
if (!decl_diff_ptr) {
|
||||
continue;
|
||||
}
|
||||
TDiff *added_element_diff = dst->Add();
|
||||
if (!added_element_diff) {
|
||||
llvm::errs() << "Adding element diff failed\n";
|
||||
abi_diff_wrappers::DiffWrapper<T> diff_wrapper(old_element, new_element,
|
||||
ir_diff_dumper, old_types,
|
||||
new_types, &type_cache_);
|
||||
if (!diff_wrapper.DumpDiff(diff_kind)) {
|
||||
llvm::errs() << "Failed to diff elements\n";
|
||||
return false;
|
||||
}
|
||||
*added_element_diff = *decl_diff_ptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "abi_diff_wrappers.h"
|
||||
|
||||
#include <ir_representation.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
@@ -21,68 +23,102 @@
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
typedef abi_diff::CompatibilityStatus CompatibilityStatus;
|
||||
|
||||
class HeaderAbiDiff {
|
||||
public:
|
||||
HeaderAbiDiff(const std::string &lib_name, const std::string &arch,
|
||||
const std::string &old_dump, const std::string &new_dump,
|
||||
const std::string &compatibility_report,
|
||||
const std::set<std::string> &ignored_symbols)
|
||||
const std::set<std::string> &ignored_symbols,
|
||||
bool check_all_apis)
|
||||
: lib_name_(lib_name), arch_(arch), old_dump_(old_dump),
|
||||
new_dump_(new_dump), cr_(compatibility_report),
|
||||
ignored_symbols_(ignored_symbols) { }
|
||||
ignored_symbols_(ignored_symbols), check_all_apis_(check_all_apis) { }
|
||||
|
||||
CompatibilityStatus GenerateCompatibilityReport();
|
||||
abi_util::CompatibilityStatusIR GenerateCompatibilityReport();
|
||||
|
||||
private:
|
||||
CompatibilityStatus CompareTUs(const abi_dump::TranslationUnit &old_tu,
|
||||
const abi_dump::TranslationUnit &new_tu);
|
||||
// Collect* methods fill in the diff_tu.
|
||||
template <typename T, typename TDiff>
|
||||
static CompatibilityStatus Collect(
|
||||
google::protobuf::RepeatedPtrField<T> *elements_added,
|
||||
google::protobuf::RepeatedPtrField<T> *elements_removed,
|
||||
google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
|
||||
const google::protobuf::RepeatedPtrField<T> &old_srcs,
|
||||
const google::protobuf::RepeatedPtrField<T> &new_srcs,
|
||||
const std::set<std::string> &ignored_symbols);
|
||||
abi_util::CompatibilityStatusIR CompareTUs(
|
||||
const abi_util::TextFormatToIRReader *old_tu,
|
||||
const abi_util::TextFormatToIRReader *new_tu,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||
|
||||
template <typename T, typename ElfSymbolType>
|
||||
bool CollectDynsymExportables(
|
||||
const std::vector<T> &old_exportables,
|
||||
const std::vector<T> &new_exportables,
|
||||
const std::vector<ElfSymbolType> &old_elf_symbols,
|
||||
const std::vector<ElfSymbolType> &new_elf_symbols,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||
|
||||
template <typename T>
|
||||
static inline void AddToMap(std::map<std::string, const T *> *dst,
|
||||
const google::protobuf::RepeatedPtrField<T> &src);
|
||||
bool Collect(
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *old_elf_map,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *new_elf_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||
|
||||
bool CollectElfSymbols(
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> &old_symbols,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> &new_symbols,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||
|
||||
bool PopulateElfElements(
|
||||
std::vector<const abi_util::ElfSymbolIR *> &elf_elements,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
template <typename T>
|
||||
static bool PopulateRemovedElements(
|
||||
google::protobuf::RepeatedPtrField<T> *dst,
|
||||
bool PopulateRemovedElements(
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map,
|
||||
const std::set<std::string> &ignored_symbols);
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *elf_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
static bool PopulateCommonElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
template <typename T>
|
||||
bool PopulateCommonElements(
|
||||
const std::map<std::string, const T *> &old_elements_map,
|
||||
const std::map<std::string, const T *> &new_elements_map,
|
||||
const std::set<std::string> &ignored_symbols);
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
static bool DumpDiffElements(
|
||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
||||
template <typename T>
|
||||
bool DumpDiffElements(
|
||||
std::vector<std::pair<const T *, const T *>> &pairs,
|
||||
const std::set<std::string> &ignored_symbols);
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
template <typename T>
|
||||
static bool DumpLoneElements(google::protobuf::RepeatedPtrField<T> *dst,
|
||||
std::vector<const T *> &elements,
|
||||
const std::set<std::string> &ignored_symbols);
|
||||
bool DumpLoneElements(
|
||||
std::vector<const T *> &elements,
|
||||
const std::map<std::string, const abi_util::ElfSymbolIR *> *elf_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
bool CollectUserDefinedTypes(
|
||||
const abi_util::TextFormatToIRReader *old_tu,
|
||||
const abi_util::TextFormatToIRReader *new_tu,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||
|
||||
template <typename T>
|
||||
bool CollectUserDefinedTypesInternal(
|
||||
const std::vector<T> &old_ud_types,
|
||||
const std::vector<T> &new_ud_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||
|
||||
private:
|
||||
const std::string &lib_name_;
|
||||
@@ -91,27 +127,6 @@ class HeaderAbiDiff {
|
||||
const std::string &new_dump_;
|
||||
const std::string &cr_;
|
||||
const std::set<std::string> &ignored_symbols_;
|
||||
bool check_all_apis_;
|
||||
std::set<std::string> type_cache_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
inline void HeaderAbiDiff::AddToMap(
|
||||
std::map<std::string, const T *> *dst,
|
||||
const google::protobuf::RepeatedPtrField<T> &src) {
|
||||
for (auto &&element : src) {
|
||||
dst->insert(std::make_pair(element.basic_abi().linker_set_key(), &element));
|
||||
}
|
||||
}
|
||||
|
||||
static inline CompatibilityStatus operator|(CompatibilityStatus f,
|
||||
CompatibilityStatus s) {
|
||||
return static_cast<CompatibilityStatus>(
|
||||
static_cast<std::underlying_type<CompatibilityStatus>::type>(f) |
|
||||
static_cast<std::underlying_type<CompatibilityStatus>::type>(s));
|
||||
}
|
||||
|
||||
static inline CompatibilityStatus operator&(
|
||||
CompatibilityStatus f, CompatibilityStatus s) {
|
||||
return static_cast<CompatibilityStatus>(
|
||||
static_cast<std::underlying_type<CompatibilityStatus>::type>(f) &
|
||||
static_cast<std::underlying_type<CompatibilityStatus>::type>(s));
|
||||
}
|
||||
|
||||
@@ -14,52 +14,23 @@
|
||||
|
||||
#include "abi_diff_wrappers.h"
|
||||
|
||||
#include<header_abi_util.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
#include <header_abi_util.h>
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
using abi_diff::RecordDeclDiff;
|
||||
using abi_diff::RecordFieldDeclDiff;
|
||||
using abi_diff::CXXBaseSpecifierDiff;
|
||||
using abi_diff::CXXVTableDiff;
|
||||
using abi_diff::EnumDeclDiff;
|
||||
using abi_diff::ReturnTypeDiff;
|
||||
using abi_diff::ParamDeclDiff;
|
||||
using abi_diff::FunctionDeclDiff;
|
||||
using abi_diff::EnumDeclDiff;
|
||||
using abi_diff::EnumFieldDeclDiff;
|
||||
using abi_diff::GlobalVarDeclDiff;
|
||||
using abi_dump::RecordDecl;
|
||||
using abi_dump::RecordFieldDecl;
|
||||
using abi_dump::EnumDecl;
|
||||
using abi_dump::EnumFieldDecl;
|
||||
using abi_dump::FunctionDecl;
|
||||
using abi_dump::ParamDecl;
|
||||
using abi_dump::VTableComponent;
|
||||
using abi_dump::CXXBaseSpecifier;
|
||||
using abi_dump::GlobalVarDecl;
|
||||
using abi_dump::BasicNamedAndTypedDecl;
|
||||
|
||||
namespace abi_diff_wrappers {
|
||||
|
||||
static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
|
||||
abi_dump::AccessSpecifier new_access) {
|
||||
static bool IsAccessDownGraded(abi_util::AccessSpecifierIR old_access,
|
||||
abi_util::AccessSpecifierIR new_access) {
|
||||
bool access_downgraded = false;
|
||||
switch (old_access) {
|
||||
case abi_dump::AccessSpecifier::protected_access:
|
||||
if (new_access == abi_dump::AccessSpecifier::private_access) {
|
||||
case abi_util::AccessSpecifierIR::ProtectedAccess:
|
||||
if (new_access == abi_util::AccessSpecifierIR::PrivateAccess) {
|
||||
access_downgraded = true;
|
||||
}
|
||||
break;
|
||||
case abi_dump::AccessSpecifier::public_access:
|
||||
if (new_access != abi_dump::AccessSpecifier::public_access) {
|
||||
case abi_util::AccessSpecifierIR::PublicAccess:
|
||||
if (new_access != abi_util::AccessSpecifierIR::PublicAccess) {
|
||||
access_downgraded = true;
|
||||
}
|
||||
break;
|
||||
@@ -69,255 +40,590 @@ static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
|
||||
return access_downgraded;
|
||||
}
|
||||
|
||||
static std::string CpptoCAdjustment(const std::string &type) {
|
||||
std::string adjusted_type_name =
|
||||
abi_util::FindAndReplace(type, "\\bstruct ", "");
|
||||
|
||||
return adjusted_type_name;
|
||||
static std::string Unwind(const std::deque<std::string> *type_queue) {
|
||||
if (!type_queue) {
|
||||
return "";
|
||||
}
|
||||
std::string stack_str;
|
||||
std::deque<std::string> type_queue_copy = *type_queue;
|
||||
while (!type_queue_copy.empty()) {
|
||||
stack_str += type_queue_copy.front() + "-> ";
|
||||
type_queue_copy.pop_front();
|
||||
}
|
||||
return stack_str;
|
||||
}
|
||||
|
||||
static bool CompareTypeNames(const abi_dump::BasicTypeAbi &old_abi,
|
||||
const abi_dump::BasicTypeAbi &new_abi) {
|
||||
// Strip of leading 'struct' keyword from type names
|
||||
std::string old_type = old_abi.name();
|
||||
std::string new_type = new_abi.name();
|
||||
old_type = CpptoCAdjustment(old_type);
|
||||
new_type = CpptoCAdjustment(new_type);
|
||||
// TODO: Add checks for C++ built-in types vs C corresponding types.
|
||||
return old_type != new_type;
|
||||
void DiffWrapperBase::CompareEnumFields(
|
||||
const std::vector<abi_util::EnumFieldIR> &old_fields,
|
||||
const std::vector<abi_util::EnumFieldIR> &new_fields,
|
||||
abi_util::EnumTypeDiffIR *enum_type_diff_ir) {
|
||||
std::map<std::string, const abi_util::EnumFieldIR *> old_fields_map;
|
||||
std::map<std::string, const abi_util::EnumFieldIR *> new_fields_map;
|
||||
abi_util::AddToMap(&old_fields_map, old_fields,
|
||||
[](const abi_util::EnumFieldIR *f)
|
||||
{return f->GetName();});
|
||||
abi_util::AddToMap(&new_fields_map, new_fields,
|
||||
[](const abi_util::EnumFieldIR *f)
|
||||
{return f->GetName();});
|
||||
std::vector<const abi_util::EnumFieldIR *> removed_fields =
|
||||
abi_util::FindRemovedElements(old_fields_map, new_fields_map);
|
||||
|
||||
std::vector<const abi_util::EnumFieldIR *> added_fields =
|
||||
abi_util::FindRemovedElements(new_fields_map, old_fields_map);
|
||||
|
||||
enum_type_diff_ir->SetFieldsAdded(std::move(added_fields));
|
||||
|
||||
enum_type_diff_ir->SetFieldsRemoved(std::move(removed_fields));
|
||||
|
||||
std::vector<std::pair<
|
||||
const abi_util::EnumFieldIR *, const abi_util::EnumFieldIR *>> cf =
|
||||
abi_util::FindCommonElements(old_fields_map, new_fields_map);
|
||||
std::vector<abi_util::EnumFieldDiffIR> enum_field_diffs;
|
||||
for (auto &&common_fields : cf) {
|
||||
if (common_fields.first->GetValue() != common_fields.second->GetValue()) {
|
||||
abi_util::EnumFieldDiffIR enum_field_diff_ir(common_fields.first,
|
||||
common_fields.second);
|
||||
enum_field_diffs.emplace_back(std::move(enum_field_diff_ir));
|
||||
}
|
||||
}
|
||||
enum_type_diff_ir->SetFieldsDiff(std::move(enum_field_diffs));
|
||||
}
|
||||
|
||||
static bool DiffBasicTypeAbi(const abi_dump::BasicTypeAbi &old_abi,
|
||||
const abi_dump::BasicTypeAbi &new_abi) {
|
||||
// We need to add a layer of indirection to account for issues when C and C++
|
||||
// are mixed. For example some types like wchar_t are in-built types for C++
|
||||
// but not for C. Another example would be clang reporting C structures
|
||||
// without the leading "struct" keyword when headers defining them are
|
||||
// included in C++ files.
|
||||
bool name_comparison = CompareTypeNames(old_abi, new_abi);
|
||||
bool size_comparison = (old_abi.size() != new_abi.size());
|
||||
bool alignment_comparison = (old_abi.alignment() != new_abi.alignment());
|
||||
return name_comparison || size_comparison || alignment_comparison;
|
||||
DiffStatus DiffWrapperBase::CompareEnumTypes(
|
||||
const abi_util::EnumTypeIR *old_type, const abi_util::EnumTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
if (old_type->GetName() != new_type->GetName()) {
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
auto enum_type_diff_ir = std::make_unique<abi_util::EnumTypeDiffIR>();
|
||||
enum_type_diff_ir->SetName(old_type->GetName());
|
||||
const std::string &old_underlying_type = old_type->GetUnderlyingType();
|
||||
const std::string &new_underlying_type = new_type->GetUnderlyingType();
|
||||
if (old_underlying_type != new_underlying_type) {
|
||||
enum_type_diff_ir->SetUnderlyingTypeDiff(
|
||||
std::make_unique<std::pair<std::string, std::string>>(
|
||||
old_underlying_type, new_underlying_type));
|
||||
}
|
||||
CompareEnumFields(old_type->GetFields(), new_type->GetFields(),
|
||||
enum_type_diff_ir.get());
|
||||
if ((enum_type_diff_ir->IsExtended() ||
|
||||
enum_type_diff_ir->IsIncompatible()) &&
|
||||
!ir_diff_dumper_->AddDiffMessageIR(enum_type_diff_ir.get(),
|
||||
Unwind(type_queue), diff_kind)) {
|
||||
llvm::errs() << "AddDiffMessage on EnumTypeDiffIR failed\n";
|
||||
::exit(1);
|
||||
}
|
||||
return DiffStatus::no_diff;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool Diff(const T &old_element, const T &new_element) {
|
||||
// Can be specialized for future changes in the format.
|
||||
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
||||
new_element.basic_abi().type_abi()) ||
|
||||
(old_element.basic_abi().name() != new_element.basic_abi().name()) ||
|
||||
IsAccessDownGraded(old_element.basic_abi().access(),
|
||||
new_element.basic_abi().access());
|
||||
bool DiffWrapperBase::CompareVTableComponents(
|
||||
const abi_util::VTableComponentIR &old_component,
|
||||
const abi_util::VTableComponentIR &new_component) {
|
||||
return old_component.GetName() == new_component.GetName() &&
|
||||
old_component.GetValue() == new_component.GetValue() &&
|
||||
old_component.GetKind() == new_component.GetKind();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Diff<EnumFieldDecl>(const EnumFieldDecl &old_element,
|
||||
const EnumFieldDecl &new_element) {
|
||||
// Can be specialized for future changes in the format.
|
||||
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
||||
new_element.basic_abi().type_abi()) ||
|
||||
(old_element.enum_field_value() != new_element.enum_field_value()) ||
|
||||
(old_element.basic_abi().name() != new_element.basic_abi().name());
|
||||
bool DiffWrapperBase::CompareVTables(
|
||||
const abi_util::RecordTypeIR *old_record,
|
||||
const abi_util::RecordTypeIR *new_record) {
|
||||
|
||||
const std::vector<abi_util::VTableComponentIR> &old_components =
|
||||
old_record->GetVTableLayout().GetVTableComponents();
|
||||
const std::vector<abi_util::VTableComponentIR> &new_components =
|
||||
new_record->GetVTableLayout().GetVTableComponents();
|
||||
if (old_components.size() > new_components.size()) {
|
||||
// Something in the vtable got deleted.
|
||||
return false;
|
||||
}
|
||||
uint32_t i = 0;
|
||||
while (i < old_components.size()) {
|
||||
auto &old_element = old_components.at(i);
|
||||
auto &new_element = new_components.at(i);
|
||||
if (!CompareVTableComponents(old_element, new_element)) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Diff<ParamDecl>(const ParamDecl &old_element,
|
||||
const ParamDecl &new_element) {
|
||||
// Can be specialized for future changes in the format.
|
||||
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
||||
new_element.basic_abi().type_abi());
|
||||
bool DiffWrapperBase::CompareSizeAndAlignment(
|
||||
const abi_util::TypeIR *old_type,
|
||||
const abi_util::TypeIR *new_type) {
|
||||
return old_type->GetSize() == new_type->GetSize() &&
|
||||
old_type->GetAlignment() == new_type->GetAlignment();
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Diff<CXXBaseSpecifier>(const CXXBaseSpecifier &old_element,
|
||||
const CXXBaseSpecifier &new_element) {
|
||||
// Can be specialized for future changes in the format.
|
||||
return (DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
||||
new_element.basic_abi().type_abi()) ||
|
||||
old_element.basic_abi().access() != new_element.basic_abi().access() ||
|
||||
old_element.is_virtual() != new_element.is_virtual());
|
||||
std::unique_ptr<abi_util::RecordFieldDiffIR>
|
||||
DiffWrapperBase::CompareCommonRecordFields(
|
||||
const abi_util::RecordFieldIR *old_field,
|
||||
const abi_util::RecordFieldIR *new_field,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
if (old_field->GetOffset() != new_field->GetOffset() ||
|
||||
// TODO: Should this be an inquality check instead ? Some compilers can
|
||||
// make signatures dependant on absolute values of access specifiers.
|
||||
IsAccessDownGraded(old_field->GetAccess(), new_field->GetAccess()) ||
|
||||
CompareAndDumpTypeDiff(old_field->GetReferencedType(),
|
||||
new_field->GetReferencedType(),
|
||||
type_queue, diff_kind) ==
|
||||
DiffStatus::direct_diff) {
|
||||
return std::make_unique<abi_util::RecordFieldDiffIR>(old_field, new_field);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool Diff<VTableComponent>(const VTableComponent &old_element,
|
||||
const VTableComponent &new_element) {
|
||||
bool kind_comparison = old_element.kind() != new_element.kind();
|
||||
bool mangled_name_comparison = old_element.mangled_component_name() !=
|
||||
new_element.mangled_component_name();
|
||||
bool value_comparison =
|
||||
old_element.component_value() != new_element.component_value();
|
||||
return kind_comparison || mangled_name_comparison || value_comparison;
|
||||
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
|
||||
std::vector<const abi_util::RecordFieldIR *>>
|
||||
DiffWrapperBase::CompareRecordFields(
|
||||
const std::vector<abi_util::RecordFieldIR> &old_fields,
|
||||
const std::vector<abi_util::RecordFieldIR> &new_fields,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
|
||||
std::vector<const abi_util::RecordFieldIR *>> diffed_and_removed_fields;
|
||||
std::map<std::string, const abi_util::RecordFieldIR *> old_fields_map;
|
||||
std::map<std::string, const abi_util::RecordFieldIR *> new_fields_map;
|
||||
std::map<uint64_t, const abi_util::RecordFieldIR *> old_fields_offset_map;
|
||||
std::map<uint64_t, const abi_util::RecordFieldIR *> new_fields_offset_map;
|
||||
|
||||
abi_util::AddToMap(&old_fields_map, old_fields,
|
||||
[](const abi_util::RecordFieldIR *f)
|
||||
{return f->GetName();});
|
||||
abi_util::AddToMap(&new_fields_map, new_fields,
|
||||
[](const abi_util::RecordFieldIR *f)
|
||||
{return f->GetName();});
|
||||
abi_util::AddToMap(&old_fields_offset_map, old_fields,
|
||||
[](const abi_util::RecordFieldIR *f)
|
||||
{return f->GetOffset();});
|
||||
abi_util::AddToMap(&new_fields_offset_map, new_fields,
|
||||
[](const abi_util::RecordFieldIR *f)
|
||||
{return f->GetOffset();});
|
||||
// If a field is removed from the map field_name -> offset see if another
|
||||
// field is present at the same offset and compare the size and type etc,
|
||||
// remove it from the removed fields if they're compatible.
|
||||
std::vector<const abi_util::RecordFieldIR *> removed_fields =
|
||||
abi_util::FindRemovedElements(old_fields_map, new_fields_map);
|
||||
uint32_t i = 0;
|
||||
for (auto &&removed_field : removed_fields) {
|
||||
// For the removed field, get the corresponding offset from the old map.
|
||||
// Compare the fields from old map and new map if there's a direct diff,
|
||||
// continue, otherwise remove that field from the removed fields map.
|
||||
uint64_t old_field_offset = removed_field->GetOffset();
|
||||
auto corresponding_field_at_same_offset =
|
||||
new_fields_offset_map.find(old_field_offset);
|
||||
// Correctly reported as removed.
|
||||
if (corresponding_field_at_same_offset == new_fields_offset_map.end()) {
|
||||
continue;
|
||||
}
|
||||
auto comparison_result = CompareCommonRecordFields(
|
||||
removed_field, corresponding_field_at_same_offset->second,
|
||||
type_queue, diff_kind);
|
||||
if (comparison_result != nullptr) {
|
||||
continue;
|
||||
}
|
||||
removed_fields.erase(removed_fields.begin() + i);
|
||||
i++;
|
||||
}
|
||||
diffed_and_removed_fields.second = std::move(removed_fields);
|
||||
std::vector<std::pair<
|
||||
const abi_util::RecordFieldIR *, const abi_util::RecordFieldIR *>> cf =
|
||||
abi_util::FindCommonElements(old_fields_map, new_fields_map);
|
||||
for (auto &&common_fields : cf) {
|
||||
std::unique_ptr<abi_util::RecordFieldDiffIR> diffed_field_ptr =
|
||||
CompareCommonRecordFields(common_fields.first, common_fields.second,
|
||||
type_queue, diff_kind);
|
||||
if (diffed_field_ptr != nullptr) {
|
||||
diffed_and_removed_fields.first.emplace_back(
|
||||
std::move(*(diffed_field_ptr.release())));
|
||||
}
|
||||
}
|
||||
return diffed_and_removed_fields;
|
||||
}
|
||||
|
||||
// This function fills in a *Diff Message's repeated field. For eg:
|
||||
// RecordDeclDiff's CXXBaseSpecifierDiff fields and well as FieldDeclDiff
|
||||
// fields.
|
||||
template <typename T, typename TDiff>
|
||||
template <typename Element, typename ElementDiff>
|
||||
bool DiffWrapperBase<T, TDiff>::GetElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
|
||||
bool differs = false;
|
||||
assert(dst != nullptr);
|
||||
bool DiffWrapperBase::CompareBaseSpecifiers(
|
||||
const std::vector<abi_util::CXXBaseSpecifierIR> &old_base_specifiers,
|
||||
const std::vector<abi_util::CXXBaseSpecifierIR> &new_base_specifiers,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
if (old_base_specifiers.size() != new_base_specifiers.size()) {
|
||||
return false;
|
||||
}
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (i < old_elements.size() && j < new_elements.size()) {
|
||||
const Element &old_element = old_elements.Get(i);
|
||||
const Element &new_element = new_elements.Get(i);
|
||||
|
||||
if (Diff(old_element, new_element)) {
|
||||
ElementDiff *diff = dst->Add();
|
||||
Element *old_elementp = nullptr;
|
||||
Element *new_elementp = nullptr;
|
||||
if (!diff || !(old_elementp = diff->mutable_old()) ||
|
||||
!(new_elementp = diff->mutable_new_())) {
|
||||
llvm::errs() << "Failed to add diff element\n";
|
||||
::exit(1);
|
||||
}
|
||||
*old_elementp = old_element;
|
||||
*new_elementp = new_element;
|
||||
diff->set_index(i);
|
||||
differs = true;
|
||||
while (i < old_base_specifiers.size()) {
|
||||
if (CompareAndDumpTypeDiff(old_base_specifiers.at(i).GetReferencedType(),
|
||||
new_base_specifiers.at(i).GetReferencedType(),
|
||||
type_queue, diff_kind) ==
|
||||
DiffStatus::direct_diff ||
|
||||
(old_base_specifiers.at(i).GetAccess() !=
|
||||
new_base_specifiers.at(i).GetAccess())) {
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
if (old_elements.size() != new_elements.size()) {
|
||||
GetExtraElementDiffs(dst, i, j, old_elements, new_elements);
|
||||
differs = true;
|
||||
}
|
||||
return differs;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
template <typename Element, typename ElementDiff>
|
||||
void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
|
||||
assert(dst != nullptr);
|
||||
while (i < old_elements.size()) {
|
||||
const Element &old_element = old_elements.Get(i);
|
||||
ElementDiff *diff = dst->Add();
|
||||
Element *old_elementp = nullptr;
|
||||
if (!diff || !(old_elementp = diff->mutable_old())) {
|
||||
llvm::errs() << "Failed to add diff element\n";
|
||||
::exit(1);
|
||||
}
|
||||
*old_elementp = old_element;
|
||||
diff->set_index(i);
|
||||
void DiffWrapperBase::CompareTemplateInfo(
|
||||
const std::vector<abi_util::TemplateElementIR> &old_template_elements,
|
||||
const std::vector<abi_util::TemplateElementIR> &new_template_elements,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
uint32_t old_template_size = old_template_elements.size();
|
||||
assert(old_template_size == new_template_elements.size());
|
||||
uint32_t i = 0;
|
||||
while (i < old_template_size) {
|
||||
const abi_util::TemplateElementIR &old_template_element =
|
||||
old_template_elements[i];
|
||||
const abi_util::TemplateElementIR &new_template_element =
|
||||
new_template_elements[i];
|
||||
CompareAndDumpTypeDiff(old_template_element.GetReferencedType(),
|
||||
new_template_element.GetReferencedType(),
|
||||
type_queue, diff_kind);
|
||||
i++;
|
||||
}
|
||||
while (j < new_elements.size()) {
|
||||
const Element &new_element = new_elements.Get(j);
|
||||
ElementDiff *diff = dst->Add();
|
||||
Element *new_elementp = nullptr;
|
||||
if (!diff || !(new_elementp = diff->mutable_new_())) {
|
||||
llvm::errs() << "Failed to add diff element\n";
|
||||
::exit(1);
|
||||
}
|
||||
*new_elementp = new_element;
|
||||
diff->set_index(j);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool DiffBasicNamedAndTypedDecl(BasicNamedAndTypedDecl *type_diff_old,
|
||||
BasicNamedAndTypedDecl *type_diff_new,
|
||||
const BasicNamedAndTypedDecl &old,
|
||||
const BasicNamedAndTypedDecl &new_) {
|
||||
assert(type_diff_old != nullptr);
|
||||
assert(type_diff_new != nullptr);
|
||||
if (DiffBasicTypeAbi(old.type_abi(), new_.type_abi()) ||
|
||||
IsAccessDownGraded(old.access(), new_.access())) {
|
||||
*(type_diff_old) = old;
|
||||
*(type_diff_new) = new_;
|
||||
DiffStatus DiffWrapperBase::CompareRecordTypes(
|
||||
const abi_util::RecordTypeIR *old_type,
|
||||
const abi_util::RecordTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
auto record_type_diff_ir = std::make_unique<abi_util::RecordTypeDiffIR>();
|
||||
// Compare names.
|
||||
if (old_type->GetName() != new_type->GetName()) {
|
||||
// Do not dump anything since the record types themselves are fundamentally
|
||||
// different.
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
record_type_diff_ir->SetName(old_type->GetName());
|
||||
if (old_type->GetAccess() != new_type->GetAccess()) {
|
||||
record_type_diff_ir->SetAccessDiff(
|
||||
std::make_unique<abi_util::AccessSpecifierDiffIR>(
|
||||
old_type->GetAccess(), new_type->GetAccess()));
|
||||
}
|
||||
|
||||
if (!CompareSizeAndAlignment(old_type, new_type)) {
|
||||
record_type_diff_ir->SetTypeDiff(
|
||||
std::make_unique<abi_util::TypeDiffIR>(
|
||||
std::make_pair(old_type->GetSize(), new_type->GetSize()),
|
||||
std::make_pair(old_type->GetAlignment(),
|
||||
new_type->GetAlignment())));
|
||||
}
|
||||
if (!CompareVTables(old_type, new_type)) {
|
||||
record_type_diff_ir->SetVTableLayoutDiff(
|
||||
std::make_unique<abi_util::VTableLayoutDiffIR>(
|
||||
old_type->GetVTableLayout(), new_type->GetVTableLayout()));
|
||||
}
|
||||
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
|
||||
std::vector<const abi_util::RecordFieldIR *>> field_diffs;
|
||||
field_diffs = CompareRecordFields(old_type->GetFields(),
|
||||
new_type->GetFields(), type_queue,
|
||||
diff_kind);
|
||||
record_type_diff_ir->SetFieldDiffs(std::move(field_diffs.first));
|
||||
record_type_diff_ir->SetFieldsRemoved(std::move(field_diffs.second));
|
||||
const std::vector<abi_util::CXXBaseSpecifierIR> &old_bases =
|
||||
old_type->GetBases();
|
||||
const std::vector<abi_util::CXXBaseSpecifierIR> &new_bases =
|
||||
new_type->GetBases();
|
||||
|
||||
if (!CompareBaseSpecifiers(old_bases, new_bases, type_queue, diff_kind)) {
|
||||
record_type_diff_ir->SetBaseSpecifierDiffs(
|
||||
std::make_unique<abi_util::CXXBaseSpecifierDiffIR>(old_bases,
|
||||
new_bases));
|
||||
}
|
||||
if (record_type_diff_ir->DiffExists() &&
|
||||
!ir_diff_dumper_->AddDiffMessageIR(record_type_diff_ir.get(),
|
||||
Unwind(type_queue), diff_kind)) {
|
||||
llvm::errs() << "AddDiffMessage on record type failed\n";
|
||||
::exit(1);
|
||||
} // No need to add a dump for an extension since records can't be "extended".
|
||||
|
||||
CompareTemplateInfo(old_type->GetTemplateElements(),
|
||||
new_type->GetTemplateElements(),
|
||||
type_queue, diff_kind);
|
||||
|
||||
return DiffStatus::no_diff;
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareLvalueReferenceTypes(
|
||||
const abi_util::LvalueReferenceTypeIR *old_type,
|
||||
const abi_util::LvalueReferenceTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
|
||||
new_type->GetReferencedType(),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareRvalueReferenceTypes(
|
||||
const abi_util::RvalueReferenceTypeIR *old_type,
|
||||
const abi_util::RvalueReferenceTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
|
||||
new_type->GetReferencedType(),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareQualifiedTypes(
|
||||
const abi_util::QualifiedTypeIR *old_type,
|
||||
const abi_util::QualifiedTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
// If all the qualifiers are not the same, return direct_diff, else
|
||||
// recursively compare the unqualified types.
|
||||
if (old_type->IsConst() != new_type->IsConst() ||
|
||||
old_type->IsVolatile() != new_type->IsVolatile() ||
|
||||
old_type->IsRestricted() != new_type->IsRestricted()) {
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
|
||||
new_type->GetReferencedType(),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::ComparePointerTypes(
|
||||
const abi_util::PointerTypeIR *old_type,
|
||||
const abi_util::PointerTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
// The following need to be the same for two pointer types to be considered
|
||||
// equivalent:
|
||||
// 1) Number of pointer indirections are the same.
|
||||
// 2) The ultimate pointee is the same.
|
||||
assert(CompareSizeAndAlignment(old_type, new_type));
|
||||
return CompareAndDumpTypeDiff(old_type->GetReferencedType(),
|
||||
new_type->GetReferencedType(),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareBuiltinTypes(
|
||||
const abi_util::BuiltinTypeIR *old_type,
|
||||
const abi_util::BuiltinTypeIR *new_type) {
|
||||
// If the size, alignment and is_unsigned are the same, return no_diff
|
||||
// else return direct_diff.
|
||||
uint64_t old_signedness = old_type->IsUnsigned();
|
||||
uint64_t new_signedness = new_type->IsUnsigned();
|
||||
|
||||
if (!CompareSizeAndAlignment(old_type, new_type) ||
|
||||
old_signedness != new_signedness ||
|
||||
old_type->IsIntegralType() != new_type->IsIntegralType()) {
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
return DiffStatus::no_diff;
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareFunctionParameters(
|
||||
const std::vector<abi_util::ParamIR> &old_parameters,
|
||||
const std::vector<abi_util::ParamIR> &new_parameters,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
size_t old_parameters_size = old_parameters.size();
|
||||
if (old_parameters_size != new_parameters.size()) {
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
uint64_t i = 0;
|
||||
while (i < old_parameters_size) {
|
||||
const abi_util::ParamIR &old_parameter = old_parameters.at(i);
|
||||
const abi_util::ParamIR &new_parameter = new_parameters.at(i);
|
||||
if ((CompareAndDumpTypeDiff(old_parameter.GetReferencedType(),
|
||||
new_parameter.GetReferencedType(),
|
||||
type_queue, diff_kind) ==
|
||||
DiffStatus::direct_diff) ||
|
||||
(old_parameter.GetIsDefault() != new_parameter.GetIsDefault())) {
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return DiffStatus::no_diff;
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareAndDumpTypeDiff(
|
||||
const abi_util::TypeIR *old_type, const abi_util::TypeIR *new_type,
|
||||
abi_util::LinkableMessageKind kind, std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
if (kind == abi_util::LinkableMessageKind::BuiltinTypeKind) {
|
||||
return CompareBuiltinTypes(
|
||||
static_cast<const abi_util::BuiltinTypeIR *>(old_type),
|
||||
static_cast<const abi_util::BuiltinTypeIR *>(new_type));
|
||||
}
|
||||
|
||||
if (kind == abi_util::LinkableMessageKind::QualifiedTypeKind) {
|
||||
return CompareQualifiedTypes(
|
||||
static_cast<const abi_util::QualifiedTypeIR *>(old_type),
|
||||
static_cast<const abi_util::QualifiedTypeIR *>(new_type),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
if (kind == abi_util::LinkableMessageKind::EnumTypeKind) {
|
||||
return CompareEnumTypes(
|
||||
static_cast<const abi_util::EnumTypeIR *>(old_type),
|
||||
static_cast<const abi_util::EnumTypeIR *>(new_type),
|
||||
type_queue, diff_kind);
|
||||
|
||||
}
|
||||
|
||||
if (kind == abi_util::LinkableMessageKind::LvalueReferenceTypeKind) {
|
||||
return CompareLvalueReferenceTypes(
|
||||
static_cast<const abi_util::LvalueReferenceTypeIR *>(old_type),
|
||||
static_cast<const abi_util::LvalueReferenceTypeIR *>(new_type),
|
||||
type_queue, diff_kind);
|
||||
|
||||
}
|
||||
|
||||
if (kind == abi_util::LinkableMessageKind::RvalueReferenceTypeKind) {
|
||||
return CompareRvalueReferenceTypes(
|
||||
static_cast<const abi_util::RvalueReferenceTypeIR *>(old_type),
|
||||
static_cast<const abi_util::RvalueReferenceTypeIR *>(new_type),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
if (kind == abi_util::LinkableMessageKind::PointerTypeKind) {
|
||||
return ComparePointerTypes(
|
||||
static_cast<const abi_util::PointerTypeIR *>(old_type),
|
||||
static_cast<const abi_util::PointerTypeIR *>(new_type),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
|
||||
if (kind == abi_util::LinkableMessageKind::RecordTypeKind) {
|
||||
return CompareRecordTypes(
|
||||
static_cast<const abi_util::RecordTypeIR *>(old_type),
|
||||
static_cast<const abi_util::RecordTypeIR *>(new_type),
|
||||
type_queue, diff_kind);
|
||||
}
|
||||
return DiffStatus::no_diff;
|
||||
}
|
||||
|
||||
static DiffStatus CompareDistinctKindMessages(
|
||||
const abi_util::TypeIR *old_type, const abi_util::TypeIR *new_type) {
|
||||
// For these types to be considered ABI compatible, the very least requirement
|
||||
// is that their sizes and alignments should be equal.
|
||||
// TODO: Fill in
|
||||
return DiffStatus::direct_diff;
|
||||
}
|
||||
|
||||
DiffStatus DiffWrapperBase::CompareAndDumpTypeDiff(
|
||||
const std::string &old_type_str, const std::string &new_type_str,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
// If either of the types are not found in their respective maps, the type
|
||||
// was not exposed in a public header and we do a simple string comparison.
|
||||
// Any diff found using a simple string comparison will be a direct diff.
|
||||
|
||||
// Check the map for types which have already been compared
|
||||
bool same_type_str = (old_type_str == new_type_str);
|
||||
if (same_type_str) {
|
||||
// These types have already been diffed, return without further comparison.
|
||||
if (!type_cache_->insert(old_type_str).second) {
|
||||
return DiffStatus::no_diff;
|
||||
}
|
||||
type_queue->push_back(old_type_str);
|
||||
}
|
||||
std::map<std::string, const abi_util::TypeIR *>::const_iterator old_it =
|
||||
old_types_.find(old_type_str);
|
||||
std::map<std::string, const abi_util::TypeIR *>::const_iterator new_it =
|
||||
new_types_.find(new_type_str);
|
||||
if (old_it == old_types_.end() || new_it == new_types_.end()) {
|
||||
// Do a simple string comparison.
|
||||
if (!type_queue->empty()) {
|
||||
type_queue->pop_back();
|
||||
}
|
||||
return (old_type_str == new_type_str) ?
|
||||
DiffStatus::no_diff : DiffStatus::direct_diff;
|
||||
}
|
||||
abi_util::LinkableMessageKind old_kind =
|
||||
old_it->second->GetKind();
|
||||
abi_util::LinkableMessageKind new_kind =
|
||||
new_it->second->GetKind();
|
||||
DiffStatus diff_status = DiffStatus::no_diff;
|
||||
if (old_kind != new_kind) {
|
||||
diff_status = CompareDistinctKindMessages(old_it->second, new_it->second);
|
||||
} else {
|
||||
diff_status = CompareAndDumpTypeDiff(old_it->second , new_it->second ,
|
||||
old_kind, type_queue, diff_kind);
|
||||
}
|
||||
if (!type_queue->empty()) {
|
||||
type_queue->pop_back();
|
||||
}
|
||||
return diff_status;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool DiffWrapper<abi_util::RecordTypeIR>::DumpDiff(
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::deque<std::string> type_queue;
|
||||
if (oldp_->GetName() != newp_->GetName()) {
|
||||
llvm::errs() << "Comparing two different unreferenced records\n";
|
||||
return false;
|
||||
}
|
||||
if (!type_cache_->insert(oldp_->GetName()).second) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
CompareRecordTypes(oldp_, newp_, &type_queue, diff_kind);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<RecordDeclDiff>
|
||||
DiffWrapper<RecordDecl, RecordDeclDiff>::Get() {
|
||||
std::unique_ptr<RecordDeclDiff> record_diff(new RecordDeclDiff());
|
||||
assert(oldp_->basic_abi().linker_set_key() ==
|
||||
newp_->basic_abi().linker_set_key());
|
||||
record_diff->set_name(oldp_->basic_abi().name());
|
||||
google::protobuf::RepeatedPtrField<RecordFieldDeclDiff> *fdiffs =
|
||||
record_diff->mutable_field_diffs();
|
||||
google::protobuf::RepeatedPtrField<CXXBaseSpecifierDiff> *bdiffs =
|
||||
record_diff->mutable_base_diffs();
|
||||
google::protobuf::RepeatedPtrField<CXXVTableDiff> *vtdiffs =
|
||||
record_diff->mutable_vtable_diffs();
|
||||
assert(fdiffs != nullptr && bdiffs != nullptr);
|
||||
// Template Information isn't diffed since the linker_set_key includes the
|
||||
// mangled name which includes template information.
|
||||
if (GetElementDiffs(fdiffs, oldp_->fields(), newp_->fields()) ||
|
||||
GetElementDiffs(bdiffs, oldp_->base_specifiers(),
|
||||
newp_->base_specifiers()) ||
|
||||
GetElementDiffs(vtdiffs, oldp_->vtable_layout().vtable_components(),
|
||||
newp_->vtable_layout().vtable_components()) ||
|
||||
DiffBasicNamedAndTypedDecl(
|
||||
record_diff->mutable_type_diff()->mutable_old(),
|
||||
record_diff->mutable_type_diff()->mutable_new_(),
|
||||
oldp_->basic_abi(), newp_->basic_abi())) {
|
||||
return record_diff;
|
||||
bool DiffWrapper<abi_util::EnumTypeIR>::DumpDiff(
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::deque<std::string> type_queue;
|
||||
if (oldp_->GetName() != newp_->GetName()) {
|
||||
llvm::errs() << "Comparing two different unreferenced enums\n";
|
||||
return false;
|
||||
}
|
||||
return nullptr;
|
||||
if (!type_cache_->insert(oldp_->GetName()).second) {
|
||||
return true;
|
||||
}
|
||||
CompareEnumTypes(oldp_, newp_, &type_queue, diff_kind);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<EnumDeclDiff>
|
||||
DiffWrapper<EnumDecl, EnumDeclDiff>::Get() {
|
||||
std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff());
|
||||
assert(oldp_->basic_abi().linker_set_key() ==
|
||||
newp_->basic_abi().linker_set_key());
|
||||
google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *fdiffs =
|
||||
enum_diff->mutable_field_diffs();
|
||||
assert(fdiffs != nullptr);
|
||||
enum_diff->set_name(oldp_->basic_abi().name());
|
||||
if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) ||
|
||||
DiffBasicNamedAndTypedDecl(
|
||||
enum_diff->mutable_type_diff()->mutable_old(),
|
||||
enum_diff->mutable_type_diff()->mutable_new_(),
|
||||
oldp_->basic_abi(), newp_->basic_abi())) {
|
||||
return enum_diff;
|
||||
bool DiffWrapper<abi_util::GlobalVarIR>::DumpDiff(
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::deque<std::string> type_queue;
|
||||
type_queue.push_back(oldp_->GetName());
|
||||
DiffStatus type_diff = CompareAndDumpTypeDiff(oldp_->GetReferencedType(),
|
||||
newp_->GetReferencedType(),
|
||||
&type_queue, diff_kind);
|
||||
DiffStatus access_diff = (oldp_->GetAccess() == newp_->GetAccess()) ?
|
||||
DiffStatus::no_diff : DiffStatus::direct_diff;
|
||||
if ((type_diff | access_diff) & DiffStatus::direct_diff) {
|
||||
abi_util::GlobalVarDiffIR global_var_diff_ir(oldp_, newp_);
|
||||
global_var_diff_ir.SetName(oldp_->GetName());
|
||||
return ir_diff_dumper_->AddDiffMessageIR(&global_var_diff_ir,
|
||||
Unwind(&type_queue), diff_kind);
|
||||
}
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::unique_ptr<FunctionDeclDiff>
|
||||
DiffWrapper<FunctionDecl, FunctionDeclDiff>::Get() {
|
||||
std::unique_ptr<FunctionDeclDiff> func_diff(new FunctionDeclDiff());
|
||||
google::protobuf::RepeatedPtrField<ParamDeclDiff> *pdiffs =
|
||||
func_diff->mutable_param_diffs();
|
||||
assert(func_diff->mutable_return_type_diffs() != nullptr);
|
||||
func_diff->set_name(oldp_->basic_abi().linker_set_key());
|
||||
if (DiffBasicNamedAndTypedDecl(
|
||||
func_diff->mutable_return_type_diffs()->mutable_old(),
|
||||
func_diff->mutable_return_type_diffs()->mutable_new_(),
|
||||
oldp_->basic_abi(), newp_->basic_abi()) ||
|
||||
GetElementDiffs(pdiffs, oldp_->parameters(), newp_->parameters())) {
|
||||
return func_diff;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
bool DiffWrapper<abi_util::FunctionIR>::DumpDiff(
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||
std::deque<std::string> type_queue;
|
||||
type_queue.push_back(oldp_->GetName());
|
||||
DiffStatus param_diffs = CompareFunctionParameters(oldp_->GetParameters(),
|
||||
newp_->GetParameters(),
|
||||
&type_queue, diff_kind);
|
||||
DiffStatus return_type_diff =
|
||||
CompareAndDumpTypeDiff(oldp_->GetReturnType(),
|
||||
newp_->GetReturnType(),
|
||||
&type_queue, diff_kind);
|
||||
CompareTemplateInfo(oldp_->GetTemplateElements(),
|
||||
newp_->GetTemplateElements(),
|
||||
&type_queue, diff_kind);
|
||||
|
||||
template <>
|
||||
std::unique_ptr<GlobalVarDeclDiff>
|
||||
DiffWrapper<GlobalVarDecl, GlobalVarDeclDiff>::Get() {
|
||||
std::unique_ptr<GlobalVarDeclDiff> global_var_diff(new GlobalVarDeclDiff());
|
||||
assert(global_var_diff->mutable_type_diff() != nullptr);
|
||||
if (DiffBasicNamedAndTypedDecl(
|
||||
global_var_diff->mutable_type_diff()->mutable_old(),
|
||||
global_var_diff->mutable_type_diff()->mutable_new_(),
|
||||
oldp_->basic_abi(), newp_->basic_abi())) {
|
||||
return global_var_diff;
|
||||
if ((param_diffs == DiffStatus::direct_diff ||
|
||||
return_type_diff == DiffStatus::direct_diff) ||
|
||||
(oldp_->GetAccess() != newp_->GetAccess())) {
|
||||
abi_util::FunctionDiffIR function_diff_ir(oldp_, newp_);
|
||||
function_diff_ir.SetName(oldp_->GetName());
|
||||
return ir_diff_dumper_->AddDiffMessageIR(&function_diff_ir,
|
||||
Unwind(&type_queue), diff_kind);
|
||||
}
|
||||
return nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // abi_diff_wrappers
|
||||
|
||||
@@ -22,45 +22,160 @@
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <ir_representation.h>
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace abi_diff_wrappers {
|
||||
|
||||
template <typename T>
|
||||
template <typename T, typename F>
|
||||
static bool IgnoreSymbol(const T *element,
|
||||
const std::set<std::string> &ignored_symbols) {
|
||||
return ignored_symbols.find(element->basic_abi().linker_set_key()) !=
|
||||
const std::set<std::string> &ignored_symbols,
|
||||
F func) {
|
||||
return ignored_symbols.find(func(element)) !=
|
||||
ignored_symbols.end();
|
||||
}
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
class DiffWrapperBase {
|
||||
public:
|
||||
virtual std::unique_ptr<TDiff> Get() = 0 ;
|
||||
protected:
|
||||
DiffWrapperBase(const T *oldp, const T *newp) : oldp_(oldp), newp_(newp) { }
|
||||
template <typename Element, typename ElementDiff>
|
||||
bool GetElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements);
|
||||
|
||||
private:
|
||||
template <typename Element, typename ElementDiff>
|
||||
void GetExtraElementDiffs(
|
||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
|
||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
||||
const google::protobuf::RepeatedPtrField<Element> &new_elements);
|
||||
|
||||
protected:
|
||||
const T *oldp_;
|
||||
const T *newp_;
|
||||
enum DiffStatus {
|
||||
// Previous stages of CompareAndTypeDiff should not dump the diff.
|
||||
no_diff = 0,
|
||||
// Previous stages of CompareAndTypeDiff should dump the diff if required.
|
||||
direct_diff = 1,
|
||||
};
|
||||
|
||||
template <typename T, typename TDiff>
|
||||
class DiffWrapper : public DiffWrapperBase<T, TDiff> {
|
||||
class DiffWrapperBase {
|
||||
public:
|
||||
DiffWrapper(const T *oldp, const T *newp)
|
||||
: DiffWrapperBase<T, TDiff>(oldp, newp) { }
|
||||
std::unique_ptr<TDiff> Get() override;
|
||||
virtual bool DumpDiff(abi_util::IRDiffDumper::DiffKind diff_kind) = 0;
|
||||
virtual ~DiffWrapperBase() { }
|
||||
protected:
|
||||
DiffWrapperBase(
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||
std::set<std::string> *type_cache)
|
||||
: ir_diff_dumper_(ir_diff_dumper),
|
||||
old_types_(old_types), new_types_(new_types),
|
||||
type_cache_(type_cache) { }
|
||||
|
||||
DiffStatus CompareAndDumpTypeDiff(const std::string &old_type_str,
|
||||
const std::string &new_type_str,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
DiffStatus CompareAndDumpTypeDiff(
|
||||
const abi_util::TypeIR *old_type, const abi_util::TypeIR *new_type,
|
||||
abi_util::LinkableMessageKind kind, std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
|
||||
DiffStatus CompareRecordTypes(const abi_util::RecordTypeIR *old_type,
|
||||
const abi_util::RecordTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
DiffStatus CompareQualifiedTypes(const abi_util::QualifiedTypeIR *old_type,
|
||||
const abi_util::QualifiedTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
DiffStatus ComparePointerTypes(const abi_util::PointerTypeIR *old_type,
|
||||
const abi_util::PointerTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
DiffStatus CompareLvalueReferenceTypes(
|
||||
const abi_util::LvalueReferenceTypeIR *old_type,
|
||||
const abi_util::LvalueReferenceTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
DiffStatus CompareRvalueReferenceTypes(
|
||||
const abi_util::RvalueReferenceTypeIR *old_type,
|
||||
const abi_util::RvalueReferenceTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
|
||||
DiffStatus CompareBuiltinTypes(const abi_util::BuiltinTypeIR *old_type,
|
||||
const abi_util::BuiltinTypeIR *new_type);
|
||||
static void CompareEnumFields(
|
||||
const std::vector<abi_util::EnumFieldIR> &old_fields,
|
||||
const std::vector<abi_util::EnumFieldIR> &new_fields,
|
||||
abi_util::EnumTypeDiffIR *enum_type_diff_ir);
|
||||
|
||||
DiffStatus CompareEnumTypes(const abi_util::EnumTypeIR *old_type,
|
||||
const abi_util::EnumTypeIR *new_type,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
std::unique_ptr<abi_util::RecordFieldDiffIR> CompareCommonRecordFields(
|
||||
const abi_util::RecordFieldIR *old_field,
|
||||
const abi_util::RecordFieldIR *new_field,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
|
||||
std::vector<const abi_util::RecordFieldIR *>>
|
||||
CompareRecordFields(
|
||||
const std::vector<abi_util::RecordFieldIR> &old_fields,
|
||||
const std::vector<abi_util::RecordFieldIR> &new_fields,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
DiffStatus CompareFunctionParameters(
|
||||
const std::vector<abi_util::ParamIR> &old_parameters,
|
||||
const std::vector<abi_util::ParamIR> &new_parameters,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
bool CompareBaseSpecifiers(
|
||||
const std::vector<abi_util::CXXBaseSpecifierIR> &old_base_specifiers,
|
||||
const std::vector<abi_util::CXXBaseSpecifierIR> &new_base_specifiers,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
bool CompareVTables(const abi_util::RecordTypeIR *old_record,
|
||||
const abi_util::RecordTypeIR *new_record);
|
||||
|
||||
bool CompareVTableComponents(
|
||||
const abi_util::VTableComponentIR &old_component,
|
||||
const abi_util::VTableComponentIR &new_component);
|
||||
|
||||
void CompareTemplateInfo(
|
||||
const std::vector<abi_util::TemplateElementIR> &old_template_elements,
|
||||
const std::vector<abi_util::TemplateElementIR> &new_template_elements,
|
||||
std::deque<std::string> *type_queue,
|
||||
abi_util::IRDiffDumper::DiffKind diff_kind);
|
||||
|
||||
|
||||
bool CompareSizeAndAlignment(const abi_util::TypeIR *old_ti,
|
||||
const abi_util::TypeIR *new_ti);
|
||||
|
||||
template <typename DiffType, typename DiffElement>
|
||||
bool AddToDiff(DiffType *mutable_diff, const DiffElement *oldp,
|
||||
const DiffElement *newp,
|
||||
std::deque<std::string> *type_queue = nullptr);
|
||||
protected:
|
||||
abi_util::IRDiffDumper *ir_diff_dumper_;
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types_;
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types_;
|
||||
std::set<std::string> *type_cache_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class DiffWrapper : public DiffWrapperBase {
|
||||
public:
|
||||
DiffWrapper(const T *oldp, const T *newp,
|
||||
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &old_types,
|
||||
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||
std::set<std::string> *type_cache)
|
||||
: DiffWrapperBase(ir_diff_dumper, old_types, new_types, type_cache),
|
||||
oldp_(oldp), newp_(newp) { }
|
||||
bool DumpDiff(abi_util::IRDiffDumper::DiffKind diff_kind) override;
|
||||
private:
|
||||
const T *oldp_;
|
||||
const T *newp_;
|
||||
};
|
||||
|
||||
} // abi_diff_wrappers
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include "abi_diff.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include <llvm/Support/CommandLine.h>
|
||||
#include <llvm/Support/FileSystem.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
@@ -49,6 +51,12 @@ static llvm::cl::opt<bool> advice_only(
|
||||
"advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
|
||||
llvm::cl::cat(header_checker_category));
|
||||
|
||||
static llvm::cl::opt<bool> check_all_apis(
|
||||
"check-all-apis",
|
||||
llvm::cl::desc("All apis, whether referenced or not, by exported symbols in"
|
||||
" the dynsym table of a shared library are checked"),
|
||||
llvm::cl::Optional, llvm::cl::cat(header_checker_category));
|
||||
|
||||
static llvm::cl::opt<bool> suppress_local_warnings(
|
||||
"suppress_local_warnings", llvm::cl::desc("suppress local warnings"),
|
||||
llvm::cl::Optional, llvm::cl::cat(header_checker_category));
|
||||
@@ -58,6 +66,13 @@ static llvm::cl::opt<bool> allow_extensions(
|
||||
llvm::cl::desc("Do not return a non zero status on extensions"),
|
||||
llvm::cl::Optional, llvm::cl::cat(header_checker_category));
|
||||
|
||||
static llvm::cl::opt<bool> allow_unreferenced_changes(
|
||||
"allow-unreferenced-changes",
|
||||
llvm::cl::desc("Do not return a non zero status on changes to data"
|
||||
" structures which are not directly referenced by exported"
|
||||
" APIs."),
|
||||
llvm::cl::Optional, llvm::cl::cat(header_checker_category));
|
||||
|
||||
static std::set<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) {
|
||||
std::ifstream symbol_ifstream(symbol_list_path);
|
||||
std::set<std::string> ignored_symbols;
|
||||
@@ -80,38 +95,50 @@ int main(int argc, const char **argv) {
|
||||
ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list);
|
||||
}
|
||||
HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report,
|
||||
ignored_symbols);
|
||||
ignored_symbols, check_all_apis);
|
||||
|
||||
CompatibilityStatus status = judge.GenerateCompatibilityReport();
|
||||
abi_util::CompatibilityStatusIR status = judge.GenerateCompatibilityReport();
|
||||
|
||||
std::string status_str = "";
|
||||
switch (status) {
|
||||
case CompatibilityStatus::INCOMPATIBLE:
|
||||
status_str = "broken";
|
||||
break;
|
||||
case CompatibilityStatus::EXTENSION:
|
||||
status_str = "extended";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::string unreferenced_change_str = "";
|
||||
std::string error_or_warning_str = "\033[36;1mwarning: \033[0m";
|
||||
|
||||
if (status == abi_util::CompatibilityStatusIR::Incompatible) {
|
||||
error_or_warning_str = "\033[31;1merror: \033[0m";
|
||||
status_str = " INCOMPATIBLE CHANGES";
|
||||
} else if (status & abi_util::CompatibilityStatusIR::Extension) {
|
||||
status_str = "EXTENDING CHANGES";
|
||||
}
|
||||
if (status & abi_util::CompatibilityStatusIR::UnreferencedChanges) {
|
||||
unreferenced_change_str = ", changes in exported headers, which are";
|
||||
unreferenced_change_str += " not directly referenced by exported symbols.";
|
||||
unreferenced_change_str += " This MIGHT be an ABI breaking change due to";
|
||||
unreferenced_change_str += " internal typecasts.";
|
||||
|
||||
}
|
||||
if (!suppress_local_warnings && status) {
|
||||
llvm::errs() << "******************************************************\n"
|
||||
<< "VNDK Abi "
|
||||
<< error_or_warning_str
|
||||
<< "VNDK library: "
|
||||
<< lib_name
|
||||
<< "'s ABI has "
|
||||
<< status_str
|
||||
<< ":"
|
||||
<< unreferenced_change_str
|
||||
<< " Please check compatiblity report at : "
|
||||
<< compatibility_report << "\n"
|
||||
<< "*****************************************************\n";
|
||||
<< "******************************************************\n";
|
||||
}
|
||||
|
||||
if ((allow_extensions &&
|
||||
(status & abi_util::CompatibilityStatusIR::Extension)) ||
|
||||
(allow_unreferenced_changes &&
|
||||
(status & abi_util::CompatibilityStatusIR::UnreferencedChanges))) {
|
||||
return abi_util::CompatibilityStatusIR::Compatible;
|
||||
}
|
||||
|
||||
if (advice_only) {
|
||||
return CompatibilityStatus::COMPATIBLE;
|
||||
return abi_util::CompatibilityStatusIR::Compatible;
|
||||
}
|
||||
|
||||
if (allow_extensions && status == CompatibilityStatus::EXTENSION) {
|
||||
return CompatibilityStatus::COMPATIBLE;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@
|
||||
#ifndef ABI_WRAPPERS_H_
|
||||
#define ABI_WRAPPERS_H_
|
||||
|
||||
#include <ir_representation.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
@@ -32,7 +34,10 @@ class ABIWrapper {
|
||||
public:
|
||||
ABIWrapper(clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *cip);
|
||||
const clang::CompilerInstance *cip,
|
||||
std::set<std::string> *type_cache,
|
||||
abi_util::IRDumper *ir_dumper,
|
||||
std::map<const clang::Decl *, std::string> &decl_to_source_cache);
|
||||
|
||||
static std::string GetDeclSourceFile(const clang::Decl *decl,
|
||||
const clang::CompilerInstance *cip);
|
||||
@@ -41,123 +46,161 @@ class ABIWrapper {
|
||||
clang::MangleContext *mangle_context);
|
||||
protected:
|
||||
abi_dump::AccessSpecifier AccessClangToDump(
|
||||
const clang::AccessSpecifier sp) const;
|
||||
const clang::AccessSpecifier sp);
|
||||
std::string GetCachedDeclSourceFile(const clang::Decl *decl,
|
||||
const clang::CompilerInstance *cip);
|
||||
|
||||
bool SetupTemplateParamNames(abi_dump::TemplateInfo *tinfo,
|
||||
clang::TemplateParameterList *pl) const;
|
||||
bool SetupTemplateArguments(const clang::TemplateArgumentList *tl,
|
||||
abi_util::TemplatedArtifactIR *ta,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupTemplateArguments(abi_dump::TemplateInfo *tinfo,
|
||||
const clang::TemplateArgumentList *tl) const;
|
||||
std::string QualTypeToString(const clang::QualType &sweet_qt);
|
||||
|
||||
std::string QualTypeToString(const clang::QualType &sweet_qt) const;
|
||||
std::string GetTagDeclQualifiedName(const clang::TagDecl *decl);
|
||||
|
||||
std::string GetTagDeclQualifiedName(const clang::TagDecl *decl) const;
|
||||
bool CreateBasicNamedAndTypedDecl(clang::QualType,
|
||||
const std::string &source_file);
|
||||
bool CreateBasicNamedAndTypedDecl(
|
||||
clang::QualType canonical_type,
|
||||
abi_util::TypeIR *typep,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupBasicTypeAbi(abi_dump::BasicTypeAbi *type_abi,
|
||||
const clang::QualType type, bool dump_size) const;
|
||||
bool CreateExtendedType(
|
||||
clang::QualType canonical_type,
|
||||
abi_util::TypeIR *typep);
|
||||
|
||||
bool SetupBasicNamedAndTypedDecl(
|
||||
abi_dump::BasicNamedAndTypedDecl *basic_named_and_typed_decl,
|
||||
const clang::QualType type, const std::string &name,
|
||||
const clang::AccessSpecifier &access, std::string key,
|
||||
bool dump_size) const;
|
||||
clang::QualType GetReferencedType(const clang::QualType qual_type);
|
||||
|
||||
std::string GetTypeLinkageName(const clang::Type *typep) const;
|
||||
std::string GetTypeLinkageName(const clang::Type *typep);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<abi_util::TypeIR> SetTypeKind(const clang::QualType qtype,
|
||||
const std::string &source_file);
|
||||
|
||||
|
||||
protected:
|
||||
const clang::CompilerInstance *cip_;
|
||||
clang::MangleContext *mangle_contextp_;
|
||||
clang::ASTContext *ast_contextp_;
|
||||
std::set<std::string> *type_cache_;
|
||||
abi_util::IRDumper *ir_dumper_;
|
||||
std::map<const clang::Decl *, std::string> &decl_to_source_file_cache_;
|
||||
};
|
||||
|
||||
class RecordDeclWrapper : public ABIWrapper {
|
||||
public:
|
||||
RecordDeclWrapper(clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::RecordDecl *decl);
|
||||
RecordDeclWrapper(
|
||||
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::RecordDecl *decl, std::set<std::string> *type_cache,
|
||||
abi_util::IRDumper *ir_dumper,
|
||||
std::map<const clang::Decl *, std::string> &decl_to_source_cache_,
|
||||
const std::string &previous_record_stages);
|
||||
|
||||
std::unique_ptr<abi_dump::RecordDecl> GetRecordDecl() const;
|
||||
bool GetRecordDecl();
|
||||
|
||||
private:
|
||||
const clang::RecordDecl *record_decl_;
|
||||
std::string previous_record_stages_;
|
||||
|
||||
private:
|
||||
bool SetupRecordInfo(abi_dump::RecordDecl *record_declp,
|
||||
const std::string &source_file) const;
|
||||
bool SetupRecordInfo(abi_util::RecordTypeIR *type,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupRecordFields(abi_dump::RecordDecl *record_declp) const;
|
||||
bool SetupRecordFields(abi_util::RecordTypeIR *record_declp,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupCXXBases(abi_dump::RecordDecl *cxxp,
|
||||
const clang::CXXRecordDecl *cxx_record_decl) const;
|
||||
bool SetupCXXBases(abi_util::RecordTypeIR *cxxp,
|
||||
const clang::CXXRecordDecl *cxx_record_decl);
|
||||
|
||||
bool SetupTemplateInfo(abi_dump::RecordDecl *record_declp,
|
||||
const clang::CXXRecordDecl *cxx_record_decl) const;
|
||||
bool SetupTemplateInfo(abi_util::RecordTypeIR *record_declp,
|
||||
const clang::CXXRecordDecl *cxx_record_decl,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupRecordVTable(abi_dump::RecordDecl *record_declp,
|
||||
const clang::CXXRecordDecl *cxx_record_decl) const;
|
||||
bool SetupRecordVTableComponent(
|
||||
abi_dump::VTableComponent *added_vtable_component,
|
||||
const clang::VTableComponent &vtable_component) const;
|
||||
bool SetupRecordVTable(abi_util::RecordTypeIR *record_declp,
|
||||
const clang::CXXRecordDecl *cxx_record_decl);
|
||||
abi_util::VTableComponentIR SetupRecordVTableComponent(
|
||||
const clang::VTableComponent &vtable_component);
|
||||
|
||||
bool SetupCXXRecordInfo(abi_dump::RecordDecl *record_declp) const;
|
||||
bool SetupCXXRecordInfo(abi_util::RecordTypeIR *record_declp,
|
||||
const std::string &source_file);
|
||||
|
||||
bool CreateAnonymousRecord(
|
||||
const clang::RecordDecl *decl, const std::string &linker_set_key);
|
||||
};
|
||||
|
||||
class FunctionDeclWrapper : public ABIWrapper {
|
||||
public:
|
||||
FunctionDeclWrapper(clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::FunctionDecl *decl);
|
||||
FunctionDeclWrapper(
|
||||
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::FunctionDecl *decl, std::set<std::string> *type_cache,
|
||||
abi_util::IRDumper *ir_dumper,
|
||||
std::map<const clang::Decl *, std::string> &decl_to_source_cache_);
|
||||
|
||||
std::unique_ptr<abi_dump::FunctionDecl> GetFunctionDecl() const;
|
||||
std::unique_ptr<abi_util::FunctionIR> GetFunctionDecl();
|
||||
|
||||
private:
|
||||
const clang::FunctionDecl *function_decl_;
|
||||
|
||||
private:
|
||||
bool SetupFunction(abi_dump::FunctionDecl *methodp,
|
||||
const std::string &source_file) const;
|
||||
bool SetupFunction(abi_util::FunctionIR *methodp,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupTemplateInfo(abi_dump::FunctionDecl *functionp) const;
|
||||
bool SetupTemplateInfo(abi_util::FunctionIR *functionp,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupFunctionParameters(abi_util::FunctionIR *functionp,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupFunctionParameter(abi_util::FunctionIR *functionp,
|
||||
const clang::QualType qual_type,
|
||||
bool has_default_arg,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupThisParameter(abi_util::FunctionIR *functionp,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupFunctionParameters(abi_dump::FunctionDecl *functionp) const;
|
||||
};
|
||||
|
||||
class EnumDeclWrapper : public ABIWrapper {
|
||||
public:
|
||||
EnumDeclWrapper(clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::EnumDecl *decl);
|
||||
EnumDeclWrapper(
|
||||
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::EnumDecl *decl,
|
||||
std::set<std::string> *type_cache, abi_util::IRDumper *ir_dumper,
|
||||
std::map<const clang::Decl *, std::string> &decl_to_source_cache_);
|
||||
|
||||
std::unique_ptr<abi_dump::EnumDecl> GetEnumDecl() const;
|
||||
bool GetEnumDecl();
|
||||
|
||||
private:
|
||||
const clang::EnumDecl *enum_decl_;
|
||||
|
||||
private:
|
||||
bool SetupEnum(abi_dump::EnumDecl *enump,
|
||||
const std::string &source_file) const;
|
||||
bool SetupEnumFields(abi_dump::EnumDecl *enump) const;
|
||||
bool SetupEnum(abi_util::EnumTypeIR *type,
|
||||
const std::string &source_file);
|
||||
|
||||
bool SetupEnumFields(abi_util::EnumTypeIR *enump);
|
||||
};
|
||||
|
||||
class GlobalVarDeclWrapper : public ABIWrapper {
|
||||
public:
|
||||
GlobalVarDeclWrapper(clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::VarDecl *decl);
|
||||
GlobalVarDeclWrapper(
|
||||
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const clang::VarDecl *decl,
|
||||
std::set<std::string> *type_cache, abi_util::IRDumper *ir_dumper,
|
||||
std::map<const clang::Decl *, std::string> &decl_to_source_cache_);
|
||||
|
||||
std::unique_ptr<abi_dump::GlobalVarDecl> GetGlobalVarDecl() const;
|
||||
bool GetGlobalVarDecl();
|
||||
|
||||
private:
|
||||
const clang::VarDecl *global_var_decl_;
|
||||
private:
|
||||
bool SetupGlobalVar(abi_dump::GlobalVarDecl *global_varp,
|
||||
const std::string &source_file) const;
|
||||
bool SetupGlobalVar(abi_util::GlobalVarIR *global_varp,
|
||||
const std::string &source_file);
|
||||
};
|
||||
|
||||
} //end namespace abi_wrapper
|
||||
|
||||
#endif // ABI_WRAPPERS_H_
|
||||
#endif // ABI_WRAPPERS_H_
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
#include <clang/Tooling/Core/QualTypeNames.h>
|
||||
#include <clang/Index/CodegenNameGenerator.h>
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
@@ -33,83 +30,64 @@ using abi_wrapper::EnumDeclWrapper;
|
||||
using abi_wrapper::GlobalVarDeclWrapper;
|
||||
|
||||
HeaderASTVisitor::HeaderASTVisitor(
|
||||
abi_dump::TranslationUnit *tu_ptr,
|
||||
clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const std::string ¤t_file_name,
|
||||
const std::set<std::string> &exported_headers,
|
||||
const clang::Decl *tu_decl)
|
||||
: tu_ptr_(tu_ptr),
|
||||
mangle_contextp_(mangle_contextp),
|
||||
const clang::Decl *tu_decl,
|
||||
std::set<std::string> *type_cache,
|
||||
abi_util::IRDumper *ir_dumper)
|
||||
: mangle_contextp_(mangle_contextp),
|
||||
ast_contextp_(ast_contextp),
|
||||
cip_(compiler_instance_p),
|
||||
current_file_name_(current_file_name),
|
||||
exported_headers_(exported_headers),
|
||||
tu_decl_(tu_decl) { }
|
||||
tu_decl_(tu_decl),
|
||||
type_cache_(type_cache),
|
||||
ir_dumper_(ir_dumper) { }
|
||||
|
||||
bool HeaderASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) {
|
||||
// Skip forward declaration.
|
||||
// Skip forward declarations, dependent records. Also skip anonymous records
|
||||
// as they will be traversed through record fields.
|
||||
if (!decl->isThisDeclarationADefinition() ||
|
||||
decl->getTypeForDecl()->isDependentType()) {
|
||||
decl->getTypeForDecl()->isDependentType() ||
|
||||
decl->isAnonymousStructOrUnion() ||
|
||||
!decl->hasNameForLinkage()) {
|
||||
return true;
|
||||
}
|
||||
RecordDeclWrapper record_decl_wrapper(
|
||||
mangle_contextp_, ast_contextp_, cip_, decl);
|
||||
std::unique_ptr<abi_dump::RecordDecl> wrapped_record_decl =
|
||||
record_decl_wrapper.GetRecordDecl();
|
||||
if (!wrapped_record_decl) {
|
||||
llvm::errs() << "Getting Record Decl failed\n";
|
||||
return false;
|
||||
}
|
||||
abi_dump::RecordDecl *added_record_declp = tu_ptr_->add_records();
|
||||
if (!added_record_declp) {
|
||||
return false;
|
||||
}
|
||||
*added_record_declp = *wrapped_record_decl;
|
||||
return true;
|
||||
mangle_contextp_, ast_contextp_, cip_, decl, type_cache_,
|
||||
ir_dumper_, decl_to_source_file_cache_, "");
|
||||
return record_decl_wrapper.GetRecordDecl();
|
||||
}
|
||||
|
||||
bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) {
|
||||
if (!decl->isThisDeclarationADefinition() ||
|
||||
decl->getTypeForDecl()->isDependentType()) {
|
||||
decl->getTypeForDecl()->isDependentType() ||
|
||||
!decl->hasNameForLinkage()) {
|
||||
return true;
|
||||
}
|
||||
EnumDeclWrapper enum_decl_wrapper(
|
||||
mangle_contextp_, ast_contextp_, cip_, decl);
|
||||
std::unique_ptr<abi_dump::EnumDecl> wrapped_enum_decl =
|
||||
enum_decl_wrapper.GetEnumDecl();
|
||||
if (!wrapped_enum_decl) {
|
||||
llvm::errs() << "Getting Enum Decl failed\n";
|
||||
return false;
|
||||
}
|
||||
abi_dump::EnumDecl *added_enum_declp = tu_ptr_->add_enums();
|
||||
if (!added_enum_declp) {
|
||||
return false;
|
||||
}
|
||||
*added_enum_declp = *wrapped_enum_decl;
|
||||
return true;
|
||||
}
|
||||
mangle_contextp_, ast_contextp_, cip_, decl, type_cache_,
|
||||
ir_dumper_, decl_to_source_file_cache_);
|
||||
return enum_decl_wrapper.GetEnumDecl();
|
||||
}
|
||||
|
||||
static bool MutateFunctionWithLinkageName(abi_dump::TranslationUnit *tu_ptr,
|
||||
const abi_dump::FunctionDecl *fd,
|
||||
static bool MutateFunctionWithLinkageName(const abi_util::FunctionIR *function,
|
||||
abi_util::IRDumper *ir_dumper,
|
||||
std::string &linkage_name) {
|
||||
abi_dump::FunctionDecl *added_function_declp = tu_ptr->add_functions();
|
||||
if (!added_function_declp) {
|
||||
return false;
|
||||
}
|
||||
*added_function_declp = *fd;
|
||||
added_function_declp->set_mangled_function_name(linkage_name);
|
||||
assert(added_function_declp->mutable_basic_abi() != nullptr);
|
||||
added_function_declp->mutable_basic_abi()->set_linker_set_key(linkage_name);
|
||||
return true;
|
||||
auto added_function = std::make_unique<abi_util::FunctionIR>();
|
||||
*added_function = *function;
|
||||
added_function->SetLinkerSetKey(linkage_name);
|
||||
return ir_dumper->AddLinkableMessageIR(added_function.get());
|
||||
}
|
||||
|
||||
static bool AddMangledFunctions(abi_dump::TranslationUnit *tu_ptr,
|
||||
const abi_dump::FunctionDecl *fd,
|
||||
static bool AddMangledFunctions(const abi_util::FunctionIR *function,
|
||||
abi_util:: IRDumper *ir_dumper,
|
||||
std::vector<std::string> &manglings) {
|
||||
for (auto &&mangling : manglings) {
|
||||
if (!MutateFunctionWithLinkageName(tu_ptr, fd, mangling)) {
|
||||
if (!MutateFunctionWithLinkageName(function, ir_dumper, mangling)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -139,23 +117,20 @@ bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) {
|
||||
return true;
|
||||
}
|
||||
FunctionDeclWrapper function_decl_wrapper(mangle_contextp_, ast_contextp_,
|
||||
cip_, decl);
|
||||
std::unique_ptr<abi_dump::FunctionDecl> wrapped_function_decl =
|
||||
function_decl_wrapper.GetFunctionDecl();
|
||||
if (!wrapped_function_decl) {
|
||||
llvm::errs() << "Getting Function Decl failed\n";
|
||||
return false;
|
||||
}
|
||||
cip_, decl, type_cache_,
|
||||
ir_dumper_,
|
||||
decl_to_source_file_cache_);
|
||||
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(tu_ptr_, wrapped_function_decl.get(), manglings);
|
||||
return AddMangledFunctions(function_wrapper.get(), ir_dumper_, manglings);
|
||||
}
|
||||
std::string linkage_name =
|
||||
ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_);
|
||||
return MutateFunctionWithLinkageName(tu_ptr_, wrapped_function_decl.get(),
|
||||
return MutateFunctionWithLinkageName(function_wrapper.get(), ir_dumper_,
|
||||
linkage_name);
|
||||
}
|
||||
|
||||
@@ -166,19 +141,10 @@ bool HeaderASTVisitor::VisitVarDecl(const clang::VarDecl *decl) {
|
||||
return true;
|
||||
}
|
||||
GlobalVarDeclWrapper global_var_decl_wrapper(mangle_contextp_, ast_contextp_,
|
||||
cip_, decl);
|
||||
std::unique_ptr<abi_dump::GlobalVarDecl> wrapped_global_var_decl =
|
||||
global_var_decl_wrapper.GetGlobalVarDecl();
|
||||
if (!wrapped_global_var_decl) {
|
||||
llvm::errs() << "Getting Global Var Decl failed\n";
|
||||
return false;
|
||||
}
|
||||
abi_dump::GlobalVarDecl *added_global_var_declp = tu_ptr_->add_global_vars();
|
||||
if (!added_global_var_declp) {
|
||||
return false;
|
||||
}
|
||||
*added_global_var_declp = *wrapped_global_var_decl;
|
||||
return true;
|
||||
cip_, decl, type_cache_,
|
||||
ir_dumper_,
|
||||
decl_to_source_file_cache_);
|
||||
return global_var_decl_wrapper.GetGlobalVarDecl();
|
||||
}
|
||||
|
||||
static bool AreHeadersExported(const std::set<std::string> &exported_headers) {
|
||||
@@ -191,6 +157,7 @@ bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) {
|
||||
return true;
|
||||
}
|
||||
std::string source_file = ABIWrapper::GetDeclSourceFile(decl, cip_);
|
||||
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.
|
||||
if ((decl != tu_decl_) && AreHeadersExported(exported_headers_) &&
|
||||
(exported_headers_.find(source_file) == exported_headers_.end())) {
|
||||
@@ -210,18 +177,19 @@ HeaderASTConsumer::HeaderASTConsumer(
|
||||
exported_headers_(exported_headers) { }
|
||||
|
||||
void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
std::ofstream text_output(out_dump_name_);
|
||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||
clang::PrintingPolicy policy(ctx.getPrintingPolicy());
|
||||
policy.SuppressTagKeyword = true;
|
||||
ctx.setPrintingPolicy(policy);
|
||||
clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl();
|
||||
std::unique_ptr<clang::MangleContext> mangle_contextp(
|
||||
ctx.createMangleContext());
|
||||
abi_dump::TranslationUnit tu;
|
||||
std::string str_out;
|
||||
HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_, file_name_,
|
||||
exported_headers_, translation_unit);
|
||||
if (!v.TraverseDecl(translation_unit) ||
|
||||
!google::protobuf::TextFormat::Print(tu, &text_os)) {
|
||||
std::set<std::string> type_cache;
|
||||
std::unique_ptr<abi_util::IRDumper> ir_dumper =
|
||||
abi_util::IRDumper::CreateIRDumper("protobuf", out_dump_name_);
|
||||
HeaderASTVisitor v(mangle_contextp.get(), &ctx, cip_, file_name_,
|
||||
exported_headers_, translation_unit, &type_cache,
|
||||
ir_dumper.get());
|
||||
if (!v.TraverseDecl(translation_unit) || !ir_dumper->Dump()) {
|
||||
llvm::errs() << "Serialization to ostream failed\n";
|
||||
::exit(1);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <ir_representation.h>
|
||||
|
||||
#include <clang/AST/AST.h>
|
||||
#include <clang/AST/ASTConsumer.h>
|
||||
#include <clang/AST/Mangle.h>
|
||||
@@ -33,13 +35,14 @@
|
||||
class HeaderASTVisitor
|
||||
: public clang::RecursiveASTVisitor<HeaderASTVisitor> {
|
||||
public:
|
||||
HeaderASTVisitor(abi_dump::TranslationUnit *tu_ptr,
|
||||
clang::MangleContext *mangle_contextp,
|
||||
HeaderASTVisitor(clang::MangleContext *mangle_contextp,
|
||||
clang::ASTContext *ast_contextp,
|
||||
const clang::CompilerInstance *compiler_instance_p,
|
||||
const std::string ¤t_file_name,
|
||||
const std::set<std::string> &exported_headers,
|
||||
const clang::Decl *tu_decl);
|
||||
const clang::Decl *tu_decl,
|
||||
std::set<std::string> *type_cache,
|
||||
abi_util::IRDumper *ir_dumper);
|
||||
|
||||
bool VisitRecordDecl(const clang::RecordDecl *decl);
|
||||
|
||||
@@ -57,7 +60,6 @@ class HeaderASTVisitor
|
||||
}
|
||||
|
||||
private:
|
||||
abi_dump::TranslationUnit *tu_ptr_;
|
||||
clang::MangleContext *mangle_contextp_;
|
||||
clang::ASTContext *ast_contextp_;
|
||||
const clang::CompilerInstance *cip_;
|
||||
@@ -65,6 +67,11 @@ class HeaderASTVisitor
|
||||
const std::set<std::string> &exported_headers_;
|
||||
// To optimize recursion into only exported abi.
|
||||
const clang::Decl *tu_decl_;
|
||||
std::set<std::string> *type_cache_;
|
||||
abi_util::IRDumper *ir_dumper_;
|
||||
// We cache the source file an AST node corresponds to, to avoid repeated
|
||||
// calls to "realpath".
|
||||
std::map<const clang::Decl *, std::string> decl_to_source_file_cache_;
|
||||
};
|
||||
|
||||
class HeaderASTConsumer : public clang::ASTConsumer {
|
||||
@@ -83,4 +90,4 @@ class HeaderASTConsumer : public clang::ASTConsumer {
|
||||
std::set<std::string> exported_headers_;
|
||||
};
|
||||
|
||||
#endif // AST_PROCESSING_H_
|
||||
#endif // AST_PROCESSING_H_
|
||||
|
||||
@@ -65,8 +65,14 @@ static llvm::cl::opt<bool> no_filter(
|
||||
"no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
|
||||
llvm::cl::cat(header_linker_category));
|
||||
|
||||
static llvm::cl::opt<bool> use_version_script(
|
||||
"use-version-script", llvm::cl::desc("Use version script instead of .so"
|
||||
" file to filter out function"
|
||||
" and object symbols if available"),
|
||||
llvm::cl::Optional, llvm::cl::cat(header_linker_category));
|
||||
|
||||
static llvm::cl::opt<std::string> so_file(
|
||||
"so", llvm::cl::desc("<path to so file>"), llvm::cl::Optional,
|
||||
"so", llvm::cl::desc("<path to so file>"), llvm::cl::Required,
|
||||
llvm::cl::cat(header_linker_category));
|
||||
|
||||
class HeaderAbiLinker {
|
||||
@@ -85,16 +91,20 @@ class HeaderAbiLinker {
|
||||
|
||||
bool LinkAndDump();
|
||||
|
||||
template <typename T>
|
||||
static std::string GetLinkageName(T &element) {
|
||||
return element.type_info().linker_set_key();
|
||||
}
|
||||
template <typename T>
|
||||
static std::string GetSourceFile(T &element) {
|
||||
return element.type_info().source_file();
|
||||
}
|
||||
private:
|
||||
bool LinkRecords(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu);
|
||||
|
||||
bool LinkTypes(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu);
|
||||
bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu);
|
||||
|
||||
bool LinkEnums(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu);
|
||||
|
||||
bool LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu);
|
||||
|
||||
@@ -110,6 +120,8 @@ class HeaderAbiLinker {
|
||||
|
||||
bool ParseSoFile();
|
||||
|
||||
bool AddElfSymbols(abi_dump::TranslationUnit *linked_tu);
|
||||
|
||||
private:
|
||||
const std::vector<std::string> &dump_files_;
|
||||
const std::vector<std::string> &exported_header_dirs_;
|
||||
@@ -120,9 +132,8 @@ class HeaderAbiLinker {
|
||||
const std::string &api_;
|
||||
// TODO: Add to a map of std::sets instead.
|
||||
std::set<std::string> exported_headers_;
|
||||
std::set<std::string> record_decl_set_;
|
||||
std::set<std::string> types_set_;
|
||||
std::set<std::string> function_decl_set_;
|
||||
std::set<std::string> enum_decl_set_;
|
||||
std::set<std::string> globvar_decl_set_;
|
||||
// Version Script Regex Matching.
|
||||
std::set<std::string> functions_regex_matched_set;
|
||||
@@ -132,12 +143,32 @@ class HeaderAbiLinker {
|
||||
std::regex globvars_vs_regex_;
|
||||
};
|
||||
|
||||
template <typename T, typename Iterable>
|
||||
static bool AddElfSymbols(google::protobuf::RepeatedPtrField<T> *dst,
|
||||
Iterable symbols) {
|
||||
for (auto &&symbol : symbols) {
|
||||
auto *added_symbol = dst->Add();
|
||||
if (added_symbol == nullptr) {
|
||||
return false;
|
||||
}
|
||||
added_symbol->set_name(symbol);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// To be called right after parsing the .so file / version script.
|
||||
bool HeaderAbiLinker::AddElfSymbols(abi_dump::TranslationUnit *linked_tu) {
|
||||
|
||||
return ::AddElfSymbols(linked_tu->mutable_elf_functions(), function_decl_set_)
|
||||
&& ::AddElfSymbols(linked_tu->mutable_elf_objects(), globvar_decl_set_);
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkAndDump() {
|
||||
abi_dump::TranslationUnit linked_tu;
|
||||
std::ofstream text_output(out_dump_name_);
|
||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||
// If a version script is available, we use that as a filter.
|
||||
if (version_script.empty()) {
|
||||
// If the user specifies that a version script should be used, use that.
|
||||
if (!use_version_script) {
|
||||
exported_headers_ =
|
||||
abi_util::CollectAllExportedHeaders(exported_header_dirs_);
|
||||
if (!ParseSoFile()) {
|
||||
@@ -149,20 +180,20 @@ bool HeaderAbiLinker::LinkAndDump() {
|
||||
return false;
|
||||
}
|
||||
|
||||
AddElfSymbols(&linked_tu);
|
||||
|
||||
for (auto &&i : dump_files_) {
|
||||
abi_dump::TranslationUnit dump_tu;
|
||||
std::ifstream input(i);
|
||||
google::protobuf::io::IstreamInputStream text_is(&input);
|
||||
if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) ||
|
||||
!LinkRecords(dump_tu, &linked_tu) ||
|
||||
!LinkTypes(dump_tu, &linked_tu) ||
|
||||
!LinkFunctions(dump_tu, &linked_tu) ||
|
||||
!LinkEnums(dump_tu, &linked_tu) ||
|
||||
!LinkGlobalVars(dump_tu, &linked_tu)) {
|
||||
llvm::errs() << "Failed to link elements\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) {
|
||||
llvm::errs() << "Serialization to ostream failed\n";
|
||||
return false;
|
||||
@@ -170,22 +201,6 @@ bool HeaderAbiLinker::LinkAndDump() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string GetSymbol(const abi_dump::RecordDecl &element) {
|
||||
return element.mangled_record_name();
|
||||
}
|
||||
|
||||
static std::string GetSymbol(const abi_dump::FunctionDecl &element) {
|
||||
return element.mangled_function_name();
|
||||
}
|
||||
|
||||
static std::string GetSymbol(const abi_dump::EnumDecl &element) {
|
||||
return element.basic_abi().linker_set_key();
|
||||
}
|
||||
|
||||
static std::string GetSymbol(const abi_dump::GlobalVarDecl &element) {
|
||||
return element.basic_abi().linker_set_key();
|
||||
}
|
||||
|
||||
static bool QueryRegexMatches(std::set<std::string> *regex_matched_link_set,
|
||||
const std::regex *vs_regex,
|
||||
const std::string &symbol) {
|
||||
@@ -219,10 +234,10 @@ static std::regex CreateRegexMatchExprFromSet(
|
||||
return std::regex(all_regex_match_str);
|
||||
}
|
||||
|
||||
//TODO: make linking decls multi-threaded b/63590537.
|
||||
template <typename T>
|
||||
inline bool HeaderAbiLinker::LinkDecl(
|
||||
google::protobuf::RepeatedPtrField<T> *dst,
|
||||
std::set<std::string> *link_set,
|
||||
google::protobuf::RepeatedPtrField<T> *dst, std::set<std::string> *link_set,
|
||||
std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
|
||||
const google::protobuf::RepeatedPtrField<T> &src, bool use_version_script) {
|
||||
assert(dst != nullptr);
|
||||
@@ -230,18 +245,20 @@ inline bool HeaderAbiLinker::LinkDecl(
|
||||
for (auto &&element : src) {
|
||||
// If we are not using a version script and exported headers are available,
|
||||
// filter out unexported abi.
|
||||
if (!exported_headers_.empty() &&
|
||||
exported_headers_.find(element.source_file()) ==
|
||||
std::string source_file = GetSourceFile(element);
|
||||
// Builtin types will not have source file information.
|
||||
if (!exported_headers_.empty() && !source_file.empty() &&
|
||||
exported_headers_.find(source_file) ==
|
||||
exported_headers_.end()) {
|
||||
continue;
|
||||
}
|
||||
std::string element_str = GetLinkageName(element);
|
||||
// Check for the existence of the element in linked dump / symbol file.
|
||||
if (!use_version_script) {
|
||||
if (!link_set->insert(element.basic_abi().linker_set_key()).second) {
|
||||
if (!link_set->insert(element_str).second) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::string element_str = GetSymbol(element);
|
||||
std::set<std::string>::iterator it =
|
||||
link_set->find(element_str);
|
||||
if (it == link_set->end()) {
|
||||
@@ -263,13 +280,52 @@ inline bool HeaderAbiLinker::LinkDecl(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkRecords(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu) {
|
||||
|
||||
template<>
|
||||
std::string HeaderAbiLinker::GetLinkageName<const abi_dump::FunctionDecl> (
|
||||
const abi_dump::FunctionDecl &element) {
|
||||
return element.linker_set_key();
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string HeaderAbiLinker::GetSourceFile<const abi_dump::FunctionDecl> (
|
||||
const abi_dump::FunctionDecl &element) {
|
||||
return element.source_file();
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string HeaderAbiLinker::GetLinkageName<const abi_dump::GlobalVarDecl> (
|
||||
const abi_dump::GlobalVarDecl &element) {
|
||||
return element.linker_set_key();
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string HeaderAbiLinker::GetSourceFile<const abi_dump::GlobalVarDecl> (
|
||||
const abi_dump::GlobalVarDecl &element) {
|
||||
return element.source_file();
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkTypes(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu) {
|
||||
assert(linked_tu != nullptr);
|
||||
// Even if version scripts are available we take in records, since the symbols
|
||||
// in the version script might reference a record exposed by the library.
|
||||
return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, nullptr,
|
||||
nullptr, dump_tu.records(), false);
|
||||
// Even if version scripts are available we take in types, since the symbols
|
||||
// in the version script might reference a type exposed by the library.
|
||||
return LinkDecl(linked_tu->mutable_record_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.record_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_enum_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.enum_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_builtin_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.builtin_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_pointer_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.pointer_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_rvalue_reference_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.rvalue_reference_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_lvalue_reference_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.lvalue_reference_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_array_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.array_types(), false) &&
|
||||
LinkDecl(linked_tu->mutable_qualified_types(), &types_set_, nullptr,
|
||||
nullptr, dump_tu.qualified_types(), false);
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
|
||||
@@ -281,15 +337,6 @@ bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
|
||||
(!version_script_.empty() || !so_file_.empty()));
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkEnums(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu) {
|
||||
assert(linked_tu != nullptr);
|
||||
// Even if version scripts are available we take in records, since the symbols
|
||||
// in the version script might reference an enum exposed by the library.
|
||||
return LinkDecl(linked_tu->mutable_enums(), &enum_decl_set_, nullptr,
|
||||
nullptr, dump_tu.enums(), false);
|
||||
}
|
||||
|
||||
bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
|
||||
abi_dump::TranslationUnit *linked_tu) {
|
||||
assert(linked_tu != nullptr);
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <llvm/Support/Endian.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
@@ -128,4 +129,60 @@ class ELFSoFileParser : public SoFileParser {
|
||||
bool IsSymbolExported(const Elf_Sym *elf_sym) const;
|
||||
};
|
||||
|
||||
template <typename T, typename K>
|
||||
std::vector<T> FindRemovedElements(
|
||||
const std::map<K, T> &old_elements_map,
|
||||
const std::map<K, T> &new_elements_map) {
|
||||
std::vector<T> removed_elements;
|
||||
for (auto &&map_element : old_elements_map) {
|
||||
auto element_key = map_element.first;
|
||||
auto new_element = new_elements_map.find(element_key);
|
||||
if (new_element == new_elements_map.end()) {
|
||||
removed_elements.emplace_back(map_element.second);
|
||||
}
|
||||
}
|
||||
return removed_elements;
|
||||
}
|
||||
|
||||
template <typename T, typename F, typename K, typename Iterable>
|
||||
inline void AddToMap(std::map<K, T> *dst, Iterable &src, F get_key) {
|
||||
for (auto &&element : src) {
|
||||
dst->insert(std::make_pair(get_key(&element), &element));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename K, typename Iterable>
|
||||
inline void AddToSet(std::set<K> *dst, Iterable &src, F get_key) {
|
||||
for (auto &&element : src) {
|
||||
dst->insert(get_key(element));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename K, typename T>
|
||||
std::vector<std::pair<T, T>> FindCommonElements(
|
||||
const std::map<K, T> &old_elements_map,
|
||||
const std::map<K, T> &new_elements_map) {
|
||||
std::vector<std::pair<T, T>> common_elements;
|
||||
typename std::map<K, T>::const_iterator old_element =
|
||||
old_elements_map.begin();
|
||||
typename std::map<K, T>::const_iterator new_element =
|
||||
new_elements_map.begin();
|
||||
while (old_element != old_elements_map.end() &&
|
||||
new_element != new_elements_map.end()) {
|
||||
if (old_element->first == new_element->first) {
|
||||
common_elements.emplace_back(std::make_pair(
|
||||
old_element->second, new_element->second));
|
||||
old_element++;
|
||||
new_element++;
|
||||
continue;
|
||||
}
|
||||
if (old_element->first < new_element->first) {
|
||||
old_element++;
|
||||
} else {
|
||||
new_element++;
|
||||
}
|
||||
}
|
||||
return common_elements;
|
||||
}
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,418 @@
|
||||
// Copyright (C) 2017 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.
|
||||
#ifndef IR_PROTOBUF_
|
||||
#define IR_PROTOBUF_
|
||||
|
||||
#include <ir_representation.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#include "proto/abi_diff.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
|
||||
// Classes which act as middle-men between clang AST parsing routines and
|
||||
// message format specific dumpers.
|
||||
namespace abi_util {
|
||||
|
||||
inline abi_diff::CompatibilityStatus CompatibilityStatusIRToProtobuf(
|
||||
CompatibilityStatusIR status) {
|
||||
switch(status) {
|
||||
case CompatibilityStatusIR::Incompatible:
|
||||
return abi_diff::CompatibilityStatus::INCOMPATIBLE;
|
||||
case CompatibilityStatusIR::Extension:
|
||||
return abi_diff::CompatibilityStatus::EXTENSION;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return abi_diff::CompatibilityStatus::COMPATIBLE;
|
||||
}
|
||||
|
||||
inline abi_dump::AccessSpecifier AccessIRToProtobuf(AccessSpecifierIR access) {
|
||||
switch (access) {
|
||||
case AccessSpecifierIR::ProtectedAccess:
|
||||
return abi_dump::AccessSpecifier::protected_access;
|
||||
case AccessSpecifierIR::PrivateAccess:
|
||||
return abi_dump::AccessSpecifier::private_access;
|
||||
default:
|
||||
return abi_dump::AccessSpecifier::public_access;
|
||||
}
|
||||
return abi_dump::AccessSpecifier::public_access;
|
||||
}
|
||||
|
||||
inline AccessSpecifierIR AccessProtobufToIR(
|
||||
abi_dump::AccessSpecifier access) {
|
||||
switch (access) {
|
||||
case abi_dump::AccessSpecifier::protected_access:
|
||||
return AccessSpecifierIR::ProtectedAccess;
|
||||
case abi_dump::AccessSpecifier::private_access:
|
||||
return AccessSpecifierIR::PrivateAccess;
|
||||
default:
|
||||
return AccessSpecifierIR::PublicAccess;
|
||||
}
|
||||
return AccessSpecifierIR::PublicAccess;
|
||||
}
|
||||
|
||||
inline abi_dump::RecordKind RecordKindIRToProtobuf(
|
||||
RecordTypeIR::RecordKind kind) {
|
||||
switch (kind) {
|
||||
case RecordTypeIR::RecordKind::struct_kind:
|
||||
return abi_dump::RecordKind::struct_kind;
|
||||
|
||||
case RecordTypeIR::RecordKind::class_kind:
|
||||
return abi_dump::RecordKind::class_kind;
|
||||
|
||||
case RecordTypeIR::RecordKind::union_kind:
|
||||
return abi_dump::RecordKind::union_kind;
|
||||
|
||||
default:
|
||||
return abi_dump::RecordKind::struct_kind;
|
||||
}
|
||||
// Should not be reached
|
||||
assert(false);
|
||||
}
|
||||
|
||||
inline abi_dump::VTableComponent::Kind VTableComponentKindIRToProtobuf(
|
||||
VTableComponentIR::Kind kind) {
|
||||
switch (kind) {
|
||||
case VTableComponentIR::Kind::VCallOffset:
|
||||
return abi_dump::VTableComponent_Kind_VCallOffset;
|
||||
|
||||
case VTableComponentIR::Kind::VBaseOffset:
|
||||
return abi_dump::VTableComponent_Kind_VBaseOffset;
|
||||
|
||||
case VTableComponentIR::Kind::OffsetToTop:
|
||||
return abi_dump::VTableComponent_Kind_OffsetToTop;
|
||||
|
||||
case VTableComponentIR::Kind::RTTI:
|
||||
return abi_dump::VTableComponent_Kind_RTTI;
|
||||
|
||||
case VTableComponentIR::Kind::FunctionPointer:
|
||||
return abi_dump::VTableComponent_Kind_FunctionPointer;
|
||||
|
||||
case VTableComponentIR::Kind::CompleteDtorPointer:
|
||||
return abi_dump::VTableComponent_Kind_CompleteDtorPointer;
|
||||
|
||||
case VTableComponentIR::Kind::DeletingDtorPointer:
|
||||
return abi_dump::VTableComponent_Kind_DeletingDtorPointer;
|
||||
|
||||
default:
|
||||
return abi_dump::VTableComponent_Kind_UnusedFunctionPointer;
|
||||
}
|
||||
// Should not be reached
|
||||
assert(false);
|
||||
}
|
||||
|
||||
inline VTableComponentIR::Kind VTableComponentKindProtobufToIR(
|
||||
abi_dump::VTableComponent_Kind kind) {
|
||||
switch (kind) {
|
||||
case abi_dump::VTableComponent_Kind_VCallOffset:
|
||||
return VTableComponentIR::Kind::VCallOffset;
|
||||
|
||||
case abi_dump::VTableComponent_Kind_VBaseOffset:
|
||||
return VTableComponentIR::Kind::VBaseOffset;
|
||||
|
||||
case abi_dump::VTableComponent_Kind_OffsetToTop:
|
||||
return VTableComponentIR::Kind::OffsetToTop;
|
||||
|
||||
case abi_dump::VTableComponent_Kind_RTTI:
|
||||
return VTableComponentIR::Kind::RTTI;
|
||||
|
||||
case abi_dump::VTableComponent_Kind_FunctionPointer:
|
||||
return VTableComponentIR::Kind::FunctionPointer;
|
||||
|
||||
case abi_dump::VTableComponent_Kind_CompleteDtorPointer:
|
||||
return VTableComponentIR::Kind::CompleteDtorPointer;
|
||||
|
||||
case abi_dump::VTableComponent_Kind_DeletingDtorPointer:
|
||||
return VTableComponentIR::Kind::DeletingDtorPointer;
|
||||
|
||||
default:
|
||||
return VTableComponentIR::Kind::UnusedFunctionPointer;
|
||||
}
|
||||
// Should not be reached
|
||||
assert(false);
|
||||
}
|
||||
|
||||
class IRToProtobufConverter {
|
||||
private:
|
||||
static bool AddTemplateInformation(
|
||||
abi_dump::TemplateInfo *ti, const abi_util::TemplatedArtifactIR *ta);
|
||||
|
||||
static bool AddTypeInfo(
|
||||
abi_dump::BasicNamedAndTypedDecl *type_info, const TypeIR *typep);
|
||||
|
||||
static bool AddRecordFields(
|
||||
abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir);
|
||||
|
||||
static bool AddBaseSpecifiers(
|
||||
abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir);
|
||||
|
||||
static bool AddVTableLayout(
|
||||
abi_dump::RecordType *record_protobuf, const RecordTypeIR *record_ir);
|
||||
|
||||
static bool AddEnumFields(abi_dump::EnumType *enum_protobuf,
|
||||
const EnumTypeIR *enum_ir);
|
||||
public:
|
||||
static abi_dump::EnumType ConvertEnumTypeIR(const EnumTypeIR *enump);
|
||||
|
||||
static abi_dump::RecordType ConvertRecordTypeIR(const RecordTypeIR *recordp);
|
||||
|
||||
static bool AddFunctionParameters(abi_dump::FunctionDecl *function_protobuf,
|
||||
const FunctionIR *function_ir);
|
||||
|
||||
static abi_dump::FunctionDecl ConvertFunctionIR(const FunctionIR *functionp);
|
||||
|
||||
static abi_dump::GlobalVarDecl ConvertGlobalVarIR(
|
||||
const GlobalVarIR *global_varp);
|
||||
|
||||
static abi_dump::PointerType ConvertPointerTypeIR(
|
||||
const PointerTypeIR *pointerp);
|
||||
|
||||
static abi_dump::QualifiedType ConvertQualifiedTypeIR(
|
||||
const QualifiedTypeIR *qualtypep);
|
||||
|
||||
static abi_dump::BuiltinType ConvertBuiltinTypeIR(
|
||||
const BuiltinTypeIR *builtin_typep);
|
||||
|
||||
static abi_dump::ArrayType ConvertArrayTypeIR(
|
||||
const ArrayTypeIR *array_typep);
|
||||
|
||||
static abi_dump::LvalueReferenceType ConvertLvalueReferenceTypeIR(
|
||||
const LvalueReferenceTypeIR *lvalue_reference_typep);
|
||||
|
||||
static abi_dump::RvalueReferenceType ConvertRvalueReferenceTypeIR(
|
||||
const RvalueReferenceTypeIR *rvalue_reference_typep);
|
||||
|
||||
static abi_dump::ElfFunction ConvertElfFunctionIR(
|
||||
const ElfFunctionIR *elf_function_ir);
|
||||
|
||||
static abi_dump::ElfObject ConvertElfObjectIR(
|
||||
const ElfObjectIR *elf_object_ir);
|
||||
};
|
||||
|
||||
class IRDiffToProtobufConverter {
|
||||
private:
|
||||
static bool AddTypeInfoDiff(abi_diff::TypeInfoDiff *type_info_diff_protobuf,
|
||||
const TypeDiffIR *type_diff_ir);
|
||||
|
||||
static bool AddVTableLayoutDiff(
|
||||
abi_diff::VTableLayoutDiff *vtable_layout_diff_protobuf,
|
||||
const VTableLayoutDiffIR *vtable_layout_diff_ir);
|
||||
|
||||
static bool AddBaseSpecifierDiffs(
|
||||
abi_diff::CXXBaseSpecifierDiff *base_specifier_diff_protobuf,
|
||||
const CXXBaseSpecifierDiffIR *base_specifier_diff_ir);
|
||||
|
||||
static bool AddRecordFieldsRemoved(
|
||||
abi_diff::RecordTypeDiff *record_diff_protobuf,
|
||||
const std::vector<const RecordFieldIR *> &record_fields_removed_ir);
|
||||
|
||||
static bool AddRecordFieldDiffs(
|
||||
abi_diff::RecordTypeDiff *record_diff_protobuf,
|
||||
const std::vector<RecordFieldDiffIR> &record_field_diff_ir);
|
||||
|
||||
static bool AddEnumUnderlyingTypeDiff(
|
||||
abi_diff::UnderlyingTypeDiff *underlying_type_diff_protobuf,
|
||||
const std::pair<std::string, std::string> *underlying_type_diff_ir);
|
||||
|
||||
public:
|
||||
static abi_diff::RecordTypeDiff ConvertRecordTypeDiffIR(
|
||||
const RecordTypeDiffIR *record_type_diffp);
|
||||
|
||||
static abi_diff::EnumTypeDiff ConvertEnumTypeDiffIR(
|
||||
const EnumTypeDiffIR *enum_type_diffp);
|
||||
|
||||
static abi_diff::FunctionDeclDiff ConvertFunctionDiffIR(
|
||||
const FunctionDiffIR *function_diffp);
|
||||
|
||||
static abi_diff::GlobalVarDeclDiff ConvertGlobalVarDiffIR(
|
||||
const GlobalVarDiffIR *global_var_diffp);
|
||||
};
|
||||
|
||||
class ProtobufIRDumper : public IRDumper, public IRToProtobufConverter {
|
||||
private:
|
||||
// Types
|
||||
bool AddRecordTypeIR(const RecordTypeIR *);
|
||||
|
||||
bool AddEnumTypeIR(const EnumTypeIR *);
|
||||
|
||||
bool AddPointerTypeIR(const PointerTypeIR *);
|
||||
|
||||
bool AddQualifiedTypeIR(const QualifiedTypeIR *);
|
||||
|
||||
bool AddLvalueReferenceTypeIR(const LvalueReferenceTypeIR *);
|
||||
|
||||
bool AddRvalueReferenceTypeIR(const RvalueReferenceTypeIR *);
|
||||
|
||||
bool AddArrayTypeIR(const ArrayTypeIR *);
|
||||
|
||||
bool AddBuiltinTypeIR(const BuiltinTypeIR *);
|
||||
|
||||
// Functions and global variables.
|
||||
bool AddFunctionIR(const FunctionIR *);
|
||||
|
||||
bool AddGlobalVarIR(const GlobalVarIR *);
|
||||
|
||||
public:
|
||||
ProtobufIRDumper(const std::string &dump_path)
|
||||
: IRDumper(dump_path), tu_ptr_(new abi_dump::TranslationUnit()) { }
|
||||
|
||||
bool AddLinkableMessageIR(const LinkableMessageIR *);
|
||||
|
||||
bool Dump() override;
|
||||
|
||||
~ProtobufIRDumper() override { }
|
||||
|
||||
private:
|
||||
std::unique_ptr<abi_dump::TranslationUnit> tu_ptr_;
|
||||
};
|
||||
|
||||
|
||||
class ProtobufTextFormatToIRReader : public TextFormatToIRReader {
|
||||
public:
|
||||
|
||||
virtual bool ReadDump() override;
|
||||
|
||||
ProtobufTextFormatToIRReader(const std::string &dump_path)
|
||||
: TextFormatToIRReader(dump_path) { }
|
||||
|
||||
private:
|
||||
std::vector<FunctionIR> ReadFunctions(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<GlobalVarIR> ReadGlobalVariables(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<EnumTypeIR> ReadEnumTypes(const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<RecordTypeIR> ReadRecordTypes(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<PointerTypeIR> ReadPointerTypes(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<BuiltinTypeIR> ReadBuiltinTypes(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<QualifiedTypeIR> ReadQualifiedTypes(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<ArrayTypeIR> ReadArrayTypes(const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<LvalueReferenceTypeIR> ReadLvalueReferenceTypes(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<RvalueReferenceTypeIR> ReadRvalueReferenceTypes(
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<ElfFunctionIR> ReadElfFunctions (
|
||||
const abi_dump::TranslationUnit &tu);
|
||||
|
||||
std::vector<ElfObjectIR> ReadElfObjects (const abi_dump::TranslationUnit &tu);
|
||||
|
||||
void ReadTypeInfo(const abi_dump::BasicNamedAndTypedDecl &type_info,
|
||||
TypeIR *typep);
|
||||
|
||||
FunctionIR FunctionProtobufToIR(const abi_dump::FunctionDecl &);
|
||||
|
||||
RecordTypeIR RecordTypeProtobufToIR(
|
||||
const abi_dump::RecordType &record_type_protobuf);
|
||||
|
||||
std::vector<RecordFieldIR> RecordFieldsProtobufToIR(
|
||||
const google::protobuf::RepeatedPtrField<abi_dump::RecordFieldDecl> &rfp);
|
||||
|
||||
std::vector<CXXBaseSpecifierIR> RecordCXXBaseSpecifiersProtobufToIR(
|
||||
const google::protobuf::RepeatedPtrField<abi_dump::CXXBaseSpecifier> &rbs);
|
||||
|
||||
std::vector<EnumFieldIR> EnumFieldsProtobufToIR(
|
||||
const google::protobuf::RepeatedPtrField<abi_dump::EnumFieldDecl> &efp);
|
||||
|
||||
EnumTypeIR EnumTypeProtobufToIR(
|
||||
const abi_dump::EnumType &enum_type_protobuf);
|
||||
|
||||
VTableLayoutIR VTableLayoutProtobufToIR(
|
||||
const abi_dump::VTableLayout &vtable_layout_protobuf);
|
||||
|
||||
TemplateInfoIR TemplateInfoProtobufToIR(
|
||||
const abi_dump::TemplateInfo &template_info_protobuf);
|
||||
};
|
||||
|
||||
class ProtobufIRDiffDumper : public IRDiffDumper {
|
||||
public:
|
||||
ProtobufIRDiffDumper(const std::string &dump_path)
|
||||
: IRDiffDumper(dump_path),
|
||||
diff_tu_(new abi_diff::TranslationUnitDiff()) { }
|
||||
|
||||
bool AddDiffMessageIR(const DiffMessageIR *, const std::string &type_stack,
|
||||
DiffKind diff_kind) override;
|
||||
|
||||
bool AddLinkableMessageIR(const LinkableMessageIR *,
|
||||
DiffKind diff_kind) override;
|
||||
|
||||
bool AddElfSymbolMessageIR(const ElfSymbolIR *, DiffKind diff_kind) override;
|
||||
|
||||
void AddLibNameIR(const std::string &name) override;
|
||||
|
||||
void AddArchIR(const std::string &arch) override;
|
||||
|
||||
void AddCompatibilityStatusIR(CompatibilityStatusIR status) override;
|
||||
|
||||
bool Dump() override;
|
||||
|
||||
CompatibilityStatusIR GetCompatibilityStatusIR() override;
|
||||
|
||||
~ProtobufIRDiffDumper() override { }
|
||||
|
||||
private:
|
||||
// User defined types.
|
||||
bool AddRecordTypeDiffIR(const RecordTypeDiffIR *,
|
||||
const std::string &type_stack, DiffKind diff_kind);
|
||||
|
||||
bool AddEnumTypeDiffIR(const EnumTypeDiffIR *,
|
||||
const std::string &type_stack, DiffKind diff_kind);
|
||||
|
||||
// Functions and global variables.
|
||||
bool AddFunctionDiffIR(const FunctionDiffIR *,
|
||||
const std::string &type_stack, DiffKind diff_kind);
|
||||
|
||||
bool AddGlobalVarDiffIR(const GlobalVarDiffIR *,
|
||||
const std::string &type_stack, DiffKind diff_kind);
|
||||
|
||||
bool AddLoneRecordTypeDiffIR(const RecordTypeIR *, DiffKind diff_kind);
|
||||
|
||||
bool AddLoneEnumTypeDiffIR(const EnumTypeIR *, DiffKind diff_kind);
|
||||
|
||||
// Functions and global variables.
|
||||
bool AddLoneFunctionDiffIR(const FunctionIR *, DiffKind diff_kind);
|
||||
|
||||
bool AddLoneGlobalVarDiffIR(const GlobalVarIR *, DiffKind diff_kind);
|
||||
|
||||
bool AddElfObjectIR(const ElfObjectIR *elf_object_ir, DiffKind diff_kind);
|
||||
|
||||
bool AddElfFunctionIR(const ElfFunctionIR *elf_function_ir,
|
||||
DiffKind diff_kind);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<abi_diff::TranslationUnitDiff> diff_tu_;
|
||||
};
|
||||
|
||||
} // abi_util
|
||||
|
||||
#endif // IR_PROTOBUF_
|
||||
@@ -0,0 +1,67 @@
|
||||
// Copyright (C) 2017 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 <ir_representation.h>
|
||||
#include <ir_representation_protobuf.h>
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||
#include "proto/abi_dump.pb.h"
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace abi_util {
|
||||
|
||||
std::unique_ptr<IRDumper> IRDumper::CreateIRDumper(
|
||||
const std::string &type, const std::string &dump_path) {
|
||||
if (type == "protobuf") {
|
||||
return std::make_unique<ProtobufIRDumper>(dump_path);
|
||||
}
|
||||
// Nothing else is supported yet.
|
||||
llvm::errs() << type << " message format is not supported yet!\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IRDiffDumper> IRDiffDumper::CreateIRDiffDumper(
|
||||
const std::string &type, const std::string &dump_path) {
|
||||
if (type == "protobuf") {
|
||||
return std::make_unique<ProtobufIRDiffDumper>(dump_path);
|
||||
}
|
||||
// Nothing else is supported yet.
|
||||
llvm::errs() << type << " message format is not supported yet!\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<TextFormatToIRReader>
|
||||
TextFormatToIRReader::CreateTextFormatToIRReader(
|
||||
const std::string &type, const std::string &dump_path) {
|
||||
if (type == "protobuf") {
|
||||
return std::make_unique<ProtobufTextFormatToIRReader>(dump_path);
|
||||
}
|
||||
// Nothing else is supported yet.
|
||||
llvm::errs() << type << " message format is not supported yet!\n";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace abi_util
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,97 +4,112 @@ import "development/vndk/tools/header-checker/proto/abi_dump.proto";
|
||||
|
||||
package abi_diff;
|
||||
|
||||
message RecordFieldDeclDiff {
|
||||
optional abi_dump.RecordFieldDecl old = 1;
|
||||
optional abi_dump.RecordFieldDecl new = 2;
|
||||
optional uint32 index = 3;
|
||||
message TypeInfo {
|
||||
optional uint64 size = 1;
|
||||
optional uint32 alignment = 2;
|
||||
}
|
||||
|
||||
message EnumFieldDeclDiff {
|
||||
optional abi_dump.EnumFieldDecl old = 1;
|
||||
optional abi_dump.EnumFieldDecl new = 2;
|
||||
optional uint32 index = 3;
|
||||
message TypeInfoDiff {
|
||||
optional TypeInfo old_type_info = 1;
|
||||
optional TypeInfo new_type_info = 2;
|
||||
}
|
||||
|
||||
message VTableLayoutDiff {
|
||||
optional abi_dump.VTableLayout old_vtable = 1;
|
||||
optional abi_dump.VTableLayout new_vtable = 2;
|
||||
}
|
||||
|
||||
message RecordFieldDeclDiff {
|
||||
optional abi_dump.RecordFieldDecl old_field = 1;
|
||||
optional abi_dump.RecordFieldDecl new_field = 2;
|
||||
}
|
||||
|
||||
message CXXBaseSpecifierDiff {
|
||||
optional abi_dump.CXXBaseSpecifier old = 1;
|
||||
optional abi_dump.CXXBaseSpecifier new = 2;
|
||||
optional uint32 index = 3;
|
||||
repeated abi_dump.CXXBaseSpecifier old_bases = 1;
|
||||
repeated abi_dump.CXXBaseSpecifier new_bases = 2;
|
||||
}
|
||||
|
||||
message CXXVTableDiff {
|
||||
optional abi_dump.VTableComponent old = 1;
|
||||
optional abi_dump.VTableComponent new = 2;
|
||||
optional uint32 index = 3;
|
||||
message RecordTypeDiff {
|
||||
optional string name = 1;
|
||||
optional string type_stack = 2;
|
||||
optional TypeInfoDiff type_info_diff = 3;
|
||||
repeated abi_dump.RecordFieldDecl fields_removed = 4;
|
||||
repeated RecordFieldDeclDiff fields_diff = 5;
|
||||
optional CXXBaseSpecifierDiff bases_diff = 6;
|
||||
optional VTableLayoutDiff vtable_layout_diff = 7;
|
||||
}
|
||||
|
||||
message BasicNamedAndTypedDeclDiff {
|
||||
optional abi_dump.BasicNamedAndTypedDecl old = 1;
|
||||
optional abi_dump.BasicNamedAndTypedDecl new = 2;
|
||||
message UnderlyingTypeDiff {
|
||||
optional string old_type = 1;
|
||||
optional string new_type = 2;
|
||||
}
|
||||
|
||||
message RecordDeclDiff {
|
||||
repeated RecordFieldDeclDiff field_diffs = 1;
|
||||
repeated CXXBaseSpecifierDiff base_diffs = 2;
|
||||
repeated CXXVTableDiff vtable_diffs = 3;
|
||||
optional BasicNamedAndTypedDeclDiff type_diff = 4;
|
||||
optional string name = 5;
|
||||
message EnumFieldDeclDiff {
|
||||
optional abi_dump.EnumFieldDecl old_field = 1;
|
||||
optional abi_dump.EnumFieldDecl new_field = 2;
|
||||
}
|
||||
|
||||
message EnumDeclDiff {
|
||||
repeated EnumFieldDeclDiff field_diffs = 1;
|
||||
optional BasicNamedAndTypedDeclDiff type_diff = 2;
|
||||
optional string name = 3;
|
||||
}
|
||||
|
||||
message ReturnTypeDiff {
|
||||
optional abi_dump.BasicNamedAndTypedDecl old = 1;
|
||||
optional abi_dump.BasicNamedAndTypedDecl new = 2;
|
||||
}
|
||||
|
||||
message ParamDeclDiff {
|
||||
optional abi_dump.ParamDecl old = 1;
|
||||
optional abi_dump.ParamDecl new = 2;
|
||||
optional uint32 index = 3;
|
||||
message EnumTypeDiff {
|
||||
optional string type_stack = 1;
|
||||
optional string name = 2;
|
||||
repeated EnumFieldDeclDiff fields_diff = 3;
|
||||
optional UnderlyingTypeDiff underlying_type_diff = 4;
|
||||
repeated abi_dump.EnumFieldDecl fields_added = 5;
|
||||
repeated abi_dump.EnumFieldDecl fields_removed = 6;
|
||||
}
|
||||
|
||||
message FunctionDeclDiff {
|
||||
optional ReturnTypeDiff return_type_diffs = 1;
|
||||
repeated ParamDeclDiff param_diffs = 2;
|
||||
optional string name = 3;
|
||||
// Template diffs are not required since in C++, they will show up in mangled
|
||||
// names and in C, templates are not supported.
|
||||
optional string type_stack = 1;
|
||||
optional string name = 2;
|
||||
optional abi_dump.FunctionDecl old = 3;
|
||||
optional abi_dump.FunctionDecl new = 4;
|
||||
}
|
||||
|
||||
message GlobalVarDeclDiff {
|
||||
optional BasicNamedAndTypedDeclDiff type_diff = 1;
|
||||
}
|
||||
|
||||
enum CompatibilityStatus {
|
||||
COMPATIBLE = 0;
|
||||
EXTENSION = 1;
|
||||
INCOMPATIBLE = 4;
|
||||
optional string type_stack = 1;
|
||||
optional string name = 2;
|
||||
optional abi_dump.GlobalVarDecl old = 3; // Old global var
|
||||
optional abi_dump.GlobalVarDecl new = 4; // New global var
|
||||
}
|
||||
|
||||
message TranslationUnitDiff {
|
||||
// Library Name
|
||||
optional string lib_name = 1;
|
||||
optional string arch = 2;
|
||||
// Differing Elements.
|
||||
repeated RecordDeclDiff records_diff = 3;
|
||||
repeated EnumDeclDiff enums_diff = 4;
|
||||
repeated FunctionDeclDiff functions_diff = 5;
|
||||
repeated GlobalVarDeclDiff global_vars_diff = 6;
|
||||
// Removed Elements.
|
||||
repeated abi_dump.RecordDecl records_removed = 7;
|
||||
repeated abi_dump.FunctionDecl functions_removed = 8;
|
||||
repeated abi_dump.EnumDecl enums_removed = 9;
|
||||
repeated abi_dump.GlobalVarDecl global_vars_removed = 10;
|
||||
// Added Elements.
|
||||
repeated abi_dump.RecordDecl records_added = 11;
|
||||
repeated abi_dump.FunctionDecl functions_added = 12;
|
||||
repeated abi_dump.EnumDecl enums_added = 13;
|
||||
repeated abi_dump.GlobalVarDecl global_vars_added = 14;
|
||||
// Records.
|
||||
repeated RecordTypeDiff record_type_diffs = 3;
|
||||
repeated RecordTypeDiff unreferenced_record_type_diffs = 4;
|
||||
repeated abi_dump.RecordType unreferenced_record_types_removed = 5;
|
||||
repeated abi_dump.RecordType unreferenced_record_types_added = 6;
|
||||
|
||||
// Enums
|
||||
repeated EnumTypeDiff enum_type_diffs = 7;
|
||||
repeated EnumTypeDiff enum_type_extension_diffs = 8;
|
||||
repeated EnumTypeDiff unreferenced_enum_type_diffs = 9;
|
||||
repeated EnumTypeDiff unreferenced_enum_type_extension_diffs = 10;
|
||||
repeated abi_dump.EnumType unreferenced_enum_types_removed = 11;
|
||||
repeated abi_dump.EnumType unreferenced_enum_types_added = 12;
|
||||
|
||||
// Functions and Global variables.
|
||||
repeated FunctionDeclDiff function_diffs = 13;
|
||||
repeated GlobalVarDeclDiff global_var_diffs = 14;
|
||||
|
||||
repeated abi_dump.FunctionDecl functions_removed = 15;
|
||||
repeated abi_dump.GlobalVarDecl global_vars_removed = 16;
|
||||
|
||||
repeated abi_dump.FunctionDecl functions_added = 17;
|
||||
repeated abi_dump.GlobalVarDecl global_vars_added = 18;
|
||||
|
||||
repeated abi_dump.ElfFunction added_elf_functions = 19;
|
||||
repeated abi_dump.ElfFunction removed_elf_functions = 20;
|
||||
|
||||
repeated abi_dump.ElfObject added_elf_objects = 21;
|
||||
repeated abi_dump.ElfObject removed_elf_objects = 22;
|
||||
|
||||
// Compatiblity Status
|
||||
optional CompatibilityStatus compatibility_status = 15;
|
||||
optional CompatibilityStatus compatibility_status = 23;
|
||||
}
|
||||
|
||||
// Not merged with TranslationUnitDiff to allow future extensions.
|
||||
@@ -108,3 +123,9 @@ message ConciseDiffReportInformation {
|
||||
message MergedTranslationUnitDiff {
|
||||
repeated ConciseDiffReportInformation diff_reports = 1;
|
||||
}
|
||||
|
||||
enum CompatibilityStatus {
|
||||
COMPATIBLE = 0;
|
||||
EXTENSION = 1;
|
||||
INCOMPATIBLE = 4;
|
||||
}
|
||||
|
||||
@@ -2,50 +2,84 @@ syntax = "proto2";
|
||||
|
||||
package abi_dump;
|
||||
|
||||
message BasicTypeAbi {
|
||||
// The type's name. for eg : a record field's type.
|
||||
optional string name = 1;
|
||||
// Optional since templated types will not have this information.
|
||||
optional uint64 size = 2 [default = 0];
|
||||
optional uint32 alignment = 3 [default = 0];
|
||||
}
|
||||
|
||||
enum AccessSpecifier {
|
||||
public_access = 1;
|
||||
private_access = 2;
|
||||
protected_access = 3;
|
||||
}
|
||||
|
||||
enum RecordKind {
|
||||
struct_kind = 1;
|
||||
class_kind = 2;
|
||||
union_kind = 3;
|
||||
}
|
||||
|
||||
message BasicNamedAndTypedDecl {
|
||||
optional BasicTypeAbi type_abi = 1;
|
||||
// The TypedDecl's name.
|
||||
optional string name = 2;
|
||||
optional AccessSpecifier access = 3;
|
||||
optional string linker_set_key = 4;
|
||||
optional string name = 1;
|
||||
optional uint64 size = 2 [default = 0];
|
||||
optional uint32 alignment = 3 [default = 0];
|
||||
optional string referenced_type = 4;
|
||||
optional string source_file = 5;
|
||||
optional string linker_set_key = 6;
|
||||
}
|
||||
|
||||
message ArrayType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
}
|
||||
|
||||
message PointerType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
}
|
||||
|
||||
message QualifiedType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
optional bool is_const = 6;
|
||||
optional bool is_volatile = 7;
|
||||
optional bool is_restricted = 8;
|
||||
}
|
||||
|
||||
message BuiltinType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
optional bool is_unsigned = 2;
|
||||
optional bool is_integral = 3;
|
||||
}
|
||||
|
||||
message LvalueReferenceType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
}
|
||||
|
||||
message RvalueReferenceType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
}
|
||||
|
||||
message FunctionDecl {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
// Mangled name.
|
||||
optional string mangled_function_name = 2;
|
||||
// Return type reference
|
||||
optional string return_type = 1;
|
||||
optional string function_name = 2;
|
||||
optional string source_file = 3;
|
||||
repeated ParamDecl parameters = 4;
|
||||
optional TemplateInfo template_info = 5;
|
||||
optional string linker_set_key = 6;
|
||||
optional AccessSpecifier access = 7 [default = public_access];
|
||||
}
|
||||
|
||||
message ParamDecl {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
optional string referenced_type = 1;
|
||||
optional bool default_arg = 2;
|
||||
}
|
||||
|
||||
message RecordFieldDecl {
|
||||
// For future additions.
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
optional string referenced_type = 1;
|
||||
optional uint64 field_offset = 2;
|
||||
optional string field_name = 3;
|
||||
optional AccessSpecifier access = 4 [default = public_access];
|
||||
}
|
||||
|
||||
message EnumFieldDecl {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
optional int64 enum_field_value = 2; // assumption: fits int64
|
||||
optional int64 enum_field_value = 1; // assumption: fits int64
|
||||
optional string name = 3;
|
||||
}
|
||||
|
||||
message TemplateInfo {
|
||||
@@ -53,17 +87,13 @@ message TemplateInfo {
|
||||
}
|
||||
|
||||
message TemplateElement {
|
||||
optional BasicTemplateElementAbi basic_abi = 1;
|
||||
message BasicTemplateElementAbi {
|
||||
optional BasicTypeAbi type_abi = 1;
|
||||
optional string name = 2;
|
||||
optional string linker_set_key = 3;
|
||||
}
|
||||
optional string referenced_type = 1;
|
||||
}
|
||||
|
||||
message CXXBaseSpecifier {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
optional string referenced_type = 1;
|
||||
optional bool is_virtual = 2;
|
||||
optional AccessSpecifier access = 3;
|
||||
}
|
||||
|
||||
message VTableComponent {
|
||||
@@ -89,30 +119,51 @@ message VTableLayout {
|
||||
repeated VTableComponent vtable_components = 1;
|
||||
}
|
||||
|
||||
message RecordDecl {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
message RecordType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
repeated RecordFieldDecl fields = 2;
|
||||
repeated CXXBaseSpecifier base_specifiers = 3;
|
||||
optional string source_file = 4;
|
||||
optional TemplateInfo template_info = 5;
|
||||
optional string mangled_record_name = 6;
|
||||
optional VTableLayout vtable_layout = 7;
|
||||
optional AccessSpecifier access = 8 [default = public_access];
|
||||
optional bool is_anonymous = 9;
|
||||
optional RecordKind record_kind = 10 [default = struct_kind];
|
||||
}
|
||||
|
||||
message EnumDecl {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
repeated EnumFieldDecl enum_fields = 2;
|
||||
optional string source_file = 3;
|
||||
message EnumType {
|
||||
optional BasicNamedAndTypedDecl type_info = 1;
|
||||
optional string underlying_type = 2;
|
||||
repeated EnumFieldDecl enum_fields = 3;
|
||||
optional AccessSpecifier access = 4 [default = public_access];
|
||||
}
|
||||
|
||||
message GlobalVarDecl {
|
||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
||||
optional string name = 1;
|
||||
optional string source_file = 2;
|
||||
optional string linker_set_key = 3;
|
||||
optional string referenced_type = 4;
|
||||
optional AccessSpecifier access = 5 [default = public_access];
|
||||
}
|
||||
|
||||
message ElfFunction {
|
||||
optional string name = 1;
|
||||
}
|
||||
|
||||
message ElfObject {
|
||||
optional string name = 1;
|
||||
}
|
||||
|
||||
message TranslationUnit {
|
||||
repeated RecordDecl records = 1;
|
||||
repeated FunctionDecl functions = 2;
|
||||
repeated EnumDecl enums = 3;
|
||||
repeated GlobalVarDecl global_vars = 4;
|
||||
repeated RecordType record_types = 1;
|
||||
repeated EnumType enum_types = 2;
|
||||
repeated PointerType pointer_types = 3;
|
||||
repeated LvalueReferenceType lvalue_reference_types = 4;
|
||||
repeated RvalueReferenceType rvalue_reference_types = 5;
|
||||
repeated BuiltinType builtin_types = 6;
|
||||
repeated QualifiedType qualified_types = 7;
|
||||
repeated ArrayType array_types = 8;
|
||||
repeated FunctionDecl functions = 9;
|
||||
repeated GlobalVarDecl global_vars = 10;
|
||||
repeated ElfFunction elf_functions = 11;
|
||||
repeated ElfObject elf_objects = 12;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,21 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct ForwardDeclaration;
|
||||
int uses_forward_decl(struct ForwardDeclaration *);
|
||||
|
||||
struct Hello {
|
||||
int foo;
|
||||
int bar;
|
||||
enum {A, B} enum_field;
|
||||
enum {C, D} enum_field2;
|
||||
struct {
|
||||
int a;
|
||||
int b;
|
||||
struct {
|
||||
int c;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
#if defined(__cplusplus)
|
||||
@@ -24,8 +36,17 @@ struct CPPHello : private HelloAgain, public ByeAgain<float_type> {
|
||||
cfloat_type cpp_bar;
|
||||
virtual int again() { return 0; }
|
||||
CPPHello() : cpp_foo(20), cpp_bar(1.234) { }
|
||||
enum Bla{BLA = 1};
|
||||
int test_enum() {return CPPHello::BLA;}
|
||||
};
|
||||
|
||||
|
||||
void fooVariadic (int &, int *, ...);
|
||||
|
||||
int boo (const CPPHello, int *, float *) {
|
||||
return CPPHello::BLA;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct StackNode {
|
||||
public:
|
||||
@@ -63,7 +84,7 @@ public:
|
||||
template<typename T>
|
||||
class List
|
||||
{
|
||||
protected:
|
||||
public:
|
||||
/*
|
||||
* One element in the list.
|
||||
*/
|
||||
@@ -74,6 +95,7 @@ protected:
|
||||
inline T& getRef() { return mVal; }
|
||||
inline const T& getRef() const { return mVal; }
|
||||
private:
|
||||
void PrivateNode();
|
||||
friend class List;
|
||||
friend class _ListIterator;
|
||||
T mVal;
|
||||
@@ -83,11 +105,20 @@ protected:
|
||||
_Node *middle;
|
||||
};
|
||||
|
||||
|
||||
typedef List<float> float_list;
|
||||
float_list float_list_test;
|
||||
|
||||
|
||||
typedef List<int> int_list;
|
||||
int_list int_list_test;
|
||||
List<float>::_Node node(2);
|
||||
int ListMangle(int_list *, StackNode<int> *);
|
||||
|
||||
template<typename IChild, typename IParent, typename BpChild, typename BpParent>
|
||||
List<IChild> castInterface(List<IParent> parent, const char *childIndicator, bool emitError) {return List<IChild>();}
|
||||
|
||||
void format() {
|
||||
castInterface<float, float, float , float>(List<float>(), "foo", true);
|
||||
}
|
||||
|
||||
#endif // EXAMPLE1_H_
|
||||
|
||||
@@ -22,6 +22,7 @@ struct HelloAgain {
|
||||
int bar_again;
|
||||
static int hello_forever;
|
||||
virtual int again();
|
||||
virtual ~HelloAgain() {}
|
||||
};
|
||||
struct NowWeCrash;
|
||||
} // namespace test2
|
||||
|
||||
Reference in New Issue
Block a user