[pypy-commit] pypy unicode-utf8-py3: merge py3.5 into branch
mattip
pypy.commits at gmail.com
Mon Aug 13 12:41:18 EDT 2018
Author: Matti Picus <matti.picus at 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,
More information about the pypy-commit
mailing list