From 4042ae5a2bc4bbca608ebb196a3d54a78d6c100c Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 21 May 2021 15:12:35 +0100 Subject: vim-patch:8.1.1800: function call functions have too many arguments Problem: Function call functions have too many arguments. Solution: Pass values in a funcexe_T struct. https://github.com/vim/vim/commit/c6538bcc1cdd1fb83732f22fdc69bd9bb66f968a Use FUNCEXE_INIT to initialize funcexe_T instances. call_callback() and other Vim listener related stuff is N/A. --- src/nvim/eval/funcs.c | 10 +++--- src/nvim/eval/userfunc.c | 80 +++++++++++++++++++++++------------------------- src/nvim/eval/userfunc.h | 23 ++++++++++++++ 3 files changed, 66 insertions(+), 47 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 4e409cca50..363640adc5 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -9420,7 +9420,6 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) int res; typval_T rettv; typval_T argv[3]; - int dummy; const char *func_name; partial_T *partial = sortinfo->item_compare_partial; @@ -9444,10 +9443,11 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero) tv_copy(TV_LIST_ITEM_TV(si2->item), &argv[1]); rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this - res = call_func((const char_u *)func_name, - -1, - &rettv, 2, argv, NULL, 0L, 0L, &dummy, true, - partial, sortinfo->item_compare_selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = sortinfo->item_compare_selfdict; + res = call_func((const char_u *)func_name, -1, &rettv, 2, argv, &funcexe); tv_clear(&argv[0]); tv_clear(&argv[1]); diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index deddec413b..516f7a1d53 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -414,12 +414,7 @@ get_func_tv( int len, // length of "name" or -1 to use strlen() typval_T *rettv, char_u **arg, // argument, pointing to the '(' - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // return: function handled range - int evaluate, - partial_T *partial, // for extra arguments - dict_T *selfdict // Dictionary for "self" + funcexe_T *funcexe // various values ) { char_u *argp; @@ -431,12 +426,13 @@ get_func_tv( * Get the arguments. */ argp = *arg; - while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) { + while (argcount < MAX_FUNC_ARGS + - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) { argp = skipwhite(argp + 1); // skip the '(' or ',' if (*argp == ')' || *argp == ',' || *argp == NUL) { break; } - if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) { + if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) { ret = FAIL; break; } @@ -463,9 +459,7 @@ get_func_tv( ((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i]; } } - ret = call_func(name, len, rettv, argcount, argvars, NULL, - firstline, lastline, doesrange, evaluate, - partial, selfdict); + ret = call_func(name, len, rettv, argcount, argvars, funcexe); funcargs.ga_len -= i; } else if (!aborting()) { @@ -1367,7 +1361,6 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, { typval_T argv[MAX_FUNC_ARGS + 1]; int argc = 0; - int dummy; int r = 0; TV_LIST_ITER(args->vval.v_list, item, { @@ -1380,9 +1373,13 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, tv_copy(TV_LIST_ITEM_TV(item), &argv[argc++]); }); - r = call_func(name, -1, rettv, argc, argv, NULL, - curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &dummy, true, partial, selfdict); + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = curwin->w_cursor.lnum; + funcexe.lastline = curwin->w_cursor.lnum; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = selfdict; + r = call_func(name, -1, rettv, argc, argv, &funcexe); func_call_skip_call: // Free the arguments. @@ -1425,10 +1422,6 @@ static void user_func_error(int error, const char_u *name) /// Call a function with its resolved parameters /// -/// "argv_func", when not NULL, can be used to fill in arguments only when the -/// invoked function uses them. It is called like this: -/// new_argcount = argv_func(current_argcount, argv, called_func_argcount) -/// /// @return FAIL if function cannot be called, else OK (even if an error /// occurred while executing the function! Set `msg_list` to capture /// the error, see do_cmdline()). @@ -1440,15 +1433,9 @@ call_func( int argcount_in, // number of "argvars" typval_T *argvars_in, // vars for arguments, must have "argcount" // PLUS ONE elements! - ArgvFunc argv_func, // function to fill in argvars - linenr_T firstline, // first line of range - linenr_T lastline, // last line of range - int *doesrange, // [out] function handled range - bool evaluate, - partial_T *partial, // optional, can be NULL - dict_T *selfdict_in // Dictionary for "self" + funcexe_T *funcexe // more arguments ) - FUNC_ATTR_NONNULL_ARG(1, 3, 5, 9) + FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6) { int ret = FAIL; int error = ERROR_NONE; @@ -1459,9 +1446,10 @@ call_func( char_u *name = NULL; int argcount = argcount_in; typval_T *argvars = argvars_in; - dict_T *selfdict = selfdict_in; + dict_T *selfdict = funcexe->selfdict; typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" is not NULL int argv_clear = 0; + partial_T *partial = funcexe->partial; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) // even when call_func() returns FAIL. @@ -1480,14 +1468,15 @@ call_func( fname = fname_trans_sid(name, fname_buf, &tofree, &error); } - *doesrange = false; + if (funcexe->doesrange != NULL) { + *funcexe->doesrange = false; + } if (partial != NULL) { // When the function has a partial with a dict and there is a dict // argument, use the dict argument. That is backwards compatible. // When the dict was bound explicitly use the one from the partial. - if (partial->pt_dict != NULL - && (selfdict_in == NULL || !partial->pt_auto)) { + if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) { selfdict = partial->pt_dict; } if (error == ERROR_NONE && partial->pt_argc > 0) { @@ -1506,7 +1495,7 @@ call_func( } } - if (error == ERROR_NONE && evaluate) { + if (error == ERROR_NONE && funcexe->evaluate) { char_u *rfname = fname; // Ignore "g:" before a function name. @@ -1549,13 +1538,13 @@ call_func( cfunc_T cb = fp->uf_cb; error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state); } else if (fp != NULL) { - if (argv_func != NULL) { + if (funcexe->argv_func != NULL) { // postponed filling in the arguments, do it now - argcount = argv_func(argcount, argvars, argv_clear, - fp->uf_args.ga_len); + argcount = funcexe->argv_func(argcount, argvars, argv_clear, + fp->uf_args.ga_len); } - if (fp->uf_flags & FC_RANGE) { - *doesrange = true; + 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) { error = ERROR_TOOFEW; @@ -1565,7 +1554,8 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argvars, rettv, firstline, lastline, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, + funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } @@ -2901,7 +2891,7 @@ void ex_call(exarg_T *eap) int len; typval_T rettv; linenr_T lnum; - int doesrange; + bool doesrange; bool failed = false; funcdict_T fudi; partial_T *partial = NULL; @@ -2965,9 +2955,15 @@ void ex_call(exarg_T *eap) curwin->w_cursor.coladd = 0; } arg = startarg; - if (get_func_tv(name, -1, &rettv, &arg, - eap->line1, eap->line2, &doesrange, - true, partial, fudi.fd_dict) == FAIL) { + + funcexe_T funcexe = FUNCEXE_INIT; + funcexe.firstline = eap->line1; + funcexe.lastline = eap->line2; + funcexe.doesrange = &doesrange; + funcexe.evaluate = true; + funcexe.partial = partial; + funcexe.selfdict = fudi.fd_dict; + if (get_func_tv(name, -1, &rettv, &arg, &funcexe) == FAIL) { failed = true; break; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index e8ad0bf1da..9af35e0411 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -30,9 +30,32 @@ typedef enum { ERROR_DELETED, } FnameTransError; +/// Used in funcexe_T. Returns the new argcount. typedef int (*ArgvFunc)(int current_argcount, typval_T *argv, int argskip, int called_func_argcount); +/// Structure passed between functions dealing with function call execution. +typedef struct { + ArgvFunc argv_func; ///< when not NULL, can be used to fill in arguments only + ///< when the invoked function uses them + linenr_T firstline; ///< first line of range + linenr_T lastline; ///< last line of range + bool *doesrange; ///< [out] if not NULL: function handled range + bool evaluate; ///< actually evaluate expressions + partial_T *partial; ///< for extra arguments + dict_T *selfdict; ///< Dictionary for "self" +} funcexe_T; + +#define FUNCEXE_INIT (funcexe_T) { \ + .argv_func = NULL, \ + .firstline = 0, \ + .lastline = 0, \ + .doesrange = NULL, \ + .evaluate = false, \ + .partial = NULL, \ + .selfdict = NULL, \ +} + #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] #define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j] -- cgit From e6be6c307a832d661d2a6269ad2d322e4bf5e9cc Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 5 Aug 2021 19:46:42 +0100 Subject: vim-patch:8.1.1803: all builtin functions are global Problem: All builtin functions are global. Solution: Add the method call operator ->. Implemented for a limited number of functions. https://github.com/vim/vim/commit/ac92e25a33c37ec5becbfffeccda136c73b761ac - Note that to *exactly* port hunk @@ -7376,18 +7444,19 from handle_subscript(), we need the :scriptversion patches (I have an open PR for those, but this patch works fine without them anyway). - Port call_internal_func() from v7.4.2058. - Adjust some error messages in tests, as they rely on the Blob patches. - Add a modeline to test_method.vim. Ignore the global_functions and base_method tables and prefer the current GPerf implementation. Instead, add an extra base_arg field to VimLFuncDef that holds the number of the argument to use as the base (1-indexed, so that 0 may be used to refer to functions that cannot be used as methods). This also means we support using any argument as a base from the get-go, rather than just the first (Vim includes this ability in future patches, however). To mark a function as usable as a method, use the "base" key as described in eval.lua. --- src/nvim/eval/funcs.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/nvim/eval/funcs.h | 1 + src/nvim/eval/userfunc.c | 24 ++++++++++-------------- src/nvim/eval/userfunc.h | 2 ++ 4 files changed, 57 insertions(+), 14 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 363640adc5..787d5aaf78 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -175,6 +175,50 @@ const VimLFuncDef *find_internal_func(const char *const name) return find_internal_func_gperf(name, len); } +int call_internal_func(const char_u *const fname, const int argcount, + typval_T *const argvars, typval_T *const rettv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL) { + return ERROR_UNKNOWN; + } else if (argcount < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount > fdef->max_argc) { + return ERROR_TOOMANY; + } + argvars[argcount].v_type = VAR_UNKNOWN; + fdef->func(argvars, rettv, fdef->data); + return ERROR_NONE; +} + +/// Invoke a method for base->method(). +int call_internal_method(const char_u *const fname, const int argcount, + typval_T *const argvars, typval_T *const rettv, + typval_T *const basetv) + FUNC_ATTR_NONNULL_ALL +{ + const VimLFuncDef *const fdef = find_internal_func((const char *)fname); + if (fdef == NULL || fdef->base_arg == 0) { + return ERROR_UNKNOWN; + } else if (argcount + 1 < fdef->min_argc) { + return ERROR_TOOFEW; + } else if (argcount + 1 > fdef->max_argc) { + return ERROR_TOOMANY; + } + + typval_T argv[MAX_FUNC_ARGS + 1]; + const ptrdiff_t base_index = fdef->base_arg - 1; + memcpy(argv, argvars, base_index * sizeof(typval_T)); + argv[base_index] = *basetv; + memcpy(argv + base_index + 1, argvars + base_index, + (argcount - base_index) * sizeof(typval_T)); + argv[argcount + 1].v_type = VAR_UNKNOWN; + + fdef->func(argv, rettv, fdef->data); + return ERROR_NONE; +} + /* * Return TRUE for a non-zero Number and a non-empty String. */ diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index a343290734..3ad0b8282a 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -14,6 +14,7 @@ typedef struct fst { char *name; ///< Name of the function. uint8_t min_argc; ///< Minimal number of arguments. uint8_t max_argc; ///< Maximal number of arguments. + uint8_t base_arg; ///< Method base arg # (1-indexed), or 0 if not a method. VimLFunc func; ///< Function implementation. FunPtr data; ///< Userdata for function implementation. } VimLFuncDef; diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 516f7a1d53..ec43987b07 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1514,7 +1514,10 @@ call_func( } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. - if (fp == NULL) { + if (funcexe->basetv != NULL) { + // TODO(seandewar): support User function: base->Method() + fp = NULL; + } else if (fp == NULL) { fp = find_func(rfname); } @@ -1560,20 +1563,13 @@ call_func( error = ERROR_NONE; } } + } else if (funcexe->basetv != NULL) { + // Find the method name in the table, call its implementation. + error = call_internal_method(fname, argcount, argvars, rettv, + funcexe->basetv); } else { // Find the function name in the table, call its implementation. - const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef != NULL) { - if (argcount < fdef->min_argc) { - error = ERROR_TOOFEW; - } else if (argcount > fdef->max_argc) { - error = ERROR_TOOMANY; - } else { - argvars[argcount].v_type = VAR_UNKNOWN; - fdef->func(argvars, rettv, fdef->data); - error = ERROR_NONE; - } - } + error = call_internal_func(fname, argcount, argvars, rettv); } /* * The function call (or "FuncUndefined" autocommand sequence) might @@ -2937,7 +2933,7 @@ void ex_call(exarg_T *eap) rettv.v_type = VAR_UNKNOWN; // tv_clear() uses this. if (*startarg != '(') { - EMSG2(_("E107: Missing parentheses: %s"), eap->arg); + EMSG2(_(e_missingparen), eap->arg); goto end; } diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 9af35e0411..513473449a 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -44,6 +44,7 @@ typedef struct { bool evaluate; ///< actually evaluate expressions partial_T *partial; ///< for extra arguments dict_T *selfdict; ///< Dictionary for "self" + typval_T *basetv; ///< base for base->method() } funcexe_T; #define FUNCEXE_INIT (funcexe_T) { \ @@ -54,6 +55,7 @@ typedef struct { .evaluate = false, \ .partial = NULL, \ .selfdict = NULL, \ + .basetv = NULL, \ } #define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] -- cgit From 003c8acc8a9863932430bfb51bee8403b964c19b Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 17:09:47 +0100 Subject: vim-patch:8.1.1807: more functions can be used as a method Problem: More functions can be used as a method. Solution: Add append(), appendbufline(), assert_equal(), etc. Also add the :eval command. https://github.com/vim/vim/commit/25e42231d3ee27feec2568fa4be2aa2bfba82ae5 :eval is already ported. --- src/nvim/eval/funcs.c | 5 +++-- src/nvim/eval/funcs.h | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 787d5aaf78..e090f3b37f 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -199,7 +199,7 @@ int call_internal_method(const char_u *const fname, const int argcount, FUNC_ATTR_NONNULL_ALL { const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef == NULL || fdef->base_arg == 0) { + if (fdef == NULL || fdef->base_arg == BASE_NONE) { return ERROR_UNKNOWN; } else if (argcount + 1 < fdef->min_argc) { return ERROR_TOOFEW; @@ -208,7 +208,8 @@ int call_internal_method(const char_u *const fname, const int argcount, } typval_T argv[MAX_FUNC_ARGS + 1]; - const ptrdiff_t base_index = fdef->base_arg - 1; + const ptrdiff_t base_index + = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1; memcpy(argv, argvars, base_index * sizeof(typval_T)); argv[base_index] = *basetv; memcpy(argv + base_index + 1, argvars + base_index, diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 3ad0b8282a..c6a0cb959e 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -9,12 +9,16 @@ typedef void (*FunPtr)(void); /// Prototype of C function that implements VimL function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data); +/// Special flags for base_arg @see VimLFuncDef +#define BASE_NONE 0 ///< Not a method (no base argument). +#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base. + /// Structure holding VimL function definition typedef struct fst { char *name; ///< Name of the function. uint8_t min_argc; ///< Minimal number of arguments. uint8_t max_argc; ///< Maximal number of arguments. - uint8_t base_arg; ///< Method base arg # (1-indexed), or 0 if not a method. + uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST. VimLFunc func; ///< Function implementation. FunPtr data; ///< Userdata for function implementation. } VimLFuncDef; -- cgit From 8d1ca37d1f413b08f5ff9ceb502fa35ffd6034c7 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 18:31:27 +0100 Subject: vim-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) https://github.com/vim/vim/commit/fcfe1a9b8950b8b211ab3b24d84b17c6847ea43f --- src/nvim/eval/userfunc.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index ec43987b07..3c0c0091f2 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1447,7 +1447,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; @@ -1514,10 +1515,7 @@ call_func( } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. - if (funcexe->basetv != NULL) { - // TODO(seandewar): support User function: base->Method() - fp = NULL; - } else if (fp == NULL) { + if (fp == NULL) { fp = find_func(rfname); } @@ -1546,6 +1544,16 @@ call_func( argcount = funcexe->argv_func(argcount, argvars, argv_clear, fp->uf_args.ga_len); } + + if (funcexe->basetv != NULL) { + // Method call: base->Method() + 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; } @@ -1557,14 +1565,15 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, + call_user_func(fp, argcount, argv, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; } } } 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); } else { -- cgit From d41b87e070037786bf2cc4486b1839169805ee21 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Fri, 6 Aug 2021 18:58:27 +0100 Subject: vim-patch:8.1.1820: using expr->FuncRef() does not work Problem: Using expr->FuncRef() does not work. Solution: Make FuncRef work as a method. https://github.com/vim/vim/commit/761fdf01c6e307c448cec2684f8b315ba6d1f454 --- src/nvim/eval/userfunc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 3c0c0091f2..9ecde327de 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1450,6 +1450,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; // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) @@ -1550,8 +1551,8 @@ call_func( memmove(&argv[1], argvars, sizeof(typval_T) * argcount); argv[0] = *funcexe->basetv; argcount++; - } else { - memcpy(argv, argvars, sizeof(typval_T) * argcount); + argvars = argv; + argv_base = 1; } if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { @@ -1565,7 +1566,7 @@ call_func( error = ERROR_DICT; } else { // Call the user function. - call_user_func(fp, argcount, argv, rettv, funcexe->firstline, + call_user_func(fp, argcount, argvars, rettv, funcexe->firstline, funcexe->lastline, (fp->uf_flags & FC_DICT) ? selfdict : NULL); error = ERROR_NONE; @@ -1602,9 +1603,11 @@ theend: user_func_error(error, (name != NULL) ? name : funcname); } + // clear the copies made from the partial while (argv_clear > 0) { - tv_clear(&argv[--argv_clear]); + tv_clear(&argv[--argv_clear + argv_base]); } + xfree(tofree); xfree(name); -- cgit From 98dfe4adc48d83e05b2ee039869853760de9c096 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 15:10:28 +0100 Subject: vim-patch:8.1.1863: confusing error when using a builtin function as method Problem: Confusing error when using a builtin function as method while it does not support that. Solution: Add a specific error message. https://github.com/vim/vim/commit/9174639a82799011cfa0013cbc4c4709b3833bf0 --- src/nvim/eval/funcs.c | 4 +++- src/nvim/eval/userfunc.c | 3 +++ src/nvim/eval/userfunc.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e090f3b37f..8a1258efd4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -199,8 +199,10 @@ int call_internal_method(const char_u *const fname, const int argcount, FUNC_ATTR_NONNULL_ALL { const VimLFuncDef *const fdef = find_internal_func((const char *)fname); - if (fdef == NULL || fdef->base_arg == BASE_NONE) { + if (fdef == NULL) { return ERROR_UNKNOWN; + } else if (fdef->base_arg == BASE_NONE) { + return ERROR_NOTMETHOD; } else if (argcount + 1 < fdef->min_argc) { return ERROR_TOOFEW; } else if (argcount + 1 > fdef->max_argc) { diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 9ecde327de..59f07ff486 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1399,6 +1399,9 @@ static void user_func_error(int error, const char_u *name) case ERROR_UNKNOWN: emsg_funcname(N_("E117: Unknown function: %s"), name); break; + case ERROR_NOTMETHOD: + emsg_funcname(N_("E276: Cannot use function as a method: %s"), name); + break; case ERROR_DELETED: emsg_funcname(N_("E933: Function was deleted: %s"), name); break; diff --git a/src/nvim/eval/userfunc.h b/src/nvim/eval/userfunc.h index 513473449a..3f111343d2 100644 --- a/src/nvim/eval/userfunc.h +++ b/src/nvim/eval/userfunc.h @@ -28,6 +28,7 @@ typedef enum { ERROR_OTHER, ERROR_BOTH, ERROR_DELETED, + ERROR_NOTMETHOD, } FnameTransError; /// Used in funcexe_T. Returns the new argcount. -- cgit From 7474db98afcae3a47732d1ba99971b500a519cb2 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Sat, 7 Aug 2021 15:18:04 +0100 Subject: vim-patch:8.1.1878: negative float before method not parsed correctly Problem: Negative float before method not parsed correctly. Solution: Apply "!" and "-" in front of expression before using ->. https://github.com/vim/vim/commit/9cfe8f6e68de4bfb5942d84f4465de914a747b3f --- src/nvim/eval/userfunc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 59f07ff486..4f10d31615 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -2980,7 +2980,8 @@ void ex_call(exarg_T *eap) } // Handle a function returning a Funcref, Dictionary or List. - if (handle_subscript((const char **)&arg, &rettv, true, true) + if (handle_subscript((const char **)&arg, &rettv, true, true, + (const char_u *)name, (const char_u **)&name) == FAIL) { failed = true; break; -- cgit From da9005af792f7a7eaae98ee9f6499af9a97fd095 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Thu, 12 Aug 2021 17:19:05 +0100 Subject: fix(v:lua): fix emsg when calling v:lua directly v:lua expressions are represented using vvlua_partial. As v:lua isn't intended to be called directly, it's given an empty pt_name. Because of this, calling v:lua directly like "v:lua()" will cause "E117: Unknown function: ", with an empty name. Instead, have call_func() show the name "v:lua" in the emsg. --- src/nvim/eval/userfunc.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 4f10d31615..5ffd578891 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1516,6 +1516,10 @@ call_func( if (len > 0) { error = ERROR_NONE; nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); + } else { + // v:lua was called directly; show its name in the emsg + XFREE_CLEAR(name); + funcname = (const char_u *)"v:lua"; } } else if (fp != NULL || !builtin_function((const char *)rfname, -1)) { // User defined function. -- cgit From b2994e35c9357a8144beaf27e1a8ea4dd133f5d4 Mon Sep 17 00:00:00 2001 From: Sean Dewar Date: Wed, 11 Aug 2021 13:47:33 +0100 Subject: feat(v:lua): support calling v:lua as a method --- src/nvim/eval/userfunc.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) (limited to 'src/nvim/eval') diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 5ffd578891..4184e4d922 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1423,6 +1423,23 @@ static void user_func_error(int error, const char_u *name) } } +/// Used by call_func to add a method base (if any) to a function argument list +/// as the first argument. @see call_func +static void argv_add_base(typval_T *const basetv, typval_T **const argvars, + int *const argcount, typval_T *const new_argvars, + int *const argv_base) + FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5) +{ + if (basetv != NULL) { + // Method call: base->Method() + memmove(&new_argvars[1], *argvars, sizeof(typval_T) * (*argcount)); + new_argvars[0] = *basetv; + (*argcount)++; + *argvars = new_argvars; + *argv_base = 1; + } +} + /// Call a function with its resolved parameters /// /// @return FAIL if function cannot be called, else OK (even if an error @@ -1515,6 +1532,7 @@ call_func( if (is_luafunc(partial)) { if (len > 0) { error = ERROR_NONE; + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); nlua_typval_call((const char *)funcname, len, argvars, argcount, rettv); } else { // v:lua was called directly; show its name in the emsg @@ -1553,14 +1571,7 @@ call_func( fp->uf_args.ga_len); } - if (funcexe->basetv != NULL) { - // Method call: base->Method() - memmove(&argv[1], argvars, sizeof(typval_T) * argcount); - argv[0] = *funcexe->basetv; - argcount++; - argvars = argv; - argv_base = 1; - } + argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base); if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) { *funcexe->doesrange = true; -- cgit