# HG changeset patch # User Bram Moolenaar # Date 1565039705 -7200 # Node ID e5397617d6ca64036c8ac046058335285ea45aaa # Parent db112d38c15297914110547d89b1c9d29e3dbb9d patch 8.1.1820: using expr->FuncRef() does not work commit https://github.com/vim/vim/commit/761fdf01c6e307c448cec2684f8b315ba6d1f454 Author: Bram Moolenaar Date: Mon Aug 5 23:10:16 2019 +0200 patch 8.1.1820: using expr->FuncRef() does not work Problem: Using expr->FuncRef() does not work. Solution: Make FuncRef work as a method. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -3655,6 +3655,74 @@ pattern_match(char_u *pat, char_u *text, } /* + * Handle a name followed by "(". Both for just "name(arg)" and for + * "expr->name(arg)". + * Returns OK or FAIL. + */ + static int +eval_func( + char_u **arg, // points to "(", will be advanced + char_u *name, + int name_len, + typval_T *rettv, + int evaluate, + typval_T *basetv) // "expr" for "expr->name(arg)" +{ + char_u *s = name; + int len = name_len; + partial_T *partial; + int ret = OK; + + if (!evaluate) + check_vars(s, len); + + /* If "s" is the name of a variable of type VAR_FUNC + * use its contents. */ + s = deref_func_name(s, &len, &partial, !evaluate); + + /* Need to make a copy, in case evaluating the arguments makes + * the name invalid. */ + s = vim_strsave(s); + if (s == NULL) + ret = FAIL; + else + { + funcexe_T funcexe; + + // Invoke the function. + vim_memset(&funcexe, 0, sizeof(funcexe)); + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.doesrange = &len; + funcexe.evaluate = evaluate; + funcexe.partial = partial; + funcexe.basetv = basetv; + ret = get_func_tv(s, len, rettv, arg, &funcexe); + } + vim_free(s); + + /* If evaluate is FALSE rettv->v_type was not set in + * get_func_tv, but it's needed in handle_subscript() to parse + * what follows. So set it here. */ + if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') + { + rettv->vval.v_string = NULL; + rettv->v_type = VAR_FUNC; + } + + /* Stop the expression evaluation when immediately + * aborting on error, or when an interrupt occurred or + * an exception was thrown but not caught. */ + if (evaluate && aborting()) + { + if (ret == OK) + clear_tv(rettv); + ret = FAIL; + } + return ret; +} + +/* * The "evaluate" argument: When FALSE, the argument is only parsed but not * executed. The function may return OK, but the rettv will be of type * VAR_UNKNOWN. The function still returns FAIL for a syntax error. @@ -4671,55 +4739,7 @@ eval7( else { if (**arg == '(') /* recursive! */ - { - partial_T *partial; - - if (!evaluate) - check_vars(s, len); - - /* If "s" is the name of a variable of type VAR_FUNC - * use its contents. */ - s = deref_func_name(s, &len, &partial, !evaluate); - - /* Need to make a copy, in case evaluating the arguments makes - * the name invalid. */ - s = vim_strsave(s); - if (s == NULL) - ret = FAIL; - else - { - funcexe_T funcexe; - - // Invoke the function. - vim_memset(&funcexe, 0, sizeof(funcexe)); - funcexe.firstline = curwin->w_cursor.lnum; - funcexe.lastline = curwin->w_cursor.lnum; - funcexe.doesrange = &len; - funcexe.evaluate = evaluate; - funcexe.partial = partial; - ret = get_func_tv(s, len, rettv, arg, &funcexe); - } - vim_free(s); - - /* If evaluate is FALSE rettv->v_type was not set in - * get_func_tv, but it's needed in handle_subscript() to parse - * what follows. So set it here. */ - if (rettv->v_type == VAR_UNKNOWN && !evaluate && **arg == '(') - { - rettv->vval.v_string = NULL; - rettv->v_type = VAR_FUNC; - } - - /* Stop the expression evaluation when immediately - * aborting on error, or when an interrupt occurred or - * an exception was thrown but not caught. */ - if (evaluate && aborting()) - { - if (ret == OK) - clear_tv(rettv); - ret = FAIL; - } - } + ret = eval_func(arg, s, len, rettv, evaluate, NULL); else if (evaluate) ret = get_var_tv(s, len, rettv, NULL, TRUE, FALSE); else @@ -4815,55 +4835,42 @@ eval_method( { char_u *name; long len; - funcexe_T funcexe; - int ret = OK; + char_u *alias; typval_T base = *rettv; + int ret; // Skip over the ->. *arg += 2; - - // Locate the method name. + rettv->v_type = VAR_UNKNOWN; + name = *arg; - for (len = 0; eval_isnamec(name[len]); ++len) - ; - if (len == 0) + len = get_name_len(arg, &alias, evaluate, TRUE); + if (alias != NULL) + name = alias; + + if (len <= 0) { if (verbose) emsg(_("E260: Missing name after ->")); - return FAIL; - } - - // Check for the "(". Skip over white space after it. - if (name[len] != '(') - { - if (verbose) - semsg(_(e_missingparen), name); - return FAIL; - } - *arg += len; - - // TODO: if "name" is a function reference, resolve it. - - vim_memset(&funcexe, 0, sizeof(funcexe)); - funcexe.evaluate = evaluate; - funcexe.basetv = &base; - rettv->v_type = VAR_UNKNOWN; - ret = get_func_tv(name, len, rettv, arg, &funcexe); + ret = FAIL; + } + else + { + if (**arg != '(') + { + if (verbose) + semsg(_(e_missingparen), name); + ret = FAIL; + } + else + ret = eval_func(arg, name, len, rettv, evaluate, &base); + } /* Clear the funcref afterwards, so that deleting it while * evaluating the arguments is possible (see test55). */ if (evaluate) clear_tv(&base); - /* Stop the expression evaluation when immediately aborting on - * error, or when an interrupt occurred or an exception was thrown - * but not caught. */ - if (aborting()) - { - if (ret == OK) - clear_tv(rettv); - ret = FAIL; - } return ret; } diff --git a/src/testdir/test_method.vim b/src/testdir/test_method.vim --- a/src/testdir/test_method.vim +++ b/src/testdir/test_method.vim @@ -1,6 +1,6 @@ " Tests for ->method() -func Test_list() +func Test_list_method() let l = [1, 2, 3] call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4)) eval l->assert_equal(l) @@ -34,7 +34,7 @@ func Test_list() call assert_fails('eval l->values()', 'E715:') endfunc -func Test_dict() +func Test_dict_method() let d = #{one: 1, two: 2, three: 3} call assert_equal(d, d->copy()) @@ -66,7 +66,7 @@ func Test_dict() call assert_equal([1, 2, 3], d->values()) endfunc -func Test_string() +func Test_string_method() call assert_equal(['1', '2', '3'], '1 2 3'->split()) call assert_equal([1, 2, 3], '1 2 3'->split()->map({i, v -> str2nr(v)})) call assert_equal([65, 66, 67], 'ABC'->str2list()) @@ -76,7 +76,7 @@ func Test_string() call assert_equal('axc', 'abc'->substitute('b', 'x', '')) endfunc -func Test_append() +func Test_method_append() new eval ['one', 'two', 'three']->append(1) call assert_equal(['', 'one', 'two', 'three'], getline(1, '$')) @@ -89,3 +89,16 @@ func Test_append() exe 'bwipe! ' .. bnr endfunc + +func Test_method_funcref() + func Concat(one, two, three) + return a:one .. a:two .. a:three + endfunc + let FuncRef = function('Concat') + eval 'foo'->FuncRef('bar', 'tail')->assert_equal('foobartail') + + let Partial = function('Concat', ['two']) + eval 'one'->Partial('three')->assert_equal('onetwothree') + + delfunc Concat +endfunc diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1498,6 +1498,7 @@ call_func( typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or // "funcexe->basetv" is not NULL int argv_clear = 0; + int argv_base = 0; partial_T *partial = funcexe->partial; // Make a copy of the name, if it comes from a funcref variable it could @@ -1590,9 +1591,9 @@ call_func( mch_memmove(&argv[1], argvars, sizeof(typval_T) * argcount); argv[0] = *funcexe->basetv; argcount++; + argvars = argv; + argv_base = 1; } - else - memcpy(argv, argvars, sizeof(typval_T) * argcount); if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) *funcexe->doesrange = TRUE; @@ -1621,7 +1622,7 @@ call_func( did_save_redo = TRUE; } ++fp->uf_calls; - call_user_func(fp, argcount, argv, rettv, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) @@ -1698,8 +1699,10 @@ call_func( } } + // clear the copies made from the partial while (argv_clear > 0) - clear_tv(&argv[--argv_clear]); + clear_tv(&argv[--argv_clear + argv_base]); + vim_free(tofree); vim_free(name); 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 */ /**/ + 1820, +/**/ 1819, /**/ 1818,