Author: Matti Picus <[email protected]>
Branch: unicode-utf8-py3
Changeset: r94791:ab11d063dfa4
Date: 2018-06-30 19:47 -0700
http://bitbucket.org/pypy/pypy/changeset/ab11d063dfa4/
Log: merge py3.5 into branch
diff too long, truncating to 2000 out of 5287 lines
diff --git a/pypy/doc/sandbox.rst b/pypy/doc/sandbox.rst
--- a/pypy/doc/sandbox.rst
+++ b/pypy/doc/sandbox.rst
@@ -3,6 +3,11 @@
PyPy's sandboxing features
==========================
+.. warning:: This is not actively maintained. You will likely have to fix
+ some issues yourself, or otherwise play around on your own. We provide
+ this documentation for historical reasions, it will not translate or
+ run on the latest PyPy code base.
+
Introduction
------------
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,9 @@
.. branch: cppyy-packaging
-Upgrade to backend 0.6.0, support exception handling from wrapped functions,
-update enum handling, const correctness for data members and associated tests,
-support anonymous enums, support for function pointer arguments
+Upgrade to backend 1.1.0, improved handling of templated methods and
+functions (in particular automatic deduction of types), improved pythonization
+interface, and a range of compatibility fixes for Python3
.. branch: socket_default_timeout_blockingness
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -270,7 +270,7 @@
if self.frame is None:
return # nothing to do in this case
space = self.space
- operr = get_generator_exit(space)
+ operr = None
# note: w_yielded_from is always None if 'self.running'
w_yf = self.w_yielded_from
if w_yf is not None:
@@ -278,6 +278,8 @@
self._gen_close_iter(space)
except OperationError as e:
operr = e
+ if operr is None:
+ operr = OperationError(space.w_GeneratorExit, space.w_None)
try:
self.send_error(operr)
except OperationError as e:
@@ -451,11 +453,6 @@
space.call_function(space.w_StopIteration, w_value))
[email protected]()
-def get_generator_exit(space):
- return OperationError(space.w_GeneratorExit,
- space.call_function(space.w_GeneratorExit))
-
def gen_close_iter(space, w_yf):
# This helper function is used by close() and throw() to
# close a subiterator being delegated to by yield-from.
diff --git a/pypy/interpreter/pyparser/automata.py
b/pypy/interpreter/pyparser/automata.py
--- a/pypy/interpreter/pyparser/automata.py
+++ b/pypy/interpreter/pyparser/automata.py
@@ -23,6 +23,10 @@
ERROR_STATE = chr(255)
+# NB: all non-ascii bytes (>= 128) will be turned into 128
+NON_ASCII = chr(128)
+
+
class DFA:
# ____________________________________________________________
def __init__(self, states, accepts, start = 0):
@@ -36,7 +40,10 @@
for key in state:
if key == DEFAULT:
continue
- maximum = max(ord(key), maximum)
+ ordkey = ord(key)
+ if ordkey > 128:
+ raise ValueError("DFA does not support matching of
specific non-ASCII character %r. Use NON_ASCII instead" % key)
+ maximum = max(ordkey, maximum)
self.max_char = maximum + 1
defaults = []
@@ -73,7 +80,7 @@
for i in range(pos, len(inVec)):
item = inVec[i]
if ord(item) > 0x80:
- item = "\x80" # NON_ASCII
+ item = NON_ASCII
accept = self.accepts[crntState]
crntState = self._next_state(item, crntState)
if crntState != ERROR_STATE:
@@ -105,6 +112,8 @@
i = pos
for i in range(pos, len(inVec)):
item = inVec[i]
+ if ord(item) > 0x80:
+ item = NON_ASCII
accept = self.accepts[crntState]
if accept:
return i
diff --git a/pypy/interpreter/pyparser/test/test_automata.py
b/pypy/interpreter/pyparser/test/test_automata.py
--- a/pypy/interpreter/pyparser/test/test_automata.py
+++ b/pypy/interpreter/pyparser/test/test_automata.py
@@ -1,4 +1,7 @@
-from pypy.interpreter.pyparser.automata import DFA, NonGreedyDFA, DEFAULT
+# coding: utf-8
+import pytest
+
+from pypy.interpreter.pyparser.automata import DFA, NonGreedyDFA, DEFAULT,
NON_ASCII
def test_states():
d = DFA([{"\x00": 1}, {"\x01": 0}], [False, True])
@@ -27,3 +30,18 @@
d = NonGreedyDFA([{"a": 1}, {DEFAULT: 0}], [False, True])
assert d.recognize("a,a?ab") == 1
assert d.recognize("c") == -1
+
+def test_nonascii():
+ d = DFA([{"a": 1}, {NON_ASCII: 1}], [False, True])
+ input = u"aüüüü".encode("utf-8")
+ assert d.recognize(input) == len(input)
+ assert d.recognize("c") == -1
+ assert d.recognize("ü") == -1
+
+ d = NonGreedyDFA([{NON_ASCII: 0, "b": 1}, {"b": 0}], [False, True])
+ input = u"üübbbb".encode("utf-8")
+ assert d.recognize(input) == len(u"üüb".encode("utf-8"))
+ assert d.recognize("c") == -1
+
+ pytest.raises(ValueError, DFA, [{"\x81": 2}], [True])
+
diff --git a/pypy/interpreter/test/test_function.py
b/pypy/interpreter/test/test_function.py
--- a/pypy/interpreter/test/test_function.py
+++ b/pypy/interpreter/test/test_function.py
@@ -170,6 +170,7 @@
h = get_h()
raises(ValueError, "f.__code__ = h.__code__")
+ @pytest.mark.skipif("config.option.runappdirect")
def test_write_code_builtin_forbidden(self):
def f(*args):
return 42
@@ -339,7 +340,6 @@
assert meth() == obj
def test_none_get_interaction(self):
- skip("XXX issue #2083")
assert type(None).__repr__(None) == 'None'
def test_none_get_interaction_2(self):
@@ -544,7 +544,9 @@
pass
r = repr(ClsB().f)
assert "ClsA.f of <" in r
- assert "ClsB object at " in r
+ assert repr(type(ClsA.f)) == "<class 'function'>"
+ assert repr(type(ClsA().f)) == "<class 'method'>"
+
def test_method_call(self):
class C(object):
diff --git a/pypy/module/_cppyy/__init__.py b/pypy/module/_cppyy/__init__.py
--- a/pypy/module/_cppyy/__init__.py
+++ b/pypy/module/_cppyy/__init__.py
@@ -1,7 +1,7 @@
from pypy.interpreter.mixedmodule import MixedModule
class Module(MixedModule):
- "This module brigdes the cppyy frontend with its backend, through PyPy.\n\
+ "This module bridges the cppyy frontend with its backend, through PyPy.\n\
See http://cppyy.readthedocs.io/en/latest for full details."
interpleveldefs = {
@@ -14,17 +14,19 @@
'_set_function_generator': 'interp_cppyy.set_function_generator',
'_register_class' : 'interp_cppyy.register_class',
'_get_nullptr' : 'interp_cppyy.get_nullptr',
- 'CPPInstanceBase' : 'interp_cppyy.W_CPPInstance',
+ 'CPPInstance' : 'interp_cppyy.W_CPPInstance',
'addressof' : 'interp_cppyy.addressof',
'_bind_object' : 'interp_cppyy._bind_object',
'bind_object' : 'interp_cppyy.bind_object',
'move' : 'interp_cppyy.move',
+ '_pin_type' : 'interp_cppyy._pin_type',
}
appleveldefs = {
'_post_import_startup' : 'pythonify._post_import_startup',
+ 'Template' : 'pythonify.CPPTemplate',
'add_pythonization' : 'pythonify.add_pythonization',
- 'Template' : 'pythonify.CPPTemplate',
+ 'remove_pythonization' : 'pythonify.remove_pythonization',
}
def __init__(self, space, *args):
diff --git a/pypy/module/_cppyy/capi/__init__.py
b/pypy/module/_cppyy/capi/__init__.py
--- a/pypy/module/_cppyy/capi/__init__.py
+++ b/pypy/module/_cppyy/capi/__init__.py
@@ -11,6 +11,3 @@
assert lltype.typeOf(ptr) == C_OBJECT
address = rffi.cast(rffi.CCHARP, ptr)
return rffi.cast(C_OBJECT, lltype.direct_ptradd(address, offset))
-
-def exchange_address(ptr, cif_descr, index):
- return rffi.ptradd(ptr, cif_descr.exchange_args[index])
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
@@ -69,7 +69,8 @@
space = self.space
cif_descr = self.cif_descr
size = cif_descr.exchange_size
- raw_string = rffi.cast(rffi.CCHARP, 0) # only ever have one in the
CAPI
+ raw_string1 = rffi.cast(rffi.CCHARP, 0)
+ raw_string2 = rffi.cast(rffi.CCHARP, 0) # have max two in any CAPI
buffer = lltype.malloc(rffi.CCHARP.TO, size, flavor='raw')
try:
for i in range(len(args)):
@@ -88,14 +89,18 @@
assert obj._voidp != rffi.cast(rffi.VOIDP, 0)
data = rffi.cast(rffi.VOIDPP, data)
data[0] = obj._voidp
- else: # only other use is sring
+ else: # only other use is string
assert obj.tc == 's'
n = len(obj._string)
- assert raw_string == rffi.cast(rffi.CCHARP, 0)
- # XXX could use rffi.get_nonmovingbuffer_final_null()
- raw_string = rffi.str2charp(obj._string)
data = rffi.cast(rffi.CCHARPP, data)
- data[0] = raw_string
+ if raw_string1 == rffi.cast(rffi.CCHARP, 0):
+ # XXX could use rffi.get_nonmovingbuffer_final_null()
+ raw_string1 = rffi.str2charp(obj._string)
+ data[0] = raw_string1
+ else:
+ assert raw_string2 == rffi.cast(rffi.CCHARP, 0)
+ raw_string2 = rffi.str2charp(obj._string)
+ data[0] = raw_string2
jit_libffi.jit_ffi_call(cif_descr,
rffi.cast(rffi.VOIDP, funcaddr),
@@ -106,8 +111,10 @@
# immediate unwrapping, the round-trip is removed
w_res = self.ctitem.copy_and_convert_to_object(resultdata)
finally:
- if raw_string != rffi.cast(rffi.CCHARP, 0):
- rffi.free_charp(raw_string)
+ if raw_string1 != rffi.cast(rffi.CCHARP, 0):
+ rffi.free_charp(raw_string1)
+ if raw_string2 != rffi.cast(rffi.CCHARP, 0):
+ rffi.free_charp(raw_string2)
lltype.free(buffer, flavor='raw')
return w_res
@@ -183,8 +190,7 @@
'constructor' : ([c_method, c_object, c_int, c_voidp],
c_object),
'call_o' : ([c_method, c_object, c_int, c_voidp, c_type],
c_object),
- 'function_address_from_index' : ([c_scope, c_index],
c_voidp), # TODO: verify
- 'function_address_from_method' : ([c_method],
c_voidp), # id.
+ 'function_address' : ([c_method],
c_voidp), # TODO: verify
# handling of function argument buffer
'allocate_function_args' : ([c_int], c_voidp),
@@ -207,6 +213,8 @@
'num_bases' : ([c_type], c_int),
'base_name' : ([c_type, c_int],
c_ccharp),
'is_subtype' : ([c_type, c_type], c_int),
+ 'smartptr_info' : ([c_ccharp, c_voidp, c_voidp],
c_int),
+ 'add_smartptr_type' : ([c_ccharp], c_void),
'base_offset' : ([c_type, c_type, c_object, c_int],
c_ptrdiff_t),
@@ -214,30 +222,31 @@
'num_methods' : ([c_scope], c_int),
'method_indices_from_name' : ([c_scope, c_ccharp],
c_index_array),
- 'method_name' : ([c_scope, c_index],
c_ccharp),
- 'method_mangled_name' : ([c_scope, c_index],
c_ccharp),
- 'method_result_type' : ([c_scope, c_index],
c_ccharp),
- 'method_num_args' : ([c_scope, c_index], c_int),
- 'method_req_args' : ([c_scope, c_index], c_int),
- 'method_arg_type' : ([c_scope, c_index, c_int],
c_ccharp),
- 'method_arg_default' : ([c_scope, c_index, c_int],
c_ccharp),
- 'method_signature' : ([c_scope, c_index, c_int],
c_ccharp),
- 'method_prototype' : ([c_scope, c_index, c_int],
c_ccharp),
+ 'get_method' : ([c_scope, c_index],
c_method),
+
+ 'method_name' : ([c_method],
c_ccharp),
+ 'method_full_name' : ([c_method],
c_ccharp),
+ 'method_mangled_name' : ([c_method],
c_ccharp),
+ 'method_result_type' : ([c_method],
c_ccharp),
+ 'method_num_args' : ([c_method], c_int),
+ 'method_req_args' : ([c_method], c_int),
+ 'method_arg_type' : ([c_method, c_int],
c_ccharp),
+ 'method_arg_default' : ([c_method, c_int],
c_ccharp),
+ 'method_signature' : ([c_method, c_int],
c_ccharp),
+ 'method_prototype' : ([c_scope, c_method, c_int],
c_ccharp),
'is_const_method' : ([c_method], c_int),
'exists_method_template' : ([c_scope, c_ccharp], c_int),
'method_is_template' : ([c_scope, c_index], c_int),
- 'method_num_template_args' : ([c_scope, c_index], c_int),
- 'method_template_arg_name' : ([c_scope, c_index, c_index],
c_ccharp),
+ 'get_method_template' : ([c_scope, c_ccharp, c_ccharp],
c_method),
- 'get_method' : ([c_scope, c_index],
c_method),
'get_global_operator' : ([c_scope, c_scope, c_scope,
c_ccharp], c_index),
# method properties
- 'is_public_method' : ([c_type, c_index], c_int),
- 'is_constructor' : ([c_type, c_index], c_int),
- 'is_destructor' : ([c_type, c_index], c_int),
- 'is_staticmethod' : ([c_type, c_index], c_int),
+ 'is_public_method' : ([c_method], c_int),
+ 'is_constructor' : ([c_method], c_int),
+ 'is_destructor' : ([c_method], c_int),
+ 'is_staticmethod' : ([c_method], c_int),
# data member reflection information
'num_datamembers' : ([c_scope], c_int),
@@ -415,13 +424,9 @@
args = [_ArgH(cppmethod), _ArgH(cppobject), _ArgL(nargs), _ArgP(cargs),
_ArgH(cppclass.handle)]
return _cdata_to_cobject(space, call_capi(space, 'call_o', args))
-def c_function_address_from_index(space, cppscope, index):
- args = [_ArgH(cppscope.handle), _ArgL(index)]
+def c_function_address(space, cppmethod):
return rffi.cast(C_FUNC_PTR,
- _cdata_to_ptr(space, call_capi(space, 'function_address_from_index',
args)))
-def c_function_address_from_method(space, cppmethod):
- return rffi.cast(C_FUNC_PTR,
- _cdata_to_ptr(space, call_capi(space, 'function_address_from_method',
[_ArgH(cppmethod)])))
+ _cdata_to_ptr(space, call_capi(space, 'function_address',
[_ArgH(cppmethod)])))
# handling of function argument buffer ---------------------------------------
def c_allocate_function_args(space, size):
@@ -479,6 +484,21 @@
if derived == base:
return bool(1)
return space.bool_w(call_capi(space, 'is_subtype', [_ArgH(derived.handle),
_ArgH(base.handle)]))
+def c_smartptr_info(space, name):
+ out_raw = lltype.malloc(rffi.ULONGP.TO, 1, flavor='raw', zero=True)
+ out_deref = lltype.malloc(rffi.ULONGP.TO, 1, flavor='raw', zero=True)
+ try:
+ args = [_ArgS(name),
+ _ArgP(rffi.cast(rffi.VOIDP, out_raw)), _ArgP(rffi.cast(rffi.VOIDP,
out_deref))]
+ result = space.bool_w(call_capi(space, 'smartptr_info', args))
+ raw = rffi.cast(C_TYPE, out_raw[0])
+ deref = rffi.cast(C_METHOD, out_deref[0])
+ finally:
+ lltype.free(out_deref, flavor='raw')
+ lltype.free(out_raw, flavor='raw')
+ return (result, raw, deref)
+def c_add_smartptr_type(space, name):
+ return space.bool_w(call_capi(space, 'add_smartptr_type', [_ArgS(name)]))
def _c_base_offset(space, derived_h, base_h, address, direction):
args = [_ArgH(derived_h), _ArgH(base_h), _ArgH(address), _ArgL(direction)]
@@ -510,30 +530,36 @@
c_free(space, rffi.cast(rffi.VOIDP, indices)) # c_free defined below
return py_indices
-def c_method_name(space, cppscope, index):
+def c_get_method(space, cppscope, index):
args = [_ArgH(cppscope.handle), _ArgL(index)]
- return charp2str_free(space, call_capi(space, 'method_name', args))
-def c_method_result_type(space, cppscope, index):
- args = [_ArgH(cppscope.handle), _ArgL(index)]
- return charp2str_free(space, call_capi(space, 'method_result_type', args))
-def c_method_num_args(space, cppscope, index):
- args = [_ArgH(cppscope.handle), _ArgL(index)]
- return space.int_w(call_capi(space, 'method_num_args', args))
-def c_method_req_args(space, cppscope, index):
- args = [_ArgH(cppscope.handle), _ArgL(index)]
- return space.int_w(call_capi(space, 'method_req_args', args))
-def c_method_arg_type(space, cppscope, index, arg_index):
- args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)]
+ return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method',
args)))
+
+def c_method_name(space, cppmeth):
+ return charp2str_free(space, call_capi(space, 'method_name',
[_ArgH(cppmeth)]))
+def c_method_full_name(space, cppmeth):
+ return charp2str_free(space, call_capi(space, 'method_full_name',
[_ArgH(cppmeth)]))
+def c_method_mangled_name(space, cppmeth):
+ return charp2str_free(space, call_capi(space, 'method_mangled_name',
[_ArgH(cppmeth)]))
+def c_method_result_type(space, cppmeth):
+ return charp2str_free(space, call_capi(space, 'method_result_type',
[_ArgH(cppmeth)]))
+def c_method_num_args(space, cppmeth):
+ return space.int_w(call_capi(space, 'method_num_args', [_ArgH(cppmeth)]))
+def c_method_req_args(space, cppmeth):
+ return space.int_w(call_capi(space, 'method_req_args', [_ArgH(cppmeth)]))
+def c_method_arg_type(space, cppmeth, arg_index):
+ args = [_ArgH(cppmeth), _ArgL(arg_index)]
return charp2str_free(space, call_capi(space, 'method_arg_type', args))
-def c_method_arg_default(space, cppscope, index, arg_index):
- args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(arg_index)]
+def c_method_arg_default(space, cppmeth, arg_index):
+ args = [_ArgH(cppmeth), _ArgL(arg_index)]
return charp2str_free(space, call_capi(space, 'method_arg_default', args))
-def c_method_signature(space, cppscope, index, show_formalargs=True):
- args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)]
+def c_method_signature(space, cppmeth, show_formalargs=True):
+ args = [_ArgH(cppmeth), _ArgL(show_formalargs)]
return charp2str_free(space, call_capi(space, 'method_signature', args))
-def c_method_prototype(space, cppscope, index, show_formalargs=True):
- args = [_ArgH(cppscope.handle), _ArgL(index), _ArgL(show_formalargs)]
+def c_method_prototype(space, cppscope, cppmeth, show_formalargs=True):
+ args = [_ArgH(cppscope.handle), _ArgH(cppmeth), _ArgL(show_formalargs)]
return charp2str_free(space, call_capi(space, 'method_prototype', args))
+def c_is_const_method(space, cppmeth):
+ return space.bool_w(call_capi(space, 'is_const_method', [_ArgH(cppmeth)]))
def c_exists_method_template(space, cppscope, name):
args = [_ArgH(cppscope.handle), _ArgS(name)]
@@ -541,21 +567,10 @@
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_method_num_template_args(space, cppscope, index):
- args = [_ArgH(cppscope.handle), _ArgL(index)]
- return space.int_w(call_capi(space, 'method_num_template_args', args))
-def c_template_args(space, cppscope, index):
- nargs = _c_method_num_template_args(space, cppscope, index)
- arg1 = _ArgH(cppscope.handle)
- arg2 = _ArgL(index)
- args = [c_resolve_name(space, charp2str_free(space,
- call_capi(space, 'method_template_arg_name', [arg1, arg2,
_ArgL(iarg)]))
- ) for iarg in range(nargs)]
- return args
-def c_get_method(space, cppscope, index):
- args = [_ArgH(cppscope.handle), _ArgL(index)]
- return rffi.cast(C_METHOD, space.uint_w(call_capi(space, 'get_method',
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)]
@@ -563,18 +578,14 @@
return rffi.cast(WLAVC_INDEX, -1)
# method properties ----------------------------------------------------------
-def c_is_public_method(space, cppclass, index):
- args = [_ArgH(cppclass.handle), _ArgL(index)]
- return space.bool_w(call_capi(space, 'is_public_method', args))
-def c_is_constructor(space, cppclass, index):
- args = [_ArgH(cppclass.handle), _ArgL(index)]
- return space.bool_w(call_capi(space, 'is_constructor', args))
-def c_is_destructor(space, cppclass, index):
- args = [_ArgH(cppclass.handle), _ArgL(index)]
- return space.bool_w(call_capi(space, 'is_destructor', args))
-def c_is_staticmethod(space, cppclass, index):
- args = [_ArgH(cppclass.handle), _ArgL(index)]
- return space.bool_w(call_capi(space, 'is_staticmethod', args))
+def c_is_public_method(space, cppmeth):
+ return space.bool_w(call_capi(space, 'is_public_method', [_ArgH(cppmeth)]))
+def c_is_constructor(space, cppmeth):
+ return space.bool_w(call_capi(space, 'is_constructor', [_ArgH(cppmeth)]))
+def c_is_destructor(space, cppmeth):
+ return space.bool_w(call_capi(space, 'is_destructor', [_ArgH(cppmeth)]))
+def c_is_staticmethod(space, cppmeth):
+ return space.bool_w(call_capi(space, 'is_staticmethod', [_ArgH(cppmeth)]))
# data member reflection information -----------------------------------------
def c_num_datamembers(space, cppscope):
@@ -676,7 +687,7 @@
space.setattr(w_pycppclass, space.newtext(m1),
space.getattr(w_pycppclass, space.newtext(m2)))
-def pythonize(space, name, w_pycppclass):
+def pythonize(space, w_pycppclass, name):
if name == "string":
space.setattr(w_pycppclass, space.newtext("c_str"),
_pythonizations["stdstring_c_str"])
_method_alias(space, w_pycppclass, "_cppyy_as_builtin", "c_str")
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
@@ -7,7 +7,7 @@
from rpython.rlib import rfloat, rawrefcount
from pypy.module._rawffi.interp_rawffi import letter2tp
-from pypy.module._rawffi.array import W_Array, W_ArrayInstance
+from pypy.module._rawffi.array import W_ArrayInstance
from pypy.module._cppyy import helper, capi, ffitypes
@@ -68,6 +68,8 @@
pass
# array type
try:
+ if hasattr(space, "fake"):
+ raise NotImplementedError
arr = space.interp_w(W_ArrayInstance, w_obj, can_be_None=True)
if arr:
return rffi.cast(rffi.VOIDP, space.uint_w(arr.getbuffer(space)))
@@ -130,20 +132,6 @@
pass
-class ArrayCache(object):
- def __init__(self, space):
- self.space = space
- def __getattr__(self, name):
- if name.startswith('array_'):
- typecode = name[len('array_'):]
- arr = self.space.interp_w(W_Array, letter2tp(self.space, typecode))
- setattr(self, name, arr)
- return arr
- raise AttributeError(name)
-
- def _freeze_(self):
- return True
-
class ArrayTypeConverterMixin(object):
_mixin_ = True
_immutable_fields_ = ['size']
@@ -162,9 +150,7 @@
# read access, so no copy needed
address_value = self._get_raw_address(space, w_obj, offset)
address = rffi.cast(rffi.ULONG, address_value)
- cache = space.fromcache(ArrayCache)
- arr = getattr(cache, 'array_' + self.typecode)
- return arr.fromaddress(space, address, self.size)
+ return W_ArrayInstance(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)
@@ -205,17 +191,15 @@
# read access, so no copy needed
address_value = self._get_raw_address(space, w_obj, offset)
address = rffi.cast(rffi.ULONGP, address_value)
- cache = space.fromcache(ArrayCache)
- arr = getattr(cache, 'array_' + self.typecode)
- return arr.fromaddress(space, address[0], self.size)
+ return W_ArrayInstance(space, letter2tp(space, self.typecode),
self.size, address[0])
def to_memory(self, space, w_obj, w_value, offset):
# copy only the pointer value
rawobject = get_rawobject_nonnull(space, w_obj)
- byteptr = rffi.cast(rffi.CCHARPP, capi.direct_ptradd(rawobject,
offset))
+ byteptr = rffi.cast(rffi.VOIDPP, capi.direct_ptradd(rawobject, offset))
buf = space.getarg_w('s*', w_value)
try:
- byteptr[0] = buf.get_raw_address()
+ byteptr[0] = rffi.cast(rffi.VOIDP, buf.get_raw_address())
except ValueError:
raise oefmt(space.w_TypeError,
"raw buffer interface not supported")
@@ -337,6 +321,10 @@
address = rffi.cast(rffi.CCHARP, self._get_raw_address(space, w_obj,
offset))
address[0] = self._unwrap_object(space, w_value)
+
+class UCharConverter(ffitypes.typeid(rffi.UCHAR), CharConverter):
+ pass
+
class FloatConverter(ffitypes.typeid(rffi.FLOAT), FloatTypeConverterMixin,
TypeConverter):
_immutable_fields_ = ['default']
@@ -398,12 +386,12 @@
arg = space.text_w(w_obj)
x[0] = rffi.cast(rffi.LONG, rffi.str2charp(arg))
ba = rffi.cast(rffi.CCHARP, address)
- ba[capi.c_function_arg_typeoffset(space)] = 'o'
+ ba[capi.c_function_arg_typeoffset(space)] = 'p'
def from_memory(self, space, w_obj, w_pycppclass, offset):
address = self._get_raw_address(space, w_obj, offset)
charpptr = rffi.cast(rffi.CCHARPP, address)
- return space.newbytes(rffi.charp2str(charpptr[0]))
+ return space.newtext(rffi.charp2str(charpptr[0]))
def free_argument(self, space, arg, call_local):
lltype.free(rffi.cast(rffi.CCHARPP, arg)[0], flavor='raw')
@@ -420,7 +408,7 @@
strsize = self.size
if charpptr[self.size-1] == '\0':
strsize = self.size-1 # rffi will add \0 back
- return space.newbytes(rffi.charpsize2str(charpptr, strsize))
+ return space.newtext(rffi.charpsize2str(charpptr, strsize))
class VoidPtrConverter(TypeConverter):
@@ -449,12 +437,12 @@
# 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)
- ptrval = rffi.cast(rffi.ULONG, rffi.cast(rffi.VOIDPP, address)[0])
- if ptrval == 0:
+ ptrval = rffi.cast(rffi.ULONGP, address)[0]
+ if ptrval == rffi.cast(rffi.ULONG, 0):
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.get_nullptr(space)
- arr = space.interp_w(W_Array, letter2tp(space, 'P'))
- return arr.fromaddress(space, ptrval, sys.maxint)
+ shape = letter2tp(space, 'P')
+ return W_ArrayInstance(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))
@@ -504,8 +492,8 @@
def _unwrap_object(self, space, w_obj):
from pypy.module._cppyy.interp_cppyy import W_CPPInstance
if isinstance(w_obj, W_CPPInstance):
- from pypy.module._cppyy.interp_cppyy import
INSTANCE_FLAGS_IS_R_VALUE
- if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE:
+ from pypy.module._cppyy.interp_cppyy import
INSTANCE_FLAGS_IS_RVALUE
+ if w_obj.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):
@@ -514,7 +502,7 @@
obj_address = capi.direct_ptradd(rawobject, offset)
return rffi.cast(capi.C_OBJECT, obj_address)
raise oefmt(space.w_TypeError,
- "cannot pass %T as %s", w_obj, self.clsdecl.name)
+ "cannot pass %T instance as %s", w_obj, self.clsdecl.name)
def cffi_type(self, space):
state = space.fromcache(ffitypes.State)
@@ -534,11 +522,18 @@
class InstanceMoveConverter(InstanceRefConverter):
def _unwrap_object(self, space, w_obj):
# moving is same as by-ref, but have to check that move is allowed
- from pypy.module._cppyy.interp_cppyy import W_CPPInstance,
INSTANCE_FLAGS_IS_R_VALUE
- if isinstance(w_obj, W_CPPInstance):
- if w_obj.flags & INSTANCE_FLAGS_IS_R_VALUE:
- w_obj.flags &= ~INSTANCE_FLAGS_IS_R_VALUE
- return InstanceRefConverter._unwrap_object(self, space, w_obj)
+ 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
+ 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
+ raise
raise oefmt(space.w_ValueError, "object is not an rvalue")
@@ -629,8 +624,7 @@
address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space,
w_obj, offset))
assign = self.clsdecl.get_overload("__assign__")
from pypy.module._cppyy import interp_cppyy
- assign.call(
- interp_cppyy.wrap_cppinstance(space, address, self.clsdecl,
do_cast=False), [w_value])
+ assign.call_impl(address, [w_value])
except Exception:
InstanceConverter.to_memory(self, space, w_obj, w_value, offset)
@@ -639,7 +633,6 @@
class StdStringRefConverter(InstancePtrConverter):
_immutable_fields_ = ['cppclass', 'typecode']
-
typecode = 'V'
def __init__(self, space, extra):
@@ -702,8 +695,7 @@
m = cppol.functions[i]
if m.signature(False) == self.signature:
x = rffi.cast(rffi.VOIDPP, address)
- x[0] = rffi.cast(rffi.VOIDP,
- capi.c_function_address_from_method(space, m.cppmethod))
+ x[0] = rffi.cast(rffi.VOIDP, capi.c_function_address(space,
m.cppmethod))
address = rffi.cast(capi.C_OBJECT, address)
ba = rffi.cast(rffi.CCHARP, address)
ba[capi.c_function_arg_typeoffset(space)] = 'p'
@@ -714,6 +706,67 @@
"no overload found matching %s", self.signature)
+class SmartPtrConverter(TypeConverter):
+ _immutable_fields = ['typecode', 'smartdecl', 'rawdecl', 'deref']
+ typecode = 'V'
+
+ def __init__(self, space, smartdecl, raw, deref):
+ from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl,
get_pythonized_cppclass
+ self.smartdecl = smartdecl
+ w_raw = get_pythonized_cppclass(space, raw)
+ self.rawdecl = space.interp_w(W_CPPClassDecl,
+ space.findattr(w_raw, space.newtext("__cppdecl__")))
+ self.deref = deref
+
+ def _unwrap_object(self, space, w_obj):
+ from pypy.module._cppyy.interp_cppyy import W_CPPInstance
+ if isinstance(w_obj, W_CPPInstance):
+ # w_obj could carry a 'hidden' smart ptr or be one, cover both
cases
+ have_match = False
+ if w_obj.smartdecl and capi.c_is_subtype(space, w_obj.smartdecl,
self.smartdecl):
+ # hidden case, do not derefence when getting obj address
+ have_match = True
+ rawobject = w_obj._rawobject # TODO: this direct access
if fugly
+ offset = capi.c_base_offset(space, w_obj.smartdecl,
self.smartdecl, rawobject, 1)
+ elif capi.c_is_subtype(space, w_obj.clsdecl, self.smartdecl):
+ # exposed smart pointer
+ have_match = True
+ rawobject = w_obj.get_rawobject()
+ offset = capi.c_base_offset(space, w_obj.clsdecl,
self.smartdecl, rawobject, 1)
+ if have_match:
+ obj_address = capi.direct_ptradd(rawobject, offset)
+ return rffi.cast(capi.C_OBJECT, obj_address)
+
+ raise oefmt(space.w_TypeError,
+ "cannot pass %T instance as %s", w_obj, self.rawdecl.name)
+
+ def convert_argument(self, space, w_obj, address, call_local):
+ x = rffi.cast(rffi.VOIDPP, address)
+ x[0] = rffi.cast(rffi.VOIDP, self._unwrap_object(space, w_obj))
+ address = rffi.cast(capi.C_OBJECT, address)
+ ba = rffi.cast(rffi.CCHARP, address)
+ ba[capi.c_function_arg_typeoffset(space)] = self.typecode
+
+ def from_memory(self, space, w_obj, w_pycppclass, offset):
+ address = rffi.cast(capi.C_OBJECT, self._get_raw_address(space, w_obj,
offset))
+ from pypy.module._cppyy import interp_cppyy
+ return interp_cppyy.wrap_cppinstance(space, address,
+ self.rawdecl, smartdecl=self.smartdecl, deref=self.deref,
do_cast=False)
+
+class SmartPtrPtrConverter(SmartPtrConverter):
+ typecode = 'o'
+
+ def from_memory(self, space, w_obj, w_pycppclass, offset):
+ self._is_abstract(space)
+
+ def to_memory(self, space, w_obj, w_value, offset):
+ self._is_abstract(space)
+
+
+class SmartPtrRefConverter(SmartPtrPtrConverter):
+ typecode = 'V'
+
+
class MacroConverter(TypeConverter):
def from_memory(self, space, w_obj, w_pycppclass, offset):
# TODO: get the actual type info from somewhere ...
@@ -729,44 +782,61 @@
# 1) full, exact match
# 1a) const-removed match
# 2) match of decorated, unqualified type
- # 3) accept ref as pointer (for the stubs, const& can be
- # by value, but that does not work for the ffi path)
- # 4) generalized cases (covers basically all user classes)
- # 5) void* or void converter (which fails on use)
+ # 3) generalized cases (covers basically all user classes)
+ # 3a) smart pointers
+ # 4) void* or void converter (which fails on use)
name = capi.c_resolve_name(space, _name)
- # 1) full, exact match
+ # full, exact match
try:
return _converters[name](space, default)
except KeyError:
pass
- # 1a) const-removed match
+ # const-removed match
try:
return _converters[helper.remove_const(name)](space, default)
except KeyError:
pass
- # 2) match of decorated, unqualified type
+ # match of decorated, unqualified type
compound = helper.compound(name)
clean_name = capi.c_resolve_name(space, helper.clean_type(name))
try:
+ return _converters[clean_name+compound](space, default)
+ except KeyError:
+ pass
+
+ # arrays
+ 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)
except KeyError:
pass
- # 3) TODO: accept ref as pointer
-
- # 4) generalized cases (covers basically all user classes)
+ # generalized cases (covers basically all user classes)
from pypy.module._cppyy import interp_cppyy
scope_decl = interp_cppyy.scope_byname(space, clean_name)
if scope_decl:
- # type check for the benefit of the annotator
from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl
clsdecl = space.interp_w(W_CPPClassDecl, scope_decl, can_be_None=False)
+
+ # check smart pointer type
+ check_smart = capi.c_smartptr_info(space, clean_name)
+ if check_smart[0]:
+ if compound == '':
+ return SmartPtrConverter(space, clsdecl, check_smart[1],
check_smart[2])
+ elif compound == '*':
+ return SmartPtrPtrConverter(space, clsdecl, check_smart[1],
check_smart[2])
+ elif compound == '&':
+ 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 == "*":
return InstancePtrConverter(space, clsdecl)
elif compound == "&":
@@ -786,7 +856,7 @@
if pos > 0:
return FunctionPointerConverter(space, name[pos+2:])
- # 5) void* or void converter (which fails on use)
+ # void* or void converter (which fails on use)
if 0 <= compound.find('*'):
return VoidPtrConverter(space, default) # "user knows best"
@@ -797,6 +867,7 @@
_converters["bool"] = BoolConverter
_converters["char"] = CharConverter
+_converters["unsigned char"] = UCharConverter
_converters["float"] = FloatConverter
_converters["const float&"] = ConstFloatRefConverter
_converters["double"] = DoubleConverter
@@ -886,6 +957,7 @@
"NOT_RPYTHON"
array_info = (
('b', rffi.sizeof(rffi.UCHAR), ("bool",)), # is debatable, but
works ...
+ ('B', rffi.sizeof(rffi.UCHAR), ("unsigned char",)),
('h', rffi.sizeof(rffi.SHORT), ("short int", "short")),
('H', rffi.sizeof(rffi.USHORT), ("unsigned short int", "unsigned
short")),
('i', rffi.sizeof(rffi.INT), ("int",)),
@@ -901,9 +973,11 @@
for tcode, tsize, names in array_info:
class ArrayConverter(ArrayTypeConverterMixin, TypeConverter):
+ _immutable_fields_ = ['typecode', 'typesize']
typecode = tcode
typesize = tsize
class PtrConverter(PtrTypeConverterMixin, TypeConverter):
+ _immutable_fields_ = ['typecode', 'typesize']
typecode = tcode
typesize = tsize
for name in names:
@@ -912,6 +986,7 @@
# special case, const char* w/ size and w/o '\0'
_a_converters["const char[]"] = CStringConverterWithSize
+ _a_converters["char[]"] = _a_converters["const char[]"] #
debatable
_build_array_converters()
@@ -919,7 +994,6 @@
def _add_aliased_converters():
"NOT_RPYTHON"
aliases = (
- ("char", "unsigned char"), # TODO: check
("char", "signed char"), # TODO: check
("const char*", "char*"),
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
@@ -5,7 +5,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib import jit_libffi
-from pypy.module._rawffi.interp_rawffi import unpack_simple_shape
+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
@@ -26,7 +26,7 @@
NULL = lltype.nullptr(jit_libffi.FFI_TYPE_P.TO)
-class FunctionExecutor(object):
+class Executor(object):
def __init__(self, space, extra):
pass
@@ -43,7 +43,7 @@
raise FastCallNotPossible
-class PtrTypeExecutor(FunctionExecutor):
+class PtrTypeExecutor(Executor):
_immutable_fields_ = ['typecode']
typecode = 'P'
@@ -56,14 +56,14 @@
raise NotImplementedError
lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
ptrval = rffi.cast(rffi.ULONG, lresult)
- arr = space.interp_w(W_Array, unpack_simple_shape(space,
space.newtext(self.typecode)))
- if ptrval == 0:
+ if ptrval == rffi.cast(rffi.ULONG, 0):
from pypy.module._cppyy import interp_cppyy
return interp_cppyy.get_nullptr(space)
- return arr.fromaddress(space, ptrval, sys.maxint)
+ shape = letter2tp(space, self.typecode)
+ return W_ArrayInstance(space, shape, sys.maxint/shape.size, ptrval)
-class VoidExecutor(FunctionExecutor):
+class VoidExecutor(Executor):
def cffi_type(self, space):
state = space.fromcache(ffitypes.State)
return state.c_void
@@ -96,7 +96,7 @@
_mixin_ = True
def __init__(self, space, extra):
- FunctionExecutor.__init__(self, space, extra)
+ Executor.__init__(self, space, extra)
self.do_assign = False
self.item = rffi.cast(self.c_type, 0)
@@ -124,8 +124,7 @@
rffi.cast(self.c_ptrtype, rffi.cast(rffi.VOIDPP, result)[0]))
-class CStringExecutor(FunctionExecutor):
-
+class CStringExecutor(Executor):
def execute(self, space, cppmethod, cppthis, num_args, args):
lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
ccpresult = rffi.cast(rffi.CCHARP, lresult)
@@ -139,8 +138,7 @@
return space.newtext(result)
-class ConstructorExecutor(FunctionExecutor):
-
+class ConstructorExecutor(Executor):
def execute(self, space, cppmethod, cpptype, num_args, args):
from pypy.module._cppyy import interp_cppyy
newthis = capi.c_constructor(space, cppmethod, cpptype, num_args, args)
@@ -148,80 +146,77 @@
return space.newlong(rffi.cast(rffi.LONG, newthis)) # really want
ptrdiff_t here
-class InstancePtrExecutor(FunctionExecutor):
- _immutable_fields_ = ['cppclass']
+class InstanceExecutor(Executor):
+ # For return of a C++ instance by pointer: MyClass* func()
+ _immutable_fields_ = ['clsdecl']
- def __init__(self, space, cppclass):
- FunctionExecutor.__init__(self, space, cppclass)
- self.cppclass = cppclass
+ def __init__(self, space, clsdecl):
+ Executor.__init__(self, space, clsdecl)
+ self.clsdecl = clsdecl
+
+ def _wrap_result(self, space, obj):
+ from pypy.module._cppyy import interp_cppyy
+ return interp_cppyy.wrap_cppinstance(space,
+ obj, self.clsdecl, do_cast=False, python_owns=True, fresh=True)
+
+ def execute(self, space, cppmethod, cppthis, num_args, args):
+ oresult = capi.c_call_o(space, cppmethod, cppthis, num_args, args,
self.clsdecl)
+ return self._wrap_result(space, rffi.cast(capi.C_OBJECT, oresult))
+
+
+class InstancePtrExecutor(InstanceExecutor):
+ # For return of a C++ instance by pointer: MyClass* func()
def cffi_type(self, space):
state = space.fromcache(ffitypes.State)
return state.c_voidp
+ def _wrap_result(self, space, obj):
+ from pypy.module._cppyy import interp_cppyy
+ return interp_cppyy.wrap_cppinstance(space, obj, self.clsdecl)
+
def execute(self, space, cppmethod, cppthis, num_args, args):
- from pypy.module._cppyy import interp_cppyy
- long_result = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
- ptr_result = rffi.cast(capi.C_OBJECT, long_result)
- pyres = interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass)
- return pyres
+ lresult = capi.c_call_l(space, cppmethod, cppthis, num_args, args)
+ return self._wrap_result(space, rffi.cast(capi.C_OBJECT, lresult))
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)
- from pypy.module._cppyy import interp_cppyy
- ptr_result = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP,
result)[0])
- return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass)
+ presult = rffi.ptradd(buffer, cif_descr.exchange_result)
+ obj = rffi.cast(capi.C_OBJECT, rffi.cast(rffi.VOIDPP, presult)[0])
+ return self._wrap_result(space, obj)
class InstancePtrPtrExecutor(InstancePtrExecutor):
+ # For return of a C++ instance by ptr-to-ptr or ptr-to-ref: MyClass*&
func()
def execute(self, space, cppmethod, cppthis, num_args, args):
- from pypy.module._cppyy import interp_cppyy
- voidp_result = capi.c_call_r(space, cppmethod, cppthis, num_args, args)
- ref_address = rffi.cast(rffi.VOIDPP, voidp_result)
- ptr_result = rffi.cast(capi.C_OBJECT, ref_address[0])
- return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass)
+ presult = capi.c_call_r(space, cppmethod, cppthis, num_args, args)
+ ref = rffi.cast(rffi.VOIDPP, presult)
+ return self._wrap_result(space, rffi.cast(capi.C_OBJECT, ref[0]))
def execute_libffi(self, space, cif_descr, funcaddr, buffer):
from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
raise FastCallNotPossible
-class InstanceExecutor(InstancePtrExecutor):
-
- def execute(self, space, cppmethod, cppthis, num_args, args):
- from pypy.module._cppyy import interp_cppyy
- long_result = capi.c_call_o(space, cppmethod, cppthis, num_args, args,
self.cppclass)
- ptr_result = rffi.cast(capi.C_OBJECT, long_result)
- return interp_cppyy.wrap_cppinstance(space, ptr_result, self.cppclass,
- do_cast=False, python_owns=True,
fresh=True)
-
- def execute_libffi(self, space, cif_descr, funcaddr, buffer):
- from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
- raise FastCallNotPossible
-
class StdStringExecutor(InstancePtrExecutor):
-
def execute(self, space, cppmethod, cppthis, num_args, args):
cstr, cstr_len = capi.c_call_s(space, cppmethod, cppthis, num_args,
args)
pystr = rffi.charpsize2str(cstr, cstr_len)
capi.c_free(space, rffi.cast(rffi.VOIDP, cstr))
- return space.newbytes(pystr)
+ return space.newbytes(pystr)
def execute_libffi(self, space, cif_descr, funcaddr, buffer):
from pypy.module._cppyy.interp_cppyy import FastCallNotPossible
raise FastCallNotPossible
class StdStringRefExecutor(InstancePtrExecutor):
-
- def __init__(self, space, cppclass):
+ def __init__(self, space, clsdecl):
from pypy.module._cppyy import interp_cppyy
- cppclass = interp_cppyy.scope_byname(space, capi.std_string_name)
- InstancePtrExecutor.__init__(self, space, cppclass)
+ clsdecl = interp_cppyy.scope_byname(space, capi.std_string_name)
+ InstancePtrExecutor.__init__(self, space, clsdecl)
class PyObjectExecutor(PtrTypeExecutor):
-
def wrap_result(self, space, lresult):
space.getbuiltinmodule("cpyext")
from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref,
decref
@@ -245,6 +240,41 @@
return self.wrap_result(space, rffi.cast(rffi.LONGP, result)[0])
+class SmartPointerExecutor(InstanceExecutor):
+ _immutable_fields_ = ['smartdecl', 'deref']
+
+ def __init__(self, space, smartdecl, raw, deref):
+ from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl,
get_pythonized_cppclass
+ w_raw = get_pythonized_cppclass(space, raw)
+ rawdecl = space.interp_w(W_CPPClassDecl, space.findattr(w_raw,
space.newtext("__cppdecl__")))
+ InstanceExecutor.__init__(self, space, rawdecl)
+ self.smartdecl = smartdecl
+ self.deref = deref
+
+ def _wrap_result(self, space, obj):
+ from pypy.module._cppyy import interp_cppyy
+ return interp_cppyy.wrap_cppinstance(space, obj, self.clsdecl,
+ self.smartdecl, self.deref, do_cast=False, python_owns=True,
fresh=True)
+
+class SmartPointerPtrExecutor(InstancePtrExecutor):
+ _immutable_fields_ = ['smartdecl', 'deref']
+
+ def __init__(self, space, smartdecl, raw, deref):
+ # TODO: share this with SmartPointerExecutor through in mixin
+ from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl,
get_pythonized_cppclass
+ w_raw = get_pythonized_cppclass(space, raw)
+ rawdecl = space.interp_w(W_CPPClassDecl, space.findattr(w_raw,
space.newtext("__cppdecl__")))
+ InstancePtrExecutor.__init__(self, space, rawdecl)
+ self.smartdecl = smartdecl
+ self.deref = deref
+
+ def _wrap_result(self, space, obj):
+ from pypy.module._cppyy import interp_cppyy
+ # TODO: this is a pointer to a smart pointer, take ownership on the
smart one?
+ return interp_cppyy.wrap_cppinstance(space, obj, self.clsdecl,
+ self.smartdecl, self.deref, do_cast=False)
+
+
_executors = {}
def get_executor(space, name):
# Matching of 'name' to an executor factory goes through up to four levels:
@@ -257,7 +287,7 @@
name = capi.c_resolve_name(space, name)
- # 1) full, qualified match
+ # full, qualified match
try:
return _executors[name](space, None)
except KeyError:
@@ -266,13 +296,13 @@
compound = helper.compound(name)
clean_name = capi.c_resolve_name(space, helper.clean_type(name))
- # 1a) clean lookup
+ # clean lookup
try:
return _executors[clean_name+compound](space, None)
except KeyError:
pass
- # 2) drop '&': by-ref is pretty much the same as by-value, python-wise
+ # drop '&': by-ref is pretty much the same as by-value, python-wise
if compound and compound[len(compound)-1] == '&':
# TODO: this does not actually work with Reflex (?)
try:
@@ -280,19 +310,29 @@
except KeyError:
pass
- # 3) types/classes, either by ref/ptr or by value
+ # types/classes, either by ref/ptr or by value
from pypy.module._cppyy import interp_cppyy
cppclass = interp_cppyy.scope_byname(space, clean_name)
if cppclass:
# type check for the benefit of the annotator
from pypy.module._cppyy.interp_cppyy import W_CPPClassDecl
- cppclass = space.interp_w(W_CPPClassDecl, cppclass, can_be_None=False)
+ clsdecl = space.interp_w(W_CPPClassDecl, cppclass, can_be_None=False)
+
+ # check smart pointer type
+ check_smart = capi.c_smartptr_info(space, clean_name)
+ if check_smart[0]:
+ if compound == '':
+ return SmartPointerExecutor(space, clsdecl, check_smart[1],
check_smart[2])
+ elif compound == '*' or compound == '&':
+ return SmartPointerPtrExecutor(space, clsdecl, check_smart[1],
check_smart[2])
+ # fall through: can still return smart pointer in non-smart way
+
if compound == '':
- return InstanceExecutor(space, cppclass)
+ return InstanceExecutor(space, clsdecl)
elif compound == '*' or compound == '&':
- return InstancePtrExecutor(space, cppclass)
+ return InstancePtrExecutor(space, clsdecl)
elif compound == '**' or compound == '*&':
- return InstancePtrPtrExecutor(space, cppclass)
+ return InstancePtrPtrExecutor(space, clsdecl)
elif "(anonymous)" in name:
# special case: enum w/o a type name
return _executors["internal_enum_type_t"](space, None)
@@ -302,7 +342,7 @@
return _executors['void*'](space, None) # allow at least passing of
the pointer
# currently used until proper lazy instantiation available in interp_cppyy
- return FunctionExecutor(space, None)
+ return Executor(space, None)
_executors["void"] = VoidExecutor
@@ -338,10 +378,10 @@
)
for c_type, stub, names in type_info:
- class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin,
FunctionExecutor):
+ class BasicExecutor(ffitypes.typeid(c_type), NumericExecutorMixin,
Executor):
_immutable_ = True
c_stubcall = staticmethod(stub)
- class BasicRefExecutor(ffitypes.typeid(c_type),
NumericRefExecutorMixin, FunctionExecutor):
+ class BasicRefExecutor(ffitypes.typeid(c_type),
NumericRefExecutorMixin, Executor):
def cffi_type(self, space):
state = space.fromcache(ffitypes.State)
return state.c_voidp
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
@@ -74,18 +74,52 @@
# allow int to pass to char and make sure that str is of length 1
if space.isinstance_w(w_value, space.w_int):
ival = space.c_int_w(w_value)
+ if ival < -128 or 127 < ival:
+ raise oefmt(space.w_ValueError, "char arg not in
range(-128,128)")
+
+ value = rffi.cast(rffi.CHAR, space.c_int_w(w_value))
+ else:
+ if space.isinstance_w(w_value, space.w_text):
+ value = space.text_w(w_value)
+ else:
+ value = space.bytes_w(w_value)
+ if len(value) != 1:
+ raise oefmt(space.w_ValueError,
+ "char expected, got string of size %d", len(value))
+
+ value = rffi.cast(rffi.CHAR, value[0])
+ return value # turn it into a "char" to the annotator
+
+ def cffi_type(self, space):
+ state = space.fromcache(State)
+ return state.c_char
+
+class UCharTypeMixin(object):
+ _mixin_ = True
+ _immutable_fields_ = ['c_type', 'c_ptrtype']
+
+ c_type = rffi.UCHAR
+ c_ptrtype = rffi.CCHARP # there's no such thing as rffi.UCHARP
+
+ def _wrap_object(self, space, obj):
+ return space.newbytes(obj)
+
+ def _unwrap_object(self, space, w_value):
+ # allow int to pass to char and make sure that str is of length 1
+ if space.isinstance_w(w_value, space.w_int):
+ ival = space.c_int_w(w_value)
if ival < 0 or 256 <= ival:
raise oefmt(space.w_ValueError, "char arg not in range(256)")
value = rffi.cast(rffi.CHAR, space.c_int_w(w_value))
- elif space.isinstance_w(w_value, space.w_text):
- value = space.text_w(w_value)
else:
- value = space.bytes_w(w_value)
-
- if len(value) != 1:
- raise oefmt(space.w_ValueError,
- "char expected, got string of size %d", len(value))
+ if space.isinstance_w(w_value, space.w_text):
+ value = space.text_w(w_value)
+ else:
+ value = space.bytes_w(w_value)
+ if len(value) != 1:
+ raise oefmt(space.w_ValueError,
+ "unsigned char expected, got string of size %d",
len(value))
value = rffi.cast(rffi.CHAR, value[0])
return value # turn it into a "char" to the annotator
@@ -280,6 +314,7 @@
"NOT_RPYTHON"
if c_type == bool: return BoolTypeMixin
if c_type == rffi.CHAR: return CharTypeMixin
+ if c_type == rffi.UCHAR: return UCharTypeMixin
if c_type == rffi.SHORT: return ShortTypeMixin
if c_type == rffi.USHORT: return UShortTypeMixin
if c_type == rffi.INT: return IntTypeMixin
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
@@ -1,3 +1,4 @@
+import sys
from rpython.rlib import rstring
@@ -116,6 +117,17 @@
# 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__"
+
# _operator_mappings["[]"] = "__setitem__" # depends on return type
# _operator_mappings["+"] = "__add__" # depends on # of args (see
__pos__)
# _operator_mappings["-"] = "__sub__" # id. (eq. __neg__)
@@ -123,7 +135,7 @@
# _operator_mappings["[]"] = "__getitem__" # depends on return type
_operator_mappings["()"] = "__call__"
-_operator_mappings["/"] = "__div__" # __truediv__ in p3
+_operator_mappings["/"] = CPPYY__div__
_operator_mappings["%"] = "__mod__"
_operator_mappings["**"] = "__pow__" # not C++
_operator_mappings["<<"] = "__lshift__"
@@ -136,7 +148,7 @@
_operator_mappings["+="] = "__iadd__"
_operator_mappings["-="] = "__isub__"
_operator_mappings["*="] = "__imul__"
-_operator_mappings["/="] = "__idiv__" # __itruediv__ in p3
+_operator_mappings["/="] = CPPYY__idiv__
_operator_mappings["%="] = "__imod__"
_operator_mappings["**="] = "__ipow__"
_operator_mappings["<<="] = "__ilshift__"
@@ -154,7 +166,7 @@
# the following type mappings are "exact"
_operator_mappings["const char*"] = "__str__"
_operator_mappings["int"] = "__int__"
-_operator_mappings["long"] = "__long__" # __int__ in p3
+_operator_mappings["long"] = CPPYY__long__
_operator_mappings["double"] = "__float__"
# the following type mappings are "okay"; the assumption is that they
@@ -163,13 +175,13 @@
_operator_mappings["char*"] = "__str__"
_operator_mappings["short"] = "__int__"
_operator_mappings["unsigned short"] = "__int__"
-_operator_mappings["unsigned int"] = "__long__" # __int__ in p3
-_operator_mappings["unsigned long"] = "__long__" # id.
-_operator_mappings["long long"] = "__long__" # id.
-_operator_mappings["unsigned long long"] = "__long__" # id.
+_operator_mappings["unsigned int"] = CPPYY__long__
+_operator_mappings["unsigned long"] = CPPYY__long__
+_operator_mappings["long long"] = CPPYY__long__
+_operator_mappings["unsigned long long"] = CPPYY__long__
_operator_mappings["float"] = "__float__"
-_operator_mappings["bool"] = "__nonzero__" # __bool__ in p3
+_operator_mappings["bool"] = CPPYY__bool__
# the following are not python, but useful to expose
_operator_mappings["->"] = "__follow__"
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
@@ -76,9 +76,7 @@
cppyy_object_t cppyy_call_o(cppyy_method_t method, cppyy_object_t self,
int nargs, void* args, cppyy_type_t result_type);
RPY_EXTERN
- cppyy_funcaddr_t cppyy_function_address_from_index(cppyy_scope_t scope,
cppyy_index_t idx);
- RPY_EXTERN
- cppyy_funcaddr_t cppyy_function_address_from_method(cppyy_method_t method);
+ cppyy_funcaddr_t cppyy_function_address(cppyy_method_t method);
/* handling of function argument buffer
----------------------------------- */
RPY_EXTERN
@@ -132,23 +130,28 @@
cppyy_index_t* cppyy_method_indices_from_name(cppyy_scope_t scope, const
char* name);
RPY_EXTERN
- char* cppyy_method_name(cppyy_scope_t scope, cppyy_index_t idx);
+ cppyy_method_t cppyy_get_method(cppyy_scope_t scope, cppyy_index_t idx);
+
RPY_EXTERN
- char* cppyy_method_mangled_name(cppyy_scope_t scope, cppyy_index_t idx);
+ char* cppyy_method_name(cppyy_method_t);
RPY_EXTERN
- char* cppyy_method_result_type(cppyy_scope_t scope, cppyy_index_t idx);
+ char* cppyy_method_full_name(cppyy_method_t);
RPY_EXTERN
- int cppyy_method_num_args(cppyy_scope_t scope, cppyy_index_t idx);
+ char* cppyy_method_mangled_name(cppyy_method_t);
RPY_EXTERN
- int cppyy_method_req_args(cppyy_scope_t scope, cppyy_index_t idx);
+ char* cppyy_method_result_type(cppyy_method_t);
RPY_EXTERN
- char* cppyy_method_arg_type(cppyy_scope_t scope, cppyy_index_t idx, int
arg_index);
+ int cppyy_method_num_args(cppyy_method_t);
RPY_EXTERN
- char* cppyy_method_arg_default(cppyy_scope_t scope, cppyy_index_t idx, int
arg_index);
+ int cppyy_method_req_args(cppyy_method_t);
RPY_EXTERN
- char* cppyy_method_signature(cppyy_scope_t scope, cppyy_index_t idx, int
show_formalargs);
+ char* cppyy_method_arg_type(cppyy_method_t, int arg_index);
RPY_EXTERN
- char* cppyy_method_prototype(cppyy_scope_t scope, cppyy_index_t idx, int
show_formalargs);
+ char* cppyy_method_arg_default(cppyy_method_t, int arg_index);
+ 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);
RPY_EXTERN
int cppyy_is_const_method(cppyy_method_t);
@@ -157,25 +160,21 @@
RPY_EXTERN
int cppyy_method_is_template(cppyy_scope_t scope, cppyy_index_t idx);
RPY_EXTERN
- int cppyy_method_num_template_args(cppyy_scope_t scope, cppyy_index_t idx);
- RPY_EXTERN
- char* cppyy_method_template_arg_name(cppyy_scope_t scope, cppyy_index_t
idx, cppyy_index_t iarg);
+ cppyy_method_t cppyy_get_method_template(cppyy_scope_t scope, const char*
name, const char* proto);
RPY_EXTERN
- cppyy_method_t cppyy_get_method(cppyy_scope_t scope, cppyy_index_t idx);
- RPY_EXTERN
cppyy_index_t cppyy_get_global_operator(
cppyy_scope_t scope, cppyy_scope_t lc, cppyy_scope_t rc, const char*
op);
/* method properties
------------------------------------------------------ */
RPY_EXTERN
- int cppyy_is_publicmethod(cppyy_type_t type, cppyy_index_t idx);
+ int cppyy_is_publicmethod(cppyy_method_t);
RPY_EXTERN
- int cppyy_is_constructor(cppyy_type_t type, cppyy_index_t idx);
+ int cppyy_is_constructor(cppyy_method_t);
RPY_EXTERN
- int cppyy_is_destructor(cppyy_type_t type, cppyy_index_t idx);
+ int cppyy_is_destructor(cppyy_method_t);
RPY_EXTERN
- int cppyy_is_staticmethod(cppyy_type_t type, cppyy_index_t idx);
+ int cppyy_is_staticmethod(cppyy_method_t);
/* data member reflection information
------------------------------------- */
RPY_EXTERN
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
@@ -14,13 +14,21 @@
from pypy.module._cffi_backend import ctypefunc
from pypy.module._cppyy import converter, executor, ffitypes, helper
+CLASS_FLAGS_IS_PINNED = 0x0001
INSTANCE_FLAGS_PYTHON_OWNS = 0x0001
INSTANCE_FLAGS_IS_REF = 0x0002
-INSTANCE_FLAGS_IS_R_VALUE = 0x0004
+INSTANCE_FLAGS_IS_RVALUE = 0x0004
OVERLOAD_FLAGS_USE_FFI = 0x0001
+FUNCTION_IS_GLOBAL = 0x0001
+FUNCTION_IS_STATIC = 0x0001
+FUNCTION_IS_METHOD = 0x0002
+FUNCTION_IS_CONSTRUCTOR = 0x0004
+FUNCTION_IS_TEMPLATE = 0x0008
+FUNCTION_IS_SETITEM = 0x0010
+
class FastCallNotPossible(Exception):
pass
@@ -100,9 +108,9 @@
state.cppscope_cache[final_scoped_name] = cppscope
if not isns:
- # build methods/data; TODO: also defer this for classes (a
functional __dir__
+ # build overloads/data; TODO: also defer this for classes (a
functional __dir__
# and instrospection for help() is enough and allows more lazy
loading)
- cppscope._build_methods()
+ cppscope._build_overloads()
cppscope._find_datamembers()
return cppscope
@@ -131,7 +139,7 @@
cppclass = space.interp_w(W_CPPClassDecl, w_cppclass)
# add back-end specific method pythonizations (doing this on the wrapped
# class allows simple aliasing of methods)
- capi.pythonize(space, cppclass.name, w_pycppclass)
+ capi.pythonize(space, w_pycppclass, cppclass.name)
state = space.fromcache(State)
state.cppclass_registry[rffi.cast(rffi.LONG, cppclass.handle)] =
w_pycppclass
@@ -150,17 +158,18 @@
#-----
-# Classes involved with methods and functions:
+# Classes involved with methods and functions come at two levels:
+# - overloads: user-facing collections of overloaded functions
+# - wrappers: internal holders of the individual C++ methods
#
-# CPPMethod: base class wrapping a single function or method
-# CPPConstructor: specialization for allocating a new object
-# CPPFunction: specialization for free and static functions
+# W_CPPOverload: instance methods (base class)
+# W_CPPConstructorOverload: constructors
+# W_CPPStaticOverload: free and static functions
+# W_CPPTemplateOverload: templated methods
+# W_CPPTemplateStaticOveload: templated free and static functions
+#
+# CPPMethod: a single function or method (base class)
# CPPSetItem: specialization for Python's __setitem__
-# CPPTemplatedCall: trampoline to instantiate and bind templated functions
-# W_CPPOverload, W_CPPConstructorOverload, W_CPPTemplateOverload:
-# user-facing, app-level, collection of overloads, with specializations
-# for constructors and templates
-# W_CPPBoundMethod: instantiated template method
#
# All methods/functions derive from CPPMethod and are collected as overload
# candidates in user-facing overload classes. Templated methods are a two-step
@@ -173,15 +182,15 @@
also takes care of offset casting and recycling of known objects through
the memory_regulator."""
- _attrs_ = ['space', 'scope', 'index', 'cppmethod', 'arg_defs',
'args_required',
+ _attrs_ = ['space', 'scope', 'cppmethod', 'arg_defs', 'args_required',
'converters', 'executor', '_funcaddr', 'cif_descr',
'uses_local']
- _immutable_ = True
+ _immutable_fields_ = ['scope', 'cppmethod', 'arg_defs', 'args_required',
+ 'converters', 'executor', 'uses_local']
- def __init__(self, space, declaring_scope, method_index, arg_defs,
args_required):
+ def __init__(self, space, declaring_scope, cppmethod, arg_defs,
args_required):
self.space = space
self.scope = declaring_scope
- self.index = method_index
- self.cppmethod = capi.c_get_method(self.space, self.scope,
method_index)
+ self.cppmethod = cppmethod
self.arg_defs = arg_defs
self.args_required = args_required
@@ -193,12 +202,6 @@
self._funcaddr = lltype.nullptr(capi.C_FUNC_PTR.TO)
self.uses_local = False
- @staticmethod
- def unpack_cppthis(space, w_cppinstance, declaring_scope):
- cppinstance = space.interp_w(W_CPPInstance, w_cppinstance)
- cppinstance._nullcheck()
- return cppinstance.get_cppthis(declaring_scope)
-
def _address_from_local_buffer(self, call_local, idx):
if not call_local:
return call_local
@@ -266,11 +269,12 @@
def do_fast_call(self, cppthis, args_w, call_local):
if self.cif_descr == lltype.nullptr(jit_libffi.CIF_DESCRIPTION):
raise FastCallNotPossible
+ jit.promote(self)
cif_descr = self.cif_descr
buffer = lltype.malloc(rffi.CCHARP.TO, cif_descr.exchange_size,
flavor='raw')
try:
# this pointer
- data = capi.exchange_address(buffer, cif_descr, 0)
+ data = rffi.ptradd(buffer, cif_descr.exchange_args[0])
x = rffi.cast(rffi.LONGP, data) # LONGP needed for
test_zjit.py
x[0] = rffi.cast(rffi.LONG, cppthis)
@@ -279,11 +283,11 @@
for i in range(len(args_w)):
conv = self.converters[i]
w_arg = args_w[i]
- data = capi.exchange_address(buffer, cif_descr, i+1)
+ data = rffi.ptradd(buffer, cif_descr.exchange_args[i+1])
conv.convert_argument_libffi(self.space, w_arg, data,
call_local)
for j in range(i+1, len(self.arg_defs)):
conv = self.converters[j]
- data = capi.exchange_address(buffer, cif_descr, j+1)
+ data = rffi.ptradd(buffer, cif_descr.exchange_args[j+1])
conv.default_argument_libffi(self.space, data)
assert self._funcaddr
@@ -295,7 +299,7 @@
return w_res
# from ctypefunc; have my own version for annotater purposes and to disable
- # memory tracking (method live time is longer than the tests)
+ # memory tracking (method life time is longer than the tests)
@jit.dont_look_inside
def _rawallocate(self, builder):
builder.space = self.space
@@ -308,7 +312,7 @@
# allocate the buffer
if we_are_translated():
rawmem = lltype.malloc(rffi.CCHARP.TO, builder.nb_bytes,
- flavor='raw', track_allocation=False)
+ flavor='raw')
rawmem = rffi.cast(jit_libffi.CIF_DESCRIPTION_P, rawmem)
else:
# gross overestimation of the length below, but too bad
@@ -340,7 +344,7 @@
self.converters = [converter.get_converter(self.space, arg_type,
arg_dflt)
for arg_type, arg_dflt in self.arg_defs]
self.executor = executor.get_executor(
- self.space, capi.c_method_result_type(self.space, self.scope,
self.index))
+ self.space, capi.c_method_result_type(self.space, self.cppmethod))
for conv in self.converters:
if conv.uses_local:
@@ -350,8 +354,8 @@
# Each CPPMethod corresponds one-to-one to a C++ equivalent and cppthis
# has been offset to the matching class. Hence, the libffi pointer is
# uniquely defined and needs to be setup only once.
- funcaddr = capi.c_function_address_from_index(self.space, self.scope,
self.index)
- if funcaddr and cppthis: # methods only for now
+ funcaddr = capi.c_function_address(self.space, self.cppmethod)
+ if funcaddr and cppthis: # TODO: methods only for now
state = self.space.fromcache(ffitypes.State)
# argument type specification (incl. cppthis)
@@ -418,10 +422,10 @@
capi.c_deallocate_function_args(self.space, args)
def signature(self, show_formalargs=True):
- return capi.c_method_signature(self.space, self.scope, self.index,
show_formalargs)
+ return capi.c_method_signature(self.space, self.cppmethod,
show_formalargs)
def prototype(self, show_formalargs=True):
- return capi.c_method_prototype(self.space, self.scope, self.index,
show_formalargs)
+ return capi.c_method_prototype(self.space, self.scope, self.cppmethod,
show_formalargs)
def priority(self):
total_arg_priority = 0
@@ -431,8 +435,11 @@
@rgc.must_be_light_finalizer
def __del__(self):
- if self.cif_descr:
- lltype.free(self.cif_descr, flavor='raw')
+ try:
+ if self.cif_descr:
+ lltype.free(self.cif_descr, flavor='raw')
+ except Exception: # TODO: happens for templates, why?
+ pass
def __repr__(self):
return "CPPMethod: %s" % self.prototype()
@@ -441,80 +448,12 @@
assert 0, "you should never have a pre-built instance of this!"
-class CPPFunction(CPPMethod):
- """Global (namespaced) / static function dispatcher."""
-
- _immutable_ = True
-
- @staticmethod
- def unpack_cppthis(space, w_cppinstance, declaring_scope):
- return capi.C_NULL_OBJECT
-
- def __repr__(self):
- return "CPPFunction: %s" % self.prototype()
-
-
-class CPPTemplatedCall(CPPMethod):
- """Method dispatcher that first resolves the template instance."""
-
- _attrs_ = ['space', 'templ_args']
- _immutable_ = True
-
- def __init__(self, space, templ_args, declaring_scope, method_index,
arg_defs, args_required):
- self.space = space
- self.templ_args = templ_args
- # TODO: might have to specialize for CPPTemplatedCall on
CPPMethod/CPPFunction here
- CPPMethod.__init__(self, space, declaring_scope, method_index,
arg_defs, args_required)
-
- def call(self, cppthis, args_w, useffi):
- assert lltype.typeOf(cppthis) == capi.C_OBJECT
- for i in range(len(args_w)):
- try:
- s = self.space.text_w(args_w[i])
- except OperationError:
- s = self.space.text_w(self.space.getattr(args_w[i],
self.space.newtext('__name__')))
- s = capi.c_resolve_name(self.space, s)
- if s != self.templ_args[i]:
- raise oefmt(self.space.w_TypeError,
- "non-matching template (got %s where %s expected)",
- s, self.templ_args[i])
- return W_CPPBoundMethod(cppthis, self, useffi)
-
- def bound_call(self, cppthis, args_w, useffi):
- return CPPMethod.call(self, cppthis, args_w, useffi)
-
- def __repr__(self):
- return "CPPTemplatedCall: %s" % self.prototype()
-
-
-class CPPConstructor(CPPMethod):
- """Method dispatcher that constructs new objects. This method can not have
- a fast path, as the allocation of the object is currently left to the
- reflection layer only, since the C++ class may have an overloaded operator
- new, disallowing malloc here."""
-
- _immutable_ = True
-
- @staticmethod
- def unpack_cppthis(space, w_cppinstance, declaring_scope):
- return rffi.cast(capi.C_OBJECT, declaring_scope.handle)
-
- def call(self, cppthis, args_w, useffi):
- # Note: this does not return a wrapped instance, just a pointer to the
- # new instance; the overload must still wrap it before returning. Also,
- # cppthis is declaring_scope.handle (as per unpack_cppthis(), above).
- return CPPMethod.call(self, cppthis, args_w, useffi)
-
- def __repr__(self):
- return "CPPConstructor: %s" % self.prototype()
-
-
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
the return type of the latter."""
- _immutable_ = True
+ _attrs_ = []
def call(self, cppthis, args_w, useffi):
end = len(args_w)-1
@@ -528,46 +467,44 @@
class W_CPPOverload(W_Root):
- """Dispatcher that is actually available at the app-level: it is a
- collection of (possibly) overloaded methods or functions. It calls these
- in order and deals with error handling and reporting."""
+ """App-level dispatcher: controls a collection of (potentially) overloaded
methods
+ or functions. Calls these in order and deals with error handling and
reporting."""
- _attrs_ = ['space', 'scope', 'functions', 'flags']
+ _attrs_ = ['space', 'scope', 'functions', 'flags', 'w_this']
_immutable_fields_ = ['scope', 'functions[*]']
- def __init__(self, space, declaring_scope, functions):
- self.space = space
- self.scope = declaring_scope
- assert len(functions)
+ def __init__(self, space, declaring_scope, functions, flags =
OVERLOAD_FLAGS_USE_FFI):
+ self.space = space
+ self.scope = declaring_scope
from rpython.rlib import debug
self.functions = debug.make_sure_not_resized(functions)
- self.flags = 0
- self.flags |= OVERLOAD_FLAGS_USE_FFI
+ self.flags = flags
+ self.w_this = self.space.w_None
- # allow user to determine ffi use rules per overload
- def fget_useffi(self, space):
- return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
+ @unwrap_spec(args_w='args_w')
+ def descr_get(self, w_cppinstance, args_w):
+ if self.space.is_w(w_cppinstance, self.space.w_None):
+ return self # unbound, so no new instance needed
+ cppol = W_CPPOverload(self.space, self.scope, self.functions,
self.flags)
+ cppol.w_this = w_cppinstance
+ return cppol # bound
- @unwrap_spec(value=bool)
- def fset_useffi(self, space, value):
- if space.is_true(value):
- self.flags |= OVERLOAD_FLAGS_USE_FFI
+ @unwrap_spec(args_w='args_w')
+ def call(self, args_w):
+ if self.space.is_w(self.w_this, self.space.w_None) and len(args_w):
+ w_this = args_w[0]
+ args_w = args_w[1:]
else:
- self.flags &= ~OVERLOAD_FLAGS_USE_FFI
-
- @jit.elidable_promote()
- def is_static(self):
- if isinstance(self.functions[0], CPPFunction):
- return self.space.w_True
- return self.space.w_False
+ w_this = self.w_this
+ cppinstance = self.space.interp_w(W_CPPInstance, w_this)
+ cppinstance._nullcheck()
+ if not capi.c_is_subtype(self.space, cppinstance.clsdecl, self.scope):
+ raise oefmt(self.space.w_TypeError,
+ "cannot pass %T instance as %s", w_this, self.scope.name)
+ return self.call_impl(cppinstance.get_cppthis(self.scope), args_w)
@jit.unroll_safe
- @unwrap_spec(args_w='args_w')
- def call(self, w_cppinstance, args_w):
- # instance handling is specific to the function type only, so take it
out
- # of the loop over function overloads
- cppthis = self.functions[0].unpack_cppthis(
- self.space, w_cppinstance, self.functions[0].scope)
+ def call_impl(self, cppthis, args_w):
assert lltype.typeOf(cppthis) == capi.C_OBJECT
# The following code tries out each of the functions in order. If
@@ -625,38 +562,96 @@
sig += '\n'+self.functions[i].prototype()
return self.space.newtext(sig)
+ # allow user to determine ffi use rules per overload
+ def fget_useffi(self, space):
+ return space.newbool(bool(self.flags & OVERLOAD_FLAGS_USE_FFI))
+
+ @unwrap_spec(value=bool)
+ def fset_useffi(self, space, value):
+ if space.is_true(value):
+ self.flags |= OVERLOAD_FLAGS_USE_FFI
+ else:
+ self.flags &= ~OVERLOAD_FLAGS_USE_FFI
+
+ def fget_doc(self, space):
+ return self.prototype()
+
def __repr__(self):
return "W_CPPOverload(%s)" % [f.prototype() for f in self.functions]
W_CPPOverload.typedef = TypeDef(
'CPPOverload',
- is_static = interp2app(W_CPPOverload.is_static),
- call = interp2app(W_CPPOverload.call),
+ __get__ = interp2app(W_CPPOverload.descr_get),
+ __call__ = interp2app(W_CPPOverload.call),
__useffi__ = GetSetProperty(W_CPPOverload.fget_useffi,
W_CPPOverload.fset_useffi),
- prototype = interp2app(W_CPPOverload.prototype),
+ __doc__ = GetSetProperty(W_CPPOverload.fget_doc)
)
+# overload collection of static (class and free) functions; these differ
+# from methods only in the handling of 'cppthis'
+class W_CPPStaticOverload(W_CPPOverload):
+ _attrs_ = []
+
+ @unwrap_spec(args_w='args_w')
+ def descr_get(self, w_cppinstance, args_w):
+ if isinstance(w_cppinstance, W_CPPInstance):
+ # two possibilities: this is a static function called on an
+ # instance and w_this must not be set, or a free function rebound
+ # onto a class and w_this should be set
+ cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance)
+ if cppinstance.clsdecl.handle != self.scope.handle:
+ cppol = W_CPPStaticOverload(self.space, self.scope,
self.functions, self.flags)
+ cppol.w_this = w_cppinstance
+ return cppol # bound
+ return self # unbound
+
+ @unwrap_spec(args_w='args_w')
+ def call(self, args_w):
+ if not self.space.is_w(self.w_this, self.space.w_None):
+ # free function used as bound method, put self back into args_w
+ cppinstance = self.space.interp_w(W_CPPInstance, self.w_this)
+ cppinstance._nullcheck()
+ args_w = [self.w_this] + args_w
+ return self.call_impl(capi.C_NULL_OBJECT, args_w)
+
+ 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),
+ __useffi__ = GetSetProperty(W_CPPStaticOverload.fget_useffi,
W_CPPStaticOverload.fset_useffi),
+ __doc__ = GetSetProperty(W_CPPStaticOverload.fget_doc)
+)
+
+
class W_CPPConstructorOverload(W_CPPOverload):
- @jit.elidable_promote()
- def is_static(self):
- return self.space.w_False
+ _attrs_ = []
- @jit.elidable_promote()
- def unpack_cppthis(self, w_cppinstance):
- return rffi.cast(capi.C_OBJECT, self.scope.handle)
+ @unwrap_spec(args_w='args_w')
+ def descr_get(self, w_cppinstance, args_w):
+ if self.space.is_w(w_cppinstance, self.space.w_None):
+ return self # unbound (TODO: probably useless)
+ cppol = W_CPPConstructorOverload(self.space, self.scope,
self.functions, self.flags)
+ cppol.w_this = w_cppinstance
+ return cppol # bound
- @jit.unroll_safe
@unwrap_spec(args_w='args_w')
- def call(self, w_cppinstance, args_w):
+ def call(self, args_w):
# 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)
- w_result = W_CPPOverload.call(self, w_cppinstance, args_w)
+ if self.space.is_w(self.w_this, self.space.w_None) and len(args_w):
+ cppinstance = self.space.interp_w(W_CPPInstance, args_w[0])
+ args_w = args_w[1:]
+ else:
+ cppinstance = self.space.interp_w(W_CPPInstance, self.w_this)
+ w_result = self.call_impl(rffi.cast(capi.C_OBJECT, self.scope.handle),
args_w)
newthis = rffi.cast(capi.C_OBJECT, self.space.uint_w(w_result))
- cppinstance = self.space.interp_w(W_CPPInstance, w_cppinstance,
can_be_None=True)
if cppinstance is not None:
cppinstance._rawobject = newthis
memory_regulator.register(cppinstance)
@@ -666,43 +661,224 @@
W_CPPConstructorOverload.typedef = TypeDef(
'CPPConstructorOverload',
- is_static = interp2app(W_CPPConstructorOverload.is_static),
- call = interp2app(W_CPPConstructorOverload.call),
- prototype = interp2app(W_CPPConstructorOverload.prototype),
+ __get__ = interp2app(W_CPPConstructorOverload.descr_get),
+ __call__ = interp2app(W_CPPConstructorOverload.call),
+ __doc__ = GetSetProperty(W_CPPConstructorOverload.fget_doc)
)
-class W_CPPTemplateOverload(W_CPPOverload):
+class TemplateOverloadMixin(object):
+ """Mixin to instantiate templated methods/functions."""
+
+ _mixin_ = True
+
+ def construct_template_args(self, w_tpArgs, args_w = None):
+ space = self.space
+ tmpl_args = ''
+ for i in range(space.len_w(w_tpArgs)):
+ w_tp = space.getitem(w_tpArgs, space.newint(i))
+ if space.isinstance_w(w_tp, space.w_text):
+ s = space.text_w(w_tp) # string describing type
+ elif space.isinstance_w(w_tp, space.w_type):
+ try:
+ # cppyy bound types
+ s = space.text_w(space.getattr(w_tp,
space.newtext('__cppname__')))
+ 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:
+ sugar = "&&"
+ elif cppinstance.flags & INSTANCE_FLAGS_IS_REF:
+ sugar = "*"
+ else:
+ sugar = "&"
+ s += sugar
+ except OperationError:
+ # generic python types
+ s = space.text_w(space.getattr(w_tp,
space.newtext('__name__')))
+ else:
+ # builtin types etc.
+ s = space.text_w(space.str(w_tp))
+ # map python types -> C++ types
+ if s == 'str': s = 'std::string'
+ if i != 0: tmpl_args += ', '
+ tmpl_args += s
+ return tmpl_args
+
+ def find_method_template(self, name, proto = ''):
+ # find/instantiate new callable function
+ space = self.space
+ cppmeth = capi.c_get_method_template(space, self.scope, name, proto)
+ if not cppmeth:
+ raise oefmt(self.space.w_AttributeError,
+ "scope '%s' has no function %s", self.scope.name, name)
+
+ funcs = []
+ ftype = self.scope._make_cppfunction(name, cppmeth, funcs)
+ if ftype & FUNCTION_IS_STATIC:
+ cppol = W_CPPStaticOverload(space, self.scope, funcs[:],
self.flags)
+ else:
+ cppol = W_CPPOverload(space, self.scope, funcs[:], self.flags)
+ 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:
+ return cppol.descr_get(self.w_this, []).call(args_w)
+ except Exception:
+ pass # completely ignore for now; have to see whether
errors become confusing
+
+ # 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('>'):
+ try:
+ existing = self.master.overloads[fullname]
+ allf = existing.functions + method.functions
+ if isinstance(existing, W_CPPStaticOverload):
+ cppol = W_CPPStaticOverload(self.space, self.scope, allf,
self.flags)
+ else:
+ cppol = W_CPPOverload(self.space, self.scope, allf,
self.flags)
+ self.master.overloads[fullname] = cppol
+ except KeyError:
+ self.master.overloads[fullname] = method
+
+ return method.descr_get(self.w_this, []).call(args_w)
+
+ def getitem_impl(self, name, args_w):
+ space = self.space
+
+ if space.isinstance_w(args_w[0], space.w_tuple):
+ w_args = args_w[0]
+ else:
+ w_args = space.newtuple(args_w)
+
+ tmpl_args = self.construct_template_args(w_args)
+ fullname = name+'<'+tmpl_args+'>'
+ try:
+ method = self.master.overloads[fullname]
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit