• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "abi_diff_wrappers.h"
16 
17 #include<header_abi_util.h>
18 
19 #pragma clang diagnostic push
20 #pragma clang diagnostic ignored "-Wunused-parameter"
21 #pragma clang diagnostic ignored "-Wnested-anon-types"
22 #include "proto/abi_dump.pb.h"
23 #include "proto/abi_diff.pb.h"
24 #pragma clang diagnostic pop
25 
26 #include <llvm/Support/raw_ostream.h>
27 
28 using abi_diff::RecordDeclDiff;
29 using abi_diff::RecordFieldDeclDiff;
30 using abi_diff::CXXBaseSpecifierDiff;
31 using abi_diff::CXXVTableDiff;
32 using abi_diff::EnumDeclDiff;
33 using abi_diff::ReturnTypeDiff;
34 using abi_diff::ParamDeclDiff;
35 using abi_diff::FunctionDeclDiff;
36 using abi_diff::EnumDeclDiff;
37 using abi_diff::EnumFieldDeclDiff;
38 using abi_diff::GlobalVarDeclDiff;
39 using abi_dump::RecordDecl;
40 using abi_dump::RecordFieldDecl;
41 using abi_dump::EnumDecl;
42 using abi_dump::EnumFieldDecl;
43 using abi_dump::FunctionDecl;
44 using abi_dump::ParamDecl;
45 using abi_dump::VTableComponent;
46 using abi_dump::CXXBaseSpecifier;
47 using abi_dump::GlobalVarDecl;
48 using abi_dump::BasicNamedAndTypedDecl;
49 
50 namespace abi_diff_wrappers {
51 
IsAccessDownGraded(abi_dump::AccessSpecifier old_access,abi_dump::AccessSpecifier new_access)52 static bool IsAccessDownGraded(abi_dump::AccessSpecifier old_access,
53                                abi_dump::AccessSpecifier new_access) {
54   bool access_downgraded = false;
55   switch (old_access) {
56     case abi_dump::AccessSpecifier::protected_access:
57       if (new_access == abi_dump::AccessSpecifier::private_access) {
58         access_downgraded = true;
59       }
60       break;
61     case abi_dump::AccessSpecifier::public_access:
62       if (new_access != abi_dump::AccessSpecifier::public_access) {
63         access_downgraded = true;
64       }
65       break;
66     default:
67       break;
68   }
69   return access_downgraded;
70 }
71 
CpptoCAdjustment(const std::string & type)72 static std::string CpptoCAdjustment(const std::string &type) {
73   std::string adjusted_type_name =
74       abi_util::FindAndReplace(type, "\\bstruct ", "");
75 
76   return adjusted_type_name;
77 }
78 
CompareTypeNames(const abi_dump::BasicTypeAbi & old_abi,const abi_dump::BasicTypeAbi & new_abi)79 static bool CompareTypeNames(const abi_dump::BasicTypeAbi &old_abi,
80                              const abi_dump::BasicTypeAbi &new_abi) {
81   // Strip of leading 'struct' keyword from type names
82   std::string old_type = old_abi.name();
83   std::string new_type = new_abi.name();
84   old_type = CpptoCAdjustment(old_type);
85   new_type = CpptoCAdjustment(new_type);
86   // TODO: Add checks for C++ built-in types vs C corresponding types.
87   return old_type != new_type;
88 }
89 
DiffBasicTypeAbi(const abi_dump::BasicTypeAbi & old_abi,const abi_dump::BasicTypeAbi & new_abi)90 static bool DiffBasicTypeAbi(const abi_dump::BasicTypeAbi &old_abi,
91                              const abi_dump::BasicTypeAbi &new_abi) {
92   // We need to add a layer of indirection to account for issues when C and C++
93   // are mixed. For example some types like wchar_t are in-built types for C++
94   // but not for C. Another example would be clang reporting C structures
95   // without the leading "struct" keyword when headers defining them are
96   // included in C++ files.
97   bool name_comparison = CompareTypeNames(old_abi, new_abi);
98   bool size_comparison = (old_abi.size() != new_abi.size());
99   bool alignment_comparison = (old_abi.alignment() != new_abi.alignment());
100   return name_comparison || size_comparison || alignment_comparison;
101 }
102 
103 template <typename T>
Diff(const T & old_element,const T & new_element)104 static bool Diff(const T &old_element, const T &new_element) {
105   // Can be specialized for future changes in the format.
106   return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
107                           new_element.basic_abi().type_abi()) ||
108       (old_element.basic_abi().name() != new_element.basic_abi().name()) ||
109       IsAccessDownGraded(old_element.basic_abi().access(),
110                          new_element.basic_abi().access());
111 }
112 
113 template <>
Diff(const EnumFieldDecl & old_element,const EnumFieldDecl & new_element)114 bool Diff<EnumFieldDecl>(const EnumFieldDecl &old_element,
115                          const EnumFieldDecl &new_element) {
116   // Can be specialized for future changes in the format.
117   return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
118                           new_element.basic_abi().type_abi()) ||
119       (old_element.enum_field_value() != new_element.enum_field_value()) ||
120       (old_element.basic_abi().name() != new_element.basic_abi().name());
121 }
122 
123 template <>
Diff(const ParamDecl & old_element,const ParamDecl & new_element)124 bool Diff<ParamDecl>(const ParamDecl &old_element,
125                      const ParamDecl &new_element) {
126   // Can be specialized for future changes in the format.
127   return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
128                           new_element.basic_abi().type_abi());
129 }
130 
131 template <>
Diff(const CXXBaseSpecifier & old_element,const CXXBaseSpecifier & new_element)132 bool Diff<CXXBaseSpecifier>(const CXXBaseSpecifier &old_element,
133                             const CXXBaseSpecifier &new_element) {
134   // Can be specialized for future changes in the format.
135   return (DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
136                            new_element.basic_abi().type_abi()) ||
137       old_element.basic_abi().access() != new_element.basic_abi().access() ||
138       old_element.is_virtual() != new_element.is_virtual());
139 }
140 
141 template <>
Diff(const VTableComponent & old_element,const VTableComponent & new_element)142 bool Diff<VTableComponent>(const VTableComponent &old_element,
143                            const VTableComponent &new_element) {
144   bool kind_comparison = old_element.kind() != new_element.kind();
145   bool mangled_name_comparison = old_element.mangled_component_name() !=
146       new_element.mangled_component_name();
147   bool value_comparison = old_element.value() != new_element.value();
148   return kind_comparison || mangled_name_comparison || value_comparison;
149 }
150 
151 // This function fills in a *Diff Message's repeated field. For eg:
152 // RecordDeclDiff's CXXBaseSpecifierDiff fields and well as FieldDeclDiff
153 // fields.
154 template <typename T, typename TDiff>
155 template <typename Element, typename ElementDiff>
GetElementDiffs(google::protobuf::RepeatedPtrField<ElementDiff> * dst,const google::protobuf::RepeatedPtrField<Element> & old_elements,const google::protobuf::RepeatedPtrField<Element> & new_elements)156 bool DiffWrapperBase<T, TDiff>::GetElementDiffs(
157     google::protobuf::RepeatedPtrField<ElementDiff> *dst,
158     const google::protobuf::RepeatedPtrField<Element> &old_elements,
159     const google::protobuf::RepeatedPtrField<Element> &new_elements) {
160   bool differs = false;
161   assert(dst != nullptr);
162   int i = 0;
163   int j = 0;
164   while (i < old_elements.size() && j < new_elements.size()) {
165     const Element &old_element = old_elements.Get(i);
166     const Element &new_element = new_elements.Get(i);
167 
168     if (Diff(old_element, new_element)) {
169       ElementDiff *diff = dst->Add();
170       Element *old_elementp = nullptr;
171       Element *new_elementp = nullptr;
172       if (!diff || !(old_elementp = diff->mutable_old()) ||
173           !(new_elementp = diff->mutable_new_())) {
174         llvm::errs() << "Failed to add diff element\n";
175         ::exit(1);
176       }
177       *old_elementp = old_element;
178       *new_elementp = new_element;
179       diff->set_index(i);
180       differs = true;
181     }
182     i++;
183     j++;
184   }
185   if (old_elements.size() != new_elements.size()) {
186     GetExtraElementDiffs(dst, i, j, old_elements, new_elements);
187     differs = true;
188   }
189   return differs;
190 }
191 
192 template <typename T, typename TDiff>
193 template <typename Element, typename ElementDiff>
GetExtraElementDiffs(google::protobuf::RepeatedPtrField<ElementDiff> * dst,int i,int j,const google::protobuf::RepeatedPtrField<Element> & old_elements,const google::protobuf::RepeatedPtrField<Element> & new_elements)194 void DiffWrapperBase<T, TDiff>::GetExtraElementDiffs(
195     google::protobuf::RepeatedPtrField<ElementDiff> *dst, int i, int j,
196     const google::protobuf::RepeatedPtrField<Element> &old_elements,
197     const google::protobuf::RepeatedPtrField<Element> &new_elements) {
198  assert(dst != nullptr);
199  while (i < old_elements.size()) {
200     const Element &old_element = old_elements.Get(i);
201     ElementDiff *diff = dst->Add();
202     Element *old_elementp = nullptr;
203     if (!diff || !(old_elementp = diff->mutable_old())) {
204       llvm::errs() << "Failed to add diff element\n";
205       ::exit(1);
206     }
207     *old_elementp = old_element;
208     diff->set_index(i);
209     i++;
210  }
211  while (j < new_elements.size()) {
212     const Element &new_element = new_elements.Get(j);
213     ElementDiff *diff = dst->Add();
214     Element *new_elementp = nullptr;
215     if (!diff || !(new_elementp = diff->mutable_new_())) {
216       llvm::errs() << "Failed to add diff element\n";
217       ::exit(1);
218     }
219     *new_elementp = new_element;
220     diff->set_index(j);
221     j++;
222  }
223 }
224 
DiffBasicNamedAndTypedDecl(BasicNamedAndTypedDecl * type_diff_old,BasicNamedAndTypedDecl * type_diff_new,const BasicNamedAndTypedDecl & old,const BasicNamedAndTypedDecl & new_)225 static bool DiffBasicNamedAndTypedDecl(BasicNamedAndTypedDecl *type_diff_old,
226                                        BasicNamedAndTypedDecl *type_diff_new,
227                                        const BasicNamedAndTypedDecl &old,
228                                        const BasicNamedAndTypedDecl &new_) {
229   assert(type_diff_old != nullptr);
230   assert(type_diff_new != nullptr);
231   if (DiffBasicTypeAbi(old.type_abi(), new_.type_abi()) ||
232       IsAccessDownGraded(old.access(), new_.access())) {
233     *(type_diff_old) = old;
234     *(type_diff_new) = new_;
235     return true;
236   }
237   return false;
238 }
239 
240 template <>
241 std::unique_ptr<RecordDeclDiff>
Get()242 DiffWrapper<RecordDecl, RecordDeclDiff>::Get() {
243   std::unique_ptr<RecordDeclDiff> record_diff(new RecordDeclDiff());
244   assert(oldp_->mangled_record_name() ==
245          newp_->mangled_record_name());
246   record_diff->set_name(oldp_->basic_abi().name());
247   google::protobuf::RepeatedPtrField<RecordFieldDeclDiff> *fdiffs =
248       record_diff->mutable_field_diffs();
249   google::protobuf::RepeatedPtrField<CXXBaseSpecifierDiff> *bdiffs =
250       record_diff->mutable_base_diffs();
251   google::protobuf::RepeatedPtrField<CXXVTableDiff> *vtdiffs =
252       record_diff->mutable_vtable_diffs();
253   assert(fdiffs != nullptr && bdiffs != nullptr);
254   // Template Information isn't diffed since the linker_set_key includes the
255   // mangled name which includes template information.
256   if (GetElementDiffs(fdiffs, oldp_->fields(), newp_->fields()) ||
257       GetElementDiffs(bdiffs, oldp_->base_specifiers(),
258                       newp_->base_specifiers()) ||
259       GetElementDiffs(vtdiffs, oldp_->vtable_layout().vtable_components(),
260                       newp_->vtable_layout().vtable_components()) ||
261       DiffBasicNamedAndTypedDecl(
262           record_diff->mutable_type_diff()->mutable_old(),
263           record_diff->mutable_type_diff()->mutable_new_(),
264           oldp_->basic_abi(), newp_->basic_abi())) {
265     return record_diff;
266   }
267   return nullptr;
268 }
269 
270 template <>
271 std::unique_ptr<EnumDeclDiff>
Get()272 DiffWrapper<EnumDecl, EnumDeclDiff>::Get() {
273   std::unique_ptr<EnumDeclDiff> enum_diff(new EnumDeclDiff());
274   assert(oldp_->basic_abi().linker_set_key() ==
275          newp_->basic_abi().linker_set_key());
276   google::protobuf::RepeatedPtrField<EnumFieldDeclDiff> *fdiffs =
277       enum_diff->mutable_field_diffs();
278   assert(fdiffs != nullptr);
279   enum_diff->set_name(oldp_->basic_abi().name());
280   if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) ||
281       DiffBasicNamedAndTypedDecl(
282           enum_diff->mutable_type_diff()->mutable_old(),
283           enum_diff->mutable_type_diff()->mutable_new_(),
284           oldp_->basic_abi(), newp_->basic_abi())) {
285     return enum_diff;
286   }
287   return nullptr;
288 }
289 
290 template <>
291 std::unique_ptr<FunctionDeclDiff>
Get()292 DiffWrapper<FunctionDecl, FunctionDeclDiff>::Get() {
293   std::unique_ptr<FunctionDeclDiff> func_diff(new FunctionDeclDiff());
294   google::protobuf::RepeatedPtrField<ParamDeclDiff> *pdiffs =
295       func_diff->mutable_param_diffs();
296   assert(func_diff->mutable_return_type_diffs() != nullptr);
297   func_diff->set_name(oldp_->basic_abi().linker_set_key());
298   if (DiffBasicNamedAndTypedDecl(
299           func_diff->mutable_return_type_diffs()->mutable_old(),
300           func_diff->mutable_return_type_diffs()->mutable_new_(),
301           oldp_->basic_abi(), newp_->basic_abi()) ||
302       GetElementDiffs(pdiffs, oldp_->parameters(), newp_->parameters())) {
303     return func_diff;
304   }
305   return nullptr;
306 }
307 
308 template <>
309 std::unique_ptr<GlobalVarDeclDiff>
Get()310 DiffWrapper<GlobalVarDecl, GlobalVarDeclDiff>::Get() {
311   std::unique_ptr<GlobalVarDeclDiff> global_var_diff(new GlobalVarDeclDiff());
312   assert(global_var_diff->mutable_type_diff() != nullptr);
313   if (DiffBasicNamedAndTypedDecl(
314           global_var_diff->mutable_type_diff()->mutable_old(),
315           global_var_diff->mutable_type_diff()->mutable_new_(),
316           oldp_->basic_abi(), newp_->basic_abi())) {
317     return global_var_diff;
318   }
319   return nullptr;
320 }
321 
322 } // abi_diff_wrappers
323