Ben Craig | 262cfb4 | 2015-07-08 20:37:15 -0500 | [diff] [blame] | 1 | |
| 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 | |
| 26 | extern int g_warn; |
| 27 | extern std::string g_curpath; |
| 28 | extern bool g_return_failure; |
| 29 | |
| 30 | void 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 | |
| 42 | void 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 | |
| 52 | void 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 | |
| 73 | void 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 | |
| 98 | void 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 |
| 125 | bool 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 | |
| 170 | bool 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. |
| 176 | bool 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 | |
| 220 | void 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 | |
| 253 | void 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 | |
| 305 | void 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 | |
| 331 | void 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 | |
| 351 | void 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 | |
| 381 | void 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 | |
| 435 | void 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 | |