| 
									
										
										
										
											2020-10-22 18:42:51 -06:00
										 |  |  | from c_parser.info import ( | 
					
						
							|  |  |  |     KIND, | 
					
						
							|  |  |  |     TypeDeclaration, | 
					
						
							|  |  |  |     POTSType, | 
					
						
							|  |  |  |     FuncPtr, | 
					
						
							| 
									
										
										
										
											2020-10-30 15:46:52 -06:00
										 |  |  | ) | 
					
						
							|  |  |  | from c_parser.match import ( | 
					
						
							| 
									
										
										
										
											2020-10-22 18:42:51 -06:00
										 |  |  |     is_pots, | 
					
						
							|  |  |  |     is_funcptr, | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | from .info import ( | 
					
						
							|  |  |  |     IGNORED, | 
					
						
							|  |  |  |     UNKNOWN, | 
					
						
							|  |  |  |     SystemType, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2020-10-30 15:46:52 -06:00
										 |  |  | from .match import ( | 
					
						
							|  |  |  |     is_system_type, | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2020-10-22 18:42:51 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_typespecs(typedecls): | 
					
						
							|  |  |  |     typespecs = {} | 
					
						
							|  |  |  |     for decl in typedecls: | 
					
						
							|  |  |  |         if decl.shortkey not in typespecs: | 
					
						
							|  |  |  |             typespecs[decl.shortkey] = [decl] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             typespecs[decl.shortkey].append(decl) | 
					
						
							|  |  |  |     return typespecs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def analyze_decl(decl, typespecs, knowntypespecs, types, knowntypes, *, | 
					
						
							|  |  |  |                  analyze_resolved=None): | 
					
						
							|  |  |  |     resolved = resolve_decl(decl, typespecs, knowntypespecs, types) | 
					
						
							|  |  |  |     if resolved is None: | 
					
						
							|  |  |  |         # The decl is supposed to be skipped or ignored. | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     if analyze_resolved is None: | 
					
						
							|  |  |  |         return resolved, None | 
					
						
							|  |  |  |     return analyze_resolved(resolved, decl, types, knowntypes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # This alias helps us avoid name collisions. | 
					
						
							|  |  |  | _analyze_decl = analyze_decl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def analyze_type_decls(types, analyze_decl, handle_unresolved=True): | 
					
						
							|  |  |  |     unresolved = set(types) | 
					
						
							|  |  |  |     while unresolved: | 
					
						
							|  |  |  |         updated = [] | 
					
						
							|  |  |  |         for decl in unresolved: | 
					
						
							|  |  |  |             resolved = analyze_decl(decl) | 
					
						
							|  |  |  |             if resolved is None: | 
					
						
							|  |  |  |                 # The decl should be skipped or ignored. | 
					
						
							|  |  |  |                 types[decl] = IGNORED | 
					
						
							|  |  |  |                 updated.append(decl) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             typedeps, _ = resolved | 
					
						
							|  |  |  |             if typedeps is None: | 
					
						
							|  |  |  |                 raise NotImplementedError(decl) | 
					
						
							|  |  |  |             if UNKNOWN in typedeps: | 
					
						
							|  |  |  |                 # At least one dependency is unknown, so this decl | 
					
						
							|  |  |  |                 # is not resolvable. | 
					
						
							|  |  |  |                 types[decl] = UNKNOWN | 
					
						
							|  |  |  |                 updated.append(decl) | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             if None in typedeps: | 
					
						
							|  |  |  |                 # XXX | 
					
						
							|  |  |  |                 # Handle direct recursive types first. | 
					
						
							|  |  |  |                 nonrecursive = 1 | 
					
						
							|  |  |  |                 if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: | 
					
						
							|  |  |  |                     nonrecursive = 0 | 
					
						
							|  |  |  |                     i = 0 | 
					
						
							|  |  |  |                     for member, dep in zip(decl.members, typedeps): | 
					
						
							|  |  |  |                         if dep is None: | 
					
						
							|  |  |  |                             if member.vartype.typespec != decl.shortkey: | 
					
						
							|  |  |  |                                 nonrecursive += 1 | 
					
						
							|  |  |  |                             else: | 
					
						
							|  |  |  |                                 typedeps[i] = decl | 
					
						
							|  |  |  |                         i += 1 | 
					
						
							|  |  |  |                 if nonrecursive: | 
					
						
							|  |  |  |                     # We don't have all dependencies resolved yet. | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |             types[decl] = resolved | 
					
						
							|  |  |  |             updated.append(decl) | 
					
						
							|  |  |  |         if updated: | 
					
						
							|  |  |  |             for decl in updated: | 
					
						
							|  |  |  |                 unresolved.remove(decl) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # XXX | 
					
						
							|  |  |  |             # Handle indirect recursive types. | 
					
						
							|  |  |  |             ... | 
					
						
							|  |  |  |             # We couldn't resolve the rest. | 
					
						
							|  |  |  |             # Let the caller deal with it! | 
					
						
							|  |  |  |             break | 
					
						
							|  |  |  |     if unresolved and handle_unresolved: | 
					
						
							|  |  |  |         if handle_unresolved is True: | 
					
						
							|  |  |  |             handle_unresolved = _handle_unresolved | 
					
						
							|  |  |  |         handle_unresolved(unresolved, types, analyze_decl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def resolve_decl(decl, typespecs, knowntypespecs, types): | 
					
						
							|  |  |  |     if decl.kind is KIND.ENUM: | 
					
						
							|  |  |  |         typedeps = [] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if decl.kind is KIND.VARIABLE: | 
					
						
							|  |  |  |             vartypes = [decl.vartype] | 
					
						
							|  |  |  |         elif decl.kind is KIND.FUNCTION: | 
					
						
							|  |  |  |             vartypes = [decl.signature.returntype] | 
					
						
							|  |  |  |         elif decl.kind is KIND.TYPEDEF: | 
					
						
							|  |  |  |             vartypes = [decl.vartype] | 
					
						
							|  |  |  |         elif decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: | 
					
						
							|  |  |  |             vartypes = [m.vartype for m in decl.members] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # Skip this one! | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         typedeps = [] | 
					
						
							|  |  |  |         for vartype in vartypes: | 
					
						
							|  |  |  |             typespec = vartype.typespec | 
					
						
							|  |  |  |             if is_pots(typespec): | 
					
						
							|  |  |  |                 typedecl = POTSType(typespec) | 
					
						
							|  |  |  |             elif is_system_type(typespec): | 
					
						
							|  |  |  |                 typedecl = SystemType(typespec) | 
					
						
							|  |  |  |             elif is_funcptr(vartype): | 
					
						
							|  |  |  |                 typedecl = FuncPtr(vartype) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 typedecl = find_typedecl(decl, typespec, typespecs) | 
					
						
							|  |  |  |                 if typedecl is None: | 
					
						
							|  |  |  |                     typedecl = find_typedecl(decl, typespec, knowntypespecs) | 
					
						
							|  |  |  |                 elif not isinstance(typedecl, TypeDeclaration): | 
					
						
							|  |  |  |                     raise NotImplementedError(repr(typedecl)) | 
					
						
							|  |  |  |                 if typedecl is None: | 
					
						
							|  |  |  |                     # We couldn't find it! | 
					
						
							|  |  |  |                     typedecl = UNKNOWN | 
					
						
							|  |  |  |                 elif typedecl not in types: | 
					
						
							|  |  |  |                     # XXX How can this happen? | 
					
						
							|  |  |  |                     typedecl = UNKNOWN | 
					
						
							|  |  |  |                 elif types[typedecl] is UNKNOWN: | 
					
						
							|  |  |  |                     typedecl = UNKNOWN | 
					
						
							|  |  |  |                 elif types[typedecl] is IGNORED: | 
					
						
							|  |  |  |                     # We don't care if it didn't resolve. | 
					
						
							|  |  |  |                     pass | 
					
						
							|  |  |  |                 elif types[typedecl] is None: | 
					
						
							|  |  |  |                     # The typedecl for the typespec hasn't been resolved yet. | 
					
						
							|  |  |  |                     typedecl = None | 
					
						
							|  |  |  |             typedeps.append(typedecl) | 
					
						
							|  |  |  |     return typedeps | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def find_typedecl(decl, typespec, typespecs): | 
					
						
							|  |  |  |     specdecls = typespecs.get(typespec) | 
					
						
							|  |  |  |     if not specdecls: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     filename = decl.filename | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(specdecls) == 1: | 
					
						
							|  |  |  |         typedecl, = specdecls | 
					
						
							|  |  |  |         if '-' in typespec and typedecl.filename != filename: | 
					
						
							|  |  |  |             # Inlined types are always in the same file. | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         return typedecl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Decide which one to return. | 
					
						
							|  |  |  |     candidates = [] | 
					
						
							|  |  |  |     samefile = None | 
					
						
							|  |  |  |     for typedecl in specdecls: | 
					
						
							|  |  |  |         type_filename = typedecl.filename | 
					
						
							|  |  |  |         if type_filename == filename: | 
					
						
							|  |  |  |             if samefile is not None: | 
					
						
							|  |  |  |                 # We expect type names to be unique in a file. | 
					
						
							|  |  |  |                 raise NotImplementedError((decl, samefile, typedecl)) | 
					
						
							|  |  |  |             samefile = typedecl | 
					
						
							|  |  |  |         elif filename.endswith('.c') and not type_filename.endswith('.h'): | 
					
						
							|  |  |  |             # If the decl is in a source file then we expect the | 
					
						
							|  |  |  |             # type to be in the same file or in a header file. | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         candidates.append(typedecl) | 
					
						
							|  |  |  |     if not candidates: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     elif len(candidates) == 1: | 
					
						
							|  |  |  |         winner, = candidates | 
					
						
							|  |  |  |         # XXX Check for inline? | 
					
						
							|  |  |  |     elif '-' in typespec: | 
					
						
							|  |  |  |         # Inlined types are always in the same file. | 
					
						
							|  |  |  |         winner = samefile | 
					
						
							|  |  |  |     elif samefile is not None: | 
					
						
							|  |  |  |         # Favor types in the same file. | 
					
						
							|  |  |  |         winner = samefile | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # We don't know which to return. | 
					
						
							|  |  |  |         raise NotImplementedError((decl, candidates)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return winner | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ############################# | 
					
						
							|  |  |  | # handling unresolved decls | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Skipped(TypeDeclaration): | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         _file = _name = _data = _parent = None | 
					
						
							|  |  |  |         super().__init__(_file, _name, _data, _parent, _shortkey='<skipped>') | 
					
						
							|  |  |  | _SKIPPED = Skipped() | 
					
						
							|  |  |  | del Skipped | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _handle_unresolved(unresolved, types, analyze_decl): | 
					
						
							|  |  |  |     #raise NotImplementedError(unresolved) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dump = True | 
					
						
							|  |  |  |     dump = False | 
					
						
							|  |  |  |     if dump: | 
					
						
							|  |  |  |         print() | 
					
						
							|  |  |  |     for decl in types:  # Preserve the original order. | 
					
						
							|  |  |  |         if decl not in unresolved: | 
					
						
							|  |  |  |             assert types[decl] is not None, decl | 
					
						
							|  |  |  |             if types[decl] in (UNKNOWN, IGNORED): | 
					
						
							|  |  |  |                 unresolved.add(decl) | 
					
						
							|  |  |  |                 if dump: | 
					
						
							|  |  |  |                     _dump_unresolved(decl, types, analyze_decl) | 
					
						
							|  |  |  |                     print() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 assert types[decl][0] is not None, (decl, types[decl]) | 
					
						
							|  |  |  |                 assert None not in types[decl][0], (decl, types[decl]) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             assert types[decl] is None | 
					
						
							|  |  |  |             if dump: | 
					
						
							|  |  |  |                 _dump_unresolved(decl, types, analyze_decl) | 
					
						
							|  |  |  |                 print() | 
					
						
							|  |  |  |     #raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for decl in unresolved: | 
					
						
							|  |  |  |         types[decl] = ([_SKIPPED], None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for decl in types: | 
					
						
							|  |  |  |         assert types[decl] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _dump_unresolved(decl, types, analyze_decl): | 
					
						
							|  |  |  |     if isinstance(decl, str): | 
					
						
							|  |  |  |         typespec = decl | 
					
						
							|  |  |  |         decl, = (d for d in types if d.shortkey == typespec) | 
					
						
							|  |  |  |     elif type(decl) is tuple: | 
					
						
							|  |  |  |         filename, typespec = decl | 
					
						
							|  |  |  |         if '-' in typespec: | 
					
						
							|  |  |  |             found = [d for d in types | 
					
						
							|  |  |  |                      if d.shortkey == typespec and d.filename == filename] | 
					
						
							|  |  |  |             #if not found: | 
					
						
							|  |  |  |             #    raise NotImplementedError(decl) | 
					
						
							|  |  |  |             decl, = found | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             found = [d for d in types if d.shortkey == typespec] | 
					
						
							|  |  |  |             if not found: | 
					
						
							|  |  |  |                 print(f'*** {typespec} ???') | 
					
						
							|  |  |  |                 return | 
					
						
							|  |  |  |                 #raise NotImplementedError(decl) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 decl, = found | 
					
						
							|  |  |  |     resolved = analyze_decl(decl) | 
					
						
							|  |  |  |     if resolved: | 
					
						
							|  |  |  |         typedeps, _ = resolved or (None, None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: | 
					
						
							|  |  |  |         print(f'*** {decl.shortkey} {decl.filename}') | 
					
						
							|  |  |  |         for member, mtype in zip(decl.members, typedeps): | 
					
						
							|  |  |  |             typespec = member.vartype.typespec | 
					
						
							|  |  |  |             if typespec == decl.shortkey: | 
					
						
							|  |  |  |                 print(f'     ~~~~: {typespec:20} - {member!r}') | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             status = None | 
					
						
							|  |  |  |             if is_pots(typespec): | 
					
						
							|  |  |  |                 mtype = typespec | 
					
						
							|  |  |  |                 status = 'okay' | 
					
						
							|  |  |  |             elif is_system_type(typespec): | 
					
						
							|  |  |  |                 mtype = typespec | 
					
						
							|  |  |  |                 status = 'okay' | 
					
						
							|  |  |  |             elif mtype is None: | 
					
						
							|  |  |  |                 if '-' in member.vartype.typespec: | 
					
						
							|  |  |  |                     mtype, = [d for d in types | 
					
						
							|  |  |  |                               if d.shortkey == member.vartype.typespec | 
					
						
							|  |  |  |                               and d.filename == decl.filename] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     found = [d for d in types | 
					
						
							|  |  |  |                              if d.shortkey == typespec] | 
					
						
							|  |  |  |                     if not found: | 
					
						
							|  |  |  |                         print(f' ???: {typespec:20}') | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     mtype, = found | 
					
						
							|  |  |  |             if status is None: | 
					
						
							|  |  |  |                 status = 'okay' if types.get(mtype) else 'oops' | 
					
						
							|  |  |  |             if mtype is _SKIPPED: | 
					
						
							|  |  |  |                 status = 'okay' | 
					
						
							|  |  |  |                 mtype = '<skipped>' | 
					
						
							|  |  |  |             elif isinstance(mtype, FuncPtr): | 
					
						
							|  |  |  |                 status = 'okay' | 
					
						
							|  |  |  |                 mtype = str(mtype.vartype) | 
					
						
							|  |  |  |             elif not isinstance(mtype, str): | 
					
						
							|  |  |  |                 if hasattr(mtype, 'vartype'): | 
					
						
							|  |  |  |                     if is_funcptr(mtype.vartype): | 
					
						
							|  |  |  |                         status = 'okay' | 
					
						
							|  |  |  |                 mtype = str(mtype).rpartition('(')[0].rstrip() | 
					
						
							|  |  |  |             status = '    okay' if status == 'okay' else f'--> {status}' | 
					
						
							|  |  |  |             print(f' {status}: {typespec:20} - {member!r} ({mtype})') | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         print(f'*** {decl} ({decl.vartype!r})') | 
					
						
							|  |  |  |         if decl.vartype.typespec.startswith('struct ') or is_funcptr(decl): | 
					
						
							|  |  |  |             _dump_unresolved( | 
					
						
							|  |  |  |                 (decl.filename, decl.vartype.typespec), | 
					
						
							|  |  |  |                 types, | 
					
						
							|  |  |  |                 analyze_decl, | 
					
						
							|  |  |  |             ) |