Python: CG trace: Better handling of builtins without __module__

Not 100% perfect, but better
This commit is contained in:
Rasmus Wriedt Larsen
2020-07-24 19:13:53 +02:00
parent 9c76618d8b
commit 2407c8b07e
5 changed files with 58 additions and 6 deletions

View File

@@ -135,12 +135,8 @@ class XMLExternalCallee extends XMLCallee {
Builtin getACallee() { Builtin getACallee() {
exists(Builtin mod | exists(Builtin mod |
not this.get_module_data() = "None" and
mod.isModule() and mod.isModule() and
mod.getName() = this.get_module_data() mod.getName() = this.get_module_data()
or
this.get_module_data() = "None" and
mod = Builtin::builtinModule()
| |
result = traverse_qualname(mod, this.get_qualname_data()) result = traverse_qualname(mod, this.get_qualname_data())
) )

View File

@@ -95,6 +95,41 @@ class Callee:
BUILTIN_FUNCTION_OR_METHOD = type(print) BUILTIN_FUNCTION_OR_METHOD = type(print)
METHOD_DESCRIPTOR_TYPE = type(dict.get)
_unknown_module_fixup_cache = dict()
def _unkown_module_fixup(func):
# TODO: Doesn't work for everything (for example: `OrderedDict.fromkeys`, `object.__new__`)
module = func.__module__
qualname = func.__qualname__
cls_name, method_name = qualname.split(".")
key = (module, qualname)
if key in _unknown_module_fixup_cache:
return _unknown_module_fixup_cache[key]
matching_classes = list()
for klass in object.__subclasses__():
# type(dict.get) == METHOD_DESCRIPTOR_TYPE
# type(dict.__new__) == BUILTIN_FUNCTION_OR_METHOD
if klass.__qualname__ == cls_name and type(
getattr(klass, method_name, None)
) in [BUILTIN_FUNCTION_OR_METHOD, METHOD_DESCRIPTOR_TYPE]:
matching_classes.append(klass)
if len(matching_classes) == 1:
klass = matching_classes[0]
ret = klass.__module__
else:
if DEBUG:
LOGGER.debug(f"Found more than one matching class for {module} {qualname}")
ret = None
_unknown_module_fixup_cache[key] = ret
return ret
@better_compare_for_dataclass @better_compare_for_dataclass
@@ -109,9 +144,19 @@ class ExternalCallee(Callee):
@classmethod @classmethod
def from_arg(cls, func): def from_arg(cls, func):
# builtin bound methods seems to always return `None` for __module__, but we
# might be able to recover the lost information by looking through all classes.
# For example, `dict().get.__module__ is None` and `dict().get.__qualname__ ==
# "dict.get"`
module = func.__module__
qualname = func.__qualname__
if module is None and qualname.count(".") == 1:
module = _unkown_module_fixup(func)
return cls( return cls(
module=func.__module__, module=module,
qualname=func.__qualname__, qualname=qualname,
is_builtin=type(func) == BUILTIN_FUNCTION_OR_METHOD, is_builtin=type(func) == BUILTIN_FUNCTION_OR_METHOD,
) )

View File

@@ -0,0 +1,3 @@
d = dict()
d.get("foo") or d.get("bar")

View File

@@ -0,0 +1,4 @@
import socket
sock = socket.socket()
print(sock.getsockname())

View File

@@ -0,0 +1,4 @@
import io
# the `io.open` is just an alias for `_io.open`, but we record the external callee as `io.open` :|
io.open("foo")