]> granicus.if.org Git - vim/commitdiff
patch 8.2.1538: Python: iteration over vim objects fails to keep reference v8.2.1538
authorBram Moolenaar <Bram@vim.org>
Sat, 29 Aug 2020 10:57:16 +0000 (12:57 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 29 Aug 2020 10:57:16 +0000 (12:57 +0200)
Problem:    Python: iteration over vim objects fails to keep reference.
Solution:   Keep a reference for the object. (Paul Ollis, closes #6803,
            closes #6806)

src/if_py_both.h
src/testdir/test_python3.vim
src/version.c

index 8eb77470a3d6b959986197ae1305f4c59fe541e1..86942b686c691dd14746dd8d4e52dc32d84caea3 100644 (file)
@@ -1442,11 +1442,12 @@ typedef struct
     destructorfun destruct;
     traversefun traverse;
     clearfun clear;
+    PyObject *iter_object;
 } IterObject;
 
     static PyObject *
 IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
-       clearfun clear)
+       clearfun clear, PyObject *iter_object)
 {
     IterObject *self;
 
@@ -1456,6 +1457,10 @@ IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
     self->destruct = destruct;
     self->traverse = traverse;
     self->clear = clear;
+    self->iter_object = iter_object;
+
+    if (iter_object)
+       Py_INCREF(iter_object);
 
     return (PyObject *)(self);
 }
@@ -1463,6 +1468,8 @@ IterNew(void *start, destructorfun destruct, nextfun next, traversefun traverse,
     static void
 IterDestructor(IterObject *self)
 {
+    if (self->iter_object)
+       Py_DECREF(self->iter_object);
     PyObject_GC_UnTrack((void *)(self));
     self->destruct(self->cur);
     PyObject_GC_Del((void *)(self));
@@ -1844,7 +1851,7 @@ DictionaryIter(DictionaryObject *self)
 
     return IterNew(dii,
            (destructorfun) PyMem_Free, (nextfun) DictionaryIterNext,
-           NULL, NULL);
+           NULL, NULL, (PyObject *)self);
 }
 
     static PyInt
@@ -2842,7 +2849,7 @@ ListIter(ListObject *self)
 
     return IterNew(lii,
            (destructorfun) ListIterDestruct, (nextfun) ListIterNext,
-           NULL, NULL);
+           NULL, NULL, (PyObject *)self);
 }
 
 static char *ListAttrs[] = {
@@ -3491,7 +3498,7 @@ OptionsIter(OptionsObject *self)
 
     return IterNew(oii,
            (destructorfun) PyMem_Free, (nextfun) OptionsIterNext,
-           NULL, NULL);
+           NULL, NULL, (PyObject *)self);
 }
 
     static int
@@ -5488,14 +5495,15 @@ BufMapIterNext(PyObject **buffer)
 }
 
     static PyObject *
-BufMapIter(PyObject *self UNUSED)
+BufMapIter(PyObject *self)
 {
     PyObject *buffer;
 
     buffer = BufferNew(firstbuf);
     return IterNew(buffer,
            (destructorfun) BufMapIterDestruct, (nextfun) BufMapIterNext,
-           (traversefun) BufMapIterTraverse, (clearfun) BufMapIterClear);
+           (traversefun) BufMapIterTraverse, (clearfun) BufMapIterClear,
+           (PyObject *)self);
 }
 
 static PyMappingMethods BufMapAsMapping = {
index 830fbba50cd518a81faa0144c6559192e616e7c4..0885c96988fbedb4ec037f7e97bc98399df6c6b4 100644 (file)
@@ -4,6 +4,15 @@ source check.vim
 CheckFeature python3
 source shared.vim
 
+func Create_vim_list()
+  return [1]
+endfunction
+
+func Create_vim_dict()
+  return {'a': 1}
+endfunction
+
+
 " This function should be called first. This sets up python functions used by
 " the other tests.
 func Test_AAA_python3_setup()
@@ -3944,4 +3953,47 @@ func Test_python3_keyboard_interrupt()
   close!
 endfunc
 
+" Regression: Iterator for a Vim object should hold a reference.
+func Test_python3_iter_ref()
+  let g:list_iter_ref_count_increase = -1
+  let g:dict_iter_ref_count_increase = -1
+  let g:bufmap_iter_ref_count_increase = -1
+  let g:options_iter_ref_count_increase = -1
+
+  py3 << trim EOF
+    import sys
+    import vim
+
+    def test_python3_iter_ref():
+      create_list = vim.Function('Create_vim_list')
+      v = create_list()
+      base_ref_count = sys.getrefcount(v)
+      for el in v:
+          vim.vars['list_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+      create_dict = vim.Function('Create_vim_dict')
+      v = create_dict()
+      base_ref_count = sys.getrefcount(v)
+      for el in v:
+          vim.vars['dict_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+      v = vim.buffers
+      base_ref_count = sys.getrefcount(v)
+      for el in v:
+          vim.vars['bufmap_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+      v = vim.options
+      base_ref_count = sys.getrefcount(v)
+      for el in v:
+          vim.vars['options_iter_ref_count_increase'] = sys.getrefcount(v) - base_ref_count
+
+    test_python3_iter_ref()
+  EOF
+
+  call assert_equal(1, g:list_iter_ref_count_increase)
+  call assert_equal(1, g:dict_iter_ref_count_increase)
+  call assert_equal(1, g:bufmap_iter_ref_count_increase)
+  call assert_equal(1, g:options_iter_ref_count_increase)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index d4d525bbdde5bf654c79c91dda0253454819a963..9e3430a10655da69cfadff1f2b74b7edd89eb43c 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1538,
 /**/
     1537,
 /**/