]> granicus.if.org Git - vim/commitdiff
patch 8.2.2864: Vim9: crash when using inline function v8.2.2864
authorBram Moolenaar <Bram@vim.org>
Tue, 18 May 2021 09:47:44 +0000 (11:47 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 18 May 2021 09:47:44 +0000 (11:47 +0200)
Problem:    Vim9: crash when using inline function.
Solution:   Check for NULL pointer. Make using inline function work inside
            lambda. (closes #8217)

src/testdir/test_vim9_func.vim
src/userfunc.c
src/version.c

index 4d57eef71eec7fa0b921faa1a215e489a97d4b05..7eca70919dd2dd479d77548c20eac40536ce0ee8 100644 (file)
@@ -2102,6 +2102,20 @@ def Test_nested_lambda()
   CheckScriptSuccess(lines)
 enddef
 
+def Test_nested_inline_lambda()
+  # TODO: use the "text" argument
+  var lines =<< trim END
+      vim9script
+      def F(text: string): func(string): func(string): string
+        return (arg: string): func(string): string => ((sep: string): string => {
+            return sep .. arg
+          })
+      enddef
+      assert_equal('--there', F('unused')('there')('--'))
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 def Shadowed(): list<number>
   var FuncList: list<func: number> = [() => 42]
   return FuncList->mapnew((_, Shadowed) => Shadowed())
index 99d4e0334edff578d6587f43ed8acfcca674ed12..b88b512c5471ea2a7dc4724516586df3b2cee6cf 100644 (file)
@@ -606,6 +606,7 @@ is_function_cmd(char_u **cmd)
 
 /*
  * Read the body of a function, put every line in "newlines".
+ * This stops at "}", "endfunction" or "enddef".
  * "newlines" must already have been initialized.
  * "eap->cmdidx" is CMD_function, CMD_def or CMD_block;
  */
@@ -945,9 +946,8 @@ get_function_body(
            line_arg = NULL;
     }
 
-    // Don't define the function when skipping commands or when an error was
-    // detected.
-    if (!eap->skip && !did_emsg)
+    // Return OK when no error was detected.
+    if (!did_emsg)
        ret = OK;
 
 theend:
@@ -960,6 +960,7 @@ theend:
 /*
  * Handle the body of a lambda.  *arg points to the "{", process statements
  * until the matching "}".
+ * When not evaluating "newargs" is NULL.
  * When successful "rettv" is set to a funcref.
  */
     static int
@@ -974,6 +975,7 @@ lambda_function_body(
        char_u      *ret_type)
 {
     int                evaluate = (evalarg->eval_flags & EVAL_EVALUATE);
+    garray_T   *gap = &evalarg->eval_ga;
     ufunc_T    *ufunc = NULL;
     exarg_T    eap;
     garray_T   newlines;
@@ -1010,6 +1012,52 @@ lambda_function_body(
        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;
+    if (gap->ga_itemsize > 0)
+    {
+       int     idx;
+       char_u  *last;
+       size_t  plen;
+       char_u  *pnl;
+
+       for (idx = 0; idx < newlines.ga_len; ++idx)
+       {
+           char_u  *p = skipwhite(((char_u **)newlines.ga_data)[idx]);
+
+           if (ga_grow(gap, 1) == FAIL)
+               goto erret;
+
+           // Going to concatenate the lines after parsing.  For an empty or
+           // comment line use an empty string.
+           // Insert NL characters at the start of each line, the string will
+           // be split again later in .get_lambda_tv().
+           if (*p == NUL || vim9_comment_start(p))
+               p = (char_u *)"";
+           plen = STRLEN(p);
+           pnl = vim_strnsave((char_u *)"\n", plen + 1);
+           if (pnl != NULL)
+               mch_memmove(pnl + 1, p, plen + 1);
+           ((char_u **)gap->ga_data)[gap->ga_len] = pnl;
+           ++gap->ga_len;
+       }
+       if (ga_grow(gap, 1) == FAIL)
+           goto erret;
+       if (cmdline != NULL)
+           // more is following after the "}", which was skipped
+           last = cmdline;
+       else
+           // nothing is following the "}"
+           last = (char_u *)"}";
+       plen = STRLEN(last);
+       pnl = vim_strnsave((char_u *)"\n", plen + 1);
+       if (pnl != NULL)
+           mch_memmove(pnl + 1, last, plen + 1);
+       ((char_u **)gap->ga_data)[gap->ga_len] = pnl;
+       ++gap->ga_len;
+    }
+
     if (cmdline != NULL)
     {
        // Something comes after the "}".
@@ -1022,6 +1070,12 @@ lambda_function_body(
     else
        *arg = (char_u *)"";
 
+    if (!evaluate)
+    {
+       ret = OK;
+       goto erret;
+    }
+
     name = get_lambda_name();
     ufunc = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
     if (ufunc == NULL)
@@ -1078,7 +1132,8 @@ erret:
        SOURCING_LNUM = lnum_save;
     vim_free(line_to_free);
     ga_clear_strings(&newlines);
-    ga_clear_strings(newargs);
+    if (newargs != NULL)
+       ga_clear_strings(newargs);
     ga_clear_strings(default_args);
     if (ufunc != NULL)
     {
@@ -1222,6 +1277,7 @@ get_lambda_tv(
        int         len;
        int         flags = 0;
        char_u      *p;
+       char_u      *line_end;
        char_u      *name = get_lambda_name();
 
        fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
@@ -1236,14 +1292,37 @@ get_lambda_tv(
        if (ga_grow(&newlines, 1) == FAIL)
            goto errret;
 
-       // Add "return " before the expression.
-       len = 7 + (int)(end - start) + 1;
+       // If there are line breaks, we need to split up the string.
+       line_end = vim_strchr(start, '\n');
+       if (line_end == NULL)
+           line_end = end;
+
+       // Add "return " before the expression (or the first line).
+       len = 7 + (int)(line_end - start) + 1;
        p = alloc(len);
        if (p == NULL)
            goto errret;
        ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
        STRCPY(p, "return ");
-       vim_strncpy(p + 7, start, end - start);
+       vim_strncpy(p + 7, start, line_end - start);
+
+       if (line_end != end)
+       {
+           // Add more lines, split by line breaks.  Thus is used when a
+           // lambda with { cmds } is encountered.
+           while (*line_end == '\n')
+           {
+               if (ga_grow(&newlines, 1) == FAIL)
+                   goto errret;
+               start = line_end + 1;
+               line_end = vim_strchr(start, '\n');
+               if (line_end == NULL)
+                   line_end = end;
+               ((char_u **)(newlines.ga_data))[newlines.ga_len++] =
+                                        vim_strnsave(start, line_end - start);
+           }
+       }
+
        if (strstr((char *)p + 7, "a:") == NULL)
            // No a: variables are used for sure.
            flags |= FC_NOARGS;
index cf5208ffc810a9a5ee58bfa551c176f219fde489..fb720506517d897f973d7f6bc14399447e1bd104 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2864,
 /**/
     2863,
 /**/