Author: fijal Branch: Changeset: r92106:bddc7d672228 Date: 2017-08-07 14:23 +0200 http://bitbucket.org/pypy/pypy/changeset/bddc7d672228/
Log: merge diff --git a/lib_pypy/_ctypes/structure.py b/lib_pypy/_ctypes/structure.py --- a/lib_pypy/_ctypes/structure.py +++ b/lib_pypy/_ctypes/structure.py @@ -40,6 +40,22 @@ else: rawfields.append((f[0], f[1]._ffishape_)) + # hack for duplicate field names + already_seen = set() + names1 = names + names = [] + for f in names1: + if f not in already_seen: + names.append(f) + already_seen.add(f) + already_seen = set() + for i in reversed(range(len(rawfields))): + if rawfields[i][0] in already_seen: + rawfields[i] = (('$DUP%d$%s' % (i, rawfields[i][0]),) + + rawfields[i][1:]) + already_seen.add(rawfields[i][0]) + # /hack + _set_shape(self, rawfields, self._is_union) fields = {} 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 @@ -60,3 +60,16 @@ Small improvement to optimize list accesses with constant indexes better by throwing away information about them less eagerly. + + +.. branch: getarrayitem-into-bridges: + +More information is retained into a bridge: knowledge about the content of +arrays (at fixed indices) is stored in guards (and thus available at the +beginning of bridges). Also, some better feeding of information about known +fields of constant objects into bridges. + +.. branch: cpyext-leakchecking + +Add support for leakfinder in cpyext tests (disabled for now, due to too many +failures). diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -1,4 +1,5 @@ import sys +import py from rpython.rlib.cache import Cache from rpython.tool.uid import HUGEVAL_BYTES @@ -1338,8 +1339,22 @@ self.setitem(w_globals, w_key, self.builtin) return statement.exec_code(self, w_globals, w_locals) + @not_rpython + def appdef(self, source): + '''Create interp-level function object from app-level source. + + The source should be in the same format as for space.appexec(): + """(foo, bar): return 'baz'""" + ''' + source = source.lstrip() + assert source.startswith('('), "incorrect header in:\n%s" % (source,) + source = py.code.Source("def anonymous%s\n" % source) + w_glob = self.newdict(module=True) + self.exec_(str(source), w_glob, w_glob) + return self.getitem(w_glob, self.newtext('anonymous')) + @specialize.arg(2) - def appexec(self, posargs_w, source): + def appexec(self, posargs_w, source, cache=True): """ return value from executing given source at applevel. The source must look like '''(x, y): @@ -1347,7 +1362,11 @@ return result ''' """ - w_func = self.fromcache(AppExecCache).getorbuild(source) + if cache: + w_func = self.fromcache(AppExecCache).getorbuild(source) + else: + # NB: since appdef() is not-RPython, using cache=False also is. + w_func = self.appdef(source) args = Arguments(self, list(posargs_w)) return self.call_args(w_func, args) @@ -1926,15 +1945,7 @@ class AppExecCache(SpaceCache): @not_rpython def build(cache, source): - space = cache.space - # XXX will change once we have our own compiler - import py - source = source.lstrip() - assert source.startswith('('), "incorrect header in:\n%s" % (source,) - source = py.code.Source("def anonymous%s\n" % source) - w_glob = space.newdict(module=True) - space.exec_(str(source), w_glob, w_glob) - return space.getitem(w_glob, space.newtext('anonymous')) + return cache.space.appdef(source) # Table describing the regular part of the interface of object spaces, diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py --- a/pypy/module/cpyext/buffer.py +++ b/pypy/module/cpyext/buffer.py @@ -73,22 +73,27 @@ if self.needs_decref: if self.releasebufferproc: func_target = rffi.cast(releasebufferproc, self.releasebufferproc) - with lltype.scoped_alloc(Py_buffer) as pybuf: - pybuf.c_buf = self.ptr - pybuf.c_len = self.size - pybuf.c_ndim = cts.cast('int', self.ndim) - pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape) - pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides) - for i in range(self.ndim): - pybuf.c_shape[i] = self.shape[i] - pybuf.c_strides[i] = self.strides[i] - if self.format: - pybuf.c_format = rffi.str2charp(self.format) - else: - pybuf.c_format = rffi.str2charp("B") + size = rffi.sizeof(cts.gettype('Py_buffer')) + pybuf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', zero=True) + pybuf = cts.cast('Py_buffer*', pybuf) + pybuf.c_buf = self.ptr + pybuf.c_len = self.size + pybuf.c_ndim = cts.cast('int', self.ndim) + pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape) + pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides) + for i in range(self.ndim): + pybuf.c_shape[i] = self.shape[i] + pybuf.c_strides[i] = self.strides[i] + fmt = rffi.str2charp(self.format if self.format else "B") + try: + pybuf.c_format = fmt generic_cpy_call(self.space, func_target, self.pyobj, pybuf) + finally: + lltype.free(fmt, flavor='raw') + lltype.free(pybuf, flavor='raw') decref(self.space, self.pyobj) self.pyobj = lltype.nullptr(PyObject.TO) + self.w_obj = None else: #do not call twice return @@ -167,6 +172,8 @@ sizep[0] = size return 0 +DEFAULT_FMT = rffi.str2charp("B") + @cpython_api([lltype.Ptr(Py_buffer), PyObject, rffi.VOIDP, Py_ssize_t, lltype.Signed, lltype.Signed], rffi.INT, error=-1) def PyBuffer_FillInfo(space, view, obj, buf, length, readonly, flags): @@ -187,7 +194,8 @@ rffi.setintfield(view, 'c_ndim', 1) view.c_format = lltype.nullptr(rffi.CCHARP.TO) if (flags & PyBUF_FORMAT) == PyBUF_FORMAT: - view.c_format = rffi.str2charp("B") + # NB: this needs to be a static string, because nothing frees it + view.c_format = DEFAULT_FMT view.c_shape = lltype.nullptr(Py_ssize_tP.TO) if (flags & PyBUF_ND) == PyBUF_ND: view.c_shape = rffi.cast(Py_ssize_tP, view.c__shape) diff --git a/pypy/module/cpyext/memoryobject.py b/pypy/module/cpyext/memoryobject.py --- a/pypy/module/cpyext/memoryobject.py +++ b/pypy/module/cpyext/memoryobject.py @@ -72,7 +72,7 @@ readonly=widen(view.c_readonly)) # Ensure view.c_buf is released upon object finalization fq.register_finalizer(buf) - # Allow subclassing W_MemeoryView + # Allow subclassing W_MemoryView w_type = from_ref(space, rffi.cast(PyObject, obj.c_ob_type)) w_obj = space.allocate_instance(W_MemoryView, w_type) w_obj.__init__(buf) @@ -177,11 +177,9 @@ return (_IsCContiguous(view) or _IsFortranContiguous(view)) return 0 -@cpython_api([PyObject], PyObject, result_is_ll=True) +@cpython_api([PyObject], PyObject) def PyMemoryView_FromObject(space, w_obj): - w_memview = space.call_method(space.builtin, "memoryview", w_obj) - py_memview = make_ref(space, w_memview, w_obj) - return py_memview + return space.call_method(space.builtin, "memoryview", w_obj) @cpython_api([Py_bufferP], PyObject, result_is_ll=True) def PyMemoryView_FromBuffer(space, view): @@ -193,6 +191,7 @@ # copy view into obj.c_view, without creating a new view.c_obj typedescr = get_typedescr(W_MemoryView.typedef) py_obj = typedescr.allocate(space, space.w_memoryview) + py_mem = rffi.cast(PyMemoryViewObject, py_obj) mview = py_mem.c_view mview.c_buf = view.c_buf 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 @@ -29,7 +29,7 @@ from pypy.module.cpyext.typeobject import subtype_dealloc return subtype_dealloc.api_func - def allocate(self, space, w_type, itemcount=0): + 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. @@ -50,7 +50,7 @@ assert size >= rffi.sizeof(PyObject.TO) buf = lltype.malloc(rffi.VOIDP.TO, size, flavor='raw', zero=True, - add_memory_pressure=True) + add_memory_pressure=True, immortal=immortal) pyobj = rffi.cast(PyObject, buf) if pytype.c_tp_itemsize: pyvarobj = rffi.cast(PyVarObject, pyobj) @@ -102,7 +102,7 @@ basestruct = tp_basestruct if tp_alloc: - def allocate(self, space, w_type, itemcount=0): + def allocate(self, space, w_type, itemcount=0, immortal=False): return tp_alloc(space, w_type, itemcount) if tp_dealloc: @@ -151,7 +151,7 @@ class InvalidPointerException(Exception): pass -def create_ref(space, w_obj, w_userdata=None): +def create_ref(space, w_obj, w_userdata=None, immortal=False): """ Allocates a PyObject, and fills its fields with info from the given interpreter object. @@ -163,7 +163,7 @@ itemcount = space.len_w(w_obj) # PyBytesObject and subclasses else: itemcount = 0 - py_obj = typedescr.allocate(space, w_type, itemcount=itemcount) + py_obj = typedescr.allocate(space, w_type, itemcount=itemcount, immortal=immortal) track_reference(space, py_obj, w_obj) # # py_obj.c_ob_refcnt should be exactly REFCNT_FROM_PYPY + 1 here, @@ -227,7 +227,7 @@ assert isinstance(w_type, W_TypeObject) return get_typedescr(w_type.layout.typedef).realize(space, ref) -def as_pyobj(space, w_obj, w_userdata=None): +def as_pyobj(space, w_obj, w_userdata=None, immortal=False): """ Returns a 'PyObject *' representing the given intepreter object. This doesn't give a new reference, but the returned 'PyObject *' @@ -239,7 +239,7 @@ 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) + py_obj = create_ref(space, w_obj, w_userdata, immortal=immortal) return py_obj else: return lltype.nullptr(PyObject.TO) @@ -270,7 +270,7 @@ return hop.inputconst(lltype.Bool, hop.s_result.const) @specialize.ll() -def make_ref(space, obj, w_userdata=None): +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. """ @@ -278,7 +278,7 @@ pyobj = rffi.cast(PyObject, obj) at_least = 1 else: - pyobj = as_pyobj(space, obj, w_userdata) + pyobj = as_pyobj(space, obj, w_userdata, immortal=immortal) at_least = rawrefcount.REFCNT_FROM_PYPY if pyobj: assert pyobj.c_ob_refcnt >= at_least 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 @@ -48,7 +48,7 @@ m as the message text. If the conversion otherwise, fails, reraise the original exception""" if isinstance(w_obj, W_ListObject): - # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM + # make sure we can return a borrowed obj from PySequence_Fast_GET_ITEM w_obj.convert_to_cpy_strategy(space) return w_obj try: @@ -313,7 +313,7 @@ self) w_clone.switch_to_object_strategy() return w_clone - + def copy_into(self, w_list, w_other): w_list.switch_to_object_strategy() w_list.strategy.copy_into(w_list, w_other) @@ -378,7 +378,7 @@ def is_empty_strategy(self): return False - + PyObjectList = lltype.Ptr(lltype.Array(PyObject, hints={'nolength': True})) diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c --- a/pypy/module/cpyext/test/array.c +++ b/pypy/module/cpyext/test/array.c @@ -1864,10 +1864,12 @@ if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' && Py_SIZE(obj2) == 1) { int ii, nn; + PyObject *ret; int n = PyList_Size(obj1); PyObject *v = getarrayitem(obj2, 0); int i = ((PyIntObject*)v)->ob_ival; - PyObject * ret = PyList_New(n*i); + Py_DECREF(v); + ret = PyList_New(n*i); for (ii = 0; ii < i; ii++) for (nn = 0; nn < n; nn++) { @@ -1880,10 +1882,12 @@ else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 'i' && Py_SIZE(obj1) == 1) { int ii, nn; + PyObject *ret; int n = PyList_Size(obj2); PyObject *v = getarrayitem(obj1, 0); int i = ((PyIntObject*)v)->ob_ival; - PyObject * ret = PyList_New(n*i); + Py_DECREF(v); + ret = PyList_New(n*i); for (ii = 0; ii < i; ii++) for (nn = 0; nn < n; nn++) { @@ -1916,34 +1920,44 @@ if (PyList_Check(obj1) && ((arrayobject*)obj2)->ob_descr->typecode == 'i' && Py_SIZE(obj2) == 1) { int nn; + PyObject *ret; int n = PyList_Size(obj1); PyObject *v = getarrayitem(obj2, 0); int i = ((PyIntObject*)v)->ob_ival; - PyObject * ret = PyList_New(n); + Py_DECREF(v); + ret = PyList_New(n); for (nn = 0; nn < n; nn++) { v = PyList_GetItem(obj1, nn); if (PyInt_Check(v)) PyList_SetItem(ret, nn, PyLong_FromLong(i * ((PyIntObject*)v)->ob_ival)); else + { + Py_INCREF(v); PyList_SetItem(ret, nn, v); + } } return ret; } else if (PyList_Check(obj2) && ((arrayobject*)obj1)->ob_descr->typecode == 'i' && Py_SIZE(obj1) == 1) { int nn; + PyObject *ret; int n = PyList_Size(obj2); PyObject *v = getarrayitem(obj1, 0); int i = ((PyIntObject*)v)->ob_ival; - PyObject * ret = PyList_New(n); + Py_DECREF(v); + ret = PyList_New(n); for (nn = 0; nn < n; nn++) { v = PyList_GetItem(obj2, nn); if (PyInt_Check(v)) PyList_SetItem(ret, nn, PyLong_FromLong(i * ((PyIntObject*)v)->ob_ival)); else + { + Py_INCREF(v); PyList_SetItem(ret, nn, v); + } } return ret; } @@ -2458,6 +2472,15 @@ Py_RETURN_NONE; } +static PyObject * +same_dealloc(PyObject *self, PyObject *args) +{ + PyObject *obj1, *obj2; + if (!PyArg_ParseTuple(args, "OO", &obj1, &obj2)) { + return NULL; + } + return PyLong_FromLong(obj1->ob_type->tp_dealloc == obj2->ob_type->tp_dealloc); +} /*********************** Install Module **************************/ @@ -2467,6 +2490,7 @@ {"readbuffer_as_string", (PyCFunction)readbuffer_as_string, METH_VARARGS, NULL}, {"get_releasebuffer_cnt", (PyCFunction)get_releasebuffer_cnt, METH_NOARGS, NULL}, {"create_and_release_buffer", (PyCFunction)create_and_release_buffer, METH_O, NULL}, + {"same_dealloc", (PyCFunction)same_dealloc, METH_VARARGS, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pypy/module/cpyext/test/test_api.py b/pypy/module/cpyext/test/test_api.py --- a/pypy/module/cpyext/test/test_api.py +++ b/pypy/module/cpyext/test/test_api.py @@ -6,7 +6,8 @@ from pypy.module.cpyext.api import ( slot_function, cpython_api, copy_header_files, INTERPLEVEL_API, Py_ssize_t, Py_ssize_tP, PyObject, cts) -from pypy.module.cpyext.test.test_cpyext import freeze_refcnts, LeakCheckingTest +from pypy.module.cpyext.test.test_cpyext import ( + freeze_refcnts, LeakCheckingTest) from pypy.interpreter.error import OperationError from rpython.rlib import rawrefcount import os @@ -21,17 +22,7 @@ class BaseApiTest(LeakCheckingTest): def setup_class(cls): space = cls.space - # warm up reference counts: - # - the posix module allocates a HCRYPTPROV on Windows - # - writing to stdout and stderr allocates a file lock - space.getbuiltinmodule("cpyext") - space.getbuiltinmodule(os.name) - space.call_function(space.getattr(space.sys.get("stderr"), - space.wrap("write")), - space.wrap("")) - space.call_function(space.getattr(space.sys.get("stdout"), - space.wrap("write")), - space.wrap("")) + cls.preload_builtins(space) class CAPI: def __getattr__(self, name): @@ -39,9 +30,6 @@ cls.api = CAPI() CAPI.__dict__.update(INTERPLEVEL_API) - print 'DONT_FREE_ANY_MORE' - rawrefcount._dont_free_any_more() - def raises(self, space, api, expected_exc, f, *args): if not callable(f): raise Exception("%s is not callable" % (f,)) diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py --- a/pypy/module/cpyext/test/test_arraymodule.py +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -1,3 +1,4 @@ +import pytest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from pypy.conftest import option @@ -111,6 +112,19 @@ res = [1, 2, 3] * arr assert res == [2, 4, 6] + @pytest.mark.xfail + def test_subclass_dealloc(self): + module = self.import_module(name='array') + class Sub(module.array): + pass + + arr = Sub('i', [2]) + module.readbuffer_as_string(arr) + class A(object): + pass + assert not module.same_dealloc(arr, module.array('i', [2])) + assert module.same_dealloc(arr, A()) + def test_subclass(self): import struct module = self.import_module(name='array') diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py --- a/pypy/module/cpyext/test/test_cpyext.py +++ b/pypy/module/cpyext/test/test_cpyext.py @@ -4,8 +4,11 @@ from pypy.tool.cpyext.extbuild import SystemCompilationInfo, HERE from pypy.interpreter.gateway import unwrap_spec, interp2app +from pypy.interpreter.error import OperationError from rpython.rtyper.lltypesystem import lltype from pypy.module.cpyext import api +from pypy.module.cpyext.api import cts +from pypy.module.cpyext.pyobject import from_ref from pypy.module.cpyext.state import State from rpython.tool import leakfinder from rpython.rlib import rawrefcount @@ -73,6 +76,58 @@ def freeze_refcnts(self): rawrefcount._dont_free_any_more() +def preload(space, name): + from pypy.module.cpyext.pyobject import make_ref + if '.' not in name: + w_obj = space.builtin.getdictvalue(space, name) + else: + module, localname = name.rsplit('.', 1) + code = "(): import {module}; return {module}.{localname}" + code = code.format(**locals()) + w_obj = space.appexec([], code) + make_ref(space, w_obj) + +def preload_expr(space, expr): + from pypy.module.cpyext.pyobject import make_ref + code = "(): return {}".format(expr) + w_obj = space.appexec([], code) + make_ref(space, w_obj) + +def is_interned_string(space, w_obj): + try: + s = space.str_w(w_obj) + except OperationError: + return False + return space.is_interned_str(s) + +def is_allowed_to_leak(space, obj): + from pypy.module.cpyext.methodobject import W_PyCFunctionObject + try: + w_obj = from_ref(space, cts.cast('PyObject*', obj._as_ptr())) + except: + return False + if isinstance(w_obj, W_PyCFunctionObject): + return True + # It's OK to "leak" some interned strings: if the pyobj is created by + # the test, but the w_obj is referred to from elsewhere. + return is_interned_string(space, w_obj) + +def _get_w_obj(space, c_obj): + return from_ref(space, cts.cast('PyObject*', c_obj._as_ptr())) + +class CpyextLeak(leakfinder.MallocMismatch): + def __str__(self): + lines = [leakfinder.MallocMismatch.__str__(self), ''] + lines.append( + "These objects are attached to the following W_Root objects:") + for c_obj in self.args[0]: + try: + lines.append(" %s" % (_get_w_obj(self.args[1], c_obj),)) + except: + pass + return '\n'.join(lines) + + class LeakCheckingTest(object): """Base class for all cpyext tests.""" spaceconfig = dict(usemodules=['cpyext', 'thread', 'struct', 'array', @@ -80,11 +135,35 @@ 'micronumpy', 'mmap' ]) + @classmethod + def preload_builtins(cls, space): + """ + Eagerly create pyobjs for various builtins so they don't look like + leaks. + """ + for name in [ + 'buffer', 'mmap.mmap', + 'types.FunctionType', 'types.CodeType', + 'types.TracebackType', 'types.FrameType']: + preload(space, name) + for expr in ['type(str.join)']: + preload_expr(space, expr) + def cleanup(self): self.space.getexecutioncontext().cleanup_cpyext_state() rawrefcount._collect() self.space.user_del_action._run_finalizers() - leakfinder.stop_tracking_allocations(check=False) + try: + # set check=True to actually enable leakfinder + leakfinder.stop_tracking_allocations(check=False) + except leakfinder.MallocMismatch as e: + result = e.args[0] + filtered_result = {} + for obj, value in result.iteritems(): + if not is_allowed_to_leak(self.space, obj): + filtered_result[obj] = value + if filtered_result: + raise CpyextLeak(filtered_result, self.space) assert not self.space.finalizer_queue.next_dead() @@ -131,6 +210,7 @@ def debug_collect(space): rawrefcount._collect() + class AppTestCpythonExtensionBase(LeakCheckingTest): def setup_class(cls): @@ -140,13 +220,8 @@ cls.w_runappdirect = space.wrap(cls.runappdirect) if not cls.runappdirect: cls.sys_info = get_cpyext_info(space) - space.getbuiltinmodule("cpyext") - # 'import os' to warm up reference counts - w_import = space.builtin.getdictvalue(space, '__import__') - space.call_function(w_import, space.wrap("os")) - #state = cls.space.fromcache(RefcountState) ZZZ - #state.non_heaptypes_w[:] = [] cls.w_debug_collect = space.wrap(interp2app(debug_collect)) + cls.preload_builtins(space) else: def w_import_module(self, name, init=None, body='', filename=None, include_dirs=None, PY_SSIZE_T_CLEAN=False): diff --git a/pypy/module/cpyext/test/test_dictobject.py b/pypy/module/cpyext/test/test_dictobject.py --- a/pypy/module/cpyext/test/test_dictobject.py +++ b/pypy/module/cpyext/test/test_dictobject.py @@ -111,70 +111,14 @@ PyDict_Update(space, w_d, w_d2) assert space.unwrap(w_d) == dict(a='b') # unchanged - def test_iter(self, space): - w_dict = space.sys.getdict(space) - py_dict = make_ref(space, w_dict) - - ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') - ppos[0] = 0 - pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - - try: - w_copy = space.newdict() - while PyDict_Next(space, w_dict, ppos, pkey, pvalue): - w_key = from_ref(space, pkey[0]) - w_value = from_ref(space, pvalue[0]) - space.setitem(w_copy, w_key, w_value) - finally: - lltype.free(ppos, flavor='raw') - lltype.free(pkey, flavor='raw') - lltype.free(pvalue, flavor='raw') - - decref(space, py_dict) # release borrowed references - - assert space.eq_w(space.len(w_copy), space.len(w_dict)) - assert space.eq_w(w_copy, w_dict) - - def test_iterkeys(self, space): - w_dict = space.sys.getdict(space) - py_dict = make_ref(space, w_dict) - - ppos = lltype.malloc(Py_ssize_tP.TO, 1, flavor='raw') - pkey = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - pvalue = lltype.malloc(PyObjectP.TO, 1, flavor='raw') - - keys_w = [] - values_w = [] - try: - ppos[0] = 0 - while PyDict_Next(space, w_dict, ppos, pkey, None): - w_key = from_ref(space, pkey[0]) - keys_w.append(w_key) - ppos[0] = 0 - while PyDict_Next(space, w_dict, ppos, None, pvalue): - w_value = from_ref(space, pvalue[0]) - values_w.append(w_value) - finally: - lltype.free(ppos, flavor='raw') - lltype.free(pkey, flavor='raw') - lltype.free(pvalue, flavor='raw') - - decref(space, py_dict) # release borrowed references - - assert space.eq_w(space.newlist(keys_w), - space.call_method(w_dict, "keys")) - assert space.eq_w(space.newlist(values_w), - space.call_method(w_dict, "values")) - def test_dictproxy(self, space): - w_dict = space.sys.get('modules') + w_dict = space.appexec([], """(): return {1: 2, 3: 4}""") w_proxy = PyDictProxy_New(space, w_dict) - assert space.contains_w(w_proxy, space.wrap('sys')) + assert space.contains_w(w_proxy, space.newint(1)) raises(OperationError, space.setitem, - w_proxy, space.wrap('sys'), space.w_None) + w_proxy, space.newint(1), space.w_None) raises(OperationError, space.delitem, - w_proxy, space.wrap('sys')) + w_proxy, space.newint(1)) raises(OperationError, space.call_method, w_proxy, 'clear') assert PyDictProxy_Check(space, w_proxy) @@ -243,6 +187,59 @@ d = {"a": 1} raises(AttributeError, module.update, d, [("c", 2)]) + def test_iter(self): + module = self.import_extension('foo', [ + ("copy", "METH_O", + ''' + Py_ssize_t pos = 0; + PyObject *key, *value; + PyObject* copy = PyDict_New(); + while (PyDict_Next(args, &pos, &key, &value)) + { + if (PyDict_SetItem(copy, key, value) < 0) + { + Py_DecRef(copy); + return NULL; + } + } + return copy; + ''')]) + d = {1: 'xyz', 3: 'abcd'} + copy = module.copy(d) + assert len(copy) == len(d) + assert copy == d + + def test_iterkeys(self): + module = self.import_extension('foo', [ + ("keys_and_values", "METH_O", + ''' + Py_ssize_t pos = 0; + PyObject *key, *value, *values; + PyObject* keys = PyList_New(0); + while (PyDict_Next(args, &pos, &key, NULL)) + { + if (PyList_Append(keys, key) < 0) + { + Py_DecRef(keys); + return NULL; + } + } + pos = 0; + values = PyList_New(0); + while (PyDict_Next(args, &pos, NULL, &value)) + { + if (PyList_Append(values, value) < 0) + { + Py_DecRef(keys); + Py_DecRef(values); + return NULL; + } + } + return Py_BuildValue("(NN)", keys, values); + ''')]) + d = {1: 'xyz', 3: 'abcd'} + assert module.keys_and_values(d) == (d.keys(), d.values()) + def test_typedict2(self): module = self.import_extension('foo', [ ("get_type_dict", "METH_O", @@ -255,6 +252,7 @@ ]) d = module.get_type_dict(1) assert d['real'].__get__(1, 1) == 1 + def test_advanced(self): module = self.import_extension('foo', [ ("dict_len", "METH_O", @@ -266,7 +264,7 @@ ''' int ret; PyObject * dict = PyTuple_GetItem(args, 0); - if (PyTuple_Size(args) < 3 || !dict || + if (PyTuple_Size(args) < 3 || !dict || !dict->ob_type->tp_as_mapping || !dict->ob_type->tp_as_mapping->mp_ass_subscript) return PyLong_FromLong(-1); @@ -279,7 +277,7 @@ ''' int ret; PyObject * dict = PyTuple_GetItem(args, 0); - if (PyTuple_Size(args) < 2 || !dict || + if (PyTuple_Size(args) < 2 || !dict || !dict->ob_type->tp_as_mapping || !dict->ob_type->tp_as_mapping->mp_ass_subscript) return PyLong_FromLong(-1); diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py --- a/pypy/module/cpyext/test/test_eval.py +++ b/pypy/module/cpyext/test/test_eval.py @@ -279,6 +279,7 @@ assert module.call_method("text") == 2 def test_CompileString_and_Exec(self): + import sys module = self.import_extension('foo', [ ("compile_string", "METH_NOARGS", """ @@ -313,6 +314,9 @@ print mod.__dict__ assert mod.f(42) == 47 + # Clean-up + del sys.modules['cpyext_test_modname'] + def test_merge_compiler_flags(self): module = self.import_extension('foo', [ ("get_flags", "METH_NOARGS", @@ -357,4 +361,4 @@ except RuntimeError as e: assert 'while calling recurse' in str(e) else: - assert False, "expected RuntimeError" + assert False, "expected RuntimeError" diff --git a/pypy/module/cpyext/test/test_floatobject.py b/pypy/module/cpyext/test/test_floatobject.py --- a/pypy/module/cpyext/test/test_floatobject.py +++ b/pypy/module/cpyext/test/test_floatobject.py @@ -104,6 +104,7 @@ PyFloatObject* pfo = (PyFloatObject*)pyobj; int res = PyFloat_Check(pyobj) && PyFloat_CheckExact(pyobj) && PyFloat_Check(pfo) && PyFloat_CheckExact(pfo); + Py_DecRef(pyobj); return PyLong_FromLong(res);"""), ]) assert module.test() == 1 diff --git a/pypy/module/cpyext/test/test_funcobject.py b/pypy/module/cpyext/test/test_funcobject.py --- a/pypy/module/cpyext/test/test_funcobject.py +++ b/pypy/module/cpyext/test/test_funcobject.py @@ -46,7 +46,7 @@ w_function = space.appexec([], """(): def func(x, y, z): return x return func - """) + """, cache=False) w_code = PyFunction_GetCode(space, w_function) assert w_code.co_name == "func" @@ -63,7 +63,7 @@ w_code = space.appexec([], """(): def func(%s): %s return func.__code__ - """ % (signature, body)) + """ % (signature, body), cache=False) ref = make_ref(space, w_code) co_flags = rffi.cast(PyCodeObject, ref).c_co_flags decref(space, ref) diff --git a/pypy/module/cpyext/test/test_longobject.py b/pypy/module/cpyext/test/test_longobject.py --- a/pypy/module/cpyext/test/test_longobject.py +++ b/pypy/module/cpyext/test/test_longobject.py @@ -313,6 +313,7 @@ ret = obj->ob_type->tp_as_number->nb_power(obj, one, one); else ret = PyLong_FromLong(-1); + Py_DECREF(one); Py_DECREF(obj); return ret; """), @@ -340,4 +341,3 @@ assert module.has_pow() == 0 assert module.has_hex() == '0x2aL' assert module.has_oct() == '052L' - diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py --- a/pypy/module/cpyext/test/test_memoryobject.py +++ b/pypy/module/cpyext/test/test_memoryobject.py @@ -4,7 +4,7 @@ from pypy.module.cpyext.test.test_api import BaseApiTest from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase from rpython.rlib.buffer import StringBuffer -from pypy.module.cpyext.pyobject import from_ref +from pypy.module.cpyext.pyobject import make_ref, from_ref from pypy.module.cpyext.memoryobject import PyMemoryViewObject only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" @@ -12,9 +12,9 @@ class TestMemoryViewObject(BaseApiTest): def test_frombuffer(self, space, api): w_buf = space.newbuffer(StringBuffer("hello")) + w_memoryview = api.PyMemoryView_FromObject(w_buf) c_memoryview = rffi.cast( - PyMemoryViewObject, api.PyMemoryView_FromObject(w_buf)) - w_memoryview = from_ref(space, c_memoryview) + PyMemoryViewObject, make_ref(space, w_memoryview)) view = c_memoryview.c_view assert view.c_ndim == 1 f = rffi.charp2str(view.c_format) @@ -32,6 +32,7 @@ assert space.eq_w(space.getattr(w_mv, w_f), space.getattr(w_memoryview, w_f)) api.Py_DecRef(ref) + api.Py_DecRef(w_memoryview) class AppTestPyBuffer_FillInfo(AppTestCpythonExtensionBase): def test_fillWithObject(self): @@ -62,7 +63,6 @@ """)]) result = module.fillinfo() assert b"hello, world." == result - del result class AppTestBufferProtocol(AppTestCpythonExtensionBase): def test_fromobject(self): @@ -172,8 +172,6 @@ # in <bound method ConcreteArray.__del__ ...> ignored def test_releasebuffer(self): - if not self.runappdirect: - skip("Fails due to ll2ctypes nonsense") module = self.import_extension('foo', [ ("create_test", "METH_NOARGS", """ diff --git a/pypy/module/cpyext/test/test_traceback.py b/pypy/module/cpyext/test/test_traceback.py --- a/pypy/module/cpyext/test/test_traceback.py +++ b/pypy/module/cpyext/test/test_traceback.py @@ -3,17 +3,19 @@ from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref from pypy.module.cpyext.pytraceback import PyTracebackObject from pypy.interpreter.pytraceback import PyTraceback -from pypy.interpreter.pyframe import PyFrame +from pypy.interpreter.baseobjspace import AppExecCache class TestPyTracebackObject(BaseApiTest): def test_traceback(self, space, api): - w_traceback = space.appexec([], """(): + src = """(): import sys try: 1/0 except: return sys.exc_info()[2] - """) + """ + w_traceback = space.appexec([], src) + py_obj = make_ref(space, w_traceback) py_traceback = rffi.cast(PyTracebackObject, py_obj) assert (from_ref(space, rffi.cast(PyObject, py_traceback.c_ob_type)) is @@ -38,3 +40,5 @@ assert lltype.normalizeptr(py_traceback) is None api.Py_DecRef(py_obj) + # hack to allow the code object to be freed + del space.fromcache(AppExecCache).content[src] diff --git a/pypy/module/cpyext/test/test_tupleobject.py b/pypy/module/cpyext/test/test_tupleobject.py --- a/pypy/module/cpyext/test/test_tupleobject.py +++ b/pypy/module/cpyext/test/test_tupleobject.py @@ -24,6 +24,7 @@ def test_tuple_realize_refuses_nulls(self, space, api): py_tuple = api.PyTuple_New(1) py.test.raises(FatalError, from_ref, space, py_tuple) + api.Py_DecRef(py_tuple) def test_tuple_resize(self, space, api): w_42 = space.wrap(42) @@ -70,6 +71,7 @@ w_tuple = from_ref(space, py_tuple) assert space.eq_w(w_tuple, space.newtuple([space.wrap(42), space.wrap(43)])) + api.Py_DecRef(py_tuple) def test_getslice(self, space, api): w_tuple = space.newtuple([space.wrap(i) for i in range(10)]) @@ -174,6 +176,7 @@ res = PyTuple_SetItem(tuple, 0, one); if (res != 0) { + Py_DECREF(one); Py_DECREF(tuple); return NULL; } @@ -187,14 +190,13 @@ /* Do something that uses the tuple, but does not incref */ t2 = PyTuple_GetSlice(tuple, 0, 1); Py_DECREF(t2); - Py_INCREF(one); res = PyTuple_SetItem(tuple, 0, one); - Py_DECREF(tuple); if (res != 0) { - Py_DECREF(one); + Py_DECREF(tuple); return NULL; } + Py_DECREF(tuple); Py_INCREF(Py_None); return Py_None; """), @@ -205,4 +207,3 @@ raises(SystemError, module.set_after_use, s) else: module.set_after_use(s) - diff --git a/pypy/module/cpyext/tupleobject.py b/pypy/module/cpyext/tupleobject.py --- a/pypy/module/cpyext/tupleobject.py +++ b/pypy/module/cpyext/tupleobject.py @@ -143,6 +143,7 @@ old_ref = tupleobj.c_ob_item[index] if pyobj_has_w_obj(ref): # similar but not quite equal to ref.c_ob_refcnt != 1 on CPython + decref(space, py_obj) raise oefmt(space.w_SystemError, "PyTuple_SetItem called on tuple after" " use of tuple") tupleobj.c_ob_item[index] = py_obj # consumes a reference diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -195,6 +195,8 @@ py_getsetdef = make_GetSet(space, w_obj) assert space.isinstance_w(w_userdata, space.w_type) w_obj = W_GetSetPropertyEx(py_getsetdef, w_userdata) + # now w_obj.getset is py_getsetdef, which was freshly allocated + # XXX how is this ever released? # XXX assign to d_dname, d_type? assert isinstance(w_obj, W_GetSetPropertyEx) py_getsetdescr.c_d_getset = w_obj.getset @@ -922,7 +924,9 @@ bases_w = [] else: bases_w = [from_ref(space, base_pyo)] - pto.c_tp_bases = make_ref(space, space.newtuple(bases_w)) + is_heaptype = bool(pto.c_tp_flags & Py_TPFLAGS_HEAPTYPE) + pto.c_tp_bases = make_ref(space, space.newtuple(bases_w), + immortal=not is_heaptype) def finish_type_2(space, pto, w_obj): """ diff --git a/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py b/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py --- a/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py +++ b/pypy/module/test_lib_pypy/ctypes_tests/test_structures.py @@ -562,3 +562,13 @@ x = X() assert x.x == 0 + + def test_duplicate_names(self): + class S(Structure): + _fields_ = [('a', c_int), + ('b', c_int), + ('a', c_byte)] + s = S(260, -123) + assert sizeof(s) == 3 * sizeof(c_int) + assert s.a == 4 # 256 + 4 + assert s.b == -123 diff --git a/pypy/objspace/std/specialisedtupleobject.py b/pypy/objspace/std/specialisedtupleobject.py --- a/pypy/objspace/std/specialisedtupleobject.py +++ b/pypy/objspace/std/specialisedtupleobject.py @@ -74,15 +74,15 @@ elif typetuple[i] == int: # mimic cpythons behavior of a hash value of -2 for -1 y = value - if y == -1: - y = -2 + y -= (y == -1) # No explicit condition, to avoid JIT bridges elif typetuple[i] == float: # get the correct hash for float which is an # integer & other less frequent cases from pypy.objspace.std.floatobject import _hash_float y = _hash_float(space, value) + y -= (y == -1) else: - y = compute_hash(value) + assert 0, "unreachable" x = (x ^ y) * mult z -= 1 mult += 82520 + z + z diff --git a/pypy/objspace/std/test/test_specialisedtupleobject.py b/pypy/objspace/std/test/test_specialisedtupleobject.py --- a/pypy/objspace/std/test/test_specialisedtupleobject.py +++ b/pypy/objspace/std/test/test_specialisedtupleobject.py @@ -37,6 +37,8 @@ self.space.eq(self.space.hash(N_w_tuple), self.space.hash(S_w_tuple))) + hash_test([-1, -1]) + hash_test([-1.0, -1.0]) hash_test([1, 2]) hash_test([1.5, 2.8]) hash_test([1.0, 2.0]) diff --git a/rpython/jit/metainterp/logger.py b/rpython/jit/metainterp/logger.py --- a/rpython/jit/metainterp/logger.py +++ b/rpython/jit/metainterp/logger.py @@ -13,10 +13,11 @@ self.guard_number = guard_number def log_loop_from_trace(self, trace, memo): + debug_start("jit-log-noopt") if not have_debug_prints(): + debug_stop("jit-log-noopt") return inputargs, ops = self._unpack_trace(trace) - debug_start("jit-log-noopt") debug_print("# Traced loop or bridge with", len(ops), "ops") logops = self._log_operations(inputargs, ops, None, memo) debug_stop("jit-log-noopt") diff --git a/rpython/jit/metainterp/optimizeopt/bridgeopt.py b/rpython/jit/metainterp/optimizeopt/bridgeopt.py --- a/rpython/jit/metainterp/optimizeopt/bridgeopt.py +++ b/rpython/jit/metainterp/optimizeopt/bridgeopt.py @@ -84,7 +84,6 @@ # heap knowledge: we store triples of known heap fields in non-virtual # structs - # XXX could be extended to arrays if optimizer.optheap: triples_struct, triples_array = optimizer.optheap.serialize_optheap(available_boxes) # can only encode descrs that have a known index into diff --git a/rpython/rtyper/lltypesystem/lltype.py b/rpython/rtyper/lltypesystem/lltype.py --- a/rpython/rtyper/lltypesystem/lltype.py +++ b/rpython/rtyper/lltypesystem/lltype.py @@ -2208,7 +2208,7 @@ return _ptr(Ptr(T), o, solid) @analyzer_for(malloc) -def ann_malloc(s_T, s_n=None, s_flavor=None, s_zero=None, +def ann_malloc(s_T, s_n=None, s_flavor=None, s_immortal=None, s_zero=None, s_track_allocation=None, s_add_memory_pressure=None, s_nonmovable=None): assert (s_n is None or s_n.knowntype == int diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py --- a/rpython/rtyper/rbuiltin.py +++ b/rpython/rtyper/rbuiltin.py @@ -347,19 +347,20 @@ # annotation of low-level types @typer_for(lltype.malloc) -def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None, - i_add_memory_pressure=None, i_nonmovable=None): +def rtype_malloc(hop, i_flavor=None, i_immortal=None, i_zero=None, + i_track_allocation=None, i_add_memory_pressure=None, i_nonmovable=None): assert hop.args_s[0].is_constant() vlist = [hop.inputarg(lltype.Void, arg=0)] opname = 'malloc' kwds_v = parse_kwds( hop, (i_flavor, lltype.Void), + (i_immortal, None), (i_zero, None), (i_track_allocation, None), (i_add_memory_pressure, None), (i_nonmovable, None)) - (v_flavor, v_zero, v_track_allocation, + (v_flavor, v_immortal, v_zero, v_track_allocation, v_add_memory_pressure, v_nonmovable) = kwds_v flags = {'flavor': 'gc'} if v_flavor is not None: _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit