Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
allendowney
GitHub Repository: allendowney/cpython
Path: blob/main/Tools/c-analyzer/c_analyzer/info.py
12 views
1
import os.path
2
3
from c_common import fsutil
4
from c_common.clsutil import classonly
5
import c_common.misc as _misc
6
from c_parser.info import (
7
KIND,
8
HighlevelParsedItem,
9
Declaration,
10
TypeDeclaration,
11
)
12
from c_parser.match import (
13
is_type_decl,
14
)
15
16
17
IGNORED = _misc.Labeled('IGNORED')
18
UNKNOWN = _misc.Labeled('UNKNOWN')
19
20
21
class SystemType(TypeDeclaration):
22
23
def __init__(self, name):
24
super().__init__(None, name, None, None, _shortkey=name)
25
26
27
class Analyzed:
28
_locked = False
29
30
@classonly
31
def is_target(cls, raw):
32
if isinstance(raw, HighlevelParsedItem):
33
return True
34
else:
35
return False
36
37
@classonly
38
def from_raw(cls, raw, **extra):
39
if isinstance(raw, cls):
40
if extra:
41
# XXX ?
42
raise NotImplementedError((raw, extra))
43
#return cls(raw.item, raw.typedecl, **raw._extra, **extra)
44
else:
45
return info
46
elif cls.is_target(raw):
47
return cls(raw, **extra)
48
else:
49
raise NotImplementedError((raw, extra))
50
51
@classonly
52
def from_resolved(cls, item, resolved, **extra):
53
if isinstance(resolved, TypeDeclaration):
54
return cls(item, typedecl=resolved, **extra)
55
else:
56
typedeps, extra = cls._parse_raw_resolved(item, resolved, extra)
57
if item.kind is KIND.ENUM:
58
if typedeps:
59
raise NotImplementedError((item, resolved, extra))
60
elif not typedeps:
61
raise NotImplementedError((item, resolved, extra))
62
return cls(item, typedeps, **extra or {})
63
64
@classonly
65
def _parse_raw_resolved(cls, item, resolved, extra_extra):
66
if resolved in (UNKNOWN, IGNORED):
67
return resolved, None
68
try:
69
typedeps, extra = resolved
70
except (TypeError, ValueError):
71
typedeps = extra = None
72
if extra:
73
# The resolved data takes precedence.
74
extra = dict(extra_extra, **extra)
75
if isinstance(typedeps, TypeDeclaration):
76
return typedeps, extra
77
elif typedeps in (None, UNKNOWN):
78
# It is still effectively unresolved.
79
return UNKNOWN, extra
80
elif None in typedeps or UNKNOWN in typedeps:
81
# It is still effectively unresolved.
82
return typedeps, extra
83
elif any(not isinstance(td, TypeDeclaration) for td in typedeps):
84
raise NotImplementedError((item, typedeps, extra))
85
return typedeps, extra
86
87
def __init__(self, item, typedecl=None, **extra):
88
assert item is not None
89
self.item = item
90
if typedecl in (UNKNOWN, IGNORED):
91
pass
92
elif item.kind is KIND.STRUCT or item.kind is KIND.UNION:
93
if isinstance(typedecl, TypeDeclaration):
94
raise NotImplementedError(item, typedecl)
95
elif typedecl is None:
96
typedecl = UNKNOWN
97
else:
98
typedecl = [UNKNOWN if d is None else d for d in typedecl]
99
elif typedecl is None:
100
typedecl = UNKNOWN
101
elif typedecl and not isinstance(typedecl, TypeDeclaration):
102
# All the other decls have a single type decl.
103
typedecl, = typedecl
104
if typedecl is None:
105
typedecl = UNKNOWN
106
self.typedecl = typedecl
107
self._extra = extra
108
self._locked = True
109
110
self._validate()
111
112
def _validate(self):
113
item = self.item
114
extra = self._extra
115
# Check item.
116
if not isinstance(item, HighlevelParsedItem):
117
raise ValueError(f'"item" must be a high-level parsed item, got {item!r}')
118
# Check extra.
119
for key, value in extra.items():
120
if key.startswith('_'):
121
raise ValueError(f'extra items starting with {"_"!r} not allowed, got {extra!r}')
122
if hasattr(item, key) and not callable(getattr(item, key)):
123
raise ValueError(f'extra cannot override item, got {value!r} for key {key!r}')
124
125
def __repr__(self):
126
kwargs = [
127
f'item={self.item!r}',
128
f'typedecl={self.typedecl!r}',
129
*(f'{k}={v!r}' for k, v in self._extra.items())
130
]
131
return f'{type(self).__name__}({", ".join(kwargs)})'
132
133
def __str__(self):
134
try:
135
return self._str
136
except AttributeError:
137
self._str, = self.render('line')
138
return self._str
139
140
def __hash__(self):
141
return hash(self.item)
142
143
def __eq__(self, other):
144
if isinstance(other, Analyzed):
145
return self.item == other.item
146
elif isinstance(other, HighlevelParsedItem):
147
return self.item == other
148
elif type(other) is tuple:
149
return self.item == other
150
else:
151
return NotImplemented
152
153
def __gt__(self, other):
154
if isinstance(other, Analyzed):
155
return self.item > other.item
156
elif isinstance(other, HighlevelParsedItem):
157
return self.item > other
158
elif type(other) is tuple:
159
return self.item > other
160
else:
161
return NotImplemented
162
163
def __dir__(self):
164
names = set(super().__dir__())
165
names.update(self._extra)
166
names.remove('_locked')
167
return sorted(names)
168
169
def __getattr__(self, name):
170
if name.startswith('_'):
171
raise AttributeError(name)
172
# The item takes precedence over the extra data (except if callable).
173
try:
174
value = getattr(self.item, name)
175
if callable(value):
176
raise AttributeError(name)
177
except AttributeError:
178
try:
179
value = self._extra[name]
180
except KeyError:
181
pass
182
else:
183
# Speed things up the next time.
184
self.__dict__[name] = value
185
return value
186
raise # re-raise
187
else:
188
return value
189
190
def __setattr__(self, name, value):
191
if self._locked and name != '_str':
192
raise AttributeError(f'readonly ({name})')
193
super().__setattr__(name, value)
194
195
def __delattr__(self, name):
196
if self._locked:
197
raise AttributeError(f'readonly ({name})')
198
super().__delattr__(name)
199
200
@property
201
def decl(self):
202
if not isinstance(self.item, Declaration):
203
raise AttributeError('decl')
204
return self.item
205
206
@property
207
def signature(self):
208
# XXX vartype...
209
...
210
211
@property
212
def istype(self):
213
return is_type_decl(self.item.kind)
214
215
@property
216
def is_known(self):
217
if self.typedecl in (UNKNOWN, IGNORED):
218
return False
219
elif isinstance(self.typedecl, TypeDeclaration):
220
return True
221
else:
222
return UNKNOWN not in self.typedecl
223
224
def fix_filename(self, relroot=fsutil.USE_CWD, **kwargs):
225
self.item.fix_filename(relroot, **kwargs)
226
return self
227
228
def as_rowdata(self, columns=None):
229
# XXX finish!
230
return self.item.as_rowdata(columns)
231
232
def render_rowdata(self, columns=None):
233
# XXX finish!
234
return self.item.render_rowdata(columns)
235
236
def render(self, fmt='line', *, itemonly=False):
237
if fmt == 'raw':
238
yield repr(self)
239
return
240
rendered = self.item.render(fmt)
241
if itemonly or not self._extra:
242
yield from rendered
243
return
244
extra = self._render_extra(fmt)
245
if not extra:
246
yield from rendered
247
elif fmt in ('brief', 'line'):
248
rendered, = rendered
249
extra, = extra
250
yield f'{rendered}\t{extra}'
251
elif fmt == 'summary':
252
raise NotImplementedError(fmt)
253
elif fmt == 'full':
254
yield from rendered
255
for line in extra:
256
yield f'\t{line}'
257
else:
258
raise NotImplementedError(fmt)
259
260
def _render_extra(self, fmt):
261
if fmt in ('brief', 'line'):
262
yield str(self._extra)
263
else:
264
raise NotImplementedError(fmt)
265
266
267
class Analysis:
268
269
_item_class = Analyzed
270
271
@classonly
272
def build_item(cls, info, resolved=None, **extra):
273
if resolved is None:
274
return cls._item_class.from_raw(info, **extra)
275
else:
276
return cls._item_class.from_resolved(info, resolved, **extra)
277
278
@classmethod
279
def from_results(cls, results):
280
self = cls()
281
for info, resolved in results:
282
self._add_result(info, resolved)
283
return self
284
285
def __init__(self, items=None):
286
self._analyzed = {type(self).build_item(item): None
287
for item in items or ()}
288
289
def __repr__(self):
290
return f'{type(self).__name__}({list(self._analyzed.keys())})'
291
292
def __iter__(self):
293
#yield from self.types
294
#yield from self.functions
295
#yield from self.variables
296
yield from self._analyzed
297
298
def __len__(self):
299
return len(self._analyzed)
300
301
def __getitem__(self, key):
302
if type(key) is int:
303
for i, val in enumerate(self._analyzed):
304
if i == key:
305
return val
306
else:
307
raise IndexError(key)
308
else:
309
return self._analyzed[key]
310
311
def fix_filenames(self, relroot=fsutil.USE_CWD, **kwargs):
312
if relroot and relroot is not fsutil.USE_CWD:
313
relroot = os.path.abspath(relroot)
314
for item in self._analyzed:
315
item.fix_filename(relroot, fixroot=False, **kwargs)
316
317
def _add_result(self, info, resolved):
318
analyzed = type(self).build_item(info, resolved)
319
self._analyzed[analyzed] = None
320
return analyzed
321
322