[pypy-commit] pypy unicode-utf8: merge default into branch
mattip
pypy.commits at gmail.com
Tue Sep 11 16:33:04 EDT 2018
Author: Matti Picus <matti.picus at gmail.com>
Branch: unicode-utf8
Changeset: r95095:4ca367f8c139
Date: 2018-09-11 20:56 +0300
http://bitbucket.org/pypy/pypy/changeset/4ca367f8c139/
Log: merge default into branch
diff too long, truncating to 2000 out of 6123 lines
diff --git a/lib-python/2.7/hashlib.py b/lib-python/2.7/hashlib.py
--- a/lib-python/2.7/hashlib.py
+++ b/lib-python/2.7/hashlib.py
@@ -136,9 +136,14 @@
__get_hash = __get_openssl_constructor
algorithms_available = algorithms_available.union(
_hashlib.openssl_md_meth_names)
-except ImportError:
+except ImportError as e:
new = __py_new
__get_hash = __get_builtin_constructor
+ # added by PyPy
+ import warnings
+ warnings.warn("The _hashlib module is not available, falling back "
+ "to a much slower implementation (%s)" % str(e),
+ RuntimeWarning)
for __func_name in __always_supported:
# try them all, some may not work due to the OpenSSL
diff --git a/lib-python/2.7/types.py b/lib-python/2.7/types.py
--- a/lib-python/2.7/types.py
+++ b/lib-python/2.7/types.py
@@ -83,9 +83,19 @@
DictProxyType = type(TypeType.__dict__)
NotImplementedType = type(NotImplemented)
-# For Jython, the following two types are identical
+#
+# On CPython, FunctionType.__code__ is a 'getset_descriptor', but
+# FunctionType.__globals__ is a 'member_descriptor', just like app-level
+# slots. On PyPy, all descriptors of built-in types are
+# 'getset_descriptor', but the app-level slots are 'member_descriptor'
+# as well. (On Jython the situation might still be different.)
+#
+# Note that MemberDescriptorType was equal to GetSetDescriptorType in
+# PyPy <= 6.0.
+#
GetSetDescriptorType = type(FunctionType.func_code)
-MemberDescriptorType = type(FunctionType.func_globals)
+class _C(object): __slots__ = 's'
+MemberDescriptorType = type(_C.s)
del sys, _f, _g, _C, _x # Not for export
diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py
--- a/lib_pypy/_ctypes/array.py
+++ b/lib_pypy/_ctypes/array.py
@@ -183,6 +183,7 @@
self._buffer = self._ffiarray(self._length_, autofree=True)
for i, arg in enumerate(args):
self[i] = arg
+ _init_no_arg_ = __init__
def _fix_index(self, index):
if index < 0:
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -120,7 +120,7 @@
raise ValueError(
"Buffer size too small (%d instead of at least %d bytes)"
% (len(buf) + offset, size + offset))
- result = self()
+ result = self._newowninstance_()
dest = result._buffer.buffer
try:
raw_addr = buf._pypy_raw_address()
@@ -131,6 +131,11 @@
memmove(dest, raw_addr, size)
return result
+ def _newowninstance_(self):
+ result = self.__new__(self)
+ result._init_no_arg_()
+ return result
+
class CArgObject(object):
""" simple wrapper around buffer, just for the case of freeing
@@ -162,6 +167,7 @@
def __init__(self, *args, **kwds):
raise TypeError("%s has no type" % (type(self),))
+ _init_no_arg_ = __init__
def _ensure_objects(self):
if '_objects' not in self.__dict__:
diff --git a/lib_pypy/_ctypes/function.py b/lib_pypy/_ctypes/function.py
--- a/lib_pypy/_ctypes/function.py
+++ b/lib_pypy/_ctypes/function.py
@@ -268,6 +268,7 @@
return
raise TypeError("Unknown constructor %s" % (args,))
+ _init_no_arg_ = __init__
def _wrap_callable(self, to_call, argtypes):
def f(*args):
@@ -557,7 +558,7 @@
keepalive, newarg, newargtype = self._conv_param(argtype, defval)
else:
import ctypes
- val = argtype._type_()
+ val = argtype._type_._newowninstance_()
keepalive = None
newarg = ctypes.byref(val)
newargtype = type(newarg)
diff --git a/lib_pypy/_ctypes/pointer.py b/lib_pypy/_ctypes/pointer.py
--- a/lib_pypy/_ctypes/pointer.py
+++ b/lib_pypy/_ctypes/pointer.py
@@ -67,8 +67,11 @@
self._buffer = ffiarray(1, autofree=True)
if value is not None:
self.contents = value
+ def _init_no_arg_(self):
+ self._buffer = ffiarray(1, autofree=True)
self._ffiarray = ffiarray
self.__init__ = __init__
+ self._init_no_arg_ = _init_no_arg_
self._type_ = TP
def _build_ffiargtype(self):
@@ -137,27 +140,21 @@
if not (isinstance(tp, _CDataMeta) and tp._is_pointer_like()):
raise TypeError("cast() argument 2 must be a pointer type, not %s"
% (tp,))
+ result = tp._newowninstance_()
if isinstance(obj, (int, long)):
- result = tp()
result._buffer[0] = obj
return result
elif obj is None:
- result = tp()
return result
elif isinstance(obj, Array):
- ptr = tp.__new__(tp)
- ptr._buffer = tp._ffiarray(1, autofree=True)
- ptr._buffer[0] = obj._buffer
- result = ptr
+ result._buffer[0] = obj._buffer
elif isinstance(obj, bytes):
- result = tp()
result._buffer[0] = buffer(obj)._pypy_raw_address()
return result
elif not (isinstance(obj, _CData) and type(obj)._is_pointer_like()):
raise TypeError("cast() argument 1 must be a pointer, not %s"
% (type(obj),))
else:
- result = tp()
result._buffer[0] = obj._buffer[0]
# The casted objects '_objects' member:
diff --git a/lib_pypy/_ctypes/primitive.py b/lib_pypy/_ctypes/primitive.py
--- a/lib_pypy/_ctypes/primitive.py
+++ b/lib_pypy/_ctypes/primitive.py
@@ -390,11 +390,14 @@
self._buffer = self._ffiarray(1, autofree=True)
if value is not DEFAULT_VALUE:
self.value = value
+ _init_no_arg_ = __init__
def _ensure_objects(self):
- if self._type_ not in 'zZP':
- assert self._objects is None
- return self._objects
+ # No '_objects' is the common case for primitives. Examples
+ # where there is an _objects is if _type in 'zZP', or if
+ # self comes from 'from_buffer(buf)'. See module/test_lib_pypy/
+ # ctypes_test/test_buffers.py: test_from_buffer_keepalive.
+ return getattr(self, '_objects', None)
def _getvalue(self):
return self._buffer[0]
diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py
--- a/lib_pypy/_ctypes/structure.py
+++ b/lib_pypy/_ctypes/structure.py
@@ -281,6 +281,7 @@
self.__setattr__(name, arg)
for name, arg in kwds.items():
self.__setattr__(name, arg)
+ _init_no_arg_ = __init__
def _subarray(self, fieldtype, name):
"""Return a _rawffi array of length 1 whose address is the same as
diff --git a/lib_pypy/cffi/_cffi_errors.h b/lib_pypy/cffi/_cffi_errors.h
--- a/lib_pypy/cffi/_cffi_errors.h
+++ b/lib_pypy/cffi/_cffi_errors.h
@@ -50,7 +50,9 @@
"import sys\n"
"class FileLike:\n"
" def write(self, x):\n"
- " of.write(x)\n"
+ " try:\n"
+ " of.write(x)\n"
+ " except: pass\n"
" self.buf += x\n"
"fl = FileLike()\n"
"fl.buf = ''\n"
diff --git a/lib_pypy/cffi/_cffi_include.h b/lib_pypy/cffi/_cffi_include.h
--- a/lib_pypy/cffi/_cffi_include.h
+++ b/lib_pypy/cffi/_cffi_include.h
@@ -8,20 +8,43 @@
the same works for the other two macros. Py_DEBUG implies them,
but not the other way around.
- Issue #350 is still open: on Windows, the code here causes it to link
- with PYTHON36.DLL (for example) instead of PYTHON3.DLL. A fix was
- attempted in 164e526a5515 and 14ce6985e1c3, but reverted: virtualenv
- does not make PYTHON3.DLL available, and so the "correctly" compiled
- version would not run inside a virtualenv. We will re-apply the fix
- after virtualenv has been fixed for some time. For explanation, see
- issue #355. For a workaround if you want PYTHON3.DLL and don't worry
- about virtualenv, see issue #350. See also 'py_limited_api' in
- setuptools_ext.py.
+ The implementation is messy (issue #350): on Windows, with _MSC_VER,
+ we have to define Py_LIMITED_API even before including pyconfig.h.
+ In that case, we guess what pyconfig.h will do to the macros above,
+ and check our guess after the #include.
+
+ Note that on Windows, with CPython 3.x, you need virtualenv version
+ >= 16.0.0. Older versions don't copy PYTHON3.DLL. As a workaround
+ you can remove the definition of Py_LIMITED_API here.
+
+ See also 'py_limited_api' in cffi/setuptools_ext.py.
*/
#if !defined(_CFFI_USE_EMBEDDING) && !defined(Py_LIMITED_API)
-# include <pyconfig.h>
-# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
-# define Py_LIMITED_API
+# ifdef _MSC_VER
+# if !defined(_DEBUG) && !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
+# define Py_LIMITED_API
+# endif
+# include <pyconfig.h>
+ /* sanity-check: Py_LIMITED_API will cause crashes if any of these
+ are also defined. Normally, the Python file PC/pyconfig.h does not
+ cause any of these to be defined, with the exception that _DEBUG
+ causes Py_DEBUG. Double-check that. */
+# ifdef Py_LIMITED_API
+# if defined(Py_DEBUG)
+# error "pyconfig.h unexpectedly defines Py_DEBUG, but Py_LIMITED_API is set"
+# endif
+# if defined(Py_TRACE_REFS)
+# error "pyconfig.h unexpectedly defines Py_TRACE_REFS, but Py_LIMITED_API is set"
+# endif
+# if defined(Py_REF_DEBUG)
+# error "pyconfig.h unexpectedly defines Py_REF_DEBUG, but Py_LIMITED_API is set"
+# endif
+# endif
+# else
+# include <pyconfig.h>
+# if !defined(Py_DEBUG) && !defined(Py_TRACE_REFS) && !defined(Py_REF_DEBUG)
+# define Py_LIMITED_API
+# endif
# endif
#endif
diff --git a/lib_pypy/cffi/backend_ctypes.py b/lib_pypy/cffi/backend_ctypes.py
--- a/lib_pypy/cffi/backend_ctypes.py
+++ b/lib_pypy/cffi/backend_ctypes.py
@@ -636,6 +636,10 @@
if isinstance(init, bytes):
init = [init[i:i+1] for i in range(len(init))]
else:
+ if isinstance(init, CTypesGenericArray):
+ if (len(init) != len(blob) or
+ not isinstance(init, CTypesArray)):
+ raise TypeError("length/type mismatch: %s" % (init,))
init = tuple(init)
if len(init) > len(blob):
raise IndexError("too many initializers")
diff --git a/lib_pypy/cffi/setuptools_ext.py b/lib_pypy/cffi/setuptools_ext.py
--- a/lib_pypy/cffi/setuptools_ext.py
+++ b/lib_pypy/cffi/setuptools_ext.py
@@ -81,13 +81,8 @@
it doesn't so far, creating troubles. That's why we check
for "not hasattr(sys, 'gettotalrefcount')" (the 2.7 compatible equivalent
of 'd' not in sys.abiflags). (http://bugs.python.org/issue28401)
-
- On Windows, it's better not to use py_limited_api until issue #355
- can be resolved (by having virtualenv copy PYTHON3.DLL). See also
- the start of _cffi_include.h.
"""
- if ('py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount')
- and sys.platform != 'win32'):
+ if 'py_limited_api' not in kwds and not hasattr(sys, 'gettotalrefcount'):
import setuptools
try:
setuptools_major_version = int(setuptools.__version__.partition('.')[0])
diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -39,14 +39,10 @@
"_csv", "_cppyy", "_pypyjson", "_jitlog"
])
-from rpython.jit.backend import detect_cpu
-try:
- if detect_cpu.autodetect().startswith('x86'):
- if not sys.platform.startswith('openbsd'):
- working_modules.add('_vmprof')
- working_modules.add('faulthandler')
-except detect_cpu.ProcessorAutodetectError:
- pass
+import rpython.rlib.rvmprof.cintf
+if rpython.rlib.rvmprof.cintf.IS_SUPPORTED:
+ working_modules.add('_vmprof')
+ working_modules.add('faulthandler')
translation_modules = default_modules.copy()
translation_modules.update([
@@ -318,3 +314,4 @@
parser = to_optparse(config) #, useoptions=["translation.*"])
option, args = parser.parse_args()
print config
+ print working_modules
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -323,7 +323,8 @@
-------------
* Hash randomization (``-R``) `is ignored in PyPy`_. In CPython
- before 3.4 it has `little point`_.
+ before 3.4 it has `little point`_. Both CPython >= 3.4 and PyPy3
+ implement the randomized SipHash algorithm and ignore ``-R``.
* You can't store non-string keys in type objects. For example::
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -7,9 +7,13 @@
.. branch: cppyy-packaging
-Upgrade to backend 1.1.0, improved handling of templated methods and
+Main items: vastly better template resolution and improved performance. In
+detail: upgrade to backend 1.4, improved handling of templated methods and
functions (in particular automatic deduction of types), improved pythonization
-interface, and a range of compatibility fixes for Python3
+interface, range of compatibility fixes for Python3, free functions now take
+fast libffi path when possible, moves for strings (incl. from Python str),
+easier/faster handling of std::vector by numpy, improved and faster object
+identity preservation
.. branch: socket_default_timeout_blockingness
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -1628,7 +1628,7 @@
else:
skip_leading_underscores = False
for name in all:
- if skip_leading_underscores and name[0]=='_':
+ if skip_leading_underscores and name and name[0] == '_':
continue
into_locals[name] = getattr(module, name)
''', filename=__file__)
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -209,8 +209,12 @@
@specialize.arg(2)
def min_max(space, args, implementation_of):
- if not jit.we_are_jitted() or len(args.arguments_w) != 1 and \
- jit.loop_unrolling_heuristic(args.arguments_w, len(args.arguments_w)):
+ # the 'normal' version includes a JIT merge point, which will make a
+ # new loop (from the interpreter or from another JIT loop). If we
+ # give exactly two arguments to the call to max(), or a JIT virtual
+ # list of arguments, then we pick the 'unroll' version with no JIT
+ # merge point.
+ if jit.isvirtual(args.arguments_w) or len(args.arguments_w) == 2:
return min_max_unroll(space, args, implementation_of)
else:
return min_max_normal(space, args, implementation_of)
diff --git a/pypy/module/_cffi_backend/ctypearray.py b/pypy/module/_cffi_backend/ctypearray.py
--- a/pypy/module/_cffi_backend/ctypearray.py
+++ b/pypy/module/_cffi_backend/ctypearray.py
@@ -104,7 +104,15 @@
return self.ctptr
def convert_from_object(self, cdata, w_ob):
- self.convert_array_from_object(cdata, w_ob)
+ if isinstance(w_ob, cdataobj.W_CData) and w_ob.ctype is self:
+ length = w_ob.get_array_length()
+ with w_ob as source:
+ source = rffi.cast(rffi.VOIDP, source)
+ target = rffi.cast(rffi.VOIDP, cdata)
+ size = rffi.cast(rffi.SIZE_T, self.ctitem.size * length)
+ rffi.c_memcpy(target, source, size)
+ else:
+ self.convert_array_from_object(cdata, w_ob)
def convert_to_object(self, cdata):
if self.length < 0:
diff --git a/pypy/module/_cffi_backend/ctypeobj.py b/pypy/module/_cffi_backend/ctypeobj.py
--- a/pypy/module/_cffi_backend/ctypeobj.py
+++ b/pypy/module/_cffi_backend/ctypeobj.py
@@ -51,9 +51,12 @@
def unpack_list_of_float_items(self, ptr, length):
return None
- def pack_list_of_items(self, cdata, w_ob):
+ def pack_list_of_items(self, cdata, w_ob, expected_length):
return False
+ def _within_bounds(self, actual_length, expected_length):
+ return expected_length < 0 or actual_length <= expected_length
+
def newp(self, w_init, allocator):
space = self.space
raise oefmt(space.w_TypeError,
@@ -102,6 +105,11 @@
# ctype 'A' must be a pointer to same type, not cdata
# 'B'", but with A=B, then give instead a different error
# message to try to clear up the confusion
+ if self is w_got.ctype:
+ raise oefmt(space.w_SystemError,
+ "initializer for ctype '%s' is correct, but we get "
+ "an internal mismatch--please report a bug",
+ self.name)
return oefmt(space.w_TypeError,
"initializer for ctype '%s' appears indeed to "
"be '%s', but the types are different (check "
diff --git a/pypy/module/_cffi_backend/ctypeprim.py b/pypy/module/_cffi_backend/ctypeprim.py
--- a/pypy/module/_cffi_backend/ctypeprim.py
+++ b/pypy/module/_cffi_backend/ctypeprim.py
@@ -287,9 +287,10 @@
return res
return None
- def pack_list_of_items(self, cdata, w_ob):
+ def pack_list_of_items(self, cdata, w_ob, expected_length):
int_list = self.space.listview_int(w_ob)
- if int_list is not None:
+ if (int_list is not None and
+ self._within_bounds(len(int_list), expected_length)):
if self.size == rffi.sizeof(rffi.LONG): # fastest path
from rpython.rlib.rrawarray import copy_list_to_raw_array
cdata = rffi.cast(rffi.LONGP, cdata)
@@ -300,7 +301,8 @@
if overflowed != 0:
self._overflow(self.space.newint(overflowed))
return True
- return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob)
+ return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob,
+ expected_length)
class W_CTypePrimitiveUnsigned(W_CTypePrimitive):
@@ -370,15 +372,17 @@
return res
return None
- def pack_list_of_items(self, cdata, w_ob):
+ def pack_list_of_items(self, cdata, w_ob, expected_length):
int_list = self.space.listview_int(w_ob)
- if int_list is not None:
+ if (int_list is not None and
+ self._within_bounds(len(int_list), expected_length)):
overflowed = misc.pack_list_to_raw_array_bounds_unsigned(
int_list, cdata, self.size, self.vrangemax)
if overflowed != 0:
self._overflow(self.space.newint(overflowed))
return True
- return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob)
+ return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob,
+ expected_length)
class W_CTypePrimitiveBool(W_CTypePrimitiveUnsigned):
@@ -466,9 +470,10 @@
return res
return None
- def pack_list_of_items(self, cdata, w_ob):
+ def pack_list_of_items(self, cdata, w_ob, expected_length):
float_list = self.space.listview_float(w_ob)
- if float_list is not None:
+ if (float_list is not None and
+ self._within_bounds(len(float_list), expected_length)):
if self.size == rffi.sizeof(rffi.DOUBLE): # fastest path
from rpython.rlib.rrawarray import copy_list_to_raw_array
cdata = rffi.cast(rffi.DOUBLEP, cdata)
@@ -478,7 +483,8 @@
misc.pack_float_list_to_raw_array(float_list, cdata,
rffi.FLOAT, rffi.FLOATP)
return True
- return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob)
+ return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob,
+ expected_length)
def unpack_ptr(self, w_ctypeptr, ptr, length):
result = self.unpack_list_of_float_items(ptr, length)
@@ -548,13 +554,15 @@
# 'list(array-of-longdouble)' returns a list of cdata objects,
# not a list of floats.
- def pack_list_of_items(self, cdata, w_ob):
+ def pack_list_of_items(self, cdata, w_ob, expected_length):
float_list = self.space.listview_float(w_ob)
- if float_list is not None:
+ if (float_list is not None and
+ self._within_bounds(len(float_list), expected_length)):
misc.pack_float_list_to_raw_array(float_list, cdata,
rffi.LONGDOUBLE, rffi.LONGDOUBLEP)
return True
- return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob)
+ return W_CTypePrimitive.pack_list_of_items(self, cdata, w_ob,
+ expected_length)
@jit.dont_look_inside
def nonzero(self, cdata):
diff --git a/pypy/module/_cffi_backend/ctypeptr.py b/pypy/module/_cffi_backend/ctypeptr.py
--- a/pypy/module/_cffi_backend/ctypeptr.py
+++ b/pypy/module/_cffi_backend/ctypeptr.py
@@ -56,7 +56,7 @@
def _convert_array_from_listview(self, cdata, lst_w):
space = self.space
- if self.length >= 0 and len(lst_w) > self.length:
+ if not self._within_bounds(len(lst_w), self.length):
raise oefmt(space.w_IndexError,
"too many initializers for '%s' (got %d)",
self.name, len(lst_w))
@@ -69,8 +69,8 @@
space = self.space
if (space.isinstance_w(w_ob, space.w_list) or
space.isinstance_w(w_ob, space.w_tuple)):
- if self.ctitem.pack_list_of_items(cdata, w_ob): # fast path
- pass
+ if self.ctitem.pack_list_of_items(cdata, w_ob, self.length):
+ pass # fast path
else:
self._convert_array_from_listview(cdata, space.listview(w_ob))
elif self.accept_str:
diff --git a/pypy/module/_cffi_backend/errorbox.py b/pypy/module/_cffi_backend/errorbox.py
--- a/pypy/module/_cffi_backend/errorbox.py
+++ b/pypy/module/_cffi_backend/errorbox.py
@@ -69,7 +69,10 @@
import sys
class FileLike:
def write(self, x):
- of.write(x)
+ try:
+ of.write(x)
+ except:
+ pass
self.buf += x
fl = FileLike()
fl.buf = ''
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1862,7 +1862,7 @@
def test_newp_copying():
"""Test that we can do newp(<type>, <cdata of the given type>) for most
- types, with the exception of arrays, like in C.
+ types, including same-type arrays.
"""
BInt = new_primitive_type("int")
p = newp(new_pointer_type(BInt), cast(BInt, 42))
@@ -1891,8 +1891,9 @@
a1 = newp(BArray, [1, 2, 3, 4])
py.test.raises(TypeError, newp, BArray, a1)
BArray6 = new_array_type(new_pointer_type(BInt), 6)
- a1 = newp(BArray6, None)
- py.test.raises(TypeError, newp, BArray6, a1)
+ a1 = newp(BArray6, [10, 20, 30])
+ a2 = newp(BArray6, a1)
+ assert list(a2) == [10, 20, 30, 0, 0, 0]
#
s1 = newp(BStructPtr, [42])
s2 = newp(BStructPtr, s1[0])
@@ -3935,8 +3936,8 @@
def test_char_pointer_conversion():
import warnings
- assert __version__.startswith(("1.8", "1.9", "1.10", "1.11", "1.12")), (
- "consider turning the warning into an error")
+ assert __version__.startswith("1."), (
+ "the warning will be an error if we ever release cffi 2.x")
BCharP = new_pointer_type(new_primitive_type("char"))
BIntP = new_pointer_type(new_primitive_type("int"))
BVoidP = new_pointer_type(new_void_type())
diff --git a/pypy/module/_cffi_backend/test/test_fastpath.py b/pypy/module/_cffi_backend/test/test_fastpath.py
--- a/pypy/module/_cffi_backend/test/test_fastpath.py
+++ b/pypy/module/_cffi_backend/test/test_fastpath.py
@@ -267,3 +267,17 @@
assert lst == [1.25, -2.5, 3.75]
if not self.runappdirect:
assert self.get_count() == 1
+
+ def test_too_many_initializers(self):
+ import _cffi_backend
+ ffi = _cffi_backend.FFI()
+ raises(IndexError, ffi.new, "int[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "int[4]", tuple(range(999)))
+ raises(IndexError, ffi.new, "unsigned int[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "float[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "long double[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "char[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "wchar_t[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "_Bool[4]", [10, 20, 30, 40, 50])
+ raises(IndexError, ffi.new, "int[4][4]", [[3,4,5,6]] * 5)
+ raises(IndexError, ffi.new, "int[4][4]", [[3,4,5,6,7]] * 4)
diff --git a/pypy/module/_cffi_backend/test/test_ffi_obj.py b/pypy/module/_cffi_backend/test/test_ffi_obj.py
--- a/pypy/module/_cffi_backend/test/test_ffi_obj.py
+++ b/pypy/module/_cffi_backend/test/test_ffi_obj.py
@@ -563,3 +563,13 @@
assert len(z) == 2
assert ffi.cast("int *", z)[0] == 0x12345
assert list(z) == [u'\U00012345', u'\x00'] # maybe a 2-unichars str
+
+ def test_ffi_array_as_init(self):
+ import _cffi_backend as _cffi1_backend
+ ffi = _cffi1_backend.FFI()
+ p = ffi.new("int[4]", [10, 20, 30, 400])
+ q = ffi.new("int[4]", p)
+ assert list(q) == [10, 20, 30, 400]
+ raises(TypeError, ffi.new, "int[3]", p)
+ raises(TypeError, ffi.new, "int[5]", p)
+ raises(TypeError, ffi.new, "int16_t[4]", p)
diff --git a/pypy/module/_cppyy/capi/loadable_capi.py b/pypy/module/_cppyy/capi/loadable_capi.py
--- a/pypy/module/_cppyy/capi/loadable_capi.py
+++ b/pypy/module/_cppyy/capi/loadable_capi.py
@@ -1,13 +1,18 @@
import os
+
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.rarithmetic import intmask
from rpython.rlib import jit, jit_libffi, libffi, rdynload, objectmodel
from rpython.rlib.rarithmetic import r_singlefloat
from rpython.tool import leakfinder
-from pypy.interpreter.gateway import interp2app
-from pypy.interpreter.error import oefmt
+from pypy.interpreter.error import OperationError, oefmt
+from pypy.interpreter.argument import Arguments
+from pypy.interpreter.gateway import interp2app, interpindirect2app
+from pypy.interpreter.typedef import TypeDef
+from pypy.objspace.std.iterobject import W_AbstractSeqIterObject
+from pypy.module._rawffi.array import W_ArrayInstance
from pypy.module._cffi_backend import ctypefunc, ctypeprim, cdataobj, misc
from pypy.module._cffi_backend import newtype
from pypy.module._cppyy import ffitypes
@@ -23,10 +28,11 @@
class _Arg: # poor man's union
_immutable_ = True
- def __init__(self, tc, h = 0, l = -1, s = '', p = rffi.cast(rffi.VOIDP, 0)):
+ def __init__(self, tc, h = 0, l = -1, d = -1., s = '', p = rffi.cast(rffi.VOIDP, 0)):
self.tc = tc
self._handle = h
self._long = l
+ self._double = d
self._string = s
self._voidp = p
@@ -40,6 +46,11 @@
def __init__(self, val):
_Arg.__init__(self, 'l', l = val)
+class _ArgD(_Arg):
+ _immutable_ = True
+ def __init__(self, val):
+ _Arg.__init__(self, 'd', d = val)
+
class _ArgS(_Arg):
_immutable_ = True
def __init__(self, val):
@@ -89,6 +100,9 @@
assert obj._voidp != rffi.cast(rffi.VOIDP, 0)
data = rffi.cast(rffi.VOIDPP, data)
data[0] = obj._voidp
+ elif obj.tc == 'd':
+ assert isinstance(argtype, ctypeprim.W_CTypePrimitiveFloat)
+ misc.write_raw_float_data(data, rffi.cast(rffi.DOUBLE, obj._double), argtype.size)
else: # only other use is string
assert obj.tc == 's'
n = len(obj._string)
@@ -182,6 +196,7 @@
'call_f' : ([c_method, c_object, c_int, c_voidp], c_float),
'call_d' : ([c_method, c_object, c_int, c_voidp], c_double),
'call_ld' : ([c_method, c_object, c_int, c_voidp], c_ldouble),
+ 'call_nld' : ([c_method, c_object, c_int, c_voidp], c_double),
'call_r' : ([c_method, c_object, c_int, c_voidp], c_voidp),
# call_s actually takes an size_t* as last parameter, but this will do
@@ -236,6 +251,8 @@
'method_prototype' : ([c_scope, c_method, c_int], c_ccharp),
'is_const_method' : ([c_method], c_int),
+ 'get_num_templated_methods': ([c_scope], c_int),
+ 'get_templated_method_name': ([c_scope, c_index], c_ccharp),
'exists_method_template' : ([c_scope, c_ccharp], c_int),
'method_is_template' : ([c_scope, c_index], c_int),
'get_method_template' : ([c_scope, c_ccharp, c_ccharp], c_method),
@@ -272,9 +289,11 @@
'stdstring2charp' : ([c_object, c_voidp], c_ccharp),
'stdstring2stdstring' : ([c_object], c_object),
- 'stdvector_valuetype' : ([c_ccharp], c_ccharp),
- 'stdvector_valuesize' : ([c_ccharp], c_size_t),
+ 'longdouble2double' : ([c_voidp], c_double),
+ 'double2longdouble' : ([c_double, c_voidp], c_void),
+ 'vectorbool_getitem' : ([c_object, c_int], c_int),
+ 'vectorbool_setitem' : ([c_object, c_int, c_int], c_void),
}
# size/offset are backend-specific but fixed after load
@@ -401,7 +420,9 @@
return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_d', args)))
def c_call_ld(space, cppmethod, cppobject, nargs, cargs):
args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)]
- return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args)))
+ #return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args)))
+ # call_nld narrows long double to double
+ return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_nld', args)))
def c_call_r(space, cppmethod, cppobject, nargs, cargs):
args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)]
@@ -561,16 +582,21 @@
def c_is_const_method(space, cppmeth):
return space.bool_w(call_capi(space, 'is_const_method', [_ArgH(cppmeth)]))
+def c_get_num_templated_methods(space, cppscope):
+ return space.int_w(call_capi(space, 'method_is_template', [_ArgH(cppscope.handle)]))
+def c_get_templated_method_name(space, cppscope, index):
+ args = [_ArgH(cppscope.handle), _ArgL(index)]
+ return charp2str_free(space, call_capi(space, 'method_is_template', args))
def c_exists_method_template(space, cppscope, name):
args = [_ArgH(cppscope.handle), _ArgS(name)]
return space.bool_w(call_capi(space, 'exists_method_template', args))
def c_method_is_template(space, cppscope, index):
args = [_ArgH(cppscope.handle), _ArgL(index)]
return space.bool_w(call_capi(space, 'method_is_template', args))
-
def c_get_method_template(space, cppscope, name, proto):
args = [_ArgH(cppscope.handle), _ArgS(name), _ArgS(proto)]
return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method_template', args)))
+
def c_get_global_operator(space, nss, lc, rc, op):
if nss is not None:
args = [_ArgH(nss.handle), _ArgH(lc.handle), _ArgH(rc.handle), _ArgS(op)]
@@ -619,7 +645,7 @@
return space.bool_w(call_capi(space, 'is_enum_data', args))
def c_get_dimension_size(space, cppscope, datamember_index, dim_idx):
args = [_ArgH(cppscope.handle), _ArgL(datamember_index), _ArgL(dim_idx)]
- return space.bool_w(call_capi(space, 'get_dimension_size', args))
+ return space.int_w(call_capi(space, 'get_dimension_size', args))
# misc helpers ---------------------------------------------------------------
def c_strtoll(space, svalue):
@@ -650,24 +676,94 @@
def c_stdstring2stdstring(space, cppobject):
return _cdata_to_cobject(space, call_capi(space, 'stdstring2stdstring', [_ArgH(cppobject)]))
-def c_stdvector_valuetype(space, pystr):
- return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)]))
+def c_longdouble2double(space, addr):
+ return space.float_w(call_capi(space, 'longdouble2double', [_ArgP(addr)]))
+def c_double2longdouble(space, dval, addr):
+ call_capi(space, 'double2longdouble', [_ArgD(dval), _ArgP(addr)])
-def c_stdvector_valuetype(space, pystr):
- return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)]))
-def c_stdvector_valuesize(space, pystr):
- return _cdata_to_size_t(space, call_capi(space, 'stdvector_valuesize', [_ArgS(pystr)]))
+def c_vectorbool_getitem(space, vbool, idx):
+ return call_capi(space, 'vectorbool_getitem', [_ArgH(vbool), _ArgL(idx)])
+def c_vectorbool_setitem(space, vbool, idx, value):
+ call_capi(space, 'vectorbool_setitem', [_ArgH(vbool), _ArgL(idx), _ArgL(value)])
# TODO: factor these out ...
# pythonizations
def stdstring_c_str(space, w_self):
"""Return a python string taking into account \0"""
-
from pypy.module._cppyy import interp_cppyy
cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False)
return space.newtext(c_stdstring2charp(space, cppstr._rawobject))
+def vbool_getindex(space, w_vbool, w_idx):
+ idx = space.getindex_w(w_idx, space.w_IndexError, "std::vector<bool> index")
+ sz = space.len_w(w_vbool)
+ if idx < 0: idx += sz
+ if idx < 0 or idx >= sz:
+ raise IndexError
+ return idx
+
+def vectorbool_getitem(space, w_self, w_idx):
+ """Index a std::vector<bool>, return the value"""
+ from pypy.module._cppyy import interp_cppyy
+ vbool = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False)
+ idx = vbool_getindex(space, w_self, w_idx)
+ item = c_vectorbool_getitem(space, vbool._rawobject, idx)
+ return space.newbool(space.is_true(item))
+
+def vectorbool_setitem(space, w_self, w_idx, w_value):
+ """Index a std::vector<bool>, set the value"""
+ from pypy.module._cppyy import interp_cppyy
+ vbool = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False)
+ idx = vbool_getindex(space, w_self, w_idx)
+ c_vectorbool_setitem(space, vbool._rawobject, idx, int(space.is_true(w_value)))
+
+class W_STLVectorIter(W_AbstractSeqIterObject):
+ # w_seq and index are in base class
+ _immutable_fields_ = ['converter', 'data', 'len', 'stride']
+
+ def __init__(self, space, w_vector):
+ W_AbstractSeqIterObject.__init__(self, w_vector)
+ # TODO: this should live in rpythonize.py or something so that the
+ # imports can move to the top w/o getting circles
+ from pypy.module._cppyy import interp_cppyy
+ assert isinstance(w_vector, interp_cppyy.W_CPPInstance)
+ vector = space.interp_w(interp_cppyy.W_CPPInstance, w_vector)
+
+ v_type = c_resolve_name(space, vector.clsdecl.name+'::value_type')
+ v_size = c_size_of_type(space, v_type)
+
+ if not v_type or not v_size:
+ raise NotImplementedError # fallback on getitem
+
+ from pypy.module._cppyy import converter
+ self.converter = converter.get_converter(space, v_type, '')
+
+ # this 'data' is from the decl, so not the pythonized data from pythonify.py
+ w_arr = space.call_obj_args(vector.clsdecl.get_overload('data'), w_vector, Arguments(space, []))
+ arr = space.interp_w(W_ArrayInstance, w_arr, can_be_None=True)
+ if not arr:
+ raise OperationError(space.w_StopIteration, space.w_None)
+
+ self.data = rffi.cast(rffi.CCHARP, space.uint_w(arr.getbuffer(space)))
+ self.len = space.uint_w(space.call_obj_args(vector.clsdecl.get_overload('size'), w_vector, Arguments(space, [])))
+ self.stride = v_size
+
+ def descr_next(self, space):
+ if self.w_seq is None:
+ raise OperationError(space.w_StopIteration, space.w_None)
+ if self.len <= self.index:
+ self.w_seq = None
+ raise OperationError(space.w_StopIteration, space.w_None)
+ offset = lltype.direct_ptradd(self.data, rffi.cast(rffi.SIZE_T, self.index*self.stride))
+ w_item = self.converter.from_memory(space, space.w_None, rffi.cast(rffi.LONG, offset))
+ self.index += 1
+ return w_item
+
+def stdvector_iter(space, w_self):
+ return W_STLVectorIter(space, w_self)
+
+
# setup pythonizations for later use at run-time
_pythonizations = {}
def register_pythonizations(space):
@@ -678,6 +774,12 @@
### std::string
stdstring_c_str,
+ ### std::vector
+ stdvector_iter,
+
+ ### std::vector<bool>
+ vectorbool_getitem,
+ vectorbool_setitem,
]
for f in allfuncs:
@@ -692,3 +794,10 @@
space.setattr(w_pycppclass, space.newtext("c_str"), _pythonizations["stdstring_c_str"])
_method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str")
_method_alias(space, w_pycppclass, "__str__", "c_str")
+
+ if name.find("std::vector<bool", 0, 16) == 0:
+ space.setattr(w_pycppclass, space.newtext("__getitem__"), _pythonizations["vectorbool_getitem"])
+ space.setattr(w_pycppclass, space.newtext("__setitem__"), _pythonizations["vectorbool_setitem"])
+
+ elif name.find("std::vector", 0, 11) == 0:
+ space.setattr(w_pycppclass, space.newtext("__iter__"), _pythonizations["stdvector_iter"])
diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py
--- a/pypy/module/_cppyy/converter.py
+++ b/pypy/module/_cppyy/converter.py
@@ -1,15 +1,12 @@
import sys
from pypy.interpreter.error import OperationError, oefmt
-
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat
from rpython.rlib import rfloat, rawrefcount
-
from pypy.module._rawffi.interp_rawffi import letter2tp
from pypy.module._rawffi.array import W_ArrayInstance
-
-from pypy.module._cppyy import helper, capi, ffitypes
+from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews
# Converter objects are used to translate between RPython and C++. They are
# defined by the type name for which they provide conversion. Uses are for
@@ -118,7 +115,7 @@
from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
raise FastCallNotPossible
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
self._is_abstract(space)
def to_memory(self, space, w_obj, w_value, offset):
@@ -145,11 +142,12 @@
state = space.fromcache(ffitypes.State)
return state.c_voidp
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
# read access, so no copy needed
address_value = self._get_raw_address(space, w_obj, offset)
address = rffi.cast(rffi.ULONG, address_value)
- return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address)
+ return lowlevelviews.W_LowLevelView(
+ space, letter2tp(space, self.typecode), self.size, address)
def to_memory(self, space, w_obj, w_value, offset):
# copy the full array (uses byte copy for now)
@@ -186,11 +184,12 @@
ba = rffi.cast(rffi.CCHARP, address)
ba[capi.c_function_arg_typeoffset(space)] = 'o'
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
# read access, so no copy needed
address_value = self._get_raw_address(space, w_obj, offset)
address = rffi.cast(rffi.ULONGP, address_value)
- return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address[0])
+ return lowlevelviews.W_LowLevelView(
+ space, letter2tp(space, self.typecode), self.size, address[0])
def to_memory(self, space, w_obj, w_value, offset):
# copy only the pointer value
@@ -212,10 +211,13 @@
x[0] = self._unwrap_object(space, w_obj)
def default_argument_libffi(self, space, address):
+ if not self.valid_default:
+ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
+ raise FastCallNotPossible
x = rffi.cast(self.c_ptrtype, address)
x[0] = self.default
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = self._get_raw_address(space, w_obj, offset)
rffiptr = rffi.cast(self.c_ptrtype, address)
return self._wrap_object(space, rffiptr[0])
@@ -225,7 +227,7 @@
rffiptr = rffi.cast(self.c_ptrtype, address)
rffiptr[0] = self._unwrap_object(space, w_value)
-class ConstRefNumericTypeConverterMixin(NumericTypeConverterMixin):
+class ConstRefNumericTypeConverterMixin(object):
_mixin_ = True
def cffi_type(self, space):
@@ -284,7 +286,7 @@
x = rffi.cast(rffi.LONGP, address)
x[0] = self._unwrap_object(space, w_obj)
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset))
if address[0] == '\x01':
return space.w_True
@@ -309,7 +311,7 @@
x = rffi.cast(self.c_ptrtype, address)
x[0] = self._unwrap_object(space, w_obj)
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset))
return space.newbytes(address[0])
@@ -322,59 +324,92 @@
pass
class FloatConverter(ffitypes.typeid(rffi.FLOAT), FloatTypeConverterMixin, TypeConverter):
- _immutable_fields_ = ['default']
+ _immutable_fields_ = ['default', 'valid_default']
def __init__(self, space, default):
- if default:
+ self.valid_default = False
+ try:
fval = float(rfloat.rstring_to_float(default))
- else:
+ self.valid_default = True
+ except Exception:
fval = float(0.)
- self.default = r_singlefloat(fval)
+ self.default = rffi.cast(rffi.FLOAT, r_singlefloat(fval))
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = self._get_raw_address(space, w_obj, offset)
rffiptr = rffi.cast(self.c_ptrtype, address)
return self._wrap_object(space, rffiptr[0])
-class ConstFloatRefConverter(FloatConverter):
+class ConstFloatRefConverter(ConstRefNumericTypeConverterMixin, FloatConverter):
_immutable_fields_ = ['typecode']
typecode = 'f'
- def cffi_type(self, space):
- state = space.fromcache(ffitypes.State)
- return state.c_voidp
-
- def convert_argument_libffi(self, space, w_obj, address, scratch):
- from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
- raise FastCallNotPossible
-
class DoubleConverter(ffitypes.typeid(rffi.DOUBLE), FloatTypeConverterMixin, TypeConverter):
- _immutable_fields_ = ['default']
+ _immutable_fields_ = ['default', 'valid_default']
def __init__(self, space, default):
- if default:
+ self.valid_default = False
+ try:
self.default = rffi.cast(self.c_type, rfloat.rstring_to_float(default))
- else:
+ self.valid_default = True
+ except Exception:
self.default = rffi.cast(self.c_type, 0.)
class ConstDoubleRefConverter(ConstRefNumericTypeConverterMixin, DoubleConverter):
_immutable_fields_ = ['typecode']
typecode = 'd'
-class LongDoubleConverter(ffitypes.typeid(rffi.LONGDOUBLE), FloatTypeConverterMixin, TypeConverter):
- _immutable_fields_ = ['default']
+class LongDoubleConverter(TypeConverter):
+ _immutable_fields_ = ['default', 'valid_default']
+ typecode = 'g'
def __init__(self, space, default):
- if default:
- fval = float(rfloat.rstring_to_float(default))
- else:
- fval = float(0.)
- self.default = r_longfloat(fval)
+ self.valid_default = False
+ try:
+ # use float() instead of cast with r_longfloat
+ fval = rffi.cast(rffi.DOUBLE, rfloat.rstring_to_float(default))
+ self.valid_default = True
+ except Exception:
+ fval = rffi.cast(rffi.DOUBLE, 0.)
+ #self.default = r_longfloat(fval)
+ self.default = fval
+
+ def convert_argument(self, space, w_obj, address):
+ x = rffi.cast(rffi.VOIDP, address)
+ capi.c_double2longdouble(space, space.float_w(w_obj), x)
+ ba = rffi.cast(rffi.CCHARP, address)
+ ba[capi.c_function_arg_typeoffset(space)] = self.typecode
+
+ def convert_argument_libffi(self, space, w_obj, address, scratch):
+ x = rffi.cast(rffi.VOIDP, address)
+ capi.c_double2longdouble(space, space.float_w(w_obj), x)
+
+ def default_argument_libffi(self, space, address):
+ if not self.valid_default:
+ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
+ raise FastCallNotPossible
+ x = rffi.cast(rffi.VOIDP, address)
+ capi.c_double2longdouble(space, self.default, x)
+
+ def from_memory(self, space, w_obj, offset):
+ address = self._get_raw_address(space, w_obj, offset)
+ rffiptr = rffi.cast(rffi.VOIDP, address)
+ return space.newfloat(capi.c_longdouble2double(space, rffiptr))
+
+ def to_memory(self, space, w_obj, w_value, offset):
+ address = self._get_raw_address(space, w_obj, offset)
+ rffiptr = rffi.cast(rffi.VOIDP, address)
+ capi.c_double2longdouble(space, space.float_w(w_value), rffiptr)
class ConstLongDoubleRefConverter(ConstRefNumericTypeConverterMixin, LongDoubleConverter):
_immutable_fields_ = ['typecode']
typecode = 'g'
+ def convert_argument_libffi(self, space, w_obj, address, scratch):
+ capi.c_double2longdouble(space, space.float_w(w_obj), rffi.cast(rffi.VOIDP, scratch))
+ x = rffi.cast(rffi.VOIDPP, address)
+ x[0] = scratch
+
class CStringConverter(TypeConverter):
def convert_argument(self, space, w_obj, address):
@@ -384,7 +419,7 @@
ba = rffi.cast(rffi.CCHARP, address)
ba[capi.c_function_arg_typeoffset(space)] = 'p'
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = self._get_raw_address(space, w_obj, offset)
charpptr = rffi.cast(rffi.CCHARPP, address)
return space.newtext(rffi.charp2str(charpptr[0]))
@@ -398,13 +433,15 @@
def __init__(self, space, extra):
self.size = extra
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = self._get_raw_address(space, w_obj, offset)
charpptr = rffi.cast(rffi.CCHARP, address)
- strsize = self.size
- if charpptr[self.size-1] == '\0':
- strsize = self.size-1 # rffi will add \0 back
- return space.newtext(rffi.charpsize2str(charpptr, strsize))
+ if 0 <= self.size and self.size != 2**31-1: # cling's code for "unknown" (?)
+ strsize = self.size
+ if charpptr[self.size-1] == '\0':
+ strsize = self.size-1 # rffi will add \0 back
+ return space.newtext(rffi.charpsize2str(charpptr, strsize))
+ return space.newtext(rffi.charp2str(charpptr))
class VoidPtrConverter(TypeConverter):
@@ -429,7 +466,7 @@
x = rffi.cast(rffi.VOIDPP, address)
x[0] = self._unwrap_object(space, w_obj)
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
# returned as a long value for the address (INTPTR_T is not proper
# per se, but rffi does not come with a PTRDIFF_T)
address = self._get_raw_address(space, w_obj, offset)
@@ -438,7 +475,7 @@
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.get_nullptr(space)
shape = letter2tp(space, 'P')
- return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
+ return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval)
def to_memory(self, space, w_obj, w_value, offset):
address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset))
@@ -491,7 +528,7 @@
from pypy.module._cppyy.interp_cppyy import W_CPPInstance
if isinstance(w_obj, W_CPPInstance):
from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_RVALUE
- if w_obj.flags & INSTANCE_FLAGS_IS_RVALUE:
+ if w_obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
# reject moves as all are explicit
raise ValueError("lvalue expected")
if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl):
@@ -523,14 +560,14 @@
from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE
obj = space.interp_w(W_CPPInstance, w_obj)
if obj:
- if obj.flags & INSTANCE_FLAGS_IS_RVALUE:
- obj.flags &= ~INSTANCE_FLAGS_IS_RVALUE
+ if obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
+ obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE
try:
return InstanceRefConverter._unwrap_object(self, space, w_obj)
except Exception:
# TODO: if the method fails on some other converter, then the next
# overload can not be an rvalue anymore
- obj.flags |= INSTANCE_FLAGS_IS_RVALUE
+ obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE
raise
raise oefmt(space.w_ValueError, "object is not an rvalue")
@@ -541,7 +578,7 @@
from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
raise FastCallNotPossible # TODO: by-value is a jit_libffi special case
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset))
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False)
@@ -561,15 +598,11 @@
return capi.C_NULL_OBJECT
raise e
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset))
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False)
- def to_memory(self, space, w_obj, w_value, offset):
- address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset))
- address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value))
-
class InstancePtrPtrConverter(InstancePtrConverter):
typecode = 'o'
@@ -591,12 +624,31 @@
from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
raise FastCallNotPossible
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset))
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.wrap_cppinstance(
space, address, self.clsdecl, do_cast=False, is_ref=True)
+ def to_memory(self, space, w_obj, w_value, offset):
+ # the actual data member is of object* type, but we receive a pointer to that
+ # data member in order to modify its value, so by convention, the internal type
+ # used is object**
+ address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset))
+ from pypy.module._cppyy.interp_cppyy import W_CPPInstance
+ cppinstance = space.interp_w(W_CPPInstance, w_value, can_be_None=True)
+ if cppinstance:
+ rawobject = cppinstance.get_rawobject()
+ offset = capi.c_base_offset(space, cppinstance.clsdecl, self.clsdecl, rawobject, 1)
+ obj_address = capi.direct_ptradd(rawobject, offset)
+ address[0] = rffi.cast(rffi.VOIDP, obj_address);
+ # register the value for potential recycling
+ from pypy.module._cppyy.interp_cppyy import memory_regulator
+ memory_regulator.register(cppinstance)
+ else:
+ raise oefmt(space.w_TypeError,
+ "cannot pass %T instance as %s", w_value, self.clsdecl.name)
+
def finalize_call(self, space, w_obj):
if self.ref_buffer:
set_rawobject(space, w_obj, self.ref_buffer[0])
@@ -606,8 +658,27 @@
lltype.free(self.ref_buffer, flavor='raw')
self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO)
+class InstanceArrayConverter(InstancePtrConverter):
+ _immutable_fields_ = ['size']
+
+ def __init__(self, space, clsdecl, array_size, dimensions):
+ InstancePtrConverter.__init__(self, space, clsdecl)
+ if array_size <= 0 or array_size == 2**31-1: # cling's code for "unknown" (?)
+ self.size = sys.maxint
+ else:
+ self.size = array_size
+ # peel one off as that should be the same as the array size
+ self.dimensions = dimensions[1:]
+
+ def from_memory(self, space, w_obj, offset):
+ address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset))
+ return lowlevelviews.W_ArrayOfInstances(space, self.clsdecl, address, self.size, self.dimensions)
+
+ def to_memory(self, space, w_obj, w_value, offset):
+ self._is_abstract(space)
+
+
class StdStringConverter(InstanceConverter):
-
def __init__(self, space, extra):
from pypy.module._cppyy import interp_cppyy
cppclass = interp_cppyy.scope_byname(space, capi.std_string_name)
@@ -633,6 +704,34 @@
def free_argument(self, space, arg):
capi.c_destruct(space, self.clsdecl, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0]))
+class StdStringMoveConverter(StdStringConverter):
+ def _unwrap_object(self, space, w_obj):
+ # moving is same as by-ref, but have to check that move is allowed
+ moveit_reason = 3
+ from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE
+ try:
+ obj = space.interp_w(W_CPPInstance, w_obj)
+ if obj and obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE:
+ obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE
+ moveit_reason = 1
+ else:
+ moveit_reason = 0
+ except:
+ pass
+
+ if moveit_reason:
+ try:
+ return StdStringConverter._unwrap_object(self, space, w_obj)
+ except Exception:
+ if moveit_reason == 1:
+ # TODO: if the method fails on some other converter, then the next
+ # overload can not be an rvalue anymore
+ obj = space.interp_w(W_CPPInstance, w_obj)
+ obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE
+ raise
+
+ raise oefmt(space.w_ValueError, "object is not an rvalue")
+
class StdStringRefConverter(InstancePtrConverter):
_immutable_fields_ = ['cppclass', 'typecode']
typecode = 'V'
@@ -749,7 +848,7 @@
ba = rffi.cast(rffi.CCHARP, address)
ba[capi.c_function_arg_typeoffset(space)] = self.typecode
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset))
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.wrap_cppinstance(space, address,
@@ -758,7 +857,7 @@
class SmartPtrPtrConverter(SmartPtrConverter):
typecode = 'o'
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
self._is_abstract(space)
def to_memory(self, space, w_obj, w_value, offset):
@@ -770,7 +869,7 @@
class MacroConverter(TypeConverter):
- def from_memory(self, space, w_obj, w_pycppclass, offset):
+ def from_memory(self, space, w_obj, offset):
# TODO: get the actual type info from somewhere ...
address = self._get_raw_address(space, w_obj, offset)
longptr = rffi.cast(rffi.LONGP, address)
@@ -803,20 +902,28 @@
pass
# match of decorated, unqualified type
- compound = helper.compound(name)
+ cpd = helper.compound(name)
clean_name = capi.c_resolve_name(space, helper.clean_type(name))
try:
- return _converters[clean_name+compound](space, default)
+ return _converters[clean_name+cpd](space, default)
except KeyError:
pass
- # arrays
+ # arrays (array_size may be negative, meaning: no size or no size found)
+ array_size = -1
+ if cpd == "[]":
+ array_size = helper.array_size(_name) # uses original arg
+ elif cpd == '*' and ':' in default:
+ # this happens for multi-dimensional arrays: those are described as pointers
+ cpd = "[]"
+ splitpos = default.find(':')
+ if 0 < splitpos: # always true, but needed for annotator
+ array_size = int(default[:splitpos])
+
try:
- # array_index may be negative to indicate no size or no size found
- array_size = helper.array_size(_name) # uses original arg
# TODO: using clean_name here drops const (e.g. const char[] will
# never be seen this way)
- return _a_converters[clean_name+compound](space, array_size)
+ return _a_converters[clean_name+cpd](space, array_size)
except KeyError:
pass
@@ -830,24 +937,28 @@
# check smart pointer type
check_smart = capi.c_smartptr_info(space, clean_name)
if check_smart[0]:
- if compound == '':
+ if cpd == '':
return SmartPtrConverter(space, clsdecl, check_smart[1], check_smart[2])
- elif compound == '*':
+ elif cpd == '*':
return SmartPtrPtrConverter(space, clsdecl, check_smart[1], check_smart[2])
- elif compound == '&':
+ elif cpd == '&':
return SmartPtrRefConverter(space, clsdecl, check_smart[1], check_smart[2])
# fall through: can still return smart pointer in non-smart way
# type check for the benefit of the annotator
- if compound == "*":
+ if cpd == "*":
return InstancePtrConverter(space, clsdecl)
- elif compound == "&":
+ elif cpd == "&":
return InstanceRefConverter(space, clsdecl)
- elif compound == "&&":
+ elif cpd == "&&":
return InstanceMoveConverter(space, clsdecl)
- elif compound == "**":
+ elif cpd in ["**", "*[]", "&*"]:
return InstancePtrPtrConverter(space, clsdecl)
- elif compound == "":
+ elif cpd == "[]" and array_size > 0:
+ # default encodes the dimensions
+ dims = default.split(':')
+ return InstanceArrayConverter(space, clsdecl, array_size, dims)
+ elif cpd == "":
return InstanceConverter(space, clsdecl)
elif "(anonymous)" in name:
# special case: enum w/o a type name
@@ -859,7 +970,7 @@
return FunctionPointerConverter(space, name[pos+2:])
# void* or void converter (which fails on use)
- if 0 <= compound.find('*'):
+ if 0 <= cpd.find('*'):
return VoidPtrConverter(space, default) # "user knows best"
# return a void converter here, so that the class can be build even
@@ -874,8 +985,8 @@
_converters["const float&"] = ConstFloatRefConverter
_converters["double"] = DoubleConverter
_converters["const double&"] = ConstDoubleRefConverter
-#_converters["long double"] = LongDoubleConverter
-#_converters["const long double&"] = ConstLongDoubleRefConverter
+_converters["long double"] = LongDoubleConverter
+_converters["const long double&"] = ConstLongDoubleRefConverter
_converters["const char*"] = CStringConverter
_converters["void*"] = VoidPtrConverter
_converters["void**"] = VoidPtrPtrConverter
@@ -885,6 +996,7 @@
_converters["std::basic_string<char>"] = StdStringConverter
_converters["const std::basic_string<char>&"] = StdStringConverter # TODO: shouldn't copy
_converters["std::basic_string<char>&"] = StdStringRefConverter
+_converters["std::basic_string<char>&&"] = StdStringMoveConverter
_converters["PyObject*"] = PyObjectConverter
@@ -908,7 +1020,12 @@
_immutable_ = True
typecode = c_tc
def __init__(self, space, default):
- self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default))
+ self.valid_default = False
+ try:
+ self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default))
+ self.valid_default = True
+ except Exception:
+ self.default = rffi.cast(self.c_type, 0)
class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter):
_immutable_ = True
for name in names:
@@ -925,7 +1042,12 @@
_immutable_ = True
typecode = c_tc
def __init__(self, space, default):
- self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default))
+ self.valid_default = False
+ try:
+ self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default))
+ self.valid_default = True
+ except Exception:
+ self.default = rffi.cast(self.c_type, 0)
class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter):
_immutable_ = True
for name in names:
@@ -945,7 +1067,12 @@
_immutable_ = True
typecode = c_tc
def __init__(self, space, default):
- self.default = rffi.cast(self.c_type, capi.c_strtoull(space, default))
+ self.valid_default = False
+ try:
+ self.default = rffi.cast(self.c_type, capi.c_strtoull(space, default))
+ self.valid_default = True
+ except Exception:
+ self.default = rffi.cast(self.c_type, 0)
class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter):
_immutable_ = True
for name in names:
@@ -1002,6 +1129,7 @@
("std::basic_string<char>", "string"),
("const std::basic_string<char>&", "const string&"),
("std::basic_string<char>&", "string&"),
+ ("std::basic_string<char>&&", "string&&"),
("PyObject*", "_object*"),
)
diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py
--- a/pypy/module/_cppyy/executor.py
+++ b/pypy/module/_cppyy/executor.py
@@ -1,14 +1,10 @@
import sys
from pypy.interpreter.error import oefmt
-
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib import jit_libffi
-
from pypy.module._rawffi.interp_rawffi import letter2tp
-from pypy.module._rawffi.array import W_Array, W_ArrayInstance
-
-from pypy.module._cppyy import helper, capi, ffitypes
+from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews
# Executor objects are used to dispatch C++ methods. They are defined by their
# return type only: arguments are converted by Converter objects, and Executors
@@ -60,7 +56,7 @@
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.get_nullptr(space)
shape = letter2tp(space, self.typecode)
- return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
+ return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval)
class VoidExecutor(Executor):
@@ -80,9 +76,6 @@
class NumericExecutorMixin(object):
_mixin_ = True
- #def _wrap_object(self, space, obj):
- # return getattr(space, self.wrapper)(obj)
-
def execute(self, space, cppmethod, cppthis, num_args, args):
result = self.c_stubcall(space, cppmethod, cppthis, num_args, args)
return self._wrap_object(space, rffi.cast(self.c_type, result))
@@ -98,19 +91,16 @@
def __init__(self, space, extra):
Executor.__init__(self, space, extra)
self.do_assign = False
- self.item = rffi.cast(self.c_type, 0)
+ self.w_item = space.w_None
def set_item(self, space, w_item):
- self.item = self._unwrap_object(space, w_item)
+ self.w_item = w_item
self.do_assign = True
- #def _wrap_object(self, space, obj):
- # return getattr(space, self.wrapper)(rffi.cast(self.c_type, obj))
-
def _wrap_reference(self, space, rffiptr):
if self.do_assign:
- rffiptr[0] = self.item
- self.do_assign = False
+ rffiptr[0] = rffi.cast(self.c_type, self._unwrap_object(space, self.w_item))
+ self.do_assign = False
return self._wrap_object(space, rffiptr[0]) # all paths, for rtyper
def execute(self, space, cppmethod, cppthis, num_args, args):
@@ -123,6 +113,48 @@
return self._wrap_reference(space,
rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0]))
+class LongDoubleExecutorMixin(object):
+ # Note: not really supported, but returns normal double
+ _mixin_ = True
+
+ def execute(self, space, cppmethod, cppthis, num_args, args):
+ result = self.c_stubcall(space, cppmethod, cppthis, num_args, args)
+ return space.newfloat(result)
+
+ def execute_libffi(self, space, cif_descr, funcaddr, buffer):
+ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
+ raise FastCallNotPossible
+
+class LongDoubleExecutor(ffitypes.typeid(rffi.LONGDOUBLE), LongDoubleExecutorMixin, Executor):
+ _immutable_ = True
+ c_stubcall = staticmethod(capi.c_call_ld)
+
+class LongDoubleRefExecutorMixin(NumericRefExecutorMixin):
+ # Note: not really supported, but returns normal double
+ _mixin_ = True
+
+ def _wrap_reference(self, space, rffiptr):
+ if self.do_assign:
+ capi.c_double2longdouble(space, space.float_w(self.w_item), rffiptr)
+ self.do_assign = False
+ return self.w_item
+ return space.newfloat(capi.c_longdouble2double(space, rffiptr))
+
+ def execute(self, space, cppmethod, cppthis, num_args, args):
+ result = capi.c_call_r(space, cppmethod, cppthis, num_args, args)
+ return self._wrap_reference(space, rffi.cast(self.c_ptrtype, result))
+
+ def execute_libffi(self, space, cif_descr, funcaddr, buffer):
+ jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer)
+ result = rffi.ptradd(buffer, cif_descr.exchange_result)
+ return self._wrap_reference(space,
+ rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0]))
+
+class LongDoubleRefExecutor(ffitypes.typeid(rffi.LONGDOUBLE), LongDoubleRefExecutorMixin, Executor):
+ def cffi_type(self, space):
+ state = space.fromcache(ffitypes.State)
+ return state.c_voidp
+
class CStringExecutor(Executor):
def execute(self, space, cppmethod, cppthis, num_args, args):
@@ -345,6 +377,10 @@
_executors["void*"] = PtrTypeExecutor
_executors["const char*"] = CStringExecutor
+# long double not really supported: narrows to double
+_executors["long double"] = LongDoubleExecutor
+_executors["long double&"] = LongDoubleRefExecutor
+
# special cases (note: 'string' aliases added below)
_executors["constructor"] = ConstructorExecutor
diff --git a/pypy/module/_cppyy/ffitypes.py b/pypy/module/_cppyy/ffitypes.py
--- a/pypy/module/_cppyy/ffitypes.py
+++ b/pypy/module/_cppyy/ffitypes.py
@@ -296,7 +296,8 @@
_immutable_fields_ = ['c_type', 'c_ptrtype', 'typecode']
c_type = rffi.LONGDOUBLE
- c_ptrtype = rffi.LONGDOUBLEP
+ # c_ptrtype = rffi.LONGDOUBLEP # useless type at this point
+ c_ptrtype = rffi.VOIDP
typecode = 'g'
# long double is not really supported ...
@@ -304,7 +305,7 @@
return r_longfloat(space.float_w(w_obj))
def _wrap_object(self, space, obj):
- return space.wrap(obj)
+ return space.newfloat(obj)
def cffi_type(self, space):
state = space.fromcache(State)
diff --git a/pypy/module/_cppyy/helper.py b/pypy/module/_cppyy/helper.py
--- a/pypy/module/_cppyy/helper.py
+++ b/pypy/module/_cppyy/helper.py
@@ -117,16 +117,10 @@
# TODO: perhaps absorb or "pythonify" these operators?
return cppname
-if sys.hexversion < 0x3000000:
- CPPYY__div__ = "__div__"
- CPPYY__idiv__ = "__idiv__"
- CPPYY__long__ = "__long__"
- CPPYY__bool__ = "__nonzero__"
-else:
- CPPYY__div__ = "__truediv__"
- CPPYY__idiv__ = "__itruediv__"
- CPPYY__long__ = "__int__"
- CPPYY__bool__ = "__bool__"
+CPPYY__div__ = "__div__"
+CPPYY__idiv__ = "__idiv__"
+CPPYY__long__ = "__long__"
+CPPYY__bool__ = "__nonzero__"
# _operator_mappings["[]"] = "__setitem__" # depends on return type
# _operator_mappings["+"] = "__add__" # depends on # of args (see __pos__)
diff --git a/pypy/module/_cppyy/include/capi.h b/pypy/module/_cppyy/include/capi.h
--- a/pypy/module/_cppyy/include/capi.h
+++ b/pypy/module/_cppyy/include/capi.h
@@ -63,6 +63,8 @@
double cppyy_call_d(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
RPY_EXTERN
long double cppyy_call_ld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
+ RPY_EXTERN
+ double cppyy_call_nld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
RPY_EXTERN
void* cppyy_call_r(cppyy_method_t method, cppyy_object_t self, int nargs, void* args);
@@ -151,11 +153,15 @@
RPY_EXTERN
char* cppyy_method_signature(cppyy_method_t, int show_formalargs);
RPY_EXTERN
- char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t idx, int show_formalargs);
+ char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t, int show_formalargs);
RPY_EXTERN
int cppyy_is_const_method(cppyy_method_t);
RPY_EXTERN
+ int get_num_templated_methods(cppyy_scope_t scope);
+ RPY_EXTERN
+ char* get_templated_method_name(cppyy_scope_t scope, cppyy_index_t imeth);
+ RPY_EXTERN
int cppyy_exists_method_template(cppyy_scope_t scope, const char* name);
RPY_EXTERN
int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx);
@@ -216,9 +222,14 @@
cppyy_object_t cppyy_stdstring2stdstring(cppyy_object_t ptr);
RPY_EXTERN
- const char* cppyy_stdvector_valuetype(const char* clname);
+ double cppyy_longdouble2double(void*);
RPY_EXTERN
- size_t cppyy_stdvector_valuesize(const char* clname);
+ void cppyy_double2longdouble(double, void*);
+
+ RPY_EXTERN
+ int cppyy_vectorbool_getitem(cppyy_object_t ptr, int idx);
+ RPY_EXTERN
+ void cppyy_vectorbool_setitem(cppyy_object_t ptr, int idx, int value);
#ifdef __cplusplus
}
diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py
--- a/pypy/module/_cppyy/interp_cppyy.py
+++ b/pypy/module/_cppyy/interp_cppyy.py
@@ -24,6 +24,7 @@
INSTANCE_FLAGS_IS_RVALUE = 0x0004
OVERLOAD_FLAGS_USE_FFI = 0x0001
+OVERLOAD_FLAGS_CREATES = 0x0002
FUNCTION_IS_GLOBAL = 0x0001
FUNCTION_IS_STATIC = 0x0001
@@ -41,6 +42,7 @@
'void**' : 100,
'float' : 30,
'double' : 10,
+ 'bool' : 1,
'const string&' : 1, } # solves a specific string ctor overload
from rpython.rlib.listsort import make_timsort_class
@@ -167,6 +169,7 @@
#
# W_CPPOverload: instance methods (base class)
# W_CPPConstructorOverload: constructors
+# W_CPPAbstractCtorOverload: to provent instantiation of abstract classes
# W_CPPStaticOverload: free and static functions
# W_CPPTemplateOverload: templated methods
# W_CPPTemplateStaticOverload: templated free and static functions
@@ -227,8 +230,10 @@
if self.converters is None:
try:
self._setup(cppthis)
- except Exception:
- pass
+ except Exception as e:
+ if self.converters is None:
+ raise oefmt(self.space.w_SystemError,
+ "unable to initialize converters (%s)", str(e))
# attempt to call directly through ffi chain
if useffi and self._funcaddr:
@@ -258,7 +263,8 @@
jit.promote(self)
cif_descr = self.cif_descr
# add extra space for const-ref support (see converter.py)
- buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw')
+ buffer = lltype.malloc(rffi.CCHARP.TO,
+ cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw')
thisoff = 0
try:
if cppthis:
@@ -412,8 +418,10 @@
def priority(self):
total_arg_priority = 0
- for p in [priority.get(arg_type, 0) for arg_type, arg_dflt in self.arg_defs]:
- total_arg_priority += p
+ for arg_type, arg_dflt in self.arg_defs:
+ total_arg_priority += priority.get(arg_type, 0)
+ if '&&' in arg_type:
+ total_arg_priority += 100
return total_arg_priority
@rgc.must_be_light_finalizer
@@ -433,7 +441,7 @@
class CPPSetItem(CPPMethod):
"""Method dispatcher specific to Python's __setitem__ mapped onto C++'s
- operator[](int). The former function takes an extra argument to assign to
+ operator[](T). The former function takes an extra argument to assign to
the return type of the latter."""
_attrs_ = []
@@ -453,6 +461,36 @@
# need forwarding, which the normal instancemethod does not provide, hence this
# derived class.
class MethodWithProps(Method):
+ # set life management of result from the call
+ def fget_creates(self, space):
+ f = space.interp_w(W_CPPOverload, self.w_function)
+ return f.fget_creates(space)
+
+ @unwrap_spec(value=bool)
+ def fset_creates(self, space, value):
+ f = space.interp_w(W_CPPOverload, self.w_function)
+ f.fset_creates(space, value)
+
+ # set ownership policy of arguments (not yet implemented)
+ def fget_mempolicy(self, space):
+ f = space.interp_w(W_CPPOverload, self.w_function)
+ return f.fget_mempolicy(space)
+
+ @unwrap_spec(value=int)
+ def fset_mempolicy(self, space, value):
+ f = space.interp_w(W_CPPOverload, self.w_function)
+ f.fset_mempolicy(space, value)
+
+ # set to release the gil during call (not yet implemented)
+ def fget_release_gil(self, space):
+ f = space.interp_w(W_CPPOverload, self.w_function)
+ return f.fget_release_gil(space)
+
+ @unwrap_spec(value=bool)
+ def fset_release_gil(self, space, value):
+ f = space.interp_w(W_CPPOverload, self.w_function)
+ f.fset_release_gil(space, value)
+
# allow user to determine ffi use rules per overload
def fget_useffi(self, space):
f = space.interp_w(W_CPPOverload, self.w_function)
@@ -468,22 +506,25 @@
__doc__ = """cpp_instancemethod(function, instance, class)
Create an instance method object.""",
- __new__ = interp2app(MethodWithProps.descr_method__new__.im_func),
- __call__ = interp2app(MethodWithProps.descr_method_call),
- __get__ = interp2app(MethodWithProps.descr_method_get),
- im_func = interp_attrproperty_w('w_function', cls=MethodWithProps),
- __func__ = interp_attrproperty_w('w_function', cls=MethodWithProps),
- im_self = interp_attrproperty_w('w_instance', cls=MethodWithProps),
- __self__ = interp_attrproperty_w('w_instance', cls=MethodWithProps),
- im_class = interp_attrproperty_w('w_class', cls=MethodWithProps),
+ __new__ = interp2app(MethodWithProps.descr_method__new__.im_func),
+ __call__ = interp2app(MethodWithProps.descr_method_call),
+ __get__ = interp2app(MethodWithProps.descr_method_get),
+ im_func = interp_attrproperty_w('w_function', cls=MethodWithProps),
+ __func__ = interp_attrproperty_w('w_function', cls=MethodWithProps),
+ im_self = interp_attrproperty_w('w_instance', cls=MethodWithProps),
+ __self__ = interp_attrproperty_w('w_instance', cls=MethodWithProps),
+ im_class = interp_attrproperty_w('w_class', cls=MethodWithProps),
__getattribute__ = interp2app(MethodWithProps.descr_method_getattribute),
- __eq__ = interp2app(MethodWithProps.descr_method_eq),
- __ne__ = descr_generic_ne,
- __hash__ = interp2app(MethodWithProps.descr_method_hash),
- __repr__ = interp2app(MethodWithProps.descr_method_repr),
- __reduce__ = interp2app(MethodWithProps.descr_method__reduce__),
- __weakref__ = make_weakref_descr(MethodWithProps),
- __useffi__ = GetSetProperty(MethodWithProps.fget_useffi, MethodWithProps.fset_useffi),
+ __eq__ = interp2app(MethodWithProps.descr_method_eq),
+ __ne__ = descr_generic_ne,
+ __hash__ = interp2app(MethodWithProps.descr_method_hash),
+ __repr__ = interp2app(MethodWithProps.descr_method_repr),
+ __reduce__ = interp2app(MethodWithProps.descr_method__reduce__),
+ __weakref__ = make_weakref_descr(MethodWithProps),
+ __creates__ = GetSetProperty(MethodWithProps.fget_creates, MethodWithProps.fset_creates),
+ __mempolicy__ = GetSetProperty(MethodWithProps.fget_mempolicy, MethodWithProps.fset_mempolicy),
+ __release_gil__ = GetSetProperty(MethodWithProps.fget_release_gil, MethodWithProps.fset_release_gil),
+ __useffi__ = GetSetProperty(MethodWithProps.fget_useffi, MethodWithProps.fset_useffi),
)
MethodWithProps.typedef.acceptable_as_base_class = False
@@ -505,6 +546,9 @@
def descr_get(self, w_obj, w_cls=None):
"""functionobject.__get__(obj[, type]) -> method"""
# TODO: check validity of w_cls if given
+ # TODO: this does not work for Python 3, which does not have
+ # unbound methods (probably no common code possible, see also
+ # pypy/interpreter/function.py)
space = self.space
asking_for_bound = (space.is_none(w_cls) or
not space.is_w(w_obj, space.w_None) or
@@ -512,7 +556,7 @@
if asking_for_bound:
return MethodWithProps(space, self, w_obj, w_cls)
else:
- return self # unbound methods don't exist in Python 3, return self
+ return MethodWithProps(space, self, None, w_cls)
@unwrap_spec(args_w='args_w')
def call_args(self, args_w):
@@ -543,7 +587,12 @@
for i in range(len(self.functions)):
cppyyfunc = self.functions[i]
try:
- return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI)
+ w_result = cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI)
+ if self.flags & OVERLOAD_FLAGS_CREATES:
+ if isinstance(w_result, W_CPPInstance):
+ cppinstance = self.space.interp_w(W_CPPInstance, w_result)
+ cppinstance.fset_python_owns(self.space, self.space.w_True)
+ return w_result
except Exception:
pass
@@ -556,6 +605,7 @@
for i in range(len(self.functions)):
cppyyfunc = self.functions[i]
try:
+ # no need to set ownership on the return value, as none of the methods execute
return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI)
except OperationError as e:
# special case if there's just one function, to prevent clogging the error message
@@ -584,6 +634,43 @@
sig += '\n'+self.functions[i].prototype()
return self.space.newtext(sig)
+ @unwrap_spec(signature='text')
+ def mp_overload(self, signature):
+ sig = '(%s)' % signature
+ for f in self.functions:
+ if f.signature(False) == sig:
+ if isinstance(self, W_CPPStaticOverload):
+ return W_CPPStaticOverload(self.space, self.scope, [f])
+ return W_CPPOverload(self.space, self.scope, [f])
+ raise oefmt(self.space.w_LookupError, "signature '%s' not found", signature)
+
+ # set life management of result from the call
+ def fget_creates(self, space):
+ return space.newbool(bool(self.flags & OVERLOAD_FLAGS_CREATES))
+
+ @unwrap_spec(value=bool)
+ def fset_creates(self, space, value):
+ if space.is_true(value):
+ self.flags |= OVERLOAD_FLAGS_CREATES
+ else:
+ self.flags &= ~OVERLOAD_FLAGS_CREATES
+
+ # set ownership policy of arguments (not yet implemented)
+ def fget_mempolicy(self, space):
+ return space.newint(0)
+
+ @unwrap_spec(value=int)
+ def fset_mempolicy(self, space, value):
+ pass
+
+ # set to release the gil during call (not yet implemented)
+ def fget_release_gil(self, space):
+ return space.newbool(True)
+
+ @unwrap_spec(value=bool)
+ def fset_release_gil(self, space, value):
+ pass
+
# allow user to determine ffi use rules per overload
def fget_useffi(self, space):
return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
@@ -607,10 +694,14 @@
W_CPPOverload.typedef = TypeDef(
'CPPOverload',
- __get__ = interp2app(W_CPPOverload.descr_get),
- __call__ = interp2app(W_CPPOverload.call_args),
- __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi),
- __doc__ = GetSetProperty(W_CPPOverload.fget_doc)
+ __get__ = interp2app(W_CPPOverload.descr_get),
+ __call__ = interp2app(W_CPPOverload.call_args),
+ __creates__ = GetSetProperty(W_CPPOverload.fget_creates, W_CPPOverload.fset_creates),
+ __mempolicy__ = GetSetProperty(W_CPPOverload.fget_mempolicy, W_CPPOverload.fset_mempolicy),
+ __release_gil__ = GetSetProperty(W_CPPOverload.fget_release_gil, W_CPPOverload.fset_release_gil),
+ __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi),
+ __overload__ = interp2app(W_CPPOverload.mp_overload),
+ __doc__ = GetSetProperty(W_CPPOverload.fget_doc)
)
@@ -632,21 +723,21 @@
@unwrap_spec(args_w='args_w')
def call_args(self, args_w):
jit.promote(self)
- #if isinstance(args_w[0], W_CPPInstance):
- # free function used as bound method, leave in place
return self.call_impl(capi.C_NULL_OBJECT, args_w)
- # free functions are implemented as methods of 'namespace' classes, remove 'instance'
- #return self.call_impl(capi.C_NULL_OBJECT, args_w[1:])
def __repr__(self):
return "W_CPPStaticOverload(%s)" % [f.prototype() for f in self.functions]
W_CPPStaticOverload.typedef = TypeDef(
'CPPStaticOverload',
- __get__ = interp2app(W_CPPStaticOverload.descr_get),
- __call__ = interp2app(W_CPPStaticOverload.call_args),
- __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi),
- __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc)
+ __get__ = interp2app(W_CPPStaticOverload.descr_get),
+ __call__ = interp2app(W_CPPStaticOverload.call_args),
+ __creates__ = GetSetProperty(W_CPPStaticOverload.fget_creates, W_CPPStaticOverload.fset_creates),
+ __mempolicy__ = GetSetProperty(W_CPPStaticOverload.fget_mempolicy, W_CPPStaticOverload.fset_mempolicy),
+ __release_gil__ = GetSetProperty(W_CPPStaticOverload.fget_release_gil, W_CPPStaticOverload.fset_release_gil),
+ __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi),
+ __overload__ = interp2app(W_CPPStaticOverload.mp_overload),
+ __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc)
)
@@ -660,11 +751,6 @@
@unwrap_spec(args_w='args_w')
def call_args(self, args_w):
jit.promote(self)
- # TODO: factor out the following:
- if capi.c_is_abstract(self.space, self.scope.handle):
- raise oefmt(self.space.w_TypeError,
- "cannot instantiate abstract class '%s'",
- self.scope.name)
cppinstance = self.space.interp_w(W_CPPInstance, args_w[0])
w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle), args_w[1:])
newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result))
@@ -677,15 +763,34 @@
W_CPPConstructorOverload.typedef = TypeDef(
'CPPConstructorOverload',
- __get__ = interp2app(W_CPPConstructorOverload.descr_get),
- __call__ = interp2app(W_CPPConstructorOverload.call_args),
- __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc)
+ __get__ = interp2app(W_CPPConstructorOverload.descr_get),
+ __call__ = interp2app(W_CPPConstructorOverload.call_args),
+ __overload__ = interp2app(W_CPPConstructorOverload.mp_overload),
+ __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc)
More information about the pypy-commit
mailing list