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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220108154015.679AE1C0D97%40moolenaar.net.

Raspunde prin e-mail lui