diff options
author | Matti Picus <matti.picus@gmail.com> | 2018-04-24 09:05:26 +0300 |
---|---|---|
committer | Matti Picus <matti.picus@gmail.com> | 2018-04-24 09:05:26 +0300 |
commit | 099f9a7a22285f3f6cb17601759eacc86ed4211a (patch) | |
tree | 5388e681f52d5eaf0edb06eb3677a77492dc0d32 | |
parent | merge py3.5 into release (diff) | |
parent | merge default into branch (diff) | |
download | pypy-099f9a7a22285f3f6cb17601759eacc86ed4211a.tar.gz pypy-099f9a7a22285f3f6cb17601759eacc86ed4211a.tar.bz2 pypy-099f9a7a22285f3f6cb17601759eacc86ed4211a.zip |
merge py3.5 into releaserelease-pypy3.5-v6.0.0
36 files changed, 621 insertions, 197 deletions
@@ -247,6 +247,7 @@ copyrighted by one or more of the following people and organizations: Lukas Vacek Omer Katz Jacek Generowicz + Tomasz Dziopa Sylvain Thenault Jakub Stasiak Andrew Dalke @@ -307,6 +308,7 @@ copyrighted by one or more of the following people and organizations: Yury V. Zaytsev florinpapa Anders Sigfridsson + Matt Jackson Nikolay Zinov rafalgalczynski@gmail.com Joshua Gilbert diff --git a/lib-python/3/datetime.py b/lib-python/3/datetime.py index 33a9a892ae..7b9bcfd6c0 100644 --- a/lib-python/3/datetime.py +++ b/lib-python/3/datetime.py @@ -7,6 +7,9 @@ time zone and DST data sources. import time as _time import math as _math +# for cpyext, use these as base classes +from __pypy__._pypydatetime import dateinterop, deltainterop, timeinterop + def _cmp(x, y): return 0 if x == y else 1 if x > y else -1 @@ -316,7 +319,7 @@ def _divide_and_round(a, b): return q -class timedelta: +class timedelta(deltainterop): """Represent the difference between two datetime objects. Supported operators: @@ -429,7 +432,7 @@ class timedelta: if abs(d) > 999999999: raise OverflowError("timedelta # of days is too large: %d" % d) - self = object.__new__(cls) + self = deltainterop.__new__(cls) self._days = d self._seconds = s self._microseconds = us @@ -638,7 +641,7 @@ timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, microseconds=999999) timedelta.resolution = timedelta(microseconds=1) -class date: +class date(dateinterop): """Concrete date type. Constructors: @@ -678,12 +681,12 @@ class date: if month is None and isinstance(year, bytes) and len(year) == 4 and \ 1 <= year[2] <= 12: # Pickle support - self = object.__new__(cls) + self = dateinterop.__new__(cls) self.__setstate(year) self._hashcode = -1 return self year, month, day = _check_date_fields(year, month, day) - self = object.__new__(cls) + self = dateinterop.__new__(cls) self._year = year self._month = month self._day = day @@ -1008,7 +1011,7 @@ class tzinfo: _tzinfo_class = tzinfo -class time: +class time(timeinterop): """Time with time zone. Constructors: @@ -1044,14 +1047,14 @@ class time: """ if isinstance(hour, bytes) and len(hour) == 6 and hour[0] < 24: # Pickle support - self = object.__new__(cls) + self = timeinterop.__new__(cls) self.__setstate(hour, minute or None) self._hashcode = -1 return self hour, minute, second, microsecond = _check_time_fields( hour, minute, second, microsecond) _check_tzinfo_arg(tzinfo) - self = object.__new__(cls) + self = timeinterop.__new__(cls) self._hour = hour self._minute = minute self._second = second @@ -1329,7 +1332,7 @@ class datetime(date): microsecond=0, tzinfo=None): if isinstance(year, bytes) and len(year) == 10 and 1 <= year[2] <= 12: # Pickle support - self = object.__new__(cls) + self = dateinterop.__new__(cls) self.__setstate(year, month) self._hashcode = -1 return self @@ -1337,7 +1340,7 @@ class datetime(date): hour, minute, second, microsecond = _check_time_fields( hour, minute, second, microsecond) _check_tzinfo_arg(tzinfo) - self = object.__new__(cls) + self = dateinterop.__new__(cls) self._year = year self._month = month self._day = day diff --git a/lib_pypy/_cffi_ssl/README.md b/lib_pypy/_cffi_ssl/README.md index e2a56e6d5b..045f2b4228 100644 --- a/lib_pypy/_cffi_ssl/README.md +++ b/lib_pypy/_cffi_ssl/README.md @@ -14,6 +14,8 @@ NOTE: currently, we have the following changes: * ``_cffi_src/openssl/x509_vfy.py`` for issue #2605 (ca4d0c90f5a1) +* ``_cffi_src/openssl/pypy_win32_extra.py`` for Win32-only functionality like ssl.enum_certificates() + # Tests? diff --git a/lib_pypy/_cffi_ssl/_cffi_src/openssl/pypy_win32_extra.py b/lib_pypy/_cffi_ssl/_cffi_src/openssl/pypy_win32_extra.py new file mode 100644 index 0000000000..36abad1e32 --- /dev/null +++ b/lib_pypy/_cffi_ssl/_cffi_src/openssl/pypy_win32_extra.py @@ -0,0 +1,84 @@ +# +# An extra bit of logic for the Win32-only functionality that is missing from the +# version from cryptography. +# + +import sys + +INCLUDES = """ +#include <Wincrypt.h> +""" + +TYPES = """ +typedef ... *HCERTSTORE; +typedef ... *HCRYPTPROV_LEGACY; + +typedef struct { + DWORD dwCertEncodingType; + BYTE *pbCertEncoded;
+ DWORD cbCertEncoded; + ...; +} CERT_CONTEXT, *PCCERT_CONTEXT; + +typedef struct { + DWORD dwCertEncodingType;
+ BYTE *pbCrlEncoded;
+ DWORD cbCrlEncoded; + ...; +} CRL_CONTEXT, *PCCRL_CONTEXT; + +typedef struct { + DWORD cUsageIdentifier;
+ LPSTR *rgpszUsageIdentifier; + ...; +} CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE; +""" + +FUNCTIONS = """ +HCERTSTORE WINAPI CertOpenStore(
+ LPCSTR lpszStoreProvider,
+ DWORD dwMsgAndCertEncodingType,
+ HCRYPTPROV_LEGACY hCryptProv,
+ DWORD dwFlags,
+ const char *pvPara
+); +PCCERT_CONTEXT WINAPI CertEnumCertificatesInStore(
+ HCERTSTORE hCertStore,
+ PCCERT_CONTEXT pPrevCertContext
+); +BOOL WINAPI CertFreeCertificateContext(
+ PCCERT_CONTEXT pCertContext
+); +BOOL WINAPI CertFreeCRLContext(
+ PCCRL_CONTEXT pCrlContext
+);
+BOOL WINAPI CertCloseStore(
+ HCERTSTORE hCertStore,
+ DWORD dwFlags
+); +BOOL WINAPI CertGetEnhancedKeyUsage(
+ PCCERT_CONTEXT pCertContext,
+ DWORD dwFlags,
+ PCERT_ENHKEY_USAGE pUsage,
+ DWORD *pcbUsage
+); +PCCRL_CONTEXT WINAPI CertEnumCRLsInStore(
+ HCERTSTORE hCertStore,
+ PCCRL_CONTEXT pPrevCrlContext
+); +""" + +MACROS = """ +#define CERT_STORE_READONLY_FLAG ... +#define CERT_SYSTEM_STORE_LOCAL_MACHINE ... +#define CRYPT_E_NOT_FOUND ... +#define CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG ... +#define CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG ... +#define X509_ASN_ENCODING ... +#define PKCS_7_ASN_ENCODING ... + +static const LPCSTR CERT_STORE_PROV_SYSTEM_A; +""" + +CUSTOMIZATIONS = """ +""" diff --git a/lib_pypy/_cffi_ssl/_stdssl/__init__.py b/lib_pypy/_cffi_ssl/_stdssl/__init__.py index 81ecfb1747..eff9b7aefd 100644 --- a/lib_pypy/_cffi_ssl/_stdssl/__init__.py +++ b/lib_pypy/_cffi_ssl/_stdssl/__init__.py @@ -24,6 +24,7 @@ from select import select from enum import IntEnum as _IntEnum if sys.platform == 'win32': + from _cffi_ssl._stdssl.win32_extra import enum_certificates, enum_crls HAVE_POLL = False else: from select import poll, POLLIN, POLLOUT diff --git a/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py b/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py new file mode 100644 index 0000000000..689e13057f --- /dev/null +++ b/lib_pypy/_cffi_ssl/_stdssl/win32_extra.py @@ -0,0 +1,101 @@ +from _pypy_openssl import lib, ffi +
+
+def enum_certificates(store_name):
+ """Retrieve certificates from Windows' cert store.
+
+store_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide
+more cert storages, too. The function returns a list of (bytes,
+encoding_type, trust) tuples. The encoding_type flag can be interpreted
+with X509_ASN_ENCODING or PKCS_7_ASN_ENCODING. The trust setting is either
+a set of OIDs or the boolean True.
+ """
+ hStore = lib.CertOpenStore(lib.CERT_STORE_PROV_SYSTEM_A, 0, ffi.NULL,
+ lib.CERT_STORE_READONLY_FLAG | lib.CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ bytes(store_name, "ascii"))
+ if hStore == ffi.NULL:
+ raise WindowsError(*ffi.getwinerror())
+
+ result = []
+ pCertCtx = ffi.NULL
+ try:
+ while True:
+ pCertCtx = lib.CertEnumCertificatesInStore(hStore, pCertCtx)
+ if pCertCtx == ffi.NULL:
+ break
+ cert = ffi.buffer(pCertCtx.pbCertEncoded, pCertCtx.cbCertEncoded)[:]
+ enc = certEncodingType(pCertCtx.dwCertEncodingType)
+ keyusage = parseKeyUsage(pCertCtx, lib.CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG)
+ if keyusage is True:
+ keyusage = parseKeyUsage(pCertCtx, lib.CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG)
+ result.append((cert, enc, keyusage))
+ finally:
+ if pCertCtx != ffi.NULL:
+ lib.CertFreeCertificateContext(pCertCtx)
+ if not lib.CertCloseStore(hStore, 0):
+ # This error case might shadow another exception.
+ raise WindowsError(*ffi.getwinerror())
+ return result
+
+
+def enum_crls(store_name):
+ """Retrieve CRLs from Windows' cert store.
+
+store_name may be one of 'CA', 'ROOT' or 'MY'. The system may provide
+more cert storages, too. The function returns a list of (bytes,
+encoding_type) tuples. The encoding_type flag can be interpreted with
+X509_ASN_ENCODING or PKCS_7_ASN_ENCODING."""
+ hStore = lib.CertOpenStore(lib.CERT_STORE_PROV_SYSTEM_A, 0, ffi.NULL,
+ lib.CERT_STORE_READONLY_FLAG | lib.CERT_SYSTEM_STORE_LOCAL_MACHINE,
+ bytes(store_name, "ascii"))
+ if hStore == ffi.NULL:
+ raise WindowsError(*ffi.getwinerror())
+
+ result = []
+ pCrlCtx = ffi.NULL
+ try:
+ while True:
+ pCrlCtx = lib.CertEnumCRLsInStore(hStore, pCrlCtx)
+ if pCrlCtx == ffi.NULL:
+ break
+ crl = ffi.buffer(pCrlCtx.pbCrlEncoded, pCrlCtx.cbCrlEncoded)[:]
+ enc = certEncodingType(pCrlCtx.dwCertEncodingType)
+ result.append((crl, enc))
+ finally:
+ if pCrlCtx != ffi.NULL:
+ lib.CertFreeCRLContext(pCrlCtx)
+ if not lib.CertCloseStore(hStore, 0):
+ # This error case might shadow another exception.
+ raise WindowsError(*ffi.getwinerror())
+ return result
+
+
+def certEncodingType(encodingType):
+ if encodingType == lib.X509_ASN_ENCODING:
+ return "x509_asn"
+ if encodingType == lib.PKCS_7_ASN_ENCODING:
+ return "pkcs_7_asn"
+ return encodingType
+
+def parseKeyUsage(pCertCtx, flags):
+ pSize = ffi.new("DWORD *")
+ if not lib.CertGetEnhancedKeyUsage(pCertCtx, flags, ffi.NULL, pSize):
+ error_with_message = ffi.getwinerror()
+ if error_with_message[0] == lib.CRYPT_E_NOT_FOUND:
+ return True
+ raise WindowsError(*error_with_message)
+
+ pUsageMem = ffi.new("char[]", pSize[0])
+ pUsage = ffi.cast("PCERT_ENHKEY_USAGE", pUsageMem)
+ if not lib.CertGetEnhancedKeyUsage(pCertCtx, flags, pUsage, pSize):
+ error_with_message = ffi.getwinerror()
+ if error_with_message[0] == lib.CRYPT_E_NOT_FOUND:
+ return True
+ raise WindowsError(*error_with_message)
+
+ retval = set()
+ for i in range(pUsage.cUsageIdentifier):
+ if pUsage.rgpszUsageIdentifier[i]:
+ oid = ffi.string(pUsage.rgpszUsageIdentifier[i]).decode('ascii')
+ retval.add(oid)
+ return retval
diff --git a/lib_pypy/_ssl/__init__.py b/lib_pypy/_ssl/__init__.py index d5933bf5fd..ca201981cd 100644 --- a/lib_pypy/_ssl/__init__.py +++ b/lib_pypy/_ssl/__init__.py @@ -16,12 +16,14 @@ if hasattr(_stdssl, 'RAND_egd'): RAND_egd = builtinify(RAND_egd) import sys -if sys.platform == "win32" and 'enum_certificates' not in globals(): - def enum_certificates(*args, **kwds): - import warnings - warnings.warn("ssl.enum_certificates() is not implemented") - return [] - def enum_crls(*args, **kwds): - import warnings - warnings.warn("ssl.enum_crls() is not implemented") - return [] +if sys.platform == "win32": + if 'enum_certificates' not in globals(): + def enum_certificates(*args, **kwds): + import warnings + warnings.warn("ssl.enum_certificates() is not implemented") + return [] + if 'enum_crls' not in globals(): + def enum_crls(*args, **kwds): + import warnings + warnings.warn("ssl.enum_crls() is not implemented") + return [] diff --git a/lib_pypy/_ssl_build.py b/lib_pypy/_ssl_build.py index 7fb1cd3e9f..bd33a7e599 100644 --- a/lib_pypy/_ssl_build.py +++ b/lib_pypy/_ssl_build.py @@ -5,6 +5,11 @@ sys.modules['_cffi_src'] = _cffi_src from _cffi_ssl._cffi_src.build_openssl import (build_ffi_for_binding, _get_openssl_libraries, extra_link_args, compiler_type) +if sys.platform == "win32": + pypy_win32_extra = ["pypy_win32_extra"] +else: + pypy_win32_extra = [] + ffi = build_ffi_for_binding( module_name="_pypy_openssl", module_prefix="_cffi_src.openssl.", @@ -44,10 +49,10 @@ ffi = build_ffi_for_binding( "x509_vfy", "pkcs7", "callbacks", - ], + ] + pypy_win32_extra, libraries=_get_openssl_libraries(sys.platform), extra_link_args=extra_link_args(compiler_type()), ) if __name__ == '__main__': - ffi.compile() + ffi.compile(verbose=True) diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst index 91e62bc6c7..0069d69c3f 100644 --- a/pypy/doc/contributor.rst +++ b/pypy/doc/contributor.rst @@ -214,6 +214,7 @@ Contributors Lukas Vacek Omer Katz Jacek Generowicz + Tomasz Dziopa Sylvain Thenault Jakub Stasiak Andrew Dalke @@ -274,6 +275,7 @@ Contributors Yury V. Zaytsev florinpapa Anders Sigfridsson + Matt Jackson Nikolay Zinov rafalgalczynski@gmail.com Joshua Gilbert diff --git a/pypy/doc/release-v6.0.0.rst b/pypy/doc/release-v6.0.0.rst index 8a8cb893fb..53e6931797 100644 --- a/pypy/doc/release-v6.0.0.rst +++ b/pypy/doc/release-v6.0.0.rst @@ -20,16 +20,18 @@ getting started writing code. We have improved our parser to emit more friendly The GC now has `hooks`_ to gain more insights into its performance -The Windows PyPy3.5 release is still considered beta-quality. There are open -issues with unicode handling especially around system calls and c-extensions. - The Matplotlib TkAgg backend now works with PyPy, as do pygame and pygobject_. +We updated the `cffi`_ module included in PyPy to version 1.11.5, and the +`cppyy`_ backend to 0.6.0. Please use these to wrap your C and C++ code, +respectively, for a JIT friendly experience. + As always, this release is 100% compatible with the previous one and fixed several issues and bugs raised by the growing community of PyPy users. We strongly recommend updating. -We updated the cffi module included in PyPy to version 1.11.5 +The Windows PyPy3.5 release is still considered beta-quality. There are open +issues with unicode handling especially around system calls and c-extensions. The utf8 branch that changes internal representation of unicode to utf8 did not make it into the release, so there is still more goodness coming. We also @@ -56,6 +58,8 @@ on pypy, or general `help`_ with making RPython's JIT even better. .. _pygobject: https://lazka.github.io/posts/2018-04_pypy-pygobject/index.html .. _`syntax errors`: https://morepypy.blogspot.com/2018/04/improving-syntaxerror-in-pypy.html .. _`hooks`: gc_info.html#gc-hooks +.. _`cffi`: http://cffi.readthedocs.io +.. _`cppyy`: https://cppyy.readthedocs.io What is PyPy? ============= diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst index 0a1e683bdc..444962b691 100644 --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -3,7 +3,7 @@ What's new in PyPy2.7 6.0+ ========================== .. this is a revision shortly after release-pypy-6.0.0 -.. startrev: ad79cc0ce9a8 +.. startrev: e50e11af23f1 diff --git a/pypy/doc/whatsnew-pypy2-6.0.0.rst b/pypy/doc/whatsnew-pypy2-6.0.0.rst index fa781af23d..61a4e36b33 100644 --- a/pypy/doc/whatsnew-pypy2-6.0.0.rst +++ b/pypy/doc/whatsnew-pypy2-6.0.0.rst @@ -125,4 +125,8 @@ Introduce GC hooks, as documented in doc/gc_info.rst .. branch: gc-hook-better-timestamp -Improve GC hooksd +Improve GC hooks + +.. branch: cppyy-packaging + +Update backend to 0.6.0 and support exceptions through wrappers diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py index f75681128a..943eef1f36 100644 --- a/pypy/interpreter/executioncontext.py +++ b/pypy/interpreter/executioncontext.py @@ -479,11 +479,17 @@ class AbstractActionFlag(object): # 'action.fire()' happens to be called any time before # the corresponding perform(), the fire() has no # effect---which is the effect we want, because - # perform() will be called anyway. + # perform() will be called anyway. All such pending + # actions with _fired == True are still inside the old + # chained list. As soon as we reset _fired to False, + # we also reset _next to None and we are ready for + # another fire(). while action is not None: + next_action = action._next + action._next = None action._fired = False action.perform(ec, frame) - action._next, action = None, action._next + action = next_action self.action_dispatcher = action_dispatcher diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py index fa8b970308..2e8ac663f7 100644 --- a/pypy/interpreter/test/test_executioncontext.py +++ b/pypy/interpreter/test/test_executioncontext.py @@ -66,6 +66,47 @@ class TestExecutionContext: """) assert events == ['one'] + def test_fire_inside_perform(self): + # test what happens if we call AsyncAction.fire() while we are in the + # middle of an AsyncAction.perform(). In particular, this happens when + # PyObjectDeallocAction.fire() is called by rawrefcount: see issue + # 2805 + events = [] + + class Action1(executioncontext.AsyncAction): + _count = 0 + + def perform(self, ec, frame): + events.append('one') + if self._count == 0: + # a1 is no longer in the queue, so it will be enqueued + a1.fire() + # + # a2 is still in the queue, so the fire() is ignored and + # it's performed in its normal order, i.e. BEFORE a3 + a2.fire() + self._count += 1 + + class Action2(executioncontext.AsyncAction): + def perform(self, ec, frame): + events.append('two') + + class Action3(executioncontext.AsyncAction): + def perform(self, ec, frame): + events.append('three') + + space = self.space + a1 = Action1(space) + a2 = Action2(space) + a3 = Action3(space) + a1.fire() + a2.fire() + a3.fire() + space.appexec([], """(): + pass + """) + assert events == ['one', 'two', 'three', 'one'] + def test_periodic_action(self): from pypy.interpreter.executioncontext import ActionFlag diff --git a/pypy/module/__pypy__/interp_pypydatetime.py b/pypy/module/__pypy__/interp_pypydatetime.py index 994235af81..2150fdd558 100644 --- a/pypy/module/__pypy__/interp_pypydatetime.py +++ b/pypy/module/__pypy__/interp_pypydatetime.py @@ -5,7 +5,7 @@ from rpython.tool.sourcetools import func_with_new_name def create_class(name): class W_Class(W_Root): - 'builtin base clasee for datetime.%s to allow interop with cpyext' % name + 'builtin base class for datetime.%s to allow interop with cpyext' % name def descr_new__(space, w_type): return space.allocate_instance(W_Class, w_type) diff --git a/pypy/module/_cppyy/__init__.py b/pypy/module/_cppyy/__init__.py index 3c6eb6f31c..d8181b077b 100644 --- a/pypy/module/_cppyy/__init__.py +++ b/pypy/module/_cppyy/__init__.py @@ -13,7 +13,7 @@ class Module(MixedModule): '_set_function_generator': 'interp_cppyy.set_function_generator', '_register_class' : 'interp_cppyy.register_class', '_get_nullptr' : 'interp_cppyy.get_nullptr', - 'CPPClassBase' : 'interp_cppyy.W_CPPClass', + 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance', 'addressof' : 'interp_cppyy.addressof', '_bind_object' : 'interp_cppyy._bind_object', 'bind_object' : 'interp_cppyy.bind_object', diff --git a/pypy/module/_cppyy/capi/loadable_capi.py b/pypy/module/_cppyy/capi/loadable_capi.py index d54dd86167..1860fc44fe 100644 --- a/pypy/module/_cppyy/capi/loadable_capi.py +++ b/pypy/module/_cppyy/capi/loadable_capi.py @@ -121,11 +121,11 @@ class State(object): # TODO: the following need to match up with the globally defined C_XYZ low-level # types (see capi/__init__.py), but by using strings here, that isn't guaranteed - c_opaque_ptr = state.c_ulong + c_opaque_ptr = state.c_ulong # not ptrdiff_t (which is signed) c_scope = c_opaque_ptr c_type = c_scope - c_object = c_opaque_ptr + c_object = c_opaque_ptr # not voidp (to stick with one handle type) c_method = c_opaque_ptr c_index = state.c_long c_index_array = state.c_voidp @@ -150,16 +150,17 @@ class State(object): self.capi_call_ifaces = { # name to opaque C++ scope representation - 'num_scopes' : ([c_scope], c_int), - 'scope_name' : ([c_scope, c_int], c_ccharp), - 'resolve_name' : ([c_ccharp], c_ccharp), + 'resolve_enum' : ([c_ccharp], c_ccharp), 'get_scope' : ([c_ccharp], c_scope), 'actual_class' : ([c_type, c_object], c_type), + 'size_of_klass' : ([c_type], c_size_t), + 'size_of_type' : ([c_ccharp], c_size_t), # memory management 'allocate' : ([c_type], c_object), 'deallocate' : ([c_type, c_object], c_void), + 'construct' : ([c_type], c_object), 'destruct' : ([c_type, c_object], c_void), # method/function dispatching @@ -182,7 +183,8 @@ class State(object): 'constructor' : ([c_method, c_object, c_int, c_voidp], c_object), 'call_o' : ([c_method, c_object, c_int, c_voidp, c_type], c_object), - 'get_function_address' : ([c_scope, c_index], c_voidp), # TODO: verify + 'function_address_from_index' : ([c_scope, c_index], c_voidp), # TODO: verify + 'function_address_from_method' : ([c_method], c_voidp), # id. # handling of function argument buffer 'allocate_function_args' : ([c_int], c_voidp), @@ -196,6 +198,8 @@ class State(object): 'is_abstract' : ([c_type], c_int), 'is_enum' : ([c_ccharp], c_int), + 'get_all_cpp_names' : ([c_scope, c_voidp], c_voidp), # const char** + # type/class reflection information 'final_name' : ([c_type], c_ccharp), 'scoped_final_name' : ([c_type], c_ccharp), @@ -208,10 +212,10 @@ class State(object): # method/function reflection information 'num_methods' : ([c_scope], c_int), - 'method_index_at' : ([c_scope, c_int], c_index), 'method_indices_from_name' : ([c_scope, c_ccharp], c_index_array), 'method_name' : ([c_scope, c_index], c_ccharp), + 'method_mangled_name' : ([c_scope, c_index], c_ccharp), 'method_result_type' : ([c_scope, c_index], c_ccharp), 'method_num_args' : ([c_scope, c_index], c_int), 'method_req_args' : ([c_scope, c_index], c_int), @@ -219,7 +223,9 @@ class State(object): 'method_arg_default' : ([c_scope, c_index, c_int], c_ccharp), 'method_signature' : ([c_scope, c_index, c_int], c_ccharp), 'method_prototype' : ([c_scope, c_index, c_int], c_ccharp), + 'is_const_method' : ([c_method], c_int), + 'exists_method_template' : ([c_scope, c_ccharp], c_int), 'method_is_template' : ([c_scope, c_index], c_int), 'method_num_template_args' : ([c_scope, c_index], c_int), 'method_template_arg_name' : ([c_scope, c_index, c_index], c_ccharp), @@ -228,7 +234,9 @@ class State(object): 'get_global_operator' : ([c_scope, c_scope, c_scope, c_ccharp], c_index), # method properties + 'is_public_method' : ([c_type, c_index], c_int), 'is_constructor' : ([c_type, c_index], c_int), + 'is_destructor' : ([c_type, c_index], c_int), 'is_staticmethod' : ([c_type, c_index], c_int), # data member reflection information @@ -236,12 +244,14 @@ class State(object): 'datamember_name' : ([c_scope, c_int], c_ccharp), 'datamember_type' : ([c_scope, c_int], c_ccharp), 'datamember_offset' : ([c_scope, c_int], c_ptrdiff_t), - 'datamember_index' : ([c_scope, c_ccharp], c_int), # data member properties 'is_publicdata' : ([c_scope, c_int], c_int), 'is_staticdata' : ([c_scope, c_int], c_int), + 'is_const_data' : ([c_scope, c_int], c_int), + 'is_enum_data' : ([c_scope, c_int], c_int), + 'get_dimension_size' : ([c_scope, c_int, c_int], c_int), # misc helpers 'strtoll' : ([c_ccharp], c_llong), @@ -328,25 +338,27 @@ def _cdata_to_ccharp(space, w_cdata): return rffi.cast(rffi.CCHARP, ptr) # name to opaque C++ scope representation ------------------------------------ -def c_num_scopes(space, cppscope): - return space.int_w(call_capi(space, 'num_scopes', [_ArgH(cppscope.handle)])) -def c_scope_name(space, cppscope, iscope): - args = [_ArgH(cppscope.handle), _ArgL(iscope)] - return charp2str_free(space, call_capi(space, 'scope_name', args)) - def c_resolve_name(space, name): return charp2str_free(space, call_capi(space, 'resolve_name', [_ArgS(name)])) +def c_resolve_enum(space, name): + return charp2str_free(space, call_capi(space, 'resolve_enum', [_ArgS(name)])) def c_get_scope_opaque(space, name): return rffi.cast(C_SCOPE, space.uint_w(call_capi(space, 'get_scope', [_ArgS(name)]))) def c_actual_class(space, cppclass, cppobj): args = [_ArgH(cppclass.handle), _ArgH(cppobj)] return rffi.cast(C_TYPE, space.uint_w(call_capi(space, 'actual_class', args))) +def c_size_of_klass(space, cppclass): + return _cdata_to_size_t(space, call_capi(space, 'size_of_klass', [_ArgH(cppclass.handle)])) +def c_size_of_type(space, name): + return _cdata_to_size_t(space, call_capi(space, 'size_of_type', [_ArgS(name)])) # memory management ---------------------------------------------------------- def c_allocate(space, cppclass): return _cdata_to_cobject(space, call_capi(space, 'allocate', [_ArgH(cppclass.handle)])) def c_deallocate(space, cppclass, cppobject): call_capi(space, 'deallocate', [_ArgH(cppclass.handle), _ArgH(cppobject)]) +def c_construct(space, cppclass): + return _cdata_to_cobject(space, call_capi(space, 'construct', [_ArgH(cppclass.handle)])) def c_destruct(space, cppclass, cppobject): call_capi(space, 'destruct', [_ArgH(cppclass.handle), _ArgH(cppobject)]) @@ -391,7 +403,7 @@ def c_call_s(space, cppmethod, cppobject, nargs, cargs): w_cstr = call_capi(space, 'call_s', [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs), _ArgP(rffi.cast(rffi.VOIDP, length))]) - cstr_len = intmask(length[0]) + cstr_len = int(intmask(length[0])) finally: lltype.free(length, flavor='raw') return _cdata_to_ccharp(space, w_cstr), cstr_len @@ -403,10 +415,13 @@ def c_call_o(space, cppmethod, cppobject, nargs, cargs, cppclass): args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs), _ArgH(cppclass.handle)] return _cdata_to_cobject(space, call_capi(space, 'call_o', args)) -def c_get_function_address(space, cppscope, index): +def c_function_address_from_index(space, cppscope, index): args = [_ArgH(cppscope.handle), _ArgL(index)] return rffi.cast(C_FUNC_PTR, - _cdata_to_ptr(space, call_capi(space, 'get_function_address', args))) + _cdata_to_ptr(space, call_capi(space, 'function_address_from_index', args))) +def c_function_address_from_method(space, cppmethod): + return rffi.cast(C_FUNC_PTR, + _cdata_to_ptr(space, call_capi(space, 'function_address_from_method', _ArgH(cppmethod)))) # handling of function argument buffer --------------------------------------- def c_allocate_function_args(space, size): @@ -430,6 +445,23 @@ def c_is_abstract(space, cpptype): def c_is_enum(space, name): return space.bool_w(call_capi(space, 'is_enum', [_ArgS(name)])) +def c_get_all_cpp_names(space, scope): + sz = lltype.malloc(rffi.SIZE_TP.TO, 1, flavor='raw', zero=True) + try: + args = [_ArgH(scope.handle), _ArgP(rffi.cast(rffi.VOIDP, sz))] + rawnames = rffi.cast(rffi.CCHARPP, + _cdata_to_ptr(space, call_capi(space, 'get_all_cpp_names', args))) + count = int(intmask(sz[0])) + finally: + lltype.free(sz, flavor='raw') + allnames = [] + for i in range(count): + pystr = rffi.charp2str(rawnames[i]) + c_free(space, rffi.cast(rffi.VOIDP, rawnames[i])) # c_free defined below + allnames.append(pystr) + c_free(space, rffi.cast(rffi.VOIDP, rawnames)) # id. + return allnames + # type/class reflection information ------------------------------------------ def c_final_name(space, cpptype): return charp2str_free(space, call_capi(space, 'final_name', [_ArgH(cpptype)])) @@ -462,13 +494,10 @@ def c_base_offset1(space, derived_h, base, address, direction): def c_num_methods(space, cppscope): args = [_ArgH(cppscope.handle)] return space.int_w(call_capi(space, 'num_methods', args)) -def c_method_index_at(space, cppscope, imethod): - args = [_ArgH(cppscope.handle), _ArgL(imethod)] - return space.int_w(call_capi(space, 'method_index_at', args)) def c_method_indices_from_name(space, cppscope, name): args = [_ArgH(cppscope.handle), _ArgS(name)] indices = rffi.cast(C_INDEX_ARRAY, - _cdata_to_ptr(space, call_capi(space, 'method_indices_from_name', args))) + _cdata_to_ptr(space, call_capi(space, 'method_indices_from_name', args))) if not indices: return [] py_indices = [] @@ -506,6 +535,9 @@ def c_method_prototype(space, cppscope, index, show_formalargs=True): args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)] return charp2str_free(space, call_capi(space, 'method_prototype', 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)) @@ -531,9 +563,15 @@ def c_get_global_operator(space, nss, lc, rc, op): return rffi.cast(WLAVC_INDEX, -1) # method properties ---------------------------------------------------------- +def c_is_public_method(space, cppclass, index): + args = [_ArgH(cppclass.handle), _ArgL(index)] + return space.bool_w(call_capi(space, 'is_public_method', args)) def c_is_constructor(space, cppclass, index): args = [_ArgH(cppclass.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'is_constructor', args)) +def c_is_destructor(space, cppclass, index): + args = [_ArgH(cppclass.handle), _ArgL(index)] + return space.bool_w(call_capi(space, 'is_destructor', args)) def c_is_staticmethod(space, cppclass, index): args = [_ArgH(cppclass.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'is_staticmethod', args)) @@ -562,6 +600,15 @@ def c_is_publicdata(space, cppscope, datamember_index): def c_is_staticdata(space, cppscope, datamember_index): args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] return space.bool_w(call_capi(space, 'is_staticdata', args)) +def c_is_const_data(space, cppscope, datamember_index): + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] + return space.bool_w(call_capi(space, 'is_const_data', args)) +def c_is_enum_data(space, cppscope, datamember_index): + args = [_ArgH(cppscope.handle), _ArgL(datamember_index)] + 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)) # misc helpers --------------------------------------------------------------- def c_strtoll(space, svalue): @@ -585,7 +632,7 @@ def c_stdstring2charp(space, cppstr): try: w_cstr = call_capi(space, 'stdstring2charp', [_ArgH(cppstr), _ArgP(rffi.cast(rffi.VOIDP, sz))]) - cstr_len = intmask(sz[0]) + cstr_len = int(intmask(sz[0])) finally: lltype.free(sz, flavor='raw') return rffi.charpsize2str(_cdata_to_ccharp(space, w_cstr), cstr_len) @@ -607,7 +654,7 @@ 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_CPPClass, w_self, can_be_None=False) + cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) return space.newtext(c_stdstring2charp(space, cppstr._rawobject)) # setup pythonizations for later use at run-time diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py index 6087951d11..83931137e4 100644 --- a/pypy/module/_cppyy/converter.py +++ b/pypy/module/_cppyy/converter.py @@ -22,8 +22,8 @@ from pypy.module._cppyy import helper, capi, ffitypes def get_rawobject(space, w_obj, can_be_None=True): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=can_be_None) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + cppinstance = space.interp_w(W_CPPInstance, w_obj, can_be_None=can_be_None) if cppinstance: rawobject = cppinstance.get_rawobject() assert lltype.typeOf(rawobject) == capi.C_OBJECT @@ -31,15 +31,15 @@ def get_rawobject(space, w_obj, can_be_None=True): return capi.C_NULL_OBJECT def set_rawobject(space, w_obj, address): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + cppinstance = space.interp_w(W_CPPInstance, w_obj, can_be_None=True) if cppinstance: assert lltype.typeOf(cppinstance._rawobject) == capi.C_OBJECT cppinstance._rawobject = rffi.cast(capi.C_OBJECT, address) def get_rawobject_nonnull(space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - cppinstance = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + cppinstance = space.interp_w(W_CPPInstance, w_obj, can_be_None=True) if cppinstance: cppinstance._nullcheck() rawobject = cppinstance.get_rawobject() @@ -502,8 +502,8 @@ class InstanceRefConverter(TypeConverter): self.clsdecl = clsdecl def _unwrap_object(self, space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - if isinstance(w_obj, W_CPPClass): + 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_R_VALUE if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: # reject moves as all are explicit @@ -534,8 +534,8 @@ class InstanceRefConverter(TypeConverter): class InstanceMoveConverter(InstanceRefConverter): def _unwrap_object(self, space, w_obj): # moving is same as by-ref, but have to check that move is allowed - from pypy.module._cppyy.interp_cppyy import W_CPPClass, INSTANCE_FLAGS_IS_R_VALUE - if isinstance(w_obj, W_CPPClass): + from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_R_VALUE + if isinstance(w_obj, W_CPPInstance): if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE: w_obj.flags &= ~INSTANCE_FLAGS_IS_R_VALUE return InstanceRefConverter._unwrap_object(self, space, w_obj) @@ -598,8 +598,8 @@ class InstancePtrPtrConverter(InstancePtrConverter): raise FastCallNotPossible def finalize_call(self, space, w_obj, call_local): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - assert isinstance(w_obj, W_CPPClass) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + assert isinstance(w_obj, W_CPPInstance) r = rffi.cast(rffi.VOIDPP, call_local) w_obj._rawobject = rffi.cast(capi.C_OBJECT, r[0]) @@ -617,8 +617,8 @@ class StdStringConverter(InstanceConverter): InstanceConverter.__init__(self, space, cppclass) def _unwrap_object(self, space, w_obj): - from pypy.module._cppyy.interp_cppyy import W_CPPClass - if isinstance(w_obj, W_CPPClass): + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + if isinstance(w_obj, W_CPPInstance): arg = InstanceConverter._unwrap_object(self, space, w_obj) return capi.c_stdstring2stdstring(space, arg) else: @@ -749,8 +749,6 @@ def get_converter(space, _name, default): return InstancePtrPtrConverter(space, clsdecl) elif compound == "": return InstanceConverter(space, clsdecl) - elif capi.c_is_enum(space, clean_name): - return _converters['unsigned'](space, default) # 5) void* or void converter (which fails on use) if 0 <= compound.find('*'): diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py index 0a9be93ec7..22c3f73836 100644 --- a/pypy/module/_cppyy/executor.py +++ b/pypy/module/_cppyy/executor.py @@ -293,8 +293,6 @@ def get_executor(space, name): return InstancePtrExecutor(space, cppclass) elif compound == '**' or compound == '*&': return InstancePtrPtrExecutor(space, cppclass) - elif capi.c_is_enum(space, clean_name): - return _executors['internal_enum_type_t'](space, None) # 4) additional special cases if compound == '*': diff --git a/pypy/module/_cppyy/include/capi.h b/pypy/module/_cppyy/include/capi.h index b232764c41..21e225d9d3 100644 --- a/pypy/module/_cppyy/include/capi.h +++ b/pypy/module/_cppyy/include/capi.h @@ -8,26 +8,29 @@ extern "C" { #endif // ifdef __cplusplus - typedef unsigned long cppyy_scope_t; + typedef ptrdiff_t cppyy_scope_t; typedef cppyy_scope_t cppyy_type_t; - typedef unsigned long cppyy_object_t; - typedef unsigned long cppyy_method_t; + typedef void* cppyy_object_t; + typedef ptrdiff_t cppyy_method_t; + typedef long cppyy_index_t; typedef void* cppyy_funcaddr_t; + typedef unsigned long cppyy_exctype_t; + /* name to opaque C++ scope representation -------------------------------- */ RPY_EXTERN - int cppyy_num_scopes(cppyy_scope_t parent); - RPY_EXTERN - char* cppyy_scope_name(cppyy_scope_t parent, cppyy_index_t iscope); - RPY_EXTERN char* cppyy_resolve_name(const char* cppitem_name); RPY_EXTERN + char* cppyy_resolve_enum(const char* enum_type); + RPY_EXTERN cppyy_scope_t cppyy_get_scope(const char* scope_name); RPY_EXTERN cppyy_type_t cppyy_actual_class(cppyy_type_t klass, cppyy_object_t obj); RPY_EXTERN - size_t cppyy_size_of(cppyy_type_t klass); + size_t cppyy_size_of_klass(cppyy_type_t klass); + RPY_EXTERN + size_t cppyy_size_of_type(const char* type_name); /* memory management ------------------------------------------------------ */ RPY_EXTERN @@ -35,48 +38,53 @@ extern "C" { RPY_EXTERN void cppyy_deallocate(cppyy_type_t type, cppyy_object_t self); RPY_EXTERN + cppyy_object_t cppyy_construct(cppyy_type_t type); + RPY_EXTERN void cppyy_destruct(cppyy_type_t type, cppyy_object_t self); /* method/function dispatching -------------------------------------------- */ RPY_EXTERN - void cppyy_call_v(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + void cppyy_call_v(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN unsigned char cppyy_call_b(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN - char cppyy_call_c(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + char cppyy_call_c(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN - short cppyy_call_h(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + short cppyy_call_h(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN - int cppyy_call_i(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + int cppyy_call_i(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN - long cppyy_call_l(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + long cppyy_call_l(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN long long cppyy_call_ll(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN - float cppyy_call_f(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + float cppyy_call_f(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN 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 - void* cppyy_call_r(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + void* cppyy_call_r(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN - char* cppyy_call_s(cppyy_method_t method, cppyy_object_t self, int nargs, void* args, size_t* length); - + char* cppyy_call_s(cppyy_method_t method, cppyy_object_t self, int nargs, void* args, size_t* length); RPY_EXTERN cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t klass, int nargs, void* args); RPY_EXTERN + void cppyy_destructor(cppyy_type_t type, cppyy_object_t self); + RPY_EXTERN cppyy_object_t cppyy_call_o(cppyy_method_t method, cppyy_object_t self, int nargs, void* args, cppyy_type_t result_type); RPY_EXTERN - cppyy_funcaddr_t cppyy_get_function_address(cppyy_scope_t scope, cppyy_index_t idx); + cppyy_funcaddr_t cppyy_function_address_from_index(cppyy_scope_t scope, cppyy_index_t idx); + RPY_EXTERN + cppyy_funcaddr_t cppyy_function_address_from_method(cppyy_method_t method); /* handling of function argument buffer ----------------------------------- */ RPY_EXTERN - void* cppyy_allocate_function_args(int nargs); + void* cppyy_allocate_function_args(int nargs); RPY_EXTERN - void cppyy_deallocate_function_args(void* args); + void cppyy_deallocate_function_args(void* args); RPY_EXTERN size_t cppyy_function_arg_sizeof(); RPY_EXTERN @@ -92,6 +100,9 @@ extern "C" { RPY_EXTERN int cppyy_is_enum(const char* type_name); + RPY_EXTERN + const char** cppyy_get_all_cpp_names(cppyy_scope_t scope, size_t* count); + /* class reflection information ------------------------------------------- */ RPY_EXTERN char* cppyy_final_name(cppyy_type_t type); @@ -105,6 +116,10 @@ extern "C" { char* cppyy_base_name(cppyy_type_t type, int base_index); RPY_EXTERN int cppyy_is_subtype(cppyy_type_t derived, cppyy_type_t base); + RPY_EXTERN + int cppyy_smartptr_info(const char* name, cppyy_type_t* raw, cppyy_method_t* deref); + RPY_EXTERN + void cppyy_add_smartptr_type(const char* type_name); /* calculate offsets between declared and actual type, up-cast: direction > 0; down-cast: direction < 0 */ RPY_EXTERN @@ -114,8 +129,6 @@ extern "C" { RPY_EXTERN int cppyy_num_methods(cppyy_scope_t scope); RPY_EXTERN - cppyy_index_t cppyy_method_index_at(cppyy_scope_t scope, int imeth); - RPY_EXTERN cppyy_index_t* cppyy_method_indices_from_name(cppyy_scope_t scope, const char* name); RPY_EXTERN @@ -136,8 +149,12 @@ extern "C" { char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx, int show_formalargs); RPY_EXTERN char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_index_t idx, int show_formalargs); + RPY_EXTERN + int cppyy_is_const_method(cppyy_method_t); 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); RPY_EXTERN int cppyy_method_num_template_args(cppyy_scope_t scope, cppyy_index_t idx); @@ -169,15 +186,20 @@ extern "C" { char* cppyy_datamember_type(cppyy_scope_t scope, int datamember_index); RPY_EXTERN ptrdiff_t cppyy_datamember_offset(cppyy_scope_t scope, int datamember_index); - RPY_EXTERN int cppyy_datamember_index(cppyy_scope_t scope, const char* name); /* data member properties ------------------------------------------------- */ RPY_EXTERN - int cppyy_is_publicdata(cppyy_type_t type, int datamember_index); + int cppyy_is_publicdata(cppyy_type_t type, cppyy_index_t datamember_index); + RPY_EXTERN + int cppyy_is_staticdata(cppyy_type_t type, cppyy_index_t datamember_index); + RPY_EXTERN + int cppyy_is_const_data(cppyy_scope_t scope, cppyy_index_t idata); + RPY_EXTERN + int cppyy_is_enum_data(cppyy_scope_t scope, cppyy_index_t idata); RPY_EXTERN - int cppyy_is_staticdata(cppyy_type_t type, int datamember_index); + int cppyy_get_dimension_size(cppyy_scope_t scope, cppyy_index_t idata, int dimension); /* misc helpers ----------------------------------------------------------- */ RPY_EXTERN @@ -197,7 +219,7 @@ extern "C" { RPY_EXTERN const char* cppyy_stdvector_valuetype(const char* clname); RPY_EXTERN - size_t cppyy_stdvector_valuesize(const char* clname); + size_t cppyy_stdvector_valuesize(const char* clname); #ifdef __cplusplus } diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py index 063498a0e6..53ae1ea788 100644 --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -19,6 +19,9 @@ INSTANCE_FLAGS_PYTHON_OWNS = 0x0001 INSTANCE_FLAGS_IS_REF = 0x0002 INSTANCE_FLAGS_IS_R_VALUE = 0x0004 +OVERLOAD_FLAGS_USE_FFI = 0x0001 + + class FastCallNotPossible(Exception): pass @@ -174,7 +177,7 @@ class CPPMethod(object): @staticmethod def unpack_cppthis(space, w_cppinstance, declaring_scope): - cppinstance = space.interp_w(W_CPPClass, w_cppinstance, can_be_None=False) + cppinstance = space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=False) cppinstance._nullcheck() return cppinstance.get_cppthis(declaring_scope) @@ -186,7 +189,7 @@ class CPPMethod(object): return rffi.cast(rffi.VOIDP, loc_idx) @jit.unroll_safe - def call(self, cppthis, args_w): + def call(self, cppthis, args_w, useffi): jit.promote(self) assert lltype.typeOf(cppthis) == capi.C_OBJECT @@ -218,16 +221,23 @@ class CPPMethod(object): try: # attempt to call directly through ffi chain - if self._funcaddr: + if useffi and self._funcaddr: try: return self.do_fast_call(cppthis, args_w, call_local) except FastCallNotPossible: pass # can happen if converters or executor does not implement ffi # ffi chain must have failed; using stub functions instead - args = self.prepare_arguments(args_w, call_local) + args, stat = self.prepare_arguments(args_w, call_local) try: - return self.executor.execute(self.space, self.cppmethod, cppthis, len(args_w), args) + result = self.executor.execute( + self.space, self.cppmethod, cppthis, len(args_w), args) + if stat[0] != rffi.cast(rffi.ULONG, 0): + what = rffi.cast(rffi.CCHARP, stat[1]) + pywhat = rffi.charp2str(what) + capi.c_free(self.space, rffi.cast(rffi.VOIDP, what)) + raise OperationError(self.space.w_Exception, self.space.newtext(pywhat)) + return result finally: self.finalize_call(args, args_w, call_local) finally: @@ -322,7 +332,7 @@ class CPPMethod(object): # Each CPPMethod corresponds one-to-one to a C++ equivalent and cppthis # has been offset to the matching class. Hence, the libffi pointer is # uniquely defined and needs to be setup only once. - funcaddr = capi.c_get_function_address(self.space, self.scope, self.index) + funcaddr = capi.c_function_address_from_index(self.space, self.scope, self.index) if funcaddr and cppthis: # methods only for now state = self.space.fromcache(ffitypes.State) @@ -373,7 +383,10 @@ class CPPMethod(object): conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_j), loc_j) capi.c_deallocate_function_args(self.space, args) raise - return args + stat = rffi.cast(rffi.ULONGP, + lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args), int(len(args_w))*stride)) + stat[0] = rffi.cast(rffi.ULONG, 0) + return args, stat @jit.unroll_safe def finalize_call(self, args, args_w, call_local): @@ -435,7 +448,7 @@ class CPPTemplatedCall(CPPMethod): # TODO: might have to specialize for CPPTemplatedCall on CPPMethod/CPPFunction here CPPMethod.__init__(self, space, declaring_scope, method_index, arg_defs, args_required) - def call(self, cppthis, args_w): + def call(self, cppthis, args_w, useffi): assert lltype.typeOf(cppthis) == capi.C_OBJECT for i in range(len(args_w)): try: @@ -447,10 +460,10 @@ class CPPTemplatedCall(CPPMethod): raise oefmt(self.space.w_TypeError, "non-matching template (got %s where %s expected)", s, self.templ_args[i]) - return W_CPPBoundMethod(cppthis, self) + return W_CPPBoundMethod(cppthis, self, useffi) - def bound_call(self, cppthis, args_w): - return CPPMethod.call(self, cppthis, args_w) + def bound_call(self, cppthis, args_w, useffi): + return CPPMethod.call(self, cppthis, args_w, useffi) def __repr__(self): return "CPPTemplatedCall: %s" % self.prototype() @@ -468,11 +481,11 @@ class CPPConstructor(CPPMethod): def unpack_cppthis(space, w_cppinstance, declaring_scope): return rffi.cast(capi.C_OBJECT, declaring_scope.handle) - def call(self, cppthis, args_w): + def call(self, cppthis, args_w, useffi): # Note: this does not return a wrapped instance, just a pointer to the # new instance; the overload must still wrap it before returning. Also, # cppthis is declaring_scope.handle (as per unpack_cppthis(), above). - return CPPMethod.call(self, cppthis, args_w) + return CPPMethod.call(self, cppthis, args_w, useffi) def __repr__(self): return "CPPConstructor: %s" % self.prototype() @@ -485,7 +498,7 @@ class CPPSetItem(CPPMethod): _immutable_ = True - def call(self, cppthis, args_w): + def call(self, cppthis, args_w, useffi): end = len(args_w)-1 if 0 <= end: w_item = args_w[end] @@ -493,7 +506,7 @@ class CPPSetItem(CPPMethod): if self.converters is None: self._setup(cppthis) self.executor.set_item(self.space, w_item) # TODO: what about threads? - CPPMethod.call(self, cppthis, args_w) + CPPMethod.call(self, cppthis, args_w, useffi) class W_CPPOverload(W_Root): @@ -501,7 +514,7 @@ class W_CPPOverload(W_Root): collection of (possibly) overloaded methods or functions. It calls these in order and deals with error handling and reporting.""" - _attrs_ = ['space', 'scope', 'functions'] + _attrs_ = ['space', 'scope', 'functions', 'flags'] _immutable_fields_ = ['scope', 'functions[*]'] def __init__(self, space, declaring_scope, functions): @@ -510,6 +523,19 @@ class W_CPPOverload(W_Root): assert len(functions) from rpython.rlib import debug self.functions = debug.make_sure_not_resized(functions) + self.flags = 0 + self.flags |= OVERLOAD_FLAGS_USE_FFI + + # allow user to determine ffi use rules per overload + def fget_useffi(self, space): + return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI)) + + @unwrap_spec(value=bool) + def fset_useffi(self, space, value): + if space.is_true(value): + self.flags |= OVERLOAD_FLAGS_USE_FFI + else: + self.flags &= ~OVERLOAD_FLAGS_USE_FFI @jit.elidable_promote() def is_static(self): @@ -540,7 +566,7 @@ class W_CPPOverload(W_Root): for i in range(len(self.functions)): cppyyfunc = self.functions[i] try: - return cppyyfunc.call(cppthis, args_w) + return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) except Exception: pass @@ -553,7 +579,7 @@ class W_CPPOverload(W_Root): for i in range(len(self.functions)): cppyyfunc = self.functions[i] try: - return cppyyfunc.call(cppthis, args_w) + 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 if len(self.functions) == 1: @@ -588,6 +614,7 @@ W_CPPOverload.typedef = TypeDef( 'CPPOverload', is_static = interp2app(W_CPPOverload.is_static), call = interp2app(W_CPPOverload.call), + __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), prototype = interp2app(W_CPPOverload.prototype), ) @@ -611,7 +638,7 @@ class W_CPPConstructorOverload(W_CPPOverload): self.scope.name) w_result = W_CPPOverload.call(self, w_cppinstance, args_w) newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result)) - cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) + cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True) if cppinstance is not None: cppinstance._rawobject = newthis memory_regulator.register(cppinstance) @@ -642,17 +669,18 @@ W_CPPTemplateOverload.typedef = TypeDef( class W_CPPBoundMethod(W_Root): - _attrs_ = ['cppthis', 'method'] + _attrs_ = ['cppthis', 'method', 'useffi'] - def __init__(self, cppthis, method): + def __init__(self, cppthis, method, useffi): self.cppthis = cppthis self.method = method + self.useffi = useffi def __call__(self, args_w): - return self.method.bound_call(self.cppthis, args_w) + return self.method.bound_call(self.cppthis, args_w, self.useffi) def __repr__(self): - return "W_CPPBoundMethod(%s)" % [f.prototype() for f in self.functions] + return "W_CPPBoundMethod(%s)" % self.method.prototype() W_CPPBoundMethod.typedef = TypeDef( 'CPPBoundMethod', @@ -682,7 +710,7 @@ class W_CPPDataMember(W_Root): return offset def get(self, w_cppinstance, w_pycppclass): - cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) + cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True) if not cppinstance: raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") @@ -690,7 +718,7 @@ class W_CPPDataMember(W_Root): return self.converter.from_memory(self.space, w_cppinstance, w_pycppclass, offset) def set(self, w_cppinstance, w_value): - cppinstance = self.space.interp_w(W_CPPClass, w_cppinstance, can_be_None=True) + cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=True) if not cppinstance: raise oefmt(self.space.w_AttributeError, "attribute access requires an instance") @@ -845,24 +873,11 @@ class W_CPPNamespaceDecl(W_CPPScopeDecl): return self.space.w_True def ns__dir__(self): - # Collect a list of everything (currently) available in the namespace. - # The backend can filter by returning empty strings. Special care is - # taken for functions, which need not be unique (overloading). - alldir = [] - for i in range(capi.c_num_scopes(self.space, self)): - sname = capi.c_scope_name(self.space, self, i) - if sname: alldir.append(self.space.newtext(sname)) - allmeth = {} - for i in range(capi.c_num_methods(self.space, self)): - idx = capi.c_method_index_at(self.space, self, i) - mname = capi.c_method_name(self.space, self, idx) - if mname: allmeth.setdefault(mname, 0) - for m in allmeth.keys(): - alldir.append(self.space.newtext(m)) - for i in range(capi.c_num_datamembers(self.space, self)): - dname = capi.c_datamember_name(self.space, self, i) - if dname: alldir.append(self.space.newtext(dname)) - return self.space.newlist(alldir) + alldir = capi.c_get_all_cpp_names(self.space, self) + w_alldir = self.space.newlist([]) + for name in alldir: + w_alldir.append(self.space.newtext(name)) + return w_alldir def missing_attribute_error(self, name): return oefmt(self.space.w_AttributeError, @@ -890,8 +905,7 @@ class W_CPPClassDecl(W_CPPScopeDecl): def _build_methods(self): assert len(self.methods) == 0 methods_temp = {} - for i in range(capi.c_num_methods(self.space, self)): - idx = capi.c_method_index_at(self.space, self, i) + for idx in range(capi.c_num_methods(self.space, self)): if capi.c_is_constructor(self.space, self, idx): pyname = '__init__' else: @@ -1029,7 +1043,7 @@ W_CPPComplexClassDecl.typedef = TypeDef( W_CPPComplexClassDecl.typedef.acceptable_as_base_class = False -class W_CPPClass(W_Root): +class W_CPPInstance(W_Root): _attrs_ = ['space', 'clsdecl', '_rawobject', 'flags', 'finalizer_registered'] _immutable_fields_ = ['clsdecl'] @@ -1109,8 +1123,8 @@ class W_CPPClass(W_Root): # find a global overload in gbl, in __gnu_cxx (for iterators), or in the # scopes of the argument classes (TODO: implement that last option) try: - # TODO: expecting w_other to be an W_CPPClass is too limiting - other = self.space.interp_w(W_CPPClass, w_other, can_be_None=False) + # TODO: expecting w_other to be an W_CPPInstance is too limiting + other = self.space.interp_w(W_CPPInstance, w_other, can_be_None=False) for name in ["", "__gnu_cxx", "__1"]: nss = scope_byname(self.space, name) meth_idx = capi.c_get_global_operator( @@ -1132,7 +1146,7 @@ class W_CPPClass(W_Root): # fallback 2: direct pointer comparison (the class comparison is needed since # the first data member in a struct and the struct have the same address) - other = self.space.interp_w(W_CPPClass, w_other, can_be_None=False) # TODO: factor out + other = self.space.interp_w(W_CPPInstance, w_other, can_be_None=False) # TODO: factor out iseq = (self._rawobject == other._rawobject) and (self.clsdecl == other.clsdecl) return self.space.newbool(iseq) @@ -1176,19 +1190,19 @@ class W_CPPClass(W_Root): if self.flags & INSTANCE_FLAGS_PYTHON_OWNS: self.destruct() -W_CPPClass.typedef = TypeDef( - 'CPPClass', - __python_owns__ = GetSetProperty(W_CPPClass.fget_python_owns, W_CPPClass.fset_python_owns), - __init__ = interp2app(W_CPPClass.instance__init__), - __eq__ = interp2app(W_CPPClass.instance__eq__), - __ne__ = interp2app(W_CPPClass.instance__ne__), - __nonzero__ = interp2app(W_CPPClass.instance__nonzero__), - __len__ = interp2app(W_CPPClass.instance__len__), - __cmp__ = interp2app(W_CPPClass.instance__cmp__), - __repr__ = interp2app(W_CPPClass.instance__repr__), - __destruct__ = interp2app(W_CPPClass.destruct), +W_CPPInstance.typedef = TypeDef( + 'CPPInstance', + __python_owns__ = GetSetProperty(W_CPPInstance.fget_python_owns, W_CPPInstance.fset_python_owns), + __init__ = interp2app(W_CPPInstance.instance__init__), + __eq__ = interp2app(W_CPPInstance.instance__eq__), + __ne__ = interp2app(W_CPPInstance.instance__ne__), + __nonzero__ = interp2app(W_CPPInstance.instance__nonzero__), + __len__ = interp2app(W_CPPInstance.instance__len__), + __cmp__ = interp2app(W_CPPInstance.instance__cmp__), + __repr__ = interp2app(W_CPPInstance.instance__repr__), + __destruct__ = interp2app(W_CPPInstance.destruct), ) -W_CPPClass.typedef.acceptable_as_base_class = True +W_CPPInstance.typedef.acceptable_as_base_class = True class MemoryRegulator: @@ -1200,7 +1214,7 @@ class MemoryRegulator: # Note that for now, the associated test carries an m_padding to make # a difference in the addresses. def __init__(self): - self.objects = rweakref.RWeakValueDictionary(int, W_CPPClass) + self.objects = rweakref.RWeakValueDictionary(int, W_CPPInstance) def register(self, obj): if not obj._rawobject: @@ -1266,8 +1280,8 @@ def wrap_cppinstance(space, rawobject, clsdecl, return obj # fresh creation - w_cppinstance = space.allocate_instance(W_CPPClass, w_pycppclass) - cppinstance = space.interp_w(W_CPPClass, w_cppinstance, can_be_None=False) + w_cppinstance = space.allocate_instance(W_CPPInstance, w_pycppclass) + cppinstance = space.interp_w(W_CPPInstance, w_cppinstance, can_be_None=False) cppinstance.__init__(space, clsdecl, rawobject, is_ref, python_owns) memory_regulator.register(cppinstance) return w_cppinstance @@ -1311,7 +1325,7 @@ def bind_object(space, w_obj, w_pycppclass, owns=False, cast=False): def move(space, w_obj): """Casts the given instance into an C++-style rvalue.""" - obj = space.interp_w(W_CPPClass, w_obj, can_be_None=True) + obj = space.interp_w(W_CPPInstance, w_obj, can_be_None=True) if obj: obj.flags |= INSTANCE_FLAGS_IS_R_VALUE return w_obj diff --git a/pypy/module/_cppyy/pythonify.py b/pypy/module/_cppyy/pythonify.py index 77c2dda55b..9b37215ce2 100644 --- a/pypy/module/_cppyy/pythonify.py +++ b/pypy/module/_cppyy/pythonify.py @@ -7,7 +7,7 @@ import sys # Metaclasses are needed to store C++ static data members as properties. Since # the interp-level does not support metaclasses, they are created at app-level. # These are the metaclass base classes: -class CPPMetaScope(type): +class CPPScope(type): def __getattr__(self, name): try: return get_scoped_pycppitem(self, name) # will cache on self @@ -15,11 +15,11 @@ class CPPMetaScope(type): raise AttributeError("%s object has no attribute '%s' (details: %s)" % (self, name, str(e))) -class CPPMetaNamespace(CPPMetaScope): +class CPPMetaNamespace(CPPScope): def __dir__(self): return self.__cppdecl__.__dir__() -class CPPMetaClass(CPPMetaScope): +class CPPClass(CPPScope): pass # namespace base class (class base class defined in _init_pythonify) @@ -173,7 +173,7 @@ def make_cppclass(scope, cl_name, decl): # get a list of base classes for class creation bases = [get_pycppclass(base) for base in decl.get_base_names()] if not bases: - bases = [CPPClass,] + bases = [CPPInstance,] else: # it's possible that the required class now has been built if one of # the base classes uses it in e.g. a function interface @@ -214,7 +214,7 @@ def make_cppclass(scope, cl_name, decl): # create a metaclass to allow properties (for static data write access) metabases = [type(base) for base in bases] - metacpp = type(CPPMetaScope)(cl_name+'_meta', _drop_cycles(metabases), d_meta) + metacpp = type(CPPScope)(cl_name+'_meta', _drop_cycles(metabases), d_meta) # create the python-side C++ class pycls = metacpp(cl_name, _drop_cycles(bases), d_class) @@ -412,11 +412,11 @@ def _init_pythonify(): # at pypy-c startup, rather than on the "import _cppyy" statement import _cppyy - # root of all proxy classes: CPPClass in pythonify exists to combine the - # CPPMetaScope metaclass with the interp-level CPPClassBase - global CPPClass - class CPPClass(_cppyy.CPPClassBase): - __metaclass__ = CPPMetaScope + # root of all proxy classes: CPPInstance in pythonify exists to combine + # the CPPScope metaclass with the interp-level CPPInstanceBase + global CPPInstance + class CPPInstance(_cppyy.CPPInstanceBase): + __metaclass__ = CPPScope pass # class generator callback @@ -438,9 +438,8 @@ def _init_pythonify(): gbl.std.move = _cppyy.move # install a type for enums to refer to - # TODO: this is correct for C++98, not for C++11 and in general there will - # be the same issue for all typedef'd builtin types setattr(gbl, 'internal_enum_type_t', int) + setattr(gbl, 'unsigned int', int) # if resolved # install for user access _cppyy.gbl = gbl diff --git a/pypy/module/_cppyy/src/dummy_backend.cxx b/pypy/module/_cppyy/src/dummy_backend.cxx index 6d1f0a51aa..b82dc7b30d 100644 --- a/pypy/module/_cppyy/src/dummy_backend.cxx +++ b/pypy/module/_cppyy/src/dummy_backend.cxx @@ -407,11 +407,9 @@ static inline char* cppstring_to_cstring(const std::string& name) { /* name to opaque C++ scope representation -------------------------------- */ -int cppyy_num_scopes(cppyy_scope_t handle) { - return 0; -} - char* cppyy_resolve_name(const char* cppitem_name) { + if (cppyy_is_enum(cppitem_name)) + return cppstring_to_cstring("internal_enum_type_t"); return cppstring_to_cstring(cppitem_name); } @@ -851,10 +849,13 @@ cppyy_object_t cppyy_constructor(cppyy_method_t method, cppyy_type_t handle, int return (cppyy_object_t)result; } -cppyy_funcaddr_t cppyy_get_function_address(cppyy_scope_t /* scope */, cppyy_index_t /* idx */) { +cppyy_funcaddr_t cppyy_function_address_from_index(cppyy_scope_t /* scope */, cppyy_index_t /* idx */) { return (cppyy_funcaddr_t)0; } +cppyy_funcaddr_t cppyy_function_address_from_method(cppyy_method_t /* method */) { + return (cppyy_funcaddr_t)0; +} /* handling of function argument buffer ----------------------------------- */ void* cppyy_allocate_function_args(int nargs) { @@ -926,10 +927,6 @@ int cppyy_num_methods(cppyy_scope_t handle) { return s_scopes[handle].m_methods.size(); } -cppyy_index_t cppyy_method_index_at(cppyy_scope_t /* scope */, int imeth) { - return (cppyy_index_t)imeth; -} - char* cppyy_method_name(cppyy_scope_t handle, cppyy_index_t method_index) { return cppstring_to_cstring(s_scopes[handle].m_methods[(int)method_index].m_name); } @@ -978,8 +975,17 @@ cppyy_method_t cppyy_get_method(cppyy_scope_t handle, cppyy_index_t method_index return (cppyy_method_t)0; } +cppyy_index_t cppyy_get_global_operator(cppyy_scope_t /* scope */, + cppyy_scope_t /* lc */, cppyy_scope_t /* rc */, const char* /* op */) { + return (cppyy_index_t)-1; +} + /* method properties ----------------------------------------------------- */ +int cppyy_is_publicmethod(cppyy_type_t /* handle */, cppyy_index_t /* method_index */) { + return 1; +} + int cppyy_is_constructor(cppyy_type_t handle, cppyy_index_t method_index) { if (s_scopes.find(handle) != s_scopes.end()) return s_scopes[handle].m_methods[method_index].m_type == kConstructor; @@ -987,6 +993,10 @@ int cppyy_is_constructor(cppyy_type_t handle, cppyy_index_t method_index) { return 0; } +int cppyy_is_destructor(cppyy_type_t /* handle */, cppyy_index_t /* method_index */) { + return 0; +} + int cppyy_is_staticmethod(cppyy_type_t handle, cppyy_index_t method_index) { if (s_scopes.find(handle) != s_scopes.end()) return s_scopes[handle].m_methods[method_index].m_type == kStatic; @@ -1014,14 +1024,22 @@ ptrdiff_t cppyy_datamember_offset(cppyy_scope_t handle, int idatambr) { /* data member properties ------------------------------------------------ */ -int cppyy_is_publicdata(cppyy_scope_t handle, int idatambr) { +int cppyy_is_publicdata(cppyy_scope_t /* handle */, cppyy_index_t /* idatambr */) { return 1; } -int cppyy_is_staticdata(cppyy_scope_t handle, int idatambr) { +int cppyy_is_staticdata(cppyy_scope_t handle, cppyy_index_t idatambr) { return s_scopes[handle].m_datambrs[idatambr].m_isstatic; } +int cppyy_is_const_data(cppyy_scope_t /* handle */, cppyy_index_t /* idatambr */) { + return 0; +} + +int cppyy_is_enum_data(cppyy_scope_t /* handle */, cppyy_index_t /* idatambr */) { + return 0; +} + /* misc helpers ----------------------------------------------------------- */ #if defined(_MSC_VER) diff --git a/pypy/module/_cppyy/test/Makefile b/pypy/module/_cppyy/test/Makefile index 2c04e91d31..c33340dc0e 100644 --- a/pypy/module/_cppyy/test/Makefile +++ b/pypy/module/_cppyy/test/Makefile @@ -14,7 +14,7 @@ all : $(dicts) HASGENREFLEX:=$(shell command -v genreflex 2> /dev/null) -cppflags=-std=c++11 -O3 -m64 -fPIC -rdynamic +cppflags=-std=c++14 -O3 -m64 -fPIC -rdynamic ifdef HASGENREFLEX genreflex_flags:=$(shell genreflex --cppflags) cppflags+=$(genreflex_flags) diff --git a/pypy/module/_cppyy/test/advancedcpp.cxx b/pypy/module/_cppyy/test/advancedcpp.cxx index c807d84930..0e39c91f83 100644 --- a/pypy/module/_cppyy/test/advancedcpp.cxx +++ b/pypy/module/_cppyy/test/advancedcpp.cxx @@ -1,5 +1,7 @@ #include "advancedcpp.h" +#include <stdexcept> + // for testing of default arguments #define IMPLEMENT_DEFAULTER_CLASS(type, tname) \ @@ -112,3 +114,13 @@ std::string overload_one_way::gime() { return "aap"; } std::string overload_the_other_way::gime() { return "aap"; } int overload_the_other_way::gime() const { return 1; } + + +// exception handling testing +void Thrower::throw_anything() { + throw 1; +} + +void Thrower::throw_exception() { + throw std::runtime_error("C++ function failed"); +} diff --git a/pypy/module/_cppyy/test/advancedcpp.h b/pypy/module/_cppyy/test/advancedcpp.h index e8f3773930..fefe0f0706 100644 --- a/pypy/module/_cppyy/test/advancedcpp.h +++ b/pypy/module/_cppyy/test/advancedcpp.h @@ -59,7 +59,7 @@ public: class a_class { // for esoteric inheritance testing public: a_class() { m_a = 1; m_da = 1.1; } - ~a_class() {} + virtual ~a_class() {} virtual int get_value() = 0; public: @@ -221,6 +221,7 @@ double pass_double_through_const_ref(const double& d); //=========================================================================== class some_abstract_class { // to test abstract class handling public: + virtual ~some_abstract_class() {} virtual void a_virtual_method() = 0; }; @@ -399,3 +400,11 @@ public: std::string gime(); int gime() const; }; + + +//=========================================================================== +class Thrower { // exception handling testing +public: + void throw_anything(); + void throw_exception(); +}; diff --git a/pypy/module/_cppyy/test/advancedcpp.xml b/pypy/module/_cppyy/test/advancedcpp.xml index 728668da5e..68a9d2c495 100644 --- a/pypy/module/_cppyy/test/advancedcpp.xml +++ b/pypy/module/_cppyy/test/advancedcpp.xml @@ -57,4 +57,6 @@ <class name="overload_one_way" /> <class name="overload_the_other_way" /> + <class name="Thrower" /> + </lcgdict> diff --git a/pypy/module/_cppyy/test/conftest.py b/pypy/module/_cppyy/test/conftest.py index d562bca27f..05c7940693 100644 --- a/pypy/module/_cppyy/test/conftest.py +++ b/pypy/module/_cppyy/test/conftest.py @@ -55,7 +55,7 @@ def pytest_configure(config): separate_module_files=[srcpath.join('dummy_backend.cxx')], include_dirs=[incpath, tstpath, cdir], compile_extra=['-DRPY_EXTERN=RPY_EXPORTED', '-DCPPYY_DUMMY_BACKEND', - '-fno-strict-aliasing', '-std=c++11'], + '-fno-strict-aliasing', '-std=c++14'], use_cpp_linker=True, ) @@ -65,7 +65,7 @@ def pytest_configure(config): outputfilename='libcppyy_dummy_backend', standalone=False) except CompilationError as e: - if '-std=c++11' in str(e): + if '-std=c++14' in str(e): global disabled disabled = str(e) return diff --git a/pypy/module/_cppyy/test/fragile.h b/pypy/module/_cppyy/test/fragile.h index ee21aa770a..ff857e6c4e 100644 --- a/pypy/module/_cppyy/test/fragile.h +++ b/pypy/module/_cppyy/test/fragile.h @@ -30,6 +30,7 @@ public: void overload(int, no_such_class* p = 0) {} }; + static const int dummy_location = 0xdead; class E { @@ -105,6 +106,7 @@ public: class M { public: + virtual ~M() {} enum E1 { kOnce=42 }; enum E2 { kTwice=12 }; }; diff --git a/pypy/module/_cppyy/test/test_advancedcpp.py b/pypy/module/_cppyy/test/test_advancedcpp.py index 760523e220..54a5eea1ec 100644 --- a/pypy/module/_cppyy/test/test_advancedcpp.py +++ b/pypy/module/_cppyy/test/test_advancedcpp.py @@ -656,3 +656,22 @@ class AppTestADVANCEDCPP: # TODO: currently fails b/c double** not understood as &double* #assert cppyy.gbl.my_global_ptr[0] == 1234. + def test22_exceptions(self): + """Catching of C++ exceptions""" + + import _cppyy as cppyy + Thrower = cppyy.gbl.Thrower + + # TODO: clean up this interface: + Thrower.__cppdecl__.get_overload('throw_anything').__useffi__ = False + Thrower.__cppdecl__.get_overload('throw_exception').__useffi__ = False + + t = Thrower() + + assert raises(Exception, t.throw_anything) + assert raises(Exception, t.throw_exception) + + try: + t.throw_exception() + except Exception, e: + "C++ function failed" in str(e) diff --git a/pypy/module/_cppyy/test/test_overloads.py b/pypy/module/_cppyy/test/test_overloads.py index bc9f32363d..5048a71463 100644 --- a/pypy/module/_cppyy/test/test_overloads.py +++ b/pypy/module/_cppyy/test/test_overloads.py @@ -15,7 +15,6 @@ class AppTestOVERLOADS: spaceconfig = dict(usemodules=['_cppyy', '_rawffi', 'itertools']) def setup_class(cls): - env = os.environ cls.w_test_dct = cls.space.newtext(test_dct) cls.w_overloads = cls.space.appexec([], """(): import ctypes diff --git a/pypy/module/_cppyy/test/test_zjit.py b/pypy/module/_cppyy/test/test_zjit.py index 4e79f39777..5c6db4c387 100644 --- a/pypy/module/_cppyy/test/test_zjit.py +++ b/pypy/module/_cppyy/test/test_zjit.py @@ -137,6 +137,7 @@ class FakeSpace(object): executor.get_executor(self, 'int').__class__.c_stubcall = staticmethod(c_call_i) self.w_AttributeError = FakeException(self, "AttributeError") + self.w_Exception = FakeException(self, "Exception") self.w_KeyError = FakeException(self, "KeyError") self.w_NotImplementedError = FakeException(self, "NotImplementedError") self.w_ReferenceError = FakeException(self, "ReferenceError") @@ -282,7 +283,7 @@ class TestFastPathJIT(LLJitMixin): inst = interp_cppyy._bind_object(space, FakeInt(0), cls, True) cls.get_overload("__init__").call(inst, [FakeInt(0)]) cppmethod = cls.get_overload(method_name) - assert isinstance(inst, interp_cppyy.W_CPPClass) + assert isinstance(inst, interp_cppyy.W_CPPInstance) i = 10 while i > 0: drv.jit_merge_point(inst=inst, cppmethod=cppmethod, i=i) diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py index ce0bacc004..135f148f08 100644 --- a/pypy/module/cpyext/cdatetime.py +++ b/pypy/module/cpyext/cdatetime.py @@ -124,7 +124,7 @@ def init_datetime(space): # app level datetime.date. If a c-extension class uses datetime.date for its # base class and defines a tp_dealloc, we will get this: # c_class->tp_dealloc == tp_dealloc_func - # c_class->tp_base == datetime.date, + # c_class->tp_base == datetime.date, # datetime.date->tp_dealloc = _PyPy_subtype_dealloc # datetime.date->tp_base = W_DateTime_Date # W_DateTime_Date->tp_dealloc = _PyPy_subtype_dealloc diff --git a/pypy/module/cpyext/test/test_datetime.py b/pypy/module/cpyext/test/test_datetime.py index cad152e5f2..5f494d9221 100644 --- a/pypy/module/cpyext/test/test_datetime.py +++ b/pypy/module/cpyext/test/test_datetime.py @@ -1,3 +1,5 @@ +import pytest + from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.cdatetime import * @@ -82,6 +84,14 @@ class TestDatetime(BaseApiTest): date = datetime.datetime.fromtimestamp(0) assert space.unwrap(space.str(w_date)) == str(date) + @pytest.mark.parametrize('name', ['Time', 'DateTime', 'Date', 'Delta']) + def test_basicsize(self, space, name): + datetime = _PyDateTime_Import(space) + py_size = getattr(datetime, "c_%sType" % name).c_tp_basicsize + c_size = rffi.sizeof(cts.gettype("PyDateTime_%s" % name)) + assert py_size == c_size + + class AppTestDatetime(AppTestCpythonExtensionBase): def test_CAPI(self): module = self.import_extension('foo', [ @@ -271,9 +281,9 @@ class AppTestDatetime(AppTestCpythonExtensionBase): 6, 6, 6, 6, args, PyDateTimeAPI->TimeType); """), ("datetime_with_tzinfo", "METH_O", - """ + """ PyObject * obj; - int tzrefcnt = args->ob_refcnt; + int tzrefcnt = args->ob_refcnt; PyDateTime_IMPORT; obj = PyDateTimeAPI->DateTime_FromDateAndTime( 2000, 6, 6, 6, 6, 6, 6, args, @@ -291,7 +301,7 @@ class AppTestDatetime(AppTestCpythonExtensionBase): return NULL; } return obj; - + """), ], prologue='#include "datetime.h"\n') from datetime import tzinfo, datetime, timedelta, time @@ -339,4 +349,4 @@ class AppTestDatetime(AppTestCpythonExtensionBase): assert module.checks(o) == (True,) * 3 + (False,) * 7 # isinstance(datetime, date) o = tzinfo() assert module.checks(o) == (False,) * 8 + (True,) * 2 - + diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py index 4186b7cef9..582f4d6452 100644 --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -968,6 +968,14 @@ class EmptyListStrategy(ListStrategy): def setslice(self, w_list, start, step, slicelength, w_other): strategy = w_other.strategy + if step != 1: + len2 = strategy.length(w_other) + if len2 == 0: + return + else: + raise oefmt(self.space.w_ValueError, + "attempt to assign sequence of size %d to extended " + "slice of size %d", len2, 0) storage = strategy.getstorage_copy(w_other) w_list.strategy = strategy w_list.lstorage = storage diff --git a/pypy/objspace/std/test/test_listobject.py b/pypy/objspace/std/test/test_listobject.py index fac306de1a..6c6f576965 100644 --- a/pypy/objspace/std/test/test_listobject.py +++ b/pypy/objspace/std/test/test_listobject.py @@ -1078,6 +1078,15 @@ class AppTestListObject(object): l[::3] = ('a', 'b') assert l == ['a', 1.1, 2.2, 'b', 4.4, 5.5] + l_int = [5]; l_int.pop() # IntListStrategy + l_empty = [] # EmptyListStrategy + raises(ValueError, "l_int[::-1] = [42]") + raises(ValueError, "l_int[::7] = [42]") + raises(ValueError, "l_empty[::-1] = [42]") + raises(ValueError, "l_empty[::7] = [42]") + l_int[::1] = [42]; assert l_int == [42] + l_empty[::1] = [42]; assert l_empty == [42] + def test_setslice_with_self(self): l = [1,2,3,4] l[:] = l |