/rpython/rtyper/lltypesystem/ll2ctypes.py
Python | 1461 lines | 1264 code | 116 blank | 81 comment | 243 complexity | c78d6b1dff699db65e540ee2fed39e08 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- import sys
- try:
- import ctypes
- import ctypes.util
- if not hasattr(ctypes, 'c_longdouble'):
- ctypes.c_longdouble = ctypes.c_double
- except ImportError:
- ctypes = None
- if sys.version_info >= (2, 6):
- load_library_kwargs = {'use_errno': True}
- else:
- load_library_kwargs = {}
- import os, platform as host_platform
- from rpython.rtyper.lltypesystem import lltype, llmemory
- from rpython.rtyper.extfunc import ExtRegistryEntry
- from rpython.rlib.objectmodel import Symbolic, ComputedIntSymbolic
- from rpython.tool.uid import fixid
- from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat, base_int, intmask
- from rpython.rlib.rarithmetic import is_emulated_long, maxint
- from rpython.annotator import model as annmodel
- from rpython.rtyper.llinterp import LLInterpreter, LLException
- from rpython.rtyper.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
- from rpython.rtyper import raddress
- from rpython.translator.platform import platform
- from array import array
- try:
- from thread import _local as tlsobject
- except ImportError:
- class tlsobject(object):
- pass
- _POSIX = os.name == "posix"
- _MS_WINDOWS = os.name == "nt"
- _LINUX = "linux" in sys.platform
- _64BIT = "64bit" in host_platform.architecture()[0]
- # ____________________________________________________________
- far_regions = None
- def allocate_ctypes(ctype):
- if far_regions:
- import random
- pieces = far_regions._ll2ctypes_pieces
- num = random.randrange(len(pieces)+1)
- if num == len(pieces):
- return ctype()
- i1, stop = pieces[num]
- i2 = i1 + ((ctypes.sizeof(ctype) or 1) + 7) & ~7
- if i2 > stop:
- raise MemoryError("out of memory in far_regions")
- pieces[num] = i2, stop
- p = lltype2ctypes(far_regions.getptr(i1))
- return ctypes.cast(p, ctypes.POINTER(ctype)).contents
- else:
- return ctype()
- def do_allocation_in_far_regions():
- """On 32 bits: this reserves 1.25GB of address space, or 2.5GB on Linux,
- which helps test this module for address values that are signed or
- unsigned.
- On 64-bits: reserves 10 times 2GB of address space. This should help
- to find 32-vs-64-bit issues in the JIT. It is likely that objects
- are further apart than 32 bits can represent; it is also possible
- to hit the corner case of being precisely e.g. 2GB - 8 bytes apart.
- Avoid this function if your OS reserves actual RAM from mmap() eagerly.
- """
- global far_regions
- if not far_regions:
- from rpython.rlib import rmmap
- if _64BIT:
- PIECESIZE = 0x80000000
- else:
- if _LINUX:
- PIECESIZE = 0x10000000
- else:
- PIECESIZE = 0x08000000
- PIECES = 10
- flags = (0,)
- if _LINUX:
- flags = (rmmap.MAP_PRIVATE|rmmap.MAP_ANONYMOUS|rmmap.MAP_NORESERVE,
- rmmap.PROT_READ|rmmap.PROT_WRITE)
- if _MS_WINDOWS:
- flags = (rmmap.MEM_RESERVE,)
- # XXX seems not to work
- m = rmmap.mmap(-1, PIECES * PIECESIZE, *flags)
- m.close = lambda : None # leak instead of giving a spurious
- # error at CPython's shutdown
- m._ll2ctypes_pieces = []
- for i in range(PIECES):
- m._ll2ctypes_pieces.append((i * PIECESIZE, (i+1) * PIECESIZE))
- far_regions = m
- # ____________________________________________________________
- _ctypes_cache = {}
- _eci_cache = {}
- def _setup_ctypes_cache():
- from rpython.rtyper.lltypesystem import rffi
- if is_emulated_long:
- signed_as_ctype = ctypes.c_longlong
- unsigned_as_ctypes = ctypes.c_ulonglong
- else:
- signed_as_ctype = ctypes.c_long
- unsigned_as_ctypes = ctypes.c_ulong
- _ctypes_cache.update({
- lltype.Signed: signed_as_ctype,
- lltype.Unsigned: unsigned_as_ctypes,
- lltype.Char: ctypes.c_ubyte,
- rffi.DOUBLE: ctypes.c_double,
- rffi.FLOAT: ctypes.c_float,
- rffi.LONGDOUBLE: ctypes.c_longdouble,
- rffi.SIGNEDCHAR: ctypes.c_byte,
- rffi.UCHAR: ctypes.c_ubyte,
- rffi.SHORT: ctypes.c_short,
- rffi.USHORT: ctypes.c_ushort,
- rffi.INT: ctypes.c_int,
- rffi.INT_real: ctypes.c_int,
- rffi.UINT: ctypes.c_uint,
- rffi.LONG: ctypes.c_long,
- rffi.ULONG: ctypes.c_ulong,
- rffi.LONGLONG: ctypes.c_longlong,
- rffi.ULONGLONG: ctypes.c_ulonglong,
- rffi.SIZE_T: ctypes.c_size_t,
- lltype.Bool: getattr(ctypes, "c_bool", ctypes.c_byte),
- llmemory.Address: ctypes.c_void_p,
- llmemory.GCREF: ctypes.c_void_p,
- llmemory.WeakRef: ctypes.c_void_p, # XXX
- })
-
- if '__int128_t' in rffi.TYPES:
- _ctypes_cache[rffi.__INT128_T] = ctypes.c_longlong # XXX: Not right at all. But for some reason, It started by while doing JIT compile after a merge with default. Can't extend ctypes, because thats a python standard, right?
- # for unicode strings, do not use ctypes.c_wchar because ctypes
- # automatically converts arrays into unicode strings.
- # Pick the unsigned int that has the same size.
- if ctypes.sizeof(ctypes.c_wchar) == ctypes.sizeof(ctypes.c_uint16):
- _ctypes_cache[lltype.UniChar] = ctypes.c_uint16
- else:
- _ctypes_cache[lltype.UniChar] = ctypes.c_uint32
- def build_ctypes_struct(S, delayed_builders, max_n=None):
- def builder():
- # called a bit later to fill in _fields_
- # (to handle recursive structure pointers)
- fields = []
- for fieldname in S._names:
- FIELDTYPE = S._flds[fieldname]
- if max_n is not None and fieldname == S._arrayfld:
- cls = get_ctypes_array_of_size(FIELDTYPE, max_n)
- else:
- if isinstance(FIELDTYPE, lltype.Ptr):
- cls = get_ctypes_type(FIELDTYPE, delayed_builders)
- else:
- cls = get_ctypes_type(FIELDTYPE, delayed_builders,
- cannot_delay=True)
- fields.append((fieldname, cls))
- CStruct._fields_ = fields
- if S._hints.get('union', False):
- base = ctypes.Union
- else:
- base = ctypes.Structure
- class CStruct(base):
- # no _fields_: filled later by builder()
- def _malloc(cls, n=None):
- if S._arrayfld is None:
- if n is not None:
- raise TypeError("%r is not variable-sized" % (S,))
- storage = allocate_ctypes(cls)
- return storage
- else:
- if n is None:
- raise TypeError("%r is variable-sized" % (S,))
- biggercls = build_ctypes_struct(S, None, n)
- bigstruct = allocate_ctypes(biggercls)
- array = getattr(bigstruct, S._arrayfld)
- if hasattr(array, 'length'):
- array.length = n
- return bigstruct
- _malloc = classmethod(_malloc)
- CStruct.__name__ = 'ctypes_%s' % (S,)
- if max_n is not None:
- CStruct._normalized_ctype = get_ctypes_type(S)
- builder() # no need to be lazy here
- else:
- delayed_builders.append((S, builder))
- return CStruct
- def build_ctypes_array(A, delayed_builders, max_n=0):
- assert max_n >= 0
- ITEM = A.OF
- ctypes_item = get_ctypes_type(ITEM, delayed_builders)
- # Python 2.5 ctypes can raise OverflowError on 64-bit builds
- for n in [maxint, 2**31]:
- MAX_SIZE = n/64
- try:
- PtrType = ctypes.POINTER(MAX_SIZE * ctypes_item)
- except (OverflowError, AttributeError), e:
- pass # ^^^ bah, blame ctypes
- else:
- break
- else:
- raise e
- class CArray(ctypes.Structure):
- if is_emulated_long:
- lentype = ctypes.c_longlong
- else:
- lentype = ctypes.c_long
- if not A._hints.get('nolength'):
- _fields_ = [('length', lentype),
- ('items', max_n * ctypes_item)]
- else:
- _fields_ = [('items', max_n * ctypes_item)]
- @classmethod
- def _malloc(cls, n=None):
- if not isinstance(n, int):
- raise TypeError, "array length must be an int"
- biggercls = get_ctypes_array_of_size(A, n)
- bigarray = allocate_ctypes(biggercls)
- if hasattr(bigarray, 'length'):
- bigarray.length = n
- return bigarray
- _ptrtype = None
- @classmethod
- def _get_ptrtype(cls):
- if cls._ptrtype:
- return cls._ptrtype
- # ctypes can raise OverflowError on 64-bit builds
- # on windows it raises AttributeError even for 2**31 (_length_ missing)
- if _MS_WINDOWS:
- other_limit = 2**31-1
- else:
- other_limit = 2**31
- for n in [maxint, other_limit]:
- cls.MAX_SIZE = n / ctypes.sizeof(ctypes_item)
- try:
- cls._ptrtype = ctypes.POINTER(cls.MAX_SIZE * ctypes_item)
- except (OverflowError, AttributeError), e:
- pass
- else:
- break
- else:
- raise e
- return cls._ptrtype
- def _indexable(self, index):
- PtrType = self._get_ptrtype()
- assert index + 1 < self.MAX_SIZE
- p = ctypes.cast(ctypes.pointer(self.items), PtrType)
- return p.contents
- def _getitem(self, index, boundscheck=True):
- if boundscheck:
- items = self.items
- else:
- items = self._indexable(index)
- cobj = items[index]
- if isinstance(ITEM, lltype.ContainerType):
- return ctypes2lltype(lltype.Ptr(ITEM), ctypes.pointer(cobj))
- else:
- return ctypes2lltype(ITEM, cobj)
- def _setitem(self, index, value, boundscheck=True):
- if boundscheck:
- items = self.items
- else:
- items = self._indexable(index)
- cobj = lltype2ctypes(value)
- items[index] = cobj
- CArray.__name__ = 'ctypes_%s*%d' % (A, max_n)
- if max_n > 0:
- CArray._normalized_ctype = get_ctypes_type(A)
- return CArray
- def get_ctypes_array_of_size(FIELDTYPE, max_n):
- if max_n > 0:
- # no need to cache the results in this case, because the exact
- # type is never seen - the array instances are cast to the
- # array's _normalized_ctype, which is always the same.
- return build_ctypes_array(FIELDTYPE, None, max_n)
- else:
- return get_ctypes_type(FIELDTYPE)
- def get_ctypes_type(T, delayed_builders=None, cannot_delay=False):
- # Check delayed builders
- if cannot_delay and delayed_builders:
- for T2, builder in delayed_builders:
- if T2 is T:
- builder()
- delayed_builders.remove((T2, builder))
- return _ctypes_cache[T]
- try:
- return _ctypes_cache[T]
- except KeyError:
- toplevel = cannot_delay or delayed_builders is None
- if toplevel:
- delayed_builders = []
- cls = build_new_ctypes_type(T, delayed_builders)
- if T not in _ctypes_cache:
- _ctypes_cache[T] = cls
- else:
- # check for buggy recursive structure logic
- assert _ctypes_cache[T] is cls
- if toplevel:
- complete_builders(delayed_builders)
- return cls
- def build_new_ctypes_type(T, delayed_builders):
- if isinstance(T, lltype.Typedef):
- T = T.OF
- if isinstance(T, lltype.Ptr):
- if isinstance(T.TO, lltype.FuncType):
- argtypes = [get_ctypes_type(ARG) for ARG in T.TO.ARGS
- if ARG is not lltype.Void]
- if T.TO.RESULT is lltype.Void:
- restype = None
- else:
- restype = get_ctypes_type(T.TO.RESULT)
- try:
- kwds = {'use_errno': True}
- return ctypes.CFUNCTYPE(restype, *argtypes, **kwds)
- except TypeError:
- # unexpected 'use_errno' argument, old ctypes version
- return ctypes.CFUNCTYPE(restype, *argtypes)
- elif isinstance(T.TO, lltype.OpaqueType):
- return ctypes.c_void_p
- else:
- return ctypes.POINTER(get_ctypes_type(T.TO, delayed_builders))
- elif T is lltype.Void:
- return ctypes.c_long # opaque pointer
- elif isinstance(T, lltype.Struct):
- return build_ctypes_struct(T, delayed_builders)
- elif isinstance(T, lltype.Array):
- return build_ctypes_array(T, delayed_builders)
- elif isinstance(T, lltype.OpaqueType):
- if T is lltype.RuntimeTypeInfo:
- return ctypes.c_char * 2
- if T.hints.get('external', None) != 'C':
- raise TypeError("%s is not external" % T)
- return ctypes.c_char * T.hints['getsize']()
- else:
- _setup_ctypes_cache()
- if T in _ctypes_cache:
- return _ctypes_cache[T]
- raise NotImplementedError(T)
- def complete_builders(delayed_builders):
- while delayed_builders:
- T, builder = delayed_builders[0]
- builder()
- delayed_builders.pop(0)
- def convert_struct(container, cstruct=None, delayed_converters=None):
- STRUCT = container._TYPE
- if cstruct is None:
- # if 'container' is an inlined substructure, convert the whole
- # bigger structure at once
- parent, parentindex = lltype.parentlink(container)
- if parent is not None:
- convert_struct(parent)
- return
- # regular case: allocate a new ctypes Structure of the proper type
- cls = get_ctypes_type(STRUCT)
- if STRUCT._arrayfld is not None:
- n = getattr(container, STRUCT._arrayfld).getlength()
- else:
- n = None
- cstruct = cls._malloc(n)
- add_storage(container, _struct_mixin, ctypes.pointer(cstruct))
- if delayed_converters is None:
- delayed_converters_was_None = True
- delayed_converters = []
- else:
- delayed_converters_was_None = False
- for field_name in STRUCT._names:
- FIELDTYPE = getattr(STRUCT, field_name)
- field_value = getattr(container, field_name)
- if not isinstance(FIELDTYPE, lltype.ContainerType):
- # regular field
- if FIELDTYPE != lltype.Void:
- def convert(field_name=field_name, field_value=field_value):
- setattr(cstruct, field_name, lltype2ctypes(field_value))
- if isinstance(FIELDTYPE, lltype.Ptr):
- delayed_converters.append(convert)
- else:
- convert()
- else:
- # inlined substructure/subarray
- if isinstance(FIELDTYPE, lltype.Struct):
- csubstruct = getattr(cstruct, field_name)
- convert_struct(field_value, csubstruct,
- delayed_converters=delayed_converters)
- elif field_name == STRUCT._arrayfld: # inlined var-sized part
- csubarray = getattr(cstruct, field_name)
- convert_array(field_value, csubarray)
- else:
- raise NotImplementedError('inlined field', FIELDTYPE)
- if delayed_converters_was_None:
- for converter in delayed_converters:
- converter()
- remove_regular_struct_content(container)
- def remove_regular_struct_content(container):
- STRUCT = container._TYPE
- for field_name in STRUCT._names:
- FIELDTYPE = getattr(STRUCT, field_name)
- if not isinstance(FIELDTYPE, lltype.ContainerType):
- delattr(container, field_name)
- def convert_array(container, carray=None):
- ARRAY = container._TYPE
- if carray is None:
- # if 'container' is an inlined substructure, convert the whole
- # bigger structure at once
- parent, parentindex = lltype.parentlink(container)
- if parent is not None:
- if not isinstance(parent, _parentable_mixin):
- convert_struct(parent)
- return
- # regular case: allocate a new ctypes array of the proper type
- cls = get_ctypes_type(ARRAY)
- carray = cls._malloc(container.getlength())
- add_storage(container, _array_mixin, ctypes.pointer(carray))
- if not isinstance(ARRAY.OF, lltype.ContainerType):
- # fish that we have enough space
- ctypes_array = ctypes.cast(carray.items,
- ctypes.POINTER(carray.items._type_))
- for i in range(container.getlength()):
- item_value = container.items[i] # fish fish
- ctypes_array[i] = lltype2ctypes(item_value)
- remove_regular_array_content(container)
- else:
- assert isinstance(ARRAY.OF, lltype.Struct)
- for i in range(container.getlength()):
- item_ptr = container.items[i] # fish fish
- convert_struct(item_ptr, carray.items[i])
- def remove_regular_array_content(container):
- for i in range(container.getlength()):
- container.items[i] = None
- def struct_use_ctypes_storage(container, ctypes_storage):
- STRUCT = container._TYPE
- assert isinstance(STRUCT, lltype.Struct)
- add_storage(container, _struct_mixin, ctypes_storage)
- remove_regular_struct_content(container)
- for field_name in STRUCT._names:
- FIELDTYPE = getattr(STRUCT, field_name)
- if isinstance(FIELDTYPE, lltype.ContainerType):
- if isinstance(FIELDTYPE, lltype.Struct):
- struct_container = getattr(container, field_name)
- struct_storage = ctypes.pointer(
- getattr(ctypes_storage.contents, field_name))
- struct_use_ctypes_storage(struct_container, struct_storage)
- struct_container._setparentstructure(container, field_name)
- elif isinstance(FIELDTYPE, lltype.Array):
- assert FIELDTYPE._hints.get('nolength', False) == False
- arraycontainer = _array_of_known_length(FIELDTYPE)
- arraycontainer._storage = ctypes.pointer(
- getattr(ctypes_storage.contents, field_name))
- arraycontainer._setparentstructure(container, field_name)
- object.__setattr__(container, field_name, arraycontainer)
- else:
- raise NotImplementedError(FIELDTYPE)
- # ____________________________________________________________
- # Ctypes-aware subclasses of the _parentable classes
- ALLOCATED = {} # mapping {address: _container}
- def get_common_subclass(cls1, cls2, cache={}):
- """Return a unique subclass with (cls1, cls2) as bases."""
- try:
- return cache[cls1, cls2]
- except KeyError:
- subcls = type('_ctypes_%s' % (cls1.__name__,),
- (cls1, cls2),
- {'__slots__': ()})
- cache[cls1, cls2] = subcls
- return subcls
- def add_storage(instance, mixin_cls, ctypes_storage):
- """Put ctypes_storage on the instance, changing its __class__ so that it
- sees the methods of the given mixin class."""
- # _storage is a ctypes pointer to a structure
- # except for Opaque objects which use a c_void_p.
- assert not isinstance(instance, _parentable_mixin) # not yet
- subcls = get_common_subclass(mixin_cls, instance.__class__)
- instance.__class__ = subcls
- instance._storage = ctypes_storage
- assert ctypes_storage # null pointer?
- class NotCtypesAllocatedStructure(ValueError):
- pass
- class _parentable_mixin(object):
- """Mixin added to _parentable containers when they become ctypes-based.
- (This is done by changing the __class__ of the instance to reference
- a subclass of both its original class and of this mixin class.)
- """
- __slots__ = ()
- def _ctypes_storage_was_allocated(self):
- addr = ctypes.cast(self._storage, ctypes.c_void_p).value
- if addr in ALLOCATED:
- raise Exception("internal ll2ctypes error - "
- "double conversion from lltype to ctypes?")
- # XXX don't store here immortal structures
- ALLOCATED[addr] = self
- def _addressof_storage(self):
- "Returns the storage address as an int"
- if self._storage is None or self._storage is True:
- raise NotCtypesAllocatedStructure("Not a ctypes allocated structure")
- return intmask(ctypes.cast(self._storage, ctypes.c_void_p).value)
- def _free(self):
- self._check() # no double-frees
- # allow the ctypes object to go away now
- addr = ctypes.cast(self._storage, ctypes.c_void_p).value
- try:
- del ALLOCATED[addr]
- except KeyError:
- raise Exception("invalid free() - data already freed or "
- "not allocated from RPython at all")
- self._storage = None
- def _getid(self):
- return self._addressof_storage()
- def __eq__(self, other):
- if isinstance(other, _llgcopaque):
- addressof_other = other.intval
- else:
- if not isinstance(other, lltype._parentable):
- return False
- if self._storage is None or other._storage is None:
- raise RuntimeError("pointer comparison with a freed structure")
- if other._storage is True:
- return False # the other container is not ctypes-based
- addressof_other = other._addressof_storage()
- # both containers are ctypes-based, compare the addresses
- return self._addressof_storage() == addressof_other
- def __ne__(self, other):
- return not (self == other)
- def __hash__(self):
- if self._storage is not None:
- return self._addressof_storage()
- else:
- return object.__hash__(self)
- def __repr__(self):
- if self._storage is None:
- return '<freed C object %s>' % (self._TYPE,)
- else:
- return '<C object %s at 0x%x>' % (self._TYPE,
- fixid(self._addressof_storage()))
- def __str__(self):
- return repr(self)
- def _setparentstructure(self, parent, parentindex):
- super(_parentable_mixin, self)._setparentstructure(parent, parentindex)
- self._keepparent = parent # always keep a strong ref
- class _struct_mixin(_parentable_mixin):
- """Mixin added to _struct containers when they become ctypes-based."""
- __slots__ = ()
- def __getattr__(self, field_name):
- T = getattr(self._TYPE, field_name)
- cobj = getattr(self._storage.contents, field_name)
- return ctypes2lltype(T, cobj)
- def __setattr__(self, field_name, value):
- if field_name.startswith('_'):
- object.__setattr__(self, field_name, value) # '_xxx' attributes
- else:
- cobj = lltype2ctypes(value)
- setattr(self._storage.contents, field_name, cobj)
- class _array_mixin(_parentable_mixin):
- """Mixin added to _array containers when they become ctypes-based."""
- __slots__ = ()
- def getitem(self, index, uninitialized_ok=False):
- return self._storage.contents._getitem(index)
- def setitem(self, index, value):
- self._storage.contents._setitem(index, value)
- class _array_of_unknown_length(_parentable_mixin, lltype._parentable):
- _kind = "array"
- __slots__ = ()
- def getbounds(self):
- # we have no clue, so we allow whatever index
- return 0, maxint
- def getitem(self, index, uninitialized_ok=False):
- res = self._storage.contents._getitem(index, boundscheck=False)
- if isinstance(self._TYPE.OF, lltype.ContainerType):
- res._obj._setparentstructure(self, index)
- return res
- def setitem(self, index, value):
- self._storage.contents._setitem(index, value, boundscheck=False)
- def getitems(self):
- if self._TYPE.OF != lltype.Char:
- raise Exception("cannot get all items of an unknown-length "
- "array of %r" % self._TYPE.OF)
- _items = []
- i = 0
- while 1:
- nextitem = self.getitem(i)
- if nextitem == '\x00':
- _items.append('\x00')
- return _items
- _items.append(nextitem)
- i += 1
- items = property(getitems)
- class _array_of_known_length(_array_of_unknown_length):
- __slots__ = ()
- def getlength(self):
- return self._storage.contents.length
- def getbounds(self):
- return 0, self.getlength()
- # ____________________________________________________________
- def _find_parent(llobj):
- parent, parentindex = lltype.parentlink(llobj)
- if parent is None:
- return llobj, 0
- next_p, next_i = _find_parent(parent)
- if isinstance(parentindex, int):
- c_tp = get_ctypes_type(lltype.typeOf(parent))
- sizeof = ctypes.sizeof(get_ctypes_type(lltype.typeOf(parent).OF))
- ofs = c_tp.items.offset + parentindex * sizeof
- return next_p, next_i + ofs
- else:
- c_tp = get_ctypes_type(lltype.typeOf(parent))
- ofs = getattr(c_tp, parentindex).offset
- return next_p, next_i + ofs
- # ____________________________________________________________
- # XXX THIS IS A HACK XXX
- # ctypes does not keep callback arguments alive. So we do. Forever
- # we need to think deeper how to approach this problem
- # additionally, this adds mess to __del__ "semantics"
- _all_callbacks = {}
- _all_callbacks_results = []
- _int2obj = {}
- _callback_exc_info = None
- _opaque_objs = [None]
- _opaque_objs_seen = {}
- def get_rtyper():
- llinterp = LLInterpreter.current_interpreter
- if llinterp is not None:
- return llinterp.typer
- else:
- return None
- def lltype2ctypes(llobj, normalize=True):
- """Convert the lltype object 'llobj' to its ctypes equivalent.
- 'normalize' should only be False in tests, where we want to
- inspect the resulting ctypes object manually.
- """
- if isinstance(llobj, lltype._uninitialized):
- return uninitialized2ctypes(llobj.TYPE)
- if isinstance(llobj, llmemory.AddressAsInt):
- cobj = ctypes.cast(lltype2ctypes(llobj.adr), ctypes.c_void_p)
- res = intmask(cobj.value)
- _int2obj[res] = llobj.adr.ptr._obj
- return res
- if isinstance(llobj, llmemory.fakeaddress):
- llobj = llobj.ptr or 0
- T = lltype.typeOf(llobj)
- if isinstance(T, lltype.Ptr):
- if not llobj: # NULL pointer
- if T == llmemory.GCREF:
- return ctypes.c_void_p(0)
- return get_ctypes_type(T)()
- if T == llmemory.GCREF:
- if isinstance(llobj._obj, _llgcopaque):
- return ctypes.c_void_p(llobj._obj.intval)
- if isinstance(llobj._obj, int): # tagged pointer
- return ctypes.c_void_p(llobj._obj)
- container = llobj._obj.container
- T = lltype.Ptr(lltype.typeOf(container))
- # otherwise it came from integer and we want a c_void_p with
- # the same value
- if getattr(container, 'llopaque', None):
- try:
- no = _opaque_objs_seen[container]
- except KeyError:
- no = len(_opaque_objs)
- _opaque_objs.append(container)
- _opaque_objs_seen[container] = no
- return no * 2 + 1
- else:
- container = llobj._obj
- if isinstance(T.TO, lltype.FuncType):
- # XXX a temporary workaround for comparison of lltype.FuncType
- key = llobj._obj.__dict__.copy()
- key['_TYPE'] = repr(key['_TYPE'])
- items = key.items()
- items.sort()
- key = tuple(items)
- if key in _all_callbacks:
- return _all_callbacks[key]
- v1voidlist = [(i, getattr(container, '_void' + str(i), None))
- for i in range(len(T.TO.ARGS))
- if T.TO.ARGS[i] is lltype.Void]
- def callback_internal(*cargs):
- cargs = list(cargs)
- for v1 in v1voidlist:
- cargs.insert(v1[0], v1[1])
- assert len(cargs) == len(T.TO.ARGS)
- llargs = []
- for ARG, carg in zip(T.TO.ARGS, cargs):
- if ARG is lltype.Void:
- llargs.append(carg)
- else:
- llargs.append(ctypes2lltype(ARG, carg))
- if hasattr(container, 'graph'):
- if LLInterpreter.current_interpreter is None:
- raise AssertionError
- llinterp = LLInterpreter.current_interpreter
- try:
- llres = llinterp.eval_graph(container.graph, llargs)
- except LLException, lle:
- llinterp._store_exception(lle)
- return 0
- #except:
- # import pdb
- # pdb.set_trace()
- else:
- try:
- llres = container._callable(*llargs)
- except LLException, lle:
- llinterp = LLInterpreter.current_interpreter
- llinterp._store_exception(lle)
- return 0
- assert lltype.typeOf(llres) == T.TO.RESULT
- if T.TO.RESULT is lltype.Void:
- return None
- res = lltype2ctypes(llres)
- if isinstance(T.TO.RESULT, lltype.Ptr):
- _all_callbacks_results.append(res)
- res = ctypes.cast(res, ctypes.c_void_p).value
- if res is None:
- return 0
- if T.TO.RESULT == lltype.SingleFloat:
- res = res.value # baaaah, cannot return a c_float()
- return res
- def callback(*cargs):
- try:
- return callback_internal(*cargs)
- except:
- import sys
- #if option.usepdb:
- # import pdb; pdb.post_mortem(sys.exc_traceback)
- global _callback_exc_info
- _callback_exc_info = sys.exc_info()
- raise
- if isinstance(T.TO.RESULT, lltype.Ptr):
- TMod = lltype.Ptr(lltype.FuncType(T.TO.ARGS,
- lltype.Signed))
- ctypes_func_type = get_ctypes_type(TMod)
- res = ctypes_func_type(callback)
- ctypes_func_type = get_ctypes_type(T)
- res = ctypes.cast(res, ctypes_func_type)
- else:
- ctypes_func_type = get_ctypes_type(T)
- res = ctypes_func_type(callback)
- _all_callbacks[key] = res
- key2 = intmask(ctypes.cast(res, ctypes.c_void_p).value)
- _int2obj[key2] = container
- return res
- index = 0
- if isinstance(container, lltype._subarray):
- topmost, index = _find_parent(container)
- container = topmost
- T = lltype.Ptr(lltype.typeOf(container))
- if container._storage is None:
- raise RuntimeError("attempting to pass a freed structure to C")
- if container._storage is True:
- # container has regular lltype storage, convert it to ctypes
- if isinstance(T.TO, lltype.Struct):
- convert_struct(container)
- elif isinstance(T.TO, lltype.Array):
- convert_array(container)
- elif isinstance(T.TO, lltype.OpaqueType):
- if T.TO != lltype.RuntimeTypeInfo:
- cbuf = ctypes.create_string_buffer(T.TO.hints['getsize']())
- else:
- cbuf = ctypes.create_string_buffer("\x00")
- cbuf = ctypes.cast(cbuf, ctypes.c_void_p)
- add_storage(container, _parentable_mixin, cbuf)
- else:
- raise NotImplementedError(T)
- container._ctypes_storage_was_allocated()
- if isinstance(T.TO, lltype.OpaqueType):
- return container._storage.value
- storage = container._storage
- p = storage
- if index:
- p = ctypes.cast(p, ctypes.c_void_p)
- p = ctypes.c_void_p(p.value + index)
- c_tp = get_ctypes_type(T.TO)
- storage.contents._normalized_ctype = c_tp
- if normalize and hasattr(storage.contents, '_normalized_ctype'):
- normalized_ctype = storage.contents._normalized_ctype
- p = ctypes.cast(p, ctypes.POINTER(normalized_ctype))
- if lltype.typeOf(llobj) == llmemory.GCREF:
- p = ctypes.cast(p, ctypes.c_void_p)
- return p
- if isinstance(llobj, Symbolic):
- if isinstance(llobj, llmemory.ItemOffset):
- llobj = ctypes.sizeof(get_ctypes_type(llobj.TYPE)) * llobj.repeat
- elif isinstance(llobj, ComputedIntSymbolic):
- llobj = llobj.compute_fn()
- else:
- raise NotImplementedError(llobj) # don't know about symbolic value
- if T is lltype.Char or T is lltype.UniChar:
- return ord(llobj)
- if T is lltype.SingleFloat:
- return ctypes.c_float(float(llobj))
- return llobj
- def ctypes2lltype(T, cobj):
- """Convert the ctypes object 'cobj' to its lltype equivalent.
- 'T' is the expected lltype type.
- """
- if T is lltype.Void:
- return None
- if isinstance(T, lltype.Typedef):
- T = T.OF
- if isinstance(T, lltype.Ptr):
- ptrval = ctypes.cast(cobj, ctypes.c_void_p).value
- if not cobj or not ptrval: # NULL pointer
- # CFunctionType.__nonzero__ is broken before Python 2.6
- return lltype.nullptr(T.TO)
- if isinstance(T.TO, lltype.Struct):
- if T.TO._gckind == 'gc' and ptrval & 1: # a tagged pointer
- gcref = _opaque_objs[ptrval // 2].hide()
- return lltype.cast_opaque_ptr(T, gcref)
- REAL_TYPE = T.TO
- if T.TO._arrayfld is not None:
- carray = getattr(cobj.contents, T.TO._arrayfld)
- container = lltype._struct(T.TO, carray.length)
- else:
- # special treatment of 'OBJECT' subclasses
- if get_rtyper() and lltype._castdepth(REAL_TYPE, OBJECT) >= 0:
- # figure out the real type of the object
- containerheader = lltype._struct(OBJECT)
- cobjheader = ctypes.cast(cobj,
- get_ctypes_type(lltype.Ptr(OBJECT)))
- struct_use_ctypes_storage(containerheader,
- cobjheader)
- REAL_TYPE = get_rtyper().get_type_for_typeptr(
- containerheader.typeptr)
- REAL_T = lltype.Ptr(REAL_TYPE)
- cobj = ctypes.cast(cobj, get_ctypes_type(REAL_T))
- container = lltype._struct(REAL_TYPE)
- struct_use_ctypes_storage(container, cobj)
- if REAL_TYPE != T.TO:
- p = container._as_ptr()
- container = lltype.cast_pointer(T, p)._as_obj()
- # special treatment of 'OBJECT_VTABLE' subclasses
- if get_rtyper() and lltype._castdepth(REAL_TYPE,
- OBJECT_VTABLE) >= 0:
- # figure out the real object that this vtable points to,
- # and just return that
- p = get_rtyper().get_real_typeptr_for_typeptr(
- container._as_ptr())
- container = lltype.cast_pointer(T, p)._as_obj()
- elif isinstance(T.TO, lltype.Array):
- if T.TO._hints.get('nolength', False):
- container = _array_of_unknown_length(T.TO)
- container._storage = type(cobj)(cobj.contents)
- else:
- container = _array_of_known_length(T.TO)
- container._storage = type(cobj)(cobj.contents)
- elif isinstance(T.TO, lltype.FuncType):
- cobjkey = intmask(ctypes.cast(cobj, ctypes.c_void_p).value)
- if cobjkey in _int2obj:
- container = _int2obj[cobjkey]
- else:
- _callable = get_ctypes_trampoline(T.TO, cobj)
- return lltype.functionptr(T.TO, getattr(cobj, '__name__', '?'),
- _callable=_callable)
- elif isinstance(T.TO, lltype.OpaqueType):
- if T == llmemory.GCREF:
- container = _llgcopaque(cobj)
- else:
- container = lltype._opaque(T.TO)
- cbuf = ctypes.cast(cobj, ctypes.c_void_p)
- add_storage(container, _parentable_mixin, cbuf)
- else:
- raise NotImplementedError(T)
- llobj = lltype._ptr(T, container, solid=True)
- elif T is llmemory.Address:
- if cobj is None:
- llobj = llmemory.NULL
- else:
- llobj = _lladdress(cobj)
- elif T is lltype.Char:
- llobj = chr(cobj)
- elif T is lltype.UniChar:
- try:
- llobj = unichr(cobj)
- except (ValueError, OverflowError):
- for tc in 'HIL':
- if array(tc).itemsize == array('u').itemsize:
- import struct
- cobj &= 256 ** struct.calcsize(tc) - 1
- llobj = array('u', array(tc, (cobj,)).tostring())[0]
- break
- else:
- raise
- elif T is lltype.Signed:
- llobj = cobj
- elif T is lltype.Bool:
- assert cobj == True or cobj == False # 0 and 1 work too
- llobj = bool(cobj)
- elif T is lltype.SingleFloat:
- if isinstance(cobj, ctypes.c_float):
- cobj = cobj.value
- llobj = r_singlefloat(cobj)
- elif T is lltype.LongFloat:
- if isinstance(cobj, ctypes.c_longdouble):
- cobj = cobj.value
- llobj = r_longfloat(cobj)
- elif T is lltype.Void:
- llobj = cobj
- else:
- from rpython.rtyper.lltypesystem import rffi
- try:
- inttype = rffi.platform.numbertype_to_rclass[T]
- except KeyError:
- llobj = cobj
- else:
- llobj = inttype(cobj)
- assert lltype.typeOf(llobj) == T
- return llobj
- def uninitialized2ctypes(T):
- "For debugging, create a ctypes object filled with 0xDD."
- ctype = get_ctypes_type(T)
- cobj = ctype()
- size = ctypes.sizeof(cobj)
- p = ctypes.cast(ctypes.pointer(cobj),
- ctypes.POINTER(ctypes.c_ubyte * size))
- for i in range(size):
- p.contents[i] = 0xDD
- if isinstance(T, lltype.Primitive):
- return cobj.value
- else:
- return cobj
- # __________ the standard C library __________
- if ctypes:
- def get_libc_name():
- if sys.platform == 'win32':
- # Parses sys.version and deduces the version of the compiler
- import distutils.msvccompiler
- version = distutils.msvccompiler.get_build_version()
- if version is None:
- # This logic works with official builds of Python.
- if sys.version_info < (2, 4):
- clibname = 'msvcrt'
- else:
- clibname = 'msvcr71'
- else:
- if version <= 6:
- clibname = 'msvcrt'
- else:
- clibname = 'msvcr%d' % (version * 10)
- # If python was built with in debug mode
- import imp
- if imp.get_suffixes()[0][0] == '_d.pyd':
- clibname += 'd'
- return clibname+'.dll'
- else:
- return ctypes.util.find_library('c')
- libc_name = get_libc_name() # Make sure the name is determined during import, not at runtime
- # XXX is this always correct???
- standard_c_lib = ctypes.CDLL(get_libc_name(), **load_library_kwargs)
- # ____________________________________________
- # xxx from ctypes.util, this code is a useful fallback on darwin too
- if sys.platform == 'darwin':
- # Andreas Degert's find function using gcc
- import re, tempfile, errno
- def _findLib_gcc_fallback(name):
- expr = r'[^\(\)\s]*lib%s\.[^\(\)\s]*' % re.escape(name)
- fdout, ccout = tempfile.mkstemp()
- os.close(fdout)
- cmd = 'if type gcc >/dev/null 2>&1; then CC=gcc; else CC=cc; fi;' \
- '$CC -Wl,-t -o ' + ccout + ' 2>&1 -l' + name
- try:
- f = os.popen(cmd)
- trace = f.read()
- f.close()
- finally:
- try:
- os.unlink(ccout)
- except OSError, e:
- if e.errno != errno.ENOENT:
- raise
- res = re.search(expr, trace)
- if not res:
- return None
- return res.group(0)
- else:
- _findLib_gcc_fallback = lambda name: None
- def get_ctypes_callable(funcptr, calling_conv):
- if not ctypes:
- raise ImportError("ctypes is needed to use ll2ctypes")
- def get_on_lib(lib, elem):
- """ Wrapper to always use lib[func] instead of lib.func
- """
- try:
- return lib[elem]
- except AttributeError:
- pass
- old_eci = funcptr._obj.compilation_info
- funcname = funcptr._obj._name
- if hasattr(old_eci, '_with_ctypes'):
- old_eci = old_eci._with_ctypes
- try:
- eci = _eci_cache[old_eci]
- except KeyError:
- eci = old_eci.compile_shared_lib(ignore_a_files=True)
- _eci_cache[old_eci] = eci
- libraries = eci.testonly_libraries + eci.libraries + eci.frameworks
- FUNCTYPE = lltype.typeOf(funcptr).TO
- cfunc = None
- if libraries:
- not_found = []
- for libname in libraries:
- libpath = None
- ext = platform.so_ext
- prefixes = platform.so_prefixes
- for dir in eci.library_dirs:
- if libpath:
- break
- for prefix in prefixes:
- tryfile = os.path.join(dir, prefix + libname + '.' + ext)
- if os.path.isfile(tryfile):
- libpath = tryfile
- break
- if not libpath:
- libpath = ctypes.util.find_library(libname)
- if not libpath:
- libpath = _findLib_gcc_fallback(libname)
- if not libpath and os.path.isabs(libname):
- libpath = libname
- if libpath:
- dllclass = getattr(ctypes, calling_conv + 'dll')
- # on ie slackware there was need for RTLD_GLOBAL here.
- # this breaks a lot of things, since passing RTLD_GLOBAL
- # creates symbol conflicts on C level.
- clib = dllclass._dlltype(libpath, **load_library_kwargs)
- cfunc = get_on_lib(clib, funcname)
- if cfunc is not None:
- break
- else:
- not_found.append(libname)
- if cfunc is None:
- cfunc = get_on_lib(standard_c_lib, funcname)
- # XXX magic: on Windows try to load the function from 'kernel32' too
- if cfunc is None and hasattr(ctypes, 'windll'):
- cfunc = get_on_lib(ctypes.windll.kernel32, funcname)
- if cfunc is None:
- # function name not found in any of the libraries
- if not libraries:
- place = 'the standard C library (missing libraries=...?)'
- elif len(not_found) == len(libraries):
- if len(not_found) == 1:
- raise NotImplementedError(
- 'cannot find the library %r' % (not_found[0],))
- else:
- raise NotImplementedError(
- 'cannot find any of the libraries %r' % (not_found,))
- elif len(libraries) == 1:
- place = 'library %r' % (libraries[0],)
- else:
- place = 'any of the libraries %r' % (libraries,)
- if not_found:
- place += ' (did not find %r)' % (not_found,)
- raise NotImplementedError("function %r not found in %s" % (
- funcname, place))
- # get_ctypes_type() can raise NotImplementedError too
- from rpython.rtyper.lltypesystem import rffi
- cfunc.argtypes = [get_ctypes_type(T) if T is not rffi.VOIDP
- else ctypes.c_void_p
- for T in FUNCTYPE.ARGS
- if not T is lltype.Void]
- if FUNCTYPE.RESULT is lltype.Void:
- cfunc.restype = None
- else:
- cfunc.restype = get_ctypes_type(FUNCTYPE.RESULT)
- return cfunc
- class LL2CtypesCallable(object):
- # a special '_callable' object that invokes ctypes
- def __init__(self, FUNCTYPE, calling_conv):
- self.FUNCTYPE = FUNCTYPE
- self.calling_conv = calling_conv
- self.trampoline = None
- #self.funcptr = ... set later
- def __call__(self, *argvalues):
- if self.trampoline is None:
- # lazily build the corresponding ctypes function object
- cfunc = get_ctypes_callable(self.funcptr, self.calling_conv)
- self.trampoline = get_ctypes_trampoline(self.FUNCTYPE, cfunc)
- # perform the call
- return self.trampoline(*argvalues)
- def get_ctypes_trampoline(FUNCTYPE, cfunc):
- RESULT = FUNCTYPE.RESULT
- container_arguments = []
- for i in range(len(FUNCTYPE.ARGS)):
- if isinstance(FUNCTYPE.ARGS[i], lltype.ContainerType):
- container_arguments.append(i)
- void_arguments = []
- for i in range(len(FUNCTYPE.ARGS)):
- if FUNCTYPE.ARGS[i] is lltype.Void:
- void_arguments.append(i)
- def callme(cargs): # an extra indirection: workaround for rlib.rstacklet
- return cfunc(*cargs)
- def invoke_via_ctypes(*argvalues):
- global _callback_exc_info
- cargs = []
- for i in range(len(argvalues)):
- if i not in void_arguments:
- cvalue = lltype2ctypes(argvalues[i])
- if i in container_arguments:
- cvalue = cvalue.contents
- cargs.append(cvalue)
- _callback_exc_info = None
- _restore_c_errno()
- cres = callme(cargs)
- _save_c_errno()
- if _callback_exc_info:
- etype, evalue, etb = _callback_exc_info
- _callback_exc_info = None
- raise etype, evalue, etb
- return ctypes2lltype(RESULT, cres)
- return invoke_via_ctypes
- def force_cast(RESTYPE, value):
- """Cast a value to a result type, trying to use the same rules as C."""
- if not isinstance(RESTYPE, lltype.LowLevelType):
- raise TypeError("rffi.cast() first arg should be a TYPE")
- if isinstance(value, llmemory.AddressAsInt):
- value = value.adr
- if isinstance(value, llmemory.fakeaddress):
- value = value.ptr or 0
- if isinstance(value, r_singlefloat):
- value = float(value)
- TYPE1 = lltype.typeOf(value)
- cvalue = lltype2ctypes(value)
- cresulttype = get_ctypes_type(RESTYPE)
- if RESTYPE == TYPE1:
- return value
- elif isinstance(TYPE1, lltype.Ptr):
- if isinstance(RESTYPE, lltype.Ptr):
- # shortcut: ptr->ptr cast
- cptr = ctypes.cast(cvalue, cresulttype)
- return ctypes2lltype(RESTYPE, cptr)
- # first cast the input pointer to an integer
- cvalue = ctypes.cast(cvalue, ctypes.c_void_p).value
- if cvalue is None:
- cvalue = 0
- elif isinstance(cvalue, (str, unicode)):
- cvalue = ord(cvalue) # character -> integer
- elif hasattr(RESTYPE, "_type") and issubclass(RESTYPE._type, base_int):
- cvalue = int(cvalue)
- elif isinstance(cvalue, r_longfloat):
- cvalue = cvalue.value
- if not isinstance(cvalue, (int, long, float)):
- raise NotImplementedError("casting %r to %r" % (TYPE1, RESTYPE))
- if isinstance(RESTYPE, lltype.Ptr):
- # upgrade to a more recent ctypes (e.g. 1.0.2) if you get
- # an OverflowError on the following line.
- cvalue = ctypes.cast(ctypes.c_void_p(cvalue), cresulttype)
- elif RESTYPE == lltype.Bool:
- cvalue = bool(cvalue)
- else:
- try:
- cvalue = cresulttype(cvalue).value # mask high bits off if needed
- except TypeError:
- cvalue = int(cvalue) # float -> int
- cvalue = cresulttype(cvalue).value # try again
- return ctypes2lltype(RESTYPE, cvalue)
- class ForceCastEntry(ExtRegistryEntry):
- _about_ = force_cast
- def compute_result_annotation(self, s_RESTYPE, s_value):
- assert s_RESTYPE.is_constant()
- RESTYPE = s_RESTYPE.const
- return annmodel.lltype_to_annotation(RESTYPE)
- def specialize_call(self, hop):
- hop.exception_cannot_occur()
- s_RESTYPE = hop.args_s[0]
- assert s_RESTYPE.is_constant()
- RESTYPE = s_RESTYPE.const
- v_arg = hop.inputarg(hop.args_r[1], arg=1)
- return hop.genop('force_cast', [v_arg], resulttype = RESTYPE)
- def typecheck_ptradd(T):
- # --- ptradd() is only for pointers to non-GC, no-length arrays.
- assert isinstance(T, lltype.Ptr)
- assert isinstance(T.TO, lltype.Array)
- assert T.TO._hints.get('nolength')
- def force_ptradd(ptr, n):
- """'ptr' must be a pointer to an array. Equivalent of 'ptr + n' in
- C, i.e. gives a pointer to the n'th item of the array. The type of
- the result is again a pointer to an array, the same as the type of
- 'ptr'.
- """
- T = lltype.typeOf(ptr)
- typecheck_ptradd(T)
- ctypes_item_type = get_ctypes_type(T.TO.OF)
- ctypes_arrayptr_type = get_ctypes_type(T)
- cptr = lltype2ctypes(ptr)
- baseaddr = ctypes.addressof(cptr.contents.items)
- addr = baseaddr + n * ctypes.sizeof(ctypes_item_type)
- cptr = ctypes.cast(ctypes…
Large files files are truncated, but you can click here to view the full file