]> granicus.if.org Git - vim/commitdiff
updated for version 7.0094 v7.0094
authorBram Moolenaar <Bram@vim.org>
Sun, 26 Jun 2005 22:34:35 +0000 (22:34 +0000)
committerBram Moolenaar <Bram@vim.org>
Sun, 26 Jun 2005 22:34:35 +0000 (22:34 +0000)
runtime/doc/repeat.txt
runtime/doc/todo.txt
src/eval.c
src/ex_cmds.h
src/ex_cmds2.c

index c08f123032494976df77910ee6ba677b1cd158d7..20c3673becf256155e61bc861749c8357929cbf9 100644 (file)
@@ -1,4 +1,4 @@
-*repeat.txt*    For Vim version 7.0aa.  Last change: 2005 Jun 25
+*repeat.txt*    For Vim version 7.0aa.  Last change: 2005 Jun 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -579,6 +579,11 @@ It is only included when Vim was compiled with "huge" features.
                this command.
 
 
+:profd[el] ...                                         *:profd* *:profdel*
+               Stop profiling for the arguments specified. See |:breakdel|
+               for the arguments.
+
+
 You must always start with a ":profile start fname" command.  The resulting
 file is written when Vim exits.  Here is an example of the output, with line
 numbers prepended for the explanation:
index f9a221754ca38ae5cd26864cfa52c692a05ccc30..b41ba4945825964589fd6ff646cacf53228b4691 100644 (file)
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jun 25
+*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jun 26
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -30,6 +30,9 @@ be worked on, but only if you sponsor Vim development.  See |sponsor|.
                                                        *known-bugs*
 -------------------- Known bugs and current work -----------------------
 
+Is the fix for memory leaks in unreferencing dict/list correct?  Is there a
+situation where a reference from outside of the structure is not counted?
+
 Add extra list of file locations.  Can be used with:
     :ltag            list of matching tags, like :tselect
 
@@ -116,7 +119,7 @@ PLANNED FOR VERSION 7.0:
          - Simple and fast sound-a-like: mapping list for first char and rest
                    vowel as first char: *
                    remove other vowels
-       - Cleanup spell help.
+       - Proofread and cleanup spell help.
     - Use "engspchk" from Charles Campbell for ideas (commands, rare words).
     - Make "en-rare" spell file?  Ask Charles Campbell.
     - References: MySpell library (in OpenOffice.org).
index f3e31e2812ebfbaa880bdd2bd9722123e73bb660..b56c34163c344dd2dfaffc82abbf1122ca91e3cd 100644 (file)
@@ -122,6 +122,12 @@ static dictitem_T  globvars_var;
  */
 static hashtab_T       compat_hashtab;
 
+/*
+ * When recursively copying lists and dicts we need to remember which ones we
+ * have done to avoid endless recursiveness.  This unique ID is used for that.
+ */
+static int current_copyID = 0;
+
 /*
  * Array to hold the hashtab with variables local to each sourced script.
  * Each item holds a variable (nameless) that points to the dict_T.
@@ -185,6 +191,8 @@ struct ufunc
 #define FC_RANGE    2          /* function accepts range */
 #define FC_DICT            4           /* Dict function, uses "self" */
 
+#define DEL_REFCOUNT   999999  /* list/dict is being deleted */
+
 /*
  * All user-defined functions are found in this hash table.
  */
@@ -374,6 +382,11 @@ static void list_remove __ARGS((list_T *l, listitem_T *item, listitem_T *item2))
 static char_u *list2string __ARGS((typval_T *tv));
 static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo));
 
+static int count_self_ref __ARGS((void *p, int type));
+static int count_ref_in_dict __ARGS((dict_T *d, void *rp, int copyID, garray_T *gap));
+static int count_ref_in_list __ARGS((list_T *l, void *rp, int copyID, garray_T *gap));
+static int count_ref_item __ARGS((typval_T *tv, void *rp, int copyID, garray_T *gap));
+
 static void dict_unref __ARGS((dict_T *d));
 static void dict_free __ARGS((dict_T *d));
 static dictitem_T *dictitem_alloc __ARGS((char_u *key));
@@ -719,7 +732,10 @@ eval_clear()
     {
        p = &vimvars[i];
        if (p->vv_di.di_tv.v_type == VAR_STRING)
+       {
            vim_free(p->vv_di.di_tv.vval.v_string);
+           p->vv_di.di_tv.vval.v_string = NULL;
+       }
     }
     hash_clear(&vimvarht);
     hash_clear(&compat_hashtab);
@@ -728,9 +744,12 @@ eval_clear()
     for (i = 1; i <= ga_scripts.ga_len; ++i)
        vars_clear(&SCRIPT_VARS(i));
     ga_clear(&ga_scripts);
+    free_scriptnames();
 
     /* global variables */
     vars_clear(&globvarht);
+
+    free_all_functions();
 }
 #endif
 
@@ -4937,8 +4956,23 @@ list_alloc()
 list_unref(l)
     list_T *l;
 {
-    if (l != NULL && --l->lv_refcount <= 0)
-       list_free(l);
+    int                selfref;
+
+    if (l != NULL && l->lv_refcount != DEL_REFCOUNT)
+    {
+       if (--l->lv_refcount > 0)
+       {
+           /* Check if the dict contains references to itself.  These need to
+            * be subtracted from the reference count to find out if we can
+            * delete the dict. */
+           selfref = count_self_ref(l, VAR_LIST);
+       }
+       else
+           selfref = 0;
+       if (l->lv_refcount - selfref == 0)
+           /* No references to the list now, free it. */
+           list_free(l);
+    }
 }
 
 /*
@@ -4950,11 +4984,14 @@ list_free(l)
     list_T *l;
 {
     listitem_T *item;
-    listitem_T *next;
 
-    for (item = l->lv_first; item != NULL; item = next)
+    /* Avoid that recursive reference to the list frees us again. */
+    l->lv_refcount = DEL_REFCOUNT;
+
+    for (item = l->lv_first; item != NULL; item = l->lv_first)
     {
-       next = item->li_next;
+       /* Remove the item before deleting it. */
+       l->lv_first = item->li_next;
        listitem_free(item);
     }
     vim_free(l);
@@ -5530,6 +5567,160 @@ list_join(gap, l, sep, echo)
     return OK;
 }
 
+/*
+ * Count the number of references for list/dict "p" inside itself.
+ * This is used to find out if there are no more references elsewhere.
+ * The tricky bit is that we must not count references in lists/dicts that are
+ * used elsewhere, but we can only know by counting their references...
+ * This is a bit slow, but required to avoid leaking memory.
+ */
+    static int
+count_self_ref(p, type)
+    void       *p;
+    int                type;
+{
+    garray_T   ga;
+    typval_T   *tv;
+    int                selfref;
+    int                i;
+    int                n;
+
+    ga_init2(&ga, sizeof(typval_T *), 10);
+    if (type == VAR_DICT)
+       selfref = count_ref_in_dict(p, p, ++current_copyID, &ga);
+    else
+       selfref = count_ref_in_list(p, p, ++current_copyID, &ga);
+    for (i = 0; i < ga.ga_len; ++i)
+    {
+       tv = ((typval_T **)ga.ga_data)[i];
+       if (tv->v_type == VAR_DICT)
+       {
+           n = count_ref_in_dict(tv->vval.v_dict, tv->vval.v_dict,
+                                                     ++current_copyID, NULL);
+           if (n < tv->vval.v_dict->dv_refcount)
+           {
+               selfref = 0;
+               break;
+           }
+       }
+       else
+       {
+           n = count_ref_in_list(tv->vval.v_list, tv->vval.v_list,
+                                                     ++current_copyID, NULL);
+           if (n < tv->vval.v_list->lv_refcount)
+           {
+               selfref = 0;
+               break;
+           }
+       }
+    }
+
+    ga_clear(&ga);
+    return selfref;
+}
+
+/*
+ * Count number of references to "rp" in dictionary "d" and its members.
+ * We use "copyID" to avoid recursing into the same list/dict twice.
+ */
+    static int
+count_ref_in_dict(d, rp, copyID, gap)
+    dict_T     *d;
+    void       *rp;
+    int                copyID;
+    garray_T   *gap;
+{
+    int                todo;
+    hashitem_T *hi;
+    int                n = 0;
+
+    todo = d->dv_hashtab.ht_used;
+    for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
+       if (!HASHITEM_EMPTY(hi))
+       {
+           --todo;
+           n += count_ref_item(&HI2DI(hi)->di_tv, rp, copyID, gap);
+       }
+    return n;
+}
+
+/*
+ * Count number of references to "rp" in list "l" and its members.
+ * We use "copyID" to avoid recursing into the same list/dict twice.
+ */
+    static int
+count_ref_in_list(l, rp, copyID, gap)
+    list_T     *l;
+    void       *rp;
+    int                copyID;
+    garray_T   *gap;
+{
+    listitem_T *li;
+    int                n = 0;
+
+    for (li = l->lv_first; li != NULL; li = li->li_next)
+       n += count_ref_item(&li->li_tv, rp, copyID, gap);
+    return n;
+}
+
+/*
+ * Count number of references to "rp" in item "tv" and any members.
+ * We use "copyID" to avoid recursing into the same list/dict twice.
+ * When "gap" is not NULL store items that require checking for only
+ * references inside the structure.
+ */
+    static int
+count_ref_item(tv, rp, copyID, gap)
+    typval_T   *tv;
+    void       *rp;
+    int                copyID;
+    garray_T   *gap;
+{
+    dict_T     *dd;
+    list_T     *ll;
+    int                n;
+
+    switch (tv->v_type)
+    {
+       case VAR_DICT:
+           dd = tv->vval.v_dict;
+           if (dd == rp)
+               return 1;       /* match, count it */
+           if (dd->dv_copyID == copyID)
+               return 0;       /* already inspected this dict */
+           dd->dv_copyID = copyID;
+           n = count_ref_in_dict(dd, rp, copyID, gap);
+           if (n > 0 && gap != NULL && dd->dv_refcount > 1)
+           {
+               /* We must later check that the references to this dict are
+                * all in the structure we are freeing. */
+               if (ga_grow(gap, 1) == FAIL)
+                   return 0;
+               ((typval_T **)gap->ga_data)[gap->ga_len++] = tv;
+           }
+           return n;
+
+       case VAR_LIST:
+           ll = tv->vval.v_list;
+           if (ll == rp)
+               return 1;       /* match, count it */
+           if (ll->lv_copyID == copyID)
+               return 0;       /* already inspected this list */
+           ll->lv_copyID = copyID;
+           n = count_ref_in_list(ll, rp, copyID, gap);
+           if (n > 0 && gap != NULL && ll->lv_refcount > 1)
+           {
+               /* We must later check that the references to this list are
+                * all in the structure we are freeing. */
+               if (ga_grow(gap, 1) == FAIL)
+                   return 0;
+               ((typval_T **)gap->ga_data)[gap->ga_len++] = tv;
+           }
+           return n;
+    }
+    return 0;
+}
+
 /*
  * Allocate an empty header for a dictionary.
  */
@@ -5557,8 +5748,23 @@ dict_alloc()
 dict_unref(d)
     dict_T *d;
 {
-    if (d != NULL && --d->dv_refcount <= 0)
-       dict_free(d);
+    int                selfref;
+
+    if (d != NULL && d->dv_refcount != DEL_REFCOUNT)
+    {
+       if (--d->dv_refcount > 0)
+       {
+           /* Check if the dict contains references to itself.  These need to
+            * be subtracted from the reference count to find out if we can
+            * delete the dict. */
+           selfref = count_self_ref(d, VAR_DICT);
+       }
+       else
+           selfref = 0;
+       if (d->dv_refcount - selfref == 0)
+           /* No references to the dict now, free it. */
+           dict_free(d);
+    }
 }
 
 /*
@@ -5571,15 +5777,24 @@ dict_free(d)
 {
     int                todo;
     hashitem_T *hi;
+    dictitem_T *di;
 
-    /* Careful: we free the dictitems while they still appear in the
-     * hashtab.  Must not try to resize the hashtab! */
+    /* Avoid that recursive reference to the dict frees us again. */
+    d->dv_refcount = DEL_REFCOUNT;
+
+    /* Lock the hashtab, we don't want it to resize while looping through it.
+     * */
+    hash_lock(&d->dv_hashtab);
     todo = d->dv_hashtab.ht_used;
     for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
     {
        if (!HASHITEM_EMPTY(hi))
        {
-           dictitem_free(HI2DI(hi));
+           /* Remove the item before deleting it, just in case there is
+            * something recursive causing trouble. */
+           di = HI2DI(hi);
+           hash_remove(&d->dv_hashtab, hi);
+           dictitem_free(di);
            --todo;
        }
     }
@@ -7663,7 +7878,6 @@ f_deepcopy(argvars, rettv)
     typval_T   *argvars;
     typval_T   *rettv;
 {
-    static int copyID = 0;
     int                noref = 0;
 
     if (argvars[1].v_type != VAR_UNKNOWN)
@@ -7671,7 +7885,7 @@ f_deepcopy(argvars, rettv)
     if (noref < 0 || noref > 1)
        EMSG(_(e_invarg));
     else
-       item_copy(&argvars[0], rettv, TRUE, noref == 0 ? ++copyID : 0);
+       item_copy(&argvars[0], rettv, TRUE, noref == 0 ? ++current_copyID : 0);
 }
 
 /*
@@ -8040,7 +8254,6 @@ f_extend(argvars, rettv)
                item = NULL;
            list_extend(l1, l2, item);
 
-           ++l1->lv_refcount;
            copy_tv(&argvars[0], rettv);
        }
     }
@@ -8106,7 +8319,6 @@ f_extend(argvars, rettv)
                }
            }
 
-           ++d1->dv_refcount;
            copy_tv(&argvars[0], rettv);
        }
     }
@@ -10421,7 +10633,6 @@ f_insert(argvars, rettv)
        if (l != NULL)
        {
            list_insert_tv(l, &argvars[1], item);
-           ++l->lv_refcount;
            copy_tv(&argvars[0], rettv);
        }
     }
@@ -14934,6 +15145,7 @@ handle_subscript(arg, rettv, evaluate, verbose)
     dict_T     *selfdict = NULL;
     char_u     *s;
     int                len;
+    typval_T   functv;
 
     while (ret == OK
            && (**arg == '['
@@ -14943,12 +15155,19 @@ handle_subscript(arg, rettv, evaluate, verbose)
     {
        if (**arg == '(')
        {
-           s = rettv->vval.v_string;
+           /* need to copy the funcref so that we can clear rettv */
+           functv = *rettv;
+           rettv->v_type = VAR_UNKNOWN;
 
            /* Invoke the function.  Recursive! */
+           s = functv.vval.v_string;
            ret = get_func_tv(s, STRLEN(s), rettv, arg,
-                   curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-                   &len, evaluate, selfdict);
+                       curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+                       &len, evaluate, selfdict);
+
+           /* Clear the funcref afterwards, so that deleting it while
+            * evaluating the arguments is possible (see test55). */
+           clear_tv(&functv);
 
            /* Stop the expression evaluation when immediately aborting on
             * error, or when an interrupt occurred or an exception was thrown
index 4acf187769b832e16da3ecf9493eee36a1b01124..6a06be5a4a619bce9725e05e318b68c4827621d1 100644 (file)
@@ -613,6 +613,8 @@ EX(CMD_promptrepl,  "promptrepl",   gui_mch_replace_dialog,
                        EXTRA|NOTRLCOM|CMDWIN),
 EX(CMD_profile,                "profile",      ex_profile,
                        BANG|EXTRA|TRLBAR|CMDWIN),
+EX(CMD_profdel,                "profdel",      ex_breakdel,
+                       EXTRA|TRLBAR|CMDWIN),
 EX(CMD_psearch,                "psearch",      ex_psearch,
                        BANG|RANGE|WHOLEFOLD|DFLALL|EXTRA),
 EX(CMD_ptag,           "ptag",         ex_ptag,
index 45bd2ee63f6a56bad9d520dcb3445f7a71932238..29687d2fc25804c0404d8503a4e8dc9c71d9c9c7 100644 (file)
@@ -591,7 +591,7 @@ ex_debuggreedy(eap)
 }
 
 /*
- * ":breakdel".
+ * ":breakdel" and ":profdel".
  */
     void
 ex_breakdel(eap)
@@ -603,13 +603,20 @@ ex_breakdel(eap)
     int                del_all = FALSE;
     int                i;
     linenr_T   best_lnum = 0;
+    garray_T   *gap;
+
+    gap = &dbg_breakp;
+#ifdef FEAT_PROFILE
+    if (eap->cmdidx == CMD_profdel)
+       gap = &prof_ga;
+#endif
 
     if (vim_isdigit(*eap->arg))
     {
        /* ":breakdel {nr}" */
        nr = atol((char *)eap->arg);
-       for (i = 0; i < dbg_breakp.ga_len; ++i)
-           if (BREAKP(i).dbg_nr == nr)
+       for (i = 0; i < gap->ga_len; ++i)
+           if (DEBUGGY(gap, i).dbg_nr == nr)
            {
                todel = i;
                break;
@@ -623,12 +630,12 @@ ex_breakdel(eap)
     else
     {
        /* ":breakdel {func|file} [lnum] {name}" */
-       if (dbg_parsearg(eap->arg, &dbg_breakp) == FAIL)
+       if (dbg_parsearg(eap->arg, gap) == FAIL)
            return;
-       bp = &BREAKP(dbg_breakp.ga_len);
-       for (i = 0; i < dbg_breakp.ga_len; ++i)
+       bp = &DEBUGGY(gap, gap->ga_len);
+       for (i = 0; i < gap->ga_len; ++i)
        {
-           bpi = &BREAKP(i);
+           bpi = &DEBUGGY(gap, i);
            if (bp->dbg_type == bpi->dbg_type
                    && STRCMP(bp->dbg_name, bpi->dbg_name) == 0
                    && (bp->dbg_lnum == bpi->dbg_lnum
@@ -646,18 +653,27 @@ ex_breakdel(eap)
     if (todel < 0)
        EMSG2(_("E161: Breakpoint not found: %s"), eap->arg);
     else
-       while (dbg_breakp.ga_len > 0)
+    {
+       while (gap->ga_len > 0)
        {
-           vim_free(BREAKP(todel).dbg_name);
-           vim_free(BREAKP(todel).dbg_prog);
-           --dbg_breakp.ga_len;
-           if (todel < dbg_breakp.ga_len)
-               mch_memmove(&BREAKP(todel), &BREAKP(todel + 1),
-                       (dbg_breakp.ga_len - todel) * sizeof(struct debuggy));
-           ++debug_tick;
+           vim_free(DEBUGGY(gap, todel).dbg_name);
+           vim_free(DEBUGGY(gap, todel).dbg_prog);
+           --gap->ga_len;
+           if (todel < gap->ga_len)
+               mch_memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
+                             (gap->ga_len - todel) * sizeof(struct debuggy));
+#ifdef FEAT_PROFILE
+           if (eap->cmdidx == CMD_breakdel)
+#endif
+               ++debug_tick;
            if (!del_all)
                break;
        }
+
+       /* If all breakpoints were removed clear the array. */
+       if (gap->ga_len == 0)
+           ga_clear(gap);
+    }
 }
 
 /*