""" Function descriptors. """ from collections import defaultdict import importlib from numba.core import types, itanium_mangler from numba.core.utils import _dynamic_modname, _dynamic_module def default_mangler(name, argtypes, *, abi_tags=(), uid=None): return itanium_mangler.mangle(name, argtypes, abi_tags=abi_tags, uid=uid) def qualifying_prefix(modname, qualname): """ Returns a new string that is used for the first half of the mangled name. """ # XXX choose a different convention for object mode return '{}.{}'.format(modname, qualname) if modname else qualname class FunctionDescriptor(object): """ Base class for function descriptors: an object used to carry useful metadata about a natively callable function. Note that while `FunctionIdentity` denotes a Python function which is being concretely compiled by Numba, `FunctionDescriptor` may be more "abstract". """ __slots__ = ('native', 'modname', 'qualname', 'doc', 'typemap', 'calltypes', 'args', 'kws', 'restype', 'argtypes', 'mangled_name', 'unique_name', 'env_name', 'global_dict', 'inline', 'noalias', 'abi_tags', 'uid') def __init__(self, native, modname, qualname, unique_name, doc, typemap, restype, calltypes, args, kws, mangler=None, argtypes=None, inline=False, noalias=False, env_name=None, global_dict=None, abi_tags=(), uid=None): self.native = native self.modname = modname self.global_dict = global_dict self.qualname = qualname self.unique_name = unique_name self.doc = doc # XXX typemap and calltypes should be on the compile result, # not the FunctionDescriptor self.typemap = typemap self.calltypes = calltypes self.args = args self.kws = kws self.restype = restype # Argument types if argtypes is not None: assert isinstance(argtypes, tuple), argtypes self.argtypes = argtypes else: # Get argument types from the type inference result # (note the "arg.FOO" convention as used in typeinfer self.argtypes = tuple(self.typemap['arg.' + a] for a in args) mangler = default_mangler if mangler is None else mangler # The mangled name *must* be unique, else the wrong function can # be chosen at link time. qualprefix = qualifying_prefix(self.modname, self.qualname) self.uid = uid self.mangled_name = mangler( qualprefix, self.argtypes, abi_tags=abi_tags, uid=uid, ) if env_name is None: env_name = mangler(".NumbaEnv.{}".format(qualprefix), self.argtypes, abi_tags=abi_tags, uid=uid) self.env_name = env_name self.inline = inline self.noalias = noalias self.abi_tags = abi_tags def lookup_globals(self): """ Return the global dictionary of the function. It may not match the Module's globals if the function is created dynamically (i.e. exec) """ return self.global_dict or self.lookup_module().__dict__ def lookup_module(self): """ Return the module in which this function is supposed to exist. This may be a dummy module if the function was dynamically generated or the module can't be found. """ if self.modname == _dynamic_modname: return _dynamic_module else: try: # ensure module exist return importlib.import_module(self.modname) except ImportError: return _dynamic_module def lookup_function(self): """ Return the original function object described by this object. """ return getattr(self.lookup_module(), self.qualname) @property def llvm_func_name(self): """ The LLVM-registered name for the raw function. """ return self.mangled_name # XXX refactor this @property def llvm_cpython_wrapper_name(self): """ The LLVM-registered name for a CPython-compatible wrapper of the raw function (i.e. a PyCFunctionWithKeywords). """ return itanium_mangler.prepend_namespace(self.mangled_name, ns='cpython') @property def llvm_cfunc_wrapper_name(self): """ The LLVM-registered name for a C-compatible wrapper of the raw function. """ return 'cfunc.' + self.mangled_name def __repr__(self): return "" % (self.unique_name) @classmethod def _get_function_info(cls, func_ir): """ Returns ------- qualname, unique_name, modname, doc, args, kws, globals ``unique_name`` must be a unique name. """ func = func_ir.func_id.func qualname = func_ir.func_id.func_qualname # XXX to func_id modname = func.__module__ doc = func.__doc__ or '' args = tuple(func_ir.arg_names) kws = () # TODO global_dict = None if modname is None: # Dynamically generated function. modname = _dynamic_modname # Retain a reference to the dictionary of the function. # This disables caching, serialization and pickling. global_dict = func_ir.func_id.func.__globals__ unique_name = func_ir.func_id.unique_name return qualname, unique_name, modname, doc, args, kws, global_dict @classmethod def _from_python_function(cls, func_ir, typemap, restype, calltypes, native, mangler=None, inline=False, noalias=False, abi_tags=()): (qualname, unique_name, modname, doc, args, kws, global_dict, ) = cls._get_function_info(func_ir) self = cls(native, modname, qualname, unique_name, doc, typemap, restype, calltypes, args, kws, mangler=mangler, inline=inline, noalias=noalias, global_dict=global_dict, abi_tags=abi_tags, uid=func_ir.func_id.unique_id) return self class PythonFunctionDescriptor(FunctionDescriptor): """ A FunctionDescriptor subclass for Numba-compiled functions. """ __slots__ = () @classmethod def from_specialized_function(cls, func_ir, typemap, restype, calltypes, mangler, inline, noalias, abi_tags): """ Build a FunctionDescriptor for a given specialization of a Python function (in nopython mode). """ return cls._from_python_function(func_ir, typemap, restype, calltypes, native=True, mangler=mangler, inline=inline, noalias=noalias, abi_tags=abi_tags) @classmethod def from_object_mode_function(cls, func_ir): """ Build a FunctionDescriptor for an object mode variant of a Python function. """ typemap = defaultdict(lambda: types.pyobject) calltypes = typemap.copy() restype = types.pyobject return cls._from_python_function(func_ir, typemap, restype, calltypes, native=False) class ExternalFunctionDescriptor(FunctionDescriptor): """ A FunctionDescriptor subclass for opaque external functions (e.g. raw C functions). """ __slots__ = () def __init__(self, name, restype, argtypes): args = ["arg%d" % i for i in range(len(argtypes))] def mangler(a, x, abi_tags, uid=None): return a super(ExternalFunctionDescriptor, self ).__init__(native=True, modname=None, qualname=name, unique_name=name, doc='', typemap=None, restype=restype, calltypes=None, args=args, kws=None, mangler=mangler, argtypes=argtypes)