1# Copyright (C) 2020 Red Hat Inc. 2# 3# Authors: 4# Eduardo Habkost <ehabkost@redhat.com> 5# 6# This work is licensed under the terms of the GNU GPL, version 2. See 7# the COPYING file in the top-level directory. 8import re 9from .regexps import * 10from .patching import * 11from .utils import * 12from .qom_macros import * 13 14TI_FIELDS = [ 'name', 'parent', 'abstract', 'interfaces', 15 'instance_size', 'instance_init', 'instance_post_init', 'instance_finalize', 16 'class_size', 'class_init', 'class_base_init', 'class_data'] 17 18RE_TI_FIELD_NAME = OR(*TI_FIELDS) 19 20RE_TI_FIELD_INIT = S(r'[ \t]*', NAMED('comments', RE_COMMENTS), 21 r'\.', NAMED('field', RE_TI_FIELD_NAME), r'\s*=\s*', 22 NAMED('value', RE_EXPRESSION), r'[ \t]*,?[ \t]*\n') 23RE_TI_FIELDS = M(RE_TI_FIELD_INIT) 24 25RE_TYPEINFO_START = S(r'^[ \t]*', M(r'(static|const)\s+', name='modifiers'), r'TypeInfo\s+', 26 NAMED('name', RE_IDENTIFIER), r'\s*=\s*{[ \t]*\n') 27 28ParsedArray = List[str] 29ParsedInitializerValue = Union[str, ParsedArray] 30class InitializerValue(NamedTuple): 31 raw: str 32 parsed: Optional[ParsedInitializerValue] 33 match: Optional[Match] 34 35class ArrayItem(FileMatch): 36 regexp = RE_ARRAY_ITEM 37 38class ArrayInitializer(FileMatch): 39 regexp = RE_ARRAY 40 41 def parsed(self) -> ParsedArray: 42 #DBG('parse_array: %r', m.group(0)) 43 return [m.group('arrayitem') for m in self.group_finditer(ArrayItem, 'arrayitems')] 44 45class FieldInitializer(FileMatch): 46 regexp = RE_TI_FIELD_INIT 47 48 @property 49 def raw(self) -> str: 50 return self.group('value') 51 52 @property 53 def parsed(self) -> ParsedInitializerValue: 54 parsed: ParsedInitializerValue = self.raw 55 #DBG("parse_initializer_value: %r", s) 56 array = self.try_group_match(ArrayInitializer, 'value') 57 if array: 58 assert isinstance(array, ArrayInitializer) 59 return array.parsed() 60 return parsed 61 62TypeInfoInitializers = Dict[str, FieldInitializer] 63 64class TypeDefinition(FileMatch): 65 """ 66 Common base class for type definitions (TypeInfo variables or OBJECT_DEFINE* macros) 67 """ 68 @property 69 def instancetype(self) -> Optional[str]: 70 return self.group('instancetype') 71 72 @property 73 def classtype(self) -> Optional[str]: 74 return self.group('classtype') 75 76 @property 77 def uppercase(self) -> Optional[str]: 78 return self.group('uppercase') 79 80 @property 81 def parent_uppercase(self) -> str: 82 return self.group('parent_uppercase') 83 84 @property 85 def initializers(self) -> Optional[TypeInfoInitializers]: 86 if getattr(self, '_inititalizers', None): 87 self._initializers: TypeInfoInitializers 88 return self._initializers 89 fields = self.group('fields') 90 if fields is None: 91 return None 92 d = dict((fm.group('field'), fm) 93 for fm in self.group_finditer(FieldInitializer, 'fields')) 94 self._initializers = d # type: ignore 95 return self._initializers 96 97 98class TypeInfoVar(TypeDefinition): 99 """TypeInfo variable declaration with initializer""" 100 regexp = S(NAMED('begin', RE_TYPEINFO_START), 101 M(NAMED('fields', RE_TI_FIELDS), 102 NAMED('endcomments', SP, RE_COMMENTS), 103 NAMED('end', r'};?\n'), 104 n='?', name='fullspec')) 105 106 def is_static(self) -> bool: 107 return 'static' in self.group('modifiers') 108 109 def is_const(self) -> bool: 110 return 'const' in self.group('modifiers') 111 112 def is_full(self) -> bool: 113 return bool(self.group('fullspec')) 114 115 def get_initializers(self) -> TypeInfoInitializers: 116 """Helper for code that needs to deal with missing initializer info""" 117 if self.initializers is None: 118 return {} 119 return self.initializers 120 121 def get_raw_initializer_value(self, field: str, default: str = '') -> str: 122 initializers = self.get_initializers() 123 if field in initializers: 124 return initializers[field].raw 125 else: 126 return default 127 128 @property 129 def typename(self) -> Optional[str]: 130 return self.get_raw_initializer_value('name') 131 132 @property 133 def uppercase(self) -> Optional[str]: 134 typename = self.typename 135 if not typename: 136 return None 137 if not typename.startswith('TYPE_'): 138 return None 139 return typename[len('TYPE_'):] 140 141 @property 142 def classtype(self) -> Optional[str]: 143 class_size = self.get_raw_initializer_value('class_size') 144 if not class_size: 145 return None 146 m = re.fullmatch(RE_SIZEOF, class_size) 147 if not m: 148 return None 149 return m.group('sizeoftype') 150 151 @property 152 def instancetype(self) -> Optional[str]: 153 instance_size = self.get_raw_initializer_value('instance_size') 154 if not instance_size: 155 return None 156 m = re.fullmatch(RE_SIZEOF, instance_size) 157 if not m: 158 return None 159 return m.group('sizeoftype') 160 161 162 #def extract_identifiers(self) -> Optional[TypeIdentifiers]: 163 # """Try to extract identifiers from names being used""" 164 # DBG("extracting idenfiers from %s", self.name) 165 #uppercase = None 166 #if typename and re.fullmatch(RE_IDENTIFIER, typename) and typename.startswith("TYPE_"): 167 # uppercase = typename[len('TYPE_'):] 168 #lowercase = None 169 #funcs = set() 170 #prefixes = set() 171 #for field,suffix in [('instance_init', '_init'), 172 # ('instance_finalize', '_finalize'), 173 # ('class_init', '_class_init')]: 174 # if field not in values: 175 # continue 176 # func = values[field].raw 177 # funcs.add(func) 178 # if func.endswith(suffix): 179 # prefixes.add(func[:-len(suffix)]) 180 # else: 181 # self.warn("function name %s doesn't have expected %s suffix", 182 # func, suffix) 183 #if len(prefixes) == 1: 184 # lowercase = prefixes.pop() 185 #elif len(prefixes) > 1: 186 # self.warn("inconsistent function names: %s", ' '.join(funcs)) 187 188 #.parent = TYPE_##PARENT_MODULE_OBJ_NAME, \ 189 #return TypeIdentifiers(typename=typename, 190 # uppercase=uppercase, lowercase=lowercase, 191 # instancetype=instancetype, classtype=classtype) 192 193 def append_field(self, field: str, value: str) -> Patch: 194 """Generate patch appending a field initializer""" 195 content = f' .{field} = {value},\n' 196 fm = self.group_match('fields') 197 assert fm 198 return fm.append(content) 199 200 def patch_field(self, field: str, replacement: str) -> Patch: 201 """Generate patch replacing a field initializer""" 202 initializers = self.initializers 203 assert initializers 204 value = initializers.get(field) 205 assert value 206 return value.make_patch(replacement) 207 208 def remove_field(self, field: str) -> Iterable[Patch]: 209 initializers = self.initializers 210 assert initializers 211 if field in initializers: 212 yield self.patch_field(field, '') 213 214 def remove_fields(self, *fields: str) -> Iterable[Patch]: 215 for f in fields: 216 yield from self.remove_field(f) 217 218 def patch_field_value(self, field: str, replacement: str) -> Patch: 219 """Replace just the value of a field initializer""" 220 initializers = self.initializers 221 assert initializers 222 value = initializers.get(field) 223 assert value 224 vm = value.group_match('value') 225 assert vm 226 return vm.make_patch(replacement) 227 228 229class RemoveRedundantClassSize(TypeInfoVar): 230 """Remove class_size when using OBJECT_DECLARE_SIMPLE_TYPE""" 231 def gen_patches(self) -> Iterable[Patch]: 232 initializers = self.initializers 233 if initializers is None: 234 return 235 if 'class_size' not in initializers: 236 return 237 238 self.debug("Handling %s", self.name) 239 m = re.fullmatch(RE_SIZEOF, initializers['class_size'].raw) 240 if not m: 241 self.warn("%s class_size is not sizeof?", self.name) 242 return 243 classtype = m.group('sizeoftype') 244 if not classtype.endswith('Class'): 245 self.warn("%s class size type (%s) is not *Class?", self.name, classtype) 246 return 247 self.debug("classtype is %s", classtype) 248 instancetype = classtype[:-len('Class')] 249 self.debug("intanceypte is %s", instancetype) 250 self.debug("searching for simpletype declaration using %s as InstanceType", instancetype) 251 decl = self.allfiles.find_match(OldStyleObjectDeclareSimpleType, 252 instancetype, 'instancetype') 253 if not decl: 254 self.debug("No simpletype declaration found for %s", instancetype) 255 return 256 self.debug("Found simple type declaration") 257 decl.debug("declaration is here") 258 yield from self.remove_field('class_size') 259 260class RemoveDeclareSimpleTypeArg(OldStyleObjectDeclareSimpleType): 261 """Remove class_size when using OBJECT_DECLARE_SIMPLE_TYPE""" 262 def gen_patches(self) -> Iterable[Patch]: 263 c = (f'OBJECT_DECLARE_SIMPLE_TYPE({self.group("instancetype")}, {self.group("lowercase")},\n' 264 f' {self.group("uppercase")})\n') 265 yield self.make_patch(c) 266 267class UseDeclareTypeExtended(TypeInfoVar): 268 """Replace TypeInfo variable with OBJECT_DEFINE_TYPE_EXTENDED""" 269 def gen_patches(self) -> Iterable[Patch]: 270 # this will just ensure the caches for find_match() and matches_for_type() 271 # will be loaded in advance: 272 find_type_checkers(self.allfiles, 'xxxxxxxxxxxxxxxxx') 273 274 if not self.is_static(): 275 self.info("Skipping non-static TypeInfo variable") 276 return 277 278 type_info_macro = self.file.find_match(TypeInfoMacro, self.name) 279 if not type_info_macro: 280 self.warn("TYPE_INFO(%s) line not found", self.name) 281 return 282 283 values = self.initializers 284 if values is None: 285 return 286 if 'name' not in values: 287 self.warn("name not set in TypeInfo variable %s", self.name) 288 return 289 290 typename = values['name'].raw 291 292 if 'parent' not in values: 293 self.warn("parent not set in TypeInfo variable %s", self.name) 294 return 295 parent_typename = values['parent'].raw 296 297 instancetype = None 298 if 'instance_size' in values: 299 m = re.fullmatch(RE_SIZEOF, values['instance_size'].raw) 300 if m: 301 instancetype = m.group('sizeoftype') 302 else: 303 self.warn("can't extract instance type in TypeInfo variable %s", self.name) 304 self.warn("instance_size is set to: %r", values['instance_size'].raw) 305 return 306 307 classtype = None 308 if 'class_size' in values: 309 m = re.fullmatch(RE_SIZEOF, values['class_size'].raw) 310 if m: 311 classtype = m.group('sizeoftype') 312 else: 313 self.warn("can't extract class type in TypeInfo variable %s", self.name) 314 self.warn("class_size is set to: %r", values['class_size'].raw) 315 return 316 317 #for t in (typename, parent_typename): 318 # if not re.fullmatch(RE_IDENTIFIER, t): 319 # self.info("type name is not a macro/constant") 320 # if instancetype or classtype: 321 # self.warn("macro/constant type name is required for instance/class type") 322 # if not self.file.force: 323 # return 324 325 # Now, the challenge is to find out the right MODULE_OBJ_NAME for the 326 # type and for the parent type 327 self.info("TypeInfo variable for %s is here", typename) 328 uppercase = find_typename_uppercase(self.allfiles, typename) 329 if not uppercase: 330 self.info("Can't find right uppercase name for %s", typename) 331 if instancetype or classtype: 332 self.warn("Can't find right uppercase name for %s", typename) 333 self.warn("This will make type validation difficult in the future") 334 return 335 336 parent_uppercase = find_typename_uppercase(self.allfiles, parent_typename) 337 if not parent_uppercase: 338 self.info("Can't find right uppercase name for parent type (%s)", parent_typename) 339 if instancetype or classtype: 340 self.warn("Can't find right uppercase name for parent type (%s)", parent_typename) 341 self.warn("This will make type validation difficult in the future") 342 return 343 344 ok = True 345 346 #checkers: List[TypeCheckerDeclaration] = list(find_type_checkers(self.allfiles, uppercase)) 347 #for c in checkers: 348 # c.info("instance type checker declaration (%s) is here", c.group('uppercase')) 349 #if not checkers: 350 # self.info("No type checkers declared for %s", uppercase) 351 # if instancetype or classtype: 352 # self.warn("Can't find where type checkers for %s (%s) are declared. We will need them to validate sizes of %s", 353 # typename, uppercase, self.name) 354 355 if not instancetype: 356 instancetype = 'void' 357 if not classtype: 358 classtype = 'void' 359 360 #checker_instancetypes = set(c.instancetype for c in checkers 361 # if c.instancetype is not None) 362 #if len(checker_instancetypes) > 1: 363 # self.warn("ambiguous set of type checkers") 364 # for c in checkers: 365 # c.warn("instancetype is %s here", c.instancetype) 366 # ok = False 367 #elif len(checker_instancetypes) == 1: 368 # checker_instancetype = checker_instancetypes.pop() 369 # DBG("checker instance type: %r", checker_instancetype) 370 # if instancetype != checker_instancetype: 371 # self.warn("type at instance_size is %r. Should instance_size be set to sizeof(%s) ?", 372 # instancetype, checker_instancetype) 373 # ok = False 374 #else: 375 # if instancetype != 'void': 376 # self.warn("instance type checker for %s (%s) not found", typename, instancetype) 377 # ok = False 378 379 #checker_classtypes = set(c.classtype for c in checkers 380 # if c.classtype is not None) 381 #if len(checker_classtypes) > 1: 382 # self.warn("ambiguous set of type checkers") 383 # for c in checkers: 384 # c.warn("classtype is %s here", c.classtype) 385 # ok = False 386 #elif len(checker_classtypes) == 1: 387 # checker_classtype = checker_classtypes.pop() 388 # DBG("checker class type: %r", checker_classtype) 389 # if classtype != checker_classtype: 390 # self.warn("type at class_size is %r. Should class_size be set to sizeof(%s) ?", 391 # classtype, checker_classtype) 392 # ok = False 393 #else: 394 # if classtype != 'void': 395 # self.warn("class type checker for %s (%s) not found", typename, classtype) 396 # ok = False 397 398 #if not ok: 399 # for c in checkers: 400 # c.warn("Type checker declaration for %s (%s) is here", 401 # typename, type(c).__name__) 402 # return 403 404 #if parent_decl is None: 405 # self.warn("Can't find where parent type %s is declared", parent_typename) 406 407 #yield self.prepend(f'DECLARE_TYPE_NAME({uppercase}, {typename})\n') 408 #if not instancetype: 409 # yield self.prepend(f'DECLARE_INSTANCE_TYPE({uppercase}, void)\n') 410 #if not classtype: 411 # yield self.prepend(f'DECLARE_CLASS_TYPE({uppercase}, void)\n') 412 self.info("%s can be patched!", self.name) 413 replaced_fields = ['name', 'parent', 'instance_size', 'class_size'] 414 begin = self.group_match('begin') 415 newbegin = f'OBJECT_DEFINE_TYPE_EXTENDED({self.name},\n' 416 newbegin += f' {instancetype}, {classtype},\n' 417 newbegin += f' {uppercase}, {parent_uppercase}' 418 if set(values.keys()) - set(replaced_fields): 419 newbegin += ',\n' 420 yield begin.make_patch(newbegin) 421 yield from self.remove_fields(*replaced_fields) 422 end = self.group_match('end') 423 yield end.make_patch(')\n') 424 yield type_info_macro.make_removal_patch() 425 426class ObjectDefineTypeExtended(TypeDefinition): 427 """OBJECT_DEFINE_TYPE_EXTENDED usage""" 428 regexp = S(r'^[ \t]*OBJECT_DEFINE_TYPE_EXTENDED\s*\(\s*', 429 NAMED('name', RE_IDENTIFIER), r'\s*,\s*', 430 NAMED('instancetype', RE_IDENTIFIER), r'\s*,\s*', 431 NAMED('classtype', RE_IDENTIFIER), r'\s*,\s*', 432 NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 433 NAMED('parent_uppercase', RE_IDENTIFIER), 434 M(r',\s*\n', 435 NAMED('fields', RE_TI_FIELDS), 436 n='?'), 437 r'\s*\);?\n?') 438 439class ObjectDefineType(TypeDefinition): 440 """OBJECT_DEFINE_TYPE usage""" 441 regexp = S(r'^[ \t]*OBJECT_DEFINE_TYPE\s*\(\s*', 442 NAMED('lowercase', RE_IDENTIFIER), r'\s*,\s*', 443 NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 444 NAMED('parent_uppercase', RE_IDENTIFIER), 445 M(r',\s*\n', 446 NAMED('fields', RE_TI_FIELDS), 447 n='?'), 448 r'\s*\);?\n?') 449 450def find_type_definitions(files: FileList, uppercase: str) -> Iterable[TypeDefinition]: 451 types: List[Type[TypeDefinition]] = [TypeInfoVar, ObjectDefineType, ObjectDefineTypeExtended] 452 for t in types: 453 for m in files.matches_of_type(t): 454 m.debug("uppercase: %s", m.uppercase) 455 yield from (m for t in types 456 for m in files.matches_of_type(t) 457 if m.uppercase == uppercase) 458 459class AddDeclareVoidClassType(TypeDeclarationFixup): 460 """Will add DECLARE_CLASS_TYPE(..., void) if possible""" 461 def gen_patches_for_type(self, uppercase: str, 462 checkers: List[TypeDeclaration], 463 fields: Dict[str, Optional[str]]) -> Iterable[Patch]: 464 defs = list(find_type_definitions(self.allfiles, uppercase)) 465 if len(defs) > 1: 466 self.warn("multiple definitions for %s", uppercase) 467 for d in defs: 468 d.warn("definition found here") 469 return 470 elif len(defs) == 0: 471 self.warn("type definition for %s not found", uppercase) 472 return 473 d = defs[0] 474 if d.classtype is None: 475 d.info("definition for %s has classtype, skipping", uppercase) 476 return 477 class_type_checkers = [c for c in checkers 478 if c.classtype is not None] 479 if class_type_checkers: 480 for c in class_type_checkers: 481 c.warn("class type checker for %s is present here", uppercase) 482 return 483 484 _,last_checker = max((m.start(), m) for m in checkers) 485 s = f'DECLARE_CLASS_TYPE({uppercase}, void)\n' 486 yield last_checker.append(s) 487 488class AddDeclareVoidInstanceType(FileMatch): 489 """Will add DECLARE_INSTANCE_TYPE(..., void) if possible""" 490 regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, 491 NAMED('name', r'TYPE_[a-zA-Z0-9_]+\b'), 492 CPP_SPACE, r'.*\n') 493 494 def gen_patches(self) -> Iterable[Patch]: 495 assert self.name.startswith('TYPE_') 496 uppercase = self.name[len('TYPE_'):] 497 defs = list(find_type_definitions(self.allfiles, uppercase)) 498 if len(defs) > 1: 499 self.warn("multiple definitions for %s", uppercase) 500 for d in defs: 501 d.warn("definition found here") 502 return 503 elif len(defs) == 0: 504 self.warn("type definition for %s not found", uppercase) 505 return 506 d = defs[0] 507 instancetype = d.instancetype 508 if instancetype is not None and instancetype != 'void': 509 return 510 511 instance_checkers = [c for c in find_type_checkers(self.allfiles, uppercase) 512 if c.instancetype] 513 if instance_checkers: 514 d.warn("instance type checker for %s already declared", uppercase) 515 for c in instance_checkers: 516 c.warn("instance checker for %s is here", uppercase) 517 return 518 519 s = f'DECLARE_INSTANCE_TYPE({uppercase}, void)\n' 520 yield self.append(s) 521 522class AddObjectDeclareType(DeclareObjCheckers): 523 """Will add OBJECT_DECLARE_TYPE(...) if possible""" 524 def gen_patches(self) -> Iterable[Patch]: 525 uppercase = self.uppercase 526 typename = self.group('typename') 527 instancetype = self.group('instancetype') 528 classtype = self.group('classtype') 529 530 if typename != f'TYPE_{uppercase}': 531 self.warn("type name mismatch: %s vs %s", typename, uppercase) 532 return 533 534 typedefs = [(t,self.allfiles.find_matches(SimpleTypedefMatch, t)) 535 for t in (instancetype, classtype)] 536 for t,tds in typedefs: 537 if not tds: 538 self.warn("typedef %s not found", t) 539 return 540 for td in tds: 541 td_type = td.group('typedef_type') 542 if td_type != f'struct {t}': 543 self.warn("typedef mismatch: %s is defined as %s", t, td_type) 544 td.warn("typedef is here") 545 return 546 547 # look for reuse of same struct type 548 other_instance_checkers = [c for c in find_type_checkers(self.allfiles, instancetype, 'instancetype') 549 if c.uppercase != uppercase] 550 if other_instance_checkers: 551 self.warn("typedef %s is being reused", instancetype) 552 for ic in other_instance_checkers: 553 ic.warn("%s is reused here", instancetype) 554 if not self.file.force: 555 return 556 557 decl_types: List[Type[TypeDeclaration]] = [DeclareClassCheckers, DeclareObjCheckers] 558 class_decls = [m for t in decl_types 559 for m in self.allfiles.find_matches(t, uppercase, 'uppercase')] 560 561 defs = list(find_type_definitions(self.allfiles, uppercase)) 562 if len(defs) > 1: 563 self.warn("multiple definitions for %s", uppercase) 564 for d in defs: 565 d.warn("definition found here") 566 if not self.file.force: 567 return 568 elif len(defs) == 0: 569 self.warn("type definition for %s not found", uppercase) 570 if not self.file.force: 571 return 572 else: 573 d = defs[0] 574 if d.instancetype != instancetype: 575 self.warn("mismatching instance type for %s (%s)", uppercase, instancetype) 576 d.warn("instance type declared here (%s)", d.instancetype) 577 if not self.file.force: 578 return 579 if d.classtype != classtype: 580 self.warn("mismatching class type for %s (%s)", uppercase, classtype) 581 d.warn("class type declared here (%s)", d.classtype) 582 if not self.file.force: 583 return 584 585 assert self.file.original_content 586 for t,tds in typedefs: 587 assert tds 588 for td in tds: 589 if td.file is not self.file: 590 continue 591 592 # delete typedefs that are truly redundant: 593 # 1) defined after DECLARE_OBJ_CHECKERS 594 if td.start() > self.start(): 595 yield td.make_removal_patch() 596 # 2) defined before DECLARE_OBJ_CHECKERS, but unused 597 elif not re.search(r'\b'+t+r'\b', self.file.original_content[td.end():self.start()]): 598 yield td.make_removal_patch() 599 600 c = (f'OBJECT_DECLARE_TYPE({instancetype}, {classtype}, {uppercase})\n') 601 yield self.make_patch(c) 602 603class AddObjectDeclareSimpleType(DeclareInstanceChecker): 604 """Will add OBJECT_DECLARE_SIMPLE_TYPE(...) if possible""" 605 def gen_patches(self) -> Iterable[Patch]: 606 uppercase = self.uppercase 607 typename = self.group('typename') 608 instancetype = self.group('instancetype') 609 610 if typename != f'TYPE_{uppercase}': 611 self.warn("type name mismatch: %s vs %s", typename, uppercase) 612 return 613 614 typedefs = [(t,self.allfiles.find_matches(SimpleTypedefMatch, t)) 615 for t in (instancetype,)] 616 for t,tds in typedefs: 617 if not tds: 618 self.warn("typedef %s not found", t) 619 return 620 for td in tds: 621 td_type = td.group('typedef_type') 622 if td_type != f'struct {t}': 623 self.warn("typedef mismatch: %s is defined as %s", t, td_type) 624 td.warn("typedef is here") 625 return 626 627 # look for reuse of same struct type 628 other_instance_checkers = [c for c in find_type_checkers(self.allfiles, instancetype, 'instancetype') 629 if c.uppercase != uppercase] 630 if other_instance_checkers: 631 self.warn("typedef %s is being reused", instancetype) 632 for ic in other_instance_checkers: 633 ic.warn("%s is reused here", instancetype) 634 if not self.file.force: 635 return 636 637 decl_types: List[Type[TypeDeclaration]] = [DeclareClassCheckers, DeclareObjCheckers] 638 class_decls = [m for t in decl_types 639 for m in self.allfiles.find_matches(t, uppercase, 'uppercase')] 640 if class_decls: 641 self.warn("class type declared for %s", uppercase) 642 for cd in class_decls: 643 cd.warn("class declaration found here") 644 return 645 646 defs = list(find_type_definitions(self.allfiles, uppercase)) 647 if len(defs) > 1: 648 self.warn("multiple definitions for %s", uppercase) 649 for d in defs: 650 d.warn("definition found here") 651 if not self.file.force: 652 return 653 elif len(defs) == 0: 654 self.warn("type definition for %s not found", uppercase) 655 if not self.file.force: 656 return 657 else: 658 d = defs[0] 659 if d.instancetype != instancetype: 660 self.warn("mismatching instance type for %s (%s)", uppercase, instancetype) 661 d.warn("instance type declared here (%s)", d.instancetype) 662 if not self.file.force: 663 return 664 if d.classtype: 665 self.warn("class type set for %s", uppercase) 666 d.warn("class type declared here") 667 if not self.file.force: 668 return 669 670 assert self.file.original_content 671 for t,tds in typedefs: 672 assert tds 673 for td in tds: 674 if td.file is not self.file: 675 continue 676 677 # delete typedefs that are truly redundant: 678 # 1) defined after DECLARE_OBJ_CHECKERS 679 if td.start() > self.start(): 680 yield td.make_removal_patch() 681 # 2) defined before DECLARE_OBJ_CHECKERS, but unused 682 elif not re.search(r'\b'+t+r'\b', self.file.original_content[td.end():self.start()]): 683 yield td.make_removal_patch() 684 685 c = (f'OBJECT_DECLARE_SIMPLE_TYPE({instancetype}, {uppercase})\n') 686 yield self.make_patch(c) 687 688 689class TypeInfoStringName(TypeInfoVar): 690 """Replace hardcoded type names with TYPE_ constant""" 691 def gen_patches(self) -> Iterable[Patch]: 692 values = self.initializers 693 if values is None: 694 return 695 if 'name' not in values: 696 self.warn("name not set in TypeInfo variable %s", self.name) 697 return 698 typename = values['name'].raw 699 if re.fullmatch(RE_IDENTIFIER, typename): 700 return 701 702 self.warn("name %s is not an identifier", typename) 703 #all_defines = [m for m in self.allfiles.matches_of_type(ExpressionDefine)] 704 #self.debug("all_defines: %r", all_defines) 705 constants = [m for m in self.allfiles.matches_of_type(ExpressionDefine) 706 if m.group('value').strip() == typename.strip()] 707 if not constants: 708 self.warn("No macro for %s found", typename) 709 return 710 if len(constants) > 1: 711 self.warn("I don't know which macro to use: %r", constants) 712 return 713 yield self.patch_field_value('name', constants[0].name) 714 715class RedundantTypeSizes(TypeInfoVar): 716 """Remove redundant instance_size/class_size from TypeInfo vars""" 717 def gen_patches(self) -> Iterable[Patch]: 718 values = self.initializers 719 if values is None: 720 return 721 if 'name' not in values: 722 self.warn("name not set in TypeInfo variable %s", self.name) 723 return 724 typename = values['name'].raw 725 if 'parent' not in values: 726 self.warn("parent not set in TypeInfo variable %s", self.name) 727 return 728 parent_typename = values['parent'].raw 729 730 if 'instance_size' not in values and 'class_size' not in values: 731 self.debug("no need to validate %s", self.name) 732 return 733 734 instance_decls = find_type_checkers(self.allfiles, typename) 735 if instance_decls: 736 self.debug("won't touch TypeInfo var that has type checkers") 737 return 738 739 parent = find_type_info(self.allfiles, parent_typename) 740 if not parent: 741 self.warn("Can't find TypeInfo for %s", parent_typename) 742 return 743 744 if 'instance_size' in values and parent.get_raw_initializer_value('instance_size') != values['instance_size'].raw: 745 self.info("instance_size mismatch") 746 parent.info("parent type declared here") 747 return 748 749 if 'class_size' in values and parent.get_raw_initializer_value('class_size') != values['class_size'].raw: 750 self.info("class_size mismatch") 751 parent.info("parent type declared here") 752 return 753 754 self.debug("will patch variable %s", self.name) 755 756 if 'instance_size' in values: 757 self.debug("deleting instance_size") 758 yield self.patch_field('instance_size', '') 759 760 if 'class_size' in values: 761 self.debug("deleting class_size") 762 yield self.patch_field('class_size', '') 763 764 765#class TypeInfoVarInitFuncs(TypeInfoVar): 766# """TypeInfo variable 767# Will create missing init functions 768# """ 769# def gen_patches(self) -> Iterable[Patch]: 770# values = self.initializers 771# if values is None: 772# self.warn("type not parsed completely: %s", self.name) 773# return 774# 775# macro = self.file.find_match(TypeInfoVar, self.name) 776# if macro is None: 777# self.warn("No TYPE_INFO macro for %s", self.name) 778# return 779# 780# ids = self.extract_identifiers() 781# if ids is None: 782# return 783# 784# DBG("identifiers extracted: %r", ids) 785# fields = set(values.keys()) 786# if ids.lowercase: 787# if 'instance_init' not in fields: 788# yield self.prepend(('static void %s_init(Object *obj)\n' 789# '{\n' 790# '}\n\n') % (ids.lowercase)) 791# yield self.append_field('instance_init', ids.lowercase+'_init') 792# 793# if 'instance_finalize' not in fields: 794# yield self.prepend(('static void %s_finalize(Object *obj)\n' 795# '{\n' 796# '}\n\n') % (ids.lowercase)) 797# yield self.append_field('instance_finalize', ids.lowercase+'_finalize') 798# 799# 800# if 'class_init' not in fields: 801# yield self.prepend(('static void %s_class_init(ObjectClass *oc, void *data)\n' 802# '{\n' 803# '}\n\n') % (ids.lowercase)) 804# yield self.append_field('class_init', ids.lowercase+'_class_init') 805 806class TypeInitMacro(FileMatch): 807 """Use of type_init(...) macro""" 808 regexp = S(r'^[ \t]*type_init\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);?[ \t]*\n') 809 810class DeleteEmptyTypeInitFunc(TypeInitMacro): 811 """Delete empty function declared using type_init(...)""" 812 def gen_patches(self) -> Iterable[Patch]: 813 fn = self.file.find_match(StaticVoidFunction, self.name) 814 DBG("function for %s: %s", self.name, fn) 815 if fn and fn.body == '': 816 yield fn.make_patch('') 817 yield self.make_patch('') 818 819class StaticVoidFunction(FileMatch): 820 """simple static void function 821 (no replacement rules) 822 """ 823 #NOTE: just like RE_FULL_STRUCT, this doesn't parse any of the body contents 824 # of the function. Tt will just look for "}" in the beginning of a line 825 regexp = S(r'static\s+void\s+', NAMED('name', RE_IDENTIFIER), r'\s*\(\s*void\s*\)\n', 826 r'{\n', 827 NAMED('body', 828 # acceptable inside the function body: 829 # - lines starting with space or tab 830 # - empty lines 831 # - preprocessor directives 832 OR(r'[ \t][^\n]*\n', 833 r'#[^\n]*\n', 834 r'\n', 835 repeat='*')), 836 r'};?\n') 837 838 @property 839 def body(self) -> str: 840 return self.group('body') 841 842 def has_preprocessor_directive(self) -> bool: 843 return bool(re.search(r'^[ \t]*#', self.body, re.MULTILINE)) 844 845def find_containing_func(m: FileMatch) -> Optional['StaticVoidFunction']: 846 """Return function containing this match""" 847 for fn in m.file.matches_of_type(StaticVoidFunction): 848 if fn.contains(m): 849 return fn 850 return None 851 852class TypeRegisterStaticCall(FileMatch): 853 """type_register_static() call 854 Will be replaced by TYPE_INFO() macro 855 """ 856 regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register_static'), 857 r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n') 858 859class UseTypeInfo(TypeRegisterStaticCall): 860 """Replace type_register_static() call with TYPE_INFO declaration""" 861 def gen_patches(self) -> Iterable[Patch]: 862 fn = find_containing_func(self) 863 if fn: 864 DBG("%r is inside %r", self, fn) 865 type_init = self.file.find_match(TypeInitMacro, fn.name) 866 if type_init is None: 867 self.warn("can't find type_init(%s) line", fn.name) 868 if not self.file.force: 869 return 870 else: 871 self.warn("can't identify the function where type_register_static(&%s) is called", self.name) 872 if not self.file.force: 873 return 874 875 #if fn.has_preprocessor_directive() and not self.file.force: 876 # self.warn("function %s has preprocessor directives, this requires --force", fn.name) 877 # return 878 879 var = self.file.find_match(TypeInfoVar, self.name) 880 if var is None: 881 self.warn("can't find TypeInfo var declaration for %s", self.name) 882 return 883 884 if not var.is_full(): 885 self.warn("variable declaration %s wasn't parsed fully", var.name) 886 if not self.file.force: 887 return 888 889 if fn and fn.contains(var): 890 self.warn("TypeInfo %s variable is inside a function", self.name) 891 if not self.file.force: 892 return 893 894 # delete type_register_static() call: 895 yield self.make_patch('') 896 # append TYPE_REGISTER(...) after variable declaration: 897 yield var.append(f'TYPE_INFO({self.name})\n') 898 899class TypeRegisterCall(FileMatch): 900 """type_register_static() call""" 901 regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register'), 902 r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n') 903 904class MakeTypeRegisterStatic(TypeRegisterCall): 905 """Make type_register() call static if variable is static const""" 906 def gen_patches(self): 907 var = self.file.find_match(TypeInfoVar, self.name) 908 if var is None: 909 self.warn("can't find TypeInfo var declaration for %s", self.name) 910 return 911 if var.is_static() and var.is_const(): 912 yield self.group_match('func_name').make_patch('type_register_static') 913 914class MakeTypeRegisterNotStatic(TypeRegisterStaticCall): 915 """Make type_register() call static if variable is static const""" 916 def gen_patches(self): 917 var = self.file.find_match(TypeInfoVar, self.name) 918 if var is None: 919 self.warn("can't find TypeInfo var declaration for %s", self.name) 920 return 921 if not var.is_static() or not var.is_const(): 922 yield self.group_match('func_name').make_patch('type_register') 923 924class TypeInfoMacro(FileMatch): 925 """TYPE_INFO macro usage""" 926 regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n') 927 928def find_type_info(files: RegexpScanner, name: str) -> Optional[TypeInfoVar]: 929 ti = [ti for ti in files.matches_of_type(TypeInfoVar) 930 if ti.get_raw_initializer_value('name') == name] 931 DBG("type info vars: %r", ti) 932 if len(ti) > 1: 933 DBG("multiple TypeInfo vars found for %s", name) 934 return None 935 if len(ti) == 0: 936 DBG("no TypeInfo var found for %s", name) 937 return None 938 return ti[0] 939 940class CreateClassStruct(DeclareInstanceChecker): 941 """Replace DECLARE_INSTANCE_CHECKER with OBJECT_DECLARE_SIMPLE_TYPE""" 942 def gen_patches(self) -> Iterable[Patch]: 943 typename = self.group('typename') 944 DBG("looking for TypeInfo variable for %s", typename) 945 var = find_type_info(self.allfiles, typename) 946 if var is None: 947 self.warn("no TypeInfo var found for %s", typename) 948 return 949 assert var.initializers 950 if 'class_size' in var.initializers: 951 self.warn("class size already set for TypeInfo %s", var.name) 952 return 953 classtype = self.group('instancetype')+'Class' 954 return 955 yield 956 #TODO: need to find out what's the parent class type... 957 #yield var.append_field('class_size', f'sizeof({classtype})') 958 #c = (f'OBJECT_DECLARE_SIMPLE_TYPE({instancetype}, {lowercase},\n' 959 # f' MODULE_OBJ_NAME, ParentClassType)\n') 960 #yield self.make_patch(c) 961 962def type_infos(file: FileInfo) -> Iterable[TypeInfoVar]: 963 return file.matches_of_type(TypeInfoVar) 964 965def full_types(file: FileInfo) -> Iterable[TypeInfoVar]: 966 return [t for t in type_infos(file) if t.is_full()] 967 968def partial_types(file: FileInfo) -> Iterable[TypeInfoVar]: 969 return [t for t in type_infos(file) if not t.is_full()] 970