(root)/
Python-3.12.0/
Tools/
c-analyzer/
c_analyzer/
info.py
       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 ESC[4;38;5;81mSystemType(ESC[4;38;5;149mTypeDeclaration):
      22  
      23      def __init__(self, name):
      24          super().__init__(None, name, None, None, _shortkey=name)
      25  
      26  
      27  class ESC[4;38;5;81mAnalyzed:
      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 ESC[4;38;5;81mAnalysis:
     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