]> granicus.if.org Git - vim/commitdiff
patch 8.2.0419: various memory leaks in Vim9 script code v8.2.0419
authorBram Moolenaar <Bram@vim.org>
Fri, 20 Mar 2020 17:39:46 +0000 (18:39 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 20 Mar 2020 17:39:46 +0000 (18:39 +0100)
Problem:    Various memory leaks in Vim9 script code.
Solution:   Fix the leaks. (Ozaki Kiichi, closes #5814)

src/proto/vim9compile.pro
src/scriptfile.c
src/structs.h
src/testdir/test_vim9_script.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c
src/vim9script.c

index 6905fc28ab52bcffd9080bb3f3a49873e3cc9389..b22bf98bf69a0383af473c85f164c59a1ea6a49e 100644 (file)
@@ -9,6 +9,7 @@ imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
 char_u *to_name_const_end(char_u *arg);
 int assignment_len(char_u *p, int *heredoc);
 void compile_def_function(ufunc_T *ufunc, int set_return_type);
+void delete_instr(isn_T *isn);
 void delete_def_function(ufunc_T *ufunc);
 void free_def_functions(void);
 /* vim: set ft=c : */
index 838d5f6420a022d3439075f3701b5fadfdd0d838..55b1ddd26058c31f450d94acd61bfe8c65474c4c 100644 (file)
@@ -1526,6 +1526,7 @@ free_scriptnames(void)
        vim_free(si->sn_vars);
 
        vim_free(si->sn_name);
+       free_imports(i);
        free_string_option(si->sn_save_cpo);
 #  ifdef FEAT_PROFILE
        ga_clear(&si->sn_prl_ga);
index 76d45ea8f2980a8eba6945512708e71833378bae..ff32cb2c8ebcbbaf34d705320b5ba654ae692d63 100644 (file)
@@ -1308,6 +1308,7 @@ typedef struct {
     int                cb_free_name;       // cb_name was allocated
 } callback_T;
 
+typedef struct isn_S isn_T;        // instruction
 typedef struct dfunc_S dfunc_T;            // :def function
 
 typedef struct jobvar_S job_T;
index c5dd1d376d1e2ec6b0701f968a23c3569806615a..a02855d1e9398a9749ecc9c78821e30170a35dda 100644 (file)
@@ -942,6 +942,16 @@ def Test_while_loop()
   assert_equal('1_3_', result)
 enddef
 
+def Test_interrupt_loop()
+  let x = 0
+  while 1
+    x += 1
+    if x == 100
+      feedkeys("\<C-C>", 'L')
+    endif
+  endwhile
+enddef
+
 def Test_substitute_cmd()
   new
   setline(1, 'something')
@@ -964,4 +974,24 @@ def Test_substitute_cmd()
   delete('Xvim9lines')
 enddef
 
+def Test_redef_failure()
+  call writefile(['def Func0(): string',  'return "Func0"', 'enddef'], 'Xdef')
+  so Xdef
+  call writefile(['def Func1(): string',  'return "Func1"', 'enddef'], 'Xdef')
+  so Xdef
+  call writefile(['def! Func0(): string', 'enddef'], 'Xdef')
+  call assert_fails('so Xdef', 'E1027:')
+  call writefile(['def Func2(): string',  'return "Func2"', 'enddef'], 'Xdef')
+  so Xdef
+  call delete('Xdef')
+
+  call assert_equal(0, Func0())
+  call assert_equal('Func1', Func1())
+  call assert_equal('Func2', Func2())
+
+  delfunc! Func0
+  delfunc! Func1
+  delfunc! Func2
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 81fff387377d93467878a41393672e76cfda5235..26939bb1366f161250b8757bafcc508439522bcc 100644 (file)
@@ -738,6 +738,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    419,
 /**/
     418,
 /**/
index 38286b32a1f2d58b2e68b516361ff93e04a3eb19..ffa09a727d473cdcdd51310b734b685da9d0d453 100644 (file)
@@ -203,7 +203,7 @@ typedef struct {
 /*
  * Instruction
  */
-typedef struct {
+struct isn_S {
     isntype_T  isn_type;
     int                isn_lnum;
     union {
@@ -231,7 +231,7 @@ typedef struct {
        loadstore_T         loadstore;
        script_T            script;
     } isn_arg;
-} isn_T;
+};
 
 /*
  * Info about a function defined with :def.  Used in "def_functions".
index 38b904d2935b67879fed804b06e58a83cbf3c395..f768fc75e938989ac705a84bef51598189161e5f 100644 (file)
@@ -129,6 +129,7 @@ static char e_syntax_at[] = N_("E1002: Syntax error at %s");
 static int compile_expr1(char_u **arg,  cctx_T *cctx);
 static int compile_expr2(char_u **arg,  cctx_T *cctx);
 static int compile_expr3(char_u **arg,  cctx_T *cctx);
+static void delete_def_function_contents(dfunc_T *dfunc);
 
 /*
  * Lookup variable "name" in the local scope and return the index.
@@ -1305,6 +1306,36 @@ reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type)
     return idx;
 }
 
+/*
+ * Remove local variables above "new_top".
+ */
+    static void
+unwind_locals(cctx_T *cctx, int new_top)
+{
+    if (cctx->ctx_locals.ga_len > new_top)
+    {
+       int     idx;
+       lvar_T  *lvar;
+
+       for (idx = new_top; idx < cctx->ctx_locals.ga_len; ++idx)
+       {
+           lvar = ((lvar_T *)cctx->ctx_locals.ga_data) + idx;
+           vim_free(lvar->lv_name);
+       }
+    }
+    cctx->ctx_locals.ga_len = new_top;
+}
+
+/*
+ * Free all local variables.
+ */
+    static void
+free_local(cctx_T *cctx)
+{
+    unwind_locals(cctx, 0);
+    ga_clear(&cctx->ctx_locals);
+}
+
 /*
  * Skip over a type definition and return a pointer to just after it.
  */
@@ -1671,6 +1702,23 @@ find_imported(char_u *name, size_t len, cctx_T *cctx)
     return NULL;
 }
 
+/*
+ * Free all imported variables.
+ */
+    static void
+free_imported(cctx_T *cctx)
+{
+    int idx;
+
+    for (idx = 0; idx < cctx->ctx_imports.ga_len; ++idx)
+    {
+       imported_T *import = ((imported_T *)cctx->ctx_imports.ga_data) + idx;
+
+       vim_free(import->imp_name);
+    }
+    ga_clear(&cctx->ctx_imports);
+}
+
 /*
  * Generate an instruction to load script-local variable "name".
  */
@@ -2127,7 +2175,10 @@ compile_lambda(char_u **arg, cctx_T *cctx)
     // Get the funcref in "rettv".
     if (get_lambda_tv(arg, &rettv, TRUE) == FAIL)
        return FAIL;
+
     ufunc = rettv.vval.v_partial->pt_func;
+    ++ufunc->uf_refcount;
+    clear_tv(&rettv);
 
     // The function will have one line: "return {expr}".
     // Compile it into instructions.
@@ -2169,10 +2220,12 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
        return FAIL;
     }
 
-    // The function will have one line: "return {expr}".
-    // Compile it into instructions.
     ufunc = rettv.vval.v_partial->pt_func;
     ++ufunc->uf_refcount;
+    clear_tv(&rettv);
+
+    // The function will have one line: "return {expr}".
+    // Compile it into instructions.
     compile_def_function(ufunc, TRUE);
 
     // compile the arguments
@@ -2181,7 +2234,6 @@ compile_lambda_call(char_u **arg, cctx_T *cctx)
        // call the compiled function
        ret = generate_CALL(cctx, ufunc, argcount);
 
-    clear_tv(&rettv);
     return ret;
 }
 
@@ -3398,7 +3450,6 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
        {
            int     cc;
            long            numval;
-           char_u          *stringval = NULL;
 
            dest = dest_option;
            if (cmdidx == CMD_const)
@@ -3420,7 +3471,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
            }
            cc = *p;
            *p = NUL;
-           opt_type = get_option_value(arg + 1, &numval, &stringval, opt_flags);
+           opt_type = get_option_value(arg + 1, &numval, NULL, opt_flags);
            *p = cc;
            if (opt_type == -3)
            {
@@ -4217,7 +4268,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
        emsg(_(e_elseif_without_if));
        return NULL;
     }
-    cctx->ctx_locals.ga_len = scope->se_local_count;
+    unwind_locals(cctx, scope->se_local_count);
 
     if (cctx->ctx_skip == MAYBE)
     {
@@ -4265,7 +4316,7 @@ compile_else(char_u *arg, cctx_T *cctx)
        emsg(_(e_else_without_if));
        return NULL;
     }
-    cctx->ctx_locals.ga_len = scope->se_local_count;
+    unwind_locals(cctx, scope->se_local_count);
 
     // jump from previous block to the end, unless the else block is empty
     if (cctx->ctx_skip == MAYBE)
@@ -4307,7 +4358,7 @@ compile_endif(char_u *arg, cctx_T *cctx)
     }
     ifscope = &scope->se_u.se_if;
     cctx->ctx_scope = scope->se_outer;
-    cctx->ctx_locals.ga_len = scope->se_local_count;
+    unwind_locals(cctx, scope->se_local_count);
 
     if (scope->se_u.se_if.is_if_label >= 0)
     {
@@ -4435,7 +4486,7 @@ compile_endfor(char_u *arg, cctx_T *cctx)
     }
     forscope = &scope->se_u.se_for;
     cctx->ctx_scope = scope->se_outer;
-    cctx->ctx_locals.ga_len = scope->se_local_count;
+    unwind_locals(cctx, scope->se_local_count);
 
     // At end of ":for" scope jump back to the FOR instruction.
     generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);
@@ -4506,7 +4557,7 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
        return NULL;
     }
     cctx->ctx_scope = scope->se_outer;
-    cctx->ctx_locals.ga_len = scope->se_local_count;
+    unwind_locals(cctx, scope->se_local_count);
 
     // At end of ":for" scope jump back to the FOR instruction.
     generate_JUMP(cctx, JUMP_ALWAYS, scope->se_u.se_while.ws_top_label);
@@ -4599,7 +4650,7 @@ compile_endblock(cctx_T *cctx)
     scope_T    *scope = cctx->ctx_scope;
 
     cctx->ctx_scope = scope->se_outer;
-    cctx->ctx_locals.ga_len = scope->se_local_count;
+    unwind_locals(cctx, scope->se_local_count);
     vim_free(scope);
 }
 
@@ -4942,9 +4993,11 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
 
     if (ufunc->uf_dfunc_idx >= 0)
     {
-       // redefining a function that was compiled before
+       // Redefining a function that was compiled before.
        dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
-       dfunc->df_deleted = FALSE;
+
+       // Free old instructions.
+       delete_def_function_contents(dfunc);
     }
     else
     {
@@ -5305,6 +5358,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
        generate_instr(&cctx, ISN_RETURN);
     }
 
+    dfunc->df_deleted = FALSE;
     dfunc->df_instr = instr->ga_data;
     dfunc->df_instr_count = instr->ga_len;
     dfunc->df_varcount = cctx.ctx_max_local;
@@ -5314,27 +5368,35 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
 erret:
     if (ret == FAIL)
     {
+       int idx;
+
+       for (idx = 0; idx < instr->ga_len; ++idx)
+           delete_instr(((isn_T *)instr->ga_data) + idx);
        ga_clear(instr);
+
        ufunc->uf_dfunc_idx = -1;
-       --def_functions.ga_len;
+       if (!dfunc->df_deleted)
+           --def_functions.ga_len;
+
+       // Don't execute this function body.
+       ga_clear_strings(&ufunc->uf_lines);
+
        if (errormsg != NULL)
            emsg(errormsg);
        else if (called_emsg == called_emsg_before)
            emsg(_("E1028: compile_def_function failed"));
-
-       // don't execute this function body
-       ufunc->uf_lines.ga_len = 0;
     }
 
     current_sctx = save_current_sctx;
+    free_imported(&cctx);
+    free_local(&cctx);
     ga_clear(&cctx.ctx_type_stack);
-    ga_clear(&cctx.ctx_locals);
 }
 
 /*
  * Delete an instruction, free what it contains.
  */
-    static void
+    void
 delete_instr(isn_T *isn)
 {
     switch (isn->isn_type)
@@ -5442,33 +5504,58 @@ delete_instr(isn_T *isn)
     }
 }
 
+/*
+ * Free all instructions for "dfunc".
+ */
+    static void
+delete_def_function_contents(dfunc_T *dfunc)
+{
+    int idx;
+
+    ga_clear(&dfunc->df_def_args_isn);
+
+    if (dfunc->df_instr != NULL)
+    {
+       for (idx = 0; idx < dfunc->df_instr_count; ++idx)
+           delete_instr(dfunc->df_instr + idx);
+       VIM_CLEAR(dfunc->df_instr);
+    }
+
+    dfunc->df_deleted = TRUE;
+}
+
 /*
  * When a user function is deleted, delete any associated def function.
  */
     void
 delete_def_function(ufunc_T *ufunc)
 {
-    int idx;
-
     if (ufunc->uf_dfunc_idx >= 0)
     {
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                         + ufunc->uf_dfunc_idx;
-       ga_clear(&dfunc->df_def_args_isn);
 
-       for (idx = 0; idx < dfunc->df_instr_count; ++idx)
-           delete_instr(dfunc->df_instr + idx);
-       VIM_CLEAR(dfunc->df_instr);
-
-       dfunc->df_deleted = TRUE;
+       delete_def_function_contents(dfunc);
     }
 }
 
 #if defined(EXITFREE) || defined(PROTO)
+/*
+ * Free all functions defined with ":def".
+ */
     void
 free_def_functions(void)
 {
-    vim_free(def_functions.ga_data);
+    int idx;
+
+    for (idx = 0; idx < def_functions.ga_len; ++idx)
+    {
+       dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + idx;
+
+       delete_def_function_contents(dfunc);
+    }
+
+    ga_clear(&def_functions);
 }
 #endif
 
index 8c8bf88f39902077c79555a0fa6b8780bfedbed5..33b6430a54cfcbb74ad36c268dbd40bdcfe0b5a0 100644 (file)
@@ -283,6 +283,7 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
        // that was defined later: we can call it directly next time.
        if (iptr != NULL)
        {
+           delete_instr(iptr);
            iptr->isn_type = ISN_DCALL;
            iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
            iptr->isn_arg.dfunc.cdf_argcount = argcount;
@@ -480,11 +481,21 @@ call_def_function(
     for (;;)
     {
        isn_T       *iptr;
-       trycmd_T    *trycmd = NULL;
+
+       veryfast_breakcheck();
+       if (got_int)
+       {
+           // Turn CTRL-C into an exception.
+           got_int = FALSE;
+           if (throw_exception("Vim:Interrupt", ET_INTERRUPT, NULL) != FAIL)
+               goto failed;
+           did_throw = TRUE;
+       }
 
        if (did_throw && !ectx.ec_in_catch)
        {
            garray_T    *trystack = &ectx.ec_trystack;
+           trycmd_T    *trycmd = NULL;
 
            // An exception jumps to the first catch, finally, or returns from
            // the current function.
@@ -782,8 +793,9 @@ call_def_function(
            // store $ENV
            case ISN_STOREENV:
                --ectx.ec_stack.ga_len;
-               vim_setenv_ext(iptr->isn_arg.string,
-                                              tv_get_string(STACK_TV_BOT(0)));
+               tv = STACK_TV_BOT(0);
+               vim_setenv_ext(iptr->isn_arg.string, tv_get_string(tv));
+               clear_tv(tv);
                break;
 
            // store @r
@@ -1038,6 +1050,7 @@ call_def_function(
            case ISN_RETURN:
                {
                    garray_T    *trystack = &ectx.ec_trystack;
+                   trycmd_T    *trycmd = NULL;
 
                    if (trystack->ga_len > 0)
                        trycmd = ((trycmd_T *)trystack->ga_data)
@@ -1146,6 +1159,8 @@ call_def_function(
            // start of ":try" block
            case ISN_TRY:
                {
+                   trycmd_T    *trycmd = NULL;
+
                    if (ga_grow(&ectx.ec_trystack, 1) == FAIL)
                        goto failed;
                    trycmd = ((trycmd_T *)ectx.ec_trystack.ga_data)
@@ -1180,7 +1195,7 @@ call_def_function(
 
                    if (trystack->ga_len > 0)
                    {
-                       trycmd = ((trycmd_T *)trystack->ga_data)
+                       trycmd_T    *trycmd = ((trycmd_T *)trystack->ga_data)
                                                        + trystack->ga_len - 1;
                        trycmd->tcd_caught = TRUE;
                    }
@@ -1196,6 +1211,8 @@ call_def_function(
 
                    if (trystack->ga_len > 0)
                    {
+                       trycmd_T    *trycmd = NULL;
+
                        --trystack->ga_len;
                        --trylevel;
                        trycmd = ((trycmd_T *)trystack->ga_data)
@@ -1677,6 +1694,7 @@ failed:
     for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
        clear_tv(STACK_TV(idx));
     vim_free(ectx.ec_stack.ga_data);
+    vim_free(ectx.ec_trystack.ga_data);
     return ret;
 }
 
index ce5cfec922ceaef11f3a9d69842f7582706ef534..e74a7b9cbb932d018a42792706ab8a95fad697e6 100644 (file)
@@ -119,11 +119,13 @@ free_imports(int sid)
 
     for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
     {
-       imported_T *imp = ((imported_T *)si->sn_imports.ga_data + idx);
+       imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
 
        vim_free(imp->imp_name);
     }
     ga_clear(&si->sn_imports);
+    ga_clear(&si->sn_var_vals);
+    ga_clear(&si->sn_type_list);
 }
 
 /*