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: [
|
cflags: [
|
||||||
"-Wall",
|
"-Wall",
|
||||||
"-Werror",
|
"-Werror",
|
||||||
"-std=c++11",
|
|
||||||
"-DGOOGLE_PROTOBUF_NO_RTTI",
|
"-DGOOGLE_PROTOBUF_NO_RTTI",
|
||||||
"-UNDEBUG"
|
"-UNDEBUG"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
cppflags: [
|
||||||
|
"-std=c++14",
|
||||||
|
],
|
||||||
|
|
||||||
target: {
|
target: {
|
||||||
windows: {
|
windows: {
|
||||||
enabled: false
|
enabled: false
|
||||||
@@ -40,8 +43,8 @@ cc_defaults {
|
|||||||
name: "header-checker-lib-defaults",
|
name: "header-checker-lib-defaults",
|
||||||
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"libclangTooling",
|
|
||||||
"libclangToolingCore",
|
"libclangToolingCore",
|
||||||
|
"libclangTooling",
|
||||||
"libclangFrontendTool",
|
"libclangFrontendTool",
|
||||||
"libclangFrontend",
|
"libclangFrontend",
|
||||||
"libclangDriver",
|
"libclangDriver",
|
||||||
@@ -134,8 +137,8 @@ cc_binary_host {
|
|||||||
],
|
],
|
||||||
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"libheader-checker-proto",
|
|
||||||
"libheader-abi-util",
|
"libheader-abi-util",
|
||||||
|
"libheader-checker-proto",
|
||||||
],
|
],
|
||||||
|
|
||||||
target: {
|
target: {
|
||||||
@@ -269,8 +272,14 @@ cc_library_static {
|
|||||||
"libLLVMMCParser",
|
"libLLVMMCParser",
|
||||||
"libLLVMCore",
|
"libLLVMCore",
|
||||||
"libLLVMSupport",
|
"libLLVMSupport",
|
||||||
|
"libheader-checker-proto",
|
||||||
],
|
],
|
||||||
|
|
||||||
|
shared_libs: [
|
||||||
|
"libprotobuf-cpp-full",
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
cflags: [
|
cflags: [
|
||||||
"-Wcast-qual",
|
"-Wcast-qual",
|
||||||
"-Wno-long-long",
|
"-Wno-long-long",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
## Usage
|
## Usage
|
||||||
header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I
|
header-abi-dumper -o <dump-file> <source_file> -I <export-include-dir-1> -I
|
||||||
<export-include-dir-2>.. -- <cflags>
|
<export-include-dir-2>.. -- <cflags>
|
||||||
|
For options : header-abi-dumper --help
|
||||||
|
|
||||||
# VNDK Header Abi Linker
|
# VNDK Header Abi Linker
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ...
|
header-abi-linker -o <linked-abi-dump> <abi-dump1> <abi-dump2> <abi-dump3> ...
|
||||||
|
For options : header-abi-linker --help
|
||||||
|
|
||||||
# VNDK Header Abi Diff
|
# VNDK Header Abi Diff
|
||||||
|
|
||||||
@@ -24,10 +26,13 @@
|
|||||||
abi's exposed by the two dumps.
|
abi's exposed by the two dumps.
|
||||||
|
|
||||||
# Return Value
|
# Return Value
|
||||||
1: InCompatible
|
0: Compatible
|
||||||
0: Compatible or Compatible Extension.
|
1: Changes to APIs unreferenced by symbols in the .dynsym table
|
||||||
|
4: Compatible Extension
|
||||||
|
8: Incompatible
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
header-abi-diff -old <old-abi-dump> -new <new-abi-dump> -o <report>
|
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 "abi_diff.h"
|
||||||
|
|
||||||
|
#include <header_abi_util.h>
|
||||||
|
|
||||||
#include <llvm/Support/raw_ostream.h>
|
#include <llvm/Support/raw_ostream.h>
|
||||||
|
|
||||||
#include <google/protobuf/text_format.h>
|
#include <google/protobuf/text_format.h>
|
||||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
CompatibilityStatus HeaderAbiDiff::GenerateCompatibilityReport() {
|
abi_util::CompatibilityStatusIR HeaderAbiDiff::GenerateCompatibilityReport() {
|
||||||
abi_dump::TranslationUnit old_tu;
|
using abi_util::TextFormatToIRReader;
|
||||||
abi_dump::TranslationUnit new_tu;
|
std::unique_ptr<abi_util::TextFormatToIRReader> old_reader =
|
||||||
std::ifstream old_input(old_dump_);
|
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", old_dump_);
|
||||||
std::ifstream new_input(new_dump_);
|
std::unique_ptr<abi_util::TextFormatToIRReader> new_reader =
|
||||||
google::protobuf::io::IstreamInputStream text_iso(&old_input);
|
TextFormatToIRReader::CreateTextFormatToIRReader("protobuf", new_dump_);
|
||||||
google::protobuf::io::IstreamInputStream text_isn(&new_input);
|
if (!old_reader || !new_reader || !old_reader->ReadDump() ||
|
||||||
|
!new_reader->ReadDump()) {
|
||||||
if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) ||
|
llvm::errs() << "Could not create Text Format readers\n";
|
||||||
!google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) {
|
|
||||||
llvm::errs() << "Failed to generate compatibility report\n";
|
|
||||||
::exit(1);
|
::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(
|
template <typename F>
|
||||||
const abi_dump::TranslationUnit &old_tu,
|
static void AddTypesToMap(std::map<std::string, const abi_util::TypeIR *> *dst,
|
||||||
const abi_dump::TranslationUnit &new_tu) {
|
const abi_util::TextFormatToIRReader *tu, F func) {
|
||||||
std::unique_ptr<abi_diff::TranslationUnitDiff> diff_tu(
|
AddToMap(dst, tu->GetRecordTypes(), func);
|
||||||
new abi_diff::TranslationUnitDiff);
|
AddToMap(dst, tu->GetEnumTypes(), func);
|
||||||
CompatibilityStatus record_status = Collect<abi_dump::RecordDecl>(
|
AddToMap(dst, tu->GetPointerTypes(), func);
|
||||||
diff_tu->mutable_records_added(), diff_tu->mutable_records_removed(),
|
AddToMap(dst, tu->GetBuiltinTypes(), func);
|
||||||
diff_tu->mutable_records_diff(), old_tu.records(), new_tu.records(),
|
AddToMap(dst, tu->GetArrayTypes(), func);
|
||||||
ignored_symbols_);
|
AddToMap(dst, tu->GetLvalueReferenceTypes(), func);
|
||||||
|
AddToMap(dst, tu->GetRvalueReferenceTypes(), func);
|
||||||
|
AddToMap(dst, tu->GetQualifiedTypes(), func);
|
||||||
|
}
|
||||||
|
|
||||||
CompatibilityStatus function_status = Collect<abi_dump::FunctionDecl>(
|
abi_util::CompatibilityStatusIR HeaderAbiDiff::CompareTUs(
|
||||||
diff_tu->mutable_functions_added(), diff_tu->mutable_functions_removed(),
|
const abi_util::TextFormatToIRReader *old_tu,
|
||||||
diff_tu->mutable_functions_diff(), old_tu.functions(),
|
const abi_util::TextFormatToIRReader *new_tu,
|
||||||
new_tu.functions(), ignored_symbols_);
|
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>(
|
// Collect fills in added, removed ,unsafe and safe function diffs.
|
||||||
diff_tu->mutable_enums_added(), diff_tu->mutable_enums_removed(),
|
if (!CollectDynsymExportables(old_tu->GetFunctions(), new_tu->GetFunctions(),
|
||||||
diff_tu->mutable_enums_diff(), old_tu.enums(), new_tu.enums(),
|
old_tu->GetElfFunctions(),
|
||||||
ignored_symbols_);
|
new_tu->GetElfFunctions(),
|
||||||
|
old_types, new_types,
|
||||||
CompatibilityStatus global_var_status = Collect<abi_dump::GlobalVarDecl>(
|
ir_diff_dumper) ||
|
||||||
diff_tu->mutable_global_vars_added(),
|
!CollectDynsymExportables(old_tu->GetGlobalVariables(),
|
||||||
diff_tu->mutable_global_vars_removed(),
|
new_tu->GetGlobalVariables(),
|
||||||
diff_tu->mutable_global_vars_diff(), old_tu.global_vars(),
|
old_tu->GetElfObjects(),
|
||||||
new_tu.global_vars(), ignored_symbols_);
|
new_tu->GetElfObjects(),
|
||||||
|
old_types, new_types,
|
||||||
CompatibilityStatus combined_status =
|
ir_diff_dumper)) {
|
||||||
record_status | function_status | enum_status | global_var_status;
|
llvm::errs() << "Unable to collect dynsym exportables\n";
|
||||||
|
|
||||||
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";
|
|
||||||
::exit(1);
|
::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;
|
return combined_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename TDiff>
|
bool HeaderAbiDiff::CollectUserDefinedTypes(
|
||||||
abi_diff::CompatibilityStatus HeaderAbiDiff::Collect(
|
const abi_util::TextFormatToIRReader *old_tu,
|
||||||
google::protobuf::RepeatedPtrField<T> *elements_added,
|
const abi_util::TextFormatToIRReader *new_tu,
|
||||||
google::protobuf::RepeatedPtrField<T> *elements_removed,
|
const std::map<std::string, const abi_util::TypeIR *> &old_types_map,
|
||||||
google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
|
const std::map<std::string, const abi_util::TypeIR *> &new_types_map,
|
||||||
const google::protobuf::RepeatedPtrField<T> &old_srcs,
|
abi_util::IRDiffDumper *ir_diff_dumper) {
|
||||||
const google::protobuf::RepeatedPtrField<T> &new_srcs,
|
return CollectUserDefinedTypesInternal(
|
||||||
const std::set<std::string> &ignored_symbols) {
|
old_tu->GetRecordTypes(), new_tu->GetRecordTypes(), old_types_map,
|
||||||
assert(elements_added != nullptr);
|
new_types_map, ir_diff_dumper) &&
|
||||||
assert(elements_removed != nullptr);
|
CollectUserDefinedTypesInternal(old_tu->GetEnumTypes(),
|
||||||
assert(elements_diff != nullptr);
|
new_tu->GetEnumTypes(), old_types_map,
|
||||||
|
new_types_map, ir_diff_dumper);
|
||||||
|
}
|
||||||
|
|
||||||
std::map<std::string, const T*> old_elements_map;
|
template <typename T>
|
||||||
std::map<std::string, const T*> new_elements_map;
|
bool HeaderAbiDiff::CollectUserDefinedTypesInternal(
|
||||||
AddToMap(&old_elements_map, old_srcs);
|
const std::vector<T> &old_ud_types,
|
||||||
AddToMap(&new_elements_map, new_srcs);
|
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,
|
abi_util::AddToMap(&old_ud_types_map, old_ud_types,
|
||||||
new_elements_map, ignored_symbols) ||
|
[](const T *e)
|
||||||
!PopulateRemovedElements(elements_added, new_elements_map,
|
{ return e->GetLinkerSetKey();});
|
||||||
old_elements_map, ignored_symbols) ||
|
|
||||||
!PopulateCommonElements(elements_diff, old_elements_map,
|
abi_util::AddToMap(&new_ud_types_map, new_ud_types,
|
||||||
new_elements_map, ignored_symbols)) {
|
[](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";
|
llvm::errs() << "Populating functions in report failed\n";
|
||||||
::exit(1);
|
return false;
|
||||||
}
|
}
|
||||||
if (elements_diff->size() || elements_removed->size()) {
|
return true;
|
||||||
return CompatibilityStatus::INCOMPATIBLE;
|
}
|
||||||
|
|
||||||
|
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 true;
|
||||||
return CompatibilityStatus::EXTENSION;
|
|
||||||
}
|
|
||||||
return CompatibilityStatus::COMPATIBLE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool HeaderAbiDiff::PopulateRemovedElements(
|
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*> &old_elements_map,
|
||||||
const std::map<std::string, const T*> &new_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,
|
||||||
std::vector<const T *> removed_elements;
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
for (auto &&map_element : old_elements_map) {
|
std::vector<const T *> removed_elements =
|
||||||
const T *element = map_element.second;
|
abi_util::FindRemovedElements(old_elements_map, new_elements_map);
|
||||||
auto new_element =
|
if (!DumpLoneElements(removed_elements, elf_map, ir_diff_dumper, diff_kind)) {
|
||||||
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)) {
|
|
||||||
llvm::errs() << "Dumping added / removed element to report failed\n";
|
llvm::errs() << "Dumping added / removed element to report failed\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename TDiff>
|
template <typename T>
|
||||||
bool HeaderAbiDiff::PopulateCommonElements(
|
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 *> &old_elements_map,
|
||||||
const std::map<std::string, const T *> &new_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,
|
||||||
std::vector<std::pair<const T *, const T *>> common_elements;
|
const std::map<std::string, const abi_util::TypeIR *> &new_types,
|
||||||
typename std::map<std::string, const T *>::const_iterator old_element =
|
abi_util::IRDiffDumper *ir_diff_dumper,
|
||||||
old_elements_map.begin();
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
typename std::map<std::string, const T *>::const_iterator new_element =
|
std::vector<std::pair<const T *, const T *>> common_elements =
|
||||||
new_elements_map.begin();
|
abi_util::FindCommonElements(old_elements_map, new_elements_map);
|
||||||
while (old_element != old_elements_map.end() &&
|
if (!DumpDiffElements(common_elements, old_types, new_types,
|
||||||
new_element != new_elements_map.end()) {
|
ir_diff_dumper, diff_kind)) {
|
||||||
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)) {
|
|
||||||
llvm::errs() << "Dumping difference in common element to report failed\n";
|
llvm::errs() << "Dumping difference in common element to report failed\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -185,48 +278,62 @@ bool HeaderAbiDiff::PopulateCommonElements(
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool HeaderAbiDiff::DumpLoneElements(
|
bool HeaderAbiDiff::DumpLoneElements(
|
||||||
google::protobuf::RepeatedPtrField<T> *dst,
|
|
||||||
std::vector<const T *> &elements,
|
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) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
T *added_element = dst->Add();
|
// The element does exist in the .dynsym table, we do not have meta-data
|
||||||
if (!added_element) {
|
// surrounding the element.
|
||||||
llvm::errs() << "Adding element diff failed\n";
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
*added_element = *element;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename TDiff>
|
|
||||||
|
template <typename T>
|
||||||
bool HeaderAbiDiff::DumpDiffElements(
|
bool HeaderAbiDiff::DumpDiffElements(
|
||||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
|
||||||
std::vector<std::pair<const T *,const T *>> &pairs,
|
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) {
|
for (auto &&pair : pairs) {
|
||||||
const T *old_element = pair.first;
|
const T *old_element = pair.first;
|
||||||
const T *new_element = pair.second;
|
const T *new_element = pair.second;
|
||||||
// Not having inheritance from protobuf messages makes this
|
|
||||||
// restrictive code.
|
if (abi_diff_wrappers::IgnoreSymbol<T>(
|
||||||
if (abi_diff_wrappers::IgnoreSymbol<T>(old_element, ignored_symbols)) {
|
old_element, ignored_symbols_,
|
||||||
|
[](const T *e) {return e->GetLinkerSetKey();})) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
abi_diff_wrappers::DiffWrapper<T, TDiff> diff_wrapper(old_element,
|
abi_diff_wrappers::DiffWrapper<T> diff_wrapper(old_element, new_element,
|
||||||
new_element);
|
ir_diff_dumper, old_types,
|
||||||
std::unique_ptr<TDiff> decl_diff_ptr = diff_wrapper.Get();
|
new_types, &type_cache_);
|
||||||
if (!decl_diff_ptr) {
|
if (!diff_wrapper.DumpDiff(diff_kind)) {
|
||||||
continue;
|
llvm::errs() << "Failed to diff elements\n";
|
||||||
}
|
|
||||||
TDiff *added_element_diff = dst->Add();
|
|
||||||
if (!added_element_diff) {
|
|
||||||
llvm::errs() << "Adding element diff failed\n";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*added_element_diff = *decl_diff_ptr;
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "abi_diff_wrappers.h"
|
#include "abi_diff_wrappers.h"
|
||||||
|
|
||||||
|
#include <ir_representation.h>
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||||
@@ -21,68 +23,102 @@
|
|||||||
#include "proto/abi_diff.pb.h"
|
#include "proto/abi_diff.pb.h"
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
typedef abi_diff::CompatibilityStatus CompatibilityStatus;
|
|
||||||
|
|
||||||
class HeaderAbiDiff {
|
class HeaderAbiDiff {
|
||||||
public:
|
public:
|
||||||
HeaderAbiDiff(const std::string &lib_name, const std::string &arch,
|
HeaderAbiDiff(const std::string &lib_name, const std::string &arch,
|
||||||
const std::string &old_dump, const std::string &new_dump,
|
const std::string &old_dump, const std::string &new_dump,
|
||||||
const std::string &compatibility_report,
|
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),
|
: lib_name_(lib_name), arch_(arch), old_dump_(old_dump),
|
||||||
new_dump_(new_dump), cr_(compatibility_report),
|
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:
|
private:
|
||||||
CompatibilityStatus CompareTUs(const abi_dump::TranslationUnit &old_tu,
|
abi_util::CompatibilityStatusIR CompareTUs(
|
||||||
const abi_dump::TranslationUnit &new_tu);
|
const abi_util::TextFormatToIRReader *old_tu,
|
||||||
// Collect* methods fill in the diff_tu.
|
const abi_util::TextFormatToIRReader *new_tu,
|
||||||
template <typename T, typename TDiff>
|
abi_util::IRDiffDumper *ir_diff_dumper);
|
||||||
static CompatibilityStatus Collect(
|
|
||||||
google::protobuf::RepeatedPtrField<T> *elements_added,
|
template <typename T, typename ElfSymbolType>
|
||||||
google::protobuf::RepeatedPtrField<T> *elements_removed,
|
bool CollectDynsymExportables(
|
||||||
google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
|
const std::vector<T> &old_exportables,
|
||||||
const google::protobuf::RepeatedPtrField<T> &old_srcs,
|
const std::vector<T> &new_exportables,
|
||||||
const google::protobuf::RepeatedPtrField<T> &new_srcs,
|
const std::vector<ElfSymbolType> &old_elf_symbols,
|
||||||
const std::set<std::string> &ignored_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>
|
template <typename T>
|
||||||
static inline void AddToMap(std::map<std::string, const T *> *dst,
|
bool Collect(
|
||||||
const google::protobuf::RepeatedPtrField<T> &src);
|
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>
|
template <typename T>
|
||||||
static bool PopulateRemovedElements(
|
bool PopulateRemovedElements(
|
||||||
google::protobuf::RepeatedPtrField<T> *dst,
|
|
||||||
const std::map<std::string, const T *> &old_elements_map,
|
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 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>
|
template <typename T>
|
||||||
static bool PopulateCommonElements(
|
bool PopulateCommonElements(
|
||||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
|
||||||
const std::map<std::string, const T *> &old_elements_map,
|
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 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>
|
template <typename T>
|
||||||
static bool DumpDiffElements(
|
bool DumpDiffElements(
|
||||||
google::protobuf::RepeatedPtrField<TDiff> *dst,
|
|
||||||
std::vector<std::pair<const T *, const T *>> &pairs,
|
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>
|
template <typename T>
|
||||||
static bool DumpLoneElements(google::protobuf::RepeatedPtrField<T> *dst,
|
bool DumpLoneElements(
|
||||||
std::vector<const T *> &elements,
|
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);
|
||||||
|
|
||||||
|
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:
|
private:
|
||||||
const std::string &lib_name_;
|
const std::string &lib_name_;
|
||||||
@@ -91,27 +127,6 @@ class HeaderAbiDiff {
|
|||||||
const std::string &new_dump_;
|
const std::string &new_dump_;
|
||||||
const std::string &cr_;
|
const std::string &cr_;
|
||||||
const std::set<std::string> &ignored_symbols_;
|
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 "abi_diff_wrappers.h"
|
||||||
|
|
||||||
#include<header_abi_util.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 <llvm/Support/raw_ostream.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 {
|
namespace abi_diff_wrappers {
|
||||||
|
|
||||||
static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
|
static bool IsAccessDownGraded(abi_util::AccessSpecifierIR old_access,
|
||||||
abi_dump::AccessSpecifier new_access) {
|
abi_util::AccessSpecifierIR new_access) {
|
||||||
bool access_downgraded = false;
|
bool access_downgraded = false;
|
||||||
switch (old_access) {
|
switch (old_access) {
|
||||||
case abi_dump::AccessSpecifier::protected_access:
|
case abi_util::AccessSpecifierIR::ProtectedAccess:
|
||||||
if (new_access == abi_dump::AccessSpecifier::private_access) {
|
if (new_access == abi_util::AccessSpecifierIR::PrivateAccess) {
|
||||||
access_downgraded = true;
|
access_downgraded = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case abi_dump::AccessSpecifier::public_access:
|
case abi_util::AccessSpecifierIR::PublicAccess:
|
||||||
if (new_access != abi_dump::AccessSpecifier::public_access) {
|
if (new_access != abi_util::AccessSpecifierIR::PublicAccess) {
|
||||||
access_downgraded = true;
|
access_downgraded = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -69,255 +40,590 @@ static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
|
|||||||
return access_downgraded;
|
return access_downgraded;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string CpptoCAdjustment(const std::string &type) {
|
static std::string Unwind(const std::deque<std::string> *type_queue) {
|
||||||
std::string adjusted_type_name =
|
if (!type_queue) {
|
||||||
abi_util::FindAndReplace(type, "\\bstruct ", "");
|
return "";
|
||||||
|
}
|
||||||
return adjusted_type_name;
|
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,
|
void DiffWrapperBase::CompareEnumFields(
|
||||||
const abi_dump::BasicTypeAbi &new_abi) {
|
const std::vector<abi_util::EnumFieldIR> &old_fields,
|
||||||
// Strip of leading 'struct' keyword from type names
|
const std::vector<abi_util::EnumFieldIR> &new_fields,
|
||||||
std::string old_type = old_abi.name();
|
abi_util::EnumTypeDiffIR *enum_type_diff_ir) {
|
||||||
std::string new_type = new_abi.name();
|
std::map<std::string, const abi_util::EnumFieldIR *> old_fields_map;
|
||||||
old_type = CpptoCAdjustment(old_type);
|
std::map<std::string, const abi_util::EnumFieldIR *> new_fields_map;
|
||||||
new_type = CpptoCAdjustment(new_type);
|
abi_util::AddToMap(&old_fields_map, old_fields,
|
||||||
// TODO: Add checks for C++ built-in types vs C corresponding types.
|
[](const abi_util::EnumFieldIR *f)
|
||||||
return old_type != new_type;
|
{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,
|
DiffStatus DiffWrapperBase::CompareEnumTypes(
|
||||||
const abi_dump::BasicTypeAbi &new_abi) {
|
const abi_util::EnumTypeIR *old_type, const abi_util::EnumTypeIR *new_type,
|
||||||
// We need to add a layer of indirection to account for issues when C and C++
|
std::deque<std::string> *type_queue,
|
||||||
// are mixed. For example some types like wchar_t are in-built types for C++
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
// but not for C. Another example would be clang reporting C structures
|
if (old_type->GetName() != new_type->GetName()) {
|
||||||
// without the leading "struct" keyword when headers defining them are
|
return DiffStatus::direct_diff;
|
||||||
// included in C++ files.
|
}
|
||||||
bool name_comparison = CompareTypeNames(old_abi, new_abi);
|
auto enum_type_diff_ir = std::make_unique<abi_util::EnumTypeDiffIR>();
|
||||||
bool size_comparison = (old_abi.size() != new_abi.size());
|
enum_type_diff_ir->SetName(old_type->GetName());
|
||||||
bool alignment_comparison = (old_abi.alignment() != new_abi.alignment());
|
const std::string &old_underlying_type = old_type->GetUnderlyingType();
|
||||||
return name_comparison || size_comparison || alignment_comparison;
|
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>
|
bool DiffWrapperBase::CompareVTableComponents(
|
||||||
static bool Diff(const T &old_element, const T &new_element) {
|
const abi_util::VTableComponentIR &old_component,
|
||||||
// Can be specialized for future changes in the format.
|
const abi_util::VTableComponentIR &new_component) {
|
||||||
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
return old_component.GetName() == new_component.GetName() &&
|
||||||
new_element.basic_abi().type_abi()) ||
|
old_component.GetValue() == new_component.GetValue() &&
|
||||||
(old_element.basic_abi().name() != new_element.basic_abi().name()) ||
|
old_component.GetKind() == new_component.GetKind();
|
||||||
IsAccessDownGraded(old_element.basic_abi().access(),
|
|
||||||
new_element.basic_abi().access());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
bool DiffWrapperBase::CompareVTables(
|
||||||
bool Diff<EnumFieldDecl>(const EnumFieldDecl &old_element,
|
const abi_util::RecordTypeIR *old_record,
|
||||||
const EnumFieldDecl &new_element) {
|
const abi_util::RecordTypeIR *new_record) {
|
||||||
// Can be specialized for future changes in the format.
|
|
||||||
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
const std::vector<abi_util::VTableComponentIR> &old_components =
|
||||||
new_element.basic_abi().type_abi()) ||
|
old_record->GetVTableLayout().GetVTableComponents();
|
||||||
(old_element.enum_field_value() != new_element.enum_field_value()) ||
|
const std::vector<abi_util::VTableComponentIR> &new_components =
|
||||||
(old_element.basic_abi().name() != new_element.basic_abi().name());
|
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 DiffWrapperBase::CompareSizeAndAlignment(
|
||||||
bool Diff<ParamDecl>(const ParamDecl &old_element,
|
const abi_util::TypeIR *old_type,
|
||||||
const ParamDecl &new_element) {
|
const abi_util::TypeIR *new_type) {
|
||||||
// Can be specialized for future changes in the format.
|
return old_type->GetSize() == new_type->GetSize() &&
|
||||||
return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
old_type->GetAlignment() == new_type->GetAlignment();
|
||||||
new_element.basic_abi().type_abi());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
std::unique_ptr<abi_util::RecordFieldDiffIR>
|
||||||
bool Diff<CXXBaseSpecifier>(const CXXBaseSpecifier &old_element,
|
DiffWrapperBase::CompareCommonRecordFields(
|
||||||
const CXXBaseSpecifier &new_element) {
|
const abi_util::RecordFieldIR *old_field,
|
||||||
// Can be specialized for future changes in the format.
|
const abi_util::RecordFieldIR *new_field,
|
||||||
return (DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
|
std::deque<std::string> *type_queue,
|
||||||
new_element.basic_abi().type_abi()) ||
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
old_element.basic_abi().access() != new_element.basic_abi().access() ||
|
if (old_field->GetOffset() != new_field->GetOffset() ||
|
||||||
old_element.is_virtual() != new_element.is_virtual());
|
// 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 <>
|
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
|
||||||
bool Diff<VTableComponent>(const VTableComponent &old_element,
|
std::vector<const abi_util::RecordFieldIR *>>
|
||||||
const VTableComponent &new_element) {
|
DiffWrapperBase::CompareRecordFields(
|
||||||
bool kind_comparison = old_element.kind() != new_element.kind();
|
const std::vector<abi_util::RecordFieldIR> &old_fields,
|
||||||
bool mangled_name_comparison = old_element.mangled_component_name() !=
|
const std::vector<abi_util::RecordFieldIR> &new_fields,
|
||||||
new_element.mangled_component_name();
|
std::deque<std::string> *type_queue,
|
||||||
bool value_comparison =
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
old_element.component_value() != new_element.component_value();
|
std::pair<std::vector<abi_util::RecordFieldDiffIR>,
|
||||||
return kind_comparison || mangled_name_comparison || value_comparison;
|
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:
|
bool DiffWrapperBase::CompareBaseSpecifiers(
|
||||||
// RecordDeclDiff's CXXBaseSpecifierDiff fields and well as FieldDeclDiff
|
const std::vector<abi_util::CXXBaseSpecifierIR> &old_base_specifiers,
|
||||||
// fields.
|
const std::vector<abi_util::CXXBaseSpecifierIR> &new_base_specifiers,
|
||||||
template <typename T, typename TDiff>
|
std::deque<std::string> *type_queue,
|
||||||
template <typename Element, typename ElementDiff>
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
bool DiffWrapperBase<T, TDiff>::GetElementDiffs(
|
if (old_base_specifiers.size() != new_base_specifiers.size()) {
|
||||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst,
|
return false;
|
||||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
}
|
||||||
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
|
|
||||||
bool differs = false;
|
|
||||||
assert(dst != nullptr);
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 0;
|
while (i < old_base_specifiers.size()) {
|
||||||
while (i < old_elements.size() && j < new_elements.size()) {
|
if (CompareAndDumpTypeDiff(old_base_specifiers.at(i).GetReferencedType(),
|
||||||
const Element &old_element = old_elements.Get(i);
|
new_base_specifiers.at(i).GetReferencedType(),
|
||||||
const Element &new_element = new_elements.Get(i);
|
type_queue, diff_kind) ==
|
||||||
|
DiffStatus::direct_diff ||
|
||||||
if (Diff(old_element, new_element)) {
|
(old_base_specifiers.at(i).GetAccess() !=
|
||||||
ElementDiff *diff = dst->Add();
|
new_base_specifiers.at(i).GetAccess())) {
|
||||||
Element *old_elementp = nullptr;
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
j++;
|
|
||||||
}
|
}
|
||||||
if (old_elements.size() != new_elements.size()) {
|
return true;
|
||||||
GetExtraElementDiffs(dst, i, j, old_elements, new_elements);
|
|
||||||
differs = true;
|
|
||||||
}
|
|
||||||
return differs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename TDiff>
|
void DiffWrapperBase::CompareTemplateInfo(
|
||||||
template <typename Element, typename ElementDiff>
|
const std::vector<abi_util::TemplateElementIR> &old_template_elements,
|
||||||
void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs(
|
const std::vector<abi_util::TemplateElementIR> &new_template_elements,
|
||||||
google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
|
std::deque<std::string> *type_queue,
|
||||||
const google::protobuf::RepeatedPtrField<Element> &old_elements,
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
const google::protobuf::RepeatedPtrField<Element> &new_elements) {
|
uint32_t old_template_size = old_template_elements.size();
|
||||||
assert(dst != nullptr);
|
assert(old_template_size == new_template_elements.size());
|
||||||
while (i < old_elements.size()) {
|
uint32_t i = 0;
|
||||||
const Element &old_element = old_elements.Get(i);
|
while (i < old_template_size) {
|
||||||
ElementDiff *diff = dst->Add();
|
const abi_util::TemplateElementIR &old_template_element =
|
||||||
Element *old_elementp = nullptr;
|
old_template_elements[i];
|
||||||
if (!diff || !(old_elementp = diff->mutable_old())) {
|
const abi_util::TemplateElementIR &new_template_element =
|
||||||
llvm::errs() << "Failed to add diff element\n";
|
new_template_elements[i];
|
||||||
::exit(1);
|
CompareAndDumpTypeDiff(old_template_element.GetReferencedType(),
|
||||||
}
|
new_template_element.GetReferencedType(),
|
||||||
*old_elementp = old_element;
|
type_queue, diff_kind);
|
||||||
diff->set_index(i);
|
|
||||||
i++;
|
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,
|
DiffStatus DiffWrapperBase::CompareRecordTypes(
|
||||||
BasicNamedAndTypedDecl *type_diff_new,
|
const abi_util::RecordTypeIR *old_type,
|
||||||
const BasicNamedAndTypedDecl &old,
|
const abi_util::RecordTypeIR *new_type,
|
||||||
const BasicNamedAndTypedDecl &new_) {
|
std::deque<std::string> *type_queue,
|
||||||
assert(type_diff_old != nullptr);
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
assert(type_diff_new != nullptr);
|
auto record_type_diff_ir = std::make_unique<abi_util::RecordTypeDiffIR>();
|
||||||
if (DiffBasicTypeAbi(old.type_abi(), new_.type_abi()) ||
|
// Compare names.
|
||||||
IsAccessDownGraded(old.access(), new_.access())) {
|
if (old_type->GetName() != new_type->GetName()) {
|
||||||
*(type_diff_old) = old;
|
// Do not dump anything since the record types themselves are fundamentally
|
||||||
*(type_diff_new) = new_;
|
// 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 true;
|
||||||
}
|
}
|
||||||
return false;
|
CompareRecordTypes(oldp_, newp_, &type_queue, diff_kind);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::unique_ptr<RecordDeclDiff>
|
bool DiffWrapper<abi_util::EnumTypeIR>::DumpDiff(
|
||||||
DiffWrapper<RecordDecl, RecordDeclDiff>::Get() {
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
std::unique_ptr<RecordDeclDiff> record_diff(new RecordDeclDiff());
|
std::deque<std::string> type_queue;
|
||||||
assert(oldp_->basic_abi().linker_set_key() ==
|
if (oldp_->GetName() != newp_->GetName()) {
|
||||||
newp_->basic_abi().linker_set_key());
|
llvm::errs() << "Comparing two different unreferenced enums\n";
|
||||||
record_diff->set_name(oldp_->basic_abi().name());
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
if (!type_cache_->insert(oldp_->GetName()).second) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
CompareEnumTypes(oldp_, newp_, &type_queue, diff_kind);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::unique_ptr<EnumDeclDiff>
|
bool DiffWrapper<abi_util::GlobalVarIR>::DumpDiff(
|
||||||
DiffWrapper<EnumDecl, EnumDeclDiff>::Get() {
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff());
|
std::deque<std::string> type_queue;
|
||||||
assert(oldp_->basic_abi().linker_set_key() ==
|
type_queue.push_back(oldp_->GetName());
|
||||||
newp_->basic_abi().linker_set_key());
|
DiffStatus type_diff = CompareAndDumpTypeDiff(oldp_->GetReferencedType(),
|
||||||
google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *fdiffs =
|
newp_->GetReferencedType(),
|
||||||
enum_diff->mutable_field_diffs();
|
&type_queue, diff_kind);
|
||||||
assert(fdiffs != nullptr);
|
DiffStatus access_diff = (oldp_->GetAccess() == newp_->GetAccess()) ?
|
||||||
enum_diff->set_name(oldp_->basic_abi().name());
|
DiffStatus::no_diff : DiffStatus::direct_diff;
|
||||||
if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) ||
|
if ((type_diff | access_diff) & DiffStatus::direct_diff) {
|
||||||
DiffBasicNamedAndTypedDecl(
|
abi_util::GlobalVarDiffIR global_var_diff_ir(oldp_, newp_);
|
||||||
enum_diff->mutable_type_diff()->mutable_old(),
|
global_var_diff_ir.SetName(oldp_->GetName());
|
||||||
enum_diff->mutable_type_diff()->mutable_new_(),
|
return ir_diff_dumper_->AddDiffMessageIR(&global_var_diff_ir,
|
||||||
oldp_->basic_abi(), newp_->basic_abi())) {
|
Unwind(&type_queue), diff_kind);
|
||||||
return enum_diff;
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
std::unique_ptr<FunctionDeclDiff>
|
bool DiffWrapper<abi_util::FunctionIR>::DumpDiff(
|
||||||
DiffWrapper<FunctionDecl, FunctionDeclDiff>::Get() {
|
abi_util::IRDiffDumper::DiffKind diff_kind) {
|
||||||
std::unique_ptr<FunctionDeclDiff> func_diff(new FunctionDeclDiff());
|
std::deque<std::string> type_queue;
|
||||||
google::protobuf::RepeatedPtrField<ParamDeclDiff> *pdiffs =
|
type_queue.push_back(oldp_->GetName());
|
||||||
func_diff->mutable_param_diffs();
|
DiffStatus param_diffs = CompareFunctionParameters(oldp_->GetParameters(),
|
||||||
assert(func_diff->mutable_return_type_diffs() != nullptr);
|
newp_->GetParameters(),
|
||||||
func_diff->set_name(oldp_->basic_abi().linker_set_key());
|
&type_queue, diff_kind);
|
||||||
if (DiffBasicNamedAndTypedDecl(
|
DiffStatus return_type_diff =
|
||||||
func_diff->mutable_return_type_diffs()->mutable_old(),
|
CompareAndDumpTypeDiff(oldp_->GetReturnType(),
|
||||||
func_diff->mutable_return_type_diffs()->mutable_new_(),
|
newp_->GetReturnType(),
|
||||||
oldp_->basic_abi(), newp_->basic_abi()) ||
|
&type_queue, diff_kind);
|
||||||
GetElementDiffs(pdiffs, oldp_->parameters(), newp_->parameters())) {
|
CompareTemplateInfo(oldp_->GetTemplateElements(),
|
||||||
return func_diff;
|
newp_->GetTemplateElements(),
|
||||||
}
|
&type_queue, diff_kind);
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
if ((param_diffs == DiffStatus::direct_diff ||
|
||||||
std::unique_ptr<GlobalVarDeclDiff>
|
return_type_diff == DiffStatus::direct_diff) ||
|
||||||
DiffWrapper<GlobalVarDecl, GlobalVarDeclDiff>::Get() {
|
(oldp_->GetAccess() != newp_->GetAccess())) {
|
||||||
std::unique_ptr<GlobalVarDeclDiff> global_var_diff(new GlobalVarDeclDiff());
|
abi_util::FunctionDiffIR function_diff_ir(oldp_, newp_);
|
||||||
assert(global_var_diff->mutable_type_diff() != nullptr);
|
function_diff_ir.SetName(oldp_->GetName());
|
||||||
if (DiffBasicNamedAndTypedDecl(
|
return ir_diff_dumper_->AddDiffMessageIR(&function_diff_ir,
|
||||||
global_var_diff->mutable_type_diff()->mutable_old(),
|
Unwind(&type_queue), diff_kind);
|
||||||
global_var_diff->mutable_type_diff()->mutable_new_(),
|
|
||||||
oldp_->basic_abi(), newp_->basic_abi())) {
|
|
||||||
return global_var_diff;
|
|
||||||
}
|
}
|
||||||
return nullptr;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // abi_diff_wrappers
|
} // abi_diff_wrappers
|
||||||
|
|||||||
@@ -22,45 +22,160 @@
|
|||||||
#include "proto/abi_diff.pb.h"
|
#include "proto/abi_diff.pb.h"
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
#include <ir_representation.h>
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
namespace abi_diff_wrappers {
|
namespace abi_diff_wrappers {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T, typename F>
|
||||||
static bool IgnoreSymbol(const T *element,
|
static bool IgnoreSymbol(const T *element,
|
||||||
const std::set<std::string> &ignored_symbols) {
|
const std::set<std::string> &ignored_symbols,
|
||||||
return ignored_symbols.find(element->basic_abi().linker_set_key()) !=
|
F func) {
|
||||||
|
return ignored_symbols.find(func(element)) !=
|
||||||
ignored_symbols.end();
|
ignored_symbols.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename TDiff>
|
enum DiffStatus {
|
||||||
class DiffWrapperBase {
|
// Previous stages of CompareAndTypeDiff should not dump the diff.
|
||||||
public:
|
no_diff = 0,
|
||||||
virtual std::unique_ptr<TDiff> Get() = 0 ;
|
// Previous stages of CompareAndTypeDiff should dump the diff if required.
|
||||||
protected:
|
direct_diff = 1,
|
||||||
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_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename TDiff>
|
class DiffWrapperBase {
|
||||||
class DiffWrapper : public DiffWrapperBase<T, TDiff> {
|
|
||||||
public:
|
public:
|
||||||
DiffWrapper(const T *oldp, const T *newp)
|
virtual bool DumpDiff(abi_util::IRDiffDumper::DiffKind diff_kind) = 0;
|
||||||
: DiffWrapperBase<T, TDiff>(oldp, newp) { }
|
virtual ~DiffWrapperBase() { }
|
||||||
std::unique_ptr<TDiff> Get() override;
|
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
|
} // abi_diff_wrappers
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "abi_diff.h"
|
#include "abi_diff.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
#include <llvm/Support/CommandLine.h>
|
#include <llvm/Support/CommandLine.h>
|
||||||
#include <llvm/Support/FileSystem.h>
|
#include <llvm/Support/FileSystem.h>
|
||||||
#include <llvm/Support/raw_ostream.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,
|
"advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
|
||||||
llvm::cl::cat(header_checker_category));
|
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(
|
static llvm::cl::opt<bool> suppress_local_warnings(
|
||||||
"suppress_local_warnings", llvm::cl::desc("suppress local warnings"),
|
"suppress_local_warnings", llvm::cl::desc("suppress local warnings"),
|
||||||
llvm::cl::Optional, llvm::cl::cat(header_checker_category));
|
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::desc("Do not return a non zero status on extensions"),
|
||||||
llvm::cl::Optional, llvm::cl::cat(header_checker_category));
|
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) {
|
static std::set<std::string> LoadIgnoredSymbols(std::string &symbol_list_path) {
|
||||||
std::ifstream symbol_ifstream(symbol_list_path);
|
std::ifstream symbol_ifstream(symbol_list_path);
|
||||||
std::set<std::string> ignored_symbols;
|
std::set<std::string> ignored_symbols;
|
||||||
@@ -80,38 +95,50 @@ int main(int argc, const char **argv) {
|
|||||||
ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list);
|
ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list);
|
||||||
}
|
}
|
||||||
HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report,
|
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 = "";
|
std::string status_str = "";
|
||||||
switch (status) {
|
std::string unreferenced_change_str = "";
|
||||||
case CompatibilityStatus::INCOMPATIBLE:
|
std::string error_or_warning_str = "\033[36;1mwarning: \033[0m";
|
||||||
status_str = "broken";
|
|
||||||
break;
|
|
||||||
case CompatibilityStatus::EXTENSION:
|
|
||||||
status_str = "extended";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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) {
|
if (!suppress_local_warnings && status) {
|
||||||
llvm::errs() << "******************************************************\n"
|
llvm::errs() << "******************************************************\n"
|
||||||
<< "VNDK Abi "
|
<< error_or_warning_str
|
||||||
|
<< "VNDK library: "
|
||||||
|
<< lib_name
|
||||||
|
<< "'s ABI has "
|
||||||
<< status_str
|
<< status_str
|
||||||
<< ":"
|
<< unreferenced_change_str
|
||||||
<< " Please check compatiblity report at : "
|
<< " Please check compatiblity report at : "
|
||||||
<< compatibility_report << "\n"
|
<< 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) {
|
if (advice_only) {
|
||||||
return CompatibilityStatus::COMPATIBLE;
|
return abi_util::CompatibilityStatusIR::Compatible;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allow_extensions && status == CompatibilityStatus::EXTENSION) {
|
|
||||||
return CompatibilityStatus::COMPATIBLE;
|
|
||||||
}
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,8 @@
|
|||||||
#ifndef ABI_WRAPPERS_H_
|
#ifndef ABI_WRAPPERS_H_
|
||||||
#define ABI_WRAPPERS_H_
|
#define ABI_WRAPPERS_H_
|
||||||
|
|
||||||
|
#include <ir_representation.h>
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||||
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
#pragma clang diagnostic ignored "-Wnested-anon-types"
|
||||||
@@ -32,7 +34,10 @@ class ABIWrapper {
|
|||||||
public:
|
public:
|
||||||
ABIWrapper(clang::MangleContext *mangle_contextp,
|
ABIWrapper(clang::MangleContext *mangle_contextp,
|
||||||
clang::ASTContext *ast_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,
|
static std::string GetDeclSourceFile(const clang::Decl *decl,
|
||||||
const clang::CompilerInstance *cip);
|
const clang::CompilerInstance *cip);
|
||||||
@@ -41,123 +46,161 @@ class ABIWrapper {
|
|||||||
clang::MangleContext *mangle_context);
|
clang::MangleContext *mangle_context);
|
||||||
protected:
|
protected:
|
||||||
abi_dump::AccessSpecifier AccessClangToDump(
|
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,
|
bool SetupTemplateArguments(const clang::TemplateArgumentList *tl,
|
||||||
clang::TemplateParameterList *pl) const;
|
abi_util::TemplatedArtifactIR *ta,
|
||||||
|
const std::string &source_file);
|
||||||
|
|
||||||
bool SetupTemplateArguments(abi_dump::TemplateInfo *tinfo,
|
std::string QualTypeToString(const clang::QualType &sweet_qt);
|
||||||
const clang::TemplateArgumentList *tl) const;
|
|
||||||
|
|
||||||
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,
|
bool CreateExtendedType(
|
||||||
const clang::QualType type, bool dump_size) const;
|
clang::QualType canonical_type,
|
||||||
|
abi_util::TypeIR *typep);
|
||||||
|
|
||||||
bool SetupBasicNamedAndTypedDecl(
|
clang::QualType GetReferencedType(const clang::QualType qual_type);
|
||||||
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;
|
|
||||||
|
|
||||||
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_;
|
const clang::CompilerInstance *cip_;
|
||||||
clang::MangleContext *mangle_contextp_;
|
clang::MangleContext *mangle_contextp_;
|
||||||
clang::ASTContext *ast_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 {
|
class RecordDeclWrapper : public ABIWrapper {
|
||||||
public:
|
public:
|
||||||
RecordDeclWrapper(clang::MangleContext *mangle_contextp,
|
RecordDeclWrapper(
|
||||||
clang::ASTContext *ast_contextp,
|
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||||
const clang::CompilerInstance *compiler_instance_p,
|
const clang::CompilerInstance *compiler_instance_p,
|
||||||
const clang::RecordDecl *decl);
|
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:
|
private:
|
||||||
const clang::RecordDecl *record_decl_;
|
const clang::RecordDecl *record_decl_;
|
||||||
|
std::string previous_record_stages_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool SetupRecordInfo(abi_dump::RecordDecl *record_declp,
|
bool SetupRecordInfo(abi_util::RecordTypeIR *type,
|
||||||
const std::string &source_file) const;
|
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,
|
bool SetupCXXBases(abi_util::RecordTypeIR *cxxp,
|
||||||
const clang::CXXRecordDecl *cxx_record_decl) const;
|
const clang::CXXRecordDecl *cxx_record_decl);
|
||||||
|
|
||||||
bool SetupTemplateInfo(abi_dump::RecordDecl *record_declp,
|
bool SetupTemplateInfo(abi_util::RecordTypeIR *record_declp,
|
||||||
const clang::CXXRecordDecl *cxx_record_decl) const;
|
const clang::CXXRecordDecl *cxx_record_decl,
|
||||||
|
const std::string &source_file);
|
||||||
|
|
||||||
bool SetupRecordVTable(abi_dump::RecordDecl *record_declp,
|
bool SetupRecordVTable(abi_util::RecordTypeIR *record_declp,
|
||||||
const clang::CXXRecordDecl *cxx_record_decl) const;
|
const clang::CXXRecordDecl *cxx_record_decl);
|
||||||
bool SetupRecordVTableComponent(
|
abi_util::VTableComponentIR SetupRecordVTableComponent(
|
||||||
abi_dump::VTableComponent *added_vtable_component,
|
const clang::VTableComponent &vtable_component);
|
||||||
const clang::VTableComponent &vtable_component) const;
|
|
||||||
|
|
||||||
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 {
|
class FunctionDeclWrapper : public ABIWrapper {
|
||||||
public:
|
public:
|
||||||
FunctionDeclWrapper(clang::MangleContext *mangle_contextp,
|
FunctionDeclWrapper(
|
||||||
clang::ASTContext *ast_contextp,
|
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||||
const clang::CompilerInstance *compiler_instance_p,
|
const clang::CompilerInstance *compiler_instance_p,
|
||||||
const clang::FunctionDecl *decl);
|
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:
|
private:
|
||||||
const clang::FunctionDecl *function_decl_;
|
const clang::FunctionDecl *function_decl_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool SetupFunction(abi_dump::FunctionDecl *methodp,
|
bool SetupFunction(abi_util::FunctionIR *methodp,
|
||||||
const std::string &source_file) const;
|
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 {
|
class EnumDeclWrapper : public ABIWrapper {
|
||||||
public:
|
public:
|
||||||
EnumDeclWrapper(clang::MangleContext *mangle_contextp,
|
EnumDeclWrapper(
|
||||||
clang::ASTContext *ast_contextp,
|
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||||
const clang::CompilerInstance *compiler_instance_p,
|
const clang::CompilerInstance *compiler_instance_p,
|
||||||
const clang::EnumDecl *decl);
|
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:
|
private:
|
||||||
const clang::EnumDecl *enum_decl_;
|
const clang::EnumDecl *enum_decl_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool SetupEnum(abi_dump::EnumDecl *enump,
|
bool SetupEnum(abi_util::EnumTypeIR *type,
|
||||||
const std::string &source_file) const;
|
const std::string &source_file);
|
||||||
bool SetupEnumFields(abi_dump::EnumDecl *enump) const;
|
|
||||||
|
bool SetupEnumFields(abi_util::EnumTypeIR *enump);
|
||||||
};
|
};
|
||||||
|
|
||||||
class GlobalVarDeclWrapper : public ABIWrapper {
|
class GlobalVarDeclWrapper : public ABIWrapper {
|
||||||
public:
|
public:
|
||||||
GlobalVarDeclWrapper(clang::MangleContext *mangle_contextp,
|
GlobalVarDeclWrapper(
|
||||||
clang::ASTContext *ast_contextp,
|
clang::MangleContext *mangle_contextp, clang::ASTContext *ast_contextp,
|
||||||
const clang::CompilerInstance *compiler_instance_p,
|
const clang::CompilerInstance *compiler_instance_p,
|
||||||
const clang::VarDecl *decl);
|
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:
|
private:
|
||||||
const clang::VarDecl *global_var_decl_;
|
const clang::VarDecl *global_var_decl_;
|
||||||
private:
|
private:
|
||||||
bool SetupGlobalVar(abi_dump::GlobalVarDecl *global_varp,
|
bool SetupGlobalVar(abi_util::GlobalVarIR *global_varp,
|
||||||
const std::string &source_file) const;
|
const std::string &source_file);
|
||||||
};
|
};
|
||||||
|
|
||||||
} //end namespace abi_wrapper
|
} //end namespace abi_wrapper
|
||||||
|
|
||||||
#endif // ABI_WRAPPERS_H_
|
#endif // ABI_WRAPPERS_H_
|
||||||
|
|||||||
@@ -19,9 +19,6 @@
|
|||||||
#include <clang/Tooling/Core/QualTypeNames.h>
|
#include <clang/Tooling/Core/QualTypeNames.h>
|
||||||
#include <clang/Index/CodegenNameGenerator.h>
|
#include <clang/Index/CodegenNameGenerator.h>
|
||||||
|
|
||||||
#include <google/protobuf/text_format.h>
|
|
||||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -33,83 +30,64 @@ using abi_wrapper::EnumDeclWrapper;
|
|||||||
using abi_wrapper::GlobalVarDeclWrapper;
|
using abi_wrapper::GlobalVarDeclWrapper;
|
||||||
|
|
||||||
HeaderASTVisitor::HeaderASTVisitor(
|
HeaderASTVisitor::HeaderASTVisitor(
|
||||||
abi_dump::TranslationUnit *tu_ptr,
|
|
||||||
clang::MangleContext *mangle_contextp,
|
clang::MangleContext *mangle_contextp,
|
||||||
clang::ASTContext *ast_contextp,
|
clang::ASTContext *ast_contextp,
|
||||||
const clang::CompilerInstance *compiler_instance_p,
|
const clang::CompilerInstance *compiler_instance_p,
|
||||||
const std::string ¤t_file_name,
|
const std::string ¤t_file_name,
|
||||||
const std::set<std::string> &exported_headers,
|
const std::set<std::string> &exported_headers,
|
||||||
const clang::Decl *tu_decl)
|
const clang::Decl *tu_decl,
|
||||||
: tu_ptr_(tu_ptr),
|
std::set<std::string> *type_cache,
|
||||||
mangle_contextp_(mangle_contextp),
|
abi_util::IRDumper *ir_dumper)
|
||||||
|
: mangle_contextp_(mangle_contextp),
|
||||||
ast_contextp_(ast_contextp),
|
ast_contextp_(ast_contextp),
|
||||||
cip_(compiler_instance_p),
|
cip_(compiler_instance_p),
|
||||||
current_file_name_(current_file_name),
|
current_file_name_(current_file_name),
|
||||||
exported_headers_(exported_headers),
|
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) {
|
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() ||
|
if (!decl->isThisDeclarationADefinition() ||
|
||||||
decl->getTypeForDecl()->isDependentType()) {
|
decl->getTypeForDecl()->isDependentType() ||
|
||||||
|
decl->isAnonymousStructOrUnion() ||
|
||||||
|
!decl->hasNameForLinkage()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
RecordDeclWrapper record_decl_wrapper(
|
RecordDeclWrapper record_decl_wrapper(
|
||||||
mangle_contextp_, ast_contextp_, cip_, decl);
|
mangle_contextp_, ast_contextp_, cip_, decl, type_cache_,
|
||||||
std::unique_ptr<abi_dump::RecordDecl> wrapped_record_decl =
|
ir_dumper_, decl_to_source_file_cache_, "");
|
||||||
record_decl_wrapper.GetRecordDecl();
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) {
|
bool HeaderASTVisitor::VisitEnumDecl(const clang::EnumDecl *decl) {
|
||||||
if (!decl->isThisDeclarationADefinition() ||
|
if (!decl->isThisDeclarationADefinition() ||
|
||||||
decl->getTypeForDecl()->isDependentType()) {
|
decl->getTypeForDecl()->isDependentType() ||
|
||||||
|
!decl->hasNameForLinkage()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
EnumDeclWrapper enum_decl_wrapper(
|
EnumDeclWrapper enum_decl_wrapper(
|
||||||
mangle_contextp_, ast_contextp_, cip_, decl);
|
mangle_contextp_, ast_contextp_, cip_, decl, type_cache_,
|
||||||
std::unique_ptr<abi_dump::EnumDecl> wrapped_enum_decl =
|
ir_dumper_, decl_to_source_file_cache_);
|
||||||
enum_decl_wrapper.GetEnumDecl();
|
return 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool MutateFunctionWithLinkageName(abi_dump::TranslationUnit *tu_ptr,
|
static bool MutateFunctionWithLinkageName(const abi_util::FunctionIR *function,
|
||||||
const abi_dump::FunctionDecl *fd,
|
abi_util::IRDumper *ir_dumper,
|
||||||
std::string &linkage_name) {
|
std::string &linkage_name) {
|
||||||
abi_dump::FunctionDecl *added_function_declp = tu_ptr->add_functions();
|
auto added_function = std::make_unique<abi_util::FunctionIR>();
|
||||||
if (!added_function_declp) {
|
*added_function = *function;
|
||||||
return false;
|
added_function->SetLinkerSetKey(linkage_name);
|
||||||
}
|
return ir_dumper->AddLinkableMessageIR(added_function.get());
|
||||||
*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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AddMangledFunctions(abi_dump::TranslationUnit *tu_ptr,
|
static bool AddMangledFunctions(const abi_util::FunctionIR *function,
|
||||||
const abi_dump::FunctionDecl *fd,
|
abi_util:: IRDumper *ir_dumper,
|
||||||
std::vector<std::string> &manglings) {
|
std::vector<std::string> &manglings) {
|
||||||
for (auto &&mangling : manglings) {
|
for (auto &&mangling : manglings) {
|
||||||
if (!MutateFunctionWithLinkageName(tu_ptr, fd, mangling)) {
|
if (!MutateFunctionWithLinkageName(function, ir_dumper, mangling)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,23 +117,20 @@ bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
FunctionDeclWrapper function_decl_wrapper(mangle_contextp_, ast_contextp_,
|
FunctionDeclWrapper function_decl_wrapper(mangle_contextp_, ast_contextp_,
|
||||||
cip_, decl);
|
cip_, decl, type_cache_,
|
||||||
std::unique_ptr<abi_dump::FunctionDecl> wrapped_function_decl =
|
ir_dumper_,
|
||||||
function_decl_wrapper.GetFunctionDecl();
|
decl_to_source_file_cache_);
|
||||||
if (!wrapped_function_decl) {
|
auto function_wrapper = function_decl_wrapper.GetFunctionDecl();
|
||||||
llvm::errs() << "Getting Function Decl failed\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Destructors and Constructors can have more than 1 symbol generated from the
|
// Destructors and Constructors can have more than 1 symbol generated from the
|
||||||
// same Decl.
|
// same Decl.
|
||||||
clang::index::CodegenNameGenerator cg(*ast_contextp_);
|
clang::index::CodegenNameGenerator cg(*ast_contextp_);
|
||||||
std::vector<std::string> manglings = cg.getAllManglings(decl);
|
std::vector<std::string> manglings = cg.getAllManglings(decl);
|
||||||
if (!manglings.empty()) {
|
if (!manglings.empty()) {
|
||||||
return AddMangledFunctions(tu_ptr_, wrapped_function_decl.get(), manglings);
|
return AddMangledFunctions(function_wrapper.get(), ir_dumper_, manglings);
|
||||||
}
|
}
|
||||||
std::string linkage_name =
|
std::string linkage_name =
|
||||||
ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_);
|
ABIWrapper::GetMangledNameDecl(decl, mangle_contextp_);
|
||||||
return MutateFunctionWithLinkageName(tu_ptr_, wrapped_function_decl.get(),
|
return MutateFunctionWithLinkageName(function_wrapper.get(), ir_dumper_,
|
||||||
linkage_name);
|
linkage_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,19 +141,10 @@ bool HeaderASTVisitor::VisitVarDecl(const clang::VarDecl *decl) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
GlobalVarDeclWrapper global_var_decl_wrapper(mangle_contextp_, ast_contextp_,
|
GlobalVarDeclWrapper global_var_decl_wrapper(mangle_contextp_, ast_contextp_,
|
||||||
cip_, decl);
|
cip_, decl, type_cache_,
|
||||||
std::unique_ptr<abi_dump::GlobalVarDecl> wrapped_global_var_decl =
|
ir_dumper_,
|
||||||
global_var_decl_wrapper.GetGlobalVarDecl();
|
decl_to_source_file_cache_);
|
||||||
if (!wrapped_global_var_decl) {
|
return global_var_decl_wrapper.GetGlobalVarDecl();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AreHeadersExported(const std::set<std::string> &exported_headers) {
|
static bool AreHeadersExported(const std::set<std::string> &exported_headers) {
|
||||||
@@ -191,6 +157,7 @@ bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
std::string source_file = ABIWrapper::GetDeclSourceFile(decl, cip_);
|
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 no exported headers are specified we assume the whole AST is exported.
|
||||||
if ((decl != tu_decl_) && AreHeadersExported(exported_headers_) &&
|
if ((decl != tu_decl_) && AreHeadersExported(exported_headers_) &&
|
||||||
(exported_headers_.find(source_file) == exported_headers_.end())) {
|
(exported_headers_.find(source_file) == exported_headers_.end())) {
|
||||||
@@ -210,18 +177,19 @@ HeaderASTConsumer::HeaderASTConsumer(
|
|||||||
exported_headers_(exported_headers) { }
|
exported_headers_(exported_headers) { }
|
||||||
|
|
||||||
void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
|
void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) {
|
||||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
clang::PrintingPolicy policy(ctx.getPrintingPolicy());
|
||||||
std::ofstream text_output(out_dump_name_);
|
policy.SuppressTagKeyword = true;
|
||||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
ctx.setPrintingPolicy(policy);
|
||||||
clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl();
|
clang::TranslationUnitDecl *translation_unit = ctx.getTranslationUnitDecl();
|
||||||
std::unique_ptr<clang::MangleContext> mangle_contextp(
|
std::unique_ptr<clang::MangleContext> mangle_contextp(
|
||||||
ctx.createMangleContext());
|
ctx.createMangleContext());
|
||||||
abi_dump::TranslationUnit tu;
|
std::set<std::string> type_cache;
|
||||||
std::string str_out;
|
std::unique_ptr<abi_util::IRDumper> ir_dumper =
|
||||||
HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_, file_name_,
|
abi_util::IRDumper::CreateIRDumper("protobuf", out_dump_name_);
|
||||||
exported_headers_, translation_unit);
|
HeaderASTVisitor v(mangle_contextp.get(), &ctx, cip_, file_name_,
|
||||||
if (!v.TraverseDecl(translation_unit) ||
|
exported_headers_, translation_unit, &type_cache,
|
||||||
!google::protobuf::TextFormat::Print(tu, &text_os)) {
|
ir_dumper.get());
|
||||||
|
if (!v.TraverseDecl(translation_unit) || !ir_dumper->Dump()) {
|
||||||
llvm::errs() << "Serialization to ostream failed\n";
|
llvm::errs() << "Serialization to ostream failed\n";
|
||||||
::exit(1);
|
::exit(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
#include "proto/abi_dump.pb.h"
|
#include "proto/abi_dump.pb.h"
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
#include <ir_representation.h>
|
||||||
|
|
||||||
#include <clang/AST/AST.h>
|
#include <clang/AST/AST.h>
|
||||||
#include <clang/AST/ASTConsumer.h>
|
#include <clang/AST/ASTConsumer.h>
|
||||||
#include <clang/AST/Mangle.h>
|
#include <clang/AST/Mangle.h>
|
||||||
@@ -33,13 +35,14 @@
|
|||||||
class HeaderASTVisitor
|
class HeaderASTVisitor
|
||||||
: public clang::RecursiveASTVisitor<HeaderASTVisitor> {
|
: public clang::RecursiveASTVisitor<HeaderASTVisitor> {
|
||||||
public:
|
public:
|
||||||
HeaderASTVisitor(abi_dump::TranslationUnit *tu_ptr,
|
HeaderASTVisitor(clang::MangleContext *mangle_contextp,
|
||||||
clang::MangleContext *mangle_contextp,
|
|
||||||
clang::ASTContext *ast_contextp,
|
clang::ASTContext *ast_contextp,
|
||||||
const clang::CompilerInstance *compiler_instance_p,
|
const clang::CompilerInstance *compiler_instance_p,
|
||||||
const std::string ¤t_file_name,
|
const std::string ¤t_file_name,
|
||||||
const std::set<std::string> &exported_headers,
|
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);
|
bool VisitRecordDecl(const clang::RecordDecl *decl);
|
||||||
|
|
||||||
@@ -57,7 +60,6 @@ class HeaderASTVisitor
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
abi_dump::TranslationUnit *tu_ptr_;
|
|
||||||
clang::MangleContext *mangle_contextp_;
|
clang::MangleContext *mangle_contextp_;
|
||||||
clang::ASTContext *ast_contextp_;
|
clang::ASTContext *ast_contextp_;
|
||||||
const clang::CompilerInstance *cip_;
|
const clang::CompilerInstance *cip_;
|
||||||
@@ -65,6 +67,11 @@ class HeaderASTVisitor
|
|||||||
const std::set<std::string> &exported_headers_;
|
const std::set<std::string> &exported_headers_;
|
||||||
// To optimize recursion into only exported abi.
|
// To optimize recursion into only exported abi.
|
||||||
const clang::Decl *tu_decl_;
|
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 {
|
class HeaderASTConsumer : public clang::ASTConsumer {
|
||||||
@@ -83,4 +90,4 @@ class HeaderASTConsumer : public clang::ASTConsumer {
|
|||||||
std::set<std::string> exported_headers_;
|
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,
|
"no-filter", llvm::cl::desc("Do not filter any abi"), llvm::cl::Optional,
|
||||||
llvm::cl::cat(header_linker_category));
|
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(
|
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));
|
llvm::cl::cat(header_linker_category));
|
||||||
|
|
||||||
class HeaderAbiLinker {
|
class HeaderAbiLinker {
|
||||||
@@ -85,16 +91,20 @@ class HeaderAbiLinker {
|
|||||||
|
|
||||||
bool LinkAndDump();
|
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:
|
private:
|
||||||
bool LinkRecords(const abi_dump::TranslationUnit &dump_tu,
|
bool LinkTypes(const abi_dump::TranslationUnit &dump_tu,
|
||||||
abi_dump::TranslationUnit *linked_tu);
|
abi_dump::TranslationUnit *linked_tu);
|
||||||
|
|
||||||
bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
|
bool LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
|
||||||
abi_dump::TranslationUnit *linked_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,
|
bool LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
|
||||||
abi_dump::TranslationUnit *linked_tu);
|
abi_dump::TranslationUnit *linked_tu);
|
||||||
|
|
||||||
@@ -110,6 +120,8 @@ class HeaderAbiLinker {
|
|||||||
|
|
||||||
bool ParseSoFile();
|
bool ParseSoFile();
|
||||||
|
|
||||||
|
bool AddElfSymbols(abi_dump::TranslationUnit *linked_tu);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::vector<std::string> &dump_files_;
|
const std::vector<std::string> &dump_files_;
|
||||||
const std::vector<std::string> &exported_header_dirs_;
|
const std::vector<std::string> &exported_header_dirs_;
|
||||||
@@ -120,9 +132,8 @@ class HeaderAbiLinker {
|
|||||||
const std::string &api_;
|
const std::string &api_;
|
||||||
// TODO: Add to a map of std::sets instead.
|
// TODO: Add to a map of std::sets instead.
|
||||||
std::set<std::string> exported_headers_;
|
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> function_decl_set_;
|
||||||
std::set<std::string> enum_decl_set_;
|
|
||||||
std::set<std::string> globvar_decl_set_;
|
std::set<std::string> globvar_decl_set_;
|
||||||
// Version Script Regex Matching.
|
// Version Script Regex Matching.
|
||||||
std::set<std::string> functions_regex_matched_set;
|
std::set<std::string> functions_regex_matched_set;
|
||||||
@@ -132,12 +143,32 @@ class HeaderAbiLinker {
|
|||||||
std::regex globvars_vs_regex_;
|
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() {
|
bool HeaderAbiLinker::LinkAndDump() {
|
||||||
abi_dump::TranslationUnit linked_tu;
|
abi_dump::TranslationUnit linked_tu;
|
||||||
std::ofstream text_output(out_dump_name_);
|
std::ofstream text_output(out_dump_name_);
|
||||||
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
google::protobuf::io::OstreamOutputStream text_os(&text_output);
|
||||||
// If a version script is available, we use that as a filter.
|
// If the user specifies that a version script should be used, use that.
|
||||||
if (version_script.empty()) {
|
if (!use_version_script) {
|
||||||
exported_headers_ =
|
exported_headers_ =
|
||||||
abi_util::CollectAllExportedHeaders(exported_header_dirs_);
|
abi_util::CollectAllExportedHeaders(exported_header_dirs_);
|
||||||
if (!ParseSoFile()) {
|
if (!ParseSoFile()) {
|
||||||
@@ -149,20 +180,20 @@ bool HeaderAbiLinker::LinkAndDump() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AddElfSymbols(&linked_tu);
|
||||||
|
|
||||||
for (auto &&i : dump_files_) {
|
for (auto &&i : dump_files_) {
|
||||||
abi_dump::TranslationUnit dump_tu;
|
abi_dump::TranslationUnit dump_tu;
|
||||||
std::ifstream input(i);
|
std::ifstream input(i);
|
||||||
google::protobuf::io::IstreamInputStream text_is(&input);
|
google::protobuf::io::IstreamInputStream text_is(&input);
|
||||||
if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) ||
|
if (!google::protobuf::TextFormat::Parse(&text_is, &dump_tu) ||
|
||||||
!LinkRecords(dump_tu, &linked_tu) ||
|
!LinkTypes(dump_tu, &linked_tu) ||
|
||||||
!LinkFunctions(dump_tu, &linked_tu) ||
|
!LinkFunctions(dump_tu, &linked_tu) ||
|
||||||
!LinkEnums(dump_tu, &linked_tu) ||
|
|
||||||
!LinkGlobalVars(dump_tu, &linked_tu)) {
|
!LinkGlobalVars(dump_tu, &linked_tu)) {
|
||||||
llvm::errs() << "Failed to link elements\n";
|
llvm::errs() << "Failed to link elements\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) {
|
if (!google::protobuf::TextFormat::Print(linked_tu, &text_os)) {
|
||||||
llvm::errs() << "Serialization to ostream failed\n";
|
llvm::errs() << "Serialization to ostream failed\n";
|
||||||
return false;
|
return false;
|
||||||
@@ -170,22 +201,6 @@ bool HeaderAbiLinker::LinkAndDump() {
|
|||||||
return true;
|
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,
|
static bool QueryRegexMatches(std::set<std::string> *regex_matched_link_set,
|
||||||
const std::regex *vs_regex,
|
const std::regex *vs_regex,
|
||||||
const std::string &symbol) {
|
const std::string &symbol) {
|
||||||
@@ -219,10 +234,10 @@ static std::regex CreateRegexMatchExprFromSet(
|
|||||||
return std::regex(all_regex_match_str);
|
return std::regex(all_regex_match_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: make linking decls multi-threaded b/63590537.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline bool HeaderAbiLinker::LinkDecl(
|
inline bool HeaderAbiLinker::LinkDecl(
|
||||||
google::protobuf::RepeatedPtrField<T> *dst,
|
google::protobuf::RepeatedPtrField<T> *dst, std::set<std::string> *link_set,
|
||||||
std::set<std::string> *link_set,
|
|
||||||
std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
|
std::set<std::string> *regex_matched_link_set, const std::regex *vs_regex,
|
||||||
const google::protobuf::RepeatedPtrField<T> &src, bool use_version_script) {
|
const google::protobuf::RepeatedPtrField<T> &src, bool use_version_script) {
|
||||||
assert(dst != nullptr);
|
assert(dst != nullptr);
|
||||||
@@ -230,18 +245,20 @@ inline bool HeaderAbiLinker::LinkDecl(
|
|||||||
for (auto &&element : src) {
|
for (auto &&element : src) {
|
||||||
// If we are not using a version script and exported headers are available,
|
// If we are not using a version script and exported headers are available,
|
||||||
// filter out unexported abi.
|
// filter out unexported abi.
|
||||||
if (!exported_headers_.empty() &&
|
std::string source_file = GetSourceFile(element);
|
||||||
exported_headers_.find(element.source_file()) ==
|
// Builtin types will not have source file information.
|
||||||
|
if (!exported_headers_.empty() && !source_file.empty() &&
|
||||||
|
exported_headers_.find(source_file) ==
|
||||||
exported_headers_.end()) {
|
exported_headers_.end()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
std::string element_str = GetLinkageName(element);
|
||||||
// Check for the existence of the element in linked dump / symbol file.
|
// Check for the existence of the element in linked dump / symbol file.
|
||||||
if (!use_version_script) {
|
if (!use_version_script) {
|
||||||
if (!link_set->insert(element.basic_abi().linker_set_key()).second) {
|
if (!link_set->insert(element_str).second) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::string element_str = GetSymbol(element);
|
|
||||||
std::set<std::string>::iterator it =
|
std::set<std::string>::iterator it =
|
||||||
link_set->find(element_str);
|
link_set->find(element_str);
|
||||||
if (it == link_set->end()) {
|
if (it == link_set->end()) {
|
||||||
@@ -263,13 +280,52 @@ inline bool HeaderAbiLinker::LinkDecl(
|
|||||||
return true;
|
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);
|
assert(linked_tu != nullptr);
|
||||||
// Even if version scripts are available we take in records, since the symbols
|
// Even if version scripts are available we take in types, since the symbols
|
||||||
// in the version script might reference a record exposed by the library.
|
// in the version script might reference a type exposed by the library.
|
||||||
return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, nullptr,
|
return LinkDecl(linked_tu->mutable_record_types(), &types_set_, nullptr,
|
||||||
nullptr, dump_tu.records(), false);
|
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,
|
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()));
|
(!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,
|
bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
|
||||||
abi_dump::TranslationUnit *linked_tu) {
|
abi_dump::TranslationUnit *linked_tu) {
|
||||||
assert(linked_tu != nullptr);
|
assert(linked_tu != nullptr);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include <llvm/Support/Endian.h>
|
#include <llvm/Support/Endian.h>
|
||||||
#include <llvm/Support/raw_ostream.h>
|
#include <llvm/Support/raw_ostream.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -128,4 +129,60 @@ class ELFSoFileParser : public SoFileParser {
|
|||||||
bool IsSymbolExported(const Elf_Sym *elf_sym) const;
|
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
|
} // 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;
|
package abi_diff;
|
||||||
|
|
||||||
message RecordFieldDeclDiff {
|
message TypeInfo {
|
||||||
optional abi_dump.RecordFieldDecl old = 1;
|
optional uint64 size = 1;
|
||||||
optional abi_dump.RecordFieldDecl new = 2;
|
optional uint32 alignment = 2;
|
||||||
optional uint32 index = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message EnumFieldDeclDiff {
|
message TypeInfoDiff {
|
||||||
optional abi_dump.EnumFieldDecl old = 1;
|
optional TypeInfo old_type_info = 1;
|
||||||
optional abi_dump.EnumFieldDecl new = 2;
|
optional TypeInfo new_type_info = 2;
|
||||||
optional uint32 index = 3;
|
}
|
||||||
|
|
||||||
|
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 {
|
message CXXBaseSpecifierDiff {
|
||||||
optional abi_dump.CXXBaseSpecifier old = 1;
|
repeated abi_dump.CXXBaseSpecifier old_bases = 1;
|
||||||
optional abi_dump.CXXBaseSpecifier new = 2;
|
repeated abi_dump.CXXBaseSpecifier new_bases = 2;
|
||||||
optional uint32 index = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message CXXVTableDiff {
|
message RecordTypeDiff {
|
||||||
optional abi_dump.VTableComponent old = 1;
|
optional string name = 1;
|
||||||
optional abi_dump.VTableComponent new = 2;
|
optional string type_stack = 2;
|
||||||
optional uint32 index = 3;
|
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 {
|
message UnderlyingTypeDiff {
|
||||||
optional abi_dump.BasicNamedAndTypedDecl old = 1;
|
optional string old_type = 1;
|
||||||
optional abi_dump.BasicNamedAndTypedDecl new = 2;
|
optional string new_type = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RecordDeclDiff {
|
message EnumFieldDeclDiff {
|
||||||
repeated RecordFieldDeclDiff field_diffs = 1;
|
optional abi_dump.EnumFieldDecl old_field = 1;
|
||||||
repeated CXXBaseSpecifierDiff base_diffs = 2;
|
optional abi_dump.EnumFieldDecl new_field = 2;
|
||||||
repeated CXXVTableDiff vtable_diffs = 3;
|
|
||||||
optional BasicNamedAndTypedDeclDiff type_diff = 4;
|
|
||||||
optional string name = 5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message EnumDeclDiff {
|
message EnumTypeDiff {
|
||||||
repeated EnumFieldDeclDiff field_diffs = 1;
|
optional string type_stack = 1;
|
||||||
optional BasicNamedAndTypedDeclDiff type_diff = 2;
|
optional string name = 2;
|
||||||
optional string name = 3;
|
repeated EnumFieldDeclDiff fields_diff = 3;
|
||||||
}
|
optional UnderlyingTypeDiff underlying_type_diff = 4;
|
||||||
|
repeated abi_dump.EnumFieldDecl fields_added = 5;
|
||||||
message ReturnTypeDiff {
|
repeated abi_dump.EnumFieldDecl fields_removed = 6;
|
||||||
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 FunctionDeclDiff {
|
message FunctionDeclDiff {
|
||||||
optional ReturnTypeDiff return_type_diffs = 1;
|
// Template diffs are not required since in C++, they will show up in mangled
|
||||||
repeated ParamDeclDiff param_diffs = 2;
|
// names and in C, templates are not supported.
|
||||||
optional string name = 3;
|
optional string type_stack = 1;
|
||||||
|
optional string name = 2;
|
||||||
|
optional abi_dump.FunctionDecl old = 3;
|
||||||
|
optional abi_dump.FunctionDecl new = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GlobalVarDeclDiff {
|
message GlobalVarDeclDiff {
|
||||||
optional BasicNamedAndTypedDeclDiff type_diff = 1;
|
optional string type_stack = 1;
|
||||||
}
|
optional string name = 2;
|
||||||
|
optional abi_dump.GlobalVarDecl old = 3; // Old global var
|
||||||
enum CompatibilityStatus {
|
optional abi_dump.GlobalVarDecl new = 4; // New global var
|
||||||
COMPATIBLE = 0;
|
|
||||||
EXTENSION = 1;
|
|
||||||
INCOMPATIBLE = 4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TranslationUnitDiff {
|
message TranslationUnitDiff {
|
||||||
// Library Name
|
// Library Name
|
||||||
optional string lib_name = 1;
|
optional string lib_name = 1;
|
||||||
optional string arch = 2;
|
optional string arch = 2;
|
||||||
// Differing Elements.
|
// Records.
|
||||||
repeated RecordDeclDiff records_diff = 3;
|
repeated RecordTypeDiff record_type_diffs = 3;
|
||||||
repeated EnumDeclDiff enums_diff = 4;
|
repeated RecordTypeDiff unreferenced_record_type_diffs = 4;
|
||||||
repeated FunctionDeclDiff functions_diff = 5;
|
repeated abi_dump.RecordType unreferenced_record_types_removed = 5;
|
||||||
repeated GlobalVarDeclDiff global_vars_diff = 6;
|
repeated abi_dump.RecordType unreferenced_record_types_added = 6;
|
||||||
// Removed Elements.
|
|
||||||
repeated abi_dump.RecordDecl records_removed = 7;
|
// Enums
|
||||||
repeated abi_dump.FunctionDecl functions_removed = 8;
|
repeated EnumTypeDiff enum_type_diffs = 7;
|
||||||
repeated abi_dump.EnumDecl enums_removed = 9;
|
repeated EnumTypeDiff enum_type_extension_diffs = 8;
|
||||||
repeated abi_dump.GlobalVarDecl global_vars_removed = 10;
|
repeated EnumTypeDiff unreferenced_enum_type_diffs = 9;
|
||||||
// Added Elements.
|
repeated EnumTypeDiff unreferenced_enum_type_extension_diffs = 10;
|
||||||
repeated abi_dump.RecordDecl records_added = 11;
|
repeated abi_dump.EnumType unreferenced_enum_types_removed = 11;
|
||||||
repeated abi_dump.FunctionDecl functions_added = 12;
|
repeated abi_dump.EnumType unreferenced_enum_types_added = 12;
|
||||||
repeated abi_dump.EnumDecl enums_added = 13;
|
|
||||||
repeated abi_dump.GlobalVarDecl global_vars_added = 14;
|
// 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
|
// Compatiblity Status
|
||||||
optional CompatibilityStatus compatibility_status = 15;
|
optional CompatibilityStatus compatibility_status = 23;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not merged with TranslationUnitDiff to allow future extensions.
|
// Not merged with TranslationUnitDiff to allow future extensions.
|
||||||
@@ -108,3 +123,9 @@ message ConciseDiffReportInformation {
|
|||||||
message MergedTranslationUnitDiff {
|
message MergedTranslationUnitDiff {
|
||||||
repeated ConciseDiffReportInformation diff_reports = 1;
|
repeated ConciseDiffReportInformation diff_reports = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CompatibilityStatus {
|
||||||
|
COMPATIBLE = 0;
|
||||||
|
EXTENSION = 1;
|
||||||
|
INCOMPATIBLE = 4;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,50 +2,84 @@ syntax = "proto2";
|
|||||||
|
|
||||||
package abi_dump;
|
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 {
|
enum AccessSpecifier {
|
||||||
public_access = 1;
|
public_access = 1;
|
||||||
private_access = 2;
|
private_access = 2;
|
||||||
protected_access = 3;
|
protected_access = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum RecordKind {
|
||||||
|
struct_kind = 1;
|
||||||
|
class_kind = 2;
|
||||||
|
union_kind = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message BasicNamedAndTypedDecl {
|
message BasicNamedAndTypedDecl {
|
||||||
optional BasicTypeAbi type_abi = 1;
|
|
||||||
// The TypedDecl's name.
|
// The TypedDecl's name.
|
||||||
optional string name = 2;
|
optional string name = 1;
|
||||||
optional AccessSpecifier access = 3;
|
optional uint64 size = 2 [default = 0];
|
||||||
optional string linker_set_key = 4;
|
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 {
|
message FunctionDecl {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
// Return type reference
|
||||||
// Mangled name.
|
optional string return_type = 1;
|
||||||
optional string mangled_function_name = 2;
|
optional string function_name = 2;
|
||||||
optional string source_file = 3;
|
optional string source_file = 3;
|
||||||
repeated ParamDecl parameters = 4;
|
repeated ParamDecl parameters = 4;
|
||||||
optional TemplateInfo template_info = 5;
|
optional TemplateInfo template_info = 5;
|
||||||
|
optional string linker_set_key = 6;
|
||||||
|
optional AccessSpecifier access = 7 [default = public_access];
|
||||||
}
|
}
|
||||||
|
|
||||||
message ParamDecl {
|
message ParamDecl {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
optional string referenced_type = 1;
|
||||||
optional bool default_arg = 2;
|
optional bool default_arg = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RecordFieldDecl {
|
message RecordFieldDecl {
|
||||||
// For future additions.
|
// 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 {
|
message EnumFieldDecl {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
optional int64 enum_field_value = 1; // assumption: fits int64
|
||||||
optional int64 enum_field_value = 2; // assumption: fits int64
|
optional string name = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message TemplateInfo {
|
message TemplateInfo {
|
||||||
@@ -53,17 +87,13 @@ message TemplateInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message TemplateElement {
|
message TemplateElement {
|
||||||
optional BasicTemplateElementAbi basic_abi = 1;
|
optional string referenced_type = 1;
|
||||||
message BasicTemplateElementAbi {
|
|
||||||
optional BasicTypeAbi type_abi = 1;
|
|
||||||
optional string name = 2;
|
|
||||||
optional string linker_set_key = 3;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message CXXBaseSpecifier {
|
message CXXBaseSpecifier {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
optional string referenced_type = 1;
|
||||||
optional bool is_virtual = 2;
|
optional bool is_virtual = 2;
|
||||||
|
optional AccessSpecifier access = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VTableComponent {
|
message VTableComponent {
|
||||||
@@ -89,30 +119,51 @@ message VTableLayout {
|
|||||||
repeated VTableComponent vtable_components = 1;
|
repeated VTableComponent vtable_components = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RecordDecl {
|
message RecordType {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
optional BasicNamedAndTypedDecl type_info = 1;
|
||||||
repeated RecordFieldDecl fields = 2;
|
repeated RecordFieldDecl fields = 2;
|
||||||
repeated CXXBaseSpecifier base_specifiers = 3;
|
repeated CXXBaseSpecifier base_specifiers = 3;
|
||||||
optional string source_file = 4;
|
|
||||||
optional TemplateInfo template_info = 5;
|
optional TemplateInfo template_info = 5;
|
||||||
optional string mangled_record_name = 6;
|
|
||||||
optional VTableLayout vtable_layout = 7;
|
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 {
|
message EnumType {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
optional BasicNamedAndTypedDecl type_info = 1;
|
||||||
repeated EnumFieldDecl enum_fields = 2;
|
optional string underlying_type = 2;
|
||||||
optional string source_file = 3;
|
repeated EnumFieldDecl enum_fields = 3;
|
||||||
|
optional AccessSpecifier access = 4 [default = public_access];
|
||||||
}
|
}
|
||||||
|
|
||||||
message GlobalVarDecl {
|
message GlobalVarDecl {
|
||||||
optional BasicNamedAndTypedDecl basic_abi = 1;
|
optional string name = 1;
|
||||||
optional string source_file = 2;
|
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 {
|
message TranslationUnit {
|
||||||
repeated RecordDecl records = 1;
|
repeated RecordType record_types = 1;
|
||||||
repeated FunctionDecl functions = 2;
|
repeated EnumType enum_types = 2;
|
||||||
repeated EnumDecl enums = 3;
|
repeated PointerType pointer_types = 3;
|
||||||
repeated GlobalVarDecl global_vars = 4;
|
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" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct ForwardDeclaration;
|
||||||
|
int uses_forward_decl(struct ForwardDeclaration *);
|
||||||
|
|
||||||
struct Hello {
|
struct Hello {
|
||||||
int foo;
|
int foo;
|
||||||
int bar;
|
int bar;
|
||||||
|
enum {A, B} enum_field;
|
||||||
|
enum {C, D} enum_field2;
|
||||||
|
struct {
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
struct {
|
||||||
|
int c;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
@@ -24,8 +36,17 @@ struct CPPHello : private HelloAgain, public ByeAgain<float_type> {
|
|||||||
cfloat_type cpp_bar;
|
cfloat_type cpp_bar;
|
||||||
virtual int again() { return 0; }
|
virtual int again() { return 0; }
|
||||||
CPPHello() : cpp_foo(20), cpp_bar(1.234) { }
|
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>
|
template<typename T>
|
||||||
struct StackNode {
|
struct StackNode {
|
||||||
public:
|
public:
|
||||||
@@ -63,7 +84,7 @@ public:
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class List
|
class List
|
||||||
{
|
{
|
||||||
protected:
|
public:
|
||||||
/*
|
/*
|
||||||
* One element in the list.
|
* One element in the list.
|
||||||
*/
|
*/
|
||||||
@@ -74,6 +95,7 @@ protected:
|
|||||||
inline T& getRef() { return mVal; }
|
inline T& getRef() { return mVal; }
|
||||||
inline const T& getRef() const { return mVal; }
|
inline const T& getRef() const { return mVal; }
|
||||||
private:
|
private:
|
||||||
|
void PrivateNode();
|
||||||
friend class List;
|
friend class List;
|
||||||
friend class _ListIterator;
|
friend class _ListIterator;
|
||||||
T mVal;
|
T mVal;
|
||||||
@@ -83,11 +105,20 @@ protected:
|
|||||||
_Node *middle;
|
_Node *middle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef List<float> float_list;
|
typedef List<float> float_list;
|
||||||
float_list float_list_test;
|
float_list float_list_test;
|
||||||
|
|
||||||
|
|
||||||
typedef List<int> int_list;
|
typedef List<int> int_list;
|
||||||
int_list int_list_test;
|
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_
|
#endif // EXAMPLE1_H_
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ struct HelloAgain {
|
|||||||
int bar_again;
|
int bar_again;
|
||||||
static int hello_forever;
|
static int hello_forever;
|
||||||
virtual int again();
|
virtual int again();
|
||||||
|
virtual ~HelloAgain() {}
|
||||||
};
|
};
|
||||||
struct NowWeCrash;
|
struct NowWeCrash;
|
||||||
} // namespace test2
|
} // namespace test2
|
||||||
|
|||||||
Reference in New Issue
Block a user