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

Reply via email to