mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-10-04 18:20:06 +00:00
scripts/codeconverter: Update to latest version
I'm not documenting every single change in the codeconverter script because most of that code will be deleted once we finish the QOM code conversion. This patch updates the script to the latest version that was used to perform changes in the QOM code. Signed-off-by: Eduardo Habkost <ehabkost@redhat.com> Message-Id: <20200916182519.415636-2-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
This commit is contained in:
parent
f62192a2fd
commit
4a15e5bef8
|
@ -5,7 +5,7 @@
|
||||||
#
|
#
|
||||||
# This work is licensed under the terms of the GNU GPL, version 2. See
|
# This work is licensed under the terms of the GNU GPL, version 2. See
|
||||||
# the COPYING file in the top-level directory.
|
# the COPYING file in the top-level directory.
|
||||||
from typing import IO, Match, NamedTuple, Optional, Literal, Iterable, Type, Dict, List, Any, TypeVar, NewType, Tuple
|
from typing import IO, Match, NamedTuple, Optional, Literal, Iterable, Type, Dict, List, Any, TypeVar, NewType, Tuple, Union
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from itertools import chain
|
from itertools import chain
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
@ -47,7 +47,7 @@ class FileMatch:
|
||||||
|
|
||||||
def __init__(self, f: 'FileInfo', m: Match) -> None:
|
def __init__(self, f: 'FileInfo', m: Match) -> None:
|
||||||
self.file: 'FileInfo' = f
|
self.file: 'FileInfo' = f
|
||||||
self.match: Match = m
|
self.match: Match[str] = m
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -68,8 +68,13 @@ def end(self) -> int:
|
||||||
def line_col(self) -> LineAndColumn:
|
def line_col(self) -> LineAndColumn:
|
||||||
return self.file.line_col(self.start())
|
return self.file.line_col(self.start())
|
||||||
|
|
||||||
def group(self, *args):
|
def group(self, group: Union[int, str]) -> str:
|
||||||
return self.match.group(*args)
|
return self.match.group(group)
|
||||||
|
|
||||||
|
def getgroup(self, group: str) -> Optional[str]:
|
||||||
|
if group not in self.match.groupdict():
|
||||||
|
return None
|
||||||
|
return self.match.group(group)
|
||||||
|
|
||||||
def log(self, level, fmt, *args) -> None:
|
def log(self, level, fmt, *args) -> None:
|
||||||
pos = self.line_col()
|
pos = self.line_col()
|
||||||
|
@ -163,18 +168,51 @@ def provided_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find_matches(klass, content: str) -> Iterable[Match]:
|
def finditer(klass, content: str, pos=0, endpos=-1) -> Iterable[Match]:
|
||||||
"""Generate match objects for class
|
"""Helper for re.finditer()"""
|
||||||
|
if endpos >= 0:
|
||||||
|
content = content[:endpos]
|
||||||
|
return klass.compiled_re().finditer(content, pos)
|
||||||
|
|
||||||
Might be reimplemented by subclasses if they
|
@classmethod
|
||||||
intend to look for matches using a different method.
|
def domatch(klass, content: str, pos=0, endpos=-1) -> Optional[Match]:
|
||||||
"""
|
"""Helper for re.match()"""
|
||||||
return klass.compiled_re().finditer(content)
|
if endpos >= 0:
|
||||||
|
content = content[:endpos]
|
||||||
|
return klass.compiled_re().match(content, pos)
|
||||||
|
|
||||||
|
def group_finditer(self, klass: Type['FileMatch'], group: Union[str, int]) -> Iterable['FileMatch']:
|
||||||
|
assert self.file.original_content
|
||||||
|
return (klass(self.file, m)
|
||||||
|
for m in klass.finditer(self.file.original_content,
|
||||||
|
self.match.start(group),
|
||||||
|
self.match.end(group)))
|
||||||
|
|
||||||
|
def try_group_match(self, klass: Type['FileMatch'], group: Union[str, int]) -> Optional['FileMatch']:
|
||||||
|
assert self.file.original_content
|
||||||
|
m = klass.domatch(self.file.original_content,
|
||||||
|
self.match.start(group),
|
||||||
|
self.match.end(group))
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return klass(self.file, m)
|
||||||
|
|
||||||
|
def group_match(self, group: Union[str, int]) -> 'FileMatch':
|
||||||
|
m = self.try_group_match(FullMatch, group)
|
||||||
|
assert m
|
||||||
|
return m
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def allfiles(self) -> 'FileList':
|
def allfiles(self) -> 'FileList':
|
||||||
return self.file.allfiles
|
return self.file.allfiles
|
||||||
|
|
||||||
|
class FullMatch(FileMatch):
|
||||||
|
"""Regexp that will match all contents of string
|
||||||
|
Useful when used with group_match()
|
||||||
|
"""
|
||||||
|
regexp = r'(?s).*' # (?s) is re.DOTALL
|
||||||
|
|
||||||
def all_subclasses(c: Type[FileMatch]) -> Iterable[Type[FileMatch]]:
|
def all_subclasses(c: Type[FileMatch]) -> Iterable[Type[FileMatch]]:
|
||||||
for sc in c.__subclasses__():
|
for sc in c.__subclasses__():
|
||||||
yield sc
|
yield sc
|
||||||
|
@ -201,7 +239,15 @@ def apply_patches(s: str, patches: Iterable[Patch]) -> str:
|
||||||
"""
|
"""
|
||||||
r = StringIO()
|
r = StringIO()
|
||||||
last = 0
|
last = 0
|
||||||
for p in sorted(patches):
|
def patch_sort_key(item: Tuple[int, Patch]) -> Tuple[int, int, int]:
|
||||||
|
"""Patches are sorted by byte position,
|
||||||
|
patches at the same byte position are applied in the order
|
||||||
|
they were generated.
|
||||||
|
"""
|
||||||
|
i,p = item
|
||||||
|
return (p.start, p.end, i)
|
||||||
|
|
||||||
|
for i,p in sorted(enumerate(patches), key=patch_sort_key):
|
||||||
DBG("Applying patch at position %d (%s) - %d (%s): %r",
|
DBG("Applying patch at position %d (%s) - %d (%s): %r",
|
||||||
p.start, line_col(s, p.start),
|
p.start, line_col(s, p.start),
|
||||||
p.end, line_col(s, p.end),
|
p.end, line_col(s, p.end),
|
||||||
|
@ -220,26 +266,35 @@ def __init__(self) -> None:
|
||||||
self.match_index: Dict[Type[Any], List[FileMatch]] = {}
|
self.match_index: Dict[Type[Any], List[FileMatch]] = {}
|
||||||
self.match_name_index: Dict[Tuple[Type[Any], str, str], Optional[FileMatch]] = {}
|
self.match_name_index: Dict[Tuple[Type[Any], str, str], Optional[FileMatch]] = {}
|
||||||
|
|
||||||
def _find_matches(self, klass: Type[Any]) -> Iterable[FileMatch]:
|
def _matches_of_type(self, klass: Type[Any]) -> Iterable[FileMatch]:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def matches_of_type(self, t: Type[T]) -> List[T]:
|
def matches_of_type(self, t: Type[T]) -> List[T]:
|
||||||
if t not in self.match_index:
|
if t not in self.match_index:
|
||||||
self.match_index[t] = list(self._find_matches(t))
|
self.match_index[t] = list(self._matches_of_type(t))
|
||||||
return self.match_index[t] # type: ignore
|
return self.match_index[t] # type: ignore
|
||||||
|
|
||||||
def find_match(self, t: Type[T], name: str, group: str='name') -> Optional[T]:
|
def find_matches(self, t: Type[T], name: str, group: str='name') -> List[T]:
|
||||||
indexkey = (t, name, group)
|
indexkey = (t, name, group)
|
||||||
if indexkey in self.match_name_index:
|
if indexkey in self.match_name_index:
|
||||||
return self.match_name_index[indexkey] # type: ignore
|
return self.match_name_index[indexkey] # type: ignore
|
||||||
r: Optional[T] = None
|
r: List[T] = []
|
||||||
for m in self.matches_of_type(t):
|
for m in self.matches_of_type(t):
|
||||||
assert isinstance(m, FileMatch)
|
assert isinstance(m, FileMatch)
|
||||||
if m.group(group) == name:
|
if m.getgroup(group) == name:
|
||||||
r = m # type: ignore
|
r.append(m) # type: ignore
|
||||||
self.match_name_index[indexkey] = r # type: ignore
|
self.match_name_index[indexkey] = r # type: ignore
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
def find_match(self, t: Type[T], name: str, group: str='name') -> Optional[T]:
|
||||||
|
l = self.find_matches(t, name, group)
|
||||||
|
if not l:
|
||||||
|
return None
|
||||||
|
if len(l) > 1:
|
||||||
|
logger.warn("multiple matches found for %r (%s=%r)", t, group, name)
|
||||||
|
return None
|
||||||
|
return l[0]
|
||||||
|
|
||||||
def reset_index(self) -> None:
|
def reset_index(self) -> None:
|
||||||
self.match_index.clear()
|
self.match_index.clear()
|
||||||
self.match_name_index.clear()
|
self.match_name_index.clear()
|
||||||
|
@ -258,18 +313,22 @@ def __init__(self, files: 'FileList', filename: os.PathLike, force:bool=False) -
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'<FileInfo {repr(self.filename)}>'
|
return f'<FileInfo {repr(self.filename)}>'
|
||||||
|
|
||||||
|
def filename_matches(self, name: str) -> bool:
|
||||||
|
nameparts = Path(name).parts
|
||||||
|
return self.filename.parts[-len(nameparts):] == nameparts
|
||||||
|
|
||||||
def line_col(self, start: int) -> LineAndColumn:
|
def line_col(self, start: int) -> LineAndColumn:
|
||||||
"""Return line and column for a match object inside original_content"""
|
"""Return line and column for a match object inside original_content"""
|
||||||
return line_col(self.original_content, start)
|
return line_col(self.original_content, start)
|
||||||
|
|
||||||
def _find_matches(self, klass: Type[Any]) -> List[FileMatch]:
|
def _matches_of_type(self, klass: Type[Any]) -> List[FileMatch]:
|
||||||
"""Build FileMatch objects for each match of regexp"""
|
"""Build FileMatch objects for each match of regexp"""
|
||||||
if not hasattr(klass, 'regexp') or klass.regexp is None:
|
if not hasattr(klass, 'regexp') or klass.regexp is None:
|
||||||
return []
|
return []
|
||||||
assert hasattr(klass, 'regexp')
|
assert hasattr(klass, 'regexp')
|
||||||
DBG("%s: scanning for %s", self.filename, klass.__name__)
|
DBG("%s: scanning for %s", self.filename, klass.__name__)
|
||||||
DBG("regexp: %s", klass.regexp)
|
DBG("regexp: %s", klass.regexp)
|
||||||
matches = [klass(self, m) for m in klass.find_matches(self.original_content)]
|
matches = [klass(self, m) for m in klass.finditer(self.original_content)]
|
||||||
DBG('%s: %d matches found for %s: %s', self.filename, len(matches),
|
DBG('%s: %d matches found for %s: %s', self.filename, len(matches),
|
||||||
klass.__name__,' '.join(names(matches)))
|
klass.__name__,' '.join(names(matches)))
|
||||||
return matches
|
return matches
|
||||||
|
@ -277,7 +336,7 @@ def _find_matches(self, klass: Type[Any]) -> List[FileMatch]:
|
||||||
def find_match(self, t: Type[T], name: str, group: str='name') -> Optional[T]:
|
def find_match(self, t: Type[T], name: str, group: str='name') -> Optional[T]:
|
||||||
for m in self.matches_of_type(t):
|
for m in self.matches_of_type(t):
|
||||||
assert isinstance(m, FileMatch)
|
assert isinstance(m, FileMatch)
|
||||||
if m.group(group) == name:
|
if m.getgroup(group) == name:
|
||||||
return m # type: ignore
|
return m # type: ignore
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -299,7 +358,16 @@ def all_matches(self) -> Iterable[FileMatch]:
|
||||||
return (m for l in lists
|
return (m for l in lists
|
||||||
for m in l)
|
for m in l)
|
||||||
|
|
||||||
def scan_for_matches(self, class_names: Optional[List[str]]=None) -> None:
|
def gen_patches(self, matches: List[FileMatch]) -> None:
|
||||||
|
for m in matches:
|
||||||
|
DBG("Generating patches for %r", m)
|
||||||
|
for i,p in enumerate(m.gen_patches()):
|
||||||
|
DBG("patch %d generated by %r:", i, m)
|
||||||
|
DBG("replace contents at %s-%s with %r",
|
||||||
|
self.line_col(p.start), self.line_col(p.end), p.replacement)
|
||||||
|
self.patches.append(p)
|
||||||
|
|
||||||
|
def scan_for_matches(self, class_names: Optional[List[str]]=None) -> Iterable[FileMatch]:
|
||||||
DBG("class names: %r", class_names)
|
DBG("class names: %r", class_names)
|
||||||
class_dict = match_class_dict()
|
class_dict = match_class_dict()
|
||||||
if class_names is None:
|
if class_names is None:
|
||||||
|
@ -309,40 +377,9 @@ def scan_for_matches(self, class_names: Optional[List[str]]=None) -> None:
|
||||||
DBG("class_names: %r", class_names)
|
DBG("class_names: %r", class_names)
|
||||||
for cn in class_names:
|
for cn in class_names:
|
||||||
matches = self.matches_of_type(class_dict[cn])
|
matches = self.matches_of_type(class_dict[cn])
|
||||||
if len(matches) > 0:
|
DBG('%d matches found for %s: %s',
|
||||||
DBG('%s: %d matches found for %s: %s', self.filename,
|
len(matches), cn, ' '.join(names(matches)))
|
||||||
len(matches), cn, ' '.join(names(matches)))
|
yield from matches
|
||||||
|
|
||||||
def gen_patches(self) -> None:
|
|
||||||
for m in self.all_matches:
|
|
||||||
for i,p in enumerate(m.gen_patches()):
|
|
||||||
DBG("patch %d generated by %r:", i, m)
|
|
||||||
DBG("replace contents at %s-%s with %r",
|
|
||||||
self.line_col(p.start), self.line_col(p.end), p.replacement)
|
|
||||||
self.patches.append(p)
|
|
||||||
|
|
||||||
def patch_content(self, max_passes=0, class_names: Optional[List[str]]=None) -> None:
|
|
||||||
"""Multi-pass content patching loop
|
|
||||||
|
|
||||||
We run multiple passes because there are rules that will
|
|
||||||
delete init functions once they become empty.
|
|
||||||
"""
|
|
||||||
passes = 0
|
|
||||||
total_patches = 0
|
|
||||||
DBG("max_passes: %r", max_passes)
|
|
||||||
while not max_passes or max_passes <= 0 or passes < max_passes:
|
|
||||||
passes += 1
|
|
||||||
self.scan_for_matches(class_names)
|
|
||||||
self.gen_patches()
|
|
||||||
DBG("patch content: pass %d: %d patches generated", passes, len(self.patches))
|
|
||||||
total_patches += len(self.patches)
|
|
||||||
if not self.patches:
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
self.apply_patches()
|
|
||||||
except PatchingError:
|
|
||||||
logger.exception("%s: failed to patch file", self.filename)
|
|
||||||
DBG("%s: %d patches applied total in %d passes", self.filename, total_patches, passes)
|
|
||||||
|
|
||||||
def apply_patches(self) -> None:
|
def apply_patches(self) -> None:
|
||||||
"""Replace self.original_content after applying patches from self.patches"""
|
"""Replace self.original_content after applying patches from self.patches"""
|
||||||
|
@ -384,14 +421,46 @@ def extend(self, *args, **kwargs):
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.files)
|
return iter(self.files)
|
||||||
|
|
||||||
def _find_matches(self, klass: Type[Any]) -> Iterable[FileMatch]:
|
def _matches_of_type(self, klass: Type[Any]) -> Iterable[FileMatch]:
|
||||||
return chain(*(f._find_matches(klass) for f in self.files))
|
return chain(*(f._matches_of_type(klass) for f in self.files))
|
||||||
|
|
||||||
def find_file(self, name) -> Optional[FileInfo]:
|
def find_file(self, name: str) -> Optional[FileInfo]:
|
||||||
"""Get file with path ending with @name"""
|
"""Get file with path ending with @name"""
|
||||||
nameparts = Path(name).parts
|
|
||||||
for f in self.files:
|
for f in self.files:
|
||||||
if f.filename.parts[:len(nameparts)] == nameparts:
|
if f.filename_matches(name):
|
||||||
return f
|
return f
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def one_pass(self, class_names: List[str]) -> int:
|
||||||
|
total_patches = 0
|
||||||
|
for f in self.files:
|
||||||
|
INFO("Scanning file %s", f.filename)
|
||||||
|
matches = list(f.scan_for_matches(class_names))
|
||||||
|
INFO("Generating patches for file %s", f.filename)
|
||||||
|
f.gen_patches(matches)
|
||||||
|
total_patches += len(f.patches)
|
||||||
|
if total_patches:
|
||||||
|
for f in self.files:
|
||||||
|
try:
|
||||||
|
f.apply_patches()
|
||||||
|
except PatchingError:
|
||||||
|
logger.exception("%s: failed to patch file", f.filename)
|
||||||
|
return total_patches
|
||||||
|
|
||||||
|
def patch_content(self, max_passes, class_names: List[str]) -> None:
|
||||||
|
"""Multi-pass content patching loop
|
||||||
|
|
||||||
|
We run multiple passes because there are rules that will
|
||||||
|
delete init functions once they become empty.
|
||||||
|
"""
|
||||||
|
passes = 0
|
||||||
|
total_patches = 0
|
||||||
|
DBG("max_passes: %r", max_passes)
|
||||||
|
while not max_passes or max_passes <= 0 or passes < max_passes:
|
||||||
|
passes += 1
|
||||||
|
INFO("Running pass: %d", passes)
|
||||||
|
count = self.one_pass(class_names)
|
||||||
|
DBG("patch content: pass %d: %d patches generated", passes, count)
|
||||||
|
total_patches += count
|
||||||
|
DBG("%d patches applied total in %d passes", total_patches, passes)
|
||||||
|
|
|
@ -23,16 +23,24 @@
|
||||||
|
|
||||||
RE_CONSTANT = OR(RE_STRING, RE_NUMBER)
|
RE_CONSTANT = OR(RE_STRING, RE_NUMBER)
|
||||||
|
|
||||||
class ConstantDefine(FileMatch):
|
class DefineDirective(FileMatch):
|
||||||
"""Simple #define preprocessor directive for a constant"""
|
"""Match any #define directive"""
|
||||||
# if the macro contents are very simple, it might be included
|
regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER), r'\b')
|
||||||
# in the match group 'value'
|
|
||||||
|
class ExpressionDefine(FileMatch):
|
||||||
|
"""Simple #define preprocessor directive for an expression"""
|
||||||
regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER),
|
regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER),
|
||||||
CPP_SPACE, NAMED('value', RE_CONSTANT), r'[ \t]*\n')
|
CPP_SPACE, NAMED('value', RE_EXPRESSION), r'[ \t]*\n')
|
||||||
|
|
||||||
def provided_identifiers(self) -> Iterable[RequiredIdentifier]:
|
def provided_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
yield RequiredIdentifier('constant', self.group('name'))
|
yield RequiredIdentifier('constant', self.group('name'))
|
||||||
|
|
||||||
|
class ConstantDefine(ExpressionDefine):
|
||||||
|
"""Simple #define preprocessor directive for a number or string constant"""
|
||||||
|
regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE, NAMED('name', RE_IDENTIFIER),
|
||||||
|
CPP_SPACE, NAMED('value', RE_CONSTANT), r'[ \t]*\n')
|
||||||
|
|
||||||
|
|
||||||
class TypeIdentifiers(NamedTuple):
|
class TypeIdentifiers(NamedTuple):
|
||||||
"""Type names found in type declarations"""
|
"""Type names found in type declarations"""
|
||||||
# TYPE_MYDEVICE
|
# TYPE_MYDEVICE
|
||||||
|
@ -236,13 +244,12 @@ class TypeCheckMacro(FileMatch):
|
||||||
"""OBJECT_CHECK/OBJECT_CLASS_CHECK/OBJECT_GET_CLASS macro definitions
|
"""OBJECT_CHECK/OBJECT_CLASS_CHECK/OBJECT_GET_CLASS macro definitions
|
||||||
Will be replaced by DECLARE_*_CHECKERS macro
|
Will be replaced by DECLARE_*_CHECKERS macro
|
||||||
"""
|
"""
|
||||||
#TODO: handle and convert INTERFACE_CHECK macros
|
|
||||||
regexp = RE_CHECK_MACRO
|
regexp = RE_CHECK_MACRO
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def checker(self) -> CheckerMacroName:
|
def checker(self) -> CheckerMacroName:
|
||||||
"""Name of checker macro being used"""
|
"""Name of checker macro being used"""
|
||||||
return self.group('checker')
|
return self.group('checker') # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def typedefname(self) -> Optional[str]:
|
def typedefname(self) -> Optional[str]:
|
||||||
|
@ -330,6 +337,8 @@ def type_identifiers(self) -> Optional[TypeIdentifiers]:
|
||||||
instancetype=instancetype, uppercase=uppercase)
|
instancetype=instancetype, uppercase=uppercase)
|
||||||
|
|
||||||
def gen_patches(self) -> Iterable[Patch]:
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
# the implementation is a bit tricky because we need to group
|
||||||
|
# macros dealing with the same type into a single declaration
|
||||||
if self.type_identifiers is None:
|
if self.type_identifiers is None:
|
||||||
self.warn("couldn't extract type information from macro %s", self.name)
|
self.warn("couldn't extract type information from macro %s", self.name)
|
||||||
return
|
return
|
||||||
|
@ -426,10 +435,61 @@ def gen_patches(self) -> Iterable[Patch]:
|
||||||
yield self.prepend("/* FIXME: %s */\n" % (issue))
|
yield self.prepend("/* FIXME: %s */\n" % (issue))
|
||||||
yield self.append(new_decl)
|
yield self.append(new_decl)
|
||||||
|
|
||||||
class DeclareInstanceChecker(FileMatch):
|
class InterfaceCheckMacro(FileMatch):
|
||||||
"""DECLARE_INSTANCE_CHECKER use
|
"""Type checking macro using INTERFACE_CHECK
|
||||||
Will be replaced with DECLARE_OBJ_CHECKERS if possible
|
Will be replaced by DECLARE_INTERFACE_CHECKER
|
||||||
"""
|
"""
|
||||||
|
regexp = S(RE_MACRO_DEFINE,
|
||||||
|
'INTERFACE_CHECK',
|
||||||
|
r'\s*\(\s*', OR(NAMED('instancetype', RE_IDENTIFIER), RE_TYPE, name='c_type'),
|
||||||
|
r'\s*,', CPP_SPACE,
|
||||||
|
OPTIONAL_PARS(RE_IDENTIFIER), r',', CPP_SPACE,
|
||||||
|
NAMED('qom_typename', RE_IDENTIFIER), r'\s*\)\n')
|
||||||
|
|
||||||
|
def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
|
yield RequiredIdentifier('include', '"qom/object.h"')
|
||||||
|
yield RequiredIdentifier('type', self.group('instancetype'))
|
||||||
|
yield RequiredIdentifier('constant', self.group('qom_typename'))
|
||||||
|
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
if self.file.filename_matches('qom/object.h'):
|
||||||
|
self.debug("skipping object.h")
|
||||||
|
return
|
||||||
|
|
||||||
|
typename = self.group('qom_typename')
|
||||||
|
uppercase = self.name
|
||||||
|
instancetype = self.group('instancetype')
|
||||||
|
c = f"DECLARE_INTERFACE_CHECKER({instancetype}, {uppercase},\n"+\
|
||||||
|
f" {typename})\n"
|
||||||
|
yield self.make_patch(c)
|
||||||
|
|
||||||
|
|
||||||
|
class TypeDeclaration(FileMatch):
|
||||||
|
"""Parent class to all type declarations"""
|
||||||
|
@property
|
||||||
|
def instancetype(self) -> Optional[str]:
|
||||||
|
return self.getgroup('instancetype')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classtype(self) -> Optional[str]:
|
||||||
|
return self.getgroup('classtype')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def typename(self) -> Optional[str]:
|
||||||
|
return self.getgroup('typename')
|
||||||
|
|
||||||
|
class TypeCheckerDeclaration(TypeDeclaration):
|
||||||
|
"""Parent class to all type checker declarations"""
|
||||||
|
@property
|
||||||
|
def typename(self) -> str:
|
||||||
|
return self.group('typename')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uppercase(self) -> str:
|
||||||
|
return self.group('uppercase')
|
||||||
|
|
||||||
|
class DeclareInstanceChecker(TypeCheckerDeclaration):
|
||||||
|
"""DECLARE_INSTANCE_CHECKER use"""
|
||||||
#TODO: replace lonely DECLARE_INSTANCE_CHECKER with DECLARE_OBJ_CHECKERS
|
#TODO: replace lonely DECLARE_INSTANCE_CHECKER with DECLARE_OBJ_CHECKERS
|
||||||
# if all types are found.
|
# if all types are found.
|
||||||
# This will require looking up the correct class type in the TypeInfo
|
# This will require looking up the correct class type in the TypeInfo
|
||||||
|
@ -445,8 +505,45 @@ def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
yield RequiredIdentifier('constant', self.group('typename'))
|
yield RequiredIdentifier('constant', self.group('typename'))
|
||||||
yield RequiredIdentifier('type', self.group('instancetype'))
|
yield RequiredIdentifier('type', self.group('instancetype'))
|
||||||
|
|
||||||
class DeclareClassCheckers(FileMatch):
|
class DeclareInterfaceChecker(TypeCheckerDeclaration):
|
||||||
"""DECLARE_INSTANCE_CHECKER use"""
|
"""DECLARE_INTERFACE_CHECKER use"""
|
||||||
|
regexp = S(r'^[ \t]*DECLARE_INTERFACE_CHECKER\s*\(\s*',
|
||||||
|
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
||||||
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'), SP,
|
||||||
|
r'\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
|
def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
|
yield RequiredIdentifier('include', '"qom/object.h"')
|
||||||
|
yield RequiredIdentifier('constant', self.group('typename'))
|
||||||
|
yield RequiredIdentifier('type', self.group('instancetype'))
|
||||||
|
|
||||||
|
class DeclareInstanceType(TypeDeclaration):
|
||||||
|
"""DECLARE_INSTANCE_TYPE use"""
|
||||||
|
regexp = S(r'^[ \t]*DECLARE_INSTANCE_TYPE\s*\(\s*',
|
||||||
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('instancetype', RE_TYPE), SP,
|
||||||
|
r'\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
|
def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
|
yield RequiredIdentifier('include', '"qom/object.h"')
|
||||||
|
yield RequiredIdentifier('type', self.group('instancetype'))
|
||||||
|
|
||||||
|
class DeclareClassType(TypeDeclaration):
|
||||||
|
"""DECLARE_CLASS_TYPE use"""
|
||||||
|
regexp = S(r'^[ \t]*DECLARE_CLASS_TYPE\s*\(\s*',
|
||||||
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('classtype', RE_TYPE), SP,
|
||||||
|
r'\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
|
def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
|
yield RequiredIdentifier('include', '"qom/object.h"')
|
||||||
|
yield RequiredIdentifier('type', self.group('classtype'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DeclareClassCheckers(TypeCheckerDeclaration):
|
||||||
|
"""DECLARE_CLASS_CHECKER use"""
|
||||||
regexp = S(r'^[ \t]*DECLARE_CLASS_CHECKERS\s*\(\s*',
|
regexp = S(r'^[ \t]*DECLARE_CLASS_CHECKERS\s*\(\s*',
|
||||||
NAMED('classtype', RE_TYPE), r'\s*,\s*',
|
NAMED('classtype', RE_TYPE), r'\s*,\s*',
|
||||||
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
@ -458,10 +555,8 @@ def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
yield RequiredIdentifier('constant', self.group('typename'))
|
yield RequiredIdentifier('constant', self.group('typename'))
|
||||||
yield RequiredIdentifier('type', self.group('classtype'))
|
yield RequiredIdentifier('type', self.group('classtype'))
|
||||||
|
|
||||||
class DeclareObjCheckers(FileMatch):
|
class DeclareObjCheckers(TypeCheckerDeclaration):
|
||||||
"""DECLARE_OBJ_CHECKERS use
|
"""DECLARE_OBJ_CHECKERS use"""
|
||||||
Will be replaced with OBJECT_DECLARE_TYPE if possible
|
|
||||||
"""
|
|
||||||
#TODO: detect when OBJECT_DECLARE_SIMPLE_TYPE can be used
|
#TODO: detect when OBJECT_DECLARE_SIMPLE_TYPE can be used
|
||||||
regexp = S(r'^[ \t]*DECLARE_OBJ_CHECKERS\s*\(\s*',
|
regexp = S(r'^[ \t]*DECLARE_OBJ_CHECKERS\s*\(\s*',
|
||||||
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
||||||
|
@ -476,44 +571,121 @@ def required_identifiers(self) -> Iterable[RequiredIdentifier]:
|
||||||
yield RequiredIdentifier('type', self.group('classtype'))
|
yield RequiredIdentifier('type', self.group('classtype'))
|
||||||
yield RequiredIdentifier('type', self.group('instancetype'))
|
yield RequiredIdentifier('type', self.group('instancetype'))
|
||||||
|
|
||||||
def gen_patches(self):
|
class TypeDeclarationFixup(FileMatch):
|
||||||
ids = TypeIdentifiers(uppercase=self.group('uppercase'),
|
"""Common base class for code that will look at a set of type declarations"""
|
||||||
typename=self.group('typename'),
|
regexp = RE_FILE_BEGIN
|
||||||
classtype=self.group('classtype'),
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
instancetype=self.group('instancetype'))
|
if self.file.filename_matches('qom/object.h'):
|
||||||
issues = ids.check_consistency()
|
self.debug("skipping object.h")
|
||||||
if issues:
|
|
||||||
for i in issues:
|
|
||||||
self.warn("inconsistent identifiers: %s", i)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.group('typename') != 'TYPE_'+self.group('uppercase'):
|
# group checkers by uppercase name:
|
||||||
self.warn("type %s mismatch with uppercase name %s", ids.typename, ids.uppercase)
|
decl_types: List[Type[TypeDeclaration]] = [DeclareInstanceChecker, DeclareInstanceType,
|
||||||
|
DeclareClassCheckers, DeclareClassType,
|
||||||
|
DeclareObjCheckers]
|
||||||
|
checker_dict: Dict[str, List[TypeDeclaration]] = {}
|
||||||
|
for t in decl_types:
|
||||||
|
for m in self.file.matches_of_type(t):
|
||||||
|
checker_dict.setdefault(m.group('uppercase'), []).append(m)
|
||||||
|
self.debug("checker_dict: %r", checker_dict)
|
||||||
|
for uppercase,checkers in checker_dict.items():
|
||||||
|
fields = ('instancetype', 'classtype', 'uppercase', 'typename')
|
||||||
|
fvalues = dict((field, set(getattr(m, field) for m in checkers
|
||||||
|
if getattr(m, field, None) is not None))
|
||||||
|
for field in fields)
|
||||||
|
for field,values in fvalues.items():
|
||||||
|
if len(values) > 1:
|
||||||
|
for c in checkers:
|
||||||
|
c.warn("%s mismatch (%s)", field, ' '.join(values))
|
||||||
|
return
|
||||||
|
|
||||||
|
field_dict = dict((f, v.pop() if v else None) for f,v in fvalues.items())
|
||||||
|
yield from self.gen_patches_for_type(uppercase, checkers, field_dict)
|
||||||
|
|
||||||
|
def find_conflicts(self, uppercase: str, checkers: List[TypeDeclaration]) -> bool:
|
||||||
|
"""Look for conflicting declarations that would make it unsafe to add new ones"""
|
||||||
|
conflicting: List[FileMatch] = []
|
||||||
|
# conflicts in the same file:
|
||||||
|
conflicting.extend(chain(self.file.find_matches(DefineDirective, uppercase),
|
||||||
|
self.file.find_matches(DeclareInterfaceChecker, uppercase, 'uppercase'),
|
||||||
|
self.file.find_matches(DeclareClassType, uppercase, 'uppercase'),
|
||||||
|
self.file.find_matches(DeclareInstanceType, uppercase, 'uppercase')))
|
||||||
|
|
||||||
|
# conflicts in another file:
|
||||||
|
conflicting.extend(o for o in chain(self.allfiles.find_matches(DeclareInstanceChecker, uppercase, 'uppercase'),
|
||||||
|
self.allfiles.find_matches(DeclareClassCheckers, uppercase, 'uppercase'),
|
||||||
|
self.allfiles.find_matches(DeclareInterfaceChecker, uppercase, 'uppercase'),
|
||||||
|
self.allfiles.find_matches(DefineDirective, uppercase))
|
||||||
|
if o is not None and o.file != self.file
|
||||||
|
# if both are .c files, there's no conflict at all:
|
||||||
|
and not (o.file.filename.suffix == '.c' and
|
||||||
|
self.file.filename.suffix == '.c'))
|
||||||
|
|
||||||
|
if conflicting:
|
||||||
|
for c in checkers:
|
||||||
|
c.warn("skipping due to conflicting %s macro", uppercase)
|
||||||
|
for o in conflicting:
|
||||||
|
if o is None:
|
||||||
|
continue
|
||||||
|
o.warn("conflicting %s macro is here", uppercase)
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def gen_patches_for_type(self, uppercase: str,
|
||||||
|
checkers: List[TypeDeclaration],
|
||||||
|
fields: Dict[str, Optional[str]]) -> Iterable[Patch]:
|
||||||
|
"""Should be reimplemented by subclasses"""
|
||||||
|
return
|
||||||
|
yield
|
||||||
|
|
||||||
|
class DeclareVoidTypes(TypeDeclarationFixup):
|
||||||
|
"""Add DECLARE_*_TYPE(..., void) when there's no declared type"""
|
||||||
|
regexp = RE_FILE_BEGIN
|
||||||
|
def gen_patches_for_type(self, uppercase: str,
|
||||||
|
checkers: List[TypeDeclaration],
|
||||||
|
fields: Dict[str, Optional[str]]) -> Iterable[Patch]:
|
||||||
|
if self.find_conflicts(uppercase, checkers):
|
||||||
return
|
return
|
||||||
|
|
||||||
typedefs = [(t,self.file.find_match(SimpleTypedefMatch, t))
|
#_,last_checker = max((m.start(), m) for m in checkers)
|
||||||
for t in (ids.instancetype, ids.classtype)]
|
_,first_checker = min((m.start(), m) for m in checkers)
|
||||||
for t,td in typedefs:
|
|
||||||
if td is None:
|
|
||||||
self.warn("typedef %s not found", t)
|
|
||||||
break
|
|
||||||
if td.start() > self.start():
|
|
||||||
self.warn("typedef %s needs to be move earlier in the file", t)
|
|
||||||
break
|
|
||||||
#HACK: check if typedef is used between its definition and the macro
|
|
||||||
#TODO: check if the only match is inside the "struct { ... }" declaration
|
|
||||||
if re.search(r'\b'+t+r'\b', self.file.original_content[td.end():self.start()]):
|
|
||||||
self.warn("typedef %s can't be moved, it is used before the macro", t)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
for t,td in typedefs:
|
|
||||||
yield td.make_removal_patch()
|
|
||||||
|
|
||||||
lowercase = ids.uppercase.lower()
|
if not any(m.instancetype for m in checkers):
|
||||||
# all is OK, we can replace the macro!
|
yield first_checker.prepend(f'DECLARE_INSTANCE_TYPE({uppercase}, void)\n')
|
||||||
c = (f'OBJECT_DECLARE_TYPE({ids.instancetype}, {ids.classtype},\n'
|
if not any(m.classtype for m in checkers):
|
||||||
f' {lowercase}, {ids.uppercase})\n')
|
yield first_checker.prepend(f'DECLARE_CLASS_TYPE({uppercase}, void)\n')
|
||||||
yield self.make_patch(c)
|
|
||||||
|
#if not all(len(v) == 1 for v in fvalues.values()):
|
||||||
|
# return
|
||||||
|
#
|
||||||
|
#final_values = dict((field, values.pop())
|
||||||
|
# for field,values in fvalues.items())
|
||||||
|
#s = (f"DECLARE_OBJ_CHECKERS({final_values['instancetype']}, {final_values['classtype']},\n"+
|
||||||
|
# f" {final_values['uppercase']}, {final_values['typename']})\n")
|
||||||
|
#for c in checkers:
|
||||||
|
# yield c.make_removal_patch()
|
||||||
|
#yield last_checker.append(s)
|
||||||
|
|
||||||
|
|
||||||
|
class AddDeclareTypeName(TypeDeclarationFixup):
|
||||||
|
"""Add DECLARE_TYPE_NAME declarations if necessary"""
|
||||||
|
def gen_patches_for_type(self, uppercase: str,
|
||||||
|
checkers: List[TypeDeclaration],
|
||||||
|
fields: Dict[str, Optional[str]]) -> Iterable[Patch]:
|
||||||
|
typename = fields.get('typename')
|
||||||
|
if typename is None:
|
||||||
|
self.warn("typename unavailable")
|
||||||
|
return
|
||||||
|
if typename == f'TYPE_{uppercase}':
|
||||||
|
self.info("already using TYPE_%s as type name", uppercase)
|
||||||
|
return
|
||||||
|
if self.file.find_match(DeclareTypeName, uppercase, 'uppercase'):
|
||||||
|
self.info("type name for %s already declared", uppercase)
|
||||||
|
return
|
||||||
|
_,first_checker = min((m.start(), m) for m in checkers)
|
||||||
|
s = f'DECLARE_TYPE_NAME({uppercase}, {typename})\n'
|
||||||
|
yield first_checker.prepend(s)
|
||||||
|
|
||||||
class TrivialClassStruct(FileMatch):
|
class TrivialClassStruct(FileMatch):
|
||||||
"""Trivial class struct"""
|
"""Trivial class struct"""
|
||||||
|
@ -527,14 +699,13 @@ class DeclareTypeName(FileMatch):
|
||||||
OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'),
|
OR(RE_IDENTIFIER, RE_STRING, RE_MACRO_CONCAT, RE_FUN_CALL, name='typename'),
|
||||||
r'\s*\);?[ \t]*\n')
|
r'\s*\);?[ \t]*\n')
|
||||||
|
|
||||||
class ObjectDeclareType(FileMatch):
|
class ObjectDeclareType(TypeCheckerDeclaration):
|
||||||
"""OBJECT_DECLARE_TYPE usage
|
"""OBJECT_DECLARE_TYPE usage
|
||||||
Will be replaced with OBJECT_DECLARE_SIMPLE_TYPE if possible
|
Will be replaced with OBJECT_DECLARE_SIMPLE_TYPE if possible
|
||||||
"""
|
"""
|
||||||
regexp = S(r'^[ \t]*OBJECT_DECLARE_TYPE\s*\(',
|
regexp = S(r'^[ \t]*OBJECT_DECLARE_TYPE\s*\(',
|
||||||
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
||||||
NAMED('classtype', RE_TYPE), r'\s*,\s*',
|
NAMED('classtype', RE_TYPE), r'\s*,\s*',
|
||||||
NAMED('lowercase', RE_IDENTIFIER), r'\s*,\s*',
|
|
||||||
NAMED('uppercase', RE_IDENTIFIER), SP,
|
NAMED('uppercase', RE_IDENTIFIER), SP,
|
||||||
r'\)[ \t]*;?[ \t]*\n')
|
r'\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
|
@ -549,14 +720,42 @@ def gen_patches(self):
|
||||||
" %(uppercase)s, %(parent_struct)s)\n" % d)
|
" %(uppercase)s, %(parent_struct)s)\n" % d)
|
||||||
yield self.make_patch(c)
|
yield self.make_patch(c)
|
||||||
|
|
||||||
def find_type_declaration(files: FileList, typename: str) -> Optional[FileMatch]:
|
class ObjectDeclareSimpleType(TypeCheckerDeclaration):
|
||||||
"""Find usage of DECLARE*CHECKER macro"""
|
"""OBJECT_DECLARE_SIMPLE_TYPE usage"""
|
||||||
for c in (DeclareInstanceChecker, DeclareClassCheckers, DeclareObjCheckers, DeclareTypeName):
|
regexp = S(r'^[ \t]*OBJECT_DECLARE_SIMPLE_TYPE\s*\(',
|
||||||
d = files.find_match(c, name=typename, group='typename')
|
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
||||||
if d:
|
NAMED('uppercase', RE_IDENTIFIER), SP,
|
||||||
return d
|
r'\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
|
class OldStyleObjectDeclareSimpleType(TypeCheckerDeclaration):
|
||||||
|
"""OBJECT_DECLARE_SIMPLE_TYPE usage (old API)"""
|
||||||
|
regexp = S(r'^[ \t]*OBJECT_DECLARE_SIMPLE_TYPE\s*\(',
|
||||||
|
NAMED('instancetype', RE_TYPE), r'\s*,\s*',
|
||||||
|
NAMED('lowercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('parent_classtype', RE_TYPE), SP,
|
||||||
|
r'\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classtype(self) -> Optional[str]:
|
||||||
|
instancetype = self.instancetype
|
||||||
|
assert instancetype
|
||||||
|
return f"{instancetype}Class"
|
||||||
|
|
||||||
|
def find_typename_uppercase(files: FileList, typename: str) -> Optional[str]:
|
||||||
|
"""Try to find what's the right MODULE_OBJ_NAME for a given type name"""
|
||||||
|
decl = files.find_match(DeclareTypeName, name=typename, group='typename')
|
||||||
|
if decl:
|
||||||
|
return decl.group('uppercase')
|
||||||
|
if typename.startswith('TYPE_'):
|
||||||
|
return typename[len('TYPE_'):]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def find_type_checkers(files:FileList, name:str, group:str='uppercase') -> Iterable[TypeCheckerDeclaration]:
|
||||||
|
"""Find usage of DECLARE*CHECKER macro"""
|
||||||
|
c: Type[TypeCheckerDeclaration]
|
||||||
|
for c in (DeclareInstanceChecker, DeclareClassCheckers, DeclareObjCheckers, ObjectDeclareType, ObjectDeclareSimpleType):
|
||||||
|
yield from files.find_matches(c, name=name, group=group)
|
||||||
|
|
||||||
class Include(FileMatch):
|
class Include(FileMatch):
|
||||||
"""#include directive"""
|
"""#include directive"""
|
||||||
|
@ -586,9 +785,13 @@ class MoveSymbols(FileMatch):
|
||||||
regexp = RE_FILE_BEGIN
|
regexp = RE_FILE_BEGIN
|
||||||
|
|
||||||
def gen_patches(self) -> Iterator[Patch]:
|
def gen_patches(self) -> Iterator[Patch]:
|
||||||
|
if self.file.filename_matches('qom/object.h'):
|
||||||
|
self.debug("skipping object.h")
|
||||||
|
return
|
||||||
|
|
||||||
index: Dict[RequiredIdentifier, SymbolUserList] = {}
|
index: Dict[RequiredIdentifier, SymbolUserList] = {}
|
||||||
definition_classes = [SimpleTypedefMatch, FullStructTypedefMatch, ConstantDefine, Include]
|
definition_classes = [SimpleTypedefMatch, FullStructTypedefMatch, ConstantDefine, Include]
|
||||||
user_classes = [TypeCheckMacro, DeclareObjCheckers, DeclareInstanceChecker, DeclareClassCheckers]
|
user_classes = [TypeCheckMacro, DeclareObjCheckers, DeclareInstanceChecker, DeclareClassCheckers, InterfaceCheckMacro]
|
||||||
|
|
||||||
# first we scan for all symbol definitions and usage:
|
# first we scan for all symbol definitions and usage:
|
||||||
for dc in definition_classes:
|
for dc in definition_classes:
|
||||||
|
@ -650,3 +853,9 @@ def gen_patches(self) -> Iterator[Patch]:
|
||||||
definition.warn("definition of %s %s needs to be moved earlier in the file", i.type, i.name)
|
definition.warn("definition of %s %s needs to be moved earlier in the file", i.type, i.name)
|
||||||
earliest.warn("definition of %s %s is used here", i.type, i.name)
|
earliest.warn("definition of %s %s is used here", i.type, i.name)
|
||||||
|
|
||||||
|
|
||||||
|
class EmptyPreprocessorConditional(FileMatch):
|
||||||
|
"""Delete empty preprocessor conditionals"""
|
||||||
|
regexp = r'^[ \t]*#(if|ifdef)[ \t].*\n+[ \t]*#endif[ \t]*\n'
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
yield self.make_removal_patch()
|
||||||
|
|
|
@ -24,11 +24,6 @@
|
||||||
|
|
||||||
RE_TYPEINFO_START = S(r'^[ \t]*', M(r'(static|const)\s+', name='modifiers'), r'TypeInfo\s+',
|
RE_TYPEINFO_START = S(r'^[ \t]*', M(r'(static|const)\s+', name='modifiers'), r'TypeInfo\s+',
|
||||||
NAMED('name', RE_IDENTIFIER), r'\s*=\s*{[ \t]*\n')
|
NAMED('name', RE_IDENTIFIER), r'\s*=\s*{[ \t]*\n')
|
||||||
RE_TYPEINFO_DEF = S(RE_TYPEINFO_START,
|
|
||||||
M(NAMED('fields', RE_TI_FIELDS),
|
|
||||||
SP, NAMED('endcomments', RE_COMMENTS),
|
|
||||||
r'};?\n',
|
|
||||||
n='?', name='fullspec'))
|
|
||||||
|
|
||||||
ParsedArray = List[str]
|
ParsedArray = List[str]
|
||||||
ParsedInitializerValue = Union[str, ParsedArray]
|
ParsedInitializerValue = Union[str, ParsedArray]
|
||||||
|
@ -36,26 +31,55 @@ class InitializerValue(NamedTuple):
|
||||||
raw: str
|
raw: str
|
||||||
parsed: Optional[ParsedInitializerValue]
|
parsed: Optional[ParsedInitializerValue]
|
||||||
match: Optional[Match]
|
match: Optional[Match]
|
||||||
TypeInfoInitializers = Dict[str, InitializerValue]
|
|
||||||
|
|
||||||
def parse_array(m: Match) -> ParsedArray:
|
class ArrayItem(FileMatch):
|
||||||
#DBG('parse_array: %r', m.group(0))
|
regexp = RE_ARRAY_ITEM
|
||||||
return [m.group('arrayitem') for m in re.finditer(RE_ARRAY_ITEM, m.group('arrayitems'))]
|
|
||||||
|
|
||||||
def parse_initializer_value(m: Match, s: str) -> InitializerValue:
|
class ArrayInitializer(FileMatch):
|
||||||
parsed: Optional[ParsedInitializerValue] = None
|
regexp = RE_ARRAY
|
||||||
#DBG("parse_initializer_value: %r", s)
|
|
||||||
array = re.match(RE_ARRAY, s)
|
|
||||||
if array:
|
|
||||||
parsed = parse_array(array)
|
|
||||||
return InitializerValue(s, parsed, m)
|
|
||||||
|
|
||||||
class TypeInfoVar(FileMatch):
|
def parsed(self) -> ParsedArray:
|
||||||
"""TypeInfo variable declaration with initializer
|
#DBG('parse_array: %r', m.group(0))
|
||||||
Will be replaced by OBJECT_DEFINE_TYPE_EXTENDED macro
|
return [m.group('arrayitem') for m in self.group_finditer(ArrayItem, 'arrayitems')]
|
||||||
(not implemented yet)
|
|
||||||
|
class FieldInitializer(FileMatch):
|
||||||
|
regexp = RE_TI_FIELD_INIT
|
||||||
|
|
||||||
|
@property
|
||||||
|
def raw(self) -> str:
|
||||||
|
return self.group('value')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parsed(self) -> ParsedInitializerValue:
|
||||||
|
parsed: ParsedInitializerValue = self.raw
|
||||||
|
#DBG("parse_initializer_value: %r", s)
|
||||||
|
array = self.try_group_match(ArrayInitializer, 'value')
|
||||||
|
if array:
|
||||||
|
assert isinstance(array, ArrayInitializer)
|
||||||
|
return array.parsed()
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
TypeInfoInitializers = Dict[str, FieldInitializer]
|
||||||
|
|
||||||
|
class TypeDefinition(FileMatch):
|
||||||
"""
|
"""
|
||||||
regexp = RE_TYPEINFO_DEF
|
Common base class for type definitions (TypeInfo variables or OBJECT_DEFINE* macros)
|
||||||
|
"""
|
||||||
|
@property
|
||||||
|
def instancetype(self) -> Optional[str]:
|
||||||
|
return self.group('instancetype')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classtype(self) -> Optional[str]:
|
||||||
|
return self.group('classtype')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uppercase(self) -> Optional[str]:
|
||||||
|
return self.group('uppercase')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def parent_uppercase(self) -> str:
|
||||||
|
return self.group('parent_uppercase')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def initializers(self) -> Optional[TypeInfoInitializers]:
|
def initializers(self) -> Optional[TypeInfoInitializers]:
|
||||||
|
@ -65,14 +89,26 @@ def initializers(self) -> Optional[TypeInfoInitializers]:
|
||||||
fields = self.group('fields')
|
fields = self.group('fields')
|
||||||
if fields is None:
|
if fields is None:
|
||||||
return None
|
return None
|
||||||
d = dict((fm.group('field'), parse_initializer_value(fm, fm.group('value')))
|
d = dict((fm.group('field'), fm)
|
||||||
for fm in re.finditer(RE_TI_FIELD_INIT, fields))
|
for fm in self.group_finditer(FieldInitializer, 'fields'))
|
||||||
self._initializers = d
|
self._initializers = d # type: ignore
|
||||||
return d
|
return self._initializers
|
||||||
|
|
||||||
|
|
||||||
|
class TypeInfoVar(TypeDefinition):
|
||||||
|
"""TypeInfo variable declaration with initializer"""
|
||||||
|
regexp = S(NAMED('begin', RE_TYPEINFO_START),
|
||||||
|
M(NAMED('fields', RE_TI_FIELDS),
|
||||||
|
NAMED('endcomments', SP, RE_COMMENTS),
|
||||||
|
NAMED('end', r'};?\n'),
|
||||||
|
n='?', name='fullspec'))
|
||||||
|
|
||||||
def is_static(self) -> bool:
|
def is_static(self) -> bool:
|
||||||
return 'static' in self.group('modifiers')
|
return 'static' in self.group('modifiers')
|
||||||
|
|
||||||
|
def is_const(self) -> bool:
|
||||||
|
return 'const' in self.group('modifiers')
|
||||||
|
|
||||||
def is_full(self) -> bool:
|
def is_full(self) -> bool:
|
||||||
return bool(self.group('fullspec'))
|
return bool(self.group('fullspec'))
|
||||||
|
|
||||||
|
@ -82,8 +118,46 @@ def get_initializers(self) -> TypeInfoInitializers:
|
||||||
return {}
|
return {}
|
||||||
return self.initializers
|
return self.initializers
|
||||||
|
|
||||||
def get_initializer_value(self, field: str) -> InitializerValue:
|
def get_raw_initializer_value(self, field: str, default: str = '') -> str:
|
||||||
return self.get_initializers().get(field, InitializerValue('', '', None))
|
initializers = self.get_initializers()
|
||||||
|
if field in initializers:
|
||||||
|
return initializers[field].raw
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
|
@property
|
||||||
|
def typename(self) -> Optional[str]:
|
||||||
|
return self.get_raw_initializer_value('name')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def uppercase(self) -> Optional[str]:
|
||||||
|
typename = self.typename
|
||||||
|
if not typename:
|
||||||
|
return None
|
||||||
|
if not typename.startswith('TYPE_'):
|
||||||
|
return None
|
||||||
|
return typename[len('TYPE_'):]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classtype(self) -> Optional[str]:
|
||||||
|
class_size = self.get_raw_initializer_value('class_size')
|
||||||
|
if not class_size:
|
||||||
|
return None
|
||||||
|
m = re.fullmatch(RE_SIZEOF, class_size)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
return m.group('sizeoftype')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def instancetype(self) -> Optional[str]:
|
||||||
|
instance_size = self.get_raw_initializer_value('instance_size')
|
||||||
|
if not instance_size:
|
||||||
|
return None
|
||||||
|
m = re.fullmatch(RE_SIZEOF, instance_size)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
return m.group('sizeoftype')
|
||||||
|
|
||||||
|
|
||||||
#def extract_identifiers(self) -> Optional[TypeIdentifiers]:
|
#def extract_identifiers(self) -> Optional[TypeIdentifiers]:
|
||||||
# """Try to extract identifiers from names being used"""
|
# """Try to extract identifiers from names being used"""
|
||||||
|
@ -116,32 +190,105 @@ def get_initializer_value(self, field: str) -> InitializerValue:
|
||||||
# uppercase=uppercase, lowercase=lowercase,
|
# uppercase=uppercase, lowercase=lowercase,
|
||||||
# instancetype=instancetype, classtype=classtype)
|
# instancetype=instancetype, classtype=classtype)
|
||||||
|
|
||||||
def append_field(self, field, value) -> Patch:
|
def append_field(self, field: str, value: str) -> Patch:
|
||||||
"""Generate patch appending a field initializer"""
|
"""Generate patch appending a field initializer"""
|
||||||
content = f' .{field} = {value},\n'
|
content = f' .{field} = {value},\n'
|
||||||
return Patch(self.match.end('fields'), self.match.end('fields'),
|
fm = self.group_match('fields')
|
||||||
content)
|
assert fm
|
||||||
|
return fm.append(content)
|
||||||
|
|
||||||
def patch_field(self, field: str, replacement: str) -> Patch:
|
def patch_field(self, field: str, replacement: str) -> Patch:
|
||||||
"""Generate patch replacing a field initializer"""
|
"""Generate patch replacing a field initializer"""
|
||||||
values = self.initializers
|
initializers = self.initializers
|
||||||
assert values
|
assert initializers
|
||||||
value = values.get(field)
|
value = initializers.get(field)
|
||||||
assert value
|
assert value
|
||||||
fm = value.match
|
return value.make_patch(replacement)
|
||||||
assert fm
|
|
||||||
fstart = self.match.start('fields') + fm.start()
|
|
||||||
fend = self.match.start('fields') + fm.end()
|
|
||||||
return Patch(fstart, fend, replacement)
|
|
||||||
|
|
||||||
|
def remove_field(self, field: str) -> Iterable[Patch]:
|
||||||
|
initializers = self.initializers
|
||||||
|
assert initializers
|
||||||
|
if field in initializers:
|
||||||
|
yield self.patch_field(field, '')
|
||||||
|
|
||||||
|
def remove_fields(self, *fields: str) -> Iterable[Patch]:
|
||||||
|
for f in fields:
|
||||||
|
yield from self.remove_field(f)
|
||||||
|
|
||||||
|
def patch_field_value(self, field: str, replacement: str) -> Patch:
|
||||||
|
"""Replace just the value of a field initializer"""
|
||||||
|
initializers = self.initializers
|
||||||
|
assert initializers
|
||||||
|
value = initializers.get(field)
|
||||||
|
assert value
|
||||||
|
vm = value.group_match('value')
|
||||||
|
assert vm
|
||||||
|
return vm.make_patch(replacement)
|
||||||
|
|
||||||
|
|
||||||
|
class RemoveRedundantClassSize(TypeInfoVar):
|
||||||
|
"""Remove class_size when using OBJECT_DECLARE_SIMPLE_TYPE"""
|
||||||
def gen_patches(self) -> Iterable[Patch]:
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
initializers = self.initializers
|
||||||
|
if initializers is None:
|
||||||
|
return
|
||||||
|
if 'class_size' not in initializers:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.debug("Handling %s", self.name)
|
||||||
|
m = re.fullmatch(RE_SIZEOF, initializers['class_size'].raw)
|
||||||
|
if not m:
|
||||||
|
self.warn("%s class_size is not sizeof?", self.name)
|
||||||
|
return
|
||||||
|
classtype = m.group('sizeoftype')
|
||||||
|
if not classtype.endswith('Class'):
|
||||||
|
self.warn("%s class size type (%s) is not *Class?", self.name, classtype)
|
||||||
|
return
|
||||||
|
self.debug("classtype is %s", classtype)
|
||||||
|
instancetype = classtype[:-len('Class')]
|
||||||
|
self.debug("intanceypte is %s", instancetype)
|
||||||
|
self.debug("searching for simpletype declaration using %s as InstanceType", instancetype)
|
||||||
|
decl = self.allfiles.find_match(OldStyleObjectDeclareSimpleType,
|
||||||
|
instancetype, 'instancetype')
|
||||||
|
if not decl:
|
||||||
|
self.debug("No simpletype declaration found for %s", instancetype)
|
||||||
|
return
|
||||||
|
self.debug("Found simple type declaration")
|
||||||
|
decl.debug("declaration is here")
|
||||||
|
yield from self.remove_field('class_size')
|
||||||
|
|
||||||
|
class RemoveDeclareSimpleTypeArg(OldStyleObjectDeclareSimpleType):
|
||||||
|
"""Remove class_size when using OBJECT_DECLARE_SIMPLE_TYPE"""
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
c = (f'OBJECT_DECLARE_SIMPLE_TYPE({self.group("instancetype")}, {self.group("lowercase")},\n'
|
||||||
|
f' {self.group("uppercase")})\n')
|
||||||
|
yield self.make_patch(c)
|
||||||
|
|
||||||
|
class UseDeclareTypeExtended(TypeInfoVar):
|
||||||
|
"""Replace TypeInfo variable with OBJECT_DEFINE_TYPE_EXTENDED"""
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
# this will just ensure the caches for find_match() and matches_for_type()
|
||||||
|
# will be loaded in advance:
|
||||||
|
find_type_checkers(self.allfiles, 'xxxxxxxxxxxxxxxxx')
|
||||||
|
|
||||||
|
if not self.is_static():
|
||||||
|
self.info("Skipping non-static TypeInfo variable")
|
||||||
|
return
|
||||||
|
|
||||||
|
type_info_macro = self.file.find_match(TypeInfoMacro, self.name)
|
||||||
|
if not type_info_macro:
|
||||||
|
self.warn("TYPE_INFO(%s) line not found", self.name)
|
||||||
|
return
|
||||||
|
|
||||||
values = self.initializers
|
values = self.initializers
|
||||||
if values is None:
|
if values is None:
|
||||||
return
|
return
|
||||||
if 'name' not in values:
|
if 'name' not in values:
|
||||||
self.warn("name not set in TypeInfo variable %s", self.name)
|
self.warn("name not set in TypeInfo variable %s", self.name)
|
||||||
return
|
return
|
||||||
|
|
||||||
typename = values['name'].raw
|
typename = values['name'].raw
|
||||||
|
|
||||||
if 'parent' not in values:
|
if 'parent' not in values:
|
||||||
self.warn("parent not set in TypeInfo variable %s", self.name)
|
self.warn("parent not set in TypeInfo variable %s", self.name)
|
||||||
return
|
return
|
||||||
|
@ -167,49 +314,403 @@ def gen_patches(self) -> Iterable[Patch]:
|
||||||
self.warn("class_size is set to: %r", values['class_size'].raw)
|
self.warn("class_size is set to: %r", values['class_size'].raw)
|
||||||
return
|
return
|
||||||
|
|
||||||
#NOTE: this will NOT work after declarations are converted
|
#for t in (typename, parent_typename):
|
||||||
# to OBJECT_DECLARE*
|
# if not re.fullmatch(RE_IDENTIFIER, t):
|
||||||
|
# self.info("type name is not a macro/constant")
|
||||||
|
# if instancetype or classtype:
|
||||||
|
# self.warn("macro/constant type name is required for instance/class type")
|
||||||
|
# if not self.file.force:
|
||||||
|
# return
|
||||||
|
|
||||||
# Now, the challenge is to find out the right MODULE_OBJ_NAME for the
|
# Now, the challenge is to find out the right MODULE_OBJ_NAME for the
|
||||||
# type and for the parent type
|
# type and for the parent type
|
||||||
instance_decl = find_type_declaration(self.allfiles, typename)
|
|
||||||
parent_decl = find_type_declaration(self.allfiles, parent_typename)
|
|
||||||
|
|
||||||
self.info("TypeInfo variable for %s is here", typename)
|
self.info("TypeInfo variable for %s is here", typename)
|
||||||
if instance_decl:
|
uppercase = find_typename_uppercase(self.allfiles, typename)
|
||||||
instance_decl.info("instance type declaration (%s) is here", instance_decl.match.group('uppercase'))
|
if not uppercase:
|
||||||
if parent_decl:
|
self.info("Can't find right uppercase name for %s", typename)
|
||||||
parent_decl.info("parent type declaration (%s) is here", parent_decl.match.group('uppercase'))
|
if instancetype or classtype:
|
||||||
|
self.warn("Can't find right uppercase name for %s", typename)
|
||||||
|
self.warn("This will make type validation difficult in the future")
|
||||||
|
return
|
||||||
|
|
||||||
|
parent_uppercase = find_typename_uppercase(self.allfiles, parent_typename)
|
||||||
|
if not parent_uppercase:
|
||||||
|
self.info("Can't find right uppercase name for parent type (%s)", parent_typename)
|
||||||
|
if instancetype or classtype:
|
||||||
|
self.warn("Can't find right uppercase name for parent type (%s)", parent_typename)
|
||||||
|
self.warn("This will make type validation difficult in the future")
|
||||||
|
return
|
||||||
|
|
||||||
ok = True
|
ok = True
|
||||||
if (instance_decl is None and (instancetype or classtype)):
|
|
||||||
self.warn("Can't find where type checkers for %s are declared. We need them to validate sizes of %s", typename, self.name)
|
|
||||||
ok = False
|
|
||||||
|
|
||||||
if (instance_decl is not None
|
#checkers: List[TypeCheckerDeclaration] = list(find_type_checkers(self.allfiles, uppercase))
|
||||||
and 'instancetype' in instance_decl.match.groupdict()
|
#for c in checkers:
|
||||||
and instancetype != instance_decl.group('instancetype')):
|
# c.info("instance type checker declaration (%s) is here", c.group('uppercase'))
|
||||||
self.warn("type at instance_size is %r. Should instance_size be set to sizeof(%s) ?",
|
#if not checkers:
|
||||||
instancetype, instance_decl.group('instancetype'))
|
# self.info("No type checkers declared for %s", uppercase)
|
||||||
instance_decl.warn("Type checker declaration for %s is here", typename)
|
# if instancetype or classtype:
|
||||||
ok = False
|
# self.warn("Can't find where type checkers for %s (%s) are declared. We will need them to validate sizes of %s",
|
||||||
if (instance_decl is not None
|
# typename, uppercase, self.name)
|
||||||
and 'classtype' in instance_decl.match.groupdict()
|
|
||||||
and classtype != instance_decl.group('classtype')):
|
|
||||||
self.warn("type at class_size is %r. Should class_size be set to sizeof(%s) ?",
|
|
||||||
classtype, instance_decl.group('classtype'))
|
|
||||||
instance_decl.warn("Type checker declaration for %s is here", typename)
|
|
||||||
ok = False
|
|
||||||
|
|
||||||
if not ok:
|
if not instancetype:
|
||||||
return
|
instancetype = 'void'
|
||||||
|
if not classtype:
|
||||||
|
classtype = 'void'
|
||||||
|
|
||||||
|
#checker_instancetypes = set(c.instancetype for c in checkers
|
||||||
|
# if c.instancetype is not None)
|
||||||
|
#if len(checker_instancetypes) > 1:
|
||||||
|
# self.warn("ambiguous set of type checkers")
|
||||||
|
# for c in checkers:
|
||||||
|
# c.warn("instancetype is %s here", c.instancetype)
|
||||||
|
# ok = False
|
||||||
|
#elif len(checker_instancetypes) == 1:
|
||||||
|
# checker_instancetype = checker_instancetypes.pop()
|
||||||
|
# DBG("checker instance type: %r", checker_instancetype)
|
||||||
|
# if instancetype != checker_instancetype:
|
||||||
|
# self.warn("type at instance_size is %r. Should instance_size be set to sizeof(%s) ?",
|
||||||
|
# instancetype, checker_instancetype)
|
||||||
|
# ok = False
|
||||||
|
#else:
|
||||||
|
# if instancetype != 'void':
|
||||||
|
# self.warn("instance type checker for %s (%s) not found", typename, instancetype)
|
||||||
|
# ok = False
|
||||||
|
|
||||||
|
#checker_classtypes = set(c.classtype for c in checkers
|
||||||
|
# if c.classtype is not None)
|
||||||
|
#if len(checker_classtypes) > 1:
|
||||||
|
# self.warn("ambiguous set of type checkers")
|
||||||
|
# for c in checkers:
|
||||||
|
# c.warn("classtype is %s here", c.classtype)
|
||||||
|
# ok = False
|
||||||
|
#elif len(checker_classtypes) == 1:
|
||||||
|
# checker_classtype = checker_classtypes.pop()
|
||||||
|
# DBG("checker class type: %r", checker_classtype)
|
||||||
|
# if classtype != checker_classtype:
|
||||||
|
# self.warn("type at class_size is %r. Should class_size be set to sizeof(%s) ?",
|
||||||
|
# classtype, checker_classtype)
|
||||||
|
# ok = False
|
||||||
|
#else:
|
||||||
|
# if classtype != 'void':
|
||||||
|
# self.warn("class type checker for %s (%s) not found", typename, classtype)
|
||||||
|
# ok = False
|
||||||
|
|
||||||
|
#if not ok:
|
||||||
|
# for c in checkers:
|
||||||
|
# c.warn("Type checker declaration for %s (%s) is here",
|
||||||
|
# typename, type(c).__name__)
|
||||||
|
# return
|
||||||
|
|
||||||
#if parent_decl is None:
|
#if parent_decl is None:
|
||||||
# self.warn("Can't find where parent type %s is declared", parent_typename)
|
# self.warn("Can't find where parent type %s is declared", parent_typename)
|
||||||
|
|
||||||
|
#yield self.prepend(f'DECLARE_TYPE_NAME({uppercase}, {typename})\n')
|
||||||
|
#if not instancetype:
|
||||||
|
# yield self.prepend(f'DECLARE_INSTANCE_TYPE({uppercase}, void)\n')
|
||||||
|
#if not classtype:
|
||||||
|
# yield self.prepend(f'DECLARE_CLASS_TYPE({uppercase}, void)\n')
|
||||||
self.info("%s can be patched!", self.name)
|
self.info("%s can be patched!", self.name)
|
||||||
return
|
replaced_fields = ['name', 'parent', 'instance_size', 'class_size']
|
||||||
yield
|
begin = self.group_match('begin')
|
||||||
|
newbegin = f'OBJECT_DEFINE_TYPE_EXTENDED({self.name},\n'
|
||||||
|
newbegin += f' {instancetype}, {classtype},\n'
|
||||||
|
newbegin += f' {uppercase}, {parent_uppercase}'
|
||||||
|
if set(values.keys()) - set(replaced_fields):
|
||||||
|
newbegin += ',\n'
|
||||||
|
yield begin.make_patch(newbegin)
|
||||||
|
yield from self.remove_fields(*replaced_fields)
|
||||||
|
end = self.group_match('end')
|
||||||
|
yield end.make_patch(')\n')
|
||||||
|
yield type_info_macro.make_removal_patch()
|
||||||
|
|
||||||
|
class ObjectDefineTypeExtended(TypeDefinition):
|
||||||
|
"""OBJECT_DEFINE_TYPE_EXTENDED usage"""
|
||||||
|
regexp = S(r'^[ \t]*OBJECT_DEFINE_TYPE_EXTENDED\s*\(\s*',
|
||||||
|
NAMED('name', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('instancetype', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('classtype', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('parent_uppercase', RE_IDENTIFIER),
|
||||||
|
M(r',\s*\n',
|
||||||
|
NAMED('fields', RE_TI_FIELDS),
|
||||||
|
n='?'),
|
||||||
|
r'\s*\);?\n?')
|
||||||
|
|
||||||
|
class ObjectDefineType(TypeDefinition):
|
||||||
|
"""OBJECT_DEFINE_TYPE usage"""
|
||||||
|
regexp = S(r'^[ \t]*OBJECT_DEFINE_TYPE\s*\(\s*',
|
||||||
|
NAMED('lowercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('uppercase', RE_IDENTIFIER), r'\s*,\s*',
|
||||||
|
NAMED('parent_uppercase', RE_IDENTIFIER),
|
||||||
|
M(r',\s*\n',
|
||||||
|
NAMED('fields', RE_TI_FIELDS),
|
||||||
|
n='?'),
|
||||||
|
r'\s*\);?\n?')
|
||||||
|
|
||||||
|
def find_type_definitions(files: FileList, uppercase: str) -> Iterable[TypeDefinition]:
|
||||||
|
types: List[Type[TypeDefinition]] = [TypeInfoVar, ObjectDefineType, ObjectDefineTypeExtended]
|
||||||
|
for t in types:
|
||||||
|
for m in files.matches_of_type(t):
|
||||||
|
m.debug("uppercase: %s", m.uppercase)
|
||||||
|
yield from (m for t in types
|
||||||
|
for m in files.matches_of_type(t)
|
||||||
|
if m.uppercase == uppercase)
|
||||||
|
|
||||||
|
class AddDeclareVoidClassType(TypeDeclarationFixup):
|
||||||
|
"""Will add DECLARE_CLASS_TYPE(..., void) if possible"""
|
||||||
|
def gen_patches_for_type(self, uppercase: str,
|
||||||
|
checkers: List[TypeDeclaration],
|
||||||
|
fields: Dict[str, Optional[str]]) -> Iterable[Patch]:
|
||||||
|
defs = list(find_type_definitions(self.allfiles, uppercase))
|
||||||
|
if len(defs) > 1:
|
||||||
|
self.warn("multiple definitions for %s", uppercase)
|
||||||
|
for d in defs:
|
||||||
|
d.warn("definition found here")
|
||||||
|
return
|
||||||
|
elif len(defs) == 0:
|
||||||
|
self.warn("type definition for %s not found", uppercase)
|
||||||
|
return
|
||||||
|
d = defs[0]
|
||||||
|
if d.classtype is None:
|
||||||
|
d.info("definition for %s has classtype, skipping", uppercase)
|
||||||
|
return
|
||||||
|
class_type_checkers = [c for c in checkers
|
||||||
|
if c.classtype is not None]
|
||||||
|
if class_type_checkers:
|
||||||
|
for c in class_type_checkers:
|
||||||
|
c.warn("class type checker for %s is present here", uppercase)
|
||||||
|
return
|
||||||
|
|
||||||
|
_,last_checker = max((m.start(), m) for m in checkers)
|
||||||
|
s = f'DECLARE_CLASS_TYPE({uppercase}, void)\n'
|
||||||
|
yield last_checker.append(s)
|
||||||
|
|
||||||
|
class AddDeclareVoidInstanceType(FileMatch):
|
||||||
|
"""Will add DECLARE_INSTANCE_TYPE(..., void) if possible"""
|
||||||
|
regexp = S(r'^[ \t]*#[ \t]*define', CPP_SPACE,
|
||||||
|
NAMED('name', r'TYPE_[a-zA-Z0-9_]+\b'),
|
||||||
|
CPP_SPACE, r'.*\n')
|
||||||
|
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
assert self.name.startswith('TYPE_')
|
||||||
|
uppercase = self.name[len('TYPE_'):]
|
||||||
|
defs = list(find_type_definitions(self.allfiles, uppercase))
|
||||||
|
if len(defs) > 1:
|
||||||
|
self.warn("multiple definitions for %s", uppercase)
|
||||||
|
for d in defs:
|
||||||
|
d.warn("definition found here")
|
||||||
|
return
|
||||||
|
elif len(defs) == 0:
|
||||||
|
self.warn("type definition for %s not found", uppercase)
|
||||||
|
return
|
||||||
|
d = defs[0]
|
||||||
|
instancetype = d.instancetype
|
||||||
|
if instancetype is not None and instancetype != 'void':
|
||||||
|
return
|
||||||
|
|
||||||
|
instance_checkers = [c for c in find_type_checkers(self.allfiles, uppercase)
|
||||||
|
if c.instancetype]
|
||||||
|
if instance_checkers:
|
||||||
|
d.warn("instance type checker for %s already declared", uppercase)
|
||||||
|
for c in instance_checkers:
|
||||||
|
c.warn("instance checker for %s is here", uppercase)
|
||||||
|
return
|
||||||
|
|
||||||
|
s = f'DECLARE_INSTANCE_TYPE({uppercase}, void)\n'
|
||||||
|
yield self.append(s)
|
||||||
|
|
||||||
|
class AddObjectDeclareType(DeclareObjCheckers):
|
||||||
|
"""Will add OBJECT_DECLARE_TYPE(...) if possible"""
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
uppercase = self.uppercase
|
||||||
|
typename = self.group('typename')
|
||||||
|
instancetype = self.group('instancetype')
|
||||||
|
classtype = self.group('classtype')
|
||||||
|
|
||||||
|
if typename != f'TYPE_{uppercase}':
|
||||||
|
self.warn("type name mismatch: %s vs %s", typename, uppercase)
|
||||||
|
return
|
||||||
|
|
||||||
|
typedefs = [(t,self.allfiles.find_matches(SimpleTypedefMatch, t))
|
||||||
|
for t in (instancetype, classtype)]
|
||||||
|
for t,tds in typedefs:
|
||||||
|
if not tds:
|
||||||
|
self.warn("typedef %s not found", t)
|
||||||
|
return
|
||||||
|
for td in tds:
|
||||||
|
td_type = td.group('typedef_type')
|
||||||
|
if td_type != f'struct {t}':
|
||||||
|
self.warn("typedef mismatch: %s is defined as %s", t, td_type)
|
||||||
|
td.warn("typedef is here")
|
||||||
|
return
|
||||||
|
|
||||||
|
# look for reuse of same struct type
|
||||||
|
other_instance_checkers = [c for c in find_type_checkers(self.allfiles, instancetype, 'instancetype')
|
||||||
|
if c.uppercase != uppercase]
|
||||||
|
if other_instance_checkers:
|
||||||
|
self.warn("typedef %s is being reused", instancetype)
|
||||||
|
for ic in other_instance_checkers:
|
||||||
|
ic.warn("%s is reused here", instancetype)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
|
decl_types: List[Type[TypeDeclaration]] = [DeclareClassCheckers, DeclareObjCheckers]
|
||||||
|
class_decls = [m for t in decl_types
|
||||||
|
for m in self.allfiles.find_matches(t, uppercase, 'uppercase')]
|
||||||
|
|
||||||
|
defs = list(find_type_definitions(self.allfiles, uppercase))
|
||||||
|
if len(defs) > 1:
|
||||||
|
self.warn("multiple definitions for %s", uppercase)
|
||||||
|
for d in defs:
|
||||||
|
d.warn("definition found here")
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
elif len(defs) == 0:
|
||||||
|
self.warn("type definition for %s not found", uppercase)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
d = defs[0]
|
||||||
|
if d.instancetype != instancetype:
|
||||||
|
self.warn("mismatching instance type for %s (%s)", uppercase, instancetype)
|
||||||
|
d.warn("instance type declared here (%s)", d.instancetype)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
if d.classtype != classtype:
|
||||||
|
self.warn("mismatching class type for %s (%s)", uppercase, classtype)
|
||||||
|
d.warn("class type declared here (%s)", d.classtype)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
|
assert self.file.original_content
|
||||||
|
for t,tds in typedefs:
|
||||||
|
assert tds
|
||||||
|
for td in tds:
|
||||||
|
if td.file is not self.file:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# delete typedefs that are truly redundant:
|
||||||
|
# 1) defined after DECLARE_OBJ_CHECKERS
|
||||||
|
if td.start() > self.start():
|
||||||
|
yield td.make_removal_patch()
|
||||||
|
# 2) defined before DECLARE_OBJ_CHECKERS, but unused
|
||||||
|
elif not re.search(r'\b'+t+r'\b', self.file.original_content[td.end():self.start()]):
|
||||||
|
yield td.make_removal_patch()
|
||||||
|
|
||||||
|
c = (f'OBJECT_DECLARE_TYPE({instancetype}, {classtype}, {uppercase})\n')
|
||||||
|
yield self.make_patch(c)
|
||||||
|
|
||||||
|
class AddObjectDeclareSimpleType(DeclareInstanceChecker):
|
||||||
|
"""Will add OBJECT_DECLARE_SIMPLE_TYPE(...) if possible"""
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
uppercase = self.uppercase
|
||||||
|
typename = self.group('typename')
|
||||||
|
instancetype = self.group('instancetype')
|
||||||
|
|
||||||
|
if typename != f'TYPE_{uppercase}':
|
||||||
|
self.warn("type name mismatch: %s vs %s", typename, uppercase)
|
||||||
|
return
|
||||||
|
|
||||||
|
typedefs = [(t,self.allfiles.find_matches(SimpleTypedefMatch, t))
|
||||||
|
for t in (instancetype,)]
|
||||||
|
for t,tds in typedefs:
|
||||||
|
if not tds:
|
||||||
|
self.warn("typedef %s not found", t)
|
||||||
|
return
|
||||||
|
for td in tds:
|
||||||
|
td_type = td.group('typedef_type')
|
||||||
|
if td_type != f'struct {t}':
|
||||||
|
self.warn("typedef mismatch: %s is defined as %s", t, td_type)
|
||||||
|
td.warn("typedef is here")
|
||||||
|
return
|
||||||
|
|
||||||
|
# look for reuse of same struct type
|
||||||
|
other_instance_checkers = [c for c in find_type_checkers(self.allfiles, instancetype, 'instancetype')
|
||||||
|
if c.uppercase != uppercase]
|
||||||
|
if other_instance_checkers:
|
||||||
|
self.warn("typedef %s is being reused", instancetype)
|
||||||
|
for ic in other_instance_checkers:
|
||||||
|
ic.warn("%s is reused here", instancetype)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
|
decl_types: List[Type[TypeDeclaration]] = [DeclareClassCheckers, DeclareObjCheckers]
|
||||||
|
class_decls = [m for t in decl_types
|
||||||
|
for m in self.allfiles.find_matches(t, uppercase, 'uppercase')]
|
||||||
|
if class_decls:
|
||||||
|
self.warn("class type declared for %s", uppercase)
|
||||||
|
for cd in class_decls:
|
||||||
|
cd.warn("class declaration found here")
|
||||||
|
return
|
||||||
|
|
||||||
|
defs = list(find_type_definitions(self.allfiles, uppercase))
|
||||||
|
if len(defs) > 1:
|
||||||
|
self.warn("multiple definitions for %s", uppercase)
|
||||||
|
for d in defs:
|
||||||
|
d.warn("definition found here")
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
elif len(defs) == 0:
|
||||||
|
self.warn("type definition for %s not found", uppercase)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
d = defs[0]
|
||||||
|
if d.instancetype != instancetype:
|
||||||
|
self.warn("mismatching instance type for %s (%s)", uppercase, instancetype)
|
||||||
|
d.warn("instance type declared here (%s)", d.instancetype)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
if d.classtype:
|
||||||
|
self.warn("class type set for %s", uppercase)
|
||||||
|
d.warn("class type declared here")
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
|
assert self.file.original_content
|
||||||
|
for t,tds in typedefs:
|
||||||
|
assert tds
|
||||||
|
for td in tds:
|
||||||
|
if td.file is not self.file:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# delete typedefs that are truly redundant:
|
||||||
|
# 1) defined after DECLARE_OBJ_CHECKERS
|
||||||
|
if td.start() > self.start():
|
||||||
|
yield td.make_removal_patch()
|
||||||
|
# 2) defined before DECLARE_OBJ_CHECKERS, but unused
|
||||||
|
elif not re.search(r'\b'+t+r'\b', self.file.original_content[td.end():self.start()]):
|
||||||
|
yield td.make_removal_patch()
|
||||||
|
|
||||||
|
c = (f'OBJECT_DECLARE_SIMPLE_TYPE({instancetype}, {uppercase})\n')
|
||||||
|
yield self.make_patch(c)
|
||||||
|
|
||||||
|
|
||||||
|
class TypeInfoStringName(TypeInfoVar):
|
||||||
|
"""Replace hardcoded type names with TYPE_ constant"""
|
||||||
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
values = self.initializers
|
||||||
|
if values is None:
|
||||||
|
return
|
||||||
|
if 'name' not in values:
|
||||||
|
self.warn("name not set in TypeInfo variable %s", self.name)
|
||||||
|
return
|
||||||
|
typename = values['name'].raw
|
||||||
|
if re.fullmatch(RE_IDENTIFIER, typename):
|
||||||
|
return
|
||||||
|
|
||||||
|
self.warn("name %s is not an identifier", typename)
|
||||||
|
#all_defines = [m for m in self.allfiles.matches_of_type(ExpressionDefine)]
|
||||||
|
#self.debug("all_defines: %r", all_defines)
|
||||||
|
constants = [m for m in self.allfiles.matches_of_type(ExpressionDefine)
|
||||||
|
if m.group('value').strip() == typename.strip()]
|
||||||
|
if not constants:
|
||||||
|
self.warn("No macro for %s found", typename)
|
||||||
|
return
|
||||||
|
if len(constants) > 1:
|
||||||
|
self.warn("I don't know which macro to use: %r", constants)
|
||||||
|
return
|
||||||
|
yield self.patch_field_value('name', constants[0].name)
|
||||||
|
|
||||||
class RedundantTypeSizes(TypeInfoVar):
|
class RedundantTypeSizes(TypeInfoVar):
|
||||||
"""Remove redundant instance_size/class_size from TypeInfo vars"""
|
"""Remove redundant instance_size/class_size from TypeInfo vars"""
|
||||||
|
@ -230,8 +731,8 @@ def gen_patches(self) -> Iterable[Patch]:
|
||||||
self.debug("no need to validate %s", self.name)
|
self.debug("no need to validate %s", self.name)
|
||||||
return
|
return
|
||||||
|
|
||||||
instance_decl = find_type_declaration(self.allfiles, typename)
|
instance_decls = find_type_checkers(self.allfiles, typename)
|
||||||
if instance_decl:
|
if instance_decls:
|
||||||
self.debug("won't touch TypeInfo var that has type checkers")
|
self.debug("won't touch TypeInfo var that has type checkers")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -240,12 +741,12 @@ def gen_patches(self) -> Iterable[Patch]:
|
||||||
self.warn("Can't find TypeInfo for %s", parent_typename)
|
self.warn("Can't find TypeInfo for %s", parent_typename)
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'instance_size' in values and parent.get_initializer_value('instance_size').raw != values['instance_size'].raw:
|
if 'instance_size' in values and parent.get_raw_initializer_value('instance_size') != values['instance_size'].raw:
|
||||||
self.info("instance_size mismatch")
|
self.info("instance_size mismatch")
|
||||||
parent.info("parent type declared here")
|
parent.info("parent type declared here")
|
||||||
return
|
return
|
||||||
|
|
||||||
if 'class_size' in values and parent.get_initializer_value('class_size').raw != values['class_size'].raw:
|
if 'class_size' in values and parent.get_raw_initializer_value('class_size') != values['class_size'].raw:
|
||||||
self.info("class_size mismatch")
|
self.info("class_size mismatch")
|
||||||
parent.info("parent type declared here")
|
parent.info("parent type declared here")
|
||||||
return
|
return
|
||||||
|
@ -303,10 +804,11 @@ def gen_patches(self) -> Iterable[Patch]:
|
||||||
# yield self.append_field('class_init', ids.lowercase+'_class_init')
|
# yield self.append_field('class_init', ids.lowercase+'_class_init')
|
||||||
|
|
||||||
class TypeInitMacro(FileMatch):
|
class TypeInitMacro(FileMatch):
|
||||||
"""type_init(...) macro use
|
"""Use of type_init(...) macro"""
|
||||||
Will be deleted if function is empty
|
|
||||||
"""
|
|
||||||
regexp = S(r'^[ \t]*type_init\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);?[ \t]*\n')
|
regexp = S(r'^[ \t]*type_init\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);?[ \t]*\n')
|
||||||
|
|
||||||
|
class DeleteEmptyTypeInitFunc(TypeInitMacro):
|
||||||
|
"""Delete empty function declared using type_init(...)"""
|
||||||
def gen_patches(self) -> Iterable[Patch]:
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
fn = self.file.find_match(StaticVoidFunction, self.name)
|
fn = self.file.find_match(StaticVoidFunction, self.name)
|
||||||
DBG("function for %s: %s", self.name, fn)
|
DBG("function for %s: %s", self.name, fn)
|
||||||
|
@ -331,7 +833,7 @@ class StaticVoidFunction(FileMatch):
|
||||||
r'#[^\n]*\n',
|
r'#[^\n]*\n',
|
||||||
r'\n',
|
r'\n',
|
||||||
repeat='*')),
|
repeat='*')),
|
||||||
r'}\n')
|
r'};?\n')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def body(self) -> str:
|
def body(self) -> str:
|
||||||
|
@ -340,34 +842,40 @@ def body(self) -> str:
|
||||||
def has_preprocessor_directive(self) -> bool:
|
def has_preprocessor_directive(self) -> bool:
|
||||||
return bool(re.search(r'^[ \t]*#', self.body, re.MULTILINE))
|
return bool(re.search(r'^[ \t]*#', self.body, re.MULTILINE))
|
||||||
|
|
||||||
class TypeRegisterCall(FileMatch):
|
def find_containing_func(m: FileMatch) -> Optional['StaticVoidFunction']:
|
||||||
|
"""Return function containing this match"""
|
||||||
|
for fn in m.file.matches_of_type(StaticVoidFunction):
|
||||||
|
if fn.contains(m):
|
||||||
|
return fn
|
||||||
|
return None
|
||||||
|
|
||||||
|
class TypeRegisterStaticCall(FileMatch):
|
||||||
"""type_register_static() call
|
"""type_register_static() call
|
||||||
Will be replaced by TYPE_INFO() macro
|
Will be replaced by TYPE_INFO() macro
|
||||||
"""
|
"""
|
||||||
regexp = S(r'^[ \t]*type_register_static\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n')
|
regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register_static'),
|
||||||
|
r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n')
|
||||||
def function(self) -> Optional['StaticVoidFunction']:
|
|
||||||
"""Return function containing this call"""
|
|
||||||
for m in self.file.matches_of_type(StaticVoidFunction):
|
|
||||||
if m.contains(self):
|
|
||||||
return m
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
class UseTypeInfo(TypeRegisterStaticCall):
|
||||||
|
"""Replace type_register_static() call with TYPE_INFO declaration"""
|
||||||
def gen_patches(self) -> Iterable[Patch]:
|
def gen_patches(self) -> Iterable[Patch]:
|
||||||
fn = self.function()
|
fn = find_containing_func(self)
|
||||||
if fn is None:
|
if fn:
|
||||||
self.warn("can't find function where type_register_static(&%s) is called", self.name)
|
DBG("%r is inside %r", self, fn)
|
||||||
return
|
type_init = self.file.find_match(TypeInitMacro, fn.name)
|
||||||
|
if type_init is None:
|
||||||
|
self.warn("can't find type_init(%s) line", fn.name)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.warn("can't identify the function where type_register_static(&%s) is called", self.name)
|
||||||
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
#if fn.has_preprocessor_directive() and not self.file.force:
|
#if fn.has_preprocessor_directive() and not self.file.force:
|
||||||
# self.warn("function %s has preprocessor directives, this requires --force", fn.name)
|
# self.warn("function %s has preprocessor directives, this requires --force", fn.name)
|
||||||
# return
|
# return
|
||||||
|
|
||||||
type_init = self.file.find_match(TypeInitMacro, fn.name)
|
|
||||||
if type_init is None:
|
|
||||||
self.warn("can't find type_init(%s) line", fn.name)
|
|
||||||
return
|
|
||||||
|
|
||||||
var = self.file.find_match(TypeInfoVar, self.name)
|
var = self.file.find_match(TypeInfoVar, self.name)
|
||||||
if var is None:
|
if var is None:
|
||||||
self.warn("can't find TypeInfo var declaration for %s", self.name)
|
self.warn("can't find TypeInfo var declaration for %s", self.name)
|
||||||
|
@ -375,24 +883,51 @@ def gen_patches(self) -> Iterable[Patch]:
|
||||||
|
|
||||||
if not var.is_full():
|
if not var.is_full():
|
||||||
self.warn("variable declaration %s wasn't parsed fully", var.name)
|
self.warn("variable declaration %s wasn't parsed fully", var.name)
|
||||||
return
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
if fn.contains(var):
|
if fn and fn.contains(var):
|
||||||
self.warn("TypeInfo %s variable is inside a function", self.name)
|
self.warn("TypeInfo %s variable is inside a function", self.name)
|
||||||
return
|
if not self.file.force:
|
||||||
|
return
|
||||||
|
|
||||||
# delete type_register_static() call:
|
# delete type_register_static() call:
|
||||||
yield self.make_patch('')
|
yield self.make_patch('')
|
||||||
# append TYPE_REGISTER(...) after variable declaration:
|
# append TYPE_REGISTER(...) after variable declaration:
|
||||||
yield var.append(f'TYPE_INFO({self.name})\n')
|
yield var.append(f'TYPE_INFO({self.name})\n')
|
||||||
|
|
||||||
|
class TypeRegisterCall(FileMatch):
|
||||||
|
"""type_register_static() call"""
|
||||||
|
regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register'),
|
||||||
|
r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n')
|
||||||
|
|
||||||
|
class MakeTypeRegisterStatic(TypeRegisterCall):
|
||||||
|
"""Make type_register() call static if variable is static const"""
|
||||||
|
def gen_patches(self):
|
||||||
|
var = self.file.find_match(TypeInfoVar, self.name)
|
||||||
|
if var is None:
|
||||||
|
self.warn("can't find TypeInfo var declaration for %s", self.name)
|
||||||
|
return
|
||||||
|
if var.is_static() and var.is_const():
|
||||||
|
yield self.group_match('func_name').make_patch('type_register_static')
|
||||||
|
|
||||||
|
class MakeTypeRegisterNotStatic(TypeRegisterStaticCall):
|
||||||
|
"""Make type_register() call static if variable is static const"""
|
||||||
|
def gen_patches(self):
|
||||||
|
var = self.file.find_match(TypeInfoVar, self.name)
|
||||||
|
if var is None:
|
||||||
|
self.warn("can't find TypeInfo var declaration for %s", self.name)
|
||||||
|
return
|
||||||
|
if not var.is_static() or not var.is_const():
|
||||||
|
yield self.group_match('func_name').make_patch('type_register')
|
||||||
|
|
||||||
class TypeInfoMacro(FileMatch):
|
class TypeInfoMacro(FileMatch):
|
||||||
"""TYPE_INFO macro usage"""
|
"""TYPE_INFO macro usage"""
|
||||||
regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n')
|
regexp = S(r'^[ \t]*TYPE_INFO\s*\(\s*', NAMED('name', RE_IDENTIFIER), r'\s*\)[ \t]*;?[ \t]*\n')
|
||||||
|
|
||||||
def find_type_info(files: RegexpScanner, name: str) -> Optional[TypeInfoVar]:
|
def find_type_info(files: RegexpScanner, name: str) -> Optional[TypeInfoVar]:
|
||||||
ti = [ti for ti in files.matches_of_type(TypeInfoVar)
|
ti = [ti for ti in files.matches_of_type(TypeInfoVar)
|
||||||
if ti.get_initializer_value('name').raw == name]
|
if ti.get_raw_initializer_value('name') == name]
|
||||||
DBG("type info vars: %r", ti)
|
DBG("type info vars: %r", ti)
|
||||||
if len(ti) > 1:
|
if len(ti) > 1:
|
||||||
DBG("multiple TypeInfo vars found for %s", name)
|
DBG("multiple TypeInfo vars found for %s", name)
|
||||||
|
|
|
@ -31,7 +31,6 @@ def test_pattern_patching():
|
||||||
files = FileList()
|
files = FileList()
|
||||||
f = FileInfo(files, of.name)
|
f = FileInfo(files, of.name)
|
||||||
f.load()
|
f.load()
|
||||||
f.scan_for_matches()
|
|
||||||
matches = f.matches_of_type(BasicPattern)
|
matches = f.matches_of_type(BasicPattern)
|
||||||
assert len(matches) == 2
|
assert len(matches) == 2
|
||||||
p2 = matches[1]
|
p2 = matches[1]
|
||||||
|
@ -40,7 +39,7 @@ def test_pattern_patching():
|
||||||
f.patches.append(p2.append('XXX'))
|
f.patches.append(p2.append('XXX'))
|
||||||
|
|
||||||
# apply all patches:
|
# apply all patches:
|
||||||
f.gen_patches()
|
f.gen_patches(matches)
|
||||||
patched = f.get_patched_content()
|
patched = f.get_patched_content()
|
||||||
assert patched == ('one line\n'+
|
assert patched == ('one line\n'+
|
||||||
'this pattern will be patched: defBBBBBhij\n'+
|
'this pattern will be patched: defBBBBBhij\n'+
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
from .qom_macros import *
|
from .qom_macros import *
|
||||||
from .qom_type_info import *
|
from .qom_type_info import *
|
||||||
|
|
||||||
def test_res():
|
def test_res() -> None:
|
||||||
def fullmatch(regexp, s):
|
def fullmatch(regexp, s):
|
||||||
return re.fullmatch(regexp, s, re.MULTILINE)
|
return re.fullmatch(regexp, s, re.MULTILINE)
|
||||||
|
|
||||||
|
@ -113,10 +113,10 @@ def fullmatch(regexp, s):
|
||||||
* need to set up reset or vmstate, and has no realize method.
|
* need to set up reset or vmstate, and has no realize method.
|
||||||
*/''')
|
*/''')
|
||||||
|
|
||||||
print(RE_TYPEINFO_DEF)
|
print(TypeInfoVar.regexp)
|
||||||
test_empty = 'static const TypeInfo x86_base_cpu_type_info = {\n'+\
|
test_empty = 'static const TypeInfo x86_base_cpu_type_info = {\n'+\
|
||||||
'};\n';
|
'};\n';
|
||||||
assert fullmatch(RE_TYPEINFO_DEF, test_empty)
|
assert fullmatch(TypeInfoVar.regexp, test_empty)
|
||||||
|
|
||||||
test_simple = r'''
|
test_simple = r'''
|
||||||
static const TypeInfo x86_base_cpu_type_info = {
|
static const TypeInfo x86_base_cpu_type_info = {
|
||||||
|
@ -125,7 +125,7 @@ def fullmatch(regexp, s):
|
||||||
.class_init = x86_cpu_base_class_init,
|
.class_init = x86_cpu_base_class_init,
|
||||||
};
|
};
|
||||||
'''
|
'''
|
||||||
assert re.search(RE_TYPEINFO_DEF, test_simple, re.MULTILINE)
|
assert re.search(TypeInfoVar.regexp, test_simple, re.MULTILINE)
|
||||||
|
|
||||||
test_interfaces = r'''
|
test_interfaces = r'''
|
||||||
static const TypeInfo acpi_ged_info = {
|
static const TypeInfo acpi_ged_info = {
|
||||||
|
@ -141,7 +141,7 @@ def fullmatch(regexp, s):
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
'''
|
'''
|
||||||
assert re.search(RE_TYPEINFO_DEF, test_interfaces, re.MULTILINE)
|
assert re.search(TypeInfoVar.regexp, test_interfaces, re.MULTILINE)
|
||||||
|
|
||||||
test_comments = r'''
|
test_comments = r'''
|
||||||
static const TypeInfo palm_misc_gpio_info = {
|
static const TypeInfo palm_misc_gpio_info = {
|
||||||
|
@ -155,7 +155,7 @@ def fullmatch(regexp, s):
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
'''
|
'''
|
||||||
assert re.search(RE_TYPEINFO_DEF, test_comments, re.MULTILINE)
|
assert re.search(TypeInfoVar.regexp, test_comments, re.MULTILINE)
|
||||||
|
|
||||||
test_comments = r'''
|
test_comments = r'''
|
||||||
static const TypeInfo tpm_crb_info = {
|
static const TypeInfo tpm_crb_info = {
|
||||||
|
@ -170,7 +170,7 @@ def fullmatch(regexp, s):
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
'''
|
'''
|
||||||
assert re.search(RE_TYPEINFO_DEF, test_comments, re.MULTILINE)
|
assert re.search(TypeInfoVar.regexp, test_comments, re.MULTILINE)
|
||||||
|
|
||||||
def test_struct_re():
|
def test_struct_re():
|
||||||
print('---')
|
print('---')
|
||||||
|
@ -232,8 +232,8 @@ def test_initial_includes():
|
||||||
|
|
||||||
/* pflash_cfi01.c */
|
/* pflash_cfi01.c */
|
||||||
'''
|
'''
|
||||||
print(repr(list(m.groupdict() for m in re.finditer(InitialIncludes.regexp, c, re.MULTILINE))))
|
print(repr(list(m.groupdict() for m in InitialIncludes.finditer(c))))
|
||||||
m = re.match(InitialIncludes.regexp, c, re.MULTILINE)
|
m = InitialIncludes.domatch(c)
|
||||||
assert m
|
assert m
|
||||||
print(repr(m.group(0)))
|
print(repr(m.group(0)))
|
||||||
assert m.group(0).endswith('#include "exec/hwaddr.h"\n')
|
assert m.group(0).endswith('#include "exec/hwaddr.h"\n')
|
||||||
|
@ -247,8 +247,8 @@ def test_initial_includes():
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
print(repr(list(m.groupdict() for m in re.finditer(InitialIncludes.regexp, c, re.MULTILINE))))
|
print(repr(list(m.groupdict() for m in InitialIncludes.finditer(c))))
|
||||||
m = re.match(InitialIncludes.regexp, c, re.MULTILINE)
|
m = InitialIncludes.domatch(c)
|
||||||
assert m
|
assert m
|
||||||
print(repr(m.group(0)))
|
print(repr(m.group(0)))
|
||||||
assert m.group(0).endswith('#include "9p.h"\n')
|
assert m.group(0).endswith('#include "9p.h"\n')
|
||||||
|
@ -274,8 +274,8 @@ def test_initial_includes():
|
||||||
/* Missing stuff:
|
/* Missing stuff:
|
||||||
SCTRL_P[12](END|ST)INC
|
SCTRL_P[12](END|ST)INC
|
||||||
'''
|
'''
|
||||||
print(repr(list(m.groupdict() for m in re.finditer(InitialIncludes.regexp, c, re.MULTILINE))))
|
print(repr(list(m.groupdict() for m in InitialIncludes.finditer(c))))
|
||||||
m = re.match(InitialIncludes.regexp, c, re.MULTILINE)
|
m = InitialIncludes.domatch(c)
|
||||||
assert m
|
assert m
|
||||||
print(repr(m.group(0)))
|
print(repr(m.group(0)))
|
||||||
assert m.group(0).endswith('#include "sysemu/dma.h"\n')
|
assert m.group(0).endswith('#include "sysemu/dma.h"\n')
|
||||||
|
|
|
@ -42,7 +42,7 @@ def process_all_files(parser: argparse.ArgumentParser, args: argparse.Namespace)
|
||||||
for t in f.matches_of_type(TypeInfoVar):
|
for t in f.matches_of_type(TypeInfoVar):
|
||||||
assert isinstance(t, TypeInfoVar)
|
assert isinstance(t, TypeInfoVar)
|
||||||
values = [f.filename, t.name] + \
|
values = [f.filename, t.name] + \
|
||||||
[t.get_initializer_value(f).raw
|
[t.get_raw_initializer_value(f)
|
||||||
for f in TI_FIELDS]
|
for f in TI_FIELDS]
|
||||||
DBG('values: %r', values)
|
DBG('values: %r', values)
|
||||||
assert all('\t' not in v for v in values)
|
assert all('\t' not in v for v in values)
|
||||||
|
@ -55,18 +55,18 @@ def process_all_files(parser: argparse.ArgumentParser, args: argparse.Namespace)
|
||||||
parser.error("--pattern is required")
|
parser.error("--pattern is required")
|
||||||
|
|
||||||
classes = [p for arg in args.patterns
|
classes = [p for arg in args.patterns
|
||||||
for p in re.split(r'[\s,]', arg)]
|
for p in re.split(r'[\s,]', arg)
|
||||||
|
if p.strip()]
|
||||||
for c in classes:
|
for c in classes:
|
||||||
if c not in match_classes:
|
if c not in match_classes \
|
||||||
|
or not match_classes[c].regexp:
|
||||||
print("Invalid pattern name: %s" % (c), file=sys.stderr)
|
print("Invalid pattern name: %s" % (c), file=sys.stderr)
|
||||||
print("Valid patterns:", file=sys.stderr)
|
print("Valid patterns:", file=sys.stderr)
|
||||||
print(PATTERN_HELP, file=sys.stderr)
|
print(PATTERN_HELP, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
DBG("classes: %r", classes)
|
DBG("classes: %r", classes)
|
||||||
for f in files:
|
files.patch_content(max_passes=args.passes, class_names=classes)
|
||||||
DBG("patching contents of %s", f.filename)
|
|
||||||
f.patch_content(max_passes=args.passes, class_names=classes)
|
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
#alltypes.extend(f.type_infos)
|
#alltypes.extend(f.type_infos)
|
||||||
|
|
Loading…
Reference in a new issue