blob: afcbd5e1a277a6ccea38417d4233db0a1eb160c9 [file] [log] [blame]
Ben Craig262cfb42015-07-08 20:37:15 -05001
2#include <cassert>
3#include <stdlib.h>
4#include <stdio.h>
5#include <stdarg.h>
6#include <time.h>
7#include <string>
8#include <algorithm>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <errno.h>
12#include <limits.h>
13
14// Careful: must include globals first for extern definitions
15#include "globals.h"
16
17#include "parse/t_program.h"
18#include "parse/t_scope.h"
19#include "parse/t_const.h"
20#include "parse/t_field.h"
21
22#include "version.h"
23
24#include "t_audit.h"
25
26extern int g_warn;
27extern std::string g_curpath;
28extern bool g_return_failure;
29
30void thrift_audit_warning(int level, const char* fmt, ...) {
31 if (g_warn < level) {
32 return;
33 }
34 va_list args;
35 printf("[Thrift Audit Warning:%s] ", g_curpath.c_str());
36 va_start(args, fmt);
37 vprintf(fmt, args);
38 va_end(args);
39 printf("\n");
40}
41
42void thrift_audit_failure(const char* fmt, ...) {
43 va_list args;
44 fprintf(stderr, "[Thrift Audit Failure:%s] ", g_curpath.c_str());
45 va_start(args, fmt);
46 vfprintf(stderr, fmt, args);
47 va_end(args);
48 fprintf(stderr, "\n");
49 g_return_failure = true;
50}
51
52void compare_namespace(t_program* newProgram, t_program* oldProgram)
53{
54 const std::map<std::string, std::string>& newNamespaceMap = newProgram->get_all_namespaces();
55 const std::map<std::string, std::string>& oldNamespaceMap = oldProgram->get_all_namespaces();
56
57 for(std::map<std::string, std::string>::const_iterator oldNamespaceMapIt = oldNamespaceMap.begin();
58 oldNamespaceMapIt != oldNamespaceMap.end();
59 oldNamespaceMapIt++)
60 {
61 std::map<std::string, std::string>::const_iterator newNamespaceMapIt = newNamespaceMap.find(oldNamespaceMapIt->first);
62 if(newNamespaceMapIt == newNamespaceMap.end())
63 {
64 thrift_audit_warning(1, "Language %s not found in new thrift file\n", (oldNamespaceMapIt->first).c_str());
65 }
66 else if((newNamespaceMapIt->second) != oldNamespaceMapIt->second)
67 {
68 thrift_audit_warning(1, "Namespace %s changed in new thrift file\n", (oldNamespaceMapIt->second).c_str());
69 }
70 }
71}
72
73void compare_enum_values(t_enum* newEnum,t_enum* oldEnum)
74{
75 const std::vector<t_enum_value*>& oldEnumValues = oldEnum->get_constants();
76 for(std::vector<t_enum_value*>::const_iterator oldEnumValuesIt = oldEnumValues.begin();
77 oldEnumValuesIt != oldEnumValues.end();
78 oldEnumValuesIt++)
79 {
80 int enumValue = (*oldEnumValuesIt)->get_value();
81 t_enum_value* newEnumValue = newEnum->get_constant_by_value(enumValue);
82 if(newEnumValue != NULL)
83 {
84 std::string enumName = (*oldEnumValuesIt)->get_name();
85 if(enumName != newEnumValue->get_name())
86 {
87 thrift_audit_warning(1, "Name of the value %d changed in enum %s\n", enumValue, oldEnum->get_name().c_str());
88 }
89 }
90 else
91 {
92 thrift_audit_failure("Enum value %d missing in %s\n", enumValue, oldEnum->get_name().c_str());
93 }
94
95 }
96}
97
98void compare_enums(const std::vector<t_enum*>& newEnumList, const std::vector<t_enum*>& oldEnumList)
99{
100 std::map<std::string,t_enum*> newEnumMap;
101 std::vector<t_enum*>::const_iterator newEnumIt;
102 for(newEnumIt = newEnumList.begin(); newEnumIt != newEnumList.end(); newEnumIt++)
103 {
104 newEnumMap[(*newEnumIt)->get_name()] = *newEnumIt;
105 }
106 std::vector<t_enum*>::const_iterator oldEnumIt;
107 for(oldEnumIt = oldEnumList.begin(); oldEnumIt != oldEnumList.end(); oldEnumIt++)
108 {
109 std::map<std::string,t_enum*>::iterator newEnumMapIt;
110 newEnumMapIt = newEnumMap.find((*oldEnumIt)->get_name());
111
112 if(newEnumMapIt == newEnumMap.end())
113 {
114 thrift_audit_warning(1, "Enum %s not found in new thrift file\n",(*oldEnumIt)->get_name().c_str());
115 }
116 else
117 {
118 compare_enum_values(newEnumMapIt->second, *oldEnumIt);
119 }
120 }
121}
122
123//This function returns 'true' if the two arguements are of same types.
124//Returns false if they are of different type
125bool compare_type(t_type* newType, t_type* oldType)
126{
127 //Comparing names of two types will work when the newType and oldType are basic types or structs or enums.
128 //However, when they are containers, get_name() returns empty for which we have to compare the type of
129 //their elements as well.
130 if((newType->get_name()).empty() && (oldType->get_name()).empty())
131 {
132
133 if(newType->is_list() && oldType->is_list())
134 {
135 t_type* newElementType = ((t_list*)newType)->get_elem_type();
136 t_type* oldElementType = ((t_list*)oldType)->get_elem_type();
137 return compare_type(newElementType, oldElementType);
138 }
139 else if(newType->is_map() && oldType->is_map())
140 {
141 t_type* newKeyType = ((t_map*)newType)->get_key_type();
142 t_type* oldKeyType = ((t_map*)oldType)->get_key_type();
143
144 t_type* newValType = ((t_map*)newType)->get_val_type();
145 t_type* oldValType = ((t_map*)oldType)->get_val_type();
146
147 return (compare_type(newKeyType, oldKeyType) && compare_type(newValType, oldValType));
148 }
149 else if(newType->is_set() && oldType->is_set())
150 {
151 t_type* newElementType = ((t_set*)newType)->get_elem_type();
152 t_type* oldElementType = ((t_set*)oldType)->get_elem_type();
153 return compare_type(newElementType, oldElementType);
154 }
155 else
156 {
157 return false;
158 }
159 }
160 else if(newType->get_name() == oldType->get_name())
161 {
162 return true;
163 }
164 else
165 {
166 return false;
167 }
168}
169
170bool compare_pair(std::pair<t_const_value*, t_const_value*> newMapPair, std::pair<t_const_value*, t_const_value*> oldMapPair)
171{
172 return compare_defaults(newMapPair.first, oldMapPair.first) && compare_defaults(newMapPair.second, oldMapPair.second);
173}
174
175// This function returns 'true' if the default values are same. Returns false if they are different.
176bool compare_defaults(t_const_value* newStructDefault, t_const_value* oldStructDefault)
177{
178 if(newStructDefault == NULL && oldStructDefault == NULL) return true;
179 else if(newStructDefault == NULL && oldStructDefault != NULL) return false;
180 else if (newStructDefault != NULL && oldStructDefault == NULL) return false;
181
182 if(newStructDefault->get_type() != oldStructDefault->get_type())
183 {
184 return false;
185 }
186
187 switch(newStructDefault->get_type())
188 {
189 case t_const_value::CV_INTEGER:
190 return (newStructDefault->get_integer() == oldStructDefault->get_integer());
191 case t_const_value::CV_DOUBLE:
192 return (newStructDefault->get_double() == oldStructDefault->get_double());
193 case t_const_value::CV_STRING:
194 return (newStructDefault->get_string() == oldStructDefault->get_string());
195 case t_const_value::CV_LIST:
196 {
197 const std::vector<t_const_value*>& oldDefaultList = oldStructDefault->get_list();
198 const std::vector<t_const_value*>& newDefaultList = newStructDefault->get_list();
199 bool defaultValuesCompare = (oldDefaultList.size() == newDefaultList.size());
200
201 return defaultValuesCompare && std::equal(newDefaultList.begin(), newDefaultList.end(), oldDefaultList.begin(), compare_defaults);
202 }
203 case t_const_value::CV_MAP:
204 {
205 const std::map<t_const_value*, t_const_value*> newMap = newStructDefault->get_map();
206 const std::map<t_const_value*, t_const_value*> oldMap = oldStructDefault->get_map();
207
208 bool defaultValuesCompare = (oldMap.size() == newMap.size());
209
210 return defaultValuesCompare && std::equal(newMap.begin(), newMap.end(), oldMap.begin(), compare_pair);
211 }
212 case t_const_value::CV_IDENTIFIER:
213 return (newStructDefault->get_identifier() == oldStructDefault->get_identifier());
214 default:
215 return false;
216 }
217
218}
219
220void compare_struct_field(t_field* newField, t_field* oldField, std::string oldStructName)
221{
222 t_type* newFieldType = newField->get_type();
223 t_type* oldFieldType = oldField->get_type();
224 if(!compare_type(newFieldType, oldFieldType))
225 {
226 thrift_audit_failure("Struct Field Type Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
227 }
228
229 // A Struct member can be optional if it is mentioned explicitly, or if it is assigned with default values.
230 bool newStructFieldOptional = (newField->get_req() != t_field::T_REQUIRED);
231 bool oldStructFieldOptional = (oldField->get_req() != t_field::T_REQUIRED);
232
233 if(newStructFieldOptional != oldStructFieldOptional)
234 {
235 thrift_audit_failure("Struct Field Requiredness Changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
236 }
237 if(newStructFieldOptional || oldStructFieldOptional)
238 {
239 if(!compare_defaults(newField->get_value(), oldField->get_value()))
240 {
241 thrift_audit_warning(1, "Default value changed for Id = %d in %s \n", newField->get_key(), oldStructName.c_str());
242 }
243 }
244
245 std::string fieldName = newField->get_name();
246 if(fieldName != oldField->get_name())
247 {
248 thrift_audit_warning(1, "Struct field name changed for Id = %d in %s\n", newField->get_key(), oldStructName.c_str());
249 }
250
251}
252
253void compare_single_struct(t_struct* newStruct, t_struct* oldStruct, const std::string& oldStructName = std::string())
254{
255 std::string structName = oldStructName.empty() ? oldStruct->get_name() : oldStructName;
256 const std::vector<t_field*>& oldStructMembersInIdOrder = oldStruct->get_sorted_members();
257 const std::vector<t_field*>& newStructMembersInIdOrder = newStruct->get_sorted_members();
258 std::vector<t_field*>::const_iterator oldStructMemberIt = oldStructMembersInIdOrder.begin();
259 std::vector<t_field*>::const_iterator newStructMemberIt = newStructMembersInIdOrder.begin();
260
261 // Since we have the struct members in their ID order, comparing their IDs can be done by traversing the two member
262 // lists together.
263 while(!(oldStructMemberIt == oldStructMembersInIdOrder.end() && newStructMemberIt == newStructMembersInIdOrder.end()))
264 {
265 if(newStructMemberIt == newStructMembersInIdOrder.end() && oldStructMemberIt != oldStructMembersInIdOrder.end())
266 {
267 // A field ID has been removed from the end.
268 thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str());
269 oldStructMemberIt++;
270 }
271 else if(newStructMemberIt != newStructMembersInIdOrder.end() && oldStructMemberIt == oldStructMembersInIdOrder.end())
272 {
273 //New field ID has been added to the end.
274 if((*newStructMemberIt)->get_req() == t_field::T_REQUIRED)
275 {
276 thrift_audit_failure("Required Struct Field Added for Id = %d in %s \n", (*newStructMemberIt)->get_key(), structName.c_str());
277 }
278 newStructMemberIt++;
279 }
280 else if((*newStructMemberIt)->get_key() == (*oldStructMemberIt)->get_key())
281 {
282 //Field ID found in both structs. Compare field types, default values.
283 compare_struct_field(*newStructMemberIt, *oldStructMemberIt, structName);
284
285 newStructMemberIt++;
286 oldStructMemberIt++;
287 }
288 else if((*newStructMemberIt)->get_key() < (*oldStructMemberIt)->get_key())
289 {
290 //New Field Id is inserted in between
291 //Adding fields to struct is fine, but adding them in the middle is suspicious. Error!!
292 thrift_audit_failure("Struct field is added in the middle with Id = %d in %s\n", (*newStructMemberIt)->get_key(), structName.c_str());
293 newStructMemberIt++;
294 }
295 else if((*newStructMemberIt)->get_key() > (*oldStructMemberIt)->get_key())
296 {
297 //A field is deleted in newStruct.
298 thrift_audit_failure("Struct Field removed for Id = %d in %s \n", (*oldStructMemberIt)->get_key(), structName.c_str());
299 oldStructMemberIt++;
300 }
301
302 }
303}
304
305void compare_structs(const std::vector<t_struct*>& newStructList, const std::vector<t_struct*>& oldStructList)
306{
307 std::map<std::string,t_struct*> newStructMap;
308 std::vector<t_struct*>::const_iterator newStructListIt;
309 for(newStructListIt = newStructList.begin(); newStructListIt != newStructList.end(); newStructListIt++)
310 {
311 newStructMap[(*newStructListIt)->get_name()] = *newStructListIt;
312 }
313
314 std::vector<t_struct*>::const_iterator oldStructListIt;
315 for(oldStructListIt = oldStructList.begin(); oldStructListIt != oldStructList.end(); oldStructListIt++)
316 {
317 std::map<std::string, t_struct*>::iterator newStructMapIt;
318 newStructMapIt = newStructMap.find((*oldStructListIt)->get_name());
319 if(newStructMapIt == newStructMap.end())
320 {
321 thrift_audit_failure("Struct %s not found in new thrift file\n", (*oldStructListIt)->get_name().c_str());
322 }
323 else
324 {
325 compare_single_struct(newStructMapIt->second, *oldStructListIt);
326 }
327 }
328
329}
330
331void compare_single_function(t_function* newFunction, t_function* oldFunction)
332{
333 t_type* newFunctionReturnType = newFunction->get_returntype();
334
335 if(newFunction->is_oneway() != oldFunction->is_oneway())
336 {
337 thrift_audit_failure("Oneway attribute changed for function %s\n",oldFunction->get_name().c_str());
338 }
339 if(!compare_type(newFunctionReturnType, oldFunction->get_returntype()))
340 {
341 thrift_audit_failure("Return type changed for function %s\n",oldFunction->get_name().c_str());
342 }
343
344 //Compare function arguments.
345 compare_single_struct(newFunction->get_arglist(), oldFunction->get_arglist());
346 std::string exceptionName = oldFunction->get_name();
347 exceptionName += "_exception";
348 compare_single_struct(newFunction->get_xceptions(), oldFunction->get_xceptions(), exceptionName);
349}
350
351void compare_functions(const std::vector<t_function*>& newFunctionList, const std::vector<t_function*>& oldFunctionList)
352{
353 std::map<std::string, t_function*> newFunctionMap;
354 std::map<std::string, t_function*>::iterator newFunctionMapIt;
355 for(std::vector<t_function*>::const_iterator newFunctionIt = newFunctionList.begin();
356 newFunctionIt != newFunctionList.end();
357 newFunctionIt++)
358 {
359 newFunctionMap[(*newFunctionIt)->get_name()] = *newFunctionIt;
360 }
361
362 for(std::vector<t_function*>::const_iterator oldFunctionIt = oldFunctionList.begin();
363 oldFunctionIt != oldFunctionList.end();
364 oldFunctionIt++)
365 {
366 newFunctionMapIt = newFunctionMap.find((*oldFunctionIt)->get_name());
367 if(newFunctionMapIt == newFunctionMap.end())
368 {
369 thrift_audit_failure("New Thrift File has missing function %s\n",(*oldFunctionIt)->get_name().c_str());
370 continue;
371 }
372 else
373 {
374 //Function is found in both thrift files. Compare return type and argument list
375 compare_single_function(newFunctionMapIt->second, *oldFunctionIt);
376 }
377 }
378
379}
380
381void compare_services(const std::vector<t_service*>& newServices, const std::vector<t_service*>& oldServices)
382{
383 std::vector<t_service*>::const_iterator oldServiceIt;
384
385 std::map<std::string, t_service*> newServiceMap;
386 for(std::vector<t_service*>::const_iterator newServiceIt = newServices.begin();
387 newServiceIt != newServices.end();
388 newServiceIt++)
389 {
390 newServiceMap[(*newServiceIt)->get_name()] = *newServiceIt;
391 }
392
393
394 for(oldServiceIt = oldServices.begin(); oldServiceIt != oldServices.end(); oldServiceIt++)
395 {
396 const std::string oldServiceName = (*oldServiceIt)->get_name();
397 std::map<std::string, t_service*>::iterator newServiceMapIt = newServiceMap.find(oldServiceName);
398
399 if(newServiceMapIt == newServiceMap.end())
400 {
401 thrift_audit_failure("New Thrift file is missing a service %s\n", oldServiceName.c_str());
402 }
403 else
404 {
405 t_service* oldServiceExtends = (*oldServiceIt)->get_extends();
406 t_service* newServiceExtends = (newServiceMapIt->second)->get_extends();
407
408 if(oldServiceExtends == NULL)
409 {
410 // It is fine to add extends. So if service in older thrift did not have any extends, we are fine.
411 // DO Nothing
412 }
413 else if(oldServiceExtends != NULL && newServiceExtends == NULL)
414 {
415 thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str());
416 }
417 else
418 {
419 std::string oldExtendsName = oldServiceExtends->get_name();
420 std::string newExtendsName = newServiceExtends->get_name();
421
422 if( newExtendsName != oldExtendsName)
423 {
424 thrift_audit_failure("Change in Service inheritance for %s\n", oldServiceName.c_str());
425 }
426 }
427
428 compare_functions((newServiceMapIt->second)->get_functions(), (*oldServiceIt)->get_functions());
429 }
430
431 }
432
433}
434
435void compare_consts(const std::vector<t_const*>& newConst, const std::vector<t_const*>& oldConst)
436{
437 std::vector<t_const*>::const_iterator newConstIt;
438 std::vector<t_const*>::const_iterator oldConstIt;
439
440 std::map<std::string, t_const*> newConstMap;
441
442 for(newConstIt = newConst.begin(); newConstIt != newConst.end(); newConstIt++)
443 {
444 newConstMap[(*newConstIt)->get_name()] = *newConstIt;
445 }
446
447 std::map<std::string, t_const*>::const_iterator newConstMapIt;
448 for(oldConstIt = oldConst.begin(); oldConstIt != oldConst.end(); oldConstIt++)
449 {
450 newConstMapIt = newConstMap.find((*oldConstIt)->get_name());
451 if(newConstMapIt == newConstMap.end())
452 {
453 thrift_audit_warning(1, "Constants Missing %s \n", ((*oldConstIt)->get_name()).c_str());
454 }
455 else if(!compare_type((newConstMapIt->second)->get_type(), (*oldConstIt)->get_type()))
456 {
457 thrift_audit_warning(1, "Constant %s is of different type \n", ((*oldConstIt)->get_name()).c_str());
458 }
459 else if(!compare_defaults((newConstMapIt->second)->get_value(), (*oldConstIt)->get_value()))
460 {
461 thrift_audit_warning(1, "Constant %s has different value\n", ((*oldConstIt)->get_name()).c_str());
462 }
463 }
464}
465
466