]> granicus.if.org Git - vim/commitdiff
patch 8.2.1712: Vim9: leaking memory when calling a lambda v8.2.1712
authorBram Moolenaar <Bram@vim.org>
Sat, 19 Sep 2020 16:19:19 +0000 (18:19 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 19 Sep 2020 16:19:19 +0000 (18:19 +0200)
Problem:    Vim9: leaking memory when calling a lambda.
Solution:   Decrement function reference from ISN_DCALL.

src/proto/userfunc.pro
src/userfunc.c
src/version.c
src/vim9compile.c

index 4e3f2dbf4f7d97892b1747d6f9aa498f285d42a0..aefedf778c38ebfc5cb74a724b1e90ac562d6449 100644 (file)
@@ -12,6 +12,7 @@ char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *e
 ufunc_T *find_func_even_dead(char_u *name, int is_global, cctx_T *cctx);
 ufunc_T *find_func(char_u *name, int is_global, cctx_T *cctx);
 int func_is_global(ufunc_T *ufunc);
+int func_name_refcount(char_u *name);
 void copy_func(char_u *lambda, char_u *global);
 int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
 void save_funccal(funccal_entry_T *entry);
index d6a7e5d16d4aa6ce1680e830af24827b1871def8..595963d628114230b687b421e19afb375f53035c 100644 (file)
@@ -1058,7 +1058,7 @@ cleanup_function_call(funccall_T *fc)
  * using function() does not count as a reference, because the function is
  * looked up by name.
  */
-    static int
+    int
 func_name_refcount(char_u *name)
 {
     return isdigit(*name) || *name == '<';
@@ -1176,8 +1176,9 @@ func_clear(ufunc_T *fp, int force)
  * Free a function and remove it from the list of functions.  Does not free
  * what a function contains, call func_clear() first.
  * When "force" is TRUE we are exiting.
+ * Returns OK when the function was actually freed.
  */
-    static void
+    static int
 func_free(ufunc_T *fp, int force)
 {
     // Only remove it when not done already, otherwise we would remove a newer
@@ -1191,7 +1192,9 @@ func_free(ufunc_T *fp, int force)
            unlink_def_function(fp);
        VIM_CLEAR(fp->uf_name_exp);
        vim_free(fp);
+       return OK;
     }
+    return FAIL;
 }
 
 /*
@@ -1890,9 +1893,13 @@ free_all_functions(void)
                    ++skipped;
                else
                {
-                   func_free(fp, FALSE);
-                   skipped = 0;
-                   break;
+                   if (func_free(fp, FALSE) == OK)
+                   {
+                       skipped = 0;
+                       break;
+                   }
+                   // did not actually free it
+                   ++skipped;
                }
            }
     }
index aa09b70d242a72afdf796ec3b80fb0d966c39063..615abc74e2d0680a5da9b5e3f756d13364c5a767 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1712,
 /**/
     1711,
 /**/
index a6b4ba4098b996aeb85aedb4d3c2e805f58faad3..0db516274aab41533c66b96abc03612feae77c11 100644 (file)
@@ -1452,7 +1452,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
                    ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
                                                         : ISN_UCALL)) == NULL)
        return FAIL;
-    if (ufunc->uf_def_status != UF_NOT_COMPILED)
+    if (isn->isn_type == ISN_DCALL)
     {
        isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
        isn->isn_arg.dfunc.cdf_argcount = argcount;
@@ -2634,8 +2634,8 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
     clear_tv(&rettv);
     ga_init2(&ufunc->uf_type_list, sizeof(type_T *), 10);
 
-    // The function will have one line: "return {expr}".
-    // Compile it into instructions.
+    // The function will have one line: "return {expr}".  Compile it into
+    // instructions so that we get any errors right now.
     compile_def_function(ufunc, TRUE, cctx);
 
     // compile the arguments
@@ -7285,7 +7285,19 @@ delete_instr(isn_T *isn)
            {
                dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                               + isn->isn_arg.funcref.fr_func;
-               func_ptr_unref(dfunc->df_ufunc);
+
+               if (func_name_refcount(dfunc->df_ufunc->uf_name))
+                   func_ptr_unref(dfunc->df_ufunc);
+           }
+           break;
+
+       case ISN_DCALL:
+           {
+               dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                              + isn->isn_arg.dfunc.cdf_idx;
+
+               if (func_name_refcount(dfunc->df_ufunc->uf_name))
+                   func_ptr_unref(dfunc->df_ufunc);
            }
            break;
 
@@ -7333,7 +7345,6 @@ delete_instr(isn_T *isn)
        case ISN_COMPARESPECIAL:
        case ISN_COMPARESTRING:
        case ISN_CONCAT:
-       case ISN_DCALL:
        case ISN_DROP:
        case ISN_ECHO:
        case ISN_ECHOERR: