Author: Matti Picus <matti.pi...@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) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit