17db96d56Sopenharmony_ciimport os.path
27db96d56Sopenharmony_ciimport re
37db96d56Sopenharmony_ci
47db96d56Sopenharmony_cifrom c_common.clsutil import classonly
57db96d56Sopenharmony_cifrom c_parser.info import (
67db96d56Sopenharmony_ci    KIND,
77db96d56Sopenharmony_ci    DeclID,
87db96d56Sopenharmony_ci    Declaration,
97db96d56Sopenharmony_ci    TypeDeclaration,
107db96d56Sopenharmony_ci    TypeDef,
117db96d56Sopenharmony_ci    Struct,
127db96d56Sopenharmony_ci    Member,
137db96d56Sopenharmony_ci    FIXED_TYPE,
147db96d56Sopenharmony_ci)
157db96d56Sopenharmony_cifrom c_parser.match import (
167db96d56Sopenharmony_ci    is_type_decl,
177db96d56Sopenharmony_ci    is_pots,
187db96d56Sopenharmony_ci    is_funcptr,
197db96d56Sopenharmony_ci)
207db96d56Sopenharmony_cifrom c_analyzer.match import (
217db96d56Sopenharmony_ci    is_system_type,
227db96d56Sopenharmony_ci    is_process_global,
237db96d56Sopenharmony_ci    is_fixed_type,
247db96d56Sopenharmony_ci    is_immutable,
257db96d56Sopenharmony_ci)
267db96d56Sopenharmony_ciimport c_analyzer as _c_analyzer
277db96d56Sopenharmony_ciimport c_analyzer.info as _info
287db96d56Sopenharmony_ciimport c_analyzer.datafiles as _datafiles
297db96d56Sopenharmony_cifrom . import _parser, REPO_ROOT
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_ci_DATA_DIR = os.path.dirname(__file__)
337db96d56Sopenharmony_ciKNOWN_FILE = os.path.join(_DATA_DIR, 'known.tsv')
347db96d56Sopenharmony_ciIGNORED_FILE = os.path.join(_DATA_DIR, 'ignored.tsv')
357db96d56Sopenharmony_ciNEED_FIX_FILE = os.path.join(_DATA_DIR, 'globals-to-fix.tsv')
367db96d56Sopenharmony_ciKNOWN_IN_DOT_C = {
377db96d56Sopenharmony_ci    'struct _odictobject': False,
387db96d56Sopenharmony_ci    'PyTupleObject': False,
397db96d56Sopenharmony_ci    'struct _typeobject': False,
407db96d56Sopenharmony_ci    'struct _arena': True,  # ???
417db96d56Sopenharmony_ci    'struct _frame': False,
427db96d56Sopenharmony_ci    'struct _ts': True,  # ???
437db96d56Sopenharmony_ci    'struct PyCodeObject': False,
447db96d56Sopenharmony_ci    'struct _is': True,  # ???
457db96d56Sopenharmony_ci    'PyWideStringList': True,  # ???
467db96d56Sopenharmony_ci    # recursive
477db96d56Sopenharmony_ci    'struct _dictkeysobject': False,
487db96d56Sopenharmony_ci}
497db96d56Sopenharmony_ci# These are loaded from the respective .tsv files upon first use.
507db96d56Sopenharmony_ci_KNOWN = {
517db96d56Sopenharmony_ci    # {(file, ID) | ID => info | bool}
527db96d56Sopenharmony_ci    #'PyWideStringList': True,
537db96d56Sopenharmony_ci}
547db96d56Sopenharmony_ci#_KNOWN = {(Struct(None, typeid.partition(' ')[-1], None)
557db96d56Sopenharmony_ci#           if typeid.startswith('struct ')
567db96d56Sopenharmony_ci#           else TypeDef(None, typeid, None)
577db96d56Sopenharmony_ci#           ): ([], {'unsupported': None if supported else True})
587db96d56Sopenharmony_ci#          for typeid, supported in _KNOWN_IN_DOT_C.items()}
597db96d56Sopenharmony_ci_IGNORED = {
607db96d56Sopenharmony_ci    # {ID => reason}
617db96d56Sopenharmony_ci}
627db96d56Sopenharmony_ci
637db96d56Sopenharmony_ciKINDS = frozenset((*KIND.TYPES, KIND.VARIABLE))
647db96d56Sopenharmony_ci
657db96d56Sopenharmony_ci
667db96d56Sopenharmony_cidef read_known():
677db96d56Sopenharmony_ci    if not _KNOWN:
687db96d56Sopenharmony_ci        # Cache a copy the first time.
697db96d56Sopenharmony_ci        extracols = None  # XXX
707db96d56Sopenharmony_ci        #extracols = ['unsupported']
717db96d56Sopenharmony_ci        known = _datafiles.read_known(KNOWN_FILE, extracols, REPO_ROOT)
727db96d56Sopenharmony_ci        # For now we ignore known.values() (i.e. "extra").
737db96d56Sopenharmony_ci        types, _ = _datafiles.analyze_known(
747db96d56Sopenharmony_ci            known,
757db96d56Sopenharmony_ci            analyze_resolved=analyze_resolved,
767db96d56Sopenharmony_ci        )
777db96d56Sopenharmony_ci        _KNOWN.update(types)
787db96d56Sopenharmony_ci    return _KNOWN.copy()
797db96d56Sopenharmony_ci
807db96d56Sopenharmony_ci
817db96d56Sopenharmony_cidef write_known():
827db96d56Sopenharmony_ci    raise NotImplementedError
837db96d56Sopenharmony_ci    datafiles.write_known(decls, IGNORED_FILE, ['unsupported'], relroot=REPO_ROOT)
847db96d56Sopenharmony_ci
857db96d56Sopenharmony_ci
867db96d56Sopenharmony_cidef read_ignored():
877db96d56Sopenharmony_ci    if not _IGNORED:
887db96d56Sopenharmony_ci        _IGNORED.update(_datafiles.read_ignored(IGNORED_FILE, relroot=REPO_ROOT))
897db96d56Sopenharmony_ci        _IGNORED.update(_datafiles.read_ignored(NEED_FIX_FILE, relroot=REPO_ROOT))
907db96d56Sopenharmony_ci    return dict(_IGNORED)
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci
937db96d56Sopenharmony_cidef write_ignored():
947db96d56Sopenharmony_ci    raise NotImplementedError
957db96d56Sopenharmony_ci    _datafiles.write_ignored(variables, IGNORED_FILE, relroot=REPO_ROOT)
967db96d56Sopenharmony_ci
977db96d56Sopenharmony_ci
987db96d56Sopenharmony_cidef analyze(filenames, *,
997db96d56Sopenharmony_ci            skip_objects=False,
1007db96d56Sopenharmony_ci            **kwargs
1017db96d56Sopenharmony_ci            ):
1027db96d56Sopenharmony_ci    if skip_objects:
1037db96d56Sopenharmony_ci        # XXX Set up a filter.
1047db96d56Sopenharmony_ci        raise NotImplementedError
1057db96d56Sopenharmony_ci
1067db96d56Sopenharmony_ci    known = read_known()
1077db96d56Sopenharmony_ci
1087db96d56Sopenharmony_ci    decls = iter_decls(filenames)
1097db96d56Sopenharmony_ci    results = _c_analyzer.analyze_decls(
1107db96d56Sopenharmony_ci        decls,
1117db96d56Sopenharmony_ci        known,
1127db96d56Sopenharmony_ci        analyze_resolved=analyze_resolved,
1137db96d56Sopenharmony_ci    )
1147db96d56Sopenharmony_ci    analysis = Analysis.from_results(results)
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    return analysis
1177db96d56Sopenharmony_ci
1187db96d56Sopenharmony_ci
1197db96d56Sopenharmony_cidef iter_decls(filenames, **kwargs):
1207db96d56Sopenharmony_ci    decls = _c_analyzer.iter_decls(
1217db96d56Sopenharmony_ci        filenames,
1227db96d56Sopenharmony_ci        # We ignore functions (and statements).
1237db96d56Sopenharmony_ci        kinds=KINDS,
1247db96d56Sopenharmony_ci        parse_files=_parser.parse_files,
1257db96d56Sopenharmony_ci        **kwargs
1267db96d56Sopenharmony_ci    )
1277db96d56Sopenharmony_ci    for decl in decls:
1287db96d56Sopenharmony_ci        if not decl.data:
1297db96d56Sopenharmony_ci            # Ignore forward declarations.
1307db96d56Sopenharmony_ci            continue
1317db96d56Sopenharmony_ci        yield decl
1327db96d56Sopenharmony_ci
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_cidef analyze_resolved(resolved, decl, types, knowntypes, extra=None):
1357db96d56Sopenharmony_ci    if decl.kind not in KINDS:
1367db96d56Sopenharmony_ci        # Skip it!
1377db96d56Sopenharmony_ci        return None
1387db96d56Sopenharmony_ci
1397db96d56Sopenharmony_ci    typedeps = resolved
1407db96d56Sopenharmony_ci    if typedeps is _info.UNKNOWN:
1417db96d56Sopenharmony_ci        if decl.kind in (KIND.STRUCT, KIND.UNION):
1427db96d56Sopenharmony_ci            typedeps = [typedeps] * len(decl.members)
1437db96d56Sopenharmony_ci        else:
1447db96d56Sopenharmony_ci            typedeps = [typedeps]
1457db96d56Sopenharmony_ci    #assert isinstance(typedeps, (list, TypeDeclaration)), typedeps
1467db96d56Sopenharmony_ci
1477db96d56Sopenharmony_ci    if extra is None:
1487db96d56Sopenharmony_ci        extra = {}
1497db96d56Sopenharmony_ci    elif 'unsupported' in extra:
1507db96d56Sopenharmony_ci        raise NotImplementedError((decl, extra))
1517db96d56Sopenharmony_ci
1527db96d56Sopenharmony_ci    unsupported = _check_unsupported(decl, typedeps, types, knowntypes)
1537db96d56Sopenharmony_ci    extra['unsupported'] = unsupported
1547db96d56Sopenharmony_ci
1557db96d56Sopenharmony_ci    return typedeps, extra
1567db96d56Sopenharmony_ci
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_cidef _check_unsupported(decl, typedeps, types, knowntypes):
1597db96d56Sopenharmony_ci    if typedeps is None:
1607db96d56Sopenharmony_ci        raise NotImplementedError(decl)
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci    if decl.kind in (KIND.STRUCT, KIND.UNION):
1637db96d56Sopenharmony_ci        return _check_members(decl, typedeps, types, knowntypes)
1647db96d56Sopenharmony_ci    elif decl.kind is KIND.ENUM:
1657db96d56Sopenharmony_ci        if typedeps:
1667db96d56Sopenharmony_ci            raise NotImplementedError((decl, typedeps))
1677db96d56Sopenharmony_ci        return None
1687db96d56Sopenharmony_ci    else:
1697db96d56Sopenharmony_ci        return _check_typedep(decl, typedeps, types, knowntypes)
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_ci
1727db96d56Sopenharmony_cidef _check_members(decl, typedeps, types, knowntypes):
1737db96d56Sopenharmony_ci    if isinstance(typedeps, TypeDeclaration):
1747db96d56Sopenharmony_ci        raise NotImplementedError((decl, typedeps))
1757db96d56Sopenharmony_ci
1767db96d56Sopenharmony_ci    #members = decl.members or ()  # A forward decl has no members.
1777db96d56Sopenharmony_ci    members = decl.members
1787db96d56Sopenharmony_ci    if not members:
1797db96d56Sopenharmony_ci        # A forward decl has no members, but that shouldn't surface here..
1807db96d56Sopenharmony_ci        raise NotImplementedError(decl)
1817db96d56Sopenharmony_ci    if len(members) != len(typedeps):
1827db96d56Sopenharmony_ci        raise NotImplementedError((decl, typedeps))
1837db96d56Sopenharmony_ci
1847db96d56Sopenharmony_ci    unsupported = []
1857db96d56Sopenharmony_ci    for member, typedecl in zip(members, typedeps):
1867db96d56Sopenharmony_ci        checked = _check_typedep(member, typedecl, types, knowntypes)
1877db96d56Sopenharmony_ci        unsupported.append(checked)
1887db96d56Sopenharmony_ci    if any(None if v is FIXED_TYPE else v for v in unsupported):
1897db96d56Sopenharmony_ci        return unsupported
1907db96d56Sopenharmony_ci    elif FIXED_TYPE in unsupported:
1917db96d56Sopenharmony_ci        return FIXED_TYPE
1927db96d56Sopenharmony_ci    else:
1937db96d56Sopenharmony_ci        return None
1947db96d56Sopenharmony_ci
1957db96d56Sopenharmony_ci
1967db96d56Sopenharmony_cidef _check_typedep(decl, typedecl, types, knowntypes):
1977db96d56Sopenharmony_ci    if not isinstance(typedecl, TypeDeclaration):
1987db96d56Sopenharmony_ci        if hasattr(type(typedecl), '__len__'):
1997db96d56Sopenharmony_ci            if len(typedecl) == 1:
2007db96d56Sopenharmony_ci                typedecl, = typedecl
2017db96d56Sopenharmony_ci    if typedecl is None:
2027db96d56Sopenharmony_ci        # XXX Fail?
2037db96d56Sopenharmony_ci        return 'typespec (missing)'
2047db96d56Sopenharmony_ci    elif typedecl is _info.UNKNOWN:
2057db96d56Sopenharmony_ci        # XXX Is this right?
2067db96d56Sopenharmony_ci        return 'typespec (unknown)'
2077db96d56Sopenharmony_ci    elif not isinstance(typedecl, TypeDeclaration):
2087db96d56Sopenharmony_ci        raise NotImplementedError((decl, typedecl))
2097db96d56Sopenharmony_ci
2107db96d56Sopenharmony_ci    if isinstance(decl, Member):
2117db96d56Sopenharmony_ci        return _check_vartype(decl, typedecl, types, knowntypes)
2127db96d56Sopenharmony_ci    elif not isinstance(decl, Declaration):
2137db96d56Sopenharmony_ci        raise NotImplementedError(decl)
2147db96d56Sopenharmony_ci    elif decl.kind is KIND.TYPEDEF:
2157db96d56Sopenharmony_ci        return _check_vartype(decl, typedecl, types, knowntypes)
2167db96d56Sopenharmony_ci    elif decl.kind is KIND.VARIABLE:
2177db96d56Sopenharmony_ci        if not is_process_global(decl):
2187db96d56Sopenharmony_ci            return None
2197db96d56Sopenharmony_ci        checked = _check_vartype(decl, typedecl, types, knowntypes)
2207db96d56Sopenharmony_ci        return 'mutable' if checked is FIXED_TYPE else checked
2217db96d56Sopenharmony_ci    else:
2227db96d56Sopenharmony_ci        raise NotImplementedError(decl)
2237db96d56Sopenharmony_ci
2247db96d56Sopenharmony_ci
2257db96d56Sopenharmony_cidef _check_vartype(decl, typedecl, types, knowntypes):
2267db96d56Sopenharmony_ci    """Return failure reason."""
2277db96d56Sopenharmony_ci    checked = _check_typespec(decl, typedecl, types, knowntypes)
2287db96d56Sopenharmony_ci    if checked:
2297db96d56Sopenharmony_ci        return checked
2307db96d56Sopenharmony_ci    if is_immutable(decl.vartype):
2317db96d56Sopenharmony_ci        return None
2327db96d56Sopenharmony_ci    if is_fixed_type(decl.vartype):
2337db96d56Sopenharmony_ci        return FIXED_TYPE
2347db96d56Sopenharmony_ci    return 'mutable'
2357db96d56Sopenharmony_ci
2367db96d56Sopenharmony_ci
2377db96d56Sopenharmony_cidef _check_typespec(decl, typedecl, types, knowntypes):
2387db96d56Sopenharmony_ci    typespec = decl.vartype.typespec
2397db96d56Sopenharmony_ci    if typedecl is not None:
2407db96d56Sopenharmony_ci        found = types.get(typedecl)
2417db96d56Sopenharmony_ci        if found is None:
2427db96d56Sopenharmony_ci            found = knowntypes.get(typedecl)
2437db96d56Sopenharmony_ci
2447db96d56Sopenharmony_ci        if found is not None:
2457db96d56Sopenharmony_ci            _, extra = found
2467db96d56Sopenharmony_ci            if extra is None:
2477db96d56Sopenharmony_ci                # XXX Under what circumstances does this happen?
2487db96d56Sopenharmony_ci                extra = {}
2497db96d56Sopenharmony_ci            unsupported = extra.get('unsupported')
2507db96d56Sopenharmony_ci            if unsupported is FIXED_TYPE:
2517db96d56Sopenharmony_ci                unsupported = None
2527db96d56Sopenharmony_ci            return 'typespec' if unsupported else None
2537db96d56Sopenharmony_ci    # Fall back to default known types.
2547db96d56Sopenharmony_ci    if is_pots(typespec):
2557db96d56Sopenharmony_ci        return None
2567db96d56Sopenharmony_ci    elif is_system_type(typespec):
2577db96d56Sopenharmony_ci        return None
2587db96d56Sopenharmony_ci    elif is_funcptr(decl.vartype):
2597db96d56Sopenharmony_ci        return None
2607db96d56Sopenharmony_ci    return 'typespec'
2617db96d56Sopenharmony_ci
2627db96d56Sopenharmony_ci
2637db96d56Sopenharmony_ciclass Analyzed(_info.Analyzed):
2647db96d56Sopenharmony_ci
2657db96d56Sopenharmony_ci    @classonly
2667db96d56Sopenharmony_ci    def is_target(cls, raw):
2677db96d56Sopenharmony_ci        if not super().is_target(raw):
2687db96d56Sopenharmony_ci            return False
2697db96d56Sopenharmony_ci        if raw.kind not in KINDS:
2707db96d56Sopenharmony_ci            return False
2717db96d56Sopenharmony_ci        return True
2727db96d56Sopenharmony_ci
2737db96d56Sopenharmony_ci    #@classonly
2747db96d56Sopenharmony_ci    #def _parse_raw_result(cls, result, extra):
2757db96d56Sopenharmony_ci    #    typedecl, extra = super()._parse_raw_result(result, extra)
2767db96d56Sopenharmony_ci    #    if typedecl is None:
2777db96d56Sopenharmony_ci    #        return None, extra
2787db96d56Sopenharmony_ci    #    raise NotImplementedError
2797db96d56Sopenharmony_ci
2807db96d56Sopenharmony_ci    def __init__(self, item, typedecl=None, *, unsupported=None, **extra):
2817db96d56Sopenharmony_ci        if 'unsupported' in extra:
2827db96d56Sopenharmony_ci            raise NotImplementedError((item, typedecl, unsupported, extra))
2837db96d56Sopenharmony_ci        if not unsupported:
2847db96d56Sopenharmony_ci            unsupported = None
2857db96d56Sopenharmony_ci        elif isinstance(unsupported, (str, TypeDeclaration)):
2867db96d56Sopenharmony_ci            unsupported = (unsupported,)
2877db96d56Sopenharmony_ci        elif unsupported is not FIXED_TYPE:
2887db96d56Sopenharmony_ci            unsupported = tuple(unsupported)
2897db96d56Sopenharmony_ci        self.unsupported = unsupported
2907db96d56Sopenharmony_ci        extra['unsupported'] = self.unsupported  # ...for __repr__(), etc.
2917db96d56Sopenharmony_ci        if self.unsupported is None:
2927db96d56Sopenharmony_ci            #self.supported = None
2937db96d56Sopenharmony_ci            self.supported = True
2947db96d56Sopenharmony_ci        elif self.unsupported is FIXED_TYPE:
2957db96d56Sopenharmony_ci            if item.kind is KIND.VARIABLE:
2967db96d56Sopenharmony_ci                raise NotImplementedError(item, typedecl, unsupported)
2977db96d56Sopenharmony_ci            self.supported = True
2987db96d56Sopenharmony_ci        else:
2997db96d56Sopenharmony_ci            self.supported = not self.unsupported
3007db96d56Sopenharmony_ci        super().__init__(item, typedecl, **extra)
3017db96d56Sopenharmony_ci
3027db96d56Sopenharmony_ci    def render(self, fmt='line', *, itemonly=False):
3037db96d56Sopenharmony_ci        if fmt == 'raw':
3047db96d56Sopenharmony_ci            yield repr(self)
3057db96d56Sopenharmony_ci            return
3067db96d56Sopenharmony_ci        rendered = super().render(fmt, itemonly=itemonly)
3077db96d56Sopenharmony_ci        # XXX ???
3087db96d56Sopenharmony_ci        #if itemonly:
3097db96d56Sopenharmony_ci        #    yield from rendered
3107db96d56Sopenharmony_ci        supported = self.supported
3117db96d56Sopenharmony_ci        if fmt in ('line', 'brief'):
3127db96d56Sopenharmony_ci            rendered, = rendered
3137db96d56Sopenharmony_ci            parts = [
3147db96d56Sopenharmony_ci                '+' if supported else '-' if supported is False else '',
3157db96d56Sopenharmony_ci                rendered,
3167db96d56Sopenharmony_ci            ]
3177db96d56Sopenharmony_ci            yield '\t'.join(parts)
3187db96d56Sopenharmony_ci        elif fmt == 'summary':
3197db96d56Sopenharmony_ci            raise NotImplementedError(fmt)
3207db96d56Sopenharmony_ci        elif fmt == 'full':
3217db96d56Sopenharmony_ci            yield from rendered
3227db96d56Sopenharmony_ci            if supported:
3237db96d56Sopenharmony_ci                yield f'\tsupported:\t{supported}'
3247db96d56Sopenharmony_ci        else:
3257db96d56Sopenharmony_ci            raise NotImplementedError(fmt)
3267db96d56Sopenharmony_ci
3277db96d56Sopenharmony_ci
3287db96d56Sopenharmony_ciclass Analysis(_info.Analysis):
3297db96d56Sopenharmony_ci    _item_class = Analyzed
3307db96d56Sopenharmony_ci
3317db96d56Sopenharmony_ci    @classonly
3327db96d56Sopenharmony_ci    def build_item(cls, info, result=None):
3337db96d56Sopenharmony_ci        if not isinstance(info, Declaration) or info.kind not in KINDS:
3347db96d56Sopenharmony_ci            raise NotImplementedError((info, result))
3357db96d56Sopenharmony_ci        return super().build_item(info, result)
3367db96d56Sopenharmony_ci
3377db96d56Sopenharmony_ci
3387db96d56Sopenharmony_cidef check_globals(analysis):
3397db96d56Sopenharmony_ci    # yield (data, failure)
3407db96d56Sopenharmony_ci    ignored = read_ignored()
3417db96d56Sopenharmony_ci    for item in analysis:
3427db96d56Sopenharmony_ci        if item.kind != KIND.VARIABLE:
3437db96d56Sopenharmony_ci            continue
3447db96d56Sopenharmony_ci        if item.supported:
3457db96d56Sopenharmony_ci            continue
3467db96d56Sopenharmony_ci        if item.id in ignored:
3477db96d56Sopenharmony_ci            continue
3487db96d56Sopenharmony_ci        reason = item.unsupported
3497db96d56Sopenharmony_ci        if not reason:
3507db96d56Sopenharmony_ci            reason = '???'
3517db96d56Sopenharmony_ci        elif not isinstance(reason, str):
3527db96d56Sopenharmony_ci            if len(reason) == 1:
3537db96d56Sopenharmony_ci                reason, = reason
3547db96d56Sopenharmony_ci        reason = f'({reason})'
3557db96d56Sopenharmony_ci        yield item, f'not supported {reason:20}\t{item.storage or ""} {item.vartype}'
356