# HG changeset patch # User Bram Moolenaar # Date 1564953305 -7200 # Node ID 9ffec4eb8d33493b4fa4467ce676aa3e3c94b578 # Parent 69800308526780529d301eaca33609d549cdb1dc patch 8.1.1816: cannot use a user defined function as a method commit https://github.com/vim/vim/commit/fcfe1a9b8950b8b211ab3b24d84b17c6847ea43f Author: Bram Moolenaar 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) diff --git a/src/eval.c b/src/eval.c --- 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; diff --git a/src/testdir/sautest/autoload/foo.vim b/src/testdir/sautest/autoload/foo.vim --- 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 diff --git a/src/testdir/test_autoload.vim b/src/testdir/test_autoload.vim --- 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() diff --git a/src/testdir/test_user_func.vim b/src/testdir/test_user_func.vim --- 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 ( ListItem() inoremap [ 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 diff --git a/src/userfunc.c b/src/userfunc.c --- 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); diff --git a/src/version.c b/src/version.c --- 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,