changeset 17638:9ffec4eb8d33 v8.1.1816

patch 8.1.1816: cannot use a user defined function as a method commit https://github.com/vim/vim/commit/fcfe1a9b8950b8b211ab3b24d84b17c6847ea43f Author: Bram Moolenaar <Bram@vim.org> Date: Sun Aug 4 23:04:39 2019 +0200 patch 8.1.1816: cannot use a user defined function as a method Problem: Cannot use a user defined function as a method. Solution: Pass the base as the first argument to the user defined function after "->". (partly by FUJIWARA Takuya)
author Bram Moolenaar <Bram@vim.org>
date Sun, 04 Aug 2019 23:15:05 +0200
parents 698003085267
children 9b97b010f12f
files src/eval.c src/testdir/sautest/autoload/foo.vim src/testdir/test_autoload.vim src/testdir/test_user_func.vim src/userfunc.c src/version.c
diffstat 6 files changed, 45 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -4734,7 +4734,7 @@ eval7(
     *arg = skipwhite(*arg);
 
     /* Handle following '[', '(' and '.' for expr[expr], expr.name,
-     * expr(expr). */
+     * expr(expr), expr->name(expr) */
     if (ret == OK)
 	ret = handle_subscript(arg, rettv, evaluate, TRUE);
 
@@ -4824,7 +4824,7 @@ eval_method(
 
     // Locate the method name.
     name = *arg;
-    for (len = 0; ASCII_ISALNUM(name[len]) || name[len] == '_'; ++len)
+    for (len = 0; eval_isnamec(name[len]); ++len)
 	;
     if (len == 0)
     {
@@ -4842,6 +4842,8 @@ eval_method(
     }
     *arg += len;
 
+    // TODO: if "name" is a function reference, resolve it.
+
     vim_memset(&funcexe, 0, sizeof(funcexe));
     funcexe.evaluate = evaluate;
     funcexe.basetv = &base;
--- a/src/testdir/sautest/autoload/foo.vim
+++ b/src/testdir/sautest/autoload/foo.vim
@@ -5,3 +5,7 @@ let foo#bar = {}
 func foo#bar.echo()
   let g:called_foo_bar_echo += 1
 endfunc
+
+func foo#addFoo(head)
+  return a:head .. 'foo'
+endfunc
--- a/src/testdir/test_autoload.vim
+++ b/src/testdir/test_autoload.vim
@@ -8,6 +8,8 @@ func Test_autoload_dict_func()
   call g:foo#bar.echo()
   call assert_equal(1, g:loaded_foo_vim)
   call assert_equal(1, g:called_foo_bar_echo)
+
+  eval 'bar'->g:foo#addFoo()->assert_equal('barfoo')
 endfunc
 
 func Test_source_autoload()
--- a/src/testdir/test_user_func.vim
+++ b/src/testdir/test_user_func.vim
@@ -47,7 +47,7 @@ func FuncWithRef(a)
 endfunc
 
 func Test_user_func()
-  let g:FuncRef=function("FuncWithRef")
+  let g:FuncRef = function("FuncWithRef")
   let g:counter = 0
   inoremap <expr> ( ListItem()
   inoremap <expr> [ ListReset()
@@ -62,6 +62,14 @@ func Test_user_func()
   call assert_equal(9, g:retval)
   call assert_equal(333, g:FuncRef(333))
 
+  let g:retval = "nop"
+  call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
+  call assert_equal('fail', 45->Compute(0, "retval"))
+  call assert_equal('nop', g:retval)
+  call assert_equal('ok', 45->Compute(5, "retval"))
+  call assert_equal(9, g:retval)
+  " call assert_equal(333, 333->g:FuncRef())
+
   enew
 
   normal oXX+-XX
@@ -144,3 +152,11 @@ func Test_default_arg()
 	\ .. "   endfunction",
 	\ execute('func Args2'))
 endfunc
+
+func s:addFoo(lead)
+  return a:lead .. 'foo'
+endfunc
+
+func Test_user_method()
+  eval 'bar'->s:addFoo()->assert_equal('barfoo')
+endfunc
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -1495,7 +1495,8 @@ call_func(
     int		argcount = argcount_in;
     typval_T	*argvars = argvars_in;
     dict_T	*selfdict = funcexe->selfdict;
-    typval_T	argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
+    typval_T	argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
+					 // "funcexe->basetv" is not NULL
     int		argv_clear = 0;
     partial_T	*partial = funcexe->partial;
 
@@ -1554,10 +1555,7 @@ call_func(
 	    /*
 	     * User defined function.
 	     */
-	    if (funcexe->basetv != NULL)
-		// TODO: support User function: base->Method()
-		fp = NULL;
-	    else if (partial != NULL && partial->pt_func != NULL)
+	    if (partial != NULL && partial->pt_func != NULL)
 		fp = partial->pt_func;
 	    else
 		fp = find_func(rfname);
@@ -1586,6 +1584,16 @@ call_func(
 		    argcount = funcexe->argv_func(argcount, argvars,
 							   fp->uf_args.ga_len);
 
+		if (funcexe->basetv != NULL)
+		{
+		    // Method call: base->Method()
+		    mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount);
+		    argv[0] = *funcexe->basetv;
+		    argcount++;
+		}
+		else
+		    memcpy(argv, argvars, sizeof(typval_T) * argcount);
+
 		if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
 		    *funcexe->doesrange = TRUE;
 		if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len)
@@ -1613,7 +1621,7 @@ call_func(
 			did_save_redo = TRUE;
 		    }
 		    ++fp->uf_calls;
-		    call_user_func(fp, argcount, argvars, rettv,
+		    call_user_func(fp, argcount, argv, rettv,
 					 funcexe->firstline, funcexe->lastline,
 				  (fp->uf_flags & FC_DICT) ? selfdict : NULL);
 		    if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
@@ -1630,7 +1638,8 @@ call_func(
 	else if (funcexe->basetv != NULL)
 	{
 	    /*
-	     * Find the method name in the table, call its implementation.
+	     * expr->method(): Find the method name in the table, call its
+	     * implementation with the base as one of the arguments.
 	     */
 	    error = call_internal_method(fname, argcount, argvars, rettv,
 							      funcexe->basetv);
--- a/src/version.c
+++ b/src/version.c
@@ -774,6 +774,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1816,
+/**/
     1815,
 /**/
     1814,