194dfc0f3SEduardo Habkost# Copyright (C) 2020 Red Hat Inc. 294dfc0f3SEduardo Habkost# 394dfc0f3SEduardo Habkost# Authors: 494dfc0f3SEduardo Habkost# Eduardo Habkost <ehabkost@redhat.com> 594dfc0f3SEduardo Habkost# 694dfc0f3SEduardo Habkost# This work is licensed under the terms of the GNU GPL, version 2. See 794dfc0f3SEduardo Habkost# the COPYING file in the top-level directory. 894dfc0f3SEduardo Habkostimport re 994dfc0f3SEduardo Habkostfrom itertools import chain 1094dfc0f3SEduardo Habkostfrom typing import * 1194dfc0f3SEduardo Habkost 1294dfc0f3SEduardo Habkostfrom .regexps import * 1394dfc0f3SEduardo Habkostfrom .patching import * 1494dfc0f3SEduardo Habkostfrom .utils import * 1594dfc0f3SEduardo Habkost 1694dfc0f3SEduardo Habkostimport logging 1794dfc0f3SEduardo Habkostlogger = logging.getLogger(__name__) 1894dfc0f3SEduardo HabkostDBG = logger.debug 1994dfc0f3SEduardo HabkostINFO = logger.info 2094dfc0f3SEduardo HabkostWARN = logger.warning 2194dfc0f3SEduardo Habkost 2294dfc0f3SEduardo Habkost# simple expressions: 2394dfc0f3SEduardo Habkost 2494dfc0f3SEduardo HabkostRE_CONSTANT = OR(RE_STRING, RE_NUMBER) 2594dfc0f3SEduardo Habkost 264a15e5beSEduardo Habkostclass DefineDirective(FileMatch): 274a15e5beSEduardo Habkost """Match any #define directive""" 284a15e5beSEduardo Habkost regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER), r'\b') 294a15e5beSEduardo Habkost 304a15e5beSEduardo Habkostclass ExpressionDefine(FileMatch): 314a15e5beSEduardo Habkost """Simple #define preprocessor directive for an expression""" 3294dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER), 334a15e5beSEduardo Habkost CPP_SPACE, NAMED('value', RE_EXPRESSION), r'[ \t]*\n') 3494dfc0f3SEduardo Habkost 3594dfc0f3SEduardo Habkost def provided_identifiers(self) -> Iterable[RequiredIdentifier]: 3694dfc0f3SEduardo Habkost yield RequiredIdentifier('constant', self.group('name')) 3794dfc0f3SEduardo Habkost 384a15e5beSEduardo Habkostclass ConstantDefine(ExpressionDefine): 394a15e5beSEduardo Habkost """Simple #define preprocessor directive for a number or string constant""" 404a15e5beSEduardo Habkost regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER), 414a15e5beSEduardo Habkost CPP_SPACE, NAMED('value', RE_CONSTANT), r'[ \t]*\n') 424a15e5beSEduardo Habkost 434a15e5beSEduardo Habkost 4494dfc0f3SEduardo Habkostclass TypeIdentifiers(NamedTuple): 4594dfc0f3SEduardo Habkost """Type names found in type declarations""" 4694dfc0f3SEduardo Habkost # TYPE_MYDEVICE 4794dfc0f3SEduardo Habkost typename: Optional[str] 4894dfc0f3SEduardo Habkost # MYDEVICE 4994dfc0f3SEduardo Habkost uppercase: Optional[str] = None 5094dfc0f3SEduardo Habkost # MyDevice 5194dfc0f3SEduardo Habkost instancetype: Optional[str] = None 5294dfc0f3SEduardo Habkost # MyDeviceClass 5394dfc0f3SEduardo Habkost classtype: Optional[str] = None 5494dfc0f3SEduardo Habkost # my_device 5594dfc0f3SEduardo Habkost lowercase: Optional[str] = None 5694dfc0f3SEduardo Habkost 5794dfc0f3SEduardo Habkost def allfields(self): 5894dfc0f3SEduardo Habkost return tuple(getattr(self, f) for f in self._fields) 5994dfc0f3SEduardo Habkost 6094dfc0f3SEduardo Habkost def merge(self, other: 'TypeIdentifiers') -> Optional['TypeIdentifiers']: 6194dfc0f3SEduardo Habkost """Check if identifiers match, return new identifier with complete list""" 6294dfc0f3SEduardo Habkost if any(not opt_compare(a, b) for a,b in zip(self, other)): 6394dfc0f3SEduardo Habkost return None 6494dfc0f3SEduardo Habkost return TypeIdentifiers(*(merge(a, b) for a,b in zip(self, other))) 6594dfc0f3SEduardo Habkost 6694dfc0f3SEduardo Habkost def __str__(self) -> str: 6794dfc0f3SEduardo Habkost values = ((f, getattr(self, f)) for f in self._fields) 6894dfc0f3SEduardo Habkost s = ', '.join('%s=%s' % (f,v) for f,v in values if v is not None) 6994dfc0f3SEduardo Habkost return f'{s}' 7094dfc0f3SEduardo Habkost 7194dfc0f3SEduardo Habkost def check_consistency(self) -> List[str]: 7294dfc0f3SEduardo Habkost """Check if identifiers are consistent with each other, 7394dfc0f3SEduardo Habkost return list of problems (or empty list if everything seems consistent) 7494dfc0f3SEduardo Habkost """ 7594dfc0f3SEduardo Habkost r = [] 7694dfc0f3SEduardo Habkost if self.typename is None: 7794dfc0f3SEduardo Habkost r.append("typename (TYPE_MYDEVICE) is unavailable") 7894dfc0f3SEduardo Habkost 7994dfc0f3SEduardo Habkost if self.uppercase is None: 8094dfc0f3SEduardo Habkost r.append("uppercase name is unavailable") 8194dfc0f3SEduardo Habkost 8294dfc0f3SEduardo Habkost if (self.instancetype is not None 8394dfc0f3SEduardo Habkost and self.classtype is not None 8494dfc0f3SEduardo Habkost and self.classtype != f'{self.instancetype}Class'): 8594dfc0f3SEduardo Habkost r.append("class typedef %s doesn't match instance typedef %s" % 8694dfc0f3SEduardo Habkost (self.classtype, self.instancetype)) 8794dfc0f3SEduardo Habkost 8894dfc0f3SEduardo Habkost if (self.uppercase is not None 8994dfc0f3SEduardo Habkost and self.typename is not None 9094dfc0f3SEduardo Habkost and f'TYPE_{self.uppercase}' != self.typename): 9194dfc0f3SEduardo Habkost r.append("uppercase name (%s) doesn't match type name (%s)" % 9294dfc0f3SEduardo Habkost (self.uppercase, self.typename)) 9394dfc0f3SEduardo Habkost 9494dfc0f3SEduardo Habkost return r 9594dfc0f3SEduardo Habkost 9694dfc0f3SEduardo Habkostclass TypedefMatch(FileMatch): 9794dfc0f3SEduardo Habkost """typedef declaration""" 9894dfc0f3SEduardo Habkost def provided_identifiers(self) -> Iterable[RequiredIdentifier]: 9994dfc0f3SEduardo Habkost yield RequiredIdentifier('type', self.group('name')) 10094dfc0f3SEduardo Habkost 10194dfc0f3SEduardo Habkostclass SimpleTypedefMatch(TypedefMatch): 10294dfc0f3SEduardo Habkost """Simple typedef declaration 10394dfc0f3SEduardo Habkost (no replacement rules)""" 10494dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*typedef', SP, 10594dfc0f3SEduardo Habkost NAMED('typedef_type', RE_TYPE), SP, 10694dfc0f3SEduardo Habkost NAMED('name', RE_IDENTIFIER), r'\s*;[ \t]*\n') 10794dfc0f3SEduardo Habkost 10894dfc0f3SEduardo HabkostRE_MACRO_DEFINE = S(r'^[ \t]*#\s*define\s+', NAMED('name', RE_IDENTIFIER), 10994dfc0f3SEduardo Habkost r'\s*\(\s*', RE_IDENTIFIER, r'\s*\)', CPP_SPACE) 11094dfc0f3SEduardo Habkost 11194dfc0f3SEduardo HabkostRE_STRUCT_ATTRIBUTE = r'QEMU_PACKED' 11294dfc0f3SEduardo Habkost 11394dfc0f3SEduardo Habkost# This doesn't parse the struct definitions completely, it just assumes 11494dfc0f3SEduardo Habkost# the closing brackets are going to be in an unindented line: 11594dfc0f3SEduardo HabkostRE_FULL_STRUCT = S('struct', SP, M(RE_IDENTIFIER, n='?', name='structname'), SP, 11694dfc0f3SEduardo Habkost NAMED('body', r'{\n', 11794dfc0f3SEduardo Habkost # acceptable inside the struct body: 11894dfc0f3SEduardo Habkost # - lines starting with space or tab 11994dfc0f3SEduardo Habkost # - empty lines 12094dfc0f3SEduardo Habkost # - preprocessor directives 12194dfc0f3SEduardo Habkost # - comments 12294dfc0f3SEduardo Habkost OR(r'[ \t][^\n]*\n', 12394dfc0f3SEduardo Habkost r'#[^\n]*\n', 12494dfc0f3SEduardo Habkost r'\n', 12594dfc0f3SEduardo Habkost S(r'[ \t]*', RE_COMMENT, r'[ \t]*\n'), 12694dfc0f3SEduardo Habkost repeat='*?'), 12794dfc0f3SEduardo Habkost r'}', M(RE_STRUCT_ATTRIBUTE, SP, n='*'))) 12894dfc0f3SEduardo HabkostRE_STRUCT_TYPEDEF = S(r'^[ \t]*typedef', SP, RE_FULL_STRUCT, SP, 12994dfc0f3SEduardo Habkost NAMED('name', RE_IDENTIFIER), r'\s*;[ \t]*\n') 13094dfc0f3SEduardo Habkost 13194dfc0f3SEduardo Habkostclass FullStructTypedefMatch(TypedefMatch): 13294dfc0f3SEduardo Habkost """typedef struct [SomeStruct] { ...} SomeType 13394dfc0f3SEduardo Habkost Will be replaced by separate struct declaration + typedef 13494dfc0f3SEduardo Habkost """ 13594dfc0f3SEduardo Habkost regexp = RE_STRUCT_TYPEDEF 13694dfc0f3SEduardo Habkost 13794dfc0f3SEduardo Habkost def make_structname(self) -> str: 13894dfc0f3SEduardo Habkost """Make struct name for struct+typedef split""" 13994dfc0f3SEduardo Habkost name = self.group('structname') 14094dfc0f3SEduardo Habkost if not name: 14194dfc0f3SEduardo Habkost name = self.name 14294dfc0f3SEduardo Habkost return name 14394dfc0f3SEduardo Habkost 14494dfc0f3SEduardo Habkost def strip_typedef(self) -> Patch: 145d30b5bc9SMichael Tokarev """generate patch that will strip typedef from the struct declaration 14694dfc0f3SEduardo Habkost 14794dfc0f3SEduardo Habkost The caller is responsible for readding the typedef somewhere else. 14894dfc0f3SEduardo Habkost """ 14994dfc0f3SEduardo Habkost name = self.make_structname() 15094dfc0f3SEduardo Habkost body = self.group('body') 15194dfc0f3SEduardo Habkost return self.make_patch(f'struct {name} {body};\n') 15294dfc0f3SEduardo Habkost 15394dfc0f3SEduardo Habkost def make_simple_typedef(self) -> str: 15494dfc0f3SEduardo Habkost structname = self.make_structname() 15594dfc0f3SEduardo Habkost name = self.name 15694dfc0f3SEduardo Habkost return f'typedef struct {structname} {name};\n' 15794dfc0f3SEduardo Habkost 15894dfc0f3SEduardo Habkost def move_typedef(self, position) -> Iterator[Patch]: 15994dfc0f3SEduardo Habkost """Generate patches to move typedef elsewhere""" 16094dfc0f3SEduardo Habkost yield self.strip_typedef() 16194dfc0f3SEduardo Habkost yield Patch(position, position, self.make_simple_typedef()) 16294dfc0f3SEduardo Habkost 16394dfc0f3SEduardo Habkost def split_typedef(self) -> Iterator[Patch]: 16494dfc0f3SEduardo Habkost """Split into struct definition + typedef in-place""" 16594dfc0f3SEduardo Habkost yield self.strip_typedef() 16694dfc0f3SEduardo Habkost yield self.append(self.make_simple_typedef()) 16794dfc0f3SEduardo Habkost 16894dfc0f3SEduardo Habkostclass StructTypedefSplit(FullStructTypedefMatch): 16994dfc0f3SEduardo Habkost """split struct+typedef declaration""" 17094dfc0f3SEduardo Habkost def gen_patches(self) -> Iterator[Patch]: 17194dfc0f3SEduardo Habkost if self.group('structname'): 17294dfc0f3SEduardo Habkost yield from self.split_typedef() 17394dfc0f3SEduardo Habkost 17494dfc0f3SEduardo Habkostclass DuplicatedTypedefs(SimpleTypedefMatch): 17594dfc0f3SEduardo Habkost """Delete ALL duplicate typedefs (unsafe)""" 17694dfc0f3SEduardo Habkost def gen_patches(self) -> Iterable[Patch]: 17794dfc0f3SEduardo Habkost other_td = [td for td in chain(self.file.matches_of_type(SimpleTypedefMatch), 17894dfc0f3SEduardo Habkost self.file.matches_of_type(FullStructTypedefMatch)) 17994dfc0f3SEduardo Habkost if td.name == self.name] 18094dfc0f3SEduardo Habkost DBG("other_td: %r", other_td) 18194dfc0f3SEduardo Habkost if any(td.start() < self.start() for td in other_td): 18294dfc0f3SEduardo Habkost # patch only if handling the first typedef 18394dfc0f3SEduardo Habkost return 18494dfc0f3SEduardo Habkost for td in other_td: 18594dfc0f3SEduardo Habkost if isinstance(td, SimpleTypedefMatch): 18694dfc0f3SEduardo Habkost DBG("other td: %r", td.match.groupdict()) 18794dfc0f3SEduardo Habkost if td.group('typedef_type') != self.group('typedef_type'): 18894dfc0f3SEduardo Habkost yield td.make_removal_patch() 18994dfc0f3SEduardo Habkost elif isinstance(td, FullStructTypedefMatch): 19094dfc0f3SEduardo Habkost DBG("other td: %r", td.match.groupdict()) 19194dfc0f3SEduardo Habkost if self.group('typedef_type') == 'struct '+td.group('structname'): 19294dfc0f3SEduardo Habkost yield td.strip_typedef() 19394dfc0f3SEduardo Habkost 19494dfc0f3SEduardo Habkostclass QOMDuplicatedTypedefs(DuplicatedTypedefs): 19594dfc0f3SEduardo Habkost """Delete duplicate typedefs if used by QOM type""" 19694dfc0f3SEduardo Habkost def gen_patches(self) -> Iterable[Patch]: 19794dfc0f3SEduardo Habkost qom_macros = [TypeCheckMacro, DeclareInstanceChecker, DeclareClassCheckers, DeclareObjCheckers] 19894dfc0f3SEduardo Habkost qom_matches = chain(*(self.file.matches_of_type(t) for t in qom_macros)) 19994dfc0f3SEduardo Habkost in_use = any(RequiredIdentifier('type', self.name) in m.required_identifiers() 20094dfc0f3SEduardo Habkost for m in qom_matches) 20194dfc0f3SEduardo Habkost if in_use: 20294dfc0f3SEduardo Habkost yield from DuplicatedTypedefs.gen_patches(self) 20394dfc0f3SEduardo Habkost 20494dfc0f3SEduardo Habkostclass QOMStructTypedefSplit(FullStructTypedefMatch): 20594dfc0f3SEduardo Habkost """split struct+typedef declaration if used by QOM type""" 20694dfc0f3SEduardo Habkost def gen_patches(self) -> Iterator[Patch]: 20794dfc0f3SEduardo Habkost qom_macros = [TypeCheckMacro, DeclareInstanceChecker, DeclareClassCheckers, DeclareObjCheckers] 20894dfc0f3SEduardo Habkost qom_matches = chain(*(self.file.matches_of_type(t) for t in qom_macros)) 20994dfc0f3SEduardo Habkost in_use = any(RequiredIdentifier('type', self.name) in m.required_identifiers() 21094dfc0f3SEduardo Habkost for m in qom_matches) 21194dfc0f3SEduardo Habkost if in_use: 21294dfc0f3SEduardo Habkost yield from self.split_typedef() 21394dfc0f3SEduardo Habkost 21494dfc0f3SEduardo Habkostdef typedefs(file: FileInfo) -> Iterable[TypedefMatch]: 21594dfc0f3SEduardo Habkost return (cast(TypedefMatch, m) 21694dfc0f3SEduardo Habkost for m in chain(file.matches_of_type(SimpleTypedefMatch), 21794dfc0f3SEduardo Habkost file.matches_of_type(FullStructTypedefMatch))) 21894dfc0f3SEduardo Habkost 21994dfc0f3SEduardo Habkostdef find_typedef(f: FileInfo, name: Optional[str]) -> Optional[TypedefMatch]: 22094dfc0f3SEduardo Habkost if not name: 22194dfc0f3SEduardo Habkost return None 22294dfc0f3SEduardo Habkost for td in typedefs(f): 22394dfc0f3SEduardo Habkost if td.name == name: 22494dfc0f3SEduardo Habkost return td 22594dfc0f3SEduardo Habkost return None 22694dfc0f3SEduardo Habkost 22794dfc0f3SEduardo HabkostCHECKER_MACROS = ['OBJECT_CHECK', 'OBJECT_CLASS_CHECK', 'OBJECT_GET_CLASS'] 22894dfc0f3SEduardo HabkostCheckerMacroName = Literal['OBJECT_CHECK', 'OBJECT_CLASS_CHECK', 'OBJECT_GET_CLASS'] 22994dfc0f3SEduardo Habkost 23094dfc0f3SEduardo HabkostRE_CHECK_MACRO = \ 23194dfc0f3SEduardo Habkost S(RE_MACRO_DEFINE, 23294dfc0f3SEduardo Habkost OR(*CHECKER_MACROS, name='checker'), 23394dfc0f3SEduardo Habkost M(r'\s*\(\s*', OR(NAMED('typedefname', RE_IDENTIFIER), RE_TYPE, name='c_type'), r'\s*,', CPP_SPACE, 23494dfc0f3SEduardo Habkost OPTIONAL_PARS(RE_IDENTIFIER), r',', CPP_SPACE, 23594dfc0f3SEduardo Habkost NAMED('qom_typename', RE_IDENTIFIER), r'\s*\)\n', 23694dfc0f3SEduardo Habkost n='?', name='check_args')) 23794dfc0f3SEduardo Habkost 23894dfc0f3SEduardo HabkostEXPECTED_CHECKER_SUFFIXES: List[Tuple[CheckerMacroName, str]] = [ 23994dfc0f3SEduardo Habkost ('OBJECT_GET_CLASS', '_GET_CLASS'), 24094dfc0f3SEduardo Habkost ('OBJECT_CLASS_CHECK', '_CLASS'), 24194dfc0f3SEduardo Habkost] 24294dfc0f3SEduardo Habkost 24394dfc0f3SEduardo Habkostclass TypeCheckMacro(FileMatch): 24494dfc0f3SEduardo Habkost """OBJECT_CHECK/OBJECT_CLASS_CHECK/OBJECT_GET_CLASS macro definitions 24594dfc0f3SEduardo Habkost Will be replaced by DECLARE_*_CHECKERS macro 24694dfc0f3SEduardo Habkost """ 24794dfc0f3SEduardo Habkost regexp = RE_CHECK_MACRO 24894dfc0f3SEduardo Habkost 24994dfc0f3SEduardo Habkost @property 25094dfc0f3SEduardo Habkost def checker(self) -> CheckerMacroName: 25194dfc0f3SEduardo Habkost """Name of checker macro being used""" 2524a15e5beSEduardo Habkost return self.group('checker') # type: ignore 25394dfc0f3SEduardo Habkost 25494dfc0f3SEduardo Habkost @property 25594dfc0f3SEduardo Habkost def typedefname(self) -> Optional[str]: 25694dfc0f3SEduardo Habkost return self.group('typedefname') 25794dfc0f3SEduardo Habkost 25894dfc0f3SEduardo Habkost def find_typedef(self) -> Optional[TypedefMatch]: 25994dfc0f3SEduardo Habkost return find_typedef(self.file, self.typedefname) 26094dfc0f3SEduardo Habkost 26194dfc0f3SEduardo Habkost def sanity_check(self) -> None: 26294dfc0f3SEduardo Habkost DBG("groups: %r", self.match.groups()) 26394dfc0f3SEduardo Habkost if not self.group('check_args'): 26494dfc0f3SEduardo Habkost self.warn("type check macro not parsed completely: %s", self.name) 26594dfc0f3SEduardo Habkost return 26694dfc0f3SEduardo Habkost DBG("type identifiers: %r", self.type_identifiers) 26794dfc0f3SEduardo Habkost if self.typedefname and self.find_typedef() is None: 26894dfc0f3SEduardo Habkost self.warn("typedef used by %s not found", self.name) 26994dfc0f3SEduardo Habkost 27094dfc0f3SEduardo Habkost def find_matching_macros(self) -> List['TypeCheckMacro']: 27194dfc0f3SEduardo Habkost """Find other check macros that generate the same macro names 27294dfc0f3SEduardo Habkost 27394dfc0f3SEduardo Habkost The returned list will always be sorted. 27494dfc0f3SEduardo Habkost """ 27594dfc0f3SEduardo Habkost my_ids = self.type_identifiers 27694dfc0f3SEduardo Habkost assert my_ids 27794dfc0f3SEduardo Habkost return [m for m in self.file.matches_of_type(TypeCheckMacro) 27894dfc0f3SEduardo Habkost if m.type_identifiers is not None 27994dfc0f3SEduardo Habkost and my_ids.uppercase is not None 28094dfc0f3SEduardo Habkost and (my_ids.uppercase == m.type_identifiers.uppercase 28194dfc0f3SEduardo Habkost or my_ids.typename == m.type_identifiers.typename)] 28294dfc0f3SEduardo Habkost 28394dfc0f3SEduardo Habkost def merge_ids(self, matches: List['TypeCheckMacro']) -> Optional[TypeIdentifiers]: 28494dfc0f3SEduardo Habkost """Try to merge info about type identifiers from all matches in a list""" 28594dfc0f3SEduardo Habkost if not matches: 28694dfc0f3SEduardo Habkost return None 28794dfc0f3SEduardo Habkost r = matches[0].type_identifiers 28894dfc0f3SEduardo Habkost if r is None: 28994dfc0f3SEduardo Habkost return None 29094dfc0f3SEduardo Habkost for m in matches[1:]: 29194dfc0f3SEduardo Habkost assert m.type_identifiers 29294dfc0f3SEduardo Habkost new = r.merge(m.type_identifiers) 29394dfc0f3SEduardo Habkost if new is None: 29494dfc0f3SEduardo Habkost self.warn("macro %s identifiers (%s) don't match macro %s (%s)", 29594dfc0f3SEduardo Habkost matches[0].name, r, m.name, m.type_identifiers) 29694dfc0f3SEduardo Habkost return None 29794dfc0f3SEduardo Habkost r = new 29894dfc0f3SEduardo Habkost return r 29994dfc0f3SEduardo Habkost 30094dfc0f3SEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 30194dfc0f3SEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 30294dfc0f3SEduardo Habkost if self.type_identifiers is None: 30394dfc0f3SEduardo Habkost return 30494dfc0f3SEduardo Habkost # to make sure typedefs will be moved above all related macros, 30594dfc0f3SEduardo Habkost # return dependencies from all of them, not just this match 30694dfc0f3SEduardo Habkost for m in self.find_matching_macros(): 30794dfc0f3SEduardo Habkost yield RequiredIdentifier('type', m.group('c_type')) 30894dfc0f3SEduardo Habkost yield RequiredIdentifier('constant', m.group('qom_typename')) 30994dfc0f3SEduardo Habkost 31094dfc0f3SEduardo Habkost @property 31194dfc0f3SEduardo Habkost def type_identifiers(self) -> Optional[TypeIdentifiers]: 31294dfc0f3SEduardo Habkost """Extract type identifier information from match""" 31394dfc0f3SEduardo Habkost typename = self.group('qom_typename') 31494dfc0f3SEduardo Habkost c_type = self.group('c_type') 31594dfc0f3SEduardo Habkost if not typename or not c_type: 31694dfc0f3SEduardo Habkost return None 31794dfc0f3SEduardo Habkost typedef = self.group('typedefname') 31894dfc0f3SEduardo Habkost classtype = None 31994dfc0f3SEduardo Habkost instancetype = None 32094dfc0f3SEduardo Habkost uppercase = None 32194dfc0f3SEduardo Habkost expected_suffix = dict(EXPECTED_CHECKER_SUFFIXES).get(self.checker) 32294dfc0f3SEduardo Habkost 32394dfc0f3SEduardo Habkost # here the available data depends on the checker macro being called: 32494dfc0f3SEduardo Habkost # - we need to remove the suffix from the macro name 32594dfc0f3SEduardo Habkost # - depending on the macro type, we know the class type name, or 32694dfc0f3SEduardo Habkost # the instance type name 32794dfc0f3SEduardo Habkost if self.checker in ('OBJECT_GET_CLASS', 'OBJECT_CLASS_CHECK'): 32894dfc0f3SEduardo Habkost classtype = c_type 32994dfc0f3SEduardo Habkost elif self.checker == 'OBJECT_CHECK': 33094dfc0f3SEduardo Habkost instancetype = c_type 33194dfc0f3SEduardo Habkost uppercase = self.name 33294dfc0f3SEduardo Habkost else: 33394dfc0f3SEduardo Habkost assert False 33494dfc0f3SEduardo Habkost if expected_suffix and self.name.endswith(expected_suffix): 33594dfc0f3SEduardo Habkost uppercase = self.name[:-len(expected_suffix)] 33694dfc0f3SEduardo Habkost return TypeIdentifiers(typename=typename, classtype=classtype, 33794dfc0f3SEduardo Habkost instancetype=instancetype, uppercase=uppercase) 33894dfc0f3SEduardo Habkost 33994dfc0f3SEduardo Habkost def gen_patches(self) -> Iterable[Patch]: 3404a15e5beSEduardo Habkost # the implementation is a bit tricky because we need to group 3414a15e5beSEduardo Habkost # macros dealing with the same type into a single declaration 34294dfc0f3SEduardo Habkost if self.type_identifiers is None: 34394dfc0f3SEduardo Habkost self.warn("couldn't extract type information from macro %s", self.name) 34494dfc0f3SEduardo Habkost return 34594dfc0f3SEduardo Habkost 34694dfc0f3SEduardo Habkost if self.name == 'INTERFACE_CLASS': 34794dfc0f3SEduardo Habkost # INTERFACE_CLASS is special and won't be patched 34894dfc0f3SEduardo Habkost return 34994dfc0f3SEduardo Habkost 35094dfc0f3SEduardo Habkost for checker,suffix in EXPECTED_CHECKER_SUFFIXES: 35194dfc0f3SEduardo Habkost if self.name.endswith(suffix): 35294dfc0f3SEduardo Habkost if self.checker != checker: 35394dfc0f3SEduardo Habkost self.warn("macro %s is using macro %s instead of %s", self.name, self.checker, checker) 35494dfc0f3SEduardo Habkost return 35594dfc0f3SEduardo Habkost break 35694dfc0f3SEduardo Habkost 35794dfc0f3SEduardo Habkost matches = self.find_matching_macros() 35894dfc0f3SEduardo Habkost DBG("found %d matching macros: %s", len(matches), ' '.join(m.name for m in matches)) 35994dfc0f3SEduardo Habkost # we will generate patches only when processing the first macro: 36094dfc0f3SEduardo Habkost if matches[0].start != self.start: 36194dfc0f3SEduardo Habkost DBG("skipping %s (will patch when handling %s)", self.name, matches[0].name) 36294dfc0f3SEduardo Habkost return 36394dfc0f3SEduardo Habkost 36494dfc0f3SEduardo Habkost 36594dfc0f3SEduardo Habkost ids = self.merge_ids(matches) 36694dfc0f3SEduardo Habkost if ids is None: 36794dfc0f3SEduardo Habkost DBG("type identifier mismatch, won't patch %s", self.name) 36894dfc0f3SEduardo Habkost return 36994dfc0f3SEduardo Habkost 37094dfc0f3SEduardo Habkost if not ids.uppercase: 37194dfc0f3SEduardo Habkost self.warn("macro %s doesn't follow the expected name pattern", self.name) 37294dfc0f3SEduardo Habkost return 37394dfc0f3SEduardo Habkost if not ids.typename: 37494dfc0f3SEduardo Habkost self.warn("macro %s: couldn't extract type name", self.name) 37594dfc0f3SEduardo Habkost return 37694dfc0f3SEduardo Habkost 37794dfc0f3SEduardo Habkost #issues = ids.check_consistency() 37894dfc0f3SEduardo Habkost #if issues: 37994dfc0f3SEduardo Habkost # for i in issues: 38094dfc0f3SEduardo Habkost # self.warn("inconsistent identifiers: %s", i) 38194dfc0f3SEduardo Habkost 38294dfc0f3SEduardo Habkost names = [n for n in (ids.instancetype, ids.classtype, ids.uppercase, ids.typename) 38394dfc0f3SEduardo Habkost if n is not None] 38494dfc0f3SEduardo Habkost if len(set(names)) != len(names): 38594dfc0f3SEduardo Habkost self.warn("duplicate names used by macro: %r", ids) 38694dfc0f3SEduardo Habkost return 38794dfc0f3SEduardo Habkost 38894dfc0f3SEduardo Habkost assert ids.classtype or ids.instancetype 38994dfc0f3SEduardo Habkost assert ids.typename 39094dfc0f3SEduardo Habkost assert ids.uppercase 39194dfc0f3SEduardo Habkost if ids.classtype and ids.instancetype: 39294dfc0f3SEduardo Habkost new_decl = (f'DECLARE_OBJ_CHECKERS({ids.instancetype}, {ids.classtype},\n' 39394dfc0f3SEduardo Habkost f' {ids.uppercase}, {ids.typename})\n') 39494dfc0f3SEduardo Habkost elif ids.classtype: 39594dfc0f3SEduardo Habkost new_decl = (f'DECLARE_CLASS_CHECKERS({ids.classtype}, {ids.uppercase},\n' 39694dfc0f3SEduardo Habkost f' {ids.typename})\n') 39794dfc0f3SEduardo Habkost elif ids.instancetype: 39894dfc0f3SEduardo Habkost new_decl = (f'DECLARE_INSTANCE_CHECKER({ids.instancetype}, {ids.uppercase},\n' 39994dfc0f3SEduardo Habkost f' {ids.typename})\n') 40094dfc0f3SEduardo Habkost else: 40194dfc0f3SEduardo Habkost assert False 40294dfc0f3SEduardo Habkost 40394dfc0f3SEduardo Habkost # we need to ensure the typedefs are already available 40494dfc0f3SEduardo Habkost issues = [] 40594dfc0f3SEduardo Habkost for t in [ids.instancetype, ids.classtype]: 40694dfc0f3SEduardo Habkost if not t: 40794dfc0f3SEduardo Habkost continue 40894dfc0f3SEduardo Habkost if re.fullmatch(RE_STRUCT_TYPE, t): 40994dfc0f3SEduardo Habkost self.info("type %s is not a typedef", t) 41094dfc0f3SEduardo Habkost continue 41194dfc0f3SEduardo Habkost td = find_typedef(self.file, t) 41294dfc0f3SEduardo Habkost #if not td and self.allfiles.find_file('include/qemu/typedefs.h'): 41394dfc0f3SEduardo Habkost # 41494dfc0f3SEduardo Habkost if not td: 41594dfc0f3SEduardo Habkost # it is OK if the typedef is in typedefs.h 41694dfc0f3SEduardo Habkost f = self.allfiles.find_file('include/qemu/typedefs.h') 41794dfc0f3SEduardo Habkost if f and find_typedef(f, t): 41894dfc0f3SEduardo Habkost self.info("typedef %s found in typedefs.h", t) 41994dfc0f3SEduardo Habkost continue 42094dfc0f3SEduardo Habkost 42194dfc0f3SEduardo Habkost issues.append("couldn't find typedef %s" % (t)) 42294dfc0f3SEduardo Habkost elif td.start() > self.start(): 42394dfc0f3SEduardo Habkost issues.append("typedef %s need to be moved earlier in the file" % (td.name)) 42494dfc0f3SEduardo Habkost 42594dfc0f3SEduardo Habkost for issue in issues: 42694dfc0f3SEduardo Habkost self.warn(issue) 42794dfc0f3SEduardo Habkost 42894dfc0f3SEduardo Habkost if issues and not self.file.force: 42994dfc0f3SEduardo Habkost return 43094dfc0f3SEduardo Habkost 43194dfc0f3SEduardo Habkost # delete all matching macros and add new declaration: 43294dfc0f3SEduardo Habkost for m in matches: 43394dfc0f3SEduardo Habkost yield m.make_patch('') 43494dfc0f3SEduardo Habkost for issue in issues: 43594dfc0f3SEduardo Habkost yield self.prepend("/* FIXME: %s */\n" % (issue)) 43694dfc0f3SEduardo Habkost yield self.append(new_decl) 43794dfc0f3SEduardo Habkost 4384a15e5beSEduardo Habkostclass InterfaceCheckMacro(FileMatch): 4394a15e5beSEduardo Habkost """Type checking macro using INTERFACE_CHECK 4404a15e5beSEduardo Habkost Will be replaced by DECLARE_INTERFACE_CHECKER 44194dfc0f3SEduardo Habkost """ 4424a15e5beSEduardo Habkost regexp = S(RE_MACRO_DEFINE, 4434a15e5beSEduardo Habkost 'INTERFACE_CHECK', 4444a15e5beSEduardo Habkost r'\s*\(\s*', OR(NAMED('instancetype', RE_IDENTIFIER), RE_TYPE, name='c_type'), 4454a15e5beSEduardo Habkost r'\s*,', CPP_SPACE, 4464a15e5beSEduardo Habkost OPTIONAL_PARS(RE_IDENTIFIER), r',', CPP_SPACE, 4474a15e5beSEduardo Habkost NAMED('qom_typename', RE_IDENTIFIER), r'\s*\)\n') 4484a15e5beSEduardo Habkost 4494a15e5beSEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 4504a15e5beSEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 4514a15e5beSEduardo Habkost yield RequiredIdentifier('type', self.group('instancetype')) 4524a15e5beSEduardo Habkost yield RequiredIdentifier('constant', self.group('qom_typename')) 4534a15e5beSEduardo Habkost 4544a15e5beSEduardo Habkost def gen_patches(self) -> Iterable[Patch]: 4554a15e5beSEduardo Habkost if self.file.filename_matches('qom/object.h'): 4564a15e5beSEduardo Habkost self.debug("skipping object.h") 4574a15e5beSEduardo Habkost return 4584a15e5beSEduardo Habkost 4594a15e5beSEduardo Habkost typename = self.group('qom_typename') 4604a15e5beSEduardo Habkost uppercase = self.name 4614a15e5beSEduardo Habkost instancetype = self.group('instancetype') 4624a15e5beSEduardo Habkost c = f"DECLARE_INTERFACE_CHECKER({instancetype}, {uppercase},\n"+\ 4634a15e5beSEduardo Habkost f" {typename})\n" 4644a15e5beSEduardo Habkost yield self.make_patch(c) 4654a15e5beSEduardo Habkost 4664a15e5beSEduardo Habkost 4674a15e5beSEduardo Habkostclass TypeDeclaration(FileMatch): 4684a15e5beSEduardo Habkost """Parent class to all type declarations""" 4694a15e5beSEduardo Habkost @property 4704a15e5beSEduardo Habkost def instancetype(self) -> Optional[str]: 4714a15e5beSEduardo Habkost return self.getgroup('instancetype') 4724a15e5beSEduardo Habkost 4734a15e5beSEduardo Habkost @property 4744a15e5beSEduardo Habkost def classtype(self) -> Optional[str]: 4754a15e5beSEduardo Habkost return self.getgroup('classtype') 4764a15e5beSEduardo Habkost 4774a15e5beSEduardo Habkost @property 4784a15e5beSEduardo Habkost def typename(self) -> Optional[str]: 4794a15e5beSEduardo Habkost return self.getgroup('typename') 4804a15e5beSEduardo Habkost 4814a15e5beSEduardo Habkostclass TypeCheckerDeclaration(TypeDeclaration): 4824a15e5beSEduardo Habkost """Parent class to all type checker declarations""" 4834a15e5beSEduardo Habkost @property 4844a15e5beSEduardo Habkost def typename(self) -> str: 4854a15e5beSEduardo Habkost return self.group('typename') 4864a15e5beSEduardo Habkost 4874a15e5beSEduardo Habkost @property 4884a15e5beSEduardo Habkost def uppercase(self) -> str: 4894a15e5beSEduardo Habkost return self.group('uppercase') 4904a15e5beSEduardo Habkost 4914a15e5beSEduardo Habkostclass DeclareInstanceChecker(TypeCheckerDeclaration): 4924a15e5beSEduardo Habkost """DECLARE_INSTANCE_CHECKER use""" 49394dfc0f3SEduardo Habkost #TODO: replace lonely DECLARE_INSTANCE_CHECKER with DECLARE_OBJ_CHECKERS 49494dfc0f3SEduardo Habkost # if all types are found. 49594dfc0f3SEduardo Habkost # This will require looking up the correct class type in the TypeInfo 49694dfc0f3SEduardo Habkost # structs in another file 49794dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*DECLARE_INSTANCE_CHECKER\s*\(\s*', 49894dfc0f3SEduardo Habkost NAMED('instancetype', RE_TYPE), r'\s*,\s*', 49994dfc0f3SEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 50094dfc0f3SEduardo Habkost OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'), SP, 50194dfc0f3SEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 50294dfc0f3SEduardo Habkost 50394dfc0f3SEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 50494dfc0f3SEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 50594dfc0f3SEduardo Habkost yield RequiredIdentifier('constant', self.group('typename')) 50694dfc0f3SEduardo Habkost yield RequiredIdentifier('type', self.group('instancetype')) 50794dfc0f3SEduardo Habkost 5084a15e5beSEduardo Habkostclass DeclareInterfaceChecker(TypeCheckerDeclaration): 5094a15e5beSEduardo Habkost """DECLARE_INTERFACE_CHECKER use""" 5104a15e5beSEduardo Habkost regexp = S(r'^[ \t]*DECLARE_INTERFACE_CHECKER\s*\(\s*', 5114a15e5beSEduardo Habkost NAMED('instancetype', RE_TYPE), r'\s*,\s*', 5124a15e5beSEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 5134a15e5beSEduardo Habkost OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'), SP, 5144a15e5beSEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 5154a15e5beSEduardo Habkost 5164a15e5beSEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 5174a15e5beSEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 5184a15e5beSEduardo Habkost yield RequiredIdentifier('constant', self.group('typename')) 5194a15e5beSEduardo Habkost yield RequiredIdentifier('type', self.group('instancetype')) 5204a15e5beSEduardo Habkost 5214a15e5beSEduardo Habkostclass DeclareInstanceType(TypeDeclaration): 5224a15e5beSEduardo Habkost """DECLARE_INSTANCE_TYPE use""" 5234a15e5beSEduardo Habkost regexp = S(r'^[ \t]*DECLARE_INSTANCE_TYPE\s*\(\s*', 5244a15e5beSEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 5254a15e5beSEduardo Habkost NAMED('instancetype', RE_TYPE), SP, 5264a15e5beSEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 5274a15e5beSEduardo Habkost 5284a15e5beSEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 5294a15e5beSEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 5304a15e5beSEduardo Habkost yield RequiredIdentifier('type', self.group('instancetype')) 5314a15e5beSEduardo Habkost 5324a15e5beSEduardo Habkostclass DeclareClassType(TypeDeclaration): 5334a15e5beSEduardo Habkost """DECLARE_CLASS_TYPE use""" 5344a15e5beSEduardo Habkost regexp = S(r'^[ \t]*DECLARE_CLASS_TYPE\s*\(\s*', 5354a15e5beSEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 5364a15e5beSEduardo Habkost NAMED('classtype', RE_TYPE), SP, 5374a15e5beSEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 5384a15e5beSEduardo Habkost 5394a15e5beSEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 5404a15e5beSEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 5414a15e5beSEduardo Habkost yield RequiredIdentifier('type', self.group('classtype')) 5424a15e5beSEduardo Habkost 5434a15e5beSEduardo Habkost 5444a15e5beSEduardo Habkost 5454a15e5beSEduardo Habkostclass DeclareClassCheckers(TypeCheckerDeclaration): 5464a15e5beSEduardo Habkost """DECLARE_CLASS_CHECKER use""" 54794dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*DECLARE_CLASS_CHECKERS\s*\(\s*', 54894dfc0f3SEduardo Habkost NAMED('classtype', RE_TYPE), r'\s*,\s*', 54994dfc0f3SEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 55094dfc0f3SEduardo Habkost OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'), SP, 55194dfc0f3SEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 55294dfc0f3SEduardo Habkost 55394dfc0f3SEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 55494dfc0f3SEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 55594dfc0f3SEduardo Habkost yield RequiredIdentifier('constant', self.group('typename')) 55694dfc0f3SEduardo Habkost yield RequiredIdentifier('type', self.group('classtype')) 55794dfc0f3SEduardo Habkost 5584a15e5beSEduardo Habkostclass DeclareObjCheckers(TypeCheckerDeclaration): 5594a15e5beSEduardo Habkost """DECLARE_OBJ_CHECKERS use""" 56094dfc0f3SEduardo Habkost #TODO: detect when OBJECT_DECLARE_SIMPLE_TYPE can be used 56194dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*DECLARE_OBJ_CHECKERS\s*\(\s*', 56294dfc0f3SEduardo Habkost NAMED('instancetype', RE_TYPE), r'\s*,\s*', 56394dfc0f3SEduardo Habkost NAMED('classtype', RE_TYPE), r'\s*,\s*', 56494dfc0f3SEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 56594dfc0f3SEduardo Habkost OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'), SP, 56694dfc0f3SEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 56794dfc0f3SEduardo Habkost 56894dfc0f3SEduardo Habkost def required_identifiers(self) -> Iterable[RequiredIdentifier]: 56994dfc0f3SEduardo Habkost yield RequiredIdentifier('include', '"qom/object.h"') 57094dfc0f3SEduardo Habkost yield RequiredIdentifier('constant', self.group('typename')) 57194dfc0f3SEduardo Habkost yield RequiredIdentifier('type', self.group('classtype')) 57294dfc0f3SEduardo Habkost yield RequiredIdentifier('type', self.group('instancetype')) 57394dfc0f3SEduardo Habkost 5744a15e5beSEduardo Habkostclass TypeDeclarationFixup(FileMatch): 5754a15e5beSEduardo Habkost """Common base class for code that will look at a set of type declarations""" 5764a15e5beSEduardo Habkost regexp = RE_FILE_BEGIN 5774a15e5beSEduardo Habkost def gen_patches(self) -> Iterable[Patch]: 5784a15e5beSEduardo Habkost if self.file.filename_matches('qom/object.h'): 5794a15e5beSEduardo Habkost self.debug("skipping object.h") 58094dfc0f3SEduardo Habkost return 58194dfc0f3SEduardo Habkost 5824a15e5beSEduardo Habkost # group checkers by uppercase name: 5834a15e5beSEduardo Habkost decl_types: List[Type[TypeDeclaration]] = [DeclareInstanceChecker, DeclareInstanceType, 5844a15e5beSEduardo Habkost DeclareClassCheckers, DeclareClassType, 5854a15e5beSEduardo Habkost DeclareObjCheckers] 5864a15e5beSEduardo Habkost checker_dict: Dict[str, List[TypeDeclaration]] = {} 5874a15e5beSEduardo Habkost for t in decl_types: 5884a15e5beSEduardo Habkost for m in self.file.matches_of_type(t): 5894a15e5beSEduardo Habkost checker_dict.setdefault(m.group('uppercase'), []).append(m) 5904a15e5beSEduardo Habkost self.debug("checker_dict: %r", checker_dict) 5914a15e5beSEduardo Habkost for uppercase,checkers in checker_dict.items(): 5924a15e5beSEduardo Habkost fields = ('instancetype', 'classtype', 'uppercase', 'typename') 5934a15e5beSEduardo Habkost fvalues = dict((field, set(getattr(m, field) for m in checkers 5944a15e5beSEduardo Habkost if getattr(m, field, None) is not None)) 5954a15e5beSEduardo Habkost for field in fields) 5964a15e5beSEduardo Habkost for field,values in fvalues.items(): 5974a15e5beSEduardo Habkost if len(values) > 1: 5984a15e5beSEduardo Habkost for c in checkers: 5994a15e5beSEduardo Habkost c.warn("%s mismatch (%s)", field, ' '.join(values)) 60094dfc0f3SEduardo Habkost return 60194dfc0f3SEduardo Habkost 6024a15e5beSEduardo Habkost field_dict = dict((f, v.pop() if v else None) for f,v in fvalues.items()) 6034a15e5beSEduardo Habkost yield from self.gen_patches_for_type(uppercase, checkers, field_dict) 60494dfc0f3SEduardo Habkost 6054a15e5beSEduardo Habkost def find_conflicts(self, uppercase: str, checkers: List[TypeDeclaration]) -> bool: 6064a15e5beSEduardo Habkost """Look for conflicting declarations that would make it unsafe to add new ones""" 6074a15e5beSEduardo Habkost conflicting: List[FileMatch] = [] 6084a15e5beSEduardo Habkost # conflicts in the same file: 6094a15e5beSEduardo Habkost conflicting.extend(chain(self.file.find_matches(DefineDirective, uppercase), 6104a15e5beSEduardo Habkost self.file.find_matches(DeclareInterfaceChecker, uppercase, 'uppercase'), 6114a15e5beSEduardo Habkost self.file.find_matches(DeclareClassType, uppercase, 'uppercase'), 6124a15e5beSEduardo Habkost self.file.find_matches(DeclareInstanceType, uppercase, 'uppercase'))) 6134a15e5beSEduardo Habkost 6144a15e5beSEduardo Habkost # conflicts in another file: 6154a15e5beSEduardo Habkost conflicting.extend(o for o in chain(self.allfiles.find_matches(DeclareInstanceChecker, uppercase, 'uppercase'), 6164a15e5beSEduardo Habkost self.allfiles.find_matches(DeclareClassCheckers, uppercase, 'uppercase'), 6174a15e5beSEduardo Habkost self.allfiles.find_matches(DeclareInterfaceChecker, uppercase, 'uppercase'), 6184a15e5beSEduardo Habkost self.allfiles.find_matches(DefineDirective, uppercase)) 6194a15e5beSEduardo Habkost if o is not None and o.file != self.file 6204a15e5beSEduardo Habkost # if both are .c files, there's no conflict at all: 6214a15e5beSEduardo Habkost and not (o.file.filename.suffix == '.c' and 6224a15e5beSEduardo Habkost self.file.filename.suffix == '.c')) 6234a15e5beSEduardo Habkost 6244a15e5beSEduardo Habkost if conflicting: 6254a15e5beSEduardo Habkost for c in checkers: 6264a15e5beSEduardo Habkost c.warn("skipping due to conflicting %s macro", uppercase) 6274a15e5beSEduardo Habkost for o in conflicting: 6284a15e5beSEduardo Habkost if o is None: 6294a15e5beSEduardo Habkost continue 6304a15e5beSEduardo Habkost o.warn("conflicting %s macro is here", uppercase) 6314a15e5beSEduardo Habkost return True 6324a15e5beSEduardo Habkost 6334a15e5beSEduardo Habkost return False 6344a15e5beSEduardo Habkost 6354a15e5beSEduardo Habkost def gen_patches_for_type(self, uppercase: str, 6364a15e5beSEduardo Habkost checkers: List[TypeDeclaration], 6374a15e5beSEduardo Habkost fields: Dict[str, Optional[str]]) -> Iterable[Patch]: 6384a15e5beSEduardo Habkost """Should be reimplemented by subclasses""" 6394a15e5beSEduardo Habkost return 6404a15e5beSEduardo Habkost yield 6414a15e5beSEduardo Habkost 6424a15e5beSEduardo Habkostclass DeclareVoidTypes(TypeDeclarationFixup): 6434a15e5beSEduardo Habkost """Add DECLARE_*_TYPE(..., void) when there's no declared type""" 6444a15e5beSEduardo Habkost regexp = RE_FILE_BEGIN 6454a15e5beSEduardo Habkost def gen_patches_for_type(self, uppercase: str, 6464a15e5beSEduardo Habkost checkers: List[TypeDeclaration], 6474a15e5beSEduardo Habkost fields: Dict[str, Optional[str]]) -> Iterable[Patch]: 6484a15e5beSEduardo Habkost if self.find_conflicts(uppercase, checkers): 6494a15e5beSEduardo Habkost return 6504a15e5beSEduardo Habkost 6514a15e5beSEduardo Habkost #_,last_checker = max((m.start(), m) for m in checkers) 6524a15e5beSEduardo Habkost _,first_checker = min((m.start(), m) for m in checkers) 6534a15e5beSEduardo Habkost 6544a15e5beSEduardo Habkost if not any(m.instancetype for m in checkers): 6554a15e5beSEduardo Habkost yield first_checker.prepend(f'DECLARE_INSTANCE_TYPE({uppercase}, void)\n') 6564a15e5beSEduardo Habkost if not any(m.classtype for m in checkers): 6574a15e5beSEduardo Habkost yield first_checker.prepend(f'DECLARE_CLASS_TYPE({uppercase}, void)\n') 6584a15e5beSEduardo Habkost 6594a15e5beSEduardo Habkost #if not all(len(v) == 1 for v in fvalues.values()): 6604a15e5beSEduardo Habkost # return 6614a15e5beSEduardo Habkost # 6624a15e5beSEduardo Habkost #final_values = dict((field, values.pop()) 6634a15e5beSEduardo Habkost # for field,values in fvalues.items()) 6644a15e5beSEduardo Habkost #s = (f"DECLARE_OBJ_CHECKERS({final_values['instancetype']}, {final_values['classtype']},\n"+ 6654a15e5beSEduardo Habkost # f" {final_values['uppercase']}, {final_values['typename']})\n") 6664a15e5beSEduardo Habkost #for c in checkers: 6674a15e5beSEduardo Habkost # yield c.make_removal_patch() 6684a15e5beSEduardo Habkost #yield last_checker.append(s) 6694a15e5beSEduardo Habkost 6704a15e5beSEduardo Habkost 6714a15e5beSEduardo Habkostclass AddDeclareTypeName(TypeDeclarationFixup): 6724a15e5beSEduardo Habkost """Add DECLARE_TYPE_NAME declarations if necessary""" 6734a15e5beSEduardo Habkost def gen_patches_for_type(self, uppercase: str, 6744a15e5beSEduardo Habkost checkers: List[TypeDeclaration], 6754a15e5beSEduardo Habkost fields: Dict[str, Optional[str]]) -> Iterable[Patch]: 6764a15e5beSEduardo Habkost typename = fields.get('typename') 6774a15e5beSEduardo Habkost if typename is None: 6784a15e5beSEduardo Habkost self.warn("typename unavailable") 6794a15e5beSEduardo Habkost return 6804a15e5beSEduardo Habkost if typename == f'TYPE_{uppercase}': 6814a15e5beSEduardo Habkost self.info("already using TYPE_%s as type name", uppercase) 6824a15e5beSEduardo Habkost return 6834a15e5beSEduardo Habkost if self.file.find_match(DeclareTypeName, uppercase, 'uppercase'): 6844a15e5beSEduardo Habkost self.info("type name for %s already declared", uppercase) 6854a15e5beSEduardo Habkost return 6864a15e5beSEduardo Habkost _,first_checker = min((m.start(), m) for m in checkers) 6874a15e5beSEduardo Habkost s = f'DECLARE_TYPE_NAME({uppercase}, {typename})\n' 6884a15e5beSEduardo Habkost yield first_checker.prepend(s) 68994dfc0f3SEduardo Habkost 69094dfc0f3SEduardo Habkostclass TrivialClassStruct(FileMatch): 69194dfc0f3SEduardo Habkost """Trivial class struct""" 69294dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*struct\s*', NAMED('name', RE_IDENTIFIER), 69394dfc0f3SEduardo Habkost r'\s*{\s*', NAMED('parent_struct', RE_IDENTIFIER), r'\s*parent(_class)?\s*;\s*};\n') 69494dfc0f3SEduardo Habkost 69594dfc0f3SEduardo Habkostclass DeclareTypeName(FileMatch): 69694dfc0f3SEduardo Habkost """DECLARE_TYPE_NAME usage""" 69794dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*DECLARE_TYPE_NAME\s*\(', 69894dfc0f3SEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 69994dfc0f3SEduardo Habkost OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'), 70094dfc0f3SEduardo Habkost r'\s*\);?[ \t]*\n') 70194dfc0f3SEduardo Habkost 7024a15e5beSEduardo Habkostclass ObjectDeclareType(TypeCheckerDeclaration): 70394dfc0f3SEduardo Habkost """OBJECT_DECLARE_TYPE usage 70494dfc0f3SEduardo Habkost Will be replaced with OBJECT_DECLARE_SIMPLE_TYPE if possible 70594dfc0f3SEduardo Habkost """ 70694dfc0f3SEduardo Habkost regexp = S(r'^[ \t]*OBJECT_DECLARE_TYPE\s*\(', 70794dfc0f3SEduardo Habkost NAMED('instancetype', RE_TYPE), r'\s*,\s*', 70894dfc0f3SEduardo Habkost NAMED('classtype', RE_TYPE), r'\s*,\s*', 70994dfc0f3SEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), SP, 71094dfc0f3SEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 71194dfc0f3SEduardo Habkost 71294dfc0f3SEduardo Habkost def gen_patches(self): 71394dfc0f3SEduardo Habkost DBG("groups: %r", self.match.groupdict()) 71494dfc0f3SEduardo Habkost trivial_struct = self.file.find_match(TrivialClassStruct, self.group('classtype')) 71594dfc0f3SEduardo Habkost if trivial_struct: 71694dfc0f3SEduardo Habkost d = self.match.groupdict().copy() 71794dfc0f3SEduardo Habkost d['parent_struct'] = trivial_struct.group("parent_struct") 71894dfc0f3SEduardo Habkost yield trivial_struct.make_removal_patch() 71994dfc0f3SEduardo Habkost c = ("OBJECT_DECLARE_SIMPLE_TYPE(%(instancetype)s, %(lowercase)s,\n" 72094dfc0f3SEduardo Habkost " %(uppercase)s, %(parent_struct)s)\n" % d) 72194dfc0f3SEduardo Habkost yield self.make_patch(c) 72294dfc0f3SEduardo Habkost 7234a15e5beSEduardo Habkostclass ObjectDeclareSimpleType(TypeCheckerDeclaration): 7244a15e5beSEduardo Habkost """OBJECT_DECLARE_SIMPLE_TYPE usage""" 7254a15e5beSEduardo Habkost regexp = S(r'^[ \t]*OBJECT_DECLARE_SIMPLE_TYPE\s*\(', 7264a15e5beSEduardo Habkost NAMED('instancetype', RE_TYPE), r'\s*,\s*', 7274a15e5beSEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), SP, 7284a15e5beSEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 7294a15e5beSEduardo Habkost 7304a15e5beSEduardo Habkostclass OldStyleObjectDeclareSimpleType(TypeCheckerDeclaration): 7314a15e5beSEduardo Habkost """OBJECT_DECLARE_SIMPLE_TYPE usage (old API)""" 7324a15e5beSEduardo Habkost regexp = S(r'^[ \t]*OBJECT_DECLARE_SIMPLE_TYPE\s*\(', 7334a15e5beSEduardo Habkost NAMED('instancetype', RE_TYPE), r'\s*,\s*', 7344a15e5beSEduardo Habkost NAMED('lowercase', RE_IDENTIFIER), r'\s*,\s*', 7354a15e5beSEduardo Habkost NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*', 7364a15e5beSEduardo Habkost NAMED('parent_classtype', RE_TYPE), SP, 7374a15e5beSEduardo Habkost r'\)[ \t]*;?[ \t]*\n') 7384a15e5beSEduardo Habkost 7394a15e5beSEduardo Habkost @property 7404a15e5beSEduardo Habkost def classtype(self) -> Optional[str]: 7414a15e5beSEduardo Habkost instancetype = self.instancetype 7424a15e5beSEduardo Habkost assert instancetype 7434a15e5beSEduardo Habkost return f"{instancetype}Class" 7444a15e5beSEduardo Habkost 7454a15e5beSEduardo Habkostdef find_typename_uppercase(files: FileList, typename: str) -> Optional[str]: 7464a15e5beSEduardo Habkost """Try to find what's the right MODULE_OBJ_NAME for a given type name""" 7474a15e5beSEduardo Habkost decl = files.find_match(DeclareTypeName, name=typename, group='typename') 7484a15e5beSEduardo Habkost if decl: 7494a15e5beSEduardo Habkost return decl.group('uppercase') 7504a15e5beSEduardo Habkost if typename.startswith('TYPE_'): 7514a15e5beSEduardo Habkost return typename[len('TYPE_'):] 75294dfc0f3SEduardo Habkost return None 75394dfc0f3SEduardo Habkost 7544a15e5beSEduardo Habkostdef find_type_checkers(files:FileList, name:str, group:str='uppercase') -> Iterable[TypeCheckerDeclaration]: 7554a15e5beSEduardo Habkost """Find usage of DECLARE*CHECKER macro""" 7564a15e5beSEduardo Habkost c: Type[TypeCheckerDeclaration] 7574a15e5beSEduardo Habkost for c in (DeclareInstanceChecker, DeclareClassCheckers, DeclareObjCheckers, ObjectDeclareType, ObjectDeclareSimpleType): 7584a15e5beSEduardo Habkost yield from files.find_matches(c, name=name, group=group) 75994dfc0f3SEduardo Habkost 76094dfc0f3SEduardo Habkostclass Include(FileMatch): 76194dfc0f3SEduardo Habkost """#include directive""" 76294dfc0f3SEduardo Habkost regexp = RE_INCLUDE 76394dfc0f3SEduardo Habkost def provided_identifiers(self) -> Iterable[RequiredIdentifier]: 76494dfc0f3SEduardo Habkost yield RequiredIdentifier('include', self.group('includepath')) 76594dfc0f3SEduardo Habkost 76694dfc0f3SEduardo Habkostclass InitialIncludes(FileMatch): 76794dfc0f3SEduardo Habkost """Initial #include block""" 76894dfc0f3SEduardo Habkost regexp = S(RE_FILE_BEGIN, 76994dfc0f3SEduardo Habkost M(SP, RE_COMMENTS, 77094dfc0f3SEduardo Habkost r'^[ \t]*#[ \t]*ifndef[ \t]+', RE_IDENTIFIER, r'[ \t]*\n', 77194dfc0f3SEduardo Habkost n='?', name='ifndef_block'), 77294dfc0f3SEduardo Habkost M(SP, RE_COMMENTS, 77394dfc0f3SEduardo Habkost OR(RE_INCLUDE, RE_SIMPLEDEFINE), 77494dfc0f3SEduardo Habkost n='*', name='includes')) 77594dfc0f3SEduardo Habkost 77694dfc0f3SEduardo Habkostclass SymbolUserList(NamedTuple): 77794dfc0f3SEduardo Habkost definitions: List[FileMatch] 77894dfc0f3SEduardo Habkost users: List[FileMatch] 77994dfc0f3SEduardo Habkost 78094dfc0f3SEduardo Habkostclass MoveSymbols(FileMatch): 78194dfc0f3SEduardo Habkost """Handle missing symbols 78294dfc0f3SEduardo Habkost - Move typedefs and defines when necessary 78394dfc0f3SEduardo Habkost - Add missing #include lines when necessary 78494dfc0f3SEduardo Habkost """ 78594dfc0f3SEduardo Habkost regexp = RE_FILE_BEGIN 78694dfc0f3SEduardo Habkost 78794dfc0f3SEduardo Habkost def gen_patches(self) -> Iterator[Patch]: 7884a15e5beSEduardo Habkost if self.file.filename_matches('qom/object.h'): 7894a15e5beSEduardo Habkost self.debug("skipping object.h") 7904a15e5beSEduardo Habkost return 7914a15e5beSEduardo Habkost 79294dfc0f3SEduardo Habkost index: Dict[RequiredIdentifier, SymbolUserList] = {} 79394dfc0f3SEduardo Habkost definition_classes = [SimpleTypedefMatch, FullStructTypedefMatch, ConstantDefine, Include] 7944a15e5beSEduardo Habkost user_classes = [TypeCheckMacro, DeclareObjCheckers, DeclareInstanceChecker, DeclareClassCheckers, InterfaceCheckMacro] 79594dfc0f3SEduardo Habkost 79694dfc0f3SEduardo Habkost # first we scan for all symbol definitions and usage: 79794dfc0f3SEduardo Habkost for dc in definition_classes: 79894dfc0f3SEduardo Habkost defs = self.file.matches_of_type(dc) 79994dfc0f3SEduardo Habkost for d in defs: 80094dfc0f3SEduardo Habkost DBG("scanning %r", d) 80194dfc0f3SEduardo Habkost for i in d.provided_identifiers(): 80294dfc0f3SEduardo Habkost index.setdefault(i, SymbolUserList([], [])).definitions.append(d) 80394dfc0f3SEduardo Habkost DBG("index: %r", list(index.keys())) 80494dfc0f3SEduardo Habkost for uc in user_classes: 80594dfc0f3SEduardo Habkost users = self.file.matches_of_type(uc) 80694dfc0f3SEduardo Habkost for u in users: 80794dfc0f3SEduardo Habkost for i in u.required_identifiers(): 80894dfc0f3SEduardo Habkost index.setdefault(i, SymbolUserList([], [])).users.append(u) 80994dfc0f3SEduardo Habkost 81094dfc0f3SEduardo Habkost # validate all symbols: 81194dfc0f3SEduardo Habkost for i,ul in index.items(): 81294dfc0f3SEduardo Habkost if not ul.users: 81394dfc0f3SEduardo Habkost # unused symbol 81494dfc0f3SEduardo Habkost continue 81594dfc0f3SEduardo Habkost 81694dfc0f3SEduardo Habkost # symbol not defined 81794dfc0f3SEduardo Habkost if len(ul.definitions) == 0: 81894dfc0f3SEduardo Habkost if i.type == 'include': 81994dfc0f3SEduardo Habkost includes, = self.file.matches_of_type(InitialIncludes) 82094dfc0f3SEduardo Habkost #FIXME: don't do this if we're already inside qom/object.h 82194dfc0f3SEduardo Habkost yield includes.append(f'#include {i.name}\n') 82294dfc0f3SEduardo Habkost else: 82394dfc0f3SEduardo Habkost u.warn("definition of %s %s not found in file", i.type, i.name) 82494dfc0f3SEduardo Habkost continue 82594dfc0f3SEduardo Habkost 82694dfc0f3SEduardo Habkost # symbol defined twice: 82794dfc0f3SEduardo Habkost if len(ul.definitions) > 1: 82894dfc0f3SEduardo Habkost ul.definitions[1].warn("%s defined twice", i.name) 82994dfc0f3SEduardo Habkost ul.definitions[0].warn("previously defined here") 83094dfc0f3SEduardo Habkost continue 83194dfc0f3SEduardo Habkost 83294dfc0f3SEduardo Habkost # symbol defined. check if all users are after its definition: 83394dfc0f3SEduardo Habkost assert len(ul.definitions) == 1 83494dfc0f3SEduardo Habkost definition = ul.definitions[0] 83594dfc0f3SEduardo Habkost DBG("handling repositioning of %r", definition) 83694dfc0f3SEduardo Habkost earliest = min(ul.users, key=lambda u: u.start()) 83794dfc0f3SEduardo Habkost if earliest.start() > definition.start(): 83894dfc0f3SEduardo Habkost DBG("%r is OK", definition) 83994dfc0f3SEduardo Habkost continue 84094dfc0f3SEduardo Habkost 84194dfc0f3SEduardo Habkost DBG("%r needs to be moved", definition) 84294dfc0f3SEduardo Habkost if isinstance(definition, SimpleTypedefMatch) \ 84394dfc0f3SEduardo Habkost or isinstance(definition, ConstantDefine): 84494dfc0f3SEduardo Habkost # simple typedef or define can be moved directly: 84594dfc0f3SEduardo Habkost yield definition.make_removal_patch() 84694dfc0f3SEduardo Habkost yield earliest.prepend(definition.group(0)) 84794dfc0f3SEduardo Habkost elif isinstance(definition, FullStructTypedefMatch) \ 84894dfc0f3SEduardo Habkost and definition.group('structname'): 84994dfc0f3SEduardo Habkost # full struct typedef is more complex: we need to remove 85094dfc0f3SEduardo Habkost # the typedef 85194dfc0f3SEduardo Habkost yield from definition.move_typedef(earliest.start()) 85294dfc0f3SEduardo Habkost else: 85394dfc0f3SEduardo Habkost definition.warn("definition of %s %s needs to be moved earlier in the file", i.type, i.name) 85494dfc0f3SEduardo Habkost earliest.warn("definition of %s %s is used here", i.type, i.name) 85594dfc0f3SEduardo Habkost 8564a15e5beSEduardo Habkost 8574a15e5beSEduardo Habkostclass EmptyPreprocessorConditional(FileMatch): 8584a15e5beSEduardo Habkost """Delete empty preprocessor conditionals""" 8594a15e5beSEduardo Habkost regexp = r'^[ \t]*#(if|ifdef)[ \t].*\n+[ \t]*#endif[ \t]*\n' 8604a15e5beSEduardo Habkost def gen_patches(self) -> Iterable[Patch]: 8614a15e5beSEduardo Habkost yield self.make_removal_patch() 862