17db96d56Sopenharmony_cifrom collections import namedtuple
27db96d56Sopenharmony_ciimport os.path
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_cifrom c_common import fsutil
57db96d56Sopenharmony_cifrom c_common.clsutil import classonly
67db96d56Sopenharmony_ciimport c_common.misc as _misc
77db96d56Sopenharmony_cifrom c_parser.info import (
87db96d56Sopenharmony_ci    KIND,
97db96d56Sopenharmony_ci    HighlevelParsedItem,
107db96d56Sopenharmony_ci    Declaration,
117db96d56Sopenharmony_ci    TypeDeclaration,
127db96d56Sopenharmony_ci)
137db96d56Sopenharmony_cifrom c_parser.match import (
147db96d56Sopenharmony_ci    is_type_decl,
157db96d56Sopenharmony_ci)
167db96d56Sopenharmony_cifrom .match import (
177db96d56Sopenharmony_ci    is_process_global,
187db96d56Sopenharmony_ci)
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_ciIGNORED = _misc.Labeled('IGNORED')
227db96d56Sopenharmony_ciUNKNOWN = _misc.Labeled('UNKNOWN')
237db96d56Sopenharmony_ci
247db96d56Sopenharmony_ci
257db96d56Sopenharmony_ciclass SystemType(TypeDeclaration):
267db96d56Sopenharmony_ci
277db96d56Sopenharmony_ci    def __init__(self, name):
287db96d56Sopenharmony_ci        super().__init__(None, name, None, None, _shortkey=name)
297db96d56Sopenharmony_ci
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ciclass Analyzed:
327db96d56Sopenharmony_ci    _locked = False
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci    @classonly
357db96d56Sopenharmony_ci    def is_target(cls, raw):
367db96d56Sopenharmony_ci        if isinstance(raw, HighlevelParsedItem):
377db96d56Sopenharmony_ci            return True
387db96d56Sopenharmony_ci        else:
397db96d56Sopenharmony_ci            return False
407db96d56Sopenharmony_ci
417db96d56Sopenharmony_ci    @classonly
427db96d56Sopenharmony_ci    def from_raw(cls, raw, **extra):
437db96d56Sopenharmony_ci        if isinstance(raw, cls):
447db96d56Sopenharmony_ci            if extra:
457db96d56Sopenharmony_ci                # XXX ?
467db96d56Sopenharmony_ci                raise NotImplementedError((raw, extra))
477db96d56Sopenharmony_ci                #return cls(raw.item, raw.typedecl, **raw._extra, **extra)
487db96d56Sopenharmony_ci            else:
497db96d56Sopenharmony_ci                return info
507db96d56Sopenharmony_ci        elif cls.is_target(raw):
517db96d56Sopenharmony_ci            return cls(raw, **extra)
527db96d56Sopenharmony_ci        else:
537db96d56Sopenharmony_ci            raise NotImplementedError((raw, extra))
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ci    @classonly
567db96d56Sopenharmony_ci    def from_resolved(cls, item, resolved, **extra):
577db96d56Sopenharmony_ci        if isinstance(resolved, TypeDeclaration):
587db96d56Sopenharmony_ci            return cls(item, typedecl=resolved, **extra)
597db96d56Sopenharmony_ci        else:
607db96d56Sopenharmony_ci            typedeps, extra = cls._parse_raw_resolved(item, resolved, extra)
617db96d56Sopenharmony_ci            if item.kind is KIND.ENUM:
627db96d56Sopenharmony_ci                if typedeps:
637db96d56Sopenharmony_ci                    raise NotImplementedError((item, resolved, extra))
647db96d56Sopenharmony_ci            elif not typedeps:
657db96d56Sopenharmony_ci                raise NotImplementedError((item, resolved, extra))
667db96d56Sopenharmony_ci            return cls(item, typedeps, **extra or {})
677db96d56Sopenharmony_ci
687db96d56Sopenharmony_ci    @classonly
697db96d56Sopenharmony_ci    def _parse_raw_resolved(cls, item, resolved, extra_extra):
707db96d56Sopenharmony_ci        if resolved in (UNKNOWN, IGNORED):
717db96d56Sopenharmony_ci            return resolved, None
727db96d56Sopenharmony_ci        try:
737db96d56Sopenharmony_ci            typedeps, extra = resolved
747db96d56Sopenharmony_ci        except (TypeError, ValueError):
757db96d56Sopenharmony_ci            typedeps = extra = None
767db96d56Sopenharmony_ci        if extra:
777db96d56Sopenharmony_ci            # The resolved data takes precedence.
787db96d56Sopenharmony_ci            extra = dict(extra_extra, **extra)
797db96d56Sopenharmony_ci        if isinstance(typedeps, TypeDeclaration):
807db96d56Sopenharmony_ci            return typedeps, extra
817db96d56Sopenharmony_ci        elif typedeps in (None, UNKNOWN):
827db96d56Sopenharmony_ci            # It is still effectively unresolved.
837db96d56Sopenharmony_ci            return UNKNOWN, extra
847db96d56Sopenharmony_ci        elif None in typedeps or UNKNOWN in typedeps:
857db96d56Sopenharmony_ci            # It is still effectively unresolved.
867db96d56Sopenharmony_ci            return typedeps, extra
877db96d56Sopenharmony_ci        elif any(not isinstance(td, TypeDeclaration) for td in typedeps):
887db96d56Sopenharmony_ci            raise NotImplementedError((item, typedeps, extra))
897db96d56Sopenharmony_ci        return typedeps, extra
907db96d56Sopenharmony_ci
917db96d56Sopenharmony_ci    def __init__(self, item, typedecl=None, **extra):
927db96d56Sopenharmony_ci        assert item is not None
937db96d56Sopenharmony_ci        self.item = item
947db96d56Sopenharmony_ci        if typedecl in (UNKNOWN, IGNORED):
957db96d56Sopenharmony_ci            pass
967db96d56Sopenharmony_ci        elif item.kind is KIND.STRUCT or item.kind is KIND.UNION:
977db96d56Sopenharmony_ci            if isinstance(typedecl, TypeDeclaration):
987db96d56Sopenharmony_ci                raise NotImplementedError(item, typedecl)
997db96d56Sopenharmony_ci            elif typedecl is None:
1007db96d56Sopenharmony_ci                typedecl = UNKNOWN
1017db96d56Sopenharmony_ci            else:
1027db96d56Sopenharmony_ci                typedecl = [UNKNOWN if d is None else d for d in typedecl]
1037db96d56Sopenharmony_ci        elif typedecl is None:
1047db96d56Sopenharmony_ci            typedecl = UNKNOWN
1057db96d56Sopenharmony_ci        elif typedecl and not isinstance(typedecl, TypeDeclaration):
1067db96d56Sopenharmony_ci            # All the other decls have a single type decl.
1077db96d56Sopenharmony_ci            typedecl, = typedecl
1087db96d56Sopenharmony_ci            if typedecl is None:
1097db96d56Sopenharmony_ci                typedecl = UNKNOWN
1107db96d56Sopenharmony_ci        self.typedecl = typedecl
1117db96d56Sopenharmony_ci        self._extra = extra
1127db96d56Sopenharmony_ci        self._locked = True
1137db96d56Sopenharmony_ci
1147db96d56Sopenharmony_ci        self._validate()
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    def _validate(self):
1177db96d56Sopenharmony_ci        item = self.item
1187db96d56Sopenharmony_ci        extra = self._extra
1197db96d56Sopenharmony_ci        # Check item.
1207db96d56Sopenharmony_ci        if not isinstance(item, HighlevelParsedItem):
1217db96d56Sopenharmony_ci            raise ValueError(f'"item" must be a high-level parsed item, got {item!r}')
1227db96d56Sopenharmony_ci        # Check extra.
1237db96d56Sopenharmony_ci        for key, value in extra.items():
1247db96d56Sopenharmony_ci            if key.startswith('_'):
1257db96d56Sopenharmony_ci                raise ValueError(f'extra items starting with {"_"!r} not allowed, got {extra!r}')
1267db96d56Sopenharmony_ci            if hasattr(item, key) and not callable(getattr(item, key)):
1277db96d56Sopenharmony_ci                raise ValueError(f'extra cannot override item, got {value!r} for key {key!r}')
1287db96d56Sopenharmony_ci
1297db96d56Sopenharmony_ci    def __repr__(self):
1307db96d56Sopenharmony_ci        kwargs = [
1317db96d56Sopenharmony_ci            f'item={self.item!r}',
1327db96d56Sopenharmony_ci            f'typedecl={self.typedecl!r}',
1337db96d56Sopenharmony_ci            *(f'{k}={v!r}' for k, v in self._extra.items())
1347db96d56Sopenharmony_ci        ]
1357db96d56Sopenharmony_ci        return f'{type(self).__name__}({", ".join(kwargs)})'
1367db96d56Sopenharmony_ci
1377db96d56Sopenharmony_ci    def __str__(self):
1387db96d56Sopenharmony_ci        try:
1397db96d56Sopenharmony_ci            return self._str
1407db96d56Sopenharmony_ci        except AttributeError:
1417db96d56Sopenharmony_ci            self._str, = self.render('line')
1427db96d56Sopenharmony_ci            return self._str
1437db96d56Sopenharmony_ci
1447db96d56Sopenharmony_ci    def __hash__(self):
1457db96d56Sopenharmony_ci        return hash(self.item)
1467db96d56Sopenharmony_ci
1477db96d56Sopenharmony_ci    def __eq__(self, other):
1487db96d56Sopenharmony_ci        if isinstance(other, Analyzed):
1497db96d56Sopenharmony_ci            return self.item == other.item
1507db96d56Sopenharmony_ci        elif isinstance(other, HighlevelParsedItem):
1517db96d56Sopenharmony_ci            return self.item == other
1527db96d56Sopenharmony_ci        elif type(other) is tuple:
1537db96d56Sopenharmony_ci            return self.item == other
1547db96d56Sopenharmony_ci        else:
1557db96d56Sopenharmony_ci            return NotImplemented
1567db96d56Sopenharmony_ci
1577db96d56Sopenharmony_ci    def __gt__(self, other):
1587db96d56Sopenharmony_ci        if isinstance(other, Analyzed):
1597db96d56Sopenharmony_ci            return self.item > other.item
1607db96d56Sopenharmony_ci        elif isinstance(other, HighlevelParsedItem):
1617db96d56Sopenharmony_ci            return self.item > other
1627db96d56Sopenharmony_ci        elif type(other) is tuple:
1637db96d56Sopenharmony_ci            return self.item > other
1647db96d56Sopenharmony_ci        else:
1657db96d56Sopenharmony_ci            return NotImplemented
1667db96d56Sopenharmony_ci
1677db96d56Sopenharmony_ci    def __dir__(self):
1687db96d56Sopenharmony_ci        names = set(super().__dir__())
1697db96d56Sopenharmony_ci        names.update(self._extra)
1707db96d56Sopenharmony_ci        names.remove('_locked')
1717db96d56Sopenharmony_ci        return sorted(names)
1727db96d56Sopenharmony_ci
1737db96d56Sopenharmony_ci    def __getattr__(self, name):
1747db96d56Sopenharmony_ci        if name.startswith('_'):
1757db96d56Sopenharmony_ci            raise AttributeError(name)
1767db96d56Sopenharmony_ci        # The item takes precedence over the extra data (except if callable).
1777db96d56Sopenharmony_ci        try:
1787db96d56Sopenharmony_ci            value = getattr(self.item, name)
1797db96d56Sopenharmony_ci            if callable(value):
1807db96d56Sopenharmony_ci                raise AttributeError(name)
1817db96d56Sopenharmony_ci        except AttributeError:
1827db96d56Sopenharmony_ci            try:
1837db96d56Sopenharmony_ci                value = self._extra[name]
1847db96d56Sopenharmony_ci            except KeyError:
1857db96d56Sopenharmony_ci                pass
1867db96d56Sopenharmony_ci            else:
1877db96d56Sopenharmony_ci                # Speed things up the next time.
1887db96d56Sopenharmony_ci                self.__dict__[name] = value
1897db96d56Sopenharmony_ci                return value
1907db96d56Sopenharmony_ci            raise  # re-raise
1917db96d56Sopenharmony_ci        else:
1927db96d56Sopenharmony_ci            return value
1937db96d56Sopenharmony_ci
1947db96d56Sopenharmony_ci    def __setattr__(self, name, value):
1957db96d56Sopenharmony_ci        if self._locked and name != '_str':
1967db96d56Sopenharmony_ci            raise AttributeError(f'readonly ({name})')
1977db96d56Sopenharmony_ci        super().__setattr__(name, value)
1987db96d56Sopenharmony_ci
1997db96d56Sopenharmony_ci    def __delattr__(self, name):
2007db96d56Sopenharmony_ci        if self._locked:
2017db96d56Sopenharmony_ci            raise AttributeError(f'readonly ({name})')
2027db96d56Sopenharmony_ci        super().__delattr__(name)
2037db96d56Sopenharmony_ci
2047db96d56Sopenharmony_ci    @property
2057db96d56Sopenharmony_ci    def decl(self):
2067db96d56Sopenharmony_ci        if not isinstance(self.item, Declaration):
2077db96d56Sopenharmony_ci            raise AttributeError('decl')
2087db96d56Sopenharmony_ci        return self.item
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ci    @property
2117db96d56Sopenharmony_ci    def signature(self):
2127db96d56Sopenharmony_ci        # XXX vartype...
2137db96d56Sopenharmony_ci        ...
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ci    @property
2167db96d56Sopenharmony_ci    def istype(self):
2177db96d56Sopenharmony_ci        return is_type_decl(self.item.kind)
2187db96d56Sopenharmony_ci
2197db96d56Sopenharmony_ci    @property
2207db96d56Sopenharmony_ci    def is_known(self):
2217db96d56Sopenharmony_ci        if self.typedecl in (UNKNOWN, IGNORED):
2227db96d56Sopenharmony_ci            return False
2237db96d56Sopenharmony_ci        elif isinstance(self.typedecl, TypeDeclaration):
2247db96d56Sopenharmony_ci            return True
2257db96d56Sopenharmony_ci        else:
2267db96d56Sopenharmony_ci            return UNKNOWN not in self.typedecl
2277db96d56Sopenharmony_ci
2287db96d56Sopenharmony_ci    def fix_filename(self, relroot=fsutil.USE_CWD, **kwargs):
2297db96d56Sopenharmony_ci        self.item.fix_filename(relroot, **kwargs)
2307db96d56Sopenharmony_ci        return self
2317db96d56Sopenharmony_ci
2327db96d56Sopenharmony_ci    def as_rowdata(self, columns=None):
2337db96d56Sopenharmony_ci        # XXX finish!
2347db96d56Sopenharmony_ci        return self.item.as_rowdata(columns)
2357db96d56Sopenharmony_ci
2367db96d56Sopenharmony_ci    def render_rowdata(self, columns=None):
2377db96d56Sopenharmony_ci        # XXX finish!
2387db96d56Sopenharmony_ci        return self.item.render_rowdata(columns)
2397db96d56Sopenharmony_ci
2407db96d56Sopenharmony_ci    def render(self, fmt='line', *, itemonly=False):
2417db96d56Sopenharmony_ci        if fmt == 'raw':
2427db96d56Sopenharmony_ci            yield repr(self)
2437db96d56Sopenharmony_ci            return
2447db96d56Sopenharmony_ci        rendered = self.item.render(fmt)
2457db96d56Sopenharmony_ci        if itemonly or not self._extra:
2467db96d56Sopenharmony_ci            yield from rendered
2477db96d56Sopenharmony_ci            return
2487db96d56Sopenharmony_ci        extra = self._render_extra(fmt)
2497db96d56Sopenharmony_ci        if not extra:
2507db96d56Sopenharmony_ci            yield from rendered
2517db96d56Sopenharmony_ci        elif fmt in ('brief', 'line'):
2527db96d56Sopenharmony_ci            rendered, = rendered
2537db96d56Sopenharmony_ci            extra, = extra
2547db96d56Sopenharmony_ci            yield f'{rendered}\t{extra}'
2557db96d56Sopenharmony_ci        elif fmt == 'summary':
2567db96d56Sopenharmony_ci            raise NotImplementedError(fmt)
2577db96d56Sopenharmony_ci        elif fmt == 'full':
2587db96d56Sopenharmony_ci            yield from rendered
2597db96d56Sopenharmony_ci            for line in extra:
2607db96d56Sopenharmony_ci                yield f'\t{line}'
2617db96d56Sopenharmony_ci        else:
2627db96d56Sopenharmony_ci            raise NotImplementedError(fmt)
2637db96d56Sopenharmony_ci
2647db96d56Sopenharmony_ci    def _render_extra(self, fmt):
2657db96d56Sopenharmony_ci        if fmt in ('brief', 'line'):
2667db96d56Sopenharmony_ci            yield str(self._extra)
2677db96d56Sopenharmony_ci        else:
2687db96d56Sopenharmony_ci            raise NotImplementedError(fmt)
2697db96d56Sopenharmony_ci
2707db96d56Sopenharmony_ci
2717db96d56Sopenharmony_ciclass Analysis:
2727db96d56Sopenharmony_ci
2737db96d56Sopenharmony_ci    _item_class = Analyzed
2747db96d56Sopenharmony_ci
2757db96d56Sopenharmony_ci    @classonly
2767db96d56Sopenharmony_ci    def build_item(cls, info, resolved=None, **extra):
2777db96d56Sopenharmony_ci        if resolved is None:
2787db96d56Sopenharmony_ci            return cls._item_class.from_raw(info, **extra)
2797db96d56Sopenharmony_ci        else:
2807db96d56Sopenharmony_ci            return cls._item_class.from_resolved(info, resolved, **extra)
2817db96d56Sopenharmony_ci
2827db96d56Sopenharmony_ci    @classmethod
2837db96d56Sopenharmony_ci    def from_results(cls, results):
2847db96d56Sopenharmony_ci        self = cls()
2857db96d56Sopenharmony_ci        for info, resolved in results:
2867db96d56Sopenharmony_ci            self._add_result(info, resolved)
2877db96d56Sopenharmony_ci        return self
2887db96d56Sopenharmony_ci
2897db96d56Sopenharmony_ci    def __init__(self, items=None):
2907db96d56Sopenharmony_ci        self._analyzed = {type(self).build_item(item): None
2917db96d56Sopenharmony_ci                          for item in items or ()}
2927db96d56Sopenharmony_ci
2937db96d56Sopenharmony_ci    def __repr__(self):
2947db96d56Sopenharmony_ci        return f'{type(self).__name__}({list(self._analyzed.keys())})'
2957db96d56Sopenharmony_ci
2967db96d56Sopenharmony_ci    def __iter__(self):
2977db96d56Sopenharmony_ci        #yield from self.types
2987db96d56Sopenharmony_ci        #yield from self.functions
2997db96d56Sopenharmony_ci        #yield from self.variables
3007db96d56Sopenharmony_ci        yield from self._analyzed
3017db96d56Sopenharmony_ci
3027db96d56Sopenharmony_ci    def __len__(self):
3037db96d56Sopenharmony_ci        return len(self._analyzed)
3047db96d56Sopenharmony_ci
3057db96d56Sopenharmony_ci    def __getitem__(self, key):
3067db96d56Sopenharmony_ci        if type(key) is int:
3077db96d56Sopenharmony_ci            for i, val in enumerate(self._analyzed):
3087db96d56Sopenharmony_ci                if i == key:
3097db96d56Sopenharmony_ci                    return val
3107db96d56Sopenharmony_ci            else:
3117db96d56Sopenharmony_ci                raise IndexError(key)
3127db96d56Sopenharmony_ci        else:
3137db96d56Sopenharmony_ci            return self._analyzed[key]
3147db96d56Sopenharmony_ci
3157db96d56Sopenharmony_ci    def fix_filenames(self, relroot=fsutil.USE_CWD, **kwargs):
3167db96d56Sopenharmony_ci        if relroot and relroot is not fsutil.USE_CWD:
3177db96d56Sopenharmony_ci            relroot = os.path.abspath(relroot)
3187db96d56Sopenharmony_ci        for item in self._analyzed:
3197db96d56Sopenharmony_ci            item.fix_filename(relroot, fixroot=False, **kwargs)
3207db96d56Sopenharmony_ci
3217db96d56Sopenharmony_ci    def _add_result(self, info, resolved):
3227db96d56Sopenharmony_ci        analyzed = type(self).build_item(info, resolved)
3237db96d56Sopenharmony_ci        self._analyzed[analyzed] = None
3247db96d56Sopenharmony_ci        return analyzed
325