| from c_parser.info import ( |
| KIND, |
| TypeDeclaration, |
| POTSType, |
| FuncPtr, |
| ) |
| from c_parser.match import ( |
| is_pots, |
| is_funcptr, |
| ) |
| from .info import ( |
| IGNORED, |
| UNKNOWN, |
| SystemType, |
| ) |
| from .match import ( |
| is_system_type, |
| ) |
| |
| |
| 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, |
| ) |