aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/userfunc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval/userfunc.c')
-rw-r--r--src/nvim/eval/userfunc.c1043
1 files changed, 569 insertions, 474 deletions
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index c46cb6ba5d..22c5b1954d 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -3,45 +3,52 @@
// User defined function support
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lauxlib.h"
#include "nvim/ascii.h"
+#include "nvim/autocmd.h"
+#include "nvim/buffer_defs.h"
#include "nvim/charset.h"
#include "nvim/debugger.h"
-#include "nvim/edit.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/eval/funcs.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/userfunc.h"
#include "nvim/eval/vars.h"
+#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_eval.h"
#include "nvim/ex_getln.h"
-#include "nvim/fileio.h"
#include "nvim/getchar.h"
+#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/insexpand.h"
+#include "nvim/keycodes.h"
#include "nvim/lua/executor.h"
+#include "nvim/macros.h"
+#include "nvim/mbyte.h"
+#include "nvim/memline_defs.h"
+#include "nvim/memory.h"
+#include "nvim/message.h"
+#include "nvim/option_defs.h"
#include "nvim/os/input.h"
+#include "nvim/path.h"
#include "nvim/profile.h"
#include "nvim/regexp.h"
#include "nvim/runtime.h"
#include "nvim/search.h"
+#include "nvim/strings.h"
+#include "nvim/types.h"
#include "nvim/ui.h"
#include "nvim/vim.h"
-// flags used in uf_flags
-#define FC_ABORT 0x01 // abort function on error
-#define FC_RANGE 0x02 // function accepts range
-#define FC_DICT 0x04 // Dict function, uses "self"
-#define FC_CLOSURE 0x08 // closure, uses outer scope variables
-#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
-#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
-#define FC_SANDBOX 0x40 // function defined in the sandbox
-#define FC_DEAD 0x80 // function kept only for reference to dfunc
-#define FC_EXPORT 0x100 // "export def Func()"
-#define FC_NOARGS 0x200 // no a: variables in lambda
-#define FC_VIM9 0x400 // defined in vim9 script file
-#define FC_CFUNC 0x800 // C function extension
-
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/userfunc.c.generated.h"
#endif
@@ -62,6 +69,8 @@ static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace
static char *e_funcdict = N_("E717: Dictionary entry already exists");
static char *e_funcref = N_("E718: Funcref required");
static char *e_nofunc = N_("E130: Unknown function: %s");
+static char e_no_white_space_allowed_before_str_str[]
+ = N_("E1068: No white space allowed before '%s': %s");
void func_init(void)
{
@@ -75,7 +84,7 @@ hashtab_T *func_tbl_get(void)
}
/// Get function arguments.
-static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int *varargs,
+static int get_function_args(char **argp, char endchar, garray_T *newargs, int *varargs,
garray_T *default_args, bool skip)
{
bool mustend = false;
@@ -85,10 +94,10 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
int i;
if (newargs != NULL) {
- ga_init(newargs, (int)sizeof(char_u *), 3);
+ ga_init(newargs, (int)sizeof(char *), 3);
}
if (default_args != NULL) {
- ga_init(default_args, (int)sizeof(char_u *), 3);
+ ga_init(default_args, (int)sizeof(char *), 3);
}
if (varargs != NULL) {
@@ -97,7 +106,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
// Isolate the arguments: "arg1, arg2, ...)"
bool any_default = false;
- while (*p != (char)endchar) {
+ while (*p != endchar) {
if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
if (varargs != NULL) {
*varargs = true;
@@ -109,9 +118,9 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
while (ASCII_ISALNUM(*p) || *p == '_') {
p++;
}
- if (arg == p || isdigit(*arg)
- || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
- || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
+ if (arg == p || isdigit((uint8_t)(*arg))
+ || (p - arg == 9 && strncmp(arg, "firstline", 9) == 0)
+ || (p - arg == 8 && strncmp(arg, "lastline", 8) == 0)) {
if (!skip) {
semsg(_("E125: Illegal argument: %s"), arg);
}
@@ -125,7 +134,7 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
// Check for duplicate argument name.
for (i = 0; i < newargs->ga_len; i++) {
- if (STRCMP(((char **)(newargs->ga_data))[i], arg) == 0) {
+ if (strcmp(((char **)(newargs->ga_data))[i], arg) == 0) {
semsg(_("E853: Duplicate argument name: %s"), arg);
xfree(arg);
goto err_ret;
@@ -142,18 +151,18 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
any_default = true;
p = skipwhite(p) + 1;
p = skipwhite(p);
- char_u *expr = (char_u *)p;
+ char *expr = p;
if (eval1(&p, &rettv, false) != FAIL) {
ga_grow(default_args, 1);
// trim trailing whitespace
- while (p > (char *)expr && ascii_iswhite(p[-1])) {
+ while (p > expr && ascii_iswhite(p[-1])) {
p--;
}
c = (char_u)(*p);
*p = NUL;
- expr = vim_strsave(expr);
- ((char **)(default_args->ga_data))[default_args->ga_len] = (char *)expr;
+ expr = xstrdup(expr);
+ ((char **)(default_args->ga_data))[default_args->ga_len] = expr;
default_args->ga_len++;
*p = (char)c;
} else {
@@ -163,6 +172,15 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
emsg(_("E989: Non-default argument follows default argument"));
mustend = true;
}
+
+ if (ascii_iswhite(*p) && *skipwhite(p) == ',') {
+ // Be tolerant when skipping
+ if (!skip) {
+ semsg(_(e_no_white_space_allowed_before_str_str), ",", p);
+ goto err_ret;
+ }
+ p = skipwhite(p);
+ }
if (*p == ',') {
p++;
} else {
@@ -170,14 +188,14 @@ static int get_function_args(char **argp, char_u endchar, garray_T *newargs, int
}
}
p = skipwhite(p);
- if (mustend && *p != (char)endchar) {
+ if (mustend && *p != endchar) {
if (!skip) {
semsg(_(e_invarg2), *argp);
}
break;
}
}
- if (*p != (char)endchar) {
+ if (*p != endchar) {
goto err_ret;
}
p++; // skip "endchar"
@@ -211,21 +229,21 @@ static void register_closure(ufunc_T *fp)
}
/// @return a name for a lambda. Returned in static memory.
-char_u *get_lambda_name(void)
+char *get_lambda_name(void)
{
- static char_u name[30];
+ static char name[30];
static int lambda_no = 0;
- snprintf((char *)name, sizeof(name), "<lambda>%d", ++lambda_no);
+ snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no);
return name;
}
-static void set_ufunc_name(ufunc_T *fp, char_u *name)
+static void set_ufunc_name(ufunc_T *fp, char *name)
{
STRCPY(fp->uf_name, name);
- if (name[0] == K_SPECIAL) {
- fp->uf_name_exp = xmalloc(STRLEN(name) + 3);
+ if ((uint8_t)name[0] == K_SPECIAL) {
+ fp->uf_name_exp = xmalloc(strlen(name) + 3);
STRCPY(fp->uf_name_exp, "<SNR>");
STRCAT(fp->uf_name_exp, fp->uf_name + 3);
}
@@ -242,13 +260,13 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
partial_T *pt = NULL;
int varargs;
int ret;
- char_u *start = (char_u *)skipwhite(*arg + 1);
- char_u *s, *e;
+ char *start = skipwhite(*arg + 1);
+ char *s, *e;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
// First, check if this is a lambda expression. "->" must exists.
- ret = get_function_args((char **)&start, '-', NULL, NULL, NULL, true);
+ ret = get_function_args(&start, '-', NULL, NULL, NULL, true);
if (ret == FAIL || *start != '>') {
return NOTDONE;
}
@@ -272,38 +290,39 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
// Get the start and the end of the expression.
*arg = skipwhite((*arg) + 1);
- s = (char_u *)(*arg);
+ s = *arg;
ret = skip_expr(arg);
if (ret == FAIL) {
goto errret;
}
- e = (char_u *)(*arg);
+ e = *arg;
*arg = skipwhite(*arg);
if (**arg != '}') {
+ semsg(_("E451: Expected }: %s"), *arg);
goto errret;
}
(*arg)++;
if (evaluate) {
int flags = 0;
- char_u *p;
+ char *p;
garray_T newlines;
- char_u *name = get_lambda_name();
+ char *name = get_lambda_name();
- fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
pt = xcalloc(1, sizeof(partial_T));
- ga_init(&newlines, (int)sizeof(char_u *), 1);
+ ga_init(&newlines, (int)sizeof(char *), 1);
ga_grow(&newlines, 1);
// Add "return " before the expression.
size_t len = (size_t)(7 + e - s + 1);
- p = (char_u *)xmalloc(len);
- ((char **)(newlines.ga_data))[newlines.ga_len++] = (char *)p;
+ p = xmalloc(len);
+ ((char **)(newlines.ga_data))[newlines.ga_len++] = p;
STRCPY(p, "return ");
- STRLCPY(p + 7, s, e - s + 1);
- if (strstr((char *)p + 7, "a:") == NULL) {
+ xstrlcpy(p + 7, s, (size_t)(e - s) + 1);
+ if (strstr(p + 7, "a:") == NULL) {
// No a: variables are used for sure.
flags |= FC_NOARGS;
}
@@ -312,7 +331,7 @@ int get_lambda_tv(char **arg, typval_T *rettv, bool evaluate)
set_ufunc_name(fp, name);
hash_add(&func_hashtab, UF2HIKEY(fp));
fp->uf_args = newargs;
- ga_init(&fp->uf_def_args, (int)sizeof(char_u *), 1);
+ ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars) {
flags |= FC_CLOSURE;
@@ -365,7 +384,7 @@ errret:
/// was not found.
///
/// @return name of the function.
-char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
+char *deref_func_name(const char *name, int *lenp, partial_T **const partialp, bool no_autoload)
FUNC_ATTR_NONNULL_ARG(1, 2)
{
if (partialp != NULL) {
@@ -376,10 +395,10 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
if (v->di_tv.vval.v_string == NULL) { // just in case
*lenp = 0;
- return (char_u *)"";
+ return "";
}
- *lenp = (int)STRLEN(v->di_tv.vval.v_string);
- return (char_u *)v->di_tv.vval.v_string;
+ *lenp = (int)strlen(v->di_tv.vval.v_string);
+ return v->di_tv.vval.v_string;
}
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
@@ -387,31 +406,31 @@ char_u *deref_func_name(const char *name, int *lenp, partial_T **const partialp,
if (pt == NULL) { // just in case
*lenp = 0;
- return (char_u *)"";
+ return "";
}
if (partialp != NULL) {
*partialp = pt;
}
- char_u *s = (char_u *)partial_name(pt);
- *lenp = (int)STRLEN(s);
+ char *s = partial_name(pt);
+ *lenp = (int)strlen(s);
return s;
}
- return (char_u *)name;
+ return (char *)name;
}
/// Give an error message with a function name. Handle <SNR> things.
///
/// @param ermsg must be passed without translation (use N_() instead of _()).
/// @param name function name
-void emsg_funcname(char *ermsg, const char_u *name)
+void emsg_funcname(char *ermsg, const char *name)
{
- char_u *p;
+ char *p;
- if (*name == K_SPECIAL) {
- p = concat_str((char_u *)"<SNR>", name + 3);
+ if ((uint8_t)(*name) == K_SPECIAL) {
+ p = concat_str("<SNR>", name + 3);
} else {
- p = (char_u *)name;
+ p = (char *)name;
}
semsg(_(ermsg), p);
@@ -429,7 +448,7 @@ void emsg_funcname(char *ermsg, const char_u *name)
/// @param funcexe various values
///
/// @return OK or FAIL.
-int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
+int get_func_tv(const char *name, int len, typval_T *rettv, char **arg, funcexe_T *funcexe)
{
char *argp;
int ret = OK;
@@ -439,12 +458,12 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
// Get the arguments.
argp = *arg;
while (argcount < MAX_FUNC_ARGS
- - (funcexe->partial == NULL ? 0 : funcexe->partial->pt_argc)) {
+ - (funcexe->fe_partial == NULL ? 0 : funcexe->fe_partial->pt_argc)) {
argp = skipwhite(argp + 1); // skip the '(' or ','
if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
}
- if (eval1(&argp, &argvars[argcount], funcexe->evaluate) == FAIL) {
+ if (eval1(&argp, &argvars[argcount], funcexe->fe_evaluate) == FAIL) {
ret = FAIL;
break;
}
@@ -463,7 +482,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
int i = 0;
if (get_vim_var_nr(VV_TESTING)) {
- // Prepare for calling garbagecollect_for_testing(), need to know
+ // Prepare for calling test_garbagecollect_now(), need to know
// what variables are used on the call stack.
if (funcargs.ga_itemsize == 0) {
ga_init(&funcargs, (int)sizeof(typval_T *), 50);
@@ -473,7 +492,7 @@ int get_func_tv(const char_u *name, int len, typval_T *rettv, char **arg, funcex
((typval_T **)funcargs.ga_data)[funcargs.ga_len++] = &argvars[i];
}
}
- ret = call_func((char *)name, len, rettv, argcount, argvars, funcexe);
+ ret = call_func(name, len, rettv, argcount, argvars, funcexe);
funcargs.ga_len -= i;
} else if (!aborting()) {
@@ -525,48 +544,47 @@ static inline bool eval_fname_sid(const char *const name)
///
/// @return transformed name: either `fname_buf` or a pointer to an allocated
/// memory.
-static char_u *fname_trans_sid(const char_u *const name, char_u *const fname_buf,
- char_u **const tofree, int *const error)
+static char *fname_trans_sid(const char *const name, char *const fname_buf, char **const tofree,
+ int *const error)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
- char_u *fname;
- const int llen = eval_fname_script((const char *)name);
- if (llen > 0) {
- fname_buf[0] = K_SPECIAL;
- fname_buf[1] = KS_EXTRA;
- fname_buf[2] = KE_SNR;
- int i = 3;
- if (eval_fname_sid((const char *)name)) { // "<SID>" or "s:"
- if (current_sctx.sc_sid <= 0) {
- *error = ERROR_SCRIPT;
- } else {
- snprintf((char *)fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
- (int64_t)current_sctx.sc_sid);
- i = (int)STRLEN(fname_buf);
- }
- }
- if ((size_t)i + STRLEN(name + llen) < FLEN_FIXED) {
- STRCPY(fname_buf + i, name + llen);
- fname = fname_buf;
+ const int llen = eval_fname_script(name);
+ if (llen == 0) {
+ return (char *)name; // no prefix
+ }
+
+ fname_buf[0] = (char)K_SPECIAL;
+ fname_buf[1] = (char)KS_EXTRA;
+ fname_buf[2] = KE_SNR;
+ int i = 3;
+ if (eval_fname_sid(name)) { // "<SID>" or "s:"
+ if (current_sctx.sc_sid <= 0) {
+ *error = FCERR_SCRIPT;
} else {
- fname = xmalloc((size_t)i + STRLEN(name + llen) + 1);
- *tofree = fname;
- memmove(fname, fname_buf, (size_t)i);
- STRCPY(fname + i, name + llen);
+ snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
+ (int64_t)current_sctx.sc_sid);
+ i = (int)strlen(fname_buf);
}
+ }
+ char *fname;
+ if ((size_t)i + strlen(name + llen) < FLEN_FIXED) {
+ STRCPY(fname_buf + i, name + llen);
+ fname = fname_buf;
} else {
- fname = (char_u *)name;
+ fname = xmalloc((size_t)i + strlen(name + llen) + 1);
+ *tofree = fname;
+ memmove(fname, fname_buf, (size_t)i);
+ STRCPY(fname + i, name + llen);
}
-
return fname;
}
/// Find a function by name, return pointer to it in ufuncs.
///
/// @return NULL for unknown function.
-ufunc_T *find_func(const char_u *name)
+ufunc_T *find_func(const char *name)
{
- hashitem_T *hi = hash_find(&func_hashtab, (char *)name);
+ hashitem_T *hi = hash_find(&func_hashtab, name);
if (!HASHITEM_EMPTY(hi)) {
return HI2UF(hi);
}
@@ -576,9 +594,9 @@ ufunc_T *find_func(const char_u *name)
/// Copy the function name of "fp" to buffer "buf".
/// "buf" must be able to hold the function name plus three bytes.
/// Takes care of script-local function names.
-static void cat_func_name(char_u *buf, ufunc_T *fp)
+static void cat_func_name(char *buf, ufunc_T *fp)
{
- if (fp->uf_name[0] == K_SPECIAL) {
+ if ((uint8_t)fp->uf_name[0] == K_SPECIAL) {
STRCPY(buf, "<SNR>");
STRCAT(buf, fp->uf_name + 3);
} else {
@@ -593,7 +611,7 @@ static void add_nr_var(dict_T *dp, dictitem_T *v, char *name, varnumber_T nr)
STRCPY(v->di_key, name);
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(dp, v);
+ hash_add(&dp->dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_NUMBER;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_number = nr;
@@ -758,9 +776,9 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_lines));
XFREE_CLEAR(fp->uf_name_exp);
- if (fp->uf_cb_free != NULL) {
- fp->uf_cb_free(fp->uf_cb_state);
- fp->uf_cb_free = NULL;
+ if (fp->uf_flags & FC_LUAREF) {
+ api_free_luaref(fp->uf_luaref);
+ fp->uf_luaref = LUA_NOREF;
}
XFREE_CLEAR(fp->uf_tml_count);
@@ -828,7 +846,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
int fixvar_idx = 0; // index in fixvar[]
int ai;
bool islambda = false;
- char_u numbuf[NUMBUFLEN];
+ char numbuf[NUMBUFLEN];
char *name;
typval_T *tv_to_free[MAX_FUNC_ARGS];
int tv_to_free_len = 0;
@@ -852,7 +870,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
saveRedobuff(&save_redo);
did_save_redo = true;
}
- ++fp->uf_calls;
+ fp->uf_calls++;
// check for CTRL-C hit
line_breakcheck();
// prepare the funccall_T structure
@@ -863,14 +881,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
fc->rettv = rettv;
fc->level = ex_nesting_level;
// Check if this function has a breakpoint.
- fc->breakpoint = dbg_find_breakpoint(false, fp->uf_name, (linenr_T)0);
+ fc->breakpoint = dbg_find_breakpoint(false, (char *)fp->uf_name, (linenr_T)0);
fc->dbg_tick = debug_tick;
// Set up fields for closure.
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp);
- if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
+ if (strncmp(fp->uf_name, "<lambda>", 8) == 0) {
islambda = true;
}
@@ -889,11 +907,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
STRCPY(name, "self");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_vars, v);
+ hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_DICT;
v->di_tv.v_lock = VAR_UNLOCKED;
v->di_tv.vval.v_dict = selfdict;
- ++selfdict->dv_refcount;
+ selfdict->dv_refcount++;
}
// Init a: variables, unless none found (in lambda).
@@ -915,7 +933,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
STRCPY(name, "000");
#endif
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
- tv_dict_add(&fc->l_avars, v);
+ hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
v->di_tv.v_type = VAR_LIST;
v->di_tv.v_lock = VAR_FIXED;
v->di_tv.vval.v_list = &fc->l_varlist;
@@ -967,14 +985,14 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
break;
}
// "..." argument a:1, a:2, etc.
- snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
- name = (char *)numbuf;
+ snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
+ name = numbuf;
}
- if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
+ if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
v = (dictitem_T *)&fc->fixvar[fixvar_idx++];
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
} else {
- v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
+ v = xmalloc(sizeof(dictitem_T) + strlen(name));
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
}
STRCPY(v->di_key, name);
@@ -993,16 +1011,16 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
// Named arguments can be accessed without the "a:" prefix in lambda
// expressions. Add to the l: dict.
tv_copy(&v->di_tv, &v->di_tv);
- tv_dict_add(&fc->l_vars, v);
+ hash_add(&fc->l_vars.dv_hashtab, (char *)v->di_key);
} else {
- tv_dict_add(&fc->l_avars, v);
+ hash_add(&fc->l_avars.dv_hashtab, (char *)v->di_key);
}
if (ai >= 0 && ai < MAX_FUNC_ARGS) {
listitem_T *li = &fc->l_listitems[ai];
*TV_LIST_ITEM_TV(li) = argvars[i];
- TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
+ TV_LIST_ITEM_TV(li)->v_lock = VAR_FIXED;
tv_list_append(&fc->l_varlist, li);
}
}
@@ -1058,7 +1076,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
bool func_not_yet_profiling_but_should =
do_profiling_yes
- && !fp->uf_profiling && has_profiling(false, fp->uf_name, NULL);
+ && !fp->uf_profiling && has_profiling(false, (char *)fp->uf_name, NULL);
if (func_not_yet_profiling_but_should) {
started_profiling = true;
@@ -1071,7 +1089,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
|| (fc->caller != NULL && fc->caller->func->uf_profiling));
if (func_or_func_caller_profiling) {
- ++fp->uf_tm_count;
+ fp->uf_tm_count++;
call_start = profile_start();
fp->uf_tm_children = profile_zero();
}
@@ -1083,7 +1101,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
const sctx_T save_current_sctx = current_sctx;
current_sctx = fp->uf_script_ctx;
save_did_emsg = did_emsg;
- did_emsg = FALSE;
+ did_emsg = false;
if (default_arg_err && (fp->uf_flags & FC_ABORT)) {
did_emsg = true;
@@ -1208,9 +1226,37 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
/// For the first we only count the name stored in func_hashtab as a reference,
/// using function() does not count as a reference, because the function is
/// looked up by name.
-static bool func_name_refcount(char_u *name)
+static bool func_name_refcount(const char *name)
+{
+ return isdigit((uint8_t)(*name)) || *name == '<';
+}
+
+/// Call a user function after checking the arguments.
+static int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv,
+ funcexe_T *funcexe, dict_T *selfdict)
+ FUNC_ATTR_NONNULL_ARG(1, 3, 4, 5)
{
- return isdigit(*name) || *name == '<';
+ if (fp->uf_flags & FC_LUAREF) {
+ return typval_exec_lua_callable(fp->uf_luaref, argcount, argvars, rettv);
+ }
+
+ if ((fp->uf_flags & FC_RANGE) && funcexe->fe_doesrange != NULL) {
+ *funcexe->fe_doesrange = true;
+ }
+ int error;
+ if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) {
+ error = FCERR_TOOFEW;
+ } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
+ error = FCERR_TOOMANY;
+ } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
+ error = FCERR_DICT;
+ } else {
+ // Call the user function.
+ call_user_func(fp, argcount, argvars, rettv, funcexe->fe_firstline, funcexe->fe_lastline,
+ (fp->uf_flags & FC_DICT) ? selfdict : NULL);
+ error = FCERR_NONE;
+ }
+ return error;
}
static funccal_entry_T *funccal_stack = NULL;
@@ -1252,7 +1298,7 @@ void free_all_functions(void)
ufunc_T *fp;
uint64_t skipped = 0;
uint64_t todo = 1;
- uint64_t used;
+ int changed;
// Clean up the current_funccal chain and the funccal stack.
while (current_funccal != NULL) {
@@ -1276,9 +1322,9 @@ void free_all_functions(void)
if (func_name_refcount(fp->uf_name)) {
skipped++;
} else {
- used = func_hashtab.ht_used;
+ changed = func_hashtab.ht_changed;
func_clear(fp, true);
- if (used != func_hashtab.ht_used) {
+ if (changed != func_hashtab.ht_changed) {
skipped = 0;
break;
}
@@ -1322,10 +1368,10 @@ void free_all_functions(void)
/// @param[in] len length of "name", or -1 for NUL terminated.
///
/// @return true if "name" looks like a builtin function name: starts with a
-/// lower case letter and doesn't contain AUTOLOAD_CHAR.
+/// lower case letter and doesn't contain AUTOLOAD_CHAR or ':'.
static bool builtin_function(const char *name, int len)
{
- if (!ASCII_ISLOWER(name[0])) {
+ if (!ASCII_ISLOWER(name[0]) || name[1] == ':') {
return false;
}
@@ -1336,7 +1382,7 @@ static bool builtin_function(const char *name, int len)
return p == NULL;
}
-int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv)
+int func_call(char *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv)
{
typval_T argv[MAX_FUNC_ARGS + 1];
int argc = 0;
@@ -1353,12 +1399,12 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *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((char *)name, -1, rettv, argc, argv, &funcexe);
+ funcexe.fe_firstline = curwin->w_cursor.lnum;
+ funcexe.fe_lastline = curwin->w_cursor.lnum;
+ funcexe.fe_evaluate = true;
+ funcexe.fe_partial = partial;
+ funcexe.fe_selfdict = selfdict;
+ r = call_func(name, -1, rettv, argc, argv, &funcexe);
func_call_skip_call:
// Free the arguments.
@@ -1369,35 +1415,50 @@ func_call_skip_call:
return r;
}
+/// call the 'callback' function and return the result as a number.
+/// Returns -2 when calling the function fails. Uses argv[0] to argv[argc - 1]
+/// for the function arguments. argv[argc] should have type VAR_UNKNOWN.
+///
+/// @param argcount number of "argvars"
+/// @param argvars vars for arguments, must have "argcount" PLUS ONE elements!
+varnumber_T callback_call_retnr(Callback *callback, int argcount, typval_T *argvars)
+{
+ typval_T rettv;
+ if (!callback_call(callback, argcount, argvars, &rettv)) {
+ return -2;
+ }
+
+ varnumber_T retval = tv_get_number_chk(&rettv, NULL);
+ tv_clear(&rettv);
+ return retval;
+}
+
/// Give an error message for the result of a function.
/// Nothing if "error" is FCERR_NONE.
-static void user_func_error(int error, const char_u *name)
+static void user_func_error(int error, const char *name)
FUNC_ATTR_NONNULL_ALL
{
switch (error) {
- case ERROR_UNKNOWN:
+ case FCERR_UNKNOWN:
emsg_funcname(N_("E117: Unknown function: %s"), name);
break;
- case ERROR_NOTMETHOD:
+ case FCERR_NOTMETHOD:
emsg_funcname(N_("E276: Cannot use function as a method: %s"), name);
break;
- case ERROR_DELETED:
+ case FCERR_DELETED:
emsg_funcname(N_("E933: Function was deleted: %s"), name);
break;
- case ERROR_TOOMANY:
+ case FCERR_TOOMANY:
emsg_funcname(_(e_toomanyarg), name);
break;
- case ERROR_TOOFEW:
- emsg_funcname(N_("E119: Not enough arguments for function: %s"),
- name);
+ case FCERR_TOOFEW:
+ emsg_funcname(N_("E119: Not enough arguments for function: %s"), name);
break;
- case ERROR_SCRIPT:
- emsg_funcname(N_("E120: Using <SID> not in a script context: %s"),
- name);
+ case FCERR_SCRIPT:
+ emsg_funcname(N_("E120: Using <SID> not in a script context: %s"), name);
break;
- case ERROR_DICT:
- emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
- name);
+ case FCERR_DICT:
+ emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"), name);
break;
}
}
@@ -1435,27 +1496,27 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
FUNC_ATTR_NONNULL_ARG(1, 3, 5, 6)
{
int ret = FAIL;
- int error = ERROR_NONE;
+ int error = FCERR_NONE;
ufunc_T *fp = NULL;
- char_u fname_buf[FLEN_FIXED + 1];
- char_u *tofree = NULL;
- char_u *fname = NULL;
- char_u *name = NULL;
+ char fname_buf[FLEN_FIXED + 1];
+ char *tofree = NULL;
+ char *fname = NULL;
+ char *name = NULL;
int argcount = argcount_in;
typval_T *argvars = argvars_in;
- dict_T *selfdict = funcexe->selfdict;
+ dict_T *selfdict = funcexe->fe_selfdict;
typval_T argv[MAX_FUNC_ARGS + 1]; // used when "partial" or
- // "funcexe->basetv" is not NULL
+ // "funcexe->fe_basetv" is not NULL
int argv_clear = 0;
int argv_base = 0;
- partial_T *partial = funcexe->partial;
+ partial_T *partial = funcexe->fe_partial;
// Initialize rettv so that it is safe for caller to invoke clear_tv(rettv)
// even when call_func() returns FAIL.
rettv->v_type = VAR_UNKNOWN;
if (len <= 0) {
- len = (int)STRLEN(funcname);
+ len = (int)strlen(funcname);
}
if (partial != NULL) {
fp = partial->pt_func;
@@ -1463,12 +1524,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
if (fp == NULL) {
// Make a copy of the name, if it comes from a funcref variable it could
// be changed or deleted in the called function.
- name = vim_strnsave((char_u *)funcname, (size_t)len);
+ name = xstrnsave(funcname, (size_t)len);
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
}
- if (funcexe->doesrange != NULL) {
- *funcexe->doesrange = false;
+ if (funcexe->fe_doesrange != NULL) {
+ *funcexe->fe_doesrange = false;
}
if (partial != NULL) {
@@ -1478,10 +1539,10 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
if (partial->pt_dict != NULL && (selfdict == NULL || !partial->pt_auto)) {
selfdict = partial->pt_dict;
}
- if (error == ERROR_NONE && partial->pt_argc > 0) {
+ if (error == FCERR_NONE && partial->pt_argc > 0) {
for (argv_clear = 0; argv_clear < partial->pt_argc; argv_clear++) {
if (argv_clear + argcount_in >= MAX_FUNC_ARGS) {
- error = ERROR_TOOMANY;
+ error = FCERR_TOOMANY;
goto theend;
}
tv_copy(&partial->pt_argv[argv_clear], &argv[argv_clear]);
@@ -1494,8 +1555,8 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
}
}
- if (error == ERROR_NONE && funcexe->evaluate) {
- char_u *rfname = fname;
+ if (error == FCERR_NONE && funcexe->fe_evaluate) {
+ char *rfname = fname;
// Ignore "g:" before a function name.
if (fp == NULL && fname[0] == 'g' && fname[1] == ':') {
@@ -1504,12 +1565,12 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
rettv->v_type = VAR_NUMBER; // default rettv is number zero
rettv->vval.v_number = 0;
- error = ERROR_UNKNOWN;
+ error = FCERR_UNKNOWN;
if (is_luafunc(partial)) {
if (len > 0) {
- error = ERROR_NONE;
- argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
+ error = FCERR_NONE;
+ argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base);
nlua_typval_call(funcname, (size_t)len, argvars, argcount, rettv);
} else {
// v:lua was called directly; show its name in the emsg
@@ -1524,71 +1585,50 @@ int call_func(const char *funcname, int len, typval_T *rettv, int argcount_in, t
// Trigger FuncUndefined event, may load the function.
if (fp == NULL
- && apply_autocmds(EVENT_FUNCUNDEFINED, (char *)rfname, (char *)rfname, true, NULL)
+ && apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, true, NULL)
&& !aborting()) {
// executed an autocommand, search for the function again
fp = find_func(rfname);
}
// Try loading a package.
- if (fp == NULL && script_autoload((const char *)rfname, STRLEN(rfname),
+ if (fp == NULL && script_autoload((const char *)rfname, strlen(rfname),
true) && !aborting()) {
// Loaded a package, search for the function again.
fp = find_func(rfname);
}
if (fp != NULL && (fp->uf_flags & FC_DELETED)) {
- error = ERROR_DELETED;
- } else if (fp != NULL && (fp->uf_flags & FC_CFUNC)) {
- cfunc_T cb = fp->uf_cb;
- error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
+ error = FCERR_DELETED;
} else if (fp != NULL) {
- if (funcexe->argv_func != NULL) {
+ if (funcexe->fe_argv_func != NULL) {
// postponed filling in the arguments, do it now
- argcount = funcexe->argv_func(argcount, argvars, argv_clear,
- fp->uf_args.ga_len);
+ argcount = funcexe->fe_argv_func(argcount, argvars, argv_clear, fp);
}
- argv_add_base(funcexe->basetv, &argvars, &argcount, argv, &argv_base);
+ argv_add_base(funcexe->fe_basetv, &argvars, &argcount, argv, &argv_base);
- 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;
- } else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) {
- error = ERROR_TOOMANY;
- } else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) {
- error = ERROR_DICT;
- } else {
- // Call the user function.
- call_user_func(fp, argcount, argvars, rettv, funcexe->firstline,
- funcexe->lastline,
- (fp->uf_flags & FC_DICT) ? selfdict : NULL);
- error = ERROR_NONE;
- }
+ error = call_user_func_check(fp, argcount, argvars, rettv, funcexe, selfdict);
}
- } else if (funcexe->basetv != NULL) {
+ } else if (funcexe->fe_basetv != NULL) {
// 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);
+ funcexe->fe_basetv);
} else {
// Find the function name in the table, call its implementation.
error = call_internal_func(fname, argcount, argvars, rettv);
}
- /*
- * The function call (or "FuncUndefined" autocommand sequence) might
- * have been aborted by an error, an interrupt, or an explicitly thrown
- * exception that has not been caught so far. This situation can be
- * tested for by calling aborting(). For an error in an internal
- * function or for the "E132" error in call_user_func(), however, the
- * throw point at which the "force_abort" flag (temporarily reset by
- * emsg()) is normally updated has not been reached yet. We need to
- * update that flag first to make aborting() reliable.
- */
+ // The function call (or "FuncUndefined" autocommand sequence) might
+ // have been aborted by an error, an interrupt, or an explicitly thrown
+ // exception that has not been caught so far. This situation can be
+ // tested for by calling aborting(). For an error in an internal
+ // function or for the "E132" error in call_user_func(), however, the
+ // throw point at which the "force_abort" flag (temporarily reset by
+ // emsg()) is normally updated has not been reached yet. We need to
+ // update that flag first to make aborting() reliable.
update_force_abort();
}
- if (error == ERROR_NONE) {
+ if (error == FCERR_NONE) {
ret = OK;
}