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