Author: Matti Picus <matti.pi...@gmail.com> Branch: unicode-utf8-py3 Changeset: r95004:6ef4fd4b0165 Date: 2018-08-13 09:40 -0700 http://bitbucket.org/pypy/pypy/changeset/6ef4fd4b0165/
Log: merge py3.5 into branch diff too long, truncating to 2000 out of 5190 lines diff --git a/lib-python/3/hashlib.py b/lib-python/3/hashlib.py --- a/lib-python/3/hashlib.py +++ b/lib-python/3/hashlib.py @@ -134,9 +134,14 @@ __get_hash = __get_openssl_constructor algorithms_available = algorithms_available.union( _hashlib.openssl_md_meth_names) -except ImportError: +except ImportError as e: new = __py_new __get_hash = __get_builtin_constructor + # added by PyPy + import warnings + warnings.warn("The _hashlib module is not available, falling back " + "to a much slower implementation (%s)" % str(e), + RuntimeWarning) try: # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA diff --git a/lib-python/3/types.py b/lib-python/3/types.py --- a/lib-python/3/types.py +++ b/lib-python/3/types.py @@ -41,9 +41,19 @@ FrameType = type(tb.tb_frame) tb = None; del tb -# For Jython, the following two types are identical +# +# On CPython, FunctionType.__code__ is a 'getset_descriptor', but +# FunctionType.__globals__ is a 'member_descriptor', just like app-level +# slots. On PyPy, all descriptors of built-in types are +# 'getset_descriptor', but the app-level slots are 'member_descriptor' +# as well. (On Jython the situation might still be different.) +# +# Note that MemberDescriptorType was equal to GetSetDescriptorType in +# PyPy <= 6.0. +# GetSetDescriptorType = type(FunctionType.__code__) -MemberDescriptorType = type(FunctionType.__globals__) +class _C: __slots__ = 's' +MemberDescriptorType = type(_C.s) del sys, _f, _g, _C, _c, # Not for export diff --git a/lib_pypy/_winapi.py b/lib_pypy/_winapi.py --- a/lib_pypy/_winapi.py +++ b/lib_pypy/_winapi.py @@ -98,9 +98,11 @@ def GetOverlappedResult(self, wait): transferred = _ffi.new('DWORD[1]', [0]) res = _kernel32.GetOverlappedResult(self.handle, self.overlapped, transferred, wait != 0) - if not res: - res = GetLastError() - if res in (ERROR_SUCCESS, ERROR_MORE_DATA, ERROR_OPERATION_ABORTED): + if res: + err = ERROR_SUCCESS + else: + err = GetLastError() + if err in (ERROR_SUCCESS, ERROR_MORE_DATA, ERROR_OPERATION_ABORTED): self.completed = 1 self.pending = 0 elif res == ERROR_IO_INCOMPLETE: @@ -133,7 +135,7 @@ assert success == 0 err = _kernel32.GetLastError() if err == ERROR_IO_PENDING: - overlapped[0].pending = 1 + ov.pending = 1 elif err == ERROR_PIPE_CONNECTED: _kernel32.SetEvent(ov.overlapped[0].hEvent) else: diff --git a/lib_pypy/cffi/_cffi_errors.h b/lib_pypy/cffi/_cffi_errors.h --- a/lib_pypy/cffi/_cffi_errors.h +++ b/lib_pypy/cffi/_cffi_errors.h @@ -50,7 +50,9 @@ "import sys\n" "class FileLike:\n" " def write(self, x):\n" - " of.write(x)\n" + " try:\n" + " of.write(x)\n" + " except: pass\n" " self.buf += x\n" "fl = FileLike()\n" "fl.buf = ''\n" diff --git a/lib_pypy/grp.py b/lib_pypy/grp.py --- a/lib_pypy/grp.py +++ b/lib_pypy/grp.py @@ -5,8 +5,8 @@ import os from _pwdgrp_cffi import ffi, lib import _structseq -import thread -_lock = thread.allocate_lock() +import _thread +_lock = _thread.allocate_lock() try: from __pypy__ import builtinify except ImportError: builtinify = lambda f: f diff --git a/lib_pypy/pwd.py b/lib_pypy/pwd.py --- a/lib_pypy/pwd.py +++ b/lib_pypy/pwd.py @@ -12,8 +12,8 @@ from _pwdgrp_cffi import ffi, lib import _structseq -import thread -_lock = thread.allocate_lock() +import _thread +_lock = _thread.allocate_lock() try: from __pypy__ import builtinify except ImportError: builtinify = lambda f: f diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -43,14 +43,10 @@ "_jitlog", ]) -from rpython.jit.backend import detect_cpu -try: - if detect_cpu.autodetect().startswith('x86'): - if not sys.platform.startswith('openbsd'): - working_modules.add('_vmprof') - working_modules.add('faulthandler') -except detect_cpu.ProcessorAutodetectError: - pass +import rpython.rlib.rvmprof.cintf +if rpython.rlib.rvmprof.cintf.IS_SUPPORTED: + working_modules.add('_vmprof') + working_modules.add('faulthandler') translation_modules = default_modules.copy() translation_modules.update([ @@ -323,3 +319,4 @@ parser = to_optparse(config) #, useoptions=["translation.*"]) option, args = parser.parse_args() print config + print working_modules diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -7,9 +7,13 @@ .. branch: cppyy-packaging -Upgrade to backend 1.1.0, improved handling of templated methods and +Main items: vastly better template resolution and improved performance. In +detail: upgrade to backend 1.4, improved handling of templated methods and functions (in particular automatic deduction of types), improved pythonization -interface, and a range of compatibility fixes for Python3 +interface, range of compatibility fixes for Python3, free functions now take +fast libffi path when possible, moves for strings (incl. from Python str), +easier/faster handling of std::vector by numpy, improved and faster object +identity preservation .. branch: socket_default_timeout_blockingness diff --git a/pypy/interpreter/astcompiler/codegen.py b/pypy/interpreter/astcompiler/codegen.py --- a/pypy/interpreter/astcompiler/codegen.py +++ b/pypy/interpreter/astcompiler/codegen.py @@ -367,8 +367,8 @@ names) self._visit_arg_annotations(args.kwonlyargs, names) kwarg = args.kwarg - if args.kwarg: - self._visit_arg_annotation(args.kwarg.arg, args.kwarg.annotation, + if kwarg: + self._visit_arg_annotation(kwarg.arg, kwarg.annotation, names) self._visit_arg_annotation("return", returns, names) l = len(names) diff --git a/pypy/interpreter/astcompiler/symtable.py b/pypy/interpreter/astcompiler/symtable.py --- a/pypy/interpreter/astcompiler/symtable.py +++ b/pypy/interpreter/astcompiler/symtable.py @@ -622,6 +622,10 @@ assert isinstance(args, ast.arguments) if args.args: self._visit_arg_annotations(args.args) + if args.vararg: + self._visit_arg_annotation(args.vararg) + if args.kwarg: + self._visit_arg_annotation(args.kwarg) if args.kwonlyargs: self._visit_arg_annotations(args.kwonlyargs) if func.returns: @@ -630,8 +634,11 @@ def _visit_arg_annotations(self, args): for arg in args: assert isinstance(arg, ast.arg) - if arg.annotation: - arg.annotation.walkabout(self) + self._visit_arg_annotation(arg) + + def _visit_arg_annotation(self, arg): + if arg.annotation: + arg.annotation.walkabout(self) def visit_Name(self, name): if name.ctx == ast.Load: diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py --- a/pypy/interpreter/pyopcode.py +++ b/pypy/interpreter/pyopcode.py @@ -2076,7 +2076,7 @@ else: skip_leading_underscores = False for name in all: - if skip_leading_underscores and name[0]=='_': + if skip_leading_underscores and name and name[0] == '_': continue into_locals[name] = getattr(module, name) ''', filename=__file__) diff --git a/pypy/interpreter/test/test_app_main.py b/pypy/interpreter/test/test_app_main.py --- a/pypy/interpreter/test/test_app_main.py +++ b/pypy/interpreter/test/test_app_main.py @@ -470,7 +470,7 @@ def test_cmd_co_name(self): child = self.spawn(['-c', - 'import sys; print sys._getframe(0).f_code.co_name']) + 'import sys; print(sys._getframe(0).f_code.co_name)']) child.expect('<module>') def test_ignore_python_inspect(self): diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py --- a/pypy/interpreter/test/test_executioncontext.py +++ b/pypy/interpreter/test/test_executioncontext.py @@ -149,7 +149,7 @@ pass """) space.getexecutioncontext().setllprofile(None, None) - assert l == ['call', 'return', 'call', 'return'] + assert l[-4:] == ['call', 'return', 'call', 'return'] def test_llprofile_c_call(self): from pypy.interpreter.function import Function, Method @@ -173,15 +173,15 @@ return """ % snippet) space.getexecutioncontext().setllprofile(None, None) - assert l == ['call', 'return', 'call', 'c_call', 'c_return', 'return'] - if isinstance(seen[0], Method): - w_class = space.type(seen[0].w_instance) + assert l[-6:] == ['call', 'return', 'call', 'c_call', 'c_return', 'return'] + if isinstance(seen[-1], Method): + w_class = space.type(seen[-1].w_instance) found = 'method %s of %s' % ( - seen[0].w_function.name, + seen[-1].w_function.name, w_class.getname(space)) else: - assert isinstance(seen[0], Function) - found = 'builtin %s' % seen[0].name + assert isinstance(seen[-1], Function) + found = 'builtin %s' % seen[-1].name assert found == expected_c_call check_snippet('l = []; l.append(42)', 'method append of list') @@ -210,7 +210,7 @@ return """ % snippet) space.getexecutioncontext().setllprofile(None, None) - assert l == ['call', 'return', 'call', 'c_call', 'c_exception', 'return'] + assert l[-6:] == ['call', 'return', 'call', 'c_call', 'c_exception', 'return'] check_snippet('d = {}; d.__getitem__(42)') diff --git a/pypy/interpreter/test/test_pyframe.py b/pypy/interpreter/test/test_pyframe.py --- a/pypy/interpreter/test/test_pyframe.py +++ b/pypy/interpreter/test/test_pyframe.py @@ -153,6 +153,8 @@ r""" seen = [] def tracer(f, event, *args): + if f.f_code.co_name == "decode": + return tracer seen.append((event, f.f_lineno)) if len(seen) == 5: f.f_lineno = 1 # bug shown only when setting lineno to 1 @@ -297,7 +299,8 @@ l = [] def trace(a,b,c): - l.append((a,b,c)) + if a.f_code.co_name != "decode": + l.append((a,b,c)) def f(): h = _testing.Hidden() diff --git a/pypy/interpreter/test/test_syntax.py b/pypy/interpreter/test/test_syntax.py --- a/pypy/interpreter/test/test_syntax.py +++ b/pypy/interpreter/test/test_syntax.py @@ -691,6 +691,25 @@ "bye" : 5, "kw" : 6, "return" : 42} """ + def test_bug_annotations_lambda(self): + """ + # those used to crash + def broken(*a: lambda x: None): + pass + + def broken(**a: lambda x: None): + pass + """ + + def test_bug_annotation_inside_nested_function(self): + """ + # this used to crash + def f1(): + def f2(*args: int): + pass + f1() + """ + class AppTestSyntaxError: def test_tokenizer_error_location(self): diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py --- a/pypy/module/__builtin__/functional.py +++ b/pypy/module/__builtin__/functional.py @@ -193,8 +193,12 @@ @specialize.arg(2) def min_max(space, args, implementation_of): - if not jit.we_are_jitted() or len(args.arguments_w) != 1 and \ - jit.loop_unrolling_heuristic(args.arguments_w, len(args.arguments_w)): + # the 'normal' version includes a JIT merge point, which will make a + # new loop (from the interpreter or from another JIT loop). If we + # give exactly two arguments to the call to max(), or a JIT virtual + # list of arguments, then we pick the 'unroll' version with no JIT + # merge point. + if jit.isvirtual(args.arguments_w) or len(args.arguments_w) == 2: return min_max_unroll(space, args, implementation_of) else: return min_max_normal(space, args, implementation_of) diff --git a/pypy/module/_cffi_backend/errorbox.py b/pypy/module/_cffi_backend/errorbox.py --- a/pypy/module/_cffi_backend/errorbox.py +++ b/pypy/module/_cffi_backend/errorbox.py @@ -69,7 +69,10 @@ import sys class FileLike: def write(self, x): - of.write(x) + try: + of.write(x) + except: + pass self.buf += x fl = FileLike() fl.buf = '' diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py --- a/pypy/module/_cffi_backend/test/_backend_test_c.py +++ b/pypy/module/_cffi_backend/test/_backend_test_c.py @@ -3935,8 +3935,8 @@ def test_char_pointer_conversion(): import warnings - assert __version__.startswith(("1.8", "1.9", "1.10", "1.11", "1.12")), ( - "consider turning the warning into an error") + assert __version__.startswith("1."), ( + "the warning will be an error if we ever release cffi 2.x") BCharP = new_pointer_type(new_primitive_type("char")) BIntP = new_pointer_type(new_primitive_type("int")) BVoidP = new_pointer_type(new_void_type()) diff --git a/pypy/module/_cppyy/capi/loadable_capi.py b/pypy/module/_cppyy/capi/loadable_capi.py --- a/pypy/module/_cppyy/capi/loadable_capi.py +++ b/pypy/module/_cppyy/capi/loadable_capi.py @@ -1,13 +1,18 @@ import os + from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import intmask from rpython.rlib import jit, jit_libffi, libffi, rdynload, objectmodel from rpython.rlib.rarithmetic import r_singlefloat from rpython.tool import leakfinder -from pypy.interpreter.gateway import interp2app -from pypy.interpreter.error import oefmt +from pypy.interpreter.error import OperationError, oefmt +from pypy.interpreter.argument import Arguments +from pypy.interpreter.gateway import interp2app, interpindirect2app +from pypy.interpreter.typedef import TypeDef +from pypy.objspace.std.iterobject import W_AbstractSeqIterObject +from pypy.module._rawffi.array import W_ArrayInstance from pypy.module._cffi_backend import ctypefunc, ctypeprim, cdataobj, misc from pypy.module._cffi_backend import newtype from pypy.module._cppyy import ffitypes @@ -23,10 +28,11 @@ class _Arg: # poor man's union _immutable_ = True - def __init__(self, tc, h = 0, l = -1, s = '', p = rffi.cast(rffi.VOIDP, 0)): + def __init__(self, tc, h = 0, l = -1, d = -1., s = '', p = rffi.cast(rffi.VOIDP, 0)): self.tc = tc self._handle = h self._long = l + self._double = d self._string = s self._voidp = p @@ -40,6 +46,11 @@ def __init__(self, val): _Arg.__init__(self, 'l', l = val) +class _ArgD(_Arg): + _immutable_ = True + def __init__(self, val): + _Arg.__init__(self, 'd', d = val) + class _ArgS(_Arg): _immutable_ = True def __init__(self, val): @@ -89,6 +100,9 @@ assert obj._voidp != rffi.cast(rffi.VOIDP, 0) data = rffi.cast(rffi.VOIDPP, data) data[0] = obj._voidp + elif obj.tc == 'd': + assert isinstance(argtype, ctypeprim.W_CTypePrimitiveFloat) + misc.write_raw_float_data(data, rffi.cast(rffi.DOUBLE, obj._double), argtype.size) else: # only other use is string assert obj.tc == 's' n = len(obj._string) @@ -182,6 +196,7 @@ 'call_f' : ([c_method, c_object, c_int, c_voidp], c_float), 'call_d' : ([c_method, c_object, c_int, c_voidp], c_double), 'call_ld' : ([c_method, c_object, c_int, c_voidp], c_ldouble), + 'call_nld' : ([c_method, c_object, c_int, c_voidp], c_double), 'call_r' : ([c_method, c_object, c_int, c_voidp], c_voidp), # call_s actually takes an size_t* as last parameter, but this will do @@ -236,6 +251,8 @@ 'method_prototype' : ([c_scope, c_method, c_int], c_ccharp), 'is_const_method' : ([c_method], c_int), + 'get_num_templated_methods': ([c_scope], c_int), + 'get_templated_method_name': ([c_scope, c_index], c_ccharp), 'exists_method_template' : ([c_scope, c_ccharp], c_int), 'method_is_template' : ([c_scope, c_index], c_int), 'get_method_template' : ([c_scope, c_ccharp, c_ccharp], c_method), @@ -272,9 +289,11 @@ 'stdstring2charp' : ([c_object, c_voidp], c_ccharp), 'stdstring2stdstring' : ([c_object], c_object), - 'stdvector_valuetype' : ([c_ccharp], c_ccharp), - 'stdvector_valuesize' : ([c_ccharp], c_size_t), + 'longdouble2double' : ([c_voidp], c_double), + 'double2longdouble' : ([c_double, c_voidp], c_void), + 'vectorbool_getitem' : ([c_object, c_int], c_int), + 'vectorbool_setitem' : ([c_object, c_int, c_int], c_void), } # size/offset are backend-specific but fixed after load @@ -401,7 +420,9 @@ return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_d', args))) def c_call_ld(space, cppmethod, cppobject, nargs, cargs): args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] - return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args))) + #return rffi.cast(rffi.LONGDOUBLE, space.float_w(call_capi(space, 'call_ld', args))) + # call_nld narrows long double to double + return rffi.cast(rffi.DOUBLE, space.float_w(call_capi(space, 'call_nld', args))) def c_call_r(space, cppmethod, cppobject, nargs, cargs): args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs)] @@ -561,16 +582,21 @@ def c_is_const_method(space, cppmeth): return space.bool_w(call_capi(space, 'is_const_method', [_ArgH(cppmeth)])) +def c_get_num_templated_methods(space, cppscope): + return space.int_w(call_capi(space, 'method_is_template', [_ArgH(cppscope.handle)])) +def c_get_templated_method_name(space, cppscope, index): + args = [_ArgH(cppscope.handle), _ArgL(index)] + return charp2str_free(space, call_capi(space, 'method_is_template', args)) def c_exists_method_template(space, cppscope, name): args = [_ArgH(cppscope.handle), _ArgS(name)] return space.bool_w(call_capi(space, 'exists_method_template', args)) def c_method_is_template(space, cppscope, index): args = [_ArgH(cppscope.handle), _ArgL(index)] return space.bool_w(call_capi(space, 'method_is_template', args)) - def c_get_method_template(space, cppscope, name, proto): args = [_ArgH(cppscope.handle), _ArgS(name), _ArgS(proto)] return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method_template', args))) + def c_get_global_operator(space, nss, lc, rc, op): if nss is not None: args = [_ArgH(nss.handle), _ArgH(lc.handle), _ArgH(rc.handle), _ArgS(op)] @@ -619,7 +645,7 @@ return space.bool_w(call_capi(space, 'is_enum_data', args)) def c_get_dimension_size(space, cppscope, datamember_index, dim_idx): args = [_ArgH(cppscope.handle), _ArgL(datamember_index), _ArgL(dim_idx)] - return space.bool_w(call_capi(space, 'get_dimension_size', args)) + return space.int_w(call_capi(space, 'get_dimension_size', args)) # misc helpers --------------------------------------------------------------- def c_strtoll(space, svalue): @@ -650,24 +676,94 @@ def c_stdstring2stdstring(space, cppobject): return _cdata_to_cobject(space, call_capi(space, 'stdstring2stdstring', [_ArgH(cppobject)])) -def c_stdvector_valuetype(space, pystr): - return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)])) +def c_longdouble2double(space, addr): + return space.float_w(call_capi(space, 'longdouble2double', [_ArgP(addr)])) +def c_double2longdouble(space, dval, addr): + call_capi(space, 'double2longdouble', [_ArgD(dval), _ArgP(addr)]) -def c_stdvector_valuetype(space, pystr): - return charp2str_free(space, call_capi(space, 'stdvector_valuetype', [_ArgS(pystr)])) -def c_stdvector_valuesize(space, pystr): - return _cdata_to_size_t(space, call_capi(space, 'stdvector_valuesize', [_ArgS(pystr)])) +def c_vectorbool_getitem(space, vbool, idx): + return call_capi(space, 'vectorbool_getitem', [_ArgH(vbool), _ArgL(idx)]) +def c_vectorbool_setitem(space, vbool, idx, value): + call_capi(space, 'vectorbool_setitem', [_ArgH(vbool), _ArgL(idx), _ArgL(value)]) # TODO: factor these out ... # pythonizations def stdstring_c_str(space, w_self): """Return a python string taking into account \0""" - from pypy.module._cppyy import interp_cppyy cppstr = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) return space.newtext(c_stdstring2charp(space, cppstr._rawobject)) +def vbool_getindex(space, w_vbool, w_idx): + idx = space.getindex_w(w_idx, space.w_IndexError, "std::vector<bool> index") + sz = space.len_w(w_vbool) + if idx < 0: idx += sz + if idx < 0 or idx >= sz: + raise IndexError + return idx + +def vectorbool_getitem(space, w_self, w_idx): + """Index a std::vector<bool>, return the value""" + from pypy.module._cppyy import interp_cppyy + vbool = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + idx = vbool_getindex(space, w_self, w_idx) + item = c_vectorbool_getitem(space, vbool._rawobject, idx) + return space.newbool(space.is_true(item)) + +def vectorbool_setitem(space, w_self, w_idx, w_value): + """Index a std::vector<bool>, set the value""" + from pypy.module._cppyy import interp_cppyy + vbool = space.interp_w(interp_cppyy.W_CPPInstance, w_self, can_be_None=False) + idx = vbool_getindex(space, w_self, w_idx) + c_vectorbool_setitem(space, vbool._rawobject, idx, int(space.is_true(w_value))) + +class W_STLVectorIter(W_AbstractSeqIterObject): + # w_seq and index are in base class + _immutable_fields_ = ['converter', 'data', 'len', 'stride'] + + def __init__(self, space, w_vector): + W_AbstractSeqIterObject.__init__(self, w_vector) + # TODO: this should live in rpythonize.py or something so that the + # imports can move to the top w/o getting circles + from pypy.module._cppyy import interp_cppyy + assert isinstance(w_vector, interp_cppyy.W_CPPInstance) + vector = space.interp_w(interp_cppyy.W_CPPInstance, w_vector) + + v_type = c_resolve_name(space, vector.clsdecl.name+'::value_type') + v_size = c_size_of_type(space, v_type) + + if not v_type or not v_size: + raise NotImplementedError # fallback on getitem + + from pypy.module._cppyy import converter + self.converter = converter.get_converter(space, v_type, '') + + # this 'data' is from the decl, so not the pythonized data from pythonify.py + w_arr = space.call_obj_args(vector.clsdecl.get_overload('data'), w_vector, Arguments(space, [])) + arr = space.interp_w(W_ArrayInstance, w_arr, can_be_None=True) + if not arr: + raise OperationError(space.w_StopIteration, space.w_None) + + self.data = rffi.cast(rffi.CCHARP, space.uint_w(arr.getbuffer(space))) + self.len = space.uint_w(space.call_obj_args(vector.clsdecl.get_overload('size'), w_vector, Arguments(space, []))) + self.stride = v_size + + def descr_next(self, space): + if self.w_seq is None: + raise OperationError(space.w_StopIteration, space.w_None) + if self.len <= self.index: + self.w_seq = None + raise OperationError(space.w_StopIteration, space.w_None) + offset = lltype.direct_ptradd(self.data, rffi.cast(rffi.SIZE_T, self.index*self.stride)) + w_item = self.converter.from_memory(space, space.w_None, rffi.cast(rffi.LONG, offset)) + self.index += 1 + return w_item + +def stdvector_iter(space, w_self): + return W_STLVectorIter(space, w_self) + + # setup pythonizations for later use at run-time _pythonizations = {} def register_pythonizations(space): @@ -678,6 +774,12 @@ ### std::string stdstring_c_str, + ### std::vector + stdvector_iter, + + ### std::vector<bool> + vectorbool_getitem, + vectorbool_setitem, ] for f in allfuncs: @@ -692,3 +794,10 @@ space.setattr(w_pycppclass, space.newtext("c_str"), _pythonizations["stdstring_c_str"]) _method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str") _method_alias(space, w_pycppclass, "__str__", "c_str") + + if name.find("std::vector<bool", 0, 16) == 0: + space.setattr(w_pycppclass, space.newtext("__getitem__"), _pythonizations["vectorbool_getitem"]) + space.setattr(w_pycppclass, space.newtext("__setitem__"), _pythonizations["vectorbool_setitem"]) + + elif name.find("std::vector", 0, 11) == 0: + space.setattr(w_pycppclass, space.newtext("__iter__"), _pythonizations["stdvector_iter"]) diff --git a/pypy/module/_cppyy/converter.py b/pypy/module/_cppyy/converter.py --- a/pypy/module/_cppyy/converter.py +++ b/pypy/module/_cppyy/converter.py @@ -1,15 +1,12 @@ import sys from pypy.interpreter.error import OperationError, oefmt - from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib.rarithmetic import r_singlefloat, r_longfloat from rpython.rlib import rfloat, rawrefcount - from pypy.module._rawffi.interp_rawffi import letter2tp from pypy.module._rawffi.array import W_ArrayInstance - -from pypy.module._cppyy import helper, capi, ffitypes +from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews # Converter objects are used to translate between RPython and C++. They are # defined by the type name for which they provide conversion. Uses are for @@ -118,7 +115,7 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): self._is_abstract(space) def to_memory(self, space, w_obj, w_value, offset): @@ -145,11 +142,12 @@ state = space.fromcache(ffitypes.State) return state.c_voidp - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONG, address_value) - return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address) + return lowlevelviews.W_LowLevelView( + space, letter2tp(space, self.typecode), self.size, address) def to_memory(self, space, w_obj, w_value, offset): # copy the full array (uses byte copy for now) @@ -186,11 +184,12 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'o' - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # read access, so no copy needed address_value = self._get_raw_address(space, w_obj, offset) address = rffi.cast(rffi.ULONGP, address_value) - return W_ArrayInstance(space, letter2tp(space, self.typecode), self.size, address[0]) + return lowlevelviews.W_LowLevelView( + space, letter2tp(space, self.typecode), self.size, address[0]) def to_memory(self, space, w_obj, w_value, offset): # copy only the pointer value @@ -212,10 +211,13 @@ x[0] = self._unwrap_object(space, w_obj) def default_argument_libffi(self, space, address): + if not self.valid_default: + from pypy.module._cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible x = rffi.cast(self.c_ptrtype, address) x[0] = self.default - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) rffiptr = rffi.cast(self.c_ptrtype, address) return self._wrap_object(space, rffiptr[0]) @@ -225,7 +227,7 @@ rffiptr = rffi.cast(self.c_ptrtype, address) rffiptr[0] = self._unwrap_object(space, w_value) -class ConstRefNumericTypeConverterMixin(NumericTypeConverterMixin): +class ConstRefNumericTypeConverterMixin(object): _mixin_ = True def cffi_type(self, space): @@ -284,7 +286,7 @@ x = rffi.cast(rffi.LONGP, address) x[0] = self._unwrap_object(space, w_obj) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset)) if address[0] == '\x01': return space.w_True @@ -309,7 +311,7 @@ x = rffi.cast(self.c_ptrtype, address) x[0] = self._unwrap_object(space, w_obj) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj, offset)) return space.newbytes(address[0]) @@ -322,59 +324,92 @@ pass class FloatConverter(ffitypes.typeid(rffi.FLOAT), FloatTypeConverterMixin, TypeConverter): - _immutable_fields_ = ['default'] + _immutable_fields_ = ['default', 'valid_default'] def __init__(self, space, default): - if default: + self.valid_default = False + try: fval = float(rfloat.rstring_to_float(default)) - else: + self.valid_default = True + except Exception: fval = float(0.) - self.default = r_singlefloat(fval) + self.default = rffi.cast(rffi.FLOAT, r_singlefloat(fval)) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) rffiptr = rffi.cast(self.c_ptrtype, address) return self._wrap_object(space, rffiptr[0]) -class ConstFloatRefConverter(FloatConverter): +class ConstFloatRefConverter(ConstRefNumericTypeConverterMixin, FloatConverter): _immutable_fields_ = ['typecode'] typecode = 'f' - def cffi_type(self, space): - state = space.fromcache(ffitypes.State) - return state.c_voidp - - def convert_argument_libffi(self, space, w_obj, address, scratch): - from pypy.module._cppyy.interp_cppyy import FastCallNotPossible - raise FastCallNotPossible - class DoubleConverter(ffitypes.typeid(rffi.DOUBLE), FloatTypeConverterMixin, TypeConverter): - _immutable_fields_ = ['default'] + _immutable_fields_ = ['default', 'valid_default'] def __init__(self, space, default): - if default: + self.valid_default = False + try: self.default = rffi.cast(self.c_type, rfloat.rstring_to_float(default)) - else: + self.valid_default = True + except Exception: self.default = rffi.cast(self.c_type, 0.) class ConstDoubleRefConverter(ConstRefNumericTypeConverterMixin, DoubleConverter): _immutable_fields_ = ['typecode'] typecode = 'd' -class LongDoubleConverter(ffitypes.typeid(rffi.LONGDOUBLE), FloatTypeConverterMixin, TypeConverter): - _immutable_fields_ = ['default'] +class LongDoubleConverter(TypeConverter): + _immutable_fields_ = ['default', 'valid_default'] + typecode = 'g' def __init__(self, space, default): - if default: - fval = float(rfloat.rstring_to_float(default)) - else: - fval = float(0.) - self.default = r_longfloat(fval) + self.valid_default = False + try: + # use float() instead of cast with r_longfloat + fval = rffi.cast(rffi.DOUBLE, rfloat.rstring_to_float(default)) + self.valid_default = True + except Exception: + fval = rffi.cast(rffi.DOUBLE, 0.) + #self.default = r_longfloat(fval) + self.default = fval + + def convert_argument(self, space, w_obj, address): + x = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, space.float_w(w_obj), x) + ba = rffi.cast(rffi.CCHARP, address) + ba[capi.c_function_arg_typeoffset(space)] = self.typecode + + def convert_argument_libffi(self, space, w_obj, address, scratch): + x = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, space.float_w(w_obj), x) + + def default_argument_libffi(self, space, address): + if not self.valid_default: + from pypy.module._cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible + x = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, self.default, x) + + def from_memory(self, space, w_obj, offset): + address = self._get_raw_address(space, w_obj, offset) + rffiptr = rffi.cast(rffi.VOIDP, address) + return space.newfloat(capi.c_longdouble2double(space, rffiptr)) + + def to_memory(self, space, w_obj, w_value, offset): + address = self._get_raw_address(space, w_obj, offset) + rffiptr = rffi.cast(rffi.VOIDP, address) + capi.c_double2longdouble(space, space.float_w(w_value), rffiptr) class ConstLongDoubleRefConverter(ConstRefNumericTypeConverterMixin, LongDoubleConverter): _immutable_fields_ = ['typecode'] typecode = 'g' + def convert_argument_libffi(self, space, w_obj, address, scratch): + capi.c_double2longdouble(space, space.float_w(w_obj), rffi.cast(rffi.VOIDP, scratch)) + x = rffi.cast(rffi.VOIDPP, address) + x[0] = scratch + class CStringConverter(TypeConverter): def convert_argument(self, space, w_obj, address): @@ -384,7 +419,7 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = 'p' - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) charpptr = rffi.cast(rffi.CCHARPP, address) return space.newtext(rffi.charp2str(charpptr[0])) @@ -398,13 +433,15 @@ def __init__(self, space, extra): self.size = extra - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = self._get_raw_address(space, w_obj, offset) charpptr = rffi.cast(rffi.CCHARP, address) - strsize = self.size - if charpptr[self.size-1] == '\0': - strsize = self.size-1 # rffi will add \0 back - return space.newtext(rffi.charpsize2str(charpptr, strsize)) + if 0 <= self.size and self.size != 2**31-1: # cling's code for "unknown" (?) + strsize = self.size + if charpptr[self.size-1] == '\0': + strsize = self.size-1 # rffi will add \0 back + return space.newtext(rffi.charpsize2str(charpptr, strsize)) + return space.newtext(rffi.charp2str(charpptr)) class VoidPtrConverter(TypeConverter): @@ -429,7 +466,7 @@ x = rffi.cast(rffi.VOIDPP, address) x[0] = self._unwrap_object(space, w_obj) - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # returned as a long value for the address (INTPTR_T is not proper # per se, but rffi does not come with a PTRDIFF_T) address = self._get_raw_address(space, w_obj, offset) @@ -438,7 +475,7 @@ from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) shape = letter2tp(space, 'P') - return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) + return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval) def to_memory(self, space, w_obj, w_value, offset): address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) @@ -491,7 +528,7 @@ from pypy.module._cppyy.interp_cppyy import W_CPPInstance if isinstance(w_obj, W_CPPInstance): from pypy.module._cppyy.interp_cppyy import INSTANCE_FLAGS_IS_RVALUE - if w_obj.flags & INSTANCE_FLAGS_IS_RVALUE: + if w_obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE: # reject moves as all are explicit raise ValueError("lvalue expected") if capi.c_is_subtype(space, w_obj.clsdecl, self.clsdecl): @@ -523,14 +560,14 @@ from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE obj = space.interp_w(W_CPPInstance, w_obj) if obj: - if obj.flags & INSTANCE_FLAGS_IS_RVALUE: - obj.flags &= ~INSTANCE_FLAGS_IS_RVALUE + if obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE: + obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE try: return InstanceRefConverter._unwrap_object(self, space, w_obj) except Exception: # TODO: if the method fails on some other converter, then the next # overload can not be an rvalue anymore - obj.flags |= INSTANCE_FLAGS_IS_RVALUE + obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE raise raise oefmt(space.w_ValueError, "object is not an rvalue") @@ -541,7 +578,7 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible # TODO: by-value is a jit_libffi special case - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) @@ -561,15 +598,11 @@ return capi.C_NULL_OBJECT raise e - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance(space, address, self.clsdecl, do_cast=False) - def to_memory(self, space, w_obj, w_value, offset): - address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) - address[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_value)) - class InstancePtrPtrConverter(InstancePtrConverter): typecode = 'o' @@ -591,12 +624,31 @@ from pypy.module._cppyy.interp_cppyy import FastCallNotPossible raise FastCallNotPossible - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance( space, address, self.clsdecl, do_cast=False, is_ref=True) + def to_memory(self, space, w_obj, w_value, offset): + # the actual data member is of object* type, but we receive a pointer to that + # data member in order to modify its value, so by convention, the internal type + # used is object** + address = rffi.cast(rffi.VOIDPP, self._get_raw_address(space, w_obj, offset)) + from pypy.module._cppyy.interp_cppyy import W_CPPInstance + cppinstance = space.interp_w(W_CPPInstance, w_value, can_be_None=True) + if cppinstance: + rawobject = cppinstance.get_rawobject() + offset = capi.c_base_offset(space, cppinstance.clsdecl, self.clsdecl, rawobject, 1) + obj_address = capi.direct_ptradd(rawobject, offset) + address[0] = rffi.cast(rffi.VOIDP, obj_address); + # register the value for potential recycling + from pypy.module._cppyy.interp_cppyy import memory_regulator + memory_regulator.register(cppinstance) + else: + raise oefmt(space.w_TypeError, + "cannot pass %T instance as %s", w_value, self.clsdecl.name) + def finalize_call(self, space, w_obj): if self.ref_buffer: set_rawobject(space, w_obj, self.ref_buffer[0]) @@ -606,8 +658,27 @@ lltype.free(self.ref_buffer, flavor='raw') self.ref_buffer = lltype.nullptr(rffi.VOIDPP.TO) +class InstanceArrayConverter(InstancePtrConverter): + _immutable_fields_ = ['size'] + + def __init__(self, space, clsdecl, array_size, dimensions): + InstancePtrConverter.__init__(self, space, clsdecl) + if array_size <= 0 or array_size == 2**31-1: # cling's code for "unknown" (?) + self.size = sys.maxint + else: + self.size = array_size + # peel one off as that should be the same as the array size + self.dimensions = dimensions[1:] + + def from_memory(self, space, w_obj, offset): + address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) + return lowlevelviews.W_ArrayOfInstances(space, self.clsdecl, address, self.size, self.dimensions) + + def to_memory(self, space, w_obj, w_value, offset): + self._is_abstract(space) + + class StdStringConverter(InstanceConverter): - def __init__(self, space, extra): from pypy.module._cppyy import interp_cppyy cppclass = interp_cppyy.scope_byname(space, capi.std_string_name) @@ -633,6 +704,34 @@ def free_argument(self, space, arg): capi.c_destruct(space, self.clsdecl, rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, arg)[0])) +class StdStringMoveConverter(StdStringConverter): + def _unwrap_object(self, space, w_obj): + # moving is same as by-ref, but have to check that move is allowed + moveit_reason = 3 + from pypy.module._cppyy.interp_cppyy import W_CPPInstance, INSTANCE_FLAGS_IS_RVALUE + try: + obj = space.interp_w(W_CPPInstance, w_obj) + if obj and obj.rt_flags & INSTANCE_FLAGS_IS_RVALUE: + obj.rt_flags &= ~INSTANCE_FLAGS_IS_RVALUE + moveit_reason = 1 + else: + moveit_reason = 0 + except: + pass + + if moveit_reason: + try: + return StdStringConverter._unwrap_object(self, space, w_obj) + except Exception: + if moveit_reason == 1: + # TODO: if the method fails on some other converter, then the next + # overload can not be an rvalue anymore + obj = space.interp_w(W_CPPInstance, w_obj) + obj.rt_flags |= INSTANCE_FLAGS_IS_RVALUE + raise + + raise oefmt(space.w_ValueError, "object is not an rvalue") + class StdStringRefConverter(InstancePtrConverter): _immutable_fields_ = ['cppclass', 'typecode'] typecode = 'V' @@ -749,7 +848,7 @@ ba = rffi.cast(rffi.CCHARP, address) ba[capi.c_function_arg_typeoffset(space)] = self.typecode - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj, offset)) from pypy.module._cppyy import interp_cppyy return interp_cppyy.wrap_cppinstance(space, address, @@ -758,7 +857,7 @@ class SmartPtrPtrConverter(SmartPtrConverter): typecode = 'o' - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): self._is_abstract(space) def to_memory(self, space, w_obj, w_value, offset): @@ -770,7 +869,7 @@ class MacroConverter(TypeConverter): - def from_memory(self, space, w_obj, w_pycppclass, offset): + def from_memory(self, space, w_obj, offset): # TODO: get the actual type info from somewhere ... address = self._get_raw_address(space, w_obj, offset) longptr = rffi.cast(rffi.LONGP, address) @@ -803,20 +902,28 @@ pass # match of decorated, unqualified type - compound = helper.compound(name) + cpd = helper.compound(name) clean_name = capi.c_resolve_name(space, helper.clean_type(name)) try: - return _converters[clean_name+compound](space, default) + return _converters[clean_name+cpd](space, default) except KeyError: pass - # arrays + # arrays (array_size may be negative, meaning: no size or no size found) + array_size = -1 + if cpd == "[]": + array_size = helper.array_size(_name) # uses original arg + elif cpd == '*' and ':' in default: + # this happens for multi-dimensional arrays: those are described as pointers + cpd = "[]" + splitpos = default.find(':') + if 0 < splitpos: # always true, but needed for annotator + array_size = int(default[:splitpos]) + try: - # array_index may be negative to indicate no size or no size found - array_size = helper.array_size(_name) # uses original arg # TODO: using clean_name here drops const (e.g. const char[] will # never be seen this way) - return _a_converters[clean_name+compound](space, array_size) + return _a_converters[clean_name+cpd](space, array_size) except KeyError: pass @@ -830,24 +937,28 @@ # check smart pointer type check_smart = capi.c_smartptr_info(space, clean_name) if check_smart[0]: - if compound == '': + if cpd == '': return SmartPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) - elif compound == '*': + elif cpd == '*': return SmartPtrPtrConverter(space, clsdecl, check_smart[1], check_smart[2]) - elif compound == '&': + elif cpd == '&': return SmartPtrRefConverter(space, clsdecl, check_smart[1], check_smart[2]) # fall through: can still return smart pointer in non-smart way # type check for the benefit of the annotator - if compound == "*": + if cpd == "*": return InstancePtrConverter(space, clsdecl) - elif compound == "&": + elif cpd == "&": return InstanceRefConverter(space, clsdecl) - elif compound == "&&": + elif cpd == "&&": return InstanceMoveConverter(space, clsdecl) - elif compound == "**": + elif cpd in ["**", "*[]", "&*"]: return InstancePtrPtrConverter(space, clsdecl) - elif compound == "": + elif cpd == "[]" and array_size > 0: + # default encodes the dimensions + dims = default.split(':') + return InstanceArrayConverter(space, clsdecl, array_size, dims) + elif cpd == "": return InstanceConverter(space, clsdecl) elif "(anonymous)" in name: # special case: enum w/o a type name @@ -859,7 +970,7 @@ return FunctionPointerConverter(space, name[pos+2:]) # void* or void converter (which fails on use) - if 0 <= compound.find('*'): + if 0 <= cpd.find('*'): return VoidPtrConverter(space, default) # "user knows best" # return a void converter here, so that the class can be build even @@ -874,8 +985,8 @@ _converters["const float&"] = ConstFloatRefConverter _converters["double"] = DoubleConverter _converters["const double&"] = ConstDoubleRefConverter -#_converters["long double"] = LongDoubleConverter -#_converters["const long double&"] = ConstLongDoubleRefConverter +_converters["long double"] = LongDoubleConverter +_converters["const long double&"] = ConstLongDoubleRefConverter _converters["const char*"] = CStringConverter _converters["void*"] = VoidPtrConverter _converters["void**"] = VoidPtrPtrConverter @@ -885,6 +996,7 @@ _converters["std::basic_string<char>"] = StdStringConverter _converters["const std::basic_string<char>&"] = StdStringConverter # TODO: shouldn't copy _converters["std::basic_string<char>&"] = StdStringRefConverter +_converters["std::basic_string<char>&&"] = StdStringMoveConverter _converters["PyObject*"] = PyObjectConverter @@ -908,7 +1020,12 @@ _immutable_ = True typecode = c_tc def __init__(self, space, default): - self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = False + try: + self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = True + except Exception: + self.default = rffi.cast(self.c_type, 0) class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter): _immutable_ = True for name in names: @@ -925,7 +1042,12 @@ _immutable_ = True typecode = c_tc def __init__(self, space, default): - self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = False + try: + self.default = rffi.cast(self.c_type, capi.c_strtoll(space, default)) + self.valid_default = True + except Exception: + self.default = rffi.cast(self.c_type, 0) class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter): _immutable_ = True for name in names: @@ -945,7 +1067,12 @@ _immutable_ = True typecode = c_tc def __init__(self, space, default): - self.default = rffi.cast(self.c_type, capi.c_strtoull(space, default)) + self.valid_default = False + try: + self.default = rffi.cast(self.c_type, capi.c_strtoull(space, default)) + self.valid_default = True + except Exception: + self.default = rffi.cast(self.c_type, 0) class ConstRefConverter(ConstRefNumericTypeConverterMixin, BasicConverter): _immutable_ = True for name in names: @@ -1002,6 +1129,7 @@ ("std::basic_string<char>", "string"), ("const std::basic_string<char>&", "const string&"), ("std::basic_string<char>&", "string&"), + ("std::basic_string<char>&&", "string&&"), ("PyObject*", "_object*"), ) diff --git a/pypy/module/_cppyy/executor.py b/pypy/module/_cppyy/executor.py --- a/pypy/module/_cppyy/executor.py +++ b/pypy/module/_cppyy/executor.py @@ -1,14 +1,10 @@ import sys from pypy.interpreter.error import oefmt - from rpython.rtyper.lltypesystem import rffi, lltype from rpython.rlib import jit_libffi - from pypy.module._rawffi.interp_rawffi import letter2tp -from pypy.module._rawffi.array import W_Array, W_ArrayInstance - -from pypy.module._cppyy import helper, capi, ffitypes +from pypy.module._cppyy import helper, capi, ffitypes, lowlevelviews # Executor objects are used to dispatch C++ methods. They are defined by their # return type only: arguments are converted by Converter objects, and Executors @@ -60,7 +56,7 @@ from pypy.module._cppyy import interp_cppyy return interp_cppyy.get_nullptr(space) shape = letter2tp(space, self.typecode) - return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval) + return lowlevelviews.W_LowLevelView(space, shape, sys.maxint/shape.size, ptrval) class VoidExecutor(Executor): @@ -80,9 +76,6 @@ class NumericExecutorMixin(object): _mixin_ = True - #def _wrap_object(self, space, obj): - # return getattr(space, self.wrapper)(obj) - def execute(self, space, cppmethod, cppthis, num_args, args): result = self.c_stubcall(space, cppmethod, cppthis, num_args, args) return self._wrap_object(space, rffi.cast(self.c_type, result)) @@ -98,19 +91,16 @@ def __init__(self, space, extra): Executor.__init__(self, space, extra) self.do_assign = False - self.item = rffi.cast(self.c_type, 0) + self.w_item = space.w_None def set_item(self, space, w_item): - self.item = self._unwrap_object(space, w_item) + self.w_item = w_item self.do_assign = True - #def _wrap_object(self, space, obj): - # return getattr(space, self.wrapper)(rffi.cast(self.c_type, obj)) - def _wrap_reference(self, space, rffiptr): if self.do_assign: - rffiptr[0] = self.item - self.do_assign = False + rffiptr[0] = rffi.cast(self.c_type, self._unwrap_object(space, self.w_item)) + self.do_assign = False return self._wrap_object(space, rffiptr[0]) # all paths, for rtyper def execute(self, space, cppmethod, cppthis, num_args, args): @@ -123,6 +113,48 @@ return self._wrap_reference(space, rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0])) +class LongDoubleExecutorMixin(object): + # Note: not really supported, but returns normal double + _mixin_ = True + + def execute(self, space, cppmethod, cppthis, num_args, args): + result = self.c_stubcall(space, cppmethod, cppthis, num_args, args) + return space.newfloat(result) + + def execute_libffi(self, space, cif_descr, funcaddr, buffer): + from pypy.module._cppyy.interp_cppyy import FastCallNotPossible + raise FastCallNotPossible + +class LongDoubleExecutor(ffitypes.typeid(rffi.LONGDOUBLE), LongDoubleExecutorMixin, Executor): + _immutable_ = True + c_stubcall = staticmethod(capi.c_call_ld) + +class LongDoubleRefExecutorMixin(NumericRefExecutorMixin): + # Note: not really supported, but returns normal double + _mixin_ = True + + def _wrap_reference(self, space, rffiptr): + if self.do_assign: + capi.c_double2longdouble(space, space.float_w(self.w_item), rffiptr) + self.do_assign = False + return self.w_item + return space.newfloat(capi.c_longdouble2double(space, rffiptr)) + + def execute(self, space, cppmethod, cppthis, num_args, args): + result = capi.c_call_r(space, cppmethod, cppthis, num_args, args) + return self._wrap_reference(space, rffi.cast(self.c_ptrtype, result)) + + def execute_libffi(self, space, cif_descr, funcaddr, buffer): + jit_libffi.jit_ffi_call(cif_descr, funcaddr, buffer) + result = rffi.ptradd(buffer, cif_descr.exchange_result) + return self._wrap_reference(space, + rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0])) + +class LongDoubleRefExecutor(ffitypes.typeid(rffi.LONGDOUBLE), LongDoubleRefExecutorMixin, Executor): + def cffi_type(self, space): + state = space.fromcache(ffitypes.State) + return state.c_voidp + class CStringExecutor(Executor): def execute(self, space, cppmethod, cppthis, num_args, args): @@ -349,6 +381,10 @@ _executors["void*"] = PtrTypeExecutor _executors["const char*"] = CStringExecutor +# long double not really supported: narrows to double +_executors["long double"] = LongDoubleExecutor +_executors["long double&"] = LongDoubleRefExecutor + # special cases (note: 'string' aliases added below) _executors["constructor"] = ConstructorExecutor diff --git a/pypy/module/_cppyy/ffitypes.py b/pypy/module/_cppyy/ffitypes.py --- a/pypy/module/_cppyy/ffitypes.py +++ b/pypy/module/_cppyy/ffitypes.py @@ -296,7 +296,8 @@ _immutable_fields_ = ['c_type', 'c_ptrtype', 'typecode'] c_type = rffi.LONGDOUBLE - c_ptrtype = rffi.LONGDOUBLEP + # c_ptrtype = rffi.LONGDOUBLEP # useless type at this point + c_ptrtype = rffi.VOIDP typecode = 'g' # long double is not really supported ... @@ -304,7 +305,7 @@ return r_longfloat(space.float_w(w_obj)) def _wrap_object(self, space, obj): - return space.wrap(obj) + return space.newfloat(obj) def cffi_type(self, space): state = space.fromcache(State) diff --git a/pypy/module/_cppyy/helper.py b/pypy/module/_cppyy/helper.py --- a/pypy/module/_cppyy/helper.py +++ b/pypy/module/_cppyy/helper.py @@ -117,16 +117,10 @@ # TODO: perhaps absorb or "pythonify" these operators? return cppname -if sys.hexversion < 0x3000000: - CPPYY__div__ = "__div__" - CPPYY__idiv__ = "__idiv__" - CPPYY__long__ = "__long__" - CPPYY__bool__ = "__nonzero__" -else: - CPPYY__div__ = "__truediv__" - CPPYY__idiv__ = "__itruediv__" - CPPYY__long__ = "__int__" - CPPYY__bool__ = "__bool__" +CPPYY__div__ = "__div__" +CPPYY__idiv__ = "__idiv__" +CPPYY__long__ = "__long__" +CPPYY__bool__ = "__nonzero__" # _operator_mappings["[]"] = "__setitem__" # depends on return type # _operator_mappings["+"] = "__add__" # depends on # of args (see __pos__) diff --git a/pypy/module/_cppyy/include/capi.h b/pypy/module/_cppyy/include/capi.h --- a/pypy/module/_cppyy/include/capi.h +++ b/pypy/module/_cppyy/include/capi.h @@ -63,6 +63,8 @@ double cppyy_call_d(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN long double cppyy_call_ld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); + RPY_EXTERN + double cppyy_call_nld(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); RPY_EXTERN void* cppyy_call_r(cppyy_method_t method, cppyy_object_t self, int nargs, void* args); @@ -151,11 +153,15 @@ RPY_EXTERN char* cppyy_method_signature(cppyy_method_t, int show_formalargs); RPY_EXTERN - char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t idx, int show_formalargs); + char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_method_t, int show_formalargs); RPY_EXTERN int cppyy_is_const_method(cppyy_method_t); RPY_EXTERN + int get_num_templated_methods(cppyy_scope_t scope); + RPY_EXTERN + char* get_templated_method_name(cppyy_scope_t scope, cppyy_index_t imeth); + RPY_EXTERN int cppyy_exists_method_template(cppyy_scope_t scope, const char* name); RPY_EXTERN int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx); @@ -216,9 +222,14 @@ cppyy_object_t cppyy_stdstring2stdstring(cppyy_object_t ptr); RPY_EXTERN - const char* cppyy_stdvector_valuetype(const char* clname); + double cppyy_longdouble2double(void*); RPY_EXTERN - size_t cppyy_stdvector_valuesize(const char* clname); + void cppyy_double2longdouble(double, void*); + + RPY_EXTERN + int cppyy_vectorbool_getitem(cppyy_object_t ptr, int idx); + RPY_EXTERN + void cppyy_vectorbool_setitem(cppyy_object_t ptr, int idx, int value); #ifdef __cplusplus } diff --git a/pypy/module/_cppyy/interp_cppyy.py b/pypy/module/_cppyy/interp_cppyy.py --- a/pypy/module/_cppyy/interp_cppyy.py +++ b/pypy/module/_cppyy/interp_cppyy.py @@ -24,6 +24,7 @@ INSTANCE_FLAGS_IS_RVALUE = 0x0004 OVERLOAD_FLAGS_USE_FFI = 0x0001 +OVERLOAD_FLAGS_CREATES = 0x0002 FUNCTION_IS_GLOBAL = 0x0001 FUNCTION_IS_STATIC = 0x0001 @@ -41,6 +42,7 @@ 'void**' : 100, 'float' : 30, 'double' : 10, + 'bool' : 1, 'const string&' : 1, } # solves a specific string ctor overload from rpython.rlib.listsort import make_timsort_class @@ -167,6 +169,7 @@ # # W_CPPOverload: instance methods (base class) # W_CPPConstructorOverload: constructors +# W_CPPAbstractCtorOverload: to provent instantiation of abstract classes # W_CPPStaticOverload: free and static functions # W_CPPTemplateOverload: templated methods # W_CPPTemplateStaticOverload: templated free and static functions @@ -227,8 +230,10 @@ if self.converters is None: try: self._setup(cppthis) - except Exception: - pass + except Exception as e: + if self.converters is None: + raise oefmt(self.space.w_SystemError, + "unable to initialize converters (%s)", str(e)) # attempt to call directly through ffi chain if useffi and self._funcaddr: @@ -258,7 +263,8 @@ jit.promote(self) cif_descr = self.cif_descr # add extra space for const-ref support (see converter.py) - buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw') + buffer = lltype.malloc(rffi.CCHARP.TO, + cif_descr.exchange_size+len(self.arg_defs)*rffi.sizeof(rffi.DOUBLE), flavor='raw') thisoff = 0 try: if cppthis: @@ -412,8 +418,10 @@ def priority(self): total_arg_priority = 0 - for p in [priority.get(arg_type, 0) for arg_type, arg_dflt in self.arg_defs]: - total_arg_priority += p + for arg_type, arg_dflt in self.arg_defs: + total_arg_priority += priority.get(arg_type, 0) + if '&&' in arg_type: + total_arg_priority += 100 return total_arg_priority @rgc.must_be_light_finalizer @@ -433,7 +441,7 @@ class CPPSetItem(CPPMethod): """Method dispatcher specific to Python's __setitem__ mapped onto C++'s - operator[](int). The former function takes an extra argument to assign to + operator[](T). The former function takes an extra argument to assign to the return type of the latter.""" _attrs_ = [] @@ -453,6 +461,36 @@ # need forwarding, which the normal instancemethod does not provide, hence this # derived class. class MethodWithProps(Method): + # set life management of result from the call + def fget_creates(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_creates(space) + + @unwrap_spec(value=bool) + def fset_creates(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_creates(space, value) + + # set ownership policy of arguments (not yet implemented) + def fget_mempolicy(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_mempolicy(space) + + @unwrap_spec(value=int) + def fset_mempolicy(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_mempolicy(space, value) + + # set to release the gil during call (not yet implemented) + def fget_release_gil(self, space): + f = space.interp_w(W_CPPOverload, self.w_function) + return f.fget_release_gil(space) + + @unwrap_spec(value=bool) + def fset_release_gil(self, space, value): + f = space.interp_w(W_CPPOverload, self.w_function) + f.fset_release_gil(space, value) + # allow user to determine ffi use rules per overload def fget_useffi(self, space): f = space.interp_w(W_CPPOverload, self.w_function) @@ -468,19 +506,22 @@ __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), - __func__ = interp_attrproperty_w('w_function', cls=MethodWithProps), - __self__ = interp_attrproperty_w('w_instance', cls=MethodWithProps), + __new__ = interp2app(MethodWithProps.descr_method__new__.im_func), + __call__ = interp2app(MethodWithProps.descr_method_call), + __get__ = interp2app(MethodWithProps.descr_method_get), + __func__ = interp_attrproperty_w('w_function', cls=MethodWithProps), + __self__ = interp_attrproperty_w('w_instance', cls=MethodWithProps), __getattribute__ = interp2app(MethodWithProps.descr_method_getattribute), - __eq__ = interp2app(MethodWithProps.descr_method_eq), - __ne__ = descr_generic_ne, - __hash__ = interp2app(MethodWithProps.descr_method_hash), - __repr__ = interp2app(MethodWithProps.descr_method_repr), - __reduce__ = interp2app(MethodWithProps.descr_method__reduce__), - __weakref__ = make_weakref_descr(MethodWithProps), - __useffi__ = GetSetProperty(MethodWithProps.fget_useffi, MethodWithProps.fset_useffi), + __eq__ = interp2app(MethodWithProps.descr_method_eq), + __ne__ = descr_generic_ne, + __hash__ = interp2app(MethodWithProps.descr_method_hash), + __repr__ = interp2app(MethodWithProps.descr_method_repr), + __reduce__ = interp2app(MethodWithProps.descr_method__reduce__), + __weakref__ = make_weakref_descr(MethodWithProps), + __creates__ = GetSetProperty(MethodWithProps.fget_creates, MethodWithProps.fset_creates), + __mempolicy__ = GetSetProperty(MethodWithProps.fget_mempolicy, MethodWithProps.fset_mempolicy), + __release_gil__ = GetSetProperty(MethodWithProps.fget_release_gil, MethodWithProps.fset_release_gil), + __useffi__ = GetSetProperty(MethodWithProps.fget_useffi, MethodWithProps.fset_useffi), ) MethodWithProps.typedef.acceptable_as_base_class = False @@ -502,6 +543,9 @@ def descr_get(self, w_obj, w_cls=None): """functionobject.__get__(obj[, type]) -> method""" # TODO: check validity of w_cls if given + # TODO: this does not work for Python 3, which does not have + # unbound methods (probably no common code possible, see also + # pypy/interpreter/function.py) space = self.space asking_for_bound = (space.is_none(w_cls) or not space.is_w(w_obj, space.w_None) or @@ -540,7 +584,12 @@ for i in range(len(self.functions)): cppyyfunc = self.functions[i] try: - return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) + w_result = cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) + if self.flags & OVERLOAD_FLAGS_CREATES: + if isinstance(w_result, W_CPPInstance): + cppinstance = self.space.interp_w(W_CPPInstance, w_result) + cppinstance.fset_python_owns(self.space, self.space.w_True) + return w_result except Exception: pass @@ -553,6 +602,7 @@ for i in range(len(self.functions)): cppyyfunc = self.functions[i] try: + # no need to set ownership on the return value, as none of the methods execute return cppyyfunc.call(cppthis, args_w, self.flags & OVERLOAD_FLAGS_USE_FFI) except OperationError as e: # special case if there's just one function, to prevent clogging the error message @@ -581,6 +631,43 @@ sig += '\n'+self.functions[i].prototype() return self.space.newtext(sig) + @unwrap_spec(signature='text') + def mp_overload(self, signature): + sig = '(%s)' % signature + for f in self.functions: + if f.signature(False) == sig: + if isinstance(self, W_CPPStaticOverload): + return W_CPPStaticOverload(self.space, self.scope, [f]) + return W_CPPOverload(self.space, self.scope, [f]) + raise oefmt(self.space.w_LookupError, "signature '%s' not found", signature) + + # set life management of result from the call + def fget_creates(self, space): + return space.newbool(bool(self.flags & OVERLOAD_FLAGS_CREATES)) + + @unwrap_spec(value=bool) + def fset_creates(self, space, value): + if space.is_true(value): + self.flags |= OVERLOAD_FLAGS_CREATES + else: + self.flags &= ~OVERLOAD_FLAGS_CREATES + + # set ownership policy of arguments (not yet implemented) + def fget_mempolicy(self, space): + return space.newint(0) + + @unwrap_spec(value=int) + def fset_mempolicy(self, space, value): + pass + + # set to release the gil during call (not yet implemented) + def fget_release_gil(self, space): + return space.newbool(True) + + @unwrap_spec(value=bool) + def fset_release_gil(self, space, value): + pass + # allow user to determine ffi use rules per overload def fget_useffi(self, space): return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI)) @@ -604,10 +691,14 @@ W_CPPOverload.typedef = TypeDef( 'CPPOverload', - __get__ = interp2app(W_CPPOverload.descr_get), - __call__ = interp2app(W_CPPOverload.call_args), - __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPOverload.fget_doc) + __get__ = interp2app(W_CPPOverload.descr_get), + __call__ = interp2app(W_CPPOverload.call_args), + __creates__ = GetSetProperty(W_CPPOverload.fget_creates, W_CPPOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPOverload.fget_mempolicy, W_CPPOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPOverload.fget_release_gil, W_CPPOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPOverload.fget_useffi, W_CPPOverload.fset_useffi), + __overload__ = interp2app(W_CPPOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPOverload.fget_doc) ) @@ -629,21 +720,21 @@ @unwrap_spec(args_w='args_w') def call_args(self, args_w): jit.promote(self) - #if isinstance(args_w[0], W_CPPInstance): - # free function used as bound method, leave in place return self.call_impl(capi.C_NULL_OBJECT, args_w) - # free functions are implemented as methods of 'namespace' classes, remove 'instance' - #return self.call_impl(capi.C_NULL_OBJECT, args_w[1:]) def __repr__(self): return "W_CPPStaticOverload(%s)" % [f.prototype() for f in self.functions] W_CPPStaticOverload.typedef = TypeDef( 'CPPStaticOverload', - __get__ = interp2app(W_CPPStaticOverload.descr_get), - __call__ = interp2app(W_CPPStaticOverload.call_args), - __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) + __get__ = interp2app(W_CPPStaticOverload.descr_get), + __call__ = interp2app(W_CPPStaticOverload.call_args), + __creates__ = GetSetProperty(W_CPPStaticOverload.fget_creates, W_CPPStaticOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPStaticOverload.fget_mempolicy, W_CPPStaticOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPStaticOverload.fget_release_gil, W_CPPStaticOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi, W_CPPStaticOverload.fset_useffi), + __overload__ = interp2app(W_CPPStaticOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc) ) @@ -657,11 +748,6 @@ @unwrap_spec(args_w='args_w') def call_args(self, args_w): jit.promote(self) - # TODO: factor out the following: - if capi.c_is_abstract(self.space, self.scope.handle): - raise oefmt(self.space.w_TypeError, - "cannot instantiate abstract class '%s'", - self.scope.name) cppinstance = self.space.interp_w(W_CPPInstance, args_w[0]) w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle), args_w[1:]) newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result)) @@ -674,15 +760,34 @@ W_CPPConstructorOverload.typedef = TypeDef( 'CPPConstructorOverload', - __get__ = interp2app(W_CPPConstructorOverload.descr_get), - __call__ = interp2app(W_CPPConstructorOverload.call_args), - __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc) + __get__ = interp2app(W_CPPConstructorOverload.descr_get), + __call__ = interp2app(W_CPPConstructorOverload.call_args), + __overload__ = interp2app(W_CPPConstructorOverload.mp_overload), + __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc) +) + +class W_CPPAbstractCtorOverload(W_CPPOverload): + _attrs_ = [] + + @unwrap_spec(args_w='args_w') + def call_args(self, args_w): + raise oefmt(self.space.w_TypeError, + "cannot instantiate abstract class '%s'", self.scope.name) + + def __repr__(self): + return "W_CPPAbstractCtorOverload" + +W_CPPAbstractCtorOverload.typedef = TypeDef( + 'CPPAbstractCtorOverload', + __get__ = interp2app(W_CPPAbstractCtorOverload.descr_get), + __call__ = interp2app(W_CPPAbstractCtorOverload.call_args), ) class TemplateOverloadMixin(object): """Mixin to instantiate templated methods/functions.""" + _attrs_ = ['tmpl_args_w'] _mixin_ = True def construct_template_args(self, w_tpArgs, args_w = None): @@ -699,7 +804,7 @@ if args_w: # try to specialize the type match for the given object cppinstance = self.space.interp_w(W_CPPInstance, args_w[i]) - if cppinstance.flags & INSTANCE_FLAGS_IS_RVALUE: + if cppinstance.rt_flags & INSTANCE_FLAGS_IS_RVALUE: sugar = "&&" elif cppinstance.flags & INSTANCE_FLAGS_IS_REF: sugar = "*" @@ -735,23 +840,37 @@ return cppol def instantiate_and_call(self, name, args_w): - # try to match with run-time instantiations - for cppol in self.master.overloads.values(): - try: - 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 + method = None + try: + # existing cached instantiations + if name[-1] == '>': # only accept full templated name, to ensure explicit + method = self.master.overloads[name] + else: + # try to match with run-time instantiations + # TODO: logically, this could be used, but in practice, it's proving too + # greedy ... maybe as a last resort? + #for cppol in self.master.overloads.values(): + # try: + # 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 + raise TypeError("pre-existing overloads failed") + except (KeyError, TypeError): + # if not known, try to deduce from argument types + w_types = self.space.newtuple([self.space.type(obj_w) for obj_w in args_w]) + proto = self.construct_template_args(w_types, args_w) + method = self.find_method_template(name, proto) - # if all failed, then try to deduce from argument types - w_types = self.space.newtuple([self.space.type(obj_w) for obj_w in args_w]) - proto = self.construct_template_args(w_types, args_w) - method = self.find_method_template(name, proto) - - # only cache result if the name retains the full template - fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) - if 0 <= fullname.rfind('>'): + # only cache result if the name retains the full template + # TODO: the problem is in part that c_method_full_name returns incorrect names, + # e.g. when default template arguments are involved, so for now use 'name' if it + # has the full templated name + if name[-1] == '>': + fullname = name + else: + fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) try: existing = self.master.overloads[fullname] allf = existing.functions + method.functions @@ -763,9 +882,10 @@ except KeyError: self.master.overloads[fullname] = method - 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)) + if method is not None: + 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 @@ -779,14 +899,9 @@ fullname = name+'<'+tmpl_args+'>' try: method = self.master.overloads[fullname] - except KeyError: - method = self.find_method_template(fullname) - # cache result (name is always full templated name) - self.master.overloads[fullname] = method - # also cache on "official" name (may include default template arguments) - c_fullname = capi.c_method_full_name(self.space, method.functions[0].cppmethod) - if c_fullname != fullname: - self.master.overloads[c_fullname] = method + except KeyError as e: + # defer instantiation until arguments are known + return self.clone(tmpl_args) return method.descr_get(self.w_this, None) @@ -795,21 +910,29 @@ """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', 'w_this'] - _immutable_fields_ = ['name'] + _attrs_ = ['name', 'tmpl_args', 'overloads', 'master', 'w_this'] + _immutable_fields_ = ['name', 'tmpl_args'] - def __init__(self, space, name, decl_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): + def __init__(self, space, name, tmpl_args, decl_scope, functions, flags = OVERLOAD_FLAGS_USE_FFI): W_CPPOverload.__init__(self, space, decl_scope, functions, flags) self.name = name + self.tmpl_args = tmpl_args self.overloads = {} self.master = self self.w_this = space.w_None + def clone(self, tmpl_args): + other = W_CPPTemplateOverload(self.space, self.name, tmpl_args, self.scope, self.functions, self.flags) + other.overloads = self.overloads + other.master = self.master + other.w_this = self.w_this + return other + 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) + cppol = W_CPPTemplateOverload(self.space, self.name, self.tmpl_args, self.scope, self.functions, self.flags) cppol.w_this = w_cppinstance cppol.master = self.master return cppol # bound @@ -819,13 +942,18 @@ # 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 + # do explicit lookup with tmpl_args if given try: - return W_CPPOverload.call_args(self, args_w) + fullname = self.name + if self.tmpl_args is not None: + fullname = fullname+'<'+self.tmpl_args+'>' + return self.instantiate_and_call(fullname, args_w) except Exception: pass - return self.instantiate_and_call(self.name, args_w) + # otherwise, try existing overloads or compile-time instantiations + # TODO: consolidate errors + return W_CPPOverload.call_args(self, [self.w_this]+args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): @@ -839,33 +967,44 @@ W_CPPTemplateOverload.typedef = TypeDef( 'CPPTemplateOverload', - __get__ = interp2app(W_CPPTemplateOverload.descr_get), - __getitem__ = interp2app(W_CPPTemplateOverload.getitem), - __call__ = interp2app(W_CPPTemplateOverload.call_args), - __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) + __get__ = interp2app(W_CPPTemplateOverload.descr_get), + __getitem__ = interp2app(W_CPPTemplateOverload.getitem), + __call__ = interp2app(W_CPPTemplateOverload.call_args), + __creates__ = GetSetProperty(W_CPPTemplateOverload.fget_creates, W_CPPTemplateOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPTemplateOverload.fget_mempolicy, W_CPPTemplateOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPTemplateOverload.fget_release_gil, W_CPPTemplateOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPTemplateOverload.fget_useffi, W_CPPTemplateOverload.fset_useffi), + __doc__ = GetSetProperty(W_CPPTemplateOverload.fget_doc) ) class W_CPPTemplateStaticOverload(W_CPPStaticOverload, 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', 'w_this'] - _immutable_fields_ = ['name'] + _attrs_ = ['name', 'tmpl_args', 'overloads', 'master', 'w_this'] + _immutable_fields_ = ['name', 'tmpl_args'] - def __init__(self, space, name, decl_scope, funcs, flags = OVERLOAD_FLAGS_USE_FFI): + def __init__(self, space, name, tmpl_args, decl_scope, funcs, flags = OVERLOAD_FLAGS_USE_FFI): W_CPPStaticOverload.__init__(self, space, decl_scope, funcs, flags) self.name = name + self.tmpl_args = tmpl_args self.overloads = {} self.master = self self.w_this = space.w_None + def clone(self, tmpl_args): + other = W_CPPTemplateStaticOverload(self.space, self.name, tmpl_args, self.scope, self.functions, self.flags) + other.overloads = self.overloads + other.master = self.master + other.w_this = self.w_this + return other + 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: - cppol = W_CPPTemplateStaticOverload(self.space, self.name, self.scope, self.functions, self.flags) + cppol = W_CPPTemplateStaticOverload(self.space, self.name, self.tmpl_args, self.scope, self.functions, self.flags) cppol.w_this = w_cppinstance cppol.master = self.master return cppol # bound @@ -875,15 +1014,20 @@ def call_args(self, args_w): # direct call: either pick non-templated overload or attempt to deduce # the template instantiation from the argument types + # TODO: refactor with W_CPPTemplateOverload - # try existing overloads or compile-time instantiations + # do explicit lookup with tmpl_args if given try: - return W_CPPStaticOverload.call_args(self, args_w) + fullname = self.name + if self.tmpl_args is not None: + fullname = fullname+'<'+self.tmpl_args+'>' + return self.instantiate_and_call(fullname, args_w) except Exception: pass - # try new instantiation - return self.instantiate_and_call(self.name, args_w) + # otherwise, try existing overloads or compile-time instantiations + # TODO: consolidate errors + return W_CPPStaticOverload.call_args(self, args_w) @unwrap_spec(args_w='args_w') def getitem(self, args_w): @@ -897,11 +1041,18 @@ W_CPPTemplateStaticOverload.typedef = TypeDef( 'CPPTemplateStaticOverload', - __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), - __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), - __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), - __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, W_CPPTemplateStaticOverload.fset_useffi), - __doc__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_doc) + __get__ = interp2app(W_CPPTemplateStaticOverload.descr_get), + __getitem__ = interp2app(W_CPPTemplateStaticOverload.getitem), + __call__ = interp2app(W_CPPTemplateStaticOverload.call_args), + __creates__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_creates, + W_CPPTemplateStaticOverload.fset_creates), + __mempolicy__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_mempolicy, + W_CPPTemplateStaticOverload.fset_mempolicy), + __release_gil__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_release_gil, + W_CPPTemplateStaticOverload.fset_release_gil), + __useffi__ = GetSetProperty(W_CPPTemplateStaticOverload.fget_useffi, _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit