|
@@ -24,11 +24,6 @@
|
|
|
|
|
|
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')
|
|
|
-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]
|
|
|
ParsedInitializerValue = Union[str, ParsedArray]
|
|
@@ -36,26 +31,55 @@ class InitializerValue(NamedTuple):
|
|
|
raw: str
|
|
|
parsed: Optional[ParsedInitializerValue]
|
|
|
match: Optional[Match]
|
|
|
-TypeInfoInitializers = Dict[str, InitializerValue]
|
|
|
-
|
|
|
-def parse_array(m: Match) -> ParsedArray:
|
|
|
- #DBG('parse_array: %r', m.group(0))
|
|
|
- return [m.group('arrayitem') for m in re.finditer(RE_ARRAY_ITEM, m.group('arrayitems'))]
|
|
|
-
|
|
|
-def parse_initializer_value(m: Match, s: str) -> InitializerValue:
|
|
|
- parsed: Optional[ParsedInitializerValue] = None
|
|
|
- #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):
|
|
|
- """TypeInfo variable declaration with initializer
|
|
|
- Will be replaced by OBJECT_DEFINE_TYPE_EXTENDED macro
|
|
|
- (not implemented yet)
|
|
|
+
|
|
|
+class ArrayItem(FileMatch):
|
|
|
+ regexp = RE_ARRAY_ITEM
|
|
|
+
|
|
|
+class ArrayInitializer(FileMatch):
|
|
|
+ regexp = RE_ARRAY
|
|
|
+
|
|
|
+ def parsed(self) -> ParsedArray:
|
|
|
+ #DBG('parse_array: %r', m.group(0))
|
|
|
+ return [m.group('arrayitem') for m in self.group_finditer(ArrayItem, 'arrayitems')]
|
|
|
+
|
|
|
+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):
|
|
|
+ """
|
|
|
+ Common base class for type definitions (TypeInfo variables or OBJECT_DEFINE* macros)
|
|
|
"""
|
|
|
- regexp = RE_TYPEINFO_DEF
|
|
|
+ @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
|
|
|
def initializers(self) -> Optional[TypeInfoInitializers]:
|
|
@@ -65,14 +89,26 @@ def initializers(self) -> Optional[TypeInfoInitializers]:
|
|
|
fields = self.group('fields')
|
|
|
if fields is None:
|
|
|
return None
|
|
|
- d = dict((fm.group('field'), parse_initializer_value(fm, fm.group('value')))
|
|
|
- for fm in re.finditer(RE_TI_FIELD_INIT, fields))
|
|
|
- self._initializers = d
|
|
|
- return d
|
|
|
+ d = dict((fm.group('field'), fm)
|
|
|
+ for fm in self.group_finditer(FieldInitializer, 'fields'))
|
|
|
+ self._initializers = d # type: ignore
|
|
|
+ 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:
|
|
|
return 'static' in self.group('modifiers')
|
|
|
|
|
|
+ def is_const(self) -> bool:
|
|
|
+ return 'const' in self.group('modifiers')
|
|
|
+
|
|
|
def is_full(self) -> bool:
|
|
|
return bool(self.group('fullspec'))
|
|
|
|
|
@@ -82,8 +118,46 @@ def get_initializers(self) -> TypeInfoInitializers:
|
|
|
return {}
|
|
|
return self.initializers
|
|
|
|
|
|
- def get_initializer_value(self, field: str) -> InitializerValue:
|
|
|
- return self.get_initializers().get(field, InitializerValue('', '', None))
|
|
|
+ def get_raw_initializer_value(self, field: str, default: str = '') -> str:
|
|
|
+ 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]:
|
|
|
# """Try to extract identifiers from names being used"""
|
|
@@ -116,32 +190,105 @@ def get_initializer_value(self, field: str) -> InitializerValue:
|
|
|
# uppercase=uppercase, lowercase=lowercase,
|
|
|
# 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"""
|
|
|
content = f' .{field} = {value},\n'
|
|
|
- return Patch(self.match.end('fields'), self.match.end('fields'),
|
|
|
- content)
|
|
|
+ fm = self.group_match('fields')
|
|
|
+ assert fm
|
|
|
+ return fm.append(content)
|
|
|
|
|
|
def patch_field(self, field: str, replacement: str) -> Patch:
|
|
|
"""Generate patch replacing a field initializer"""
|
|
|
- values = self.initializers
|
|
|
- assert values
|
|
|
- value = values.get(field)
|
|
|
+ initializers = self.initializers
|
|
|
+ assert initializers
|
|
|
+ value = initializers.get(field)
|
|
|
assert value
|
|
|
- fm = value.match
|
|
|
- assert fm
|
|
|
- fstart = self.match.start('fields') + fm.start()
|
|
|
- fend = self.match.start('fields') + fm.end()
|
|
|
- return Patch(fstart, fend, replacement)
|
|
|
+ return value.make_patch(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]:
|
|
|
+ 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
|
|
|
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 'parent' not in values:
|
|
|
self.warn("parent not set in TypeInfo variable %s", self.name)
|
|
|
return
|
|
@@ -167,49 +314,403 @@ def gen_patches(self) -> Iterable[Patch]:
|
|
|
self.warn("class_size is set to: %r", values['class_size'].raw)
|
|
|
return
|
|
|
|
|
|
- #NOTE: this will NOT work after declarations are converted
|
|
|
- # to OBJECT_DECLARE*
|
|
|
+ #for t in (typename, parent_typename):
|
|
|
+ # 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
|
|
|
# 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)
|
|
|
- if instance_decl:
|
|
|
- instance_decl.info("instance type declaration (%s) is here", instance_decl.match.group('uppercase'))
|
|
|
- if parent_decl:
|
|
|
- parent_decl.info("parent type declaration (%s) is here", parent_decl.match.group('uppercase'))
|
|
|
+ uppercase = find_typename_uppercase(self.allfiles, typename)
|
|
|
+ if not uppercase:
|
|
|
+ self.info("Can't find right uppercase name for %s", typename)
|
|
|
+ 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
|
|
|
|
|
|
- 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
|
|
|
- and 'instancetype' in instance_decl.match.groupdict()
|
|
|
- and instancetype != instance_decl.group('instancetype')):
|
|
|
- self.warn("type at instance_size is %r. Should instance_size be set to sizeof(%s) ?",
|
|
|
- instancetype, instance_decl.group('instancetype'))
|
|
|
- instance_decl.warn("Type checker declaration for %s is here", typename)
|
|
|
- ok = False
|
|
|
- if (instance_decl is not None
|
|
|
- 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:
|
|
|
+ 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
|
|
|
+
|
|
|
+ #checkers: List[TypeCheckerDeclaration] = list(find_type_checkers(self.allfiles, uppercase))
|
|
|
+ #for c in checkers:
|
|
|
+ # c.info("instance type checker declaration (%s) is here", c.group('uppercase'))
|
|
|
+ #if not checkers:
|
|
|
+ # self.info("No type checkers declared for %s", uppercase)
|
|
|
+ # if instancetype or classtype:
|
|
|
+ # self.warn("Can't find where type checkers for %s (%s) are declared. We will need them to validate sizes of %s",
|
|
|
+ # typename, uppercase, self.name)
|
|
|
+
|
|
|
+ if not instancetype:
|
|
|
+ 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:
|
|
|
# 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)
|
|
|
- return
|
|
|
- yield
|
|
|
+ replaced_fields = ['name', 'parent', 'instance_size', 'class_size']
|
|
|
+ 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):
|
|
|
"""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)
|
|
|
return
|
|
|
|
|
|
- instance_decl = find_type_declaration(self.allfiles, typename)
|
|
|
- if instance_decl:
|
|
|
+ instance_decls = find_type_checkers(self.allfiles, typename)
|
|
|
+ if instance_decls:
|
|
|
self.debug("won't touch TypeInfo var that has type checkers")
|
|
|
return
|
|
|
|
|
@@ -240,12 +741,12 @@ def gen_patches(self) -> Iterable[Patch]:
|
|
|
self.warn("Can't find TypeInfo for %s", parent_typename)
|
|
|
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")
|
|
|
parent.info("parent type declared here")
|
|
|
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")
|
|
|
parent.info("parent type declared here")
|
|
|
return
|
|
@@ -303,10 +804,11 @@ def gen_patches(self) -> Iterable[Patch]:
|
|
|
# yield self.append_field('class_init', ids.lowercase+'_class_init')
|
|
|
|
|
|
class TypeInitMacro(FileMatch):
|
|
|
- """type_init(...) macro use
|
|
|
- Will be deleted if function is empty
|
|
|
- """
|
|
|
+ """Use of type_init(...) macro"""
|
|
|
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]:
|
|
|
fn = self.file.find_match(StaticVoidFunction, self.name)
|
|
|
DBG("function for %s: %s", self.name, fn)
|
|
@@ -331,7 +833,7 @@ class StaticVoidFunction(FileMatch):
|
|
|
r'#[^\n]*\n',
|
|
|
r'\n',
|
|
|
repeat='*')),
|
|
|
- r'}\n')
|
|
|
+ r'};?\n')
|
|
|
|
|
|
@property
|
|
|
def body(self) -> str:
|
|
@@ -340,34 +842,40 @@ def body(self) -> str:
|
|
|
def has_preprocessor_directive(self) -> bool:
|
|
|
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
|
|
|
Will be replaced by TYPE_INFO() macro
|
|
|
"""
|
|
|
- regexp = S(r'^[ \t]*type_register_static\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
|
|
|
+ regexp = S(r'^[ \t]*', NAMED('func_name', 'type_register_static'),
|
|
|
+ r'\s*\(&\s*', NAMED('name', RE_IDENTIFIER), r'\s*\);[ \t]*\n')
|
|
|
|
|
|
+class UseTypeInfo(TypeRegisterStaticCall):
|
|
|
+ """Replace type_register_static() call with TYPE_INFO declaration"""
|
|
|
def gen_patches(self) -> Iterable[Patch]:
|
|
|
- fn = self.function()
|
|
|
- if fn is None:
|
|
|
- self.warn("can't find function where type_register_static(&%s) is called", self.name)
|
|
|
- return
|
|
|
+ fn = find_containing_func(self)
|
|
|
+ if fn:
|
|
|
+ DBG("%r is inside %r", self, fn)
|
|
|
+ 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:
|
|
|
# self.warn("function %s has preprocessor directives, this requires --force", fn.name)
|
|
|
# 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)
|
|
|
if var is None:
|
|
|
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():
|
|
|
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)
|
|
|
- return
|
|
|
+ if not self.file.force:
|
|
|
+ return
|
|
|
|
|
|
# delete type_register_static() call:
|
|
|
yield self.make_patch('')
|
|
|
# append TYPE_REGISTER(...) after variable declaration:
|
|
|
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):
|
|
|
"""TYPE_INFO macro usage"""
|
|
|
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]:
|
|
|
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)
|
|
|
if len(ti) > 1:
|
|
|
DBG("multiple TypeInfo vars found for %s", name)
|