1from c_parser.info import ( 2 KIND, 3 TypeDeclaration, 4 POTSType, 5 FuncPtr, 6) 7from c_parser.match import ( 8 is_pots, 9 is_funcptr, 10) 11from .info import ( 12 IGNORED, 13 UNKNOWN, 14 SystemType, 15) 16from .match import ( 17 is_system_type, 18) 19 20 21def get_typespecs(typedecls): 22 typespecs = {} 23 for decl in typedecls: 24 if decl.shortkey not in typespecs: 25 typespecs[decl.shortkey] = [decl] 26 else: 27 typespecs[decl.shortkey].append(decl) 28 return typespecs 29 30 31def analyze_decl(decl, typespecs, knowntypespecs, types, knowntypes, *, 32 analyze_resolved=None): 33 resolved = resolve_decl(decl, typespecs, knowntypespecs, types) 34 if resolved is None: 35 # The decl is supposed to be skipped or ignored. 36 return None 37 if analyze_resolved is None: 38 return resolved, None 39 return analyze_resolved(resolved, decl, types, knowntypes) 40 41# This alias helps us avoid name collisions. 42_analyze_decl = analyze_decl 43 44 45def analyze_type_decls(types, analyze_decl, handle_unresolved=True): 46 unresolved = set(types) 47 while unresolved: 48 updated = [] 49 for decl in unresolved: 50 resolved = analyze_decl(decl) 51 if resolved is None: 52 # The decl should be skipped or ignored. 53 types[decl] = IGNORED 54 updated.append(decl) 55 continue 56 typedeps, _ = resolved 57 if typedeps is None: 58 raise NotImplementedError(decl) 59 if UNKNOWN in typedeps: 60 # At least one dependency is unknown, so this decl 61 # is not resolvable. 62 types[decl] = UNKNOWN 63 updated.append(decl) 64 continue 65 if None in typedeps: 66 # XXX 67 # Handle direct recursive types first. 68 nonrecursive = 1 69 if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: 70 nonrecursive = 0 71 i = 0 72 for member, dep in zip(decl.members, typedeps): 73 if dep is None: 74 if member.vartype.typespec != decl.shortkey: 75 nonrecursive += 1 76 else: 77 typedeps[i] = decl 78 i += 1 79 if nonrecursive: 80 # We don't have all dependencies resolved yet. 81 continue 82 types[decl] = resolved 83 updated.append(decl) 84 if updated: 85 for decl in updated: 86 unresolved.remove(decl) 87 else: 88 # XXX 89 # Handle indirect recursive types. 90 ... 91 # We couldn't resolve the rest. 92 # Let the caller deal with it! 93 break 94 if unresolved and handle_unresolved: 95 if handle_unresolved is True: 96 handle_unresolved = _handle_unresolved 97 handle_unresolved(unresolved, types, analyze_decl) 98 99 100def resolve_decl(decl, typespecs, knowntypespecs, types): 101 if decl.kind is KIND.ENUM: 102 typedeps = [] 103 else: 104 if decl.kind is KIND.VARIABLE: 105 vartypes = [decl.vartype] 106 elif decl.kind is KIND.FUNCTION: 107 vartypes = [decl.signature.returntype] 108 elif decl.kind is KIND.TYPEDEF: 109 vartypes = [decl.vartype] 110 elif decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: 111 vartypes = [m.vartype for m in decl.members] 112 else: 113 # Skip this one! 114 return None 115 116 typedeps = [] 117 for vartype in vartypes: 118 typespec = vartype.typespec 119 if is_pots(typespec): 120 typedecl = POTSType(typespec) 121 elif is_system_type(typespec): 122 typedecl = SystemType(typespec) 123 elif is_funcptr(vartype): 124 typedecl = FuncPtr(vartype) 125 else: 126 typedecl = find_typedecl(decl, typespec, typespecs) 127 if typedecl is None: 128 typedecl = find_typedecl(decl, typespec, knowntypespecs) 129 elif not isinstance(typedecl, TypeDeclaration): 130 raise NotImplementedError(repr(typedecl)) 131 if typedecl is None: 132 # We couldn't find it! 133 typedecl = UNKNOWN 134 elif typedecl not in types: 135 # XXX How can this happen? 136 typedecl = UNKNOWN 137 elif types[typedecl] is UNKNOWN: 138 typedecl = UNKNOWN 139 elif types[typedecl] is IGNORED: 140 # We don't care if it didn't resolve. 141 pass 142 elif types[typedecl] is None: 143 # The typedecl for the typespec hasn't been resolved yet. 144 typedecl = None 145 typedeps.append(typedecl) 146 return typedeps 147 148 149def find_typedecl(decl, typespec, typespecs): 150 specdecls = typespecs.get(typespec) 151 if not specdecls: 152 return None 153 154 filename = decl.filename 155 156 if len(specdecls) == 1: 157 typedecl, = specdecls 158 if '-' in typespec and typedecl.filename != filename: 159 # Inlined types are always in the same file. 160 return None 161 return typedecl 162 163 # Decide which one to return. 164 candidates = [] 165 samefile = None 166 for typedecl in specdecls: 167 type_filename = typedecl.filename 168 if type_filename == filename: 169 if samefile is not None: 170 # We expect type names to be unique in a file. 171 raise NotImplementedError((decl, samefile, typedecl)) 172 samefile = typedecl 173 elif filename.endswith('.c') and not type_filename.endswith('.h'): 174 # If the decl is in a source file then we expect the 175 # type to be in the same file or in a header file. 176 continue 177 candidates.append(typedecl) 178 if not candidates: 179 return None 180 elif len(candidates) == 1: 181 winner, = candidates 182 # XXX Check for inline? 183 elif '-' in typespec: 184 # Inlined types are always in the same file. 185 winner = samefile 186 elif samefile is not None: 187 # Favor types in the same file. 188 winner = samefile 189 else: 190 # We don't know which to return. 191 raise NotImplementedError((decl, candidates)) 192 193 return winner 194 195 196############################# 197# handling unresolved decls 198 199class Skipped(TypeDeclaration): 200 def __init__(self): 201 _file = _name = _data = _parent = None 202 super().__init__(_file, _name, _data, _parent, _shortkey='<skipped>') 203_SKIPPED = Skipped() 204del Skipped 205 206 207def _handle_unresolved(unresolved, types, analyze_decl): 208 #raise NotImplementedError(unresolved) 209 210 dump = True 211 dump = False 212 if dump: 213 print() 214 for decl in types: # Preserve the original order. 215 if decl not in unresolved: 216 assert types[decl] is not None, decl 217 if types[decl] in (UNKNOWN, IGNORED): 218 unresolved.add(decl) 219 if dump: 220 _dump_unresolved(decl, types, analyze_decl) 221 print() 222 else: 223 assert types[decl][0] is not None, (decl, types[decl]) 224 assert None not in types[decl][0], (decl, types[decl]) 225 else: 226 assert types[decl] is None 227 if dump: 228 _dump_unresolved(decl, types, analyze_decl) 229 print() 230 #raise NotImplementedError 231 232 for decl in unresolved: 233 types[decl] = ([_SKIPPED], None) 234 235 for decl in types: 236 assert types[decl] 237 238 239def _dump_unresolved(decl, types, analyze_decl): 240 if isinstance(decl, str): 241 typespec = decl 242 decl, = (d for d in types if d.shortkey == typespec) 243 elif type(decl) is tuple: 244 filename, typespec = decl 245 if '-' in typespec: 246 found = [d for d in types 247 if d.shortkey == typespec and d.filename == filename] 248 #if not found: 249 # raise NotImplementedError(decl) 250 decl, = found 251 else: 252 found = [d for d in types if d.shortkey == typespec] 253 if not found: 254 print(f'*** {typespec} ???') 255 return 256 #raise NotImplementedError(decl) 257 else: 258 decl, = found 259 resolved = analyze_decl(decl) 260 if resolved: 261 typedeps, _ = resolved or (None, None) 262 263 if decl.kind is KIND.STRUCT or decl.kind is KIND.UNION: 264 print(f'*** {decl.shortkey} {decl.filename}') 265 for member, mtype in zip(decl.members, typedeps): 266 typespec = member.vartype.typespec 267 if typespec == decl.shortkey: 268 print(f' ~~~~: {typespec:20} - {member!r}') 269 continue 270 status = None 271 if is_pots(typespec): 272 mtype = typespec 273 status = 'okay' 274 elif is_system_type(typespec): 275 mtype = typespec 276 status = 'okay' 277 elif mtype is None: 278 if '-' in member.vartype.typespec: 279 mtype, = [d for d in types 280 if d.shortkey == member.vartype.typespec 281 and d.filename == decl.filename] 282 else: 283 found = [d for d in types 284 if d.shortkey == typespec] 285 if not found: 286 print(f' ???: {typespec:20}') 287 continue 288 mtype, = found 289 if status is None: 290 status = 'okay' if types.get(mtype) else 'oops' 291 if mtype is _SKIPPED: 292 status = 'okay' 293 mtype = '<skipped>' 294 elif isinstance(mtype, FuncPtr): 295 status = 'okay' 296 mtype = str(mtype.vartype) 297 elif not isinstance(mtype, str): 298 if hasattr(mtype, 'vartype'): 299 if is_funcptr(mtype.vartype): 300 status = 'okay' 301 mtype = str(mtype).rpartition('(')[0].rstrip() 302 status = ' okay' if status == 'okay' else f'--> {status}' 303 print(f' {status}: {typespec:20} - {member!r} ({mtype})') 304 else: 305 print(f'*** {decl} ({decl.vartype!r})') 306 if decl.vartype.typespec.startswith('struct ') or is_funcptr(decl): 307 _dump_unresolved( 308 (decl.filename, decl.vartype.typespec), 309 types, 310 analyze_decl, 311 ) 312