1 from c_parser.info import (
2 KIND,
3 TypeDeclaration,
4 POTSType,
5 FuncPtr,
6 )
7 from c_parser.match import (
8 is_pots,
9 is_funcptr,
10 )
11 from .info import (
12 IGNORED,
13 UNKNOWN,
14 SystemType,
15 )
16 from .match import (
17 is_system_type,
18 )
19
20
21 def 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
31 def 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
45 def 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
100 def 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
149 def 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
199 class ESC[4;38;5;81mSkipped(ESC[4;38;5;149mTypeDeclaration):
200 def __init__(self):
201 _file = _name = _data = _parent = None
202 super().__init__(_file, _name, _data, _parent, _shortkey='<skipped>')
203 _SKIPPED = Skipped()
204 del Skipped
205
206
207 def _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
239 def _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 )