To: vim_dev@googlegroups.com Subject: Patch 8.2.4040 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4040 Problem: Keeping track of allocated lines in user functions is too complicated. Solution: Instead of freeing individual lines keep them all until the end. Files: src/alloc.c, src/proto/alloc.pro, src/vim9compile.c, src/userfunc.c, src/proto/userfunc.pro, src/message.c, src/usercmd.c, src/viminfo.c, src/testdir/test_vim9_func.vim *** ../vim-8.2.4039/src/alloc.c 2022-01-08 12:41:12.200795557 +0000 --- src/alloc.c 2022-01-08 14:44:41.710763922 +0000 *************** *** 702,708 **** } void ! ga_init2(garray_T *gap, int itemsize, int growsize) { ga_init(gap); gap->ga_itemsize = itemsize; --- 702,708 ---- } void ! ga_init2(garray_T *gap, size_t itemsize, int growsize) { ga_init(gap); gap->ga_itemsize = itemsize; *************** *** 789,795 **** * When out of memory nothing changes and FAIL is returned. */ int ! ga_add_string(garray_T *gap, char_u *p) { char_u *cp = vim_strsave(p); --- 789,795 ---- * When out of memory nothing changes and FAIL is returned. */ int ! ga_copy_string(garray_T *gap, char_u *p) { char_u *cp = vim_strsave(p); *************** *** 805,810 **** --- 805,823 ---- return OK; } + /* + * Add string "p" to "gap". + * When out of memory "p" is freed and FAIL is returned. + */ + int + ga_add_string(garray_T *gap, char_u *p) + { + if (ga_grow(gap, 1) == FAIL) + return FAIL; + ((char_u **)(gap->ga_data))[gap->ga_len++] = p; + return OK; + } + /* * Concatenate a string to a growarray which contains bytes. * When "s" is NULL does not do anything. *** ../vim-8.2.4039/src/proto/alloc.pro 2021-08-06 20:51:33.896689086 +0100 --- src/proto/alloc.pro 2022-01-08 14:44:10.438826145 +0000 *************** *** 17,26 **** void ga_clear_strings(garray_T *gap); int ga_copy_strings(garray_T *from, garray_T *to); void ga_init(garray_T *gap); ! void ga_init2(garray_T *gap, int itemsize, int growsize); int ga_grow(garray_T *gap, int n); int ga_grow_inner(garray_T *gap, int n); char_u *ga_concat_strings(garray_T *gap, char *sep); int ga_add_string(garray_T *gap, char_u *p); void ga_concat(garray_T *gap, char_u *s); void ga_concat_len(garray_T *gap, char_u *s, size_t len); --- 17,27 ---- void ga_clear_strings(garray_T *gap); int ga_copy_strings(garray_T *from, garray_T *to); void ga_init(garray_T *gap); ! void ga_init2(garray_T *gap, size_t itemsize, int growsize); int ga_grow(garray_T *gap, int n); int ga_grow_inner(garray_T *gap, int n); char_u *ga_concat_strings(garray_T *gap, char *sep); + int ga_copy_string(garray_T *gap, char_u *p); int ga_add_string(garray_T *gap, char_u *p); void ga_concat(garray_T *gap, char_u *s); void ga_concat_len(garray_T *gap, char_u *s, size_t len); *** ../vim-8.2.4039/src/vim9compile.c 2022-01-06 21:10:24.469027861 +0000 --- src/vim9compile.c 2022-01-08 14:53:52.765751633 +0000 *************** *** 810,816 **** * Compile a nested :def command. */ static char_u * ! compile_nested_function(exarg_T *eap, cctx_T *cctx, char_u **line_to_free) { int is_global = *eap->arg == 'g' && eap->arg[1] == ':'; char_u *name_start = eap->arg; --- 810,816 ---- * Compile a nested :def command. */ static char_u * ! compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) { int is_global = *eap->arg == 'g' && eap->arg[1] == ':'; char_u *name_start = eap->arg; *************** *** 876,882 **** goto theend; } ! ufunc = define_function(eap, lambda_name, line_to_free); if (ufunc == NULL) { r = eap->skip ? OK : FAIL; --- 876,882 ---- goto theend; } ! ufunc = define_function(eap, lambda_name, lines_to_free); if (ufunc == NULL) { r = eap->skip ? OK : FAIL; *************** *** 2496,2502 **** cctx_T *outer_cctx) { char_u *line = NULL; ! char_u *line_to_free = NULL; char_u *p; char *errormsg = NULL; // error message cctx_T cctx; --- 2496,2502 ---- cctx_T *outer_cctx) { char_u *line = NULL; ! garray_T lines_to_free; char_u *p; char *errormsg = NULL; // error message cctx_T cctx; *************** *** 2514,2519 **** --- 2514,2522 ---- #endif int debug_lnum = -1; + // allocated lines are freed at the end + ga_init2(&lines_to_free, sizeof(char_u *), 50); + // When using a function that was compiled before: Free old instructions. // The index is reused. Otherwise add a new entry in "def_functions". if (ufunc->uf_dfunc_idx > 0) *************** *** 2681,2688 **** if (line != NULL) { line = vim_strsave(line); ! vim_free(line_to_free); ! line_to_free = line; } } --- 2684,2691 ---- if (line != NULL) { line = vim_strsave(line); ! if (ga_add_string(&lines_to_free, line) == FAIL) ! goto erret; } } *************** *** 2926,2932 **** case CMD_def: case CMD_function: ea.arg = p; ! line = compile_nested_function(&ea, &cctx, &line_to_free); break; case CMD_return: --- 2929,2935 ---- case CMD_def: case CMD_function: ea.arg = p; ! line = compile_nested_function(&ea, &cctx, &lines_to_free); break; case CMD_return: *************** *** 3236,3242 **** if (do_estack_push) estack_pop(); ! vim_free(line_to_free); free_imported(&cctx); free_locals(&cctx); ga_clear(&cctx.ctx_type_stack); --- 3239,3245 ---- if (do_estack_push) estack_pop(); ! ga_clear_strings(&lines_to_free); free_imported(&cctx); free_locals(&cctx); ga_clear(&cctx.ctx_type_stack); *** ../vim-8.2.4039/src/userfunc.c 2022-01-08 12:41:12.212795547 +0000 --- src/userfunc.c 2022-01-08 14:53:27.613795429 +0000 *************** *** 166,178 **** /* * Handle line continuation in function arguments or body. ! * Get a next line, store it in "eap" if appropriate and use "line_to_free" to ! * handle freeing the line later. */ static char_u * get_function_line( exarg_T *eap, ! char_u **line_to_free, int indent, getline_opt_T getline_options) { --- 166,178 ---- /* * Handle line continuation in function arguments or body. ! * Get a next line, store it in "eap" if appropriate and put the line in ! * "lines_to_free" to free the line later. */ static char_u * get_function_line( exarg_T *eap, ! garray_T *lines_to_free, int indent, getline_opt_T getline_options) { *************** *** 184,193 **** theline = eap->getline(':', eap->cookie, indent, getline_options); if (theline != NULL) { ! if (*eap->cmdlinep == *line_to_free) *eap->cmdlinep = theline; ! vim_free(*line_to_free); ! *line_to_free = theline; } return theline; --- 184,194 ---- theline = eap->getline(':', eap->cookie, indent, getline_options); if (theline != NULL) { ! if (lines_to_free->ga_len > 0 ! && *eap->cmdlinep == ((char_u **)lines_to_free->ga_data) ! [lines_to_free->ga_len - 1]) *eap->cmdlinep = theline; ! ga_add_string(lines_to_free, theline); } return theline; *************** *** 210,216 **** garray_T *default_args, int skip, exarg_T *eap, ! char_u **line_to_free) { int mustend = FALSE; char_u *arg; --- 211,217 ---- garray_T *default_args, int skip, exarg_T *eap, ! garray_T *lines_to_free) { int mustend = FALSE; char_u *arg; *************** *** 241,247 **** && (*p == NUL || (VIM_ISWHITE(*whitep) && *p == '#'))) { // End of the line, get the next one. ! char_u *theline = get_function_line(eap, line_to_free, 0, GETLINE_CONCAT_CONT); if (theline == NULL) --- 242,248 ---- && (*p == NUL || (VIM_ISWHITE(*whitep) && *p == '#'))) { // End of the line, get the next one. ! char_u *theline = get_function_line(eap, lines_to_free, 0, GETLINE_CONCAT_CONT); if (theline == NULL) *************** *** 677,683 **** exarg_T *eap, garray_T *newlines, char_u *line_arg_in, ! char_u **line_to_free) { linenr_T sourcing_lnum_top = SOURCING_LNUM; linenr_T sourcing_lnum_off; --- 678,684 ---- exarg_T *eap, garray_T *newlines, char_u *line_arg_in, ! garray_T *lines_to_free) { linenr_T sourcing_lnum_top = SOURCING_LNUM; linenr_T sourcing_lnum_off; *************** *** 744,750 **** } else { ! theline = get_function_line(eap, line_to_free, indent, getline_options); } if (KeyTyped) --- 745,751 ---- } else { ! theline = get_function_line(eap, lines_to_free, indent, getline_options); } if (KeyTyped) *************** *** 854,867 **** { // Another command follows. If the line came from "eap" // we can simply point into it, otherwise we need to ! // change "eap->cmdlinep". eap->nextcmd = nextcmd; ! if (*line_to_free != NULL ! && *eap->cmdlinep != *line_to_free) { vim_free(*eap->cmdlinep); ! *eap->cmdlinep = *line_to_free; ! *line_to_free = NULL; } } break; --- 855,874 ---- { // Another command follows. If the line came from "eap" // we can simply point into it, otherwise we need to ! // change "eap->cmdlinep" to point to the last fetched ! // line. eap->nextcmd = nextcmd; ! if (lines_to_free->ga_len > 0 ! && *eap->cmdlinep != ! ((char_u **)lines_to_free->ga_data) ! [lines_to_free->ga_len - 1]) { + // *cmdlinep will be freed later, thus remove the + // line from lines_to_free. vim_free(*eap->cmdlinep); ! *eap->cmdlinep = ((char_u **)lines_to_free->ga_data) ! [lines_to_free->ga_len - 1]; ! --lines_to_free->ga_len; } } break; *************** *** 1118,1124 **** garray_T newlines; char_u *cmdline = NULL; int ret = FAIL; - char_u *line_to_free = NULL; partial_T *pt; char_u *name; int lnum_save = -1; --- 1125,1130 ---- *************** *** 1144,1155 **** } ga_init2(&newlines, (int)sizeof(char_u *), 10); ! if (get_function_body(&eap, &newlines, NULL, &line_to_free) == FAIL) ! { ! if (cmdline != line_to_free) ! vim_free(cmdline); goto erret; - } // When inside a lambda must add the function lines to evalarg.eval_ga. evalarg->eval_break_count += newlines.ga_len; --- 1150,1158 ---- } ga_init2(&newlines, (int)sizeof(char_u *), 10); ! if (get_function_body(&eap, &newlines, NULL, ! &evalarg->eval_tofree_ga) == FAIL) goto erret; // When inside a lambda must add the function lines to evalarg.eval_ga. evalarg->eval_break_count += newlines.ga_len; *************** *** 1208,1215 **** { ((char_u **)(tfgap->ga_data))[tfgap->ga_len++] = cmdline; evalarg->eval_using_cmdline = TRUE; - if (cmdline == line_to_free) - line_to_free = NULL; } } else --- 1211,1216 ---- *************** *** 1278,1284 **** erret: if (lnum_save >= 0) SOURCING_LNUM = lnum_save; - vim_free(line_to_free); ga_clear_strings(&newlines); if (newargs != NULL) ga_clear_strings(newargs); --- 1279,1284 ---- *************** *** 3957,3966 **** * ":function" also supporting nested ":def". * When "name_arg" is not NULL this is a nested function, using "name_arg" for * the function name. * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * ! define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free) { int j; int c; --- 3957,3967 ---- * ":function" also supporting nested ":def". * When "name_arg" is not NULL this is a nested function, using "name_arg" for * the function name. + * "lines_to_free" is a list of strings to be freed later. * Returns a pointer to the function or NULL if no function defined. */ ufunc_T * ! define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free) { int j; int c; *************** *** 4229,4235 **** if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, line_to_free) == FAIL) goto errret_2; whitep = p; --- 4230,4236 ---- if (get_function_args(&p, ')', &newargs, eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE, NULL, &varargs, &default_args, eap->skip, ! eap, lines_to_free) == FAIL) goto errret_2; whitep = p; *************** *** 4339,4345 **** // Do not define the function when getting the body fails and when // skipping. ! if (get_function_body(eap, &newlines, line_arg, line_to_free) == FAIL || eap->skip) goto erret; --- 4340,4346 ---- // Do not define the function when getting the body fails and when // skipping. ! if (get_function_body(eap, &newlines, line_arg, lines_to_free) == FAIL || eap->skip) goto erret; *************** *** 4645,4654 **** void ex_function(exarg_T *eap) { ! char_u *line_to_free = NULL; ! (void)define_function(eap, NULL, &line_to_free); ! vim_free(line_to_free); } /* --- 4646,4656 ---- void ex_function(exarg_T *eap) { ! garray_T lines_to_free; ! ga_init2(&lines_to_free, sizeof(char_u *), 50); ! (void)define_function(eap, NULL, &lines_to_free); ! ga_clear_strings(&lines_to_free); } /* *** ../vim-8.2.4039/src/proto/userfunc.pro 2021-12-26 14:22:55.661931074 +0000 --- src/proto/userfunc.pro 2022-01-08 14:53:42.313769813 +0000 *************** *** 38,44 **** char_u *get_scriptlocal_funcname(char_u *funcname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, char_u **line_to_free); void ex_function(exarg_T *eap); void ex_defcompile(exarg_T *eap); int eval_fname_script(char_u *p); --- 38,44 ---- char_u *get_scriptlocal_funcname(char_u *funcname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ! ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free); void ex_function(exarg_T *eap); void ex_defcompile(exarg_T *eap); int eval_fname_script(char_u *p); *** ../vim-8.2.4039/src/message.c 2022-01-08 12:41:12.204795554 +0000 --- src/message.c 2022-01-08 14:26:45.048068580 +0000 *************** *** 587,593 **** if (STRCMP("RESET", error) == 0) ga_clear_strings(&ignore_error_list); else ! ga_add_string(&ignore_error_list, error); } static int --- 587,593 ---- if (STRCMP("RESET", error) == 0) ga_clear_strings(&ignore_error_list); else ! ga_copy_string(&ignore_error_list, error); } static int *** ../vim-8.2.4039/src/usercmd.c 2022-01-08 12:41:12.208795550 +0000 --- src/usercmd.c 2022-01-08 14:27:10.416050982 +0000 *************** *** 1021,1027 **** char_u *line = NULL; ga_init2(&ga, sizeof(char_u *), 10); ! if (ga_add_string(&ga, p) == FAIL) return retp; // If the argument ends in "}" it must have been concatenated already --- 1021,1027 ---- char_u *line = NULL; ga_init2(&ga, sizeof(char_u *), 10); ! if (ga_copy_string(&ga, p) == FAIL) return retp; // If the argument ends in "}" it must have been concatenated already *************** *** 1038,1044 **** emsg(_(e_missing_rcurly)); break; } ! if (ga_add_string(&ga, line) == FAIL) break; if (*skipwhite(line) == '}') break; --- 1038,1044 ---- emsg(_(e_missing_rcurly)); break; } ! if (ga_copy_string(&ga, line) == FAIL) break; if (*skipwhite(line) == '}') break; *** ../vim-8.2.4039/src/viminfo.c 2022-01-05 20:24:34.280005633 +0000 --- src/viminfo.c 2022-01-08 14:27:34.928032942 +0000 *************** *** 2730,2736 **** { // Continuation line of an unrecognized item. if (writing) ! ga_add_string(&virp->vir_barlines, virp->vir_line); } else { --- 2730,2736 ---- { // Continuation line of an unrecognized item. if (writing) ! ga_copy_string(&virp->vir_barlines, virp->vir_line); } else { *************** *** 2769,2775 **** default: // copy unrecognized line (for future use) if (writing) ! ga_add_string(&virp->vir_barlines, virp->vir_line); } for (i = 0; i < values.ga_len; ++i) { --- 2769,2775 ---- default: // copy unrecognized line (for future use) if (writing) ! ga_copy_string(&virp->vir_barlines, virp->vir_line); } for (i = 0; i < values.ga_len; ++i) { *** ../vim-8.2.4039/src/testdir/test_vim9_func.vim 2022-01-06 12:23:26.833759704 +0000 --- src/testdir/test_vim9_func.vim 2022-01-08 15:05:39.520564261 +0000 *************** *** 1757,1762 **** --- 1757,1777 ---- CheckScriptFailure(lines, 'E1173: Text found after endfunction: BBBB') enddef + def Test_error_in_function_args() + var lines =<< trim END + def FirstFunction() + def SecondFunction(J = + # Nois + # one + + enddef|BBBB + enddef + # Compile all functions + defcompile + END + CheckScriptFailure(lines, 'E488:') + enddef + def Test_return_type_wrong() CheckScriptFailure([ 'def Func(): number', *************** *** 2048,2054 **** endfunc def Run_Test_free_dict_while_in_funcstack() - # this was freeing the TermRun() default argument dictionary while it was # still referenced in a funcstack_T var lines =<< trim END --- 2063,2068 ---- *** ../vim-8.2.4039/src/version.c 2022-01-08 13:36:24.134742355 +0000 --- src/version.c 2022-01-08 14:59:16.421199494 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4040, /**/ -- hundred-and-one symptoms of being an internet addict: 262. Your computer has it's own phone line - but your daughter doesn't. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///