Mercurial > vim
diff src/vim9compile.c @ 19726:ad37a198a708 v8.2.0419
patch 8.2.0419: various memory leaks in Vim9 script code
Commit: https://github.com/vim/vim/commit/20431c9dbb592ebe0666bf042af7d2b373107372
Author: Bram Moolenaar <Bram@vim.org>
Date: Fri Mar 20 18:39:46 2020 +0100
patch 8.2.0419: various memory leaks in Vim9 script code
Problem: Various memory leaks in Vim9 script code.
Solution: Fix the leaks. (Ozaki Kiichi, closes https://github.com/vim/vim/issues/5814)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Fri, 20 Mar 2020 18:45:04 +0100 |
parents | 2fee087c94cb |
children | 9daed26b788b |
line wrap: on
line diff
--- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -129,6 +129,7 @@ static char e_syntax_at[] = N_("E1002: 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. @@ -1306,6 +1307,36 @@ reserve_local(cctx_T *cctx, char_u *name } /* + * 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,6 +1703,23 @@ find_imported(char_u *name, size_t len, } /* + * 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,7 +2175,10 @@ compile_lambda(char_u **arg, cctx_T *cct // 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 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. - ufunc = rettv.vval.v_partial->pt_func; - ++ufunc->uf_refcount; compile_def_function(ufunc, TRUE); // compile the arguments @@ -2181,7 +2234,6 @@ compile_lambda_call(char_u **arg, cctx_T // 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 { 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 } 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 *cc 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 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 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 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) @@ -5443,32 +5505,57 @@ 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