Patch 8.2.0419
Problem: Various memory leaks in Vim9 script code.
Solution: Fix the leaks. (Ozaki Kiichi, closes #5814)
Files: src/proto/
vim9compile.pro, src/scriptfile.c, src/structs.h,
src/testdir/test_vim9_script.vim, src/vim9.h, src/vim9compile.c,
src/vim9execute.c, src/vim9script.c
*** ../vim-8.2.0418/src/proto/
vim9compile.pro 2020-03-09 19:25:21.208186448 +0100
--- src/proto/
vim9compile.pro 2020-03-20 18:26:23.847779599 +0100
***************
*** 9,14 ****
--- 9,15 ----
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 : */
*** ../vim-8.2.0418/src/scriptfile.c 2020-03-18 15:23:10.983695087 +0100
--- src/scriptfile.c 2020-03-20 18:26:23.847779599 +0100
***************
*** 1526,1531 ****
--- 1526,1532 ----
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);
*** ../vim-8.2.0418/src/structs.h 2020-02-26 16:15:31.076386941 +0100
--- src/structs.h 2020-03-20 18:26:23.847779599 +0100
***************
*** 1308,1313 ****
--- 1308,1314 ----
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;
*** ../vim-8.2.0418/src/testdir/test_vim9_script.vim 2020-03-09 19:25:21.208186448 +0100
--- src/testdir/test_vim9_script.vim 2020-03-20 18:26:23.847779599 +0100
***************
*** 942,947 ****
--- 942,957 ----
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,967 ****
--- 974,997 ----
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
*** ../vim-8.2.0418/src/vim9.h 2020-03-04 22:20:23.362900855 +0100
--- src/vim9.h 2020-03-20 18:26:23.847779599 +0100
***************
*** 203,209 ****
/*
* Instruction
*/
! typedef struct {
isntype_T isn_type;
int isn_lnum;
union {
--- 203,209 ----
/*
* Instruction
*/
! struct isn_S {
isntype_T isn_type;
int isn_lnum;
union {
***************
*** 231,237 ****
loadstore_T loadstore;
script_T script;
} isn_arg;
! } isn_T;
/*
* Info about a function defined with :def. Used in "def_functions".
--- 231,237 ----
loadstore_T loadstore;
script_T script;
} isn_arg;
! };
/*
* Info about a function defined with :def. Used in "def_functions".
*** ../vim-8.2.0418/src/vim9compile.c 2020-03-09 19:25:21.208186448 +0100
--- src/vim9compile.c 2020-03-20 18:32:31.294616471 +0100
***************
*** 129,134 ****
--- 129,135 ----
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.
***************
*** 1306,1311 ****
--- 1307,1342 ----
}
/*
+ * 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.
*/
char_u *
***************
*** 1672,1677 ****
--- 1703,1725 ----
}
/*
+ * 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".
*/
static int
***************
*** 2127,2133 ****
--- 2175,2184 ----
// 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,2178 ****
return FAIL;
}
- // The function will have one line: "return {expr}".
- // Compile it into instructions.
ufunc = rettv.vval.v_partial->pt_func;
++ufunc->uf_refcount;
compile_def_function(ufunc, TRUE);
// compile the arguments
--- 2220,2231 ----
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.
compile_def_function(ufunc, TRUE);
// compile the arguments
***************
*** 2181,2187 ****
// call the compiled function
ret = generate_CALL(cctx, ufunc, argcount);
- clear_tv(&rettv);
return ret;
}
--- 2234,2239 ----
***************
*** 3398,3404 ****
{
int cc;
long numval;
- char_u *stringval = NULL;
dest = dest_option;
if (cmdidx == CMD_const)
--- 3450,3455 ----
***************
*** 3420,3426 ****
}
cc = *p;
*p = NUL;
! opt_type = get_option_value(arg + 1, &numval, &stringval, opt_flags);
*p = cc;
if (opt_type == -3)
{
--- 3471,3477 ----
}
cc = *p;
*p = NUL;
! opt_type = get_option_value(arg + 1, &numval, NULL, opt_flags);
*p = cc;
if (opt_type == -3)
{
***************
*** 4217,4223 ****
emsg(_(e_elseif_without_if));
return NULL;
}
! cctx->ctx_locals.ga_len = scope->se_local_count;
if (cctx->ctx_skip == MAYBE)
{
--- 4268,4274 ----
emsg(_(e_elseif_without_if));
return NULL;
}
! unwind_locals(cctx, scope->se_local_count);
if (cctx->ctx_skip == MAYBE)
{
***************
*** 4265,4271 ****
emsg(_(e_else_without_if));
return NULL;
}
! cctx->ctx_locals.ga_len = scope->se_local_count;
// jump from previous block to the end, unless the else block is empty
if (cctx->ctx_skip == MAYBE)
--- 4316,4322 ----
emsg(_(e_else_without_if));
return NULL;
}
! 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,4313 ****
}
ifscope = &scope->se_u.se_if;
cctx->ctx_scope = scope->se_outer;
! cctx->ctx_locals.ga_len = scope->se_local_count;
if (scope->se_u.se_if.is_if_label >= 0)
{
--- 4358,4364 ----
}
ifscope = &scope->se_u.se_if;
cctx->ctx_scope = scope->se_outer;
! unwind_locals(cctx, scope->se_local_count);
if (scope->se_u.se_if.is_if_label >= 0)
{
***************
*** 4435,4441 ****
}
forscope = &scope->se_u.se_for;
cctx->ctx_scope = scope->se_outer;
! cctx->ctx_locals.ga_len = scope->se_local_count;
// At end of ":for" scope jump back to the FOR instruction.
generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);
--- 4486,4492 ----
}
forscope = &scope->se_u.se_for;
cctx->ctx_scope = scope->se_outer;
! 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,4512 ****
return NULL;
}
cctx->ctx_scope = scope->se_outer;
! cctx->ctx_locals.ga_len = 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);
--- 4557,4563 ----
return NULL;
}
cctx->ctx_scope = scope->se_outer;
! 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,4605 ****
scope_T *scope = cctx->ctx_scope;
cctx->ctx_scope = scope->se_outer;
! cctx->ctx_locals.ga_len = scope->se_local_count;
vim_free(scope);
}
--- 4650,4656 ----
scope_T *scope = cctx->ctx_scope;
cctx->ctx_scope = scope->se_outer;
! unwind_locals(cctx, scope->se_local_count);
vim_free(scope);
}
***************
*** 4942,4950 ****
if (ufunc->uf_dfunc_idx >= 0)
{
! // redefining a function that was compiled before
dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
! dfunc->df_deleted = FALSE;
}
else
{
--- 4993,5003 ----
if (ufunc->uf_dfunc_idx >= 0)
{
! // Redefining a function that was compiled before.
dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx;
!
! // Free old instructions.
! delete_def_function_contents(dfunc);
}
else
{
***************
*** 5305,5310 ****
--- 5358,5364 ----
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,5340 ****
erret:
if (ret == FAIL)
{
ga_clear(instr);
ufunc->uf_dfunc_idx = -1;
! --def_functions.ga_len;
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;
ga_clear(&cctx.ctx_type_stack);
- ga_clear(&cctx.ctx_locals);
}
/*
* Delete an instruction, free what it contains.
*/
! static void
delete_instr(isn_T *isn)
{
switch (isn->isn_type)
--- 5368,5402 ----
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;
! 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"));
}
current_sctx = save_current_sctx;
+ free_imported(&cctx);
+ free_local(&cctx);
ga_clear(&cctx.ctx_type_stack);
}
/*
* Delete an instruction, free what it contains.
*/
! void
delete_instr(isn_T *isn)
{
switch (isn->isn_type)
***************
*** 5443,5474 ****
}
/*
* 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;
}
}
#if defined(EXITFREE) || defined(PROTO)
void
free_def_functions(void)
{
! vim_free(def_functions.ga_data);
}
#endif
--- 5505,5561 ----
}
/*
+ * 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)
{
if (ufunc->uf_dfunc_idx >= 0)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
! delete_def_function_contents(dfunc);
}
}
#if defined(EXITFREE) || defined(PROTO)
+ /*
+ * Free all functions defined with ":def".
+ */
void
free_def_functions(void)
{
! 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
*** ../vim-8.2.0418/src/vim9execute.c 2020-03-12 19:15:36.212964855 +0100
--- src/vim9execute.c 2020-03-20 18:26:23.851779585 +0100
***************
*** 283,288 ****
--- 283,289 ----
// 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,490 ****
for (;;)
{
isn_T *iptr;
! trycmd_T *trycmd = NULL;
if (did_throw && !ectx.ec_in_catch)
{
garray_T *trystack = &ectx.ec_trystack;
// An exception jumps to the first catch, finally, or returns from
// the current function.
--- 481,501 ----
for (;;)
{
isn_T *iptr;
!
! 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,789 ****
// store $ENV
case ISN_STOREENV:
--ectx.ec_stack.ga_len;
! vim_setenv_ext(iptr->isn_arg.string,
! tv_get_string(STACK_TV_BOT(0)));
break;
// store @r
--- 793,801 ----
// store $ENV
case ISN_STOREENV:
--ectx.ec_stack.ga_len;
! tv = STACK_TV_BOT(0);
! vim_setenv_ext(iptr->isn_arg.string, tv_get_string(tv));
! clear_tv(tv);
break;
// store @r
***************
*** 1038,1043 ****
--- 1050,1056 ----
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,1151 ****
--- 1159,1166 ----
// 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,1186 ****
if (trystack->ga_len > 0)
{
! trycmd = ((trycmd_T *)trystack->ga_data)
+ trystack->ga_len - 1;
trycmd->tcd_caught = TRUE;
}
--- 1195,1201 ----
if (trystack->ga_len > 0)
{
! trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)
+ trystack->ga_len - 1;
trycmd->tcd_caught = TRUE;
}
***************
*** 1196,1201 ****
--- 1211,1218 ----
if (trystack->ga_len > 0)
{
+ trycmd_T *trycmd = NULL;
+
--trystack->ga_len;
--trylevel;
trycmd = ((trycmd_T *)trystack->ga_data)
***************
*** 1677,1682 ****
--- 1694,1700 ----
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;
}
*** ../vim-8.2.0418/src/vim9script.c 2020-03-09 19:25:21.208186448 +0100
--- src/vim9script.c 2020-03-20 18:26:23.851779585 +0100
***************
*** 119,129 ****
for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
{
! imported_T *imp = ((imported_T *)si->sn_imports.ga_data + idx);
vim_free(imp->imp_name);
}
ga_clear(&si->sn_imports);
}
/*
--- 119,131 ----
for (idx = 0; idx < si->sn_imports.ga_len; ++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);
}
/*
*** ../vim-8.2.0418/src/version.c 2020-03-20 18:20:47.084975607 +0100
--- src/version.c 2020-03-20 18:24:29.700185662 +0100
***************
*** 740,741 ****
--- 740,743 ----
{ /* Add new patch number below this line */
+ /**/
+ 419,
/**/
--
You were lucky to have a LAKE! There were a hundred and sixty of
us living in a small shoebox in the middle of the road.
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///