Author: Wim Lavrijsen <wlavrij...@lbl.gov> Branch: Changeset: r94811:53d95e0b1421 Date: 2018-07-05 18:11 -0700 http://bitbucket.org/pypy/pypy/changeset/53d95e0b1421/
Log: merge cppyy-packaging: fixes performance regression (see: https://bitbucket.org/wlav/cppyy/issues/34/class-member-function- calls-not-being) 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 @@ -82,10 +82,9 @@ class TypeConverter(object): - _immutable_fields_ = ['cffi_name', 'uses_local', 'name'] + _immutable_fields_ = ['cffi_name', 'name'] cffi_name = None - uses_local = False name = "" def __init__(self, space, extra): @@ -108,10 +107,10 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): self._is_abstract(space) - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible @@ -125,10 +124,10 @@ def to_memory(self, space, w_obj, w_value, offset): self._is_abstract(space) - def finalize_call(self, space, w_obj, call_local): + def finalize_call(self, space, w_obj): pass - def free_argument(self, space, arg, call_local): + def free_argument(self, space, arg): pass @@ -172,7 +171,7 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): w_tc = space.findattr(w_obj, space.newtext('typecode')) if w_tc is not None and space.text_w(w_tc) != self.typecode: raise oefmt(space.w_TypeError, @@ -208,7 +207,7 @@ class NumericTypeConverterMixin(object): _mixin_ = True - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) @@ -228,26 +227,23 @@ class ConstRefNumericTypeConverterMixin(NumericTypeConverterMixin): _mixin_ = True - _immutable_fields_ = ['uses_local'] - - uses_local = True def cffi_type(self, space): state = space.fromcache(ffitypes.State) return state.c_voidp - def convert_argument_libffi(self, space, w_obj, address, call_local): - assert rffi.sizeof(self.c_type) <= 2*rffi.sizeof(rffi.VOIDP) # see interp_cppyy.py + def convert_argument_libffi(self, space, w_obj, address, scratch): obj = self._unwrap_object(space, w_obj) - typed_buf = rffi.cast(self.c_ptrtype, call_local) + typed_buf = rffi.cast(self.c_ptrtype, scratch) typed_buf[0] = obj x = rffi.cast(rffi.VOIDPP, address) - x[0] = call_local + x[0] = scratch + class IntTypeConverterMixin(NumericTypeConverterMixin): _mixin_ = True - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) ba = rffi.cast(rffi.CCHARP, address) @@ -256,7 +252,7 @@ class FloatTypeConverterMixin(NumericTypeConverterMixin): _mixin_ = True - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) ba = rffi.cast(rffi.CCHARP, address) @@ -273,18 +269,18 @@ state = space.fromcache(ffitypes.State) return state.c_void - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): self._is_abstract(space) class BoolConverter(ffitypes.typeid(bool), TypeConverter): - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.LONGP, address) x[0] = self._unwrap_object(space, w_obj) ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'b' - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): x = rffi.cast(rffi.LONGP, address) x[0] = self._unwrap_object(space, w_obj) @@ -303,13 +299,13 @@ address[0] = '\x00' class CharConverter(ffitypes.typeid(rffi.CHAR), TypeConverter): - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.CCHARP, address) x[0] = self._unwrap_object(space, w_obj) ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'b' - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) @@ -348,7 +344,7 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible @@ -381,7 +377,7 @@ class CStringConverter(TypeConverter): - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.LONGP, address) arg = space.text_w(w_obj) x[0] = rffi.cast(rffi.LONG, rffi.str2charp(arg)) @@ -393,7 +389,7 @@ charpptr = rffi.cast(rffi.CCHARPP, address) return space.newtext(rffi.charp2str(charpptr[0])) - def free_argument(self, space, arg, call_local): + def free_argument(self, space, arg): lltype.free(rffi.cast(rffi.CCHARPP, arg)[0], flavor='raw') class CStringConverterWithSize(CStringConverter): @@ -423,13 +419,13 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.VOIDPP, address) x[0] = self._unwrap_object(space, w_obj) ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'o' - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): x = rffi.cast(rffi.VOIDPP, address) x[0] = self._unwrap_object(space, w_obj) @@ -452,37 +448,39 @@ address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value)) class VoidPtrPtrConverter(TypeConverter): - _immutable_fields_ = ['uses_local', 'typecode'] + typecode = 'p' - uses_local = True - typecode = 'a' + def __init__(self, space, extra): + self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.VOIDPP, address) - ba = rffi.cast(rffi.CCHARP, address) try: x[0] = get_rawbuffer(space, w_obj) except TypeError: - r = rffi.cast(rffi.VOIDPP, call_local) - r[0] = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) - x[0] = rffi.cast(rffi.VOIDP, call_local) + ptr = rffi.cast(rffi.VOIDP, get_rawobject(space, w_obj)) + self.ref_buffer = lltype.malloc(rffi.VOIDPP.TO, 1, flavor='raw') + self.ref_buffer[0] = ptr + x[0] = self.ref_buffer + ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = self.typecode - def finalize_call(self, space, w_obj, call_local): - r = rffi.cast(rffi.VOIDPP, call_local) - try: - set_rawobject(space, w_obj, r[0]) - except OperationError: - pass # no set on buffer/array/None + def finalize_call(self, space, w_obj): + if self.ref_buffer: + set_rawobject(space, w_obj, self.ref_buffer[0]) + + def free_argument(self, space, arg): + if self.ref_buffer: + lltype.free(self.ref_buffer, flavor='raw') + self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) class VoidPtrRefConverter(VoidPtrPtrConverter): - _immutable_fields_ = ['uses_local', 'typecode'] - uses_local = True + _immutable_fields_ = ['typecode'] typecode = 'V' class InstanceRefConverter(TypeConverter): _immutable_fields_ = ['typecode', 'clsdecl'] - typecode = 'V' + typecode = 'V' def __init__(self, space, clsdecl): from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl @@ -508,14 +506,14 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.VOIDPP, address) x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) address = rffi.cast(capi.C_OBJECT, address) ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = self.typecode - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): x = rffi.cast(rffi.VOIDPP, address) x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) @@ -539,7 +537,7 @@ class InstanceConverter(InstanceRefConverter): - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible # TODO: by-value is a jit_libffi special case @@ -551,9 +549,8 @@ def to_memory(self, space, w_obj, w_value, offset): self._is_abstract(space) - class InstancePtrConverter(InstanceRefConverter): - typecode = 'o' + typecode = 'o' def _unwrap_object(self, space, w_obj): try: @@ -574,36 +571,41 @@ address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value)) class InstancePtrPtrConverter(InstancePtrConverter): - _immutable_fields_ = ['uses_local'] + typecode = 'o' - uses_local = True + def __init__(self, space, extra): + InstancePtrConverter.__init__(self, space, extra) + self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) - def convert_argument(self, space, w_obj, address, call_local): - r = rffi.cast(rffi.VOIDPP, call_local) - r[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.VOIDPP, address) - x[0] = rffi.cast(rffi.VOIDP, call_local) - address = rffi.cast(capi.C_OBJECT, address) + ptr = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) + self.ref_buffer = lltype.malloc(rffi.VOIDPP.TO, 1, flavor='raw') + self.ref_buffer[0] = ptr + x[0] = self.ref_buffer ba = rffi.cast(rffi.CCHARP, address) - ba[capi.c_function_arg_typeoffset(space)] = 'o' + ba[capi.c_function_arg_typeoffset(space)] = self.typecode - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): # TODO: finalize_call not yet called for fast call (see interp_cppyy.py) from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible - def finalize_call(self, space, w_obj, call_local): - 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]) - def from_memory(self, space, w_obj, w_pycppclass, 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 finalize_call(self, space, w_obj): + if self.ref_buffer: + set_rawobject(space, w_obj, self.ref_buffer[0]) + + def free_argument(self, space, arg): + if self.ref_buffer: + lltype.free(self.ref_buffer, flavor='raw') + self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) + class StdStringConverter(InstanceConverter): def __init__(self, space, extra): @@ -628,7 +630,7 @@ except Exception: InstanceConverter.to_memory(self, space, w_obj, w_value, offset) - def free_argument(self, space, arg, call_local): + def free_argument(self, space, arg): capi.c_destruct(space, self.clsdecl, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0])) class StdStringRefConverter(InstancePtrConverter): @@ -646,7 +648,7 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): if hasattr(space, "fake"): raise NotImplementedError space.getbuiltinmodule("cpyext") @@ -657,7 +659,7 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'a' - def convert_argument_libffi(self, space, w_obj, address, call_local): + def convert_argument_libffi(self, space, w_obj, address, scratch): # TODO: free_argument not yet called for fast call (see interp_cppyy.py) from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible @@ -671,7 +673,7 @@ x = rffi.cast(rffi.VOIDPP, address) x[0] = rffi.cast(rffi.VOIDP, ref)""" - def free_argument(self, space, arg, call_local): + def free_argument(self, space, arg): if hasattr(space, "fake"): raise NotImplementedError space.getbuiltinmodule("cpyext") @@ -685,7 +687,7 @@ def __init__(self, space, signature): self.signature = signature - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): # TODO: atm, does not actually get an overload, but a staticmethod from pypy.module._cppyy.interp_cppyy import W_CPPOverload cppol = space.interp_w(W_CPPOverload, w_obj) @@ -740,7 +742,7 @@ raise oefmt(space.w_TypeError, "cannot pass %T instance as %s", w_obj, self.rawdecl.name) - def convert_argument(self, space, w_obj, address, call_local): + def convert_argument(self, space, w_obj, address): x = rffi.cast(rffi.VOIDPP, address) x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj)) address = rffi.cast(capi.C_OBJECT, address) 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 @@ -1,6 +1,9 @@ import pypy.module._cppyy.capi as capi from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.function import Method +from pypy.interpreter.argument import Arguments +from pypy.interpreter.typedef import interp_attrproperty_w, descr_generic_ne, make_weakref_descr from pypy.interpreter.gateway import interp2app, unwrap_spec from pypy.interpreter.typedef import TypeDef, GetSetProperty, interp_attrproperty from pypy.interpreter.baseobjspace import W_Root @@ -166,11 +169,13 @@ # W_CPPConstructorOverload: constructors # W_CPPStaticOverload: free and static functions # W_CPPTemplateOverload: templated methods -# W_CPPTemplateStaticOveload: templated free and static functions +# W_CPPTemplateStaticOverload: templated free and static functions # # CPPMethod: a single function or method (base class) # CPPSetItem: specialization for Python's __setitem__ # +# MethodWithProps: python instancemethod that forwards properties +# # All methods/functions derive from CPPMethod and are collected as overload # candidates in user-facing overload classes. Templated methods are a two-step # process, where first the template is instantiated (or selected if already @@ -183,13 +188,13 @@ the memory_regulator.""" _attrs_ = ['space', 'scope', 'cppmethod', 'arg_defs', 'args_required', - 'converters', 'executor', '_funcaddr', 'cif_descr', 'uses_local'] + 'converters', 'executor', '_funcaddr', 'cif_descr'] _immutable_fields_ = ['scope', 'cppmethod', 'arg_defs', 'args_required', - 'converters', 'executor', 'uses_local'] + 'converters', 'executor', '_funcaddr', 'cif_descr'] - def __init__(self, space, declaring_scope, cppmethod, arg_defs, args_required): + def __init__(self, space, decl_scope, cppmethod, arg_defs, args_required): self.space = space - self.scope = declaring_scope + self.scope = decl_scope self.cppmethod = cppmethod self.arg_defs = arg_defs self.args_required = args_required @@ -200,14 +205,6 @@ self.executor = None self.cif_descr = lltype.nullptr(jit_libffi.CIF_DESCRIPTION) self._funcaddr = lltype.nullptr(capi.C_FUNC_PTR.TO) - self.uses_local = False - - def _address_from_local_buffer(self, call_local, idx): - if not call_local: - return call_local - stride = 2*rffi.sizeof(rffi.VOIDP) - loc_idx = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, call_local), idx*stride) - return rffi.cast(rffi.VOIDP, loc_idx) @jit.unroll_safe def call(self, cppthis, args_w, useffi): @@ -233,45 +230,35 @@ except Exception: pass - # some calls, e.g. for ptr-ptr or reference need a local array to store data for - # the duration of the call - if self.uses_local: - call_local = lltype.malloc(rffi.VOIDP.TO, 2*len(args_w), flavor='raw') - else: - call_local = lltype.nullptr(rffi.VOIDP.TO) + # attempt to call directly through ffi chain + if useffi and self._funcaddr: + try: + return self.do_fast_call(cppthis, args_w) + except FastCallNotPossible: + pass # can happen if converters or executor does not implement ffi + # ffi chain must have failed; using stub functions instead + args, stat = self.prepare_arguments(args_w) try: - # attempt to call directly through ffi chain - 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, stat = self.prepare_arguments(args_w, call_local) - try: - 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) + 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: - if call_local: - lltype.free(call_local, flavor='raw') + self.finalize_call(args, args_w) @jit.unroll_safe - def do_fast_call(self, cppthis, args_w, call_local): + def do_fast_call(self, cppthis, args_w): if self.cif_descr == lltype.nullptr(jit_libffi.CIF_DESCRIPTION): raise FastCallNotPossible jit.promote(self) cif_descr = self.cif_descr - buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size, flavor='raw') + # add extra space for const-ref support (see converter.py) + buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size+len(args_w)*rffi.sizeof(rffi.DOUBLE), flavor='raw') try: # this pointer data = rffi.ptradd(buffer, cif_descr.exchange_args[0]) @@ -284,7 +271,8 @@ conv = self.converters[i] w_arg = args_w[i] data = rffi.ptradd(buffer, cif_descr.exchange_args[i+1]) - conv.convert_argument_libffi(self.space, w_arg, data, call_local) + scratch = rffi.ptradd(buffer, cif_descr.exchange_size+i*rffi.sizeof(rffi.DOUBLE)) + conv.convert_argument_libffi(self.space, w_arg, data, scratch) for j in range(i+1, len(self.arg_defs)): conv = self.converters[j] data = rffi.ptradd(buffer, cif_descr.exchange_args[j+1]) @@ -346,11 +334,6 @@ self.executor = executor.get_executor( self.space, capi.c_method_result_type(self.space, self.cppmethod)) - for conv in self.converters: - if conv.uses_local: - self.uses_local = True - break - # 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. @@ -386,7 +369,7 @@ self._funcaddr = funcaddr @jit.unroll_safe - def prepare_arguments(self, args_w, call_local): + def prepare_arguments(self, args_w): args = capi.c_allocate_function_args(self.space, len(args_w)) stride = capi.c_function_arg_sizeof(self.space) for i in range(len(args_w)): @@ -394,15 +377,13 @@ w_arg = args_w[i] try: arg_i = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args), i*stride) - loc_i = self._address_from_local_buffer(call_local, i) - conv.convert_argument(self.space, w_arg, rffi.cast(capi.C_OBJECT, arg_i), loc_i) + conv.convert_argument(self.space, w_arg, rffi.cast(capi.C_OBJECT, arg_i)) except: # fun :-( for j in range(i): conv = self.converters[j] arg_j = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args), j*stride) - loc_j = self._address_from_local_buffer(call_local, j) - conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_j), loc_j) + conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_j)) capi.c_deallocate_function_args(self.space, args) raise stat = rffi.cast(rffi.ULONGP, @@ -411,14 +392,13 @@ return args, stat @jit.unroll_safe - def finalize_call(self, args, args_w, call_local): + def finalize_call(self, args, args_w): stride = capi.c_function_arg_sizeof(self.space) for i in range(len(args_w)): conv = self.converters[i] arg_i = lltype.direct_ptradd(rffi.cast(rffi.CCHARP, args), i*stride) - loc_i = self._address_from_local_buffer(call_local, i) - conv.finalize_call(self.space, args_w[i], loc_i) - conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_i), loc_i) + conv.finalize_call(self.space, args_w[i]) + conv.free_argument(self.space, rffi.cast(capi.C_OBJECT, arg_i)) capi.c_deallocate_function_args(self.space, args) def signature(self, show_formalargs=True): @@ -466,42 +446,81 @@ CPPMethod.call(self, cppthis, args_w, useffi) +# CPPOverloads have settable flags that control memory and ffi behavior. These flags +# need forwarding, which the normal instancemethod does not provide, hence this +# derived class. +class MethodWithProps(Method): + # allow user to determine ffi use rules per overload + def fget_useffi(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_useffi(space) + + @unwrap_spec(value=bool) + def fset_useffi(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_useffi(space, value) + +MethodWithProps.typedef = TypeDef( + "cpp_instancemethod", + __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), + __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), + ) +MethodWithProps.typedef.acceptable_as_base_class = False + + class W_CPPOverload(W_Root): """App-level dispatcher: controls a collection of (potentially) overloaded methods or functions. Calls these in order and deals with error handling and reporting.""" - _attrs_ = ['space', 'scope', 'functions', 'flags', 'w_this'] + _attrs_ = ['space', 'scope', 'functions', 'flags'] _immutable_fields_ = ['scope', 'functions[*]'] - def __init__(self, space, declaring_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): + def __init__(self, space, decl_scope, funcs, flags = OVERLOAD_FLAGS_USE_FFI): self.space = space - self.scope = declaring_scope + self.scope = decl_scope from rpython.rlib import debug - self.functions = debug.make_sure_not_resized(functions) + self.functions = debug.make_sure_not_resized(funcs) self.flags = flags - self.w_this = self.space.w_None + + def descr_get(self, w_obj, w_cls=None): + """functionobject.__get__(obj[, type]) -> method""" + # TODO: check validity of w_cls if given + space = self.space + asking_for_bound = (space.is_none(w_cls) or + not space.is_w(w_obj, space.w_None) or + space.is_w(w_cls, space.type(space.w_None))) + if asking_for_bound: + return MethodWithProps(space, self, w_obj, w_cls) + else: + return MethodWithProps(space, self, None, w_cls) @unwrap_spec(args_w='args_w') - def descr_get(self, w_cppinstance, args_w): - if self.space.is_w(w_cppinstance, self.space.w_None): - return self # unbound, so no new instance needed - cppol = W_CPPOverload(self.space, self.scope, self.functions, self.flags) - cppol.w_this = w_cppinstance - return cppol # bound - - @unwrap_spec(args_w='args_w') - def call(self, args_w): - if self.space.is_w(self.w_this, self.space.w_None) and len(args_w): - w_this = args_w[0] - args_w = args_w[1:] - else: - w_this = self.w_this + def call_args(self, args_w): + jit.promote(self) + w_this = args_w[0] cppinstance = self.space.interp_w(W_CPPInstance, w_this) cppinstance._nullcheck() if not capi.c_is_subtype(self.space, cppinstance.clsdecl, self.scope): raise oefmt(self.space.w_TypeError, "cannot pass %T instance as %s", w_this, self.scope.name) - return self.call_impl(cppinstance.get_cppthis(self.scope), args_w) + return self.call_impl(cppinstance.get_cppthis(self.scope), args_w[1:]) @jit.unroll_safe def call_impl(self, cppthis, args_w): @@ -576,13 +595,17 @@ def fget_doc(self, space): return self.prototype() + def getname(self, space): + # for the benefit of Method/instancemethod + return capi.c_method_name(space, self.functions[0].cppmethod) + def __repr__(self): return "W_CPPOverload(%s)" % [f.prototype() for f in self.functions] W_CPPOverload.typedef = TypeDef( 'CPPOverload', __get__ = interp2app(W_CPPOverload.descr_get), - __call__ = interp2app(W_CPPOverload.call), + __call__ = interp2app(W_CPPOverload.call_args), __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), __doc__ = GetSetProperty(W_CPPOverload.fget_doc) ) @@ -593,27 +616,24 @@ class W_CPPStaticOverload(W_CPPOverload): _attrs_ = [] - @unwrap_spec(args_w='args_w') - def descr_get(self, w_cppinstance, args_w): - if isinstance(w_cppinstance, W_CPPInstance): + def descr_get(self, w_obj, w_cls=None): + if isinstance(w_obj, W_CPPInstance): # two possibilities: this is a static function called on an # instance and w_this must not be set, or a free function rebound # onto a class and w_this should be set - cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance) + cppinstance = self.space.interp_w(W_CPPInstance, w_obj) if cppinstance.clsdecl.handle != self.scope.handle: - cppol = W_CPPStaticOverload(self.space, self.scope, self.functions, self.flags) - cppol.w_this = w_cppinstance - return cppol # bound + return MethodWithProps(self.space, self, w_obj, w_cls) # bound return self # unbound @unwrap_spec(args_w='args_w') - def call(self, args_w): - if not self.space.is_w(self.w_this, self.space.w_None): - # free function used as bound method, put self back into args_w - cppinstance = self.space.interp_w(W_CPPInstance, self.w_this) - cppinstance._nullcheck() - args_w = [self.w_this] + 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] @@ -621,7 +641,7 @@ W_CPPStaticOverload.typedef = TypeDef( 'CPPStaticOverload', __get__ = interp2app(W_CPPStaticOverload.descr_get), - __call__ = interp2app(W_CPPStaticOverload.call), + __call__ = interp2app(W_CPPStaticOverload.call_args), __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) ) @@ -631,26 +651,15 @@ _attrs_ = [] @unwrap_spec(args_w='args_w') - def descr_get(self, w_cppinstance, args_w): - if self.space.is_w(w_cppinstance, self.space.w_None): - return self # unbound (TODO: probably useless) - cppol = W_CPPConstructorOverload(self.space, self.scope, self.functions, self.flags) - cppol.w_this = w_cppinstance - return cppol # bound - - @unwrap_spec(args_w='args_w') - def call(self, 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) - if self.space.is_w(self.w_this, self.space.w_None) and len(args_w): - cppinstance = self.space.interp_w(W_CPPInstance, args_w[0]) - args_w = args_w[1:] - else: - cppinstance = self.space.interp_w(W_CPPInstance, self.w_this) - w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle), args_w) + 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)) if cppinstance is not None: cppinstance._rawobject = newthis @@ -662,7 +671,7 @@ W_CPPConstructorOverload.typedef = TypeDef( 'CPPConstructorOverload', __get__ = interp2app(W_CPPConstructorOverload.descr_get), - __call__ = interp2app(W_CPPConstructorOverload.call), + __call__ = interp2app(W_CPPConstructorOverload.call_args), __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc) ) @@ -725,7 +734,9 @@ # try to match with run-time instantiations for cppol in self.master.overloads.values(): try: - return cppol.descr_get(self.w_this, []).call(args_w) + if not self.space.is_w(self.w_this, self.space.w_None): + return self.space.call_obj_args(cppol, self.w_this, Arguments(self.space, args_w)) + return self.space.call_args(cppol, Arguments(self.space, args_w)) except Exception: pass # completely ignore for now; have to see whether errors become confusing @@ -748,7 +759,9 @@ except KeyError: self.master.overloads[fullname] = method - return method.descr_get(self.w_this, []).call(args_w) + if not self.space.is_w(self.w_this, self.space.w_None): + return self.space.call_obj_args(method, self.w_this, Arguments(self.space, args_w)) + return self.space.call_args(method, Arguments(self.space, args_w)) def getitem_impl(self, name, args_w): space = self.space @@ -771,25 +784,25 @@ if c_fullname != fullname: self.master.overloads[c_fullname] = method - return method.descr_get(self.w_this, []) + return method.descr_get(self.w_this, None) class W_CPPTemplateOverload(W_CPPOverload, TemplateOverloadMixin): """App-level dispatcher to allow both lookup/instantiation of templated methods and dispatch among overloads between templated and non-templated method.""" - _attrs_ = ['name', 'overloads', 'master'] + _attrs_ = ['name', 'overloads', 'master', 'w_this'] _immutable_fields_ = ['name'] - def __init__(self, space, name, declaring_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): - W_CPPOverload.__init__(self, space, declaring_scope, functions, flags) + def __init__(self, space, name, decl_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): + W_CPPOverload.__init__(self, space, decl_scope, functions, flags) self.name = name self.overloads = {} self.master = self + self.w_this = space.w_None - @unwrap_spec(args_w='args_w') - def descr_get(self, w_cppinstance, args_w): - # like W_CPPOverload, but returns W_CPPTemplateOverload + def descr_get(self, w_cppinstance, w_cls=None): + # TODO: don't return copy, but bind in an external object (like W_CPPOverload) if self.space.is_w(w_cppinstance, self.space.w_None): return self # unbound, so no new instance needed cppol = W_CPPTemplateOverload(self.space, self.name, self.scope, self.functions, self.flags) @@ -798,13 +811,13 @@ return cppol # bound @unwrap_spec(args_w='args_w') - def call(self, args_w): + def call_args(self, args_w): # direct call: either pick non-templated overload or attempt to deduce # the template instantiation from the argument types # try existing overloads or compile-time instantiations try: - return W_CPPOverload.call(self, args_w) + return W_CPPOverload.call_args(self, args_w) except Exception: pass @@ -814,6 +827,9 @@ def getitem(self, args_w): return self.getitem_impl(self.name, args_w) + def getname(self, space): + return self.name + def __repr__(self): return "W_CPPTemplateOverload(%s)" % [f.prototype() for f in self.functions] @@ -821,7 +837,7 @@ 'CPPTemplateOverload', __get__ = interp2app(W_CPPTemplateOverload.descr_get), __getitem__ = interp2app(W_CPPTemplateOverload.getitem), - __call__ = interp2app(W_CPPTemplateOverload.call), + __call__ = interp2app(W_CPPTemplateOverload.call_args), __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) ) @@ -830,18 +846,18 @@ """App-level dispatcher to allow both lookup/instantiation of templated methods and dispatch among overloads between templated and non-templated method.""" - _attrs_ = ['name', 'overloads', 'master'] + _attrs_ = ['name', 'overloads', 'master', 'w_this'] _immutable_fields_ = ['name'] - def __init__(self, space, name, declaring_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): - W_CPPStaticOverload.__init__(self, space, declaring_scope, functions, flags) + def __init__(self, space, name, decl_scope, funcs, flags = OVERLOAD_FLAGS_USE_FFI): + W_CPPStaticOverload.__init__(self, space, decl_scope, funcs, flags) self.name = name self.overloads = {} self.master = self + self.w_this = space.w_None - @unwrap_spec(args_w='args_w') - def descr_get(self, w_cppinstance, args_w): - # like W_CPPStaticOverload, but returns W_CPPTemplateStaticOverload + def descr_get(self, w_cppinstance, w_cls=None): + # TODO: don't return copy, but bind in an external object (like W_CPPOverload) if isinstance(w_cppinstance, W_CPPInstance): cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance) if cppinstance.clsdecl.handle != self.scope.handle: @@ -852,13 +868,13 @@ return self # unbound @unwrap_spec(args_w='args_w') - def call(self, args_w): + def call_args(self, args_w): # direct call: either pick non-templated overload or attempt to deduce # the template instantiation from the argument types # try existing overloads or compile-time instantiations try: - return W_CPPStaticOverload.call(self, args_w) + return W_CPPStaticOverload.call_args(self, args_w) except Exception: pass @@ -869,6 +885,9 @@ def getitem(self, args_w): return self.getitem_impl(self.name, args_w) + def getname(self, space): + return self.name + def __repr__(self): return "W_CPPTemplateStaticOverload(%s)" % [f.prototype() for f in self.functions] @@ -876,7 +895,7 @@ 'CPPTemplateStaticOverload', __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), - __call__ = interp2app(W_CPPTemplateStaticOverload.call), + __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi), __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc) ) @@ -898,9 +917,9 @@ _attrs_ = ['space', 'scope', 'converter', 'offset'] _immutable_fields = ['scope', 'converter', 'offset'] - def __init__(self, space, declaring_scope, type_name, offset): + def __init__(self, space, decl_scope, type_name, offset): self.space = space - self.scope = declaring_scope + self.scope = decl_scope self.converter = converter.get_converter(self.space, type_name, '') self.offset = offset @@ -1417,7 +1436,7 @@ ol = W_CPPStaticOverload(self.space, nss, funcs[:]) # TODO: cache this operator (not done yet, as the above does not # select all overloads) - return ol.call([self, w_other]) + return ol.call_args([self, w_other]) except OperationError as e: if not e.match(self.space, self.space.w_TypeError): raise diff --git a/pypy/module/_cppyy/test/test_zjit.py b/pypy/module/_cppyy/test/test_zjit.py --- a/pypy/module/_cppyy/test/test_zjit.py +++ b/pypy/module/_cppyy/test/test_zjit.py @@ -205,6 +205,9 @@ def exception_match(self, typ, sub): return typ is sub + def is_none(self, w_obj): + return w_obj is None + def is_w(self, w_one, w_two): return w_one is w_two @@ -268,6 +271,9 @@ def call_function(self, w_func, *args_w): return None + def call_obj_args(self, w_callable, w_obj, args): + return w_callable.call_args([w_obj]+args) + def _freeze_(self): return True @@ -283,19 +289,19 @@ def f(): cls = interp_cppyy.scope_byname(space, "example01") inst = interp_cppyy._bind_object(space, FakeInt(0), cls, True) - cls.get_overload("__init__").descr_get(inst, []).call([FakeInt(0)]) + cls.get_overload("__init__").descr_get(inst, []).call_args([FakeInt(0)]) cppmethod = cls.get_overload(method_name) assert isinstance(inst, interp_cppyy.W_CPPInstance) i = 10 while i > 0: drv.jit_merge_point(inst=inst, cppmethod=cppmethod, i=i) - cppmethod.descr_get(inst, []).call([FakeInt(i)]) + cppmethod.descr_get(inst, []).call_args([FakeInt(i)]) i -= 1 return 7 f() space = FakeSpace() result = self.meta_interp(f, [], listops=True, backendopt=True, listcomp=True) - self.check_jitcell_token_count(0) # same for fast and slow path?? + self.check_jitcell_token_count(1) # rely on replacement of capi calls to raise exception instead (see FakeSpace.__init__) @py.test.mark.dont_track_allocations("cppmethod.cif_descr kept 'leaks'") _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit