[pypy-commit] pypy py3.5: hg merge default
rlamy
pypy.commits at gmail.com
Fri Jan 5 15:17:55 EST 2018
Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: py3.5
Changeset: r93628:799e8d00a8ec
Date: 2018-01-05 20:17 +0000
http://bitbucket.org/pypy/pypy/changeset/799e8d00a8ec/
Log: hg merge default
diff too long, truncating to 2000 out of 4420 lines
diff --git a/.hgtags b/.hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -46,3 +46,7 @@
84a2f3e6a7f88f2fe698e473998755b3bd1a12e2 release-pypy2.7-v5.9.0
0e7ea4fe15e82d5124e805e2e4a37cae1a402d4b release-pypy2.7-v5.10.0
a91df6163fb76df245091f741dbf6a23ddc72374 release-pypy3.5-v5.10.0
+a91df6163fb76df245091f741dbf6a23ddc72374 release-pypy3.5-v5.10.0
+0000000000000000000000000000000000000000 release-pypy3.5-v5.10.0
+0000000000000000000000000000000000000000 release-pypy3.5-v5.10.0
+09f9160b643e3f02ccb8c843b2fbb4e5cbf54082 release-pypy3.5-v5.10.0
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -339,8 +339,10 @@
Stanisław Halik
Julien Phalip
Roman Podoliaka
+ Steve Papanik
Eli Stevens
Boglarka Vezer
+ gabrielg
PavloKapyshin
Tomer Chachamu
Christopher Groskopf
@@ -363,11 +365,13 @@
Konrad Delong
Dinu Gherman
pizi
+ Tomáš Pružina
James Robert
Armin Ronacher
Diana Popa
Mads Kiilerich
Brett Cannon
+ Caleb Hattingh
aliceinwire
Zooko Wilcox-O Hearn
James Lan
@@ -388,6 +392,7 @@
Jason Madden
Yaroslav Fedevych
Even Wiik Thomassen
+ m at funkyhat.org
Stefan Marr
Heinrich-Heine University, Germany
diff --git a/lib_pypy/_ctypes/array.py b/lib_pypy/_ctypes/array.py
--- a/lib_pypy/_ctypes/array.py
+++ b/lib_pypy/_ctypes/array.py
@@ -12,7 +12,8 @@
if cls == (_CData,): # this is the Array class defined below
res._ffiarray = None
return res
- if not hasattr(res, '_length_') or not isinstance(res._length_, int):
+ if not hasattr(res, '_length_') or not isinstance(res._length_,
+ (int, long)):
raise AttributeError(
"class must define a '_length_' attribute, "
"which must be a positive integer")
diff --git a/pypy/doc/contributor.rst b/pypy/doc/contributor.rst
--- a/pypy/doc/contributor.rst
+++ b/pypy/doc/contributor.rst
@@ -217,6 +217,7 @@
Alejandro J. Cura
Vladimir Kryachko
Gabriel
+ Thomas Hisch
Mark Williams
Kunal Grover
Nathan Taylor
@@ -306,8 +307,10 @@
Stanisław Halik
Julien Phalip
Roman Podoliaka
+ Steve Papanik
Eli Stevens
Boglarka Vezer
+ gabrielg
PavloKapyshin
Tomer Chachamu
Christopher Groskopf
@@ -330,11 +333,13 @@
Konrad Delong
Dinu Gherman
pizi
+ Tomáš Pružina
James Robert
Armin Ronacher
Diana Popa
Mads Kiilerich
Brett Cannon
+ Caleb Hattingh
aliceinwire
Zooko Wilcox-O Hearn
James Lan
@@ -355,4 +360,5 @@
Jason Madden
Yaroslav Fedevych
Even Wiik Thomassen
+ m at funkyhat.org
Stefan Marr
diff --git a/pypy/doc/cpython_differences.rst b/pypy/doc/cpython_differences.rst
--- a/pypy/doc/cpython_differences.rst
+++ b/pypy/doc/cpython_differences.rst
@@ -558,6 +558,15 @@
``del foo.bar`` where ``foo`` is a module (or class) that contains the
function ``bar``, is significantly slower than CPython.
+* Various built-in functions in CPython accept only positional arguments
+ and not keyword arguments. That can be considered a long-running
+ historical detail: newer functions tend to accept keyword arguments
+ and older function are occasionally fixed to do so as well. In PyPy,
+ most built-in functions accept keyword arguments (``help()`` shows the
+ argument names). But don't rely on it too much because future
+ versions of PyPy may have to rename the arguments if CPython starts
+ accepting them too.
+
.. _`is ignored in PyPy`: http://bugs.python.org/issue14621
.. _`little point`: http://events.ccc.de/congress/2012/Fahrplan/events/5152.en.html
.. _`#2072`: https://bitbucket.org/pypy/pypy/issue/2072/
diff --git a/pypy/doc/tool/makecontributor.py b/pypy/doc/tool/makecontributor.py
--- a/pypy/doc/tool/makecontributor.py
+++ b/pypy/doc/tool/makecontributor.py
@@ -81,6 +81,7 @@
'Yasir Suhail':['yasirs'],
'Squeaky': ['squeaky'],
"Amaury Forgeot d'Arc": ['amauryfa at gmail.com'],
+ "Dodan Mihai": ['mihai.dodan at gmail.com'],
}
alias_map = {}
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
@@ -5,3 +5,8 @@
.. this is a revision shortly after release-pypy2.7-v5.10.0
.. startrev: 6b024edd9d12
+.. branch: cpyext-avoid-roundtrip
+
+Big refactoring of some cpyext code, which avoids a lot of nonsense when
+calling C from Python and vice-versa: the result is a big speedup in
+function/method calls, up to 6 times faster.
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
@@ -682,8 +682,8 @@
if hasattr(space, "fake"):
raise NotImplementedError
space.getbuiltinmodule("cpyext")
- from pypy.module.cpyext.pyobject import Py_DecRef, PyObject
- Py_DecRef(space, rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0]))
+ from pypy.module.cpyext.pyobject import decref, PyObject
+ decref(space, rffi.cast(PyObject, rffi.cast(rffi.VOIDPP, arg)[0]))
class MacroConverter(TypeConverter):
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
@@ -224,11 +224,11 @@
def wrap_result(self, space, lresult):
space.getbuiltinmodule("cpyext")
- from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref, Py_DecRef
+ from pypy.module.cpyext.pyobject import PyObject, from_ref, make_ref, decref
result = rffi.cast(PyObject, lresult)
w_obj = from_ref(space, result)
if result:
- Py_DecRef(space, result)
+ decref(space, result)
return w_obj
def execute(self, space, cppmethod, cppthis, num_args, args):
diff --git a/pypy/module/cpyext/__init__.py b/pypy/module/cpyext/__init__.py
--- a/pypy/module/cpyext/__init__.py
+++ b/pypy/module/cpyext/__init__.py
@@ -16,11 +16,12 @@
space.fromcache(State).startup(space)
method = pypy.module.cpyext.typeobject.get_new_method_def(space)
# the w_self argument here is a dummy, the only thing done with w_obj
- # is call space.type on it
- w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space, method, space.w_None)
- space.appexec([space.type(w_obj)], """(methodtype):
+ # is call type() on it
+ w_obj = pypy.module.cpyext.methodobject.W_PyCFunctionObject(space,
+ method, space.w_None)
+ space.appexec([w_obj], """(meth):
from pickle import Pickler
- Pickler.dispatch[methodtype] = Pickler.save_global
+ Pickler.dispatch[type(meth)] = Pickler.save_global
""")
def register_atexit(self, function):
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -206,6 +206,9 @@
# id. Invariant: this variable always contain 0 when the PyPy GIL is
# released. It should also contain 0 when regular RPython code
# executes. In non-cpyext-related code, it will thus always be 0.
+# When cpyext-related C code runs, it contains the thread id (usually)
+# or the value -1 (only for state.C.PyXxx() functions which are short-
+# running and should not themselves release the GIL).
#
# **make_generic_cpy_call():** RPython to C, with the GIL held. Before
# the call, must assert that the global variable is 0 and set the
@@ -257,14 +260,73 @@
cpyext_namespace = NameManager('cpyext_')
-class ApiFunction(object):
- def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL,
- c_name=None, cdecl=None, gil=None,
- result_borrowed=False, result_is_ll=False):
+class BaseApiFunction(object):
+ def __init__(self, argtypes, restype, callable):
self.argtypes = argtypes
self.restype = restype
self.functype = lltype.Ptr(lltype.FuncType(argtypes, restype))
self.callable = callable
+ self.cdecl = None # default
+ #
+ def get_llhelper(space):
+ return llhelper(self.functype, self.get_wrapper(space))
+ self.get_llhelper = get_llhelper
+
+ def get_api_decl(self, name, c_writer):
+ restype = self.get_c_restype(c_writer)
+ args = self.get_c_args(c_writer)
+ res = self.API_VISIBILITY % (restype,)
+ return "{res} {name}({args});".format(**locals())
+
+ def get_c_restype(self, c_writer):
+ if self.cdecl:
+ return self.cdecl.tp.result.get_c_name()
+ return c_writer.gettype(self.restype).replace('@', '').strip()
+
+ def get_c_args(self, c_writer):
+ if self.cdecl:
+ args = [tp.get_c_name('arg%d' % i) for i, tp in
+ enumerate(self.cdecl.tp.args)]
+ return ', '.join(args) or "void"
+ args = []
+ for i, argtype in enumerate(self.argtypes):
+ if argtype is CONST_STRING:
+ arg = 'const char *@'
+ elif argtype is CONST_STRINGP:
+ arg = 'const char **@'
+ elif argtype is CONST_WSTRING:
+ arg = 'const wchar_t *@'
+ else:
+ arg = c_writer.gettype(argtype)
+ arg = arg.replace('@', 'arg%d' % (i,)).strip()
+ args.append(arg)
+ args = ', '.join(args) or "void"
+ return args
+
+ def get_ptr_decl(self, name, c_writer):
+ restype = self.get_c_restype(c_writer)
+ args = self.get_c_args(c_writer)
+ return "{restype} (*{name})({args});".format(**locals())
+
+ def get_ctypes_impl(self, name, c_writer):
+ restype = self.get_c_restype(c_writer)
+ args = self.get_c_args(c_writer)
+ callargs = ', '.join('arg%d' % (i,)
+ for i in range(len(self.argtypes)))
+ if self.restype is lltype.Void:
+ body = "{ _pypyAPI.%s(%s); }" % (name, callargs)
+ else:
+ body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
+ return '%s %s(%s)\n%s' % (restype, name, args, body)
+
+
+class ApiFunction(BaseApiFunction):
+ API_VISIBILITY = "PyAPI_FUNC(%s)"
+
+ def __init__(self, argtypes, restype, callable, error=CANNOT_FAIL,
+ c_name=None, cdecl=None, gil=None,
+ result_borrowed=False, result_is_ll=False):
+ BaseApiFunction.__init__(self, argtypes, restype, callable)
self.error_value = error
self.c_name = c_name
self.cdecl = cdecl
@@ -282,10 +344,6 @@
self.gil = gil
self.result_borrowed = result_borrowed
self.result_is_ll = result_is_ll
- #
- def get_llhelper(space):
- return llhelper(self.functype, self.get_wrapper(space))
- self.get_llhelper = get_llhelper
def __repr__(self):
return "<cpyext function %s>" % (self.callable.__name__,)
@@ -383,57 +441,17 @@
arg = input_arg
newargs += (arg, )
try:
- return self.callable(space, *newargs)
+ result = self.callable(space, *newargs)
finally:
keepalive_until_here(*keepalives)
+ #
+ # this is just a sanity check to ensure that we don't forget to
+ # specify result_is_ll=True
+ if self.restype == PyObject:
+ assert self.result_is_ll == is_pyobj(result)
+ return result
return unwrapper
- def get_c_restype(self, c_writer):
- if self.cdecl:
- return self.cdecl.tp.result.get_c_name()
- return c_writer.gettype(self.restype).replace('@', '').strip()
-
- def get_c_args(self, c_writer):
- if self.cdecl:
- args = [tp.get_c_name('arg%d' % i) for i, tp in
- enumerate(self.cdecl.tp.args)]
- return ', '.join(args) or "void"
- args = []
- for i, argtype in enumerate(self.argtypes):
- if argtype is CONST_STRING:
- arg = 'const char *@'
- elif argtype is CONST_STRINGP:
- arg = 'const char **@'
- elif argtype is CONST_WSTRING:
- arg = 'const wchar_t *@'
- else:
- arg = c_writer.gettype(argtype)
- arg = arg.replace('@', 'arg%d' % (i,)).strip()
- args.append(arg)
- args = ', '.join(args) or "void"
- return args
-
- def get_api_decl(self, name, c_writer):
- restype = self.get_c_restype(c_writer)
- args = self.get_c_args(c_writer)
- return "PyAPI_FUNC({restype}) {name}({args});".format(**locals())
-
- def get_ptr_decl(self, name, c_writer):
- restype = self.get_c_restype(c_writer)
- args = self.get_c_args(c_writer)
- return "{restype} (*{name})({args});".format(**locals())
-
- def get_ctypes_impl(self, name, c_writer):
- restype = self.get_c_restype(c_writer)
- args = self.get_c_args(c_writer)
- callargs = ', '.join('arg%d' % (i,)
- for i in range(len(self.argtypes)))
- if self.restype is lltype.Void:
- body = "{ _pypyAPI.%s(%s); }" % (name, callargs)
- else:
- body = "{ return _pypyAPI.%s(%s); }" % (name, callargs)
- return '%s %s(%s)\n%s' % (restype, name, args, body)
-
DEFAULT_HEADER = 'pypy_decl.h'
def cpython_api(argtypes, restype, error=_NOT_SPECIFIED, header=DEFAULT_HEADER,
@@ -471,6 +489,26 @@
return unwrapper
return decorate
+class COnlyApiFunction(BaseApiFunction):
+ API_VISIBILITY = "extern %s"
+
+ def get_wrapper(self, space):
+ return self.callable
+
+ def __call__(self, *args):
+ raise TypeError("the function %s should not be directly "
+ "called from RPython, but only from C" % (self.func,))
+
+def c_only(argtypes, restype):
+ def decorate(func):
+ header = DEFAULT_HEADER
+ if func.__name__ in FUNCTIONS_BY_HEADER[header]:
+ raise ValueError("%s already registered" % func.__name__)
+ api_function = COnlyApiFunction(argtypes, restype, func)
+ FUNCTIONS_BY_HEADER[header][func.__name__] = api_function
+ return api_function
+ return decorate
+
def api_func_from_cdef(func, cdef, cts,
error=_NOT_SPECIFIED, header=DEFAULT_HEADER,
result_is_ll=False):
@@ -613,6 +651,11 @@
'PyBytes_FromFormat', 'PyBytes_FromFormatV',
'PyType_FromSpec',
+ 'Py_IncRef', 'Py_DecRef', 'PyObject_Free', 'PyObject_GC_Del', 'PyType_GenericAlloc',
+ '_PyObject_New', '_PyObject_NewVar',
+ '_PyObject_GC_New', '_PyObject_GC_NewVar',
+ 'PyObject_Init', 'PyObject_InitVar',
+ 'PyTuple_New',
]
TYPES = {}
FORWARD_DECLS = []
@@ -950,8 +993,14 @@
# see "Handling of the GIL" above (careful, we don't have the GIL here)
tid = rthread.get_or_make_ident()
- _gil_auto = (gil_auto_workaround and cpyext_glob_tid_ptr[0] != tid)
- if gil_acquire or _gil_auto:
+ _gil_auto = False
+ if gil_auto_workaround and cpyext_glob_tid_ptr[0] != tid:
+ # replace '-1' with the real tid, now that we have the tid
+ if cpyext_glob_tid_ptr[0] == -1:
+ cpyext_glob_tid_ptr[0] = tid
+ else:
+ _gil_auto = True
+ if _gil_auto or gil_acquire:
if cpyext_glob_tid_ptr[0] == tid:
deadlock_error(pname)
rgil.acquire()
@@ -1073,6 +1122,7 @@
setdefenc = rffi.llexternal('_%s_setfilesystemdefaultencoding' % prefix,
[rffi.CCHARP], lltype.Void,
compilation_info=eci, _nowrapper=True)
+ @init_function
def init_types(space):
from pypy.module.cpyext.typeobject import py_type_ready
from pypy.module.sys.interp_encoding import getfilesystemencoding
@@ -1080,7 +1130,7 @@
py_type_ready(space, get_capsule_type())
s = space.text_w(getfilesystemencoding(space))
setdefenc(rffi.str2charp(s, track_allocation=False)) # "leaks"
- INIT_FUNCTIONS.append(init_types)
+
from pypy.module.posix.interp_posix import add_fork_hook
global py_fatalerror
py_fatalerror = rffi.llexternal('%s_FatalError' % prefix,
@@ -1092,6 +1142,45 @@
_reinit_tls()
add_fork_hook('child', reinit_tls)
+
+def attach_c_functions(space, eci, prefix):
+ state = space.fromcache(State)
+ state.C._Py_Dealloc = rffi.llexternal('_Py_Dealloc',
+ [PyObject], lltype.Void,
+ compilation_info=eci,
+ _nowrapper=True)
+ state.C.PyObject_Free = rffi.llexternal(
+ mangle_name(prefix, 'PyObject_Free'),
+ [rffi.VOIDP], lltype.Void,
+ compilation_info=eci,
+ _nowrapper=True)
+ state.C.PyType_GenericAlloc = rffi.llexternal(
+ mangle_name(prefix, 'PyType_GenericAlloc'),
+ [PyTypeObjectPtr, Py_ssize_t], PyObject,
+ compilation_info=eci,
+ _nowrapper=True)
+ state.C._PyPy_int_dealloc = rffi.llexternal(
+ '_PyPy_int_dealloc', [PyObject], lltype.Void,
+ compilation_info=eci, _nowrapper=True)
+ state.C.PyTuple_New = rffi.llexternal(
+ mangle_name(prefix, 'PyTuple_New'),
+ [Py_ssize_t], PyObject,
+ compilation_info=eci,
+ _nowrapper=True)
+ state.C._PyPy_tuple_dealloc = rffi.llexternal(
+ '_PyPy_tuple_dealloc', [PyObject], lltype.Void,
+ compilation_info=eci, _nowrapper=True)
+ _, state.C.set_marker = rffi.CExternVariable(
+ Py_ssize_t, '_pypy_rawrefcount_w_marker_deallocating',
+ eci, _nowrapper=True, c_type='Py_ssize_t')
+ state.C._PyPy_subtype_dealloc = rffi.llexternal(
+ '_PyPy_subtype_dealloc', [PyObject], lltype.Void,
+ compilation_info=eci, _nowrapper=True)
+ state.C._PyPy_object_dealloc = rffi.llexternal(
+ '_PyPy_object_dealloc', [PyObject], lltype.Void,
+ compilation_info=eci, _nowrapper=True)
+
+
def init_function(func):
INIT_FUNCTIONS.append(func)
return func
@@ -1160,6 +1249,7 @@
space.fromcache(State).install_dll(eci)
modulename = py.path.local(eci.libraries[-1])
+ attach_c_functions(space, eci, prefix)
run_bootstrap_functions(space)
# load the bridge, and init structure
@@ -1200,7 +1290,6 @@
in_dll = ll2ctypes.get_ctypes_type(PyObject.TO).in_dll(bridge, mname)
py_obj = ll2ctypes.ctypes2lltype(PyObject, ctypes.pointer(in_dll))
builder.prepare(py_obj, w_obj)
- builder.attach_all(space)
pypyAPI = ctypes.POINTER(ctypes.c_void_p).in_dll(bridge, 'pypyAPI')
@@ -1211,6 +1300,12 @@
ll2ctypes.lltype2ctypes(func.get_llhelper(space)),
ctypes.c_void_p)
+ # we need to call this *after* the init code above, because it might
+ # indirectly call some functions which are attached to pypyAPI (e.g., we
+ # if do tuple_attach of the prebuilt empty tuple, we need to call
+ # _PyPy_Malloc)
+ builder.attach_all(space)
+
setup_init_functions(eci, prefix)
return modulename.new(ext='')
@@ -1407,6 +1502,7 @@
source_dir / "pylifecycle.c",
source_dir / "object.c",
source_dir / "typeobject.c",
+ source_dir / "tupleobject.c",
]
def build_eci(code, use_micronumpy=False, translating=False):
@@ -1504,6 +1600,7 @@
eci = build_eci(code, use_micronumpy, translating=True)
space.fromcache(State).install_dll(eci)
+ attach_c_functions(space, eci, prefix)
run_bootstrap_functions(space)
# emit uninitialized static data
@@ -1703,7 +1800,7 @@
@specialize.memo()
def make_generic_cpy_call(FT, expect_null, convert_result):
- from pypy.module.cpyext.pyobject import is_pyobj, as_pyobj
+ from pypy.module.cpyext.pyobject import is_pyobj, make_ref, decref
from pypy.module.cpyext.pyobject import get_w_obj_and_decref
from pypy.module.cpyext.pyerrors import PyErr_Occurred
unrolling_arg_types = unrolling_iterable(enumerate(FT.ARGS))
@@ -1731,15 +1828,17 @@
@specialize.ll()
def generic_cpy_call(space, func, *args):
boxed_args = ()
- keepalives = ()
+ to_decref = ()
assert len(args) == len(FT.ARGS)
for i, ARG in unrolling_arg_types:
arg = args[i]
+ _pyobj = None
if is_PyObject(ARG):
if not is_pyobj(arg):
- keepalives += (arg,)
- arg = as_pyobj(space, arg)
+ arg = make_ref(space, arg)
+ _pyobj = arg
boxed_args += (arg,)
+ to_decref += (_pyobj,)
# see "Handling of the GIL" above
tid = rthread.get_ident()
@@ -1753,7 +1852,11 @@
finally:
assert cpyext_glob_tid_ptr[0] == tid
cpyext_glob_tid_ptr[0] = 0
- keepalive_until_here(*keepalives)
+ for i, ARG in unrolling_arg_types:
+ # note that this loop is nicely unrolled statically by RPython
+ _pyobj = to_decref[i]
+ if _pyobj is not None:
+ decref(space, _pyobj)
if convert_result and is_PyObject(RESULT_TYPE):
if not is_pyobj(result):
diff --git a/pypy/module/cpyext/bytearrayobject.py b/pypy/module/cpyext/bytearrayobject.py
--- a/pypy/module/cpyext/bytearrayobject.py
+++ b/pypy/module/cpyext/bytearrayobject.py
@@ -3,12 +3,12 @@
from pypy.interpreter.error import OperationError, oefmt
from pypy.objspace.std.bytearrayobject import new_bytearray
from pypy.module.cpyext.api import (
- cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
- PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL)
+ cpython_api, cpython_struct, build_type_checkers,
+ PyVarObjectFields, Py_ssize_t, CONST_STRING)
from pypy.module.cpyext.pyerrors import PyErr_BadArgument
from pypy.module.cpyext.pyobject import (
- PyObject, PyObjectP, Py_DecRef, make_ref, from_ref,
- make_typedescr, get_typedescr, Py_IncRef)
+ PyObject, PyObjectP, make_ref, from_ref,
+ make_typedescr, get_typedescr)
# Type PyByteArrayObject represents a mutable array of bytes.
# The Python API is that of a sequence;
# the bytes are mapped to ints in [0, 256).
diff --git a/pypy/module/cpyext/bytesobject.py b/pypy/module/cpyext/bytesobject.py
--- a/pypy/module/cpyext/bytesobject.py
+++ b/pypy/module/cpyext/bytesobject.py
@@ -5,8 +5,8 @@
PyVarObjectFields, Py_ssize_t, CONST_STRING, CANNOT_FAIL, slot_function)
from pypy.module.cpyext.pyerrors import PyErr_BadArgument
from pypy.module.cpyext.pyobject import (
- PyObject, PyObjectP, Py_DecRef, make_ref, from_ref, track_reference,
- make_typedescr, get_typedescr, as_pyobj, Py_IncRef, get_w_obj_and_decref,
+ PyObject, PyObjectP, decref, make_ref, from_ref, track_reference,
+ make_typedescr, get_typedescr, as_pyobj, get_w_obj_and_decref,
pyobj_has_w_obj)
from pypy.objspace.std.bytesobject import W_BytesObject
@@ -203,7 +203,7 @@
try:
py_newstr = new_empty_str(space, newsize)
except MemoryError:
- Py_DecRef(space, ref[0])
+ decref(space, ref[0])
ref[0] = lltype.nullptr(PyObject.TO)
raise
to_cp = newsize
@@ -212,7 +212,7 @@
to_cp = oldsize
for i in range(to_cp):
py_newstr.c_ob_sval[i] = py_str.c_ob_sval[i]
- Py_DecRef(space, ref[0])
+ decref(space, ref[0])
ref[0] = rffi.cast(PyObject, py_newstr)
return 0
@@ -246,7 +246,7 @@
try:
PyBytes_Concat(space, ref, newpart)
finally:
- Py_DecRef(space, newpart)
+ decref(space, newpart)
@cpython_api([PyObject, PyObject], PyObject)
def _PyBytes_Join(space, w_sep, w_seq):
diff --git a/pypy/module/cpyext/classobject.py b/pypy/module/cpyext/classobject.py
--- a/pypy/module/cpyext/classobject.py
+++ b/pypy/module/cpyext/classobject.py
@@ -1,5 +1,5 @@
from rpython.rtyper.lltypesystem import rffi, lltype
-from pypy.module.cpyext.api import cpython_api, CANNOT_FAIL
+from pypy.module.cpyext.api import CANNOT_FAIL, cpython_api
from pypy.module.cpyext.pyobject import PyObject
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.function import Method
diff --git a/pypy/module/cpyext/dictobject.py b/pypy/module/cpyext/dictobject.py
--- a/pypy/module/cpyext/dictobject.py
+++ b/pypy/module/cpyext/dictobject.py
@@ -9,7 +9,7 @@
bootstrap_function, slot_function)
from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, as_pyobj,
make_typedescr, track_reference, create_ref, from_ref, decref,
- Py_IncRef)
+ incref)
from pypy.module.cpyext.object import _dealloc
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
@@ -279,7 +279,7 @@
w_keys = space.newlist(space.listview(w_keyview))
w_keys.switch_to_object_strategy()
py_dict.c__tmpkeys = create_ref(space, w_keys)
- Py_IncRef(space, py_dict.c__tmpkeys)
+ incref(space, py_dict.c__tmpkeys)
else:
if not py_dict.c__tmpkeys:
# pos should have been 0, cannot fail so return 0
diff --git a/pypy/module/cpyext/exception.py b/pypy/module/cpyext/exception.py
--- a/pypy/module/cpyext/exception.py
+++ b/pypy/module/cpyext/exception.py
@@ -1,7 +1,7 @@
# Provide implementation of PyException_ functions.
from pypy.module.cpyext.api import cpython_api
-from pypy.module.cpyext.pyobject import PyObject, from_ref, Py_DecRef
+from pypy.module.cpyext.pyobject import PyObject, from_ref, decref
from rpython.rtyper.lltypesystem import rffi, lltype
@@ -48,7 +48,7 @@
This steals a reference to ctx."""
if ctx:
w_ctx = from_ref(space, ctx)
- Py_DecRef(space, ctx)
+ decref(space, ctx)
else:
w_ctx = space.w_None
space.setattr(w_exc, space.newtext('__context__'), w_ctx)
@@ -72,7 +72,7 @@
This steals a reference to cause."""
if cause:
w_cause = from_ref(space, cause)
- Py_DecRef(space, cause)
+ decref(space, cause)
else:
w_cause = space.w_None
space.setattr(w_exc, space.newtext('__cause__'), w_cause)
diff --git a/pypy/module/cpyext/frameobject.py b/pypy/module/cpyext/frameobject.py
--- a/pypy/module/cpyext/frameobject.py
+++ b/pypy/module/cpyext/frameobject.py
@@ -3,7 +3,7 @@
cpython_api, bootstrap_function, PyObjectFields, cpython_struct,
CANNOT_FAIL, slot_function)
from pypy.module.cpyext.pyobject import (
- PyObject, Py_DecRef, make_ref, from_ref, track_reference,
+ PyObject, decref, make_ref, from_ref, track_reference,
make_typedescr, get_typedescr)
from pypy.module.cpyext.state import State
from pypy.module.cpyext.pystate import PyThreadState
@@ -43,9 +43,9 @@
def frame_dealloc(space, py_obj):
py_frame = rffi.cast(PyFrameObject, py_obj)
py_code = rffi.cast(PyObject, py_frame.c_f_code)
- Py_DecRef(space, py_code)
- Py_DecRef(space, py_frame.c_f_globals)
- Py_DecRef(space, py_frame.c_f_locals)
+ decref(space, py_code)
+ decref(space, py_frame.c_f_globals)
+ decref(space, py_frame.c_f_locals)
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/funcobject.py b/pypy/module/cpyext/funcobject.py
--- a/pypy/module/cpyext/funcobject.py
+++ b/pypy/module/cpyext/funcobject.py
@@ -4,7 +4,7 @@
cpython_api, bootstrap_function, cpython_struct, build_type_checkers,
slot_function)
from pypy.module.cpyext.pyobject import (
- PyObject, make_ref, from_ref, Py_DecRef, make_typedescr)
+ PyObject, make_ref, from_ref, decref, make_typedescr)
from rpython.rlib.unroll import unrolling_iterable
from pypy.interpreter.error import OperationError
from pypy.interpreter.function import Function, Method
@@ -62,7 +62,7 @@
@slot_function([PyObject], lltype.Void)
def function_dealloc(space, py_obj):
py_func = rffi.cast(PyFunctionObject, py_obj)
- Py_DecRef(space, py_func.c_func_name)
+ decref(space, py_func.c_func_name)
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
@@ -81,8 +81,8 @@
@slot_function([PyObject], lltype.Void)
def code_dealloc(space, py_obj):
py_code = rffi.cast(PyCodeObject, py_obj)
- Py_DecRef(space, py_code.c_co_name)
- Py_DecRef(space, py_code.c_co_filename)
+ decref(space, py_code.c_co_name)
+ decref(space, py_code.c_co_filename)
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/include/object.h b/pypy/module/cpyext/include/object.h
--- a/pypy/module/cpyext/include/object.h
+++ b/pypy/module/cpyext/include/object.h
@@ -59,6 +59,11 @@
} while (0)
#endif
+PyAPI_FUNC(void) Py_IncRef(PyObject *);
+PyAPI_FUNC(void) Py_DecRef(PyObject *);
+extern Py_ssize_t _pypy_rawrefcount_w_marker_deallocating;
+PyAPI_FUNC(void) _Py_Dealloc(PyObject *);
+
#define Py_CLEAR(op) \
do { \
@@ -87,6 +92,10 @@
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
#define Py_SIZE(ob) (((PyVarObject*)(ob))->ob_size)
+#define _Py_NewReference(op) \
+ ( ((PyObject *)(op))->ob_refcnt = 1, \
+ ((PyObject *)(op))->ob_pypy_link = 0 )
+
#define _Py_ForgetReference(ob) /* nothing */
#define Py_None (&_Py_NoneStruct)
@@ -246,8 +255,16 @@
) & ~(SIZEOF_VOID_P - 1) \
)
-#define PyObject_INIT PyObject_Init
-#define PyObject_INIT_VAR PyObject_InitVar
+
+#define PyObject_INIT(op, typeobj) \
+ ( Py_TYPE(op) = (typeobj), ((PyObject *)(op))->ob_refcnt = 1,\
+ ((PyObject *)(op))->ob_pypy_link = 0, (op) )
+#define PyObject_INIT_VAR(op, typeobj, size) \
+ ( Py_SIZE(op) = (size), PyObject_INIT((op), (typeobj)) )
+
+
+PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, Py_ssize_t);
+
/*
#define PyObject_NEW(type, typeobj) \
( (type *) PyObject_Init( \
@@ -334,12 +351,26 @@
*/
+/* on CPython, these are in objimpl.h */
+
+PyAPI_FUNC(void) PyObject_Free(void *);
+PyAPI_FUNC(void) PyObject_GC_Del(void *);
+
#define PyObject_MALLOC PyObject_Malloc
#define PyObject_REALLOC PyObject_Realloc
#define PyObject_FREE PyObject_Free
#define PyObject_Del PyObject_Free
#define PyObject_DEL PyObject_Free
+PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *);
+PyAPI_FUNC(PyVarObject *) _PyObject_NewVar(PyTypeObject *, Py_ssize_t);
+PyAPI_FUNC(PyObject *) _PyObject_GC_New(PyTypeObject *);
+PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, Py_ssize_t);
+
+PyAPI_FUNC(PyObject *) PyObject_Init(PyObject *, PyTypeObject *);
+PyAPI_FUNC(PyVarObject *) PyObject_InitVar(PyVarObject *,
+ PyTypeObject *, Py_ssize_t);
+
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *);
#endif
@@ -349,6 +380,8 @@
PyAPI_FUNC(int) PyPyType_Register(PyTypeObject *);
#define PyObject_Length PyObject_Size
#define _PyObject_GC_Del PyObject_GC_Del
+PyAPI_FUNC(void) _PyPy_subtype_dealloc(PyObject *);
+PyAPI_FUNC(void) _PyPy_object_dealloc(PyObject *);
#ifdef __cplusplus
diff --git a/pypy/module/cpyext/include/tupleobject.h b/pypy/module/cpyext/include/tupleobject.h
--- a/pypy/module/cpyext/include/tupleobject.h
+++ b/pypy/module/cpyext/include/tupleobject.h
@@ -16,9 +16,13 @@
*/
} PyTupleObject;
+PyAPI_FUNC(PyObject *) PyTuple_New(Py_ssize_t size);
+PyAPI_FUNC(void) _PyPy_tuple_dealloc(PyObject *);
+
/* defined in varargswrapper.c */
PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...);
+
/* Macro, trading safety for speed */
#define PyTuple_GET_ITEM(op, i) (((PyTupleObject *)(op))->ob_item[i])
#define PyTuple_GET_SIZE(op) Py_SIZE(op)
diff --git a/pypy/module/cpyext/mapping.py b/pypy/module/cpyext/mapping.py
--- a/pypy/module/cpyext/mapping.py
+++ b/pypy/module/cpyext/mapping.py
@@ -1,7 +1,8 @@
from rpython.rtyper.lltypesystem import lltype, rffi
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t)
-from pypy.module.cpyext.pyobject import PyObject
+from pypy.module.cpyext.pyobject import (
+ PyObject, hack_for_result_often_existing_obj)
@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
@@ -40,12 +41,13 @@
return space.call_function(space.w_list,
space.call_method(w_obj, "items"))
- at cpython_api([PyObject, CONST_STRING], PyObject)
+ at cpython_api([PyObject, CONST_STRING], PyObject, result_is_ll=True)
def PyMapping_GetItemString(space, w_obj, key):
"""Return element of o corresponding to the object key or NULL on failure.
This is the equivalent of the Python expression o[key]."""
w_key = space.newtext(rffi.charp2str(key))
- return space.getitem(w_obj, w_key)
+ w_res = space.getitem(w_obj, w_key)
+ return hack_for_result_often_existing_obj(space, w_res)
@cpython_api([PyObject, CONST_STRING, PyObject], rffi.INT_real, error=-1)
def PyMapping_SetItemString(space, w_obj, key, w_value):
diff --git a/pypy/module/cpyext/methodobject.py b/pypy/module/cpyext/methodobject.py
--- a/pypy/module/cpyext/methodobject.py
+++ b/pypy/module/cpyext/methodobject.py
@@ -14,7 +14,9 @@
cpython_api, generic_cpy_call, CANNOT_FAIL, slot_function, cts,
build_type_checkers)
from pypy.module.cpyext.pyobject import (
- Py_DecRef, from_ref, make_ref, as_pyobj, make_typedescr)
+ decref, from_ref, make_ref, as_pyobj, make_typedescr)
+from pypy.module.cpyext.state import State
+from pypy.module.cpyext.tupleobject import tuple_from_args_w
PyMethodDef = cts.gettype('PyMethodDef')
PyCFunction = cts.gettype('PyCFunction')
@@ -38,8 +40,8 @@
@slot_function([PyObject], lltype.Void)
def cfunction_dealloc(space, py_obj):
py_func = rffi.cast(PyCFunctionObject, py_obj)
- Py_DecRef(space, py_func.c_m_self)
- Py_DecRef(space, py_func.c_m_module)
+ decref(space, py_func.c_m_self)
+ decref(space, py_func.c_m_module)
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
@@ -77,31 +79,29 @@
return None
class W_PyCFunctionObject(W_Root):
- # TODO create a slightly different class depending on the c_ml_flags
+ _immutable_fields_ = ["flags"]
+
def __init__(self, space, ml, w_self, w_module=None):
self.ml = ml
self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, self.ml.c_ml_name))
+ self.flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
self.w_self = w_self
self.w_module = w_module
- def call(self, space, w_self, w_args, w_kw):
- # Call the C function
- if w_self is None:
- w_self = self.w_self
- flags = rffi.cast(lltype.Signed, self.ml.c_ml_flags)
- flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
- if not flags & METH_KEYWORDS and space.is_true(w_kw):
+ def descr_call(self, space, __args__):
+ return self.call(space, self.w_self, __args__)
+
+ def call(self, space, w_self, __args__):
+ flags = self.flags & ~(METH_CLASS | METH_STATIC | METH_COEXIST)
+ length = len(__args__.arguments_w)
+ if not flags & METH_KEYWORDS and __args__.keywords:
raise oefmt(space.w_TypeError,
"%s() takes no keyword arguments", self.name)
-
- func = self.ml.c_ml_meth
- length = space.int_w(space.len(w_args))
if flags & METH_KEYWORDS:
- func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
- return generic_cpy_call(space, func, w_self, w_args, w_kw)
+ return self.call_keywords(space, w_self, __args__)
elif flags & METH_NOARGS:
if length == 0:
- return generic_cpy_call(space, func, w_self, None)
+ return self.call_noargs(space, w_self, __args__)
raise oefmt(space.w_TypeError,
"%s() takes no arguments", self.name)
elif flags & METH_O:
@@ -109,13 +109,47 @@
raise oefmt(space.w_TypeError,
"%s() takes exactly one argument (%d given)",
self.name, length)
- w_arg = space.getitem(w_args, space.newint(0))
- return generic_cpy_call(space, func, w_self, w_arg)
+ return self.call_o(space, w_self, __args__)
elif flags & METH_VARARGS:
- return generic_cpy_call(space, func, w_self, w_args)
+ return self.call_varargs(space, w_self, __args__)
else: # shouldn't happen!
raise oefmt(space.w_RuntimeError, "unknown calling convention")
+ def call_noargs(self, space, w_self, __args__):
+ func = self.ml.c_ml_meth
+ return generic_cpy_call(space, func, w_self, None)
+
+ def call_o(self, space, w_self, __args__):
+ func = self.ml.c_ml_meth
+ w_o = __args__.arguments_w[0]
+ return generic_cpy_call(space, func, w_self, w_o)
+
+ def call_varargs(self, space, w_self, __args__):
+ state = space.fromcache(State)
+ func = self.ml.c_ml_meth
+ py_args = tuple_from_args_w(space, __args__.arguments_w)
+ try:
+ return generic_cpy_call(space, func, w_self, py_args)
+ finally:
+ decref(space, py_args)
+
+ def call_keywords(self, space, w_self, __args__):
+ func = rffi.cast(PyCFunctionKwArgs, self.ml.c_ml_meth)
+ py_args = tuple_from_args_w(space, __args__.arguments_w)
+ w_kwargs = None
+ if __args__.keywords:
+ # CCC: we should probably have a @jit.look_inside_iff if the
+ # keyword count is constant, as we do in Arguments.unpack
+ w_kwargs = space.newdict()
+ for i in range(len(__args__.keywords)):
+ key = __args__.keywords[i]
+ w_obj = __args__.keywords_w[i]
+ space.setitem(w_kwargs, space.newtext(key), w_obj)
+ try:
+ return generic_cpy_call(space, func, w_self, py_args, w_kwargs)
+ finally:
+ decref(space, py_args)
+
def get_doc(self, space):
c_doc = self.ml.c_ml_doc
if c_doc:
@@ -134,27 +168,11 @@
return space.newtext(txtsig)
return space.w_None
-class W_PyCFunctionObjectNoArgs(W_PyCFunctionObject):
- def call(self, space, w_self, w_args, w_kw):
- # Call the C function
- if w_self is None:
- w_self = self.w_self
- func = self.ml.c_ml_meth
- return generic_cpy_call(space, func, w_self, None)
+class W_PyCMethodObject(W_PyCFunctionObject):
-class W_PyCFunctionObjectSingleObject(W_PyCFunctionObject):
- def call(self, space, w_self, w_o, w_kw):
- if w_self is None:
- w_self = self.w_self
- func = self.ml.c_ml_meth
- return generic_cpy_call(space, func, w_self, w_o)
-
-class W_PyCMethodObject(W_PyCFunctionObject):
- w_self = None
def __init__(self, space, ml, w_type):
+ W_PyCFunctionObject.__init__(self, space, ml, w_self=None)
self.space = space
- self.ml = ml
- self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, ml.c_ml_name))
self.w_objclass = w_type
def __repr__(self):
@@ -167,14 +185,13 @@
self.name, w_objclass.name))
def descr_call(self, space, __args__):
- args_w, kw_w = __args__.unpack()
- if len(args_w) < 1:
+ if len(__args__.arguments_w) == 0:
w_objclass = self.w_objclass
assert isinstance(w_objclass, W_TypeObject)
raise oefmt(space.w_TypeError,
"descriptor '%8' of '%s' object needs an argument",
self.name, w_objclass.name)
- w_instance = args_w[0]
+ w_instance = __args__.arguments_w[0]
# XXX: needs a stricter test
if not space.isinstance_w(w_instance, self.w_objclass):
w_objclass = self.w_objclass
@@ -182,12 +199,10 @@
raise oefmt(space.w_TypeError,
"descriptor '%8' requires a '%s' object but received a '%T'",
self.name, w_objclass.name, w_instance)
- w_args = space.newtuple(args_w[1:])
- w_kw = space.newdict()
- for key, w_obj in kw_w.items():
- space.setitem(w_kw, space.newtext(key), w_obj)
- ret = self.call(space, w_instance, w_args, w_kw)
- return ret
+ #
+ # CCC: we can surely do better than this
+ __args__ = __args__.replace_arguments(__args__.arguments_w[1:])
+ return self.call(space, w_instance, __args__)
# PyPy addition, for Cython
_, _ = build_type_checkers("MethodDescr", W_PyCMethodObject)
@@ -203,16 +218,25 @@
return isinstance(w_obj, BuiltinFunction)
class W_PyCClassMethodObject(W_PyCFunctionObject):
- w_self = None
+
def __init__(self, space, ml, w_type):
+ W_PyCFunctionObject.__init__(self, space, ml, w_self=None)
self.space = space
- self.ml = ml
- self.name = rffi.charp2str(rffi.cast(rffi.CCHARP, ml.c_ml_name))
self.w_objclass = w_type
def __repr__(self):
return self.space.unwrap(self.descr_method_repr())
+ def descr_call(self, space, __args__):
+ if len(__args__.arguments_w) == 0:
+ raise oefmt(space.w_TypeError,
+ "descriptor '%s' of '%s' object needs an argument",
+ self.name, self.w_objclass.getname(space))
+ w_instance = __args__.arguments_w[0] # XXX typecheck missing
+ # CCC: we can surely do better than this
+ __args__ = __args__.replace_arguments(__args__.arguments_w[1:])
+ return self.call(space, w_instance, __args__)
+
def descr_method_repr(self):
return self.getrepr(
self.space, u"built-in method '%s' of '%s' object" %
@@ -278,45 +302,6 @@
space.setitem(w_kw, space.newtext(key), w_obj)
return self.call(space, w_self, w_args, w_kw)
-def cfunction_descr_call_noargs(space, w_self):
- # special case for calling with flags METH_NOARGS
- self = space.interp_w(W_PyCFunctionObjectNoArgs, w_self)
- return self.call(space, None, None, None)
-
-def cfunction_descr_call_single_object(space, w_self, w_o):
- # special case for calling with flags METH_O
- self = space.interp_w(W_PyCFunctionObjectSingleObject, w_self)
- return self.call(space, None, w_o, None)
-
- at jit.dont_look_inside
-def cfunction_descr_call(space, w_self, __args__):
- # specialize depending on the W_PyCFunctionObject
- self = space.interp_w(W_PyCFunctionObject, w_self)
- args_w, kw_w = __args__.unpack()
- # XXX __args__.unpack is slow
- w_args = space.newtuple(args_w)
- w_kw = space.newdict()
- for key, w_obj in kw_w.items():
- space.setitem(w_kw, space.newtext(key), w_obj)
- ret = self.call(space, None, w_args, w_kw)
- return ret
-
- at jit.dont_look_inside
-def cclassmethod_descr_call(space, w_self, __args__):
- self = space.interp_w(W_PyCFunctionObject, w_self)
- args_w, kw_w = __args__.unpack()
- if len(args_w) < 1:
- raise oefmt(space.w_TypeError,
- "descriptor '%8' of '%N' object needs an argument",
- self.name, self.w_objclass)
- w_instance = args_w[0] # XXX typecheck missing
- w_args = space.newtuple(args_w[1:])
- w_kw = space.newdict()
- for key, w_obj in kw_w.items():
- space.setitem(w_kw, space.newtext(key), w_obj)
- ret = self.call(space, w_instance, w_args, w_kw)
- return ret
-
def cmethod_descr_get(space, w_function, w_obj, w_cls=None):
if w_obj is None or space.is_w(w_obj, space.w_None):
return w_function
@@ -331,7 +316,7 @@
W_PyCFunctionObject.typedef = TypeDef(
'builtin_function_or_method',
- __call__ = interp2app(cfunction_descr_call),
+ __call__ = interp2app(W_PyCFunctionObject.descr_call),
__doc__ = GetSetProperty(W_PyCFunctionObject.get_doc),
__text_signature__ = GetSetProperty(W_PyCFunctionObject.get_txtsig),
__module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObject),
@@ -340,28 +325,6 @@
)
W_PyCFunctionObject.typedef.acceptable_as_base_class = False
-W_PyCFunctionObjectNoArgs.typedef = TypeDef(
- 'builtin_function_or_method', W_PyCFunctionObject.typedef,
- __call__ = interp2app(cfunction_descr_call_noargs),
- __doc__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_doc),
- __text_signature__ = GetSetProperty(W_PyCFunctionObjectNoArgs.get_txtsig),
- __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectNoArgs),
- __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectNoArgs,
- wrapfn="newtext_or_none"),
- )
-W_PyCFunctionObjectNoArgs.typedef.acceptable_as_base_class = False
-
-W_PyCFunctionObjectSingleObject.typedef = TypeDef(
- 'builtin_function_or_method', W_PyCFunctionObject.typedef,
- __call__ = interp2app(cfunction_descr_call_single_object),
- __doc__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_doc),
- __text_signature__ = GetSetProperty(W_PyCFunctionObjectSingleObject.get_txtsig),
- __module__ = interp_attrproperty_w('w_module', cls=W_PyCFunctionObjectSingleObject),
- __name__ = interp_attrproperty('name', cls=W_PyCFunctionObjectSingleObject,
- wrapfn="newtext_or_none"),
- )
-W_PyCFunctionObjectSingleObject.typedef.acceptable_as_base_class = False
-
W_PyCMethodObject.typedef = TypeDef(
'method_descriptor',
__get__ = interp2app(cmethod_descr_get),
@@ -376,7 +339,7 @@
W_PyCClassMethodObject.typedef = TypeDef(
'classmethod',
__get__ = interp2app(cclassmethod_descr_get),
- __call__ = interp2app(cclassmethod_descr_call),
+ __call__ = interp2app(W_PyCClassMethodObject.descr_call),
__name__ = interp_attrproperty('name', cls=W_PyCClassMethodObject,
wrapfn="newtext_or_none"),
__objclass__ = interp_attrproperty_w('w_objclass',
diff --git a/pypy/module/cpyext/modsupport.py b/pypy/module/cpyext/modsupport.py
--- a/pypy/module/cpyext/modsupport.py
+++ b/pypy/module/cpyext/modsupport.py
@@ -8,8 +8,7 @@
from pypy.interpreter.module import Module
from pypy.module.cpyext.methodobject import (
W_PyCFunctionObject, PyCFunction_NewEx, PyDescr_NewMethod,
- PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New,
- W_PyCFunctionObjectNoArgs, W_PyCFunctionObjectSingleObject)
+ PyMethodDef, PyDescr_NewClassMethod, PyStaticMethod_New)
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
from pypy.module.cpyext.state import State
from pypy.interpreter.error import oefmt
@@ -167,14 +166,6 @@
"exception", w_mod.w_name)
cur_slot = rffi.ptradd(cur_slot, 1)
-def _create_pyc_function_object(space, method, w_self, w_name, flags):
- flags &= ~(METH_CLASS | METH_STATIC | METH_COEXIST)
- if flags == METH_NOARGS:
- return W_PyCFunctionObjectNoArgs(space, method, w_self, w_name)
- if flags == METH_O:
- return W_PyCFunctionObjectSingleObject(space, method, w_self, w_name)
- return W_PyCFunctionObject(space, method, w_self, w_name)
-
def convert_method_defs(space, dict_w, methods, w_type, w_self=None, name=None):
w_name = space.newtext_or_none(name)
methods = rffi.cast(rffi.CArrayPtr(PyMethodDef), methods)
@@ -193,8 +184,7 @@
raise oefmt(space.w_ValueError,
"module functions cannot set METH_CLASS or "
"METH_STATIC")
- w_obj = _create_pyc_function_object(space, method, w_self,
- w_name, flags)
+ w_obj = W_PyCFunctionObject(space, method, w_self, w_name)
else:
if methodname in dict_w and not (flags & METH_COEXIST):
continue
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -3,9 +3,10 @@
cpython_api, generic_cpy_call, CANNOT_FAIL, Py_ssize_t,
PyVarObject, size_t, slot_function, cts,
Py_TPFLAGS_HEAPTYPE, Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT,
- Py_GE, CONST_STRING, FILEP, fwrite)
+ Py_GE, CONST_STRING, FILEP, fwrite, c_only)
from pypy.module.cpyext.pyobject import (
- PyObject, PyObjectP, from_ref, Py_IncRef, Py_DecRef, get_typedescr)
+ PyObject, PyObjectP, from_ref, incref, decref,
+ get_typedescr, hack_for_result_often_existing_obj)
from pypy.module.cpyext.typeobject import PyTypeObjectPtr
from pypy.module.cpyext.pyerrors import PyErr_NoMemory, PyErr_BadInternalCall
from pypy.objspace.std.typeobject import W_TypeObject
@@ -32,31 +33,20 @@
# XXX FIXME
return realloc(ptr, size)
- at cpython_api([rffi.VOIDP], lltype.Void)
-def PyObject_Free(space, ptr):
+ at c_only([rffi.VOIDP], lltype.Void)
+def _PyPy_Free(ptr):
lltype.free(ptr, flavor='raw')
- at cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True)
-def _PyObject_New(space, type):
- return _PyObject_NewVar(space, type, 0)
+ at c_only([Py_ssize_t], rffi.VOIDP)
+def _PyPy_Malloc(size):
+ # XXX: the malloc inside BaseCpyTypedescr.allocate and
+ # typeobject.type_alloc specify zero=True, so this is why we use it also
+ # here. However, CPython does a simple non-initialized malloc, so we
+ # should investigate whether we can remove zero=True as well
+ return lltype.malloc(rffi.VOIDP.TO, size,
+ flavor='raw', zero=True,
+ add_memory_pressure=True)
- at cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True)
-def _PyObject_NewVar(space, type, itemcount):
- w_type = from_ref(space, rffi.cast(PyObject, type))
- assert isinstance(w_type, W_TypeObject)
- typedescr = get_typedescr(w_type.layout.typedef)
- py_obj = typedescr.allocate(space, w_type, itemcount=itemcount)
- #py_obj.c_ob_refcnt = 0 --- will be set to 1 again by PyObject_Init{Var}
- if type.c_tp_itemsize == 0:
- w_obj = PyObject_Init(space, py_obj, type)
- else:
- py_objvar = rffi.cast(PyVarObject, py_obj)
- w_obj = PyObject_InitVar(space, py_objvar, type, itemcount)
- return py_obj
-
- at slot_function([PyObject], lltype.Void)
-def PyObject_dealloc(space, obj):
- return _dealloc(space, obj)
def _dealloc(space, obj):
# This frees an object after its refcount dropped to zero, so we
@@ -66,19 +56,7 @@
obj_voidp = rffi.cast(rffi.VOIDP, obj)
generic_cpy_call(space, pto.c_tp_free, obj_voidp)
if pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE:
- Py_DecRef(space, rffi.cast(PyObject, pto))
-
- at cpython_api([PyTypeObjectPtr], PyObject, result_is_ll=True)
-def _PyObject_GC_New(space, type):
- return _PyObject_New(space, type)
-
- at cpython_api([PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True)
-def _PyObject_GC_NewVar(space, type, itemcount):
- return _PyObject_NewVar(space, type, itemcount)
-
- at cpython_api([rffi.VOIDP], lltype.Void)
-def PyObject_GC_Del(space, obj):
- PyObject_Free(space, obj)
+ decref(space, rffi.cast(PyObject, pto))
@cpython_api([PyObject], PyObjectP, error=CANNOT_FAIL)
def _PyObject_GetDictPtr(space, op):
@@ -92,20 +70,22 @@
def PyObject_Not(space, w_obj):
return not space.is_true(w_obj)
- at cpython_api([PyObject, PyObject], PyObject)
+ at cpython_api([PyObject, PyObject], PyObject, result_is_ll=True)
def PyObject_GetAttr(space, w_obj, w_name):
"""Retrieve an attribute named attr_name from object o. Returns the attribute
value on success, or NULL on failure. This is the equivalent of the Python
expression o.attr_name."""
- return space.getattr(w_obj, w_name)
+ w_res = space.getattr(w_obj, w_name)
+ return hack_for_result_often_existing_obj(space, w_res)
- at cpython_api([PyObject, CONST_STRING], PyObject)
+ at cpython_api([PyObject, CONST_STRING], PyObject, result_is_ll=True)
def PyObject_GetAttrString(space, w_obj, name_ptr):
"""Retrieve an attribute named attr_name from object o. Returns the attribute
value on success, or NULL on failure. This is the equivalent of the Python
expression o.attr_name."""
name = rffi.charp2str(name_ptr)
- return space.getattr(w_obj, space.newtext(name))
+ w_res = space.getattr(w_obj, space.newtext(name))
+ return hack_for_result_often_existing_obj(space, w_res)
@cpython_api([PyObject, PyObject], rffi.INT_real, error=CANNOT_FAIL)
def PyObject_HasAttr(space, w_obj, w_name):
@@ -164,11 +144,12 @@
and 0 otherwise. This function always succeeds."""
return int(space.is_true(space.callable(w_obj)))
- at cpython_api([PyObject, PyObject], PyObject)
+ at cpython_api([PyObject, PyObject], PyObject, result_is_ll=True)
def PyObject_GetItem(space, w_obj, w_key):
"""Return element of o corresponding to the object key or NULL on failure.
This is the equivalent of the Python expression o[key]."""
- return space.getitem(w_obj, w_key)
+ w_res = space.getitem(w_obj, w_key)
+ return hack_for_result_often_existing_obj(space, w_res)
@cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1)
def PyObject_SetItem(space, w_obj, w_key, w_value):
@@ -184,29 +165,6 @@
space.delitem(w_obj, w_key)
return 0
- at cpython_api([PyObject, PyTypeObjectPtr], PyObject, result_is_ll=True)
-def PyObject_Init(space, obj, type):
- """Initialize a newly-allocated object op with its type and initial
- reference. Returns the initialized object. If type indicates that the
- object participates in the cyclic garbage detector, it is added to the
- detector's set of observed objects. Other fields of the object are not
- affected."""
- if not obj:
- PyErr_NoMemory(space)
- obj.c_ob_type = type
- obj.c_ob_pypy_link = 0
- obj.c_ob_refcnt = 1
- return obj
-
- at cpython_api([PyVarObject, PyTypeObjectPtr, Py_ssize_t], PyObject, result_is_ll=True)
-def PyObject_InitVar(space, py_obj, type, size):
- """This does everything PyObject_Init() does, and also initializes the
- length information for a variable-size object."""
- if not py_obj:
- PyErr_NoMemory(space)
- py_obj.c_ob_size = size
- return PyObject_Init(space, rffi.cast(PyObject, py_obj), type)
-
@cpython_api([PyObject], PyObject)
def PyObject_Type(space, w_obj):
"""When o is non-NULL, returns a type object corresponding to the object type
@@ -315,7 +273,7 @@
@cpython_api([PyObject], PyObject, result_is_ll=True)
def PyObject_SelfIter(space, ref):
"""Undocumented function, this is what CPython does."""
- Py_IncRef(space, ref)
+ incref(space, ref)
return ref
@cpython_api([PyObject, PyObject], PyObject)
diff --git a/pypy/module/cpyext/pyerrors.py b/pypy/module/cpyext/pyerrors.py
--- a/pypy/module/cpyext/pyerrors.py
+++ b/pypy/module/cpyext/pyerrors.py
@@ -10,7 +10,7 @@
from pypy.module.exceptions.interp_exceptions import W_RuntimeWarning
from pypy.module.exceptions.interp_exceptions import W_StopIteration
from pypy.module.cpyext.pyobject import (
- PyObject, PyObjectP, make_ref, from_ref, Py_DecRef)
+ PyObject, PyObjectP, make_ref, from_ref, decref, get_w_obj_and_decref)
from pypy.module.cpyext.state import State
from pypy.module.cpyext.import_ import PyImport_Import
from rpython.rlib import rposix, jit
@@ -40,7 +40,7 @@
@slot_function([PyObject], lltype.Void)
def stopiteration_dealloc(space, py_obj):
py_stopiteration = rffi.cast(PyStopIterationObject, py_obj)
- Py_DecRef(space, py_stopiteration.c_value)
+ decref(space, py_stopiteration.c_value)
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
@@ -97,7 +97,7 @@
ptraceback[0] = lltype.nullptr(PyObject.TO)
@cpython_api([PyObject, PyObject, PyObject], lltype.Void)
-def PyErr_Restore(space, w_type, w_value, w_traceback):
+def PyErr_Restore(space, py_type, py_value, py_traceback):
"""Set the error indicator from the three objects. If the error indicator is
already set, it is cleared first. If the objects are NULL, the error
indicator is cleared. Do not pass a NULL type and non-NULL value or
@@ -112,13 +112,14 @@
error indicator temporarily; use PyErr_Fetch() to save the current
exception state."""
state = space.fromcache(State)
+ w_type = get_w_obj_and_decref(space, py_type)
+ w_value = get_w_obj_and_decref(space, py_value)
+ w_traceback = get_w_obj_and_decref(space, py_traceback)
+ # XXX do something with w_traceback
if w_type is None:
state.clear_exception()
return
state.set_exception(OperationError(w_type, w_value))
- Py_DecRef(space, w_type)
- Py_DecRef(space, w_value)
- Py_DecRef(space, w_traceback)
@cpython_api([PyObjectP, PyObjectP, PyObjectP], lltype.Void)
def PyErr_NormalizeException(space, exc_p, val_p, tb_p):
@@ -140,8 +141,8 @@
w_evalue = space.w_None
operr = OperationError(w_etype, w_evalue)
operr.normalize_exception(space)
- Py_DecRef(space, exc_p[0])
- Py_DecRef(space, val_p[0])
+ decref(space, exc_p[0])
+ decref(space, val_p[0])
exc_p[0] = make_ref(space, operr.w_type)
val_p[0] = make_ref(space, operr.get_w_value(space))
@@ -424,7 +425,7 @@
ptraceback[0] = lltype.nullptr(PyObject.TO)
@cpython_api([PyObject, PyObject, PyObject], lltype.Void)
-def PyErr_SetExcInfo(space, w_type, w_value, w_traceback):
+def PyErr_SetExcInfo(space, py_type, py_value, py_traceback):
"""---Cython extension---
Set the exception info, as known from ``sys.exc_info()``. This refers
@@ -440,6 +441,9 @@
restore the exception state temporarily. Use
:c:func:`PyErr_GetExcInfo` to read the exception state.
"""
+ w_type = get_w_obj_and_decref(space, py_type)
+ w_value = get_w_obj_and_decref(space, py_value)
+ w_traceback = get_w_obj_and_decref(space, py_traceback)
if w_value is None or space.is_w(w_value, space.w_None):
operror = None
else:
@@ -453,9 +457,6 @@
#
ec = space.getexecutioncontext()
ec.set_sys_exc_info(operror)
- Py_DecRef(space, w_type)
- Py_DecRef(space, w_value)
- Py_DecRef(space, w_traceback)
@cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
def PyOS_InterruptOccurred(space):
diff --git a/pypy/module/cpyext/pyobject.py b/pypy/module/cpyext/pyobject.py
--- a/pypy/module/cpyext/pyobject.py
+++ b/pypy/module/cpyext/pyobject.py
@@ -7,13 +7,13 @@
from pypy.module.cpyext.api import (
cpython_api, bootstrap_function, PyObject, PyObjectP, ADDR,
CANNOT_FAIL, Py_TPFLAGS_HEAPTYPE, PyTypeObjectPtr, is_PyObject,
- PyVarObject)
+ PyVarObject, Py_ssize_t, init_function, cts)
from pypy.module.cpyext.state import State
from pypy.objspace.std.typeobject import W_TypeObject
from pypy.objspace.std.objectobject import W_ObjectObject
-from rpython.rlib.objectmodel import specialize
+from rpython.rlib.objectmodel import specialize, we_are_translated
from rpython.rlib.objectmodel import keepalive_until_here
-from rpython.rtyper.annlowlevel import llhelper
+from rpython.rtyper.annlowlevel import llhelper, cast_instance_to_base_ptr
from rpython.rlib import rawrefcount, jit
from rpython.rlib.debug import ll_assert, fatalerror
@@ -26,9 +26,10 @@
W_BaseObject = W_ObjectObject
def get_dealloc(self, space):
- from pypy.module.cpyext.typeobject import subtype_dealloc
- return subtype_dealloc.api_func.get_llhelper(space)
+ state = space.fromcache(State)
+ return state.C._PyPy_subtype_dealloc
+ # CCC port to C
def allocate(self, space, w_type, itemcount=0, immortal=False):
# typically called from PyType_GenericAlloc via typedescr.allocate
# this returns a PyObject with ob_refcnt == 1.
@@ -39,7 +40,7 @@
# Don't increase refcount for non-heaptypes
flags = rffi.cast(lltype.Signed, pytype.c_tp_flags)
if flags & Py_TPFLAGS_HEAPTYPE:
- Py_IncRef(space, w_type)
+ incref(space, pytype)
if pytype:
size = pytype.c_tp_basicsize
@@ -97,17 +98,21 @@
assert not kw, "Extra arguments to make_typedescr"
null_dealloc = lltype.nullptr(lltype.FuncType([PyObject], lltype.Void))
+ assert not isinstance(tp_basestruct, lltype.Ptr), "should pass .TO"
class CpyTypedescr(BaseCpyTypedescr):
basestruct = tp_basestruct
if tp_alloc:
def allocate(self, space, w_type, itemcount=0, immortal=False):
- return tp_alloc(space, w_type, itemcount)
+ return tp_alloc(self, space, w_type, itemcount)
- if tp_dealloc:
+ if hasattr(tp_dealloc, 'api_func'):
def get_dealloc(self, space):
return tp_dealloc.api_func.get_llhelper(space)
+ elif tp_dealloc:
+ def get_dealloc(self, space):
+ return tp_dealloc
if tp_attach:
def attach(self, space, pyobj, w_obj, w_userdata=None):
@@ -123,10 +128,10 @@
@bootstrap_function
def init_pyobject(space):
- from pypy.module.cpyext.object import PyObject_dealloc
# typedescr for the 'object' type
+ state = space.fromcache(State)
make_typedescr(space.w_object.layout.typedef,
- dealloc=PyObject_dealloc)
+ dealloc=state.C._PyPy_object_dealloc)
# almost all types, which should better inherit from object.
make_typedescr(None)
@@ -180,7 +185,7 @@
"""
Ties together a PyObject and an interpreter object.
The PyObject's refcnt is increased by REFCNT_FROM_PYPY.
- The reference in 'py_obj' is not stolen! Remember to Py_DecRef()
+ The reference in 'py_obj' is not stolen! Remember to decref()
it is you need to.
"""
# XXX looks like a PyObject_GC_TRACK
@@ -238,8 +243,8 @@
use keepalive_until_here(w_obj) some time later.** In case of
doubt, use the safer make_ref().
"""
+ assert not is_pyobj(w_obj)
if w_obj is not None:
- assert not is_pyobj(w_obj)
py_obj = rawrefcount.from_obj(PyObject, w_obj)
if not py_obj:
py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal)
@@ -277,37 +282,44 @@
hop.exception_cannot_occur()
return hop.inputconst(lltype.Bool, hop.s_result.const)
- at specialize.ll()
-def make_ref(space, obj, w_userdata=None, immortal=False):
- """Increment the reference counter of the PyObject and return it.
- Can be called with either a PyObject or a W_Root.
- """
- if is_pyobj(obj):
- pyobj = rffi.cast(PyObject, obj)
- at_least = 1
- else:
- pyobj = as_pyobj(space, obj, w_userdata, immortal=immortal)
- at_least = rawrefcount.REFCNT_FROM_PYPY
- if pyobj:
- assert pyobj.c_ob_refcnt >= at_least
+def get_pyobj_and_incref(space, w_obj, w_userdata=None, immortal=False):
+ pyobj = as_pyobj(space, w_obj, w_userdata, immortal=immortal)
+ if pyobj: # != NULL
+ assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
pyobj.c_ob_refcnt += 1
- keepalive_until_here(obj)
+ keepalive_until_here(w_obj)
return pyobj
+def hack_for_result_often_existing_obj(space, w_obj):
+ # Equivalent to get_pyobj_and_incref() and not to make_ref():
+ # it builds a PyObject from a W_Root, but ensures that the result
+ # gets attached to the original W_Root. This is needed to work around
+ # some obscure abuses: https://github.com/numpy/numpy/issues/9850
+ return get_pyobj_and_incref(space, w_obj)
+
+def make_ref(space, w_obj, w_userdata=None, immortal=False):
+ """Turn the W_Root into a corresponding PyObject. You should
+ decref the returned PyObject later. Note that it is often the
+ case, but not guaranteed, that make_ref() returns always the
+ same PyObject for the same W_Root; for example, integers.
+ """
+ assert not is_pyobj(w_obj)
+ if False and w_obj is not None and space.type(w_obj) is space.w_int:
+ # XXX: adapt for pypy3
+ state = space.fromcache(State)
+ intval = space.int_w(w_obj)
+ return state.ccall("PyInt_FromLong", intval)
+ return get_pyobj_and_incref(space, w_obj, w_userdata, immortal=False)
@specialize.ll()
-def get_w_obj_and_decref(space, obj):
+def get_w_obj_and_decref(space, pyobj):
"""Decrement the reference counter of the PyObject and return the
- corresponding W_Root object (so the reference count is at least
- REFCNT_FROM_PYPY and cannot be zero). Can be called with either
- a PyObject or a W_Root.
+ corresponding W_Root object (so the reference count after the decref
+ is at least REFCNT_FROM_PYPY and cannot be zero).
"""
- if is_pyobj(obj):
- pyobj = rffi.cast(PyObject, obj)
- w_obj = from_ref(space, pyobj)
- else:
- w_obj = obj
- pyobj = as_pyobj(space, w_obj)
+ assert is_pyobj(pyobj)
+ pyobj = rffi.cast(PyObject, pyobj)
+ w_obj = from_ref(space, pyobj)
if pyobj:
pyobj.c_ob_refcnt -= 1
assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
@@ -316,51 +328,37 @@
@specialize.ll()
-def incref(space, obj):
- make_ref(space, obj)
+def incref(space, pyobj):
+ assert is_pyobj(pyobj)
+ pyobj = rffi.cast(PyObject, pyobj)
+ assert pyobj.c_ob_refcnt >= 1
+ pyobj.c_ob_refcnt += 1
@specialize.ll()
-def decref(space, obj):
- if is_pyobj(obj):
- obj = rffi.cast(PyObject, obj)
- if obj:
- assert obj.c_ob_refcnt > 0
- assert obj.c_ob_pypy_link == 0 or obj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY
- obj.c_ob_refcnt -= 1
- if obj.c_ob_refcnt == 0:
- _Py_Dealloc(space, obj)
- #else:
- # w_obj = rawrefcount.to_obj(W_Root, ref)
- # if w_obj is not None:
- # assert obj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
- else:
- get_w_obj_and_decref(space, obj)
+def decref(space, pyobj):
+ from pypy.module.cpyext.api import generic_cpy_call
+ assert is_pyobj(pyobj)
+ pyobj = rffi.cast(PyObject, pyobj)
+ if pyobj:
+ assert pyobj.c_ob_refcnt > 0
+ assert (pyobj.c_ob_pypy_link == 0 or
+ pyobj.c_ob_refcnt > rawrefcount.REFCNT_FROM_PYPY)
+ pyobj.c_ob_refcnt -= 1
+ if pyobj.c_ob_refcnt == 0:
+ state = space.fromcache(State)
+ generic_cpy_call(space, state.C._Py_Dealloc, pyobj)
+ #else:
+ # w_obj = rawrefcount.to_obj(W_Root, ref)
+ # if w_obj is not None:
+ # assert pyobj.c_ob_refcnt >= rawrefcount.REFCNT_FROM_PYPY
- at cpython_api([PyObject], lltype.Void)
-def Py_IncRef(space, obj):
- incref(space, obj)
-
- at cpython_api([PyObject], lltype.Void)
-def Py_DecRef(space, obj):
- decref(space, obj)
-
- at cpython_api([PyObject], lltype.Void)
-def _Py_NewReference(space, obj):
- obj.c_ob_refcnt = 1
- # XXX is it always useful to create the W_Root object here?
- w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type))
- assert isinstance(w_type, W_TypeObject)
- get_typedescr(w_type.layout.typedef).realize(space, obj)
-
- at cpython_api([PyObject], lltype.Void)
-def _Py_Dealloc(space, obj):
- from pypy.module.cpyext.api import generic_cpy_call
- pto = obj.c_ob_type
- #print >>sys.stderr, "Calling dealloc slot", pto.c_tp_dealloc, "of", obj, \
- # "'s type which is", rffi.charp2str(pto.c_tp_name)
- rawrefcount.mark_deallocating(w_marker_deallocating, obj)
- generic_cpy_call(space, pto.c_tp_dealloc, obj)
+ at init_function
+def write_w_marker_deallocating(space):
+ if we_are_translated():
+ llptr = cast_instance_to_base_ptr(w_marker_deallocating)
+ state = space.fromcache(State)
+ state.C.set_marker(rffi.cast(Py_ssize_t, llptr))
@cpython_api([rffi.VOIDP], lltype.Signed, error=CANNOT_FAIL)
def _Py_HashPointer(space, ptr):
diff --git a/pypy/module/cpyext/pystate.py b/pypy/module/cpyext/pystate.py
--- a/pypy/module/cpyext/pystate.py
+++ b/pypy/module/cpyext/pystate.py
@@ -1,6 +1,6 @@
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, cpython_struct)
-from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref
+from pypy.module.cpyext.pyobject import PyObject, decref, make_ref
from pypy.interpreter.error import OperationError
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib import rthread
@@ -75,7 +75,7 @@
def ThreadState_dealloc(ts, space):
assert space is not None
- Py_DecRef(space, ts.c_dict)
+ decref(space, ts.c_dict)
ThreadStateCapsule = encapsulator(PyThreadState.TO,
dealloc=ThreadState_dealloc)
@@ -323,7 +323,7 @@
interpreter lock must be held."""
if not space.config.translation.thread:
raise NoThreads
- Py_DecRef(space, tstate.c_dict)
+ decref(space, tstate.c_dict)
tstate.c_dict = lltype.nullptr(PyObject.TO)
space.threadlocals.leave_thread(space)
space.getexecutioncontext().cleanup_cpyext_state()
diff --git a/pypy/module/cpyext/pytraceback.py b/pypy/module/cpyext/pytraceback.py
--- a/pypy/module/cpyext/pytraceback.py
+++ b/pypy/module/cpyext/pytraceback.py
@@ -4,7 +4,7 @@
cpython_api, bootstrap_function, cpython_struct,
slot_function)
from pypy.module.cpyext.pyobject import (
- PyObject, make_ref, from_ref, Py_DecRef, make_typedescr)
+ PyObject, make_ref, from_ref, decref, make_typedescr)
from pypy.module.cpyext.frameobject import PyFrameObject
from pypy.interpreter.error import OperationError
from pypy.interpreter.pytraceback import PyTraceback
@@ -44,7 +44,7 @@
@slot_function([PyObject], lltype.Void)
def traceback_dealloc(space, py_obj):
py_traceback = rffi.cast(PyTracebackObject, py_obj)
- Py_DecRef(space, rffi.cast(PyObject, py_traceback.c_tb_next))
- Py_DecRef(space, rffi.cast(PyObject, py_traceback.c_tb_frame))
+ decref(space, rffi.cast(PyObject, py_traceback.c_tb_next))
+ decref(space, rffi.cast(PyObject, py_traceback.c_tb_frame))
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/sequence.py b/pypy/module/cpyext/sequence.py
--- a/pypy/module/cpyext/sequence.py
+++ b/pypy/module/cpyext/sequence.py
@@ -1,11 +1,13 @@
from rpython.rlib import rerased, jit
+from rpython.rlib.objectmodel import keepalive_until_here
from pypy.interpreter.error import OperationError, oefmt
from pypy.objspace.std.listobject import (
ListStrategy, UNROLL_CUTOFF, W_ListObject, ObjectListStrategy)
from pypy.module.cpyext.api import (
cpython_api, CANNOT_FAIL, CONST_STRING, Py_ssize_t, PyObject, PyObjectP)
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
+from pypy.module.cpyext.pyobject import as_pyobj, incref
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.objspace.std import tupleobject
@@ -47,6 +49,8 @@
converted to a sequence, and raises a TypeError, raise a new TypeError with
m as the message text. If the conversion otherwise, fails, reraise the
original exception"""
+ if isinstance(w_obj, tupleobject.W_TupleObject):
+ return w_obj # CCC avoid the double conversion that occurs here
if isinstance(w_obj, W_ListObject):
# make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM
w_obj.convert_to_cpy_strategy(space)
@@ -58,6 +62,7 @@
raise OperationError(space.w_TypeError, space.newtext(rffi.charp2str(m)))
raise e
+# CCC this should be written as a C macro
@cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_borrowed=True)
def PySequence_Fast_GET_ITEM(space, w_obj, index):
"""Return the ith element of o, assuming that o was returned by
@@ -99,6 +104,11 @@
cpy_strategy = space.fromcache(CPyListStrategy)
if w_obj.strategy is cpy_strategy:
return w_obj.get_raw_items() # asserts it's a cpyext strategy
+ elif isinstance(w_obj, tupleobject.W_TupleObject):
+ from pypy.module.cpyext.tupleobject import PyTupleObject
+ py_obj = as_pyobj(space, w_obj)
+ py_tuple = rffi.cast(PyTupleObject, py_obj)
+ return rffi.cast(PyObjectP, py_tuple.c_ob_item)
raise oefmt(space.w_TypeError,
"PySequence_Fast_ITEMS called but object is not the result of "
"PySequence_Fast")
@@ -123,7 +133,7 @@
space.delslice(w_obj, space.newint(start), space.newint(end))
return 0
- at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject)
+ at cpython_api([rffi.VOIDP, Py_ssize_t], PyObject, result_is_ll=True)
def PySequence_ITEM(space, w_obj, i):
"""Return the ith element of o or NULL on failure. Macro form of
PySequence_GetItem() but without checking that
@@ -132,12 +142,32 @@
This function used an int type for i. This might require
changes in your code for properly supporting 64-bit systems."""
- return space.getitem(w_obj, space.newint(i))
+ # XXX we should call Py*_GET_ITEM() instead of Py*_GetItem()
+ # from here, but we cannot because we are also called from
+ # PySequence_GetItem()
+ if isinstance(w_obj, tupleobject.W_TupleObject):
+ from pypy.module.cpyext.tupleobject import PyTuple_GetItem
+ py_obj = as_pyobj(space, w_obj)
+ py_res = PyTuple_GetItem(space, py_obj, i)
+ incref(space, py_res)
+ keepalive_until_here(w_obj)
+ return py_res
+ if isinstance(w_obj, W_ListObject):
+ from pypy.module.cpyext.listobject import PyList_GetItem
+ py_obj = as_pyobj(space, w_obj)
+ py_res = PyList_GetItem(space, py_obj, i)
+ incref(space, py_res)
+ keepalive_until_here(w_obj)
+ return py_res
+ return make_ref(space, space.getitem(w_obj, space.newint(i)))
- at cpython_api([PyObject, Py_ssize_t], PyObject)
+ at cpython_api([PyObject, Py_ssize_t], PyObject, result_is_ll=True)
def PySequence_GetItem(space, w_obj, i):
"""Return the ith element of o, or NULL on failure. This is the equivalent of
the Python expression o[i]."""
+ if i < 0:
+ l = PySequence_Length(space, w_obj)
+ i += l
return PySequence_ITEM(space, w_obj, i)
@cpython_api([PyObject], PyObject)
diff --git a/pypy/module/cpyext/setobject.py b/pypy/module/cpyext/setobject.py
--- a/pypy/module/cpyext/setobject.py
+++ b/pypy/module/cpyext/setobject.py
@@ -2,7 +2,7 @@
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
build_type_checkers)
-from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
+from pypy.module.cpyext.pyobject import (PyObject, PyObjectP,
make_ref, from_ref)
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
from pypy.objspace.std.setobject import W_SetObject, W_FrozensetObject, newset
diff --git a/pypy/module/cpyext/sliceobject.py b/pypy/module/cpyext/sliceobject.py
--- a/pypy/module/cpyext/sliceobject.py
+++ b/pypy/module/cpyext/sliceobject.py
@@ -3,7 +3,7 @@
cpython_api, cpython_struct, bootstrap_function, build_type_checkers,
CANNOT_FAIL, Py_ssize_t, Py_ssize_tP, PyObjectFields, slot_function)
from pypy.module.cpyext.pyobject import (
- Py_DecRef, PyObject, make_ref, make_typedescr)
+ decref, PyObject, make_ref, make_typedescr)
from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
from pypy.interpreter.error import OperationError
from pypy.objspace.std.sliceobject import W_SliceObject
@@ -41,9 +41,9 @@
"""Frees allocated PySliceObject resources.
"""
py_slice = rffi.cast(PySliceObject, py_obj)
- Py_DecRef(space, py_slice.c_start)
- Py_DecRef(space, py_slice.c_stop)
- Py_DecRef(space, py_slice.c_step)
+ decref(space, py_slice.c_start)
+ decref(space, py_slice.c_stop)
+ decref(space, py_slice.c_step)
from pypy.module.cpyext.object import _dealloc
_dealloc(space, py_obj)
diff --git a/pypy/module/cpyext/src/intobject.c b/pypy/module/cpyext/src/intobject.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/src/intobject.c
@@ -0,0 +1,108 @@
+
+/* Integer object implementation -- copied&adapted from CPython */
+
+#include "Python.h"
+
+/* Integers are quite normal objects, to make object handling uniform.
+ (Using odd pointers to represent integers would save much space
+ but require extra checks for this special case throughout the code.)
+ Since a typical Python program spends much of its time allocating
+ and deallocating integers, these operations should be very fast.
+ Therefore we use a dedicated allocation scheme with a much lower
+ overhead (in space and time) than straight malloc(): a simple
+ dedicated free list, filled when necessary with memory from malloc().
+
+ block_list is a singly-linked list of all PyIntBlocks ever allocated,
+ linked via their next members. PyIntBlocks are never returned to the
+ system before shutdown (PyInt_Fini).
+
+ free_list is a singly-linked list of available PyIntObjects, linked
+ via abuse of their ob_type members.
+*/
+
+#define BLOCK_SIZE 1000 /* 1K less typical malloc overhead */
+#define BHEAD_SIZE 8 /* Enough for a 64-bit pointer */
+#define N_INTOBJECTS ((BLOCK_SIZE - BHEAD_SIZE) / sizeof(PyIntObject))
+
+struct _intblock {
+ struct _intblock *next;
+ PyIntObject objects[N_INTOBJECTS];
+};
+
+typedef struct _intblock PyIntBlock;
+
+static PyIntBlock *block_list = NULL;
+static PyIntObject *free_list = NULL;
+
+static PyIntObject *
+fill_free_list(void)
+{
+ PyIntObject *p, *q;
+ /* Python's object allocator isn't appropriate for large blocks. */
+ p = (PyIntObject *) PyMem_MALLOC(sizeof(PyIntBlock));
+ if (p == NULL)
+ return (PyIntObject *) PyErr_NoMemory();
+ ((PyIntBlock *)p)->next = block_list;
+ block_list = (PyIntBlock *)p;
+ /* Link the int objects together, from rear to front, then return
+ the address of the last int object in the block. */
+ p = &((PyIntBlock *)p)->objects[0];
+ q = p + N_INTOBJECTS;
+ while (--q > p)
+ Py_TYPE(q) = (struct _typeobject *)(q-1);
+ Py_TYPE(q) = NULL;
+ return p + N_INTOBJECTS - 1;
+}
+
+#ifndef NSMALLPOSINTS
+#define NSMALLPOSINTS 257
+#endif
+#ifndef NSMALLNEGINTS
+#define NSMALLNEGINTS 5
+#endif
+#if NSMALLNEGINTS + NSMALLPOSINTS > 0
+/* References to small integers are saved in this array so that they
+ can be shared.
+ The integers that are saved are those in the range
+ -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
+*/
+static PyIntObject *small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
+#endif
+
+PyObject *
+PyInt_FromLong(long ival)
+{
+ register PyIntObject *v;
+ /*
+#if NSMALLNEGINTS + NSMALLPOSINTS > 0
+ if (-NSMALLNEGINTS <= ival && ival < NSMALLPOSINTS) {
+ v = small_ints[ival + NSMALLNEGINTS];
+ Py_INCREF(v);
+ return (PyObject *) v;
+ }
+#endif
+ */
+ if (free_list == NULL) {
+ if ((free_list = fill_free_list()) == NULL)
+ return NULL;
+ }
+ /* Inline PyObject_New */
+ v = free_list;
+ free_list = (PyIntObject *)Py_TYPE(v);
+ (void)PyObject_INIT(v, &PyInt_Type);
+ v->ob_ival = ival;
+ return (PyObject *) v;
+}
+
+/* this is CPython's int_dealloc */
+void
+_PyPy_int_dealloc(PyObject *obj)
+{
+ PyIntObject *v = (PyIntObject *)obj;
+ if (PyInt_CheckExact(v)) {
+ Py_TYPE(v) = (struct _typeobject *)free_list;
More information about the pypy-commit
mailing list