]> granicus.if.org Git - vim/commitdiff
updated for version 7.4.609 v7.4.609
authorBram Moolenaar <Bram@vim.org>
Tue, 3 Feb 2015 11:55:18 +0000 (12:55 +0100)
committerBram Moolenaar <Bram@vim.org>
Tue, 3 Feb 2015 11:55:18 +0000 (12:55 +0100)
Problem:    For complicated list and dict use the garbage collector can run
            out of stack space.
Solution:   Use a stack of dicts and lists to be marked, thus making it
            iterative instead of recursive. (Ben Fritz)

src/eval.c
src/if_lua.c
src/if_py_both.h
src/if_python.c
src/if_python3.c
src/proto/eval.pro
src/proto/if_lua.pro
src/proto/if_python.pro
src/proto/if_python3.pro
src/structs.h
src/version.c

index c9c179a051d6ca9a274d1eeecc6335b4f7bba20e..6d5d762f27e7e67a3ae1ddaf4249087bf77af8ce 100644 (file)
@@ -93,7 +93,6 @@ typedef struct lval_S
     char_u     *ll_newkey;     /* New key for Dict in alloc. mem or NULL. */
 } lval_T;
 
-
 static char *e_letunexp        = N_("E18: Unexpected characters in :let");
 static char *e_listidx = N_("E684: list index out of range: %ld");
 static char *e_undefvar = N_("E121: Undefined variable: %s");
@@ -6811,6 +6810,7 @@ list_join(gap, l, sep, echo_style, copyID)
 garbage_collect()
 {
     int                copyID;
+    int                abort = FALSE;
     buf_T      *buf;
     win_T      *wp;
     int                i;
@@ -6841,82 +6841,95 @@ garbage_collect()
      * the item is referenced elsewhere the funccal must not be freed. */
     for (fc = previous_funccal; fc != NULL; fc = fc->caller)
     {
-       set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1);
-       set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1);
+       abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1,
+                                                                       NULL);
+       abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1,
+                                                                       NULL);
     }
 
     /* script-local variables */
     for (i = 1; i <= ga_scripts.ga_len; ++i)
-       set_ref_in_ht(&SCRIPT_VARS(i), copyID);
+       abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL);
 
     /* buffer-local variables */
     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
-       set_ref_in_item(&buf->b_bufvar.di_tv, copyID);
+       abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID,
+                                                                 NULL, NULL);
 
     /* window-local variables */
     FOR_ALL_TAB_WINDOWS(tp, wp)
-       set_ref_in_item(&wp->w_winvar.di_tv, copyID);
+       abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
+                                                                 NULL, NULL);
 #ifdef FEAT_AUTOCMD
     if (aucmd_win != NULL)
-       set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID);
+       abort = abort || set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID,
+                                                                 NULL, NULL);
 #endif
 
 #ifdef FEAT_WINDOWS
     /* tabpage-local variables */
     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
-       set_ref_in_item(&tp->tp_winvar.di_tv, copyID);
+       abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID,
+                                                                 NULL, NULL);
 #endif
 
     /* global variables */
-    set_ref_in_ht(&globvarht, copyID);
+    abort = abort || set_ref_in_ht(&globvarht, copyID, NULL);
 
     /* function-local variables */
     for (fc = current_funccal; fc != NULL; fc = fc->caller)
     {
-       set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID);
-       set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID);
+       abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL);
+       abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL);
     }
 
     /* v: vars */
-    set_ref_in_ht(&vimvarht, copyID);
+    abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL);
 
 #ifdef FEAT_LUA
-    set_ref_in_lua(copyID);
+    abort = abort || set_ref_in_lua(copyID);
 #endif
 
 #ifdef FEAT_PYTHON
-    set_ref_in_python(copyID);
+    abort = abort || set_ref_in_python(copyID);
 #endif
 
 #ifdef FEAT_PYTHON3
-    set_ref_in_python3(copyID);
+    abort = abort || set_ref_in_python3(copyID);
 #endif
 
-    /*
-     * 2. Free lists and dictionaries that are not referenced.
-     */
-    did_free = free_unref_items(copyID);
-
-    /*
-     * 3. Check if any funccal can be freed now.
-     */
-    for (pfc = &previous_funccal; *pfc != NULL; )
+    if (!abort)
     {
-       if (can_free_funccal(*pfc, copyID))
+       /*
+        * 2. Free lists and dictionaries that are not referenced.
+        */
+       did_free = free_unref_items(copyID);
+
+       /*
+        * 3. Check if any funccal can be freed now.
+        */
+       for (pfc = &previous_funccal; *pfc != NULL; )
        {
-           fc = *pfc;
-           *pfc = fc->caller;
-           free_funccal(fc, TRUE);
-           did_free = TRUE;
-           did_free_funccal = TRUE;
+           if (can_free_funccal(*pfc, copyID))
+           {
+               fc = *pfc;
+               *pfc = fc->caller;
+               free_funccal(fc, TRUE);
+               did_free = TRUE;
+               did_free_funccal = TRUE;
+           }
+           else
+               pfc = &(*pfc)->caller;
        }
-       else
-           pfc = &(*pfc)->caller;
+       if (did_free_funccal)
+           /* When a funccal was freed some more items might be garbage
+            * collected, so run again. */
+           (void)garbage_collect();
+    }
+    else if (p_verbose > 0)
+    {
+       verb_msg((char_u *)_("Not enough memory to set references, garbage collection aborted!"));
     }
-    if (did_free_funccal)
-       /* When a funccal was freed some more items might be garbage
-        * collected, so run again. */
-       (void)garbage_collect();
 
     return did_free;
 }
@@ -6976,48 +6989,112 @@ free_unref_items(copyID)
 
 /*
  * Mark all lists and dicts referenced through hashtab "ht" with "copyID".
+ * "list_stack" is used to add lists to be marked.  Can be NULL.
+ *
+ * Returns TRUE if setting references failed somehow.
  */
-    void
-set_ref_in_ht(ht, copyID)
-    hashtab_T  *ht;
-    int                copyID;
+    int
+set_ref_in_ht(ht, copyID, list_stack)
+    hashtab_T      *ht;
+    int                    copyID;
+    list_stack_T    **list_stack;
 {
     int                todo;
+    int                abort = FALSE;
     hashitem_T *hi;
+    hashtab_T  *cur_ht;
+    ht_stack_T *ht_stack = NULL;
+    ht_stack_T *tempitem;
 
-    todo = (int)ht->ht_used;
-    for (hi = ht->ht_array; todo > 0; ++hi)
-       if (!HASHITEM_EMPTY(hi))
+    cur_ht = ht;
+    for (;;)
+    {
+       if (!abort)
        {
-           --todo;
-           set_ref_in_item(&HI2DI(hi)->di_tv, copyID);
+           /* Mark each item in the hashtab.  If the item contains a hashtab
+            * it is added to ht_stack, if it contains a list it is added to
+            * list_stack. */
+           todo = (int)cur_ht->ht_used;
+           for (hi = cur_ht->ht_array; todo > 0; ++hi)
+               if (!HASHITEM_EMPTY(hi))
+               {
+                   --todo;
+                   abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
+                                                      &ht_stack, list_stack);
+               }
        }
+
+       if (ht_stack == NULL)
+           break;
+
+       /* take an item from the stack */
+       cur_ht = ht_stack->ht;
+       tempitem = ht_stack;
+       ht_stack = ht_stack->prev;
+       free(tempitem);
+    }
+
+    return abort;
 }
 
 /*
  * Mark all lists and dicts referenced through list "l" with "copyID".
+ * "ht_stack" is used to add hashtabs to be marked.  Can be NULL.
+ *
+ * Returns TRUE if setting references failed somehow.
  */
-    void
-set_ref_in_list(l, copyID)
+    int
+set_ref_in_list(l, copyID, ht_stack)
     list_T     *l;
     int                copyID;
+    ht_stack_T **ht_stack;
 {
-    listitem_T *li;
+    listitem_T  *li;
+    int                 abort = FALSE;
+    list_T      *cur_l;
+    list_stack_T *list_stack = NULL;
+    list_stack_T *tempitem;
 
-    for (li = l->lv_first; li != NULL; li = li->li_next)
-       set_ref_in_item(&li->li_tv, copyID);
+    cur_l = l;
+    for (;;)
+    {
+       if (!abort)
+           /* Mark each item in the list.  If the item contains a hashtab
+            * it is added to ht_stack, if it contains a list it is added to
+            * list_stack. */
+           for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next)
+               abort = abort || set_ref_in_item(&li->li_tv, copyID,
+                                                      ht_stack, &list_stack);
+       if (list_stack == NULL)
+           break;
+
+       /* take an item from the stack */
+       cur_l = list_stack->list;
+       tempitem = list_stack;
+       list_stack = list_stack->prev;
+       free(tempitem);
+    }
+
+    return abort;
 }
 
 /*
  * Mark all lists and dicts referenced through typval "tv" with "copyID".
+ * "list_stack" is used to add lists to be marked.  Can be NULL.
+ * "ht_stack" is used to add hashtabs to be marked.  Can be NULL.
+ *
+ * Returns TRUE if setting references failed somehow.
  */
-    void
-set_ref_in_item(tv, copyID)
-    typval_T   *tv;
-    int                copyID;
+    int
+set_ref_in_item(tv, copyID, ht_stack, list_stack)
+    typval_T       *tv;
+    int                    copyID;
+    ht_stack_T     **ht_stack;
+    list_stack_T    **list_stack;
 {
     dict_T     *dd;
     list_T     *ll;
+    int                abort = FALSE;
 
     switch (tv->v_type)
     {
@@ -7027,7 +7104,23 @@ set_ref_in_item(tv, copyID)
            {
                /* Didn't see this dict yet. */
                dd->dv_copyID = copyID;
-               set_ref_in_ht(&dd->dv_hashtab, copyID);
+               if (ht_stack == NULL)
+               {
+                   abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
+               }
+               else
+               {
+                   ht_stack_T *newitem = (ht_stack_T*)malloc(
+                                                         sizeof(ht_stack_T));
+                   if (newitem == NULL)
+                       abort = TRUE;
+                   else
+                   {
+                       newitem->ht = &dd->dv_hashtab;
+                       newitem->prev = *ht_stack;
+                       *ht_stack = newitem;
+                   }
+               }
            }
            break;
 
@@ -7037,11 +7130,27 @@ set_ref_in_item(tv, copyID)
            {
                /* Didn't see this list yet. */
                ll->lv_copyID = copyID;
-               set_ref_in_list(ll, copyID);
+               if (list_stack == NULL)
+               {
+                   abort = set_ref_in_list(ll, copyID, ht_stack);
+               }
+               else
+               {
+                   list_stack_T *newitem = (list_stack_T*)malloc(
+                                                       sizeof(list_stack_T));
+                   if (newitem == NULL)
+                       abort = TRUE;
+                   else
+                   {
+                       newitem->list = ll;
+                       newitem->prev = *list_stack;
+                       *list_stack = newitem;
+                   }
+               }
            }
            break;
     }
-    return;
+    return abort;
 }
 
 /*
index 7efd2cba651fbbe816875cb0b9e9859e1834d733..148d624203084c954230de740cfd645b35959382 100644 (file)
@@ -1523,12 +1523,14 @@ luaV_luaeval (lua_State *L)
     static int
 luaV_setref (lua_State *L)
 {
-    int copyID = lua_tointeger(L, 1);
-    typval_T tv;
+    int                copyID = lua_tointeger(L, 1);
+    int                abort = FALSE;
+    typval_T   tv;
+
     luaV_getfield(L, LUAVIM_LIST);
     luaV_getfield(L, LUAVIM_DICT);
     lua_pushnil(L);
-    while (lua_next(L, lua_upvalueindex(1)) != 0) /* traverse cache table */
+    while (!abort && lua_next(L, lua_upvalueindex(1)) != 0) /* traverse cache table */
     {
        lua_getmetatable(L, -1);
        if (lua_rawequal(L, -1, 2)) /* list? */
@@ -1542,9 +1544,9 @@ luaV_setref (lua_State *L)
            tv.vval.v_dict = (dict_T *) lua_touserdata(L, 4); /* key */
        }
        lua_pop(L, 2); /* metatable and value */
-       set_ref_in_item(&tv, copyID);
+       abort = set_ref_in_item(&tv, copyID, NULL, NULL);
     }
-    return 0;
+    lua_pushinteger(L, abort);
 }
 
     static int
@@ -1770,13 +1772,23 @@ do_luaeval (char_u *str, typval_T *arg, typval_T *rettv)
     lua_call(L, 3, 0);
 }
 
-    void
+    int
 set_ref_in_lua (int copyID)
 {
-    if (!lua_isopen()) return;
-    luaV_getfield(L, LUAVIM_SETREF);
-    lua_pushinteger(L, copyID);
-    lua_call(L, 1, 0);
+    int aborted = 0;
+
+    if (lua_isopen())
+    {
+       luaV_getfield(L, LUAVIM_SETREF);
+       /* call the function with 1 arg, getting 1 result back */
+       lua_pushinteger(L, copyID);
+       lua_call(L, 1, 1);
+       /* get the result */
+       aborted = lua_tointeger(L, -1);
+       /* pop result off the stack */
+       lua_pop(L, 1);
+    }
+    return aborted;
 }
 
 #endif
index 206d2987a5ae7ad8438197075525d60a73f0de95..a46b42adda25b60d4ea01089c0ecec0029c22b66 100644 (file)
@@ -5502,34 +5502,41 @@ run_eval(const char *cmd, typval_T *rettv
     PyErr_Clear();
 }
 
-    static void
+    static int
 set_ref_in_py(const int copyID)
 {
     pylinkedlist_T     *cur;
     dict_T     *dd;
     list_T     *ll;
+    int                abort = FALSE;
 
     if (lastdict != NULL)
-       for(cur = lastdict ; cur != NULL ; cur = cur->pll_prev)
+    {
+       for(cur = lastdict ; !abort && cur != NULL ; cur = cur->pll_prev)
        {
            dd = ((DictionaryObject *) (cur->pll_obj))->dict;
            if (dd->dv_copyID != copyID)
            {
                dd->dv_copyID = copyID;
-               set_ref_in_ht(&dd->dv_hashtab, copyID);
+               abort = abort || set_ref_in_ht(&dd->dv_hashtab, copyID, NULL);
            }
        }
+    }
 
     if (lastlist != NULL)
-       for(cur = lastlist ; cur != NULL ; cur = cur->pll_prev)
+    {
+       for(cur = lastlist ; !abort && cur != NULL ; cur = cur->pll_prev)
        {
            ll = ((ListObject *) (cur->pll_obj))->list;
            if (ll->lv_copyID != copyID)
            {
                ll->lv_copyID = copyID;
-               set_ref_in_list(ll, copyID);
+               abort = abort || set_ref_in_list(ll, copyID, NULL);
            }
        }
+    }
+
+    return abort;
 }
 
     static int
index b7bfa785caf421b921f57418f0ca3a2b583c874a..2ef0dfc9ac8b5b4332dc45de24be9971aadf315d 100644 (file)
@@ -1567,8 +1567,8 @@ Py_GetProgramName(void)
 }
 #endif /* Python 1.4 */
 
-    void
+    int
 set_ref_in_python (int copyID)
 {
-    set_ref_in_py(copyID);
+    return set_ref_in_py(copyID);
 }
index 374641d82e37e29ca0d7198c707f4b8462386642..300c24f6695344f72142b1d37a4fee9a08370d59 100644 (file)
@@ -1649,8 +1649,8 @@ do_py3eval (char_u *str, typval_T *rettv)
     }
 }
 
-    void
+    int
 set_ref_in_python3 (int copyID)
 {
-    set_ref_in_py(copyID);
+    int set_ref_in_py(copyID);
 }
index b264365fe1cce518fb0be1e244d363dbc2105fd7..30416fb8782a55265cfb77c31f95bf97b0cde38f 100644 (file)
@@ -62,9 +62,9 @@ int list_insert_tv __ARGS((list_T *l, typval_T *tv, listitem_T *item));
 void list_insert __ARGS((list_T *l, listitem_T *ni, listitem_T *item));
 void vimlist_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2));
 int garbage_collect __ARGS((void));
-void set_ref_in_ht __ARGS((hashtab_T *ht, int copyID));
-void set_ref_in_list __ARGS((list_T *l, int copyID));
-void set_ref_in_item __ARGS((typval_T *tv, int copyID));
+int set_ref_in_ht __ARGS((hashtab_T *ht, int copyID, list_stack_T **list_stack));
+int set_ref_in_list __ARGS((list_T *l, int copyID, ht_stack_T **ht_stack));
+int set_ref_in_item __ARGS((typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack));
 dict_T *dict_alloc __ARGS((void));
 void dict_unref __ARGS((dict_T *d));
 void dict_free __ARGS((dict_T *d, int recurse));
index 8db66a693e9526a48a0dcebf0214a1e02d270bcb..10395d577789a869566a2dd5ec7cd9644a65d6cb 100644 (file)
@@ -7,5 +7,5 @@ void ex_luafile __ARGS((exarg_T *eap));
 void lua_buffer_free __ARGS((buf_T *buf));
 void lua_window_free __ARGS((win_T *win));
 void do_luaeval __ARGS((char_u *str, typval_T *arg, typval_T *rettv));
-void set_ref_in_lua __ARGS((int copyID));
+int set_ref_in_lua __ARGS((int copyID));
 /* vim: set ft=c : */
index 369a5d895577bbdb9792fa3dba3df2cad23c0f7a..be206e4f30f0027863448c61ab4e9e4ec3ebe0ba 100644 (file)
@@ -9,5 +9,5 @@ void python_buffer_free __ARGS((buf_T *buf));
 void python_window_free __ARGS((win_T *win));
 void python_tabpage_free __ARGS((tabpage_T *tab));
 void do_pyeval __ARGS((char_u *str, typval_T *rettv));
-void set_ref_in_python __ARGS((int copyID));
+int set_ref_in_python __ARGS((int copyID));
 /* vim: set ft=c : */
index 1e2a522fbbaabd2f226275eae29d79e9c1bfc626..8d0489c1f79fcb2597d21fed7fdc08d9997fa19d 100644 (file)
@@ -9,5 +9,5 @@ void python3_buffer_free __ARGS((buf_T *buf));
 void python3_window_free __ARGS((win_T *win));
 void python3_tabpage_free __ARGS((tabpage_T *tab));
 void do_py3eval __ARGS((char_u *str, typval_T *rettv));
-void set_ref_in_python3 __ARGS((int copyID));
+int set_ref_in_python3 __ARGS((int copyID));
 /* vim: set ft=c : */
index 7e60a387a2e55b95e2f1698f65dc3e74ff381384..60bc870eb3e65539dd59f5212663b26296ecb8cb 100644 (file)
@@ -1223,6 +1223,20 @@ struct dictvar_S
     dict_T     *dv_used_prev;  /* previous dict in used dicts list */
 };
 
+/* structure used for explicit stack while garbage collecting hash tables */
+typedef struct ht_stack_S
+{
+    hashtab_T          *ht;
+    struct ht_stack_S  *prev;
+} ht_stack_T;
+
+/* structure used for explicit stack while garbage collecting lists */
+typedef struct list_stack_S
+{
+    list_T             *list;
+    struct list_stack_S        *prev;
+} list_stack_T;
+
 /* values for b_syn_spell: what to do with toplevel text */
 #define SYNSPL_DEFAULT 0       /* spell check if @Spell not defined */
 #define SYNSPL_TOP     1       /* spell check toplevel text */
index 8e264398158f793007db00b67b601d3aa98d3d55..36d701e8da6075328dddba9be6bfa20120420b80 100644 (file)
@@ -741,6 +741,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    609,
 /**/
     608,
 /**/