Skip to content

Commit fcfe1a9

Browse files
committed
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)
1 parent 7a4ea1d commit fcfe1a9

File tree

6 files changed

+45
-10
lines changed

6 files changed

+45
-10
lines changed

src/eval.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4734,7 +4734,7 @@ eval7(
47344734
*arg = skipwhite(*arg);
47354735

47364736
/* Handle following '[', '(' and '.' for expr[expr], expr.name,
4737-
* expr(expr). */
4737+
* expr(expr), expr->name(expr) */
47384738
if (ret == OK)
47394739
ret = handle_subscript(arg, rettv, evaluate, TRUE);
47404740

@@ -4824,7 +4824,7 @@ eval_method(
48244824

48254825
// Locate the method name.
48264826
name = *arg;
4827-
for (len = 0; ASCII_ISALNUM(name[len]) || name[len] == '_'; ++len)
4827+
for (len = 0; eval_isnamec(name[len]); ++len)
48284828
;
48294829
if (len == 0)
48304830
{
@@ -4842,6 +4842,8 @@ eval_method(
48424842
}
48434843
*arg += len;
48444844

4845+
// TODO: if "name" is a function reference, resolve it.
4846+
48454847
vim_memset(&funcexe, 0, sizeof(funcexe));
48464848
funcexe.evaluate = evaluate;
48474849
funcexe.basetv = &base;

src/testdir/sautest/autoload/foo.vim

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ let foo#bar = {}
55
func foo#bar.echo()
66
let g:called_foo_bar_echo += 1
77
endfunc
8+
9+
func foo#addFoo(head)
10+
return a:head .. 'foo'
11+
endfunc

src/testdir/test_autoload.vim

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ func Test_autoload_dict_func()
88
call g:foo#bar.echo()
99
call assert_equal(1, g:loaded_foo_vim)
1010
call assert_equal(1, g:called_foo_bar_echo)
11+
12+
eval 'bar'->g:foo#addFoo()->assert_equal('barfoo')
1113
endfunc
1214

1315
func Test_source_autoload()

src/testdir/test_user_func.vim

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func FuncWithRef(a)
4747
endfunc
4848

4949
func Test_user_func()
50-
let g:FuncRef=function("FuncWithRef")
50+
let g:FuncRef = function("FuncWithRef")
5151
let g:counter = 0
5252
inoremap <expr> ( ListItem()
5353
inoremap <expr> [ ListReset()
@@ -62,6 +62,14 @@ func Test_user_func()
6262
call assert_equal(9, g:retval)
6363
call assert_equal(333, g:FuncRef(333))
6464

65+
let g:retval = "nop"
66+
call assert_equal('xxx4asdf', "xxx"->Table(4, "asdf"))
67+
call assert_equal('fail', 45->Compute(0, "retval"))
68+
call assert_equal('nop', g:retval)
69+
call assert_equal('ok', 45->Compute(5, "retval"))
70+
call assert_equal(9, g:retval)
71+
" call assert_equal(333, 333->g:FuncRef())
72+
6573
enew
6674

6775
normal oXX+-XX
@@ -144,3 +152,11 @@ func Test_default_arg()
144152
\ .. " endfunction",
145153
\ execute('func Args2'))
146154
endfunc
155+
156+
func s:addFoo(lead)
157+
return a:lead .. 'foo'
158+
endfunc
159+
160+
func Test_user_method()
161+
eval 'bar'->s:addFoo()->assert_equal('barfoo')
162+
endfunc

src/userfunc.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,7 +1495,8 @@ call_func(
14951495
int argcount = argcount_in;
14961496
typval_T *argvars = argvars_in;
14971497
dict_T *selfdict = funcexe->selfdict;
1498-
typval_T argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
1498+
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
1499+
// "funcexe->basetv" is not NULL
14991500
int argv_clear = 0;
15001501
partial_T *partial = funcexe->partial;
15011502

@@ -1554,10 +1555,7 @@ call_func(
15541555
/*
15551556
* User defined function.
15561557
*/
1557-
if (funcexe->basetv != NULL)
1558-
// TODO: support User function: base->Method()
1559-
fp = NULL;
1560-
else if (partial != NULL && partial->pt_func != NULL)
1558+
if (partial != NULL && partial->pt_func != NULL)
15611559
fp = partial->pt_func;
15621560
else
15631561
fp = find_func(rfname);
@@ -1586,6 +1584,16 @@ call_func(
15861584
argcount = funcexe->argv_func(argcount, argvars,
15871585
fp->uf_args.ga_len);
15881586

1587+
if (funcexe->basetv != NULL)
1588+
{
1589+
// Method call: base->Method()
1590+
mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount);
1591+
argv[0] = *funcexe->basetv;
1592+
argcount++;
1593+
}
1594+
else
1595+
memcpy(argv, argvars, sizeof(typval_T) * argcount);
1596+
15891597
if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL)
15901598
*funcexe->doesrange = TRUE;
15911599
if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len)
@@ -1613,7 +1621,7 @@ call_func(
16131621
did_save_redo = TRUE;
16141622
}
16151623
++fp->uf_calls;
1616-
call_user_func(fp, argcount, argvars, rettv,
1624+
call_user_func(fp, argcount, argv, rettv,
16171625
funcexe->firstline, funcexe->lastline,
16181626
(fp->uf_flags & FC_DICT) ? selfdict : NULL);
16191627
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
@@ -1630,7 +1638,8 @@ call_func(
16301638
else if (funcexe->basetv != NULL)
16311639
{
16321640
/*
1633-
* Find the method name in the table, call its implementation.
1641+
* expr->method(): Find the method name in the table, call its
1642+
* implementation with the base as one of the arguments.
16341643
*/
16351644
error = call_internal_method(fname, argcount, argvars, rettv,
16361645
funcexe->basetv);

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,8 @@ static char *(features[]) =
773773

774774
static int included_patches[] =
775775
{ /* Add new patch number below this line */
776+
/**/
777+
1816,
776778
/**/
777779
1815,
778780
/**/

0 commit comments

Comments
 (0)