]> granicus.if.org Git - vim/commitdiff
updated for version 7.3.055 v7.3.055
authorBram Moolenaar <Bram@vim.org>
Wed, 10 Nov 2010 19:41:57 +0000 (20:41 +0100)
committerBram Moolenaar <Bram@vim.org>
Wed, 10 Nov 2010 19:41:57 +0000 (20:41 +0100)
Problem:    Recursively nested lists and dictionaries cause a near-endless
            loop when comparing them with a copy. (ZyX)
Solution:   Limit recursiveness in a way that non-recursive structures can
            still be nested very deep.
Files:      src/eval.c, src/testdir/test55.in, src/testdir/test55.ok

src/eval.c
src/testdir/test55.in
src/testdir/test55.ok
src/version.c

index d7cee74350841711eb5e2658beed566b2e4ff019..7c3abf1f57be355454c92847f2f4b3bdb84efd2e 100644 (file)
@@ -434,9 +434,9 @@ static listitem_T *listitem_alloc __ARGS((void));
 static void listitem_free __ARGS((listitem_T *item));
 static void listitem_remove __ARGS((list_T *l, listitem_T *item));
 static long list_len __ARGS((list_T *l));
-static int list_equal __ARGS((list_T *l1, list_T *l2, int ic));
-static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic));
-static int tv_equal __ARGS((typval_T *tv1, typval_T *tv2, int ic));
+static int list_equal __ARGS((list_T *l1, list_T *l2, int ic, int recursive));
+static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic, int recursive));
+static int tv_equal __ARGS((typval_T *tv1, typval_T *tv2, int ic, int recursive));
 static listitem_T *list_find __ARGS((list_T *l, long n));
 static long list_find_nr __ARGS((list_T *l, long idx, int *errorp));
 static long list_idx_of_item __ARGS((list_T *l, listitem_T *item));
@@ -4350,7 +4350,8 @@ eval4(arg, rettv, evaluate)
                else
                {
                    /* Compare two Lists for being equal or unequal. */
-                   n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, ic);
+                   n1 = list_equal(rettv->vval.v_list, var2.vval.v_list,
+                                                                  ic, FALSE);
                    if (type == TYPE_NEQUAL)
                        n1 = !n1;
                }
@@ -4379,7 +4380,8 @@ eval4(arg, rettv, evaluate)
                else
                {
                    /* Compare two Dictionaries for being equal or unequal. */
-                   n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, ic);
+                   n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict,
+                                                                  ic, FALSE);
                    if (type == TYPE_NEQUAL)
                        n1 = !n1;
                }
@@ -5914,10 +5916,11 @@ list_len(l)
  * Return TRUE when two lists have exactly the same values.
  */
     static int
-list_equal(l1, l2, ic)
+list_equal(l1, l2, ic, recursive)
     list_T     *l1;
     list_T     *l2;
     int                ic;     /* ignore case for strings */
+    int                recursive;  /* TRUE when used recursively */
 {
     listitem_T *item1, *item2;
 
@@ -5931,7 +5934,7 @@ list_equal(l1, l2, ic)
     for (item1 = l1->lv_first, item2 = l2->lv_first;
            item1 != NULL && item2 != NULL;
                               item1 = item1->li_next, item2 = item2->li_next)
-       if (!tv_equal(&item1->li_tv, &item2->li_tv, ic))
+       if (!tv_equal(&item1->li_tv, &item2->li_tv, ic, recursive))
            return FALSE;
     return item1 == NULL && item2 == NULL;
 }
@@ -5953,10 +5956,11 @@ dict_lookup(hi)
  * Return TRUE when two dictionaries have exactly the same key/values.
  */
     static int
-dict_equal(d1, d2, ic)
+dict_equal(d1, d2, ic, recursive)
     dict_T     *d1;
     dict_T     *d2;
     int                ic;     /* ignore case for strings */
+    int                recursive; /* TRUE when used recursively */
 {
     hashitem_T *hi;
     dictitem_T *item2;
@@ -5977,7 +5981,7 @@ dict_equal(d1, d2, ic)
            item2 = dict_find(d2, hi->hi_key, -1);
            if (item2 == NULL)
                return FALSE;
-           if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic))
+           if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
                return FALSE;
            --todo;
        }
@@ -5985,41 +5989,54 @@ dict_equal(d1, d2, ic)
     return TRUE;
 }
 
+static int tv_equal_recurse_limit;
+
 /*
  * Return TRUE if "tv1" and "tv2" have the same value.
  * Compares the items just like "==" would compare them, but strings and
  * numbers are different.  Floats and numbers are also different.
  */
     static int
-tv_equal(tv1, tv2, ic)
+tv_equal(tv1, tv2, ic, recursive)
     typval_T *tv1;
     typval_T *tv2;
-    int            ic;     /* ignore case */
+    int             ic;            /* ignore case */
+    int             recursive;     /* TRUE when used recursively */
 {
     char_u     buf1[NUMBUFLEN], buf2[NUMBUFLEN];
     char_u     *s1, *s2;
-    static int  recursive = 0;     /* cach recursive loops */
+    static int  recursive_cnt = 0;         /* catch recursive loops */
     int                r;
 
     if (tv1->v_type != tv2->v_type)
        return FALSE;
+
     /* Catch lists and dicts that have an endless loop by limiting
-     * recursiveness to 1000.  We guess they are equal then. */
-    if (recursive >= 1000)
+     * recursiveness to a limit.  We guess they are equal then.
+     * A fixed limit has the problem of still taking an awful long time.
+     * Reduce the limit every time running into it. That should work fine for
+     * deeply linked structures that are not recursively linked and catch
+     * recursiveness quickly. */
+    if (!recursive)
+       tv_equal_recurse_limit = 1000;
+    if (recursive_cnt >= tv_equal_recurse_limit)
+    {
+       --tv_equal_recurse_limit;
        return TRUE;
+    }
 
     switch (tv1->v_type)
     {
        case VAR_LIST:
-           ++recursive;
-           r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic);
-           --recursive;
+           ++recursive_cnt;
+           r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
+           --recursive_cnt;
            return r;
 
        case VAR_DICT:
-           ++recursive;
-           r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic);
-           --recursive;
+           ++recursive_cnt;
+           r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE);
+           --recursive_cnt;
            return r;
 
        case VAR_FUNC:
@@ -9391,7 +9408,7 @@ f_count(argvars, rettv)
            }
 
            for ( ; li != NULL; li = li->li_next)
-               if (tv_equal(&li->li_tv, &argvars[1], ic))
+               if (tv_equal(&li->li_tv, &argvars[1], ic, FALSE))
                    ++n;
        }
     }
@@ -9418,7 +9435,7 @@ f_count(argvars, rettv)
                if (!HASHITEM_EMPTY(hi))
                {
                    --todo;
-                   if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic))
+                   if (tv_equal(&HI2DI(hi)->di_tv, &argvars[1], ic, FALSE))
                        ++n;
                }
            }
@@ -12574,7 +12591,7 @@ f_index(argvars, rettv)
        }
 
        for ( ; item != NULL; item = item->li_next, ++idx)
-           if (tv_equal(&item->li_tv, &argvars[1], ic))
+           if (tv_equal(&item->li_tv, &argvars[1], ic, FALSE))
            {
                rettv->vval.v_number = idx;
                break;
index 7d0008e7e45e117ad132054de598d5eafa330f7e..0cac34866519c6b5dab73673f412a7c470692887 100644 (file)
@@ -342,7 +342,18 @@ let l = [0, 1, 2, 3]
 :$put =(d == d)
 :$put =(l != deepcopy(l))
 :$put =(d != deepcopy(d))
+:"
+:" compare complex recursively linked list and dict
+:let l = []
+:call add(l, l)
+:let dict4 = {"l": l}
+:call add(dict4.l, dict4)
+:let lcopy = deepcopy(l)
+:let dict4copy = deepcopy(dict4)
+:$put =(l == lcopy)
+:$put =(dict4 == dict4copy)
 :endfun
+:"
 :call Test(1, 2, [3, 4], {5: 6})  " This may take a while
 :"
 :delfunc Test
index da8300290af16074d56aece80e41d4b9ab1383c8..6e402354aa9c152dcf406a6416aae2f0a6e6ec68 100644 (file)
@@ -109,3 +109,5 @@ caught a:000[3]
 1
 0
 0
+1
+1
index ab330b5ef91110d2eedb428cce925e138c25dd96..7b26cfd425929e09bd58cb720ab33cce189c7574 100644 (file)
@@ -714,6 +714,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    55,
 /**/
     54,
 /**/