| import os.path |
| |
| from c_analyzer_common import files |
| from c_analyzer_common.info import UNKNOWN |
| from c_parser import declarations, info |
| from .info import Symbol |
| from .source import _find_symbol |
| |
| |
| # XXX need tests: |
| # * look_up_known_symbol() |
| # * symbol_from_source() |
| # * get_resolver() |
| # * symbols_to_variables() |
| |
| def look_up_known_symbol(symbol, knownvars, *, |
| match_files=(lambda f1, f2: f1 == f2), |
| ): |
| """Return the known variable matching the given symbol. |
| |
| "knownvars" is a mapping of common.ID to parser.Variable. |
| |
| "match_files" is used to verify if two filenames point to |
| the same file. |
| """ |
| if not knownvars: |
| return None |
| |
| if symbol.funcname == UNKNOWN: |
| if not symbol.filename or symbol.filename == UNKNOWN: |
| for varid in knownvars: |
| if not varid.funcname: |
| continue |
| if varid.name == symbol.name: |
| return knownvars[varid] |
| else: |
| return None |
| else: |
| for varid in knownvars: |
| if not varid.funcname: |
| continue |
| if not match_files(varid.filename, symbol.filename): |
| continue |
| if varid.name == symbol.name: |
| return knownvars[varid] |
| else: |
| return None |
| elif not symbol.filename or symbol.filename == UNKNOWN: |
| raise NotImplementedError |
| else: |
| return knownvars.get(symbol.id) |
| |
| |
| def find_in_source(symbol, dirnames, *, |
| _perfilecache={}, |
| _find_symbol=_find_symbol, |
| _iter_files=files.iter_files_by_suffix, |
| ): |
| """Return the Variable matching the given Symbol. |
| |
| If there is no match then return None. |
| """ |
| if symbol.filename and symbol.filename != UNKNOWN: |
| filenames = [symbol.filename] |
| else: |
| filenames = _iter_files(dirnames, ('.c', '.h')) |
| |
| if symbol.funcname and symbol.funcname != UNKNOWN: |
| raise NotImplementedError |
| |
| (filename, funcname, decl |
| ) = _find_symbol(symbol.name, filenames, _perfilecache) |
| if filename == UNKNOWN: |
| return None |
| return info.Variable.from_parts(filename, funcname, symbol.name, decl) |
| |
| |
| def get_resolver(knownvars=None, dirnames=None, *, |
| _look_up_known=look_up_known_symbol, |
| _from_source=find_in_source, |
| ): |
| """Return a "resolver" func for the given known vars and dirnames. |
| |
| The func takes a single Symbol and returns a corresponding Variable. |
| If the symbol was located then the variable will be valid, populated |
| with the corresponding information. Otherwise None is returned. |
| """ |
| if knownvars: |
| knownvars = dict(knownvars) # a copy |
| def resolve_known(symbol): |
| found = _look_up_known(symbol, knownvars) |
| if found is None: |
| return None |
| elif symbol.funcname == UNKNOWN: |
| knownvars.pop(found.id) |
| elif not symbol.filename or symbol.filename == UNKNOWN: |
| knownvars.pop(found.id) |
| return found |
| if dirnames: |
| def resolve(symbol): |
| found = resolve_known(symbol) |
| if found is None: |
| return None |
| #return _from_source(symbol, dirnames) |
| else: |
| for dirname in dirnames: |
| if not dirname.endswith(os.path.sep): |
| dirname += os.path.sep |
| if found.filename.startswith(dirname): |
| break |
| else: |
| return None |
| return found |
| else: |
| resolve = resolve_known |
| elif dirnames: |
| def resolve(symbol): |
| return _from_source(symbol, dirnames) |
| else: |
| def resolve(symbol): |
| return None |
| return resolve |
| |
| |
| def symbols_to_variables(symbols, *, |
| resolve=(lambda s: look_up_known_symbol(s, None)), |
| ): |
| """Yield the variable the matches each given symbol. |
| |
| Use get_resolver() for a "resolve" func to use. |
| """ |
| for symbol in symbols: |
| if isinstance(symbol, info.Variable): |
| # XXX validate? |
| yield symbol |
| continue |
| if symbol.kind != Symbol.KIND.VARIABLE: |
| continue |
| resolved = resolve(symbol) |
| if resolved is None: |
| #raise NotImplementedError(symbol) |
| resolved = info.Variable( |
| id=symbol.id, |
| storage=UNKNOWN, |
| vartype=UNKNOWN, |
| ) |
| yield resolved |