diff src/eval.c @ 364:5332dd13733c v7.0094

updated for version 7.0094
author vimboss
date Sun, 26 Jun 2005 22:34:35 +0000
parents 6c62b9b939bd
children 4b9fef49d7ff
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -123,6 +123,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 *
 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)
-    {
-	next = item->li_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)
+    {
+	/* Remove the item before deleting it. */
+	l->lv_first = item->li_next;
 	listitem_free(item);
     }
     vim_free(l);
@@ -5531,6 +5568,160 @@ list_join(gap, l, sep, echo)
 }
 
 /*
+ * 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.
  */
     dict_T *
@@ -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;
-
-    /* Careful: we free the dictitems while they still appear in the
-     * hashtab.  Must not try to resize the hashtab! */
+    dictitem_T	*di;
+
+    /* 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, v
     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, v
     {
 	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