mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #32293 from zeertzjq/vim-9.1.1063
vim-patch:8.2.{0825,1445,2505},9.0.1142,9.1.{1063,1066,1071}
This commit is contained in:
commit
efa3677f28
@ -2660,7 +2660,7 @@ int tv_dict_add_func(dict_T *const d, const char *const key, const size_t key_le
|
|||||||
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
|
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
|
||||||
|
|
||||||
item->di_tv.v_type = VAR_FUNC;
|
item->di_tv.v_type = VAR_FUNC;
|
||||||
item->di_tv.vval.v_string = xstrdup(fp->uf_name);
|
item->di_tv.vval.v_string = xmemdupz(fp->uf_name, fp->uf_namelen);
|
||||||
if (tv_dict_add(d, item) == FAIL) {
|
if (tv_dict_add(d, item) == FAIL) {
|
||||||
tv_dict_item_free(item);
|
tv_dict_item_free(item);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
@ -357,9 +357,10 @@ struct ufunc {
|
|||||||
funccall_T *uf_scoped; ///< l: local variables for closure
|
funccall_T *uf_scoped; ///< l: local variables for closure
|
||||||
char *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
|
char *uf_name_exp; ///< if "uf_name[]" starts with SNR the name with
|
||||||
///< "<SNR>" as a string, otherwise NULL
|
///< "<SNR>" as a string, otherwise NULL
|
||||||
char uf_name[]; ///< Name of function (actual size equals name);
|
size_t uf_namelen; ///< Length of uf_name (excluding the NUL)
|
||||||
///< can start with <SNR>123_
|
char uf_name[]; ///< Name of function (actual size equals name);
|
||||||
///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
|
///< can start with <SNR>123_
|
||||||
|
///< (<SNR> is K_SPECIAL KS_EXTRA KE_SNR)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct partial_S {
|
struct partial_S {
|
||||||
|
@ -264,25 +264,47 @@ static void register_closure(ufunc_T *fp)
|
|||||||
[current_funccal->fc_ufuncs.ga_len++] = fp;
|
[current_funccal->fc_ufuncs.ga_len++] = fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char lambda_name[8 + NUMBUFLEN];
|
||||||
|
static size_t lambda_namelen = 0;
|
||||||
|
|
||||||
/// @return a name for a lambda. Returned in static memory.
|
/// @return a name for a lambda. Returned in static memory.
|
||||||
char *get_lambda_name(void)
|
char *get_lambda_name(void)
|
||||||
{
|
{
|
||||||
static char name[30];
|
|
||||||
static int lambda_no = 0;
|
static int lambda_no = 0;
|
||||||
|
|
||||||
snprintf(name, sizeof(name), "<lambda>%d", ++lambda_no);
|
int n = snprintf(lambda_name, sizeof(lambda_name), "<lambda>%d", ++lambda_no);
|
||||||
return name;
|
if (n < 1) {
|
||||||
|
lambda_namelen = 0;
|
||||||
|
} else if (n >= (int)sizeof(lambda_name)) {
|
||||||
|
lambda_namelen = sizeof(lambda_name) - 1;
|
||||||
|
} else {
|
||||||
|
lambda_namelen = (size_t)n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lambda_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_ufunc_name(ufunc_T *fp, char *name)
|
/// Get the length of the last lambda name.
|
||||||
|
size_t get_lambda_name_len(void)
|
||||||
{
|
{
|
||||||
|
return lambda_namelen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allocate a "ufunc_T" for a function called "name".
|
||||||
|
static ufunc_T *alloc_ufunc(const char *name, size_t namelen)
|
||||||
|
{
|
||||||
|
size_t len = offsetof(ufunc_T, uf_name) + namelen + 1;
|
||||||
|
ufunc_T *fp = xcalloc(1, len);
|
||||||
STRCPY(fp->uf_name, name);
|
STRCPY(fp->uf_name, name);
|
||||||
|
fp->uf_namelen = namelen;
|
||||||
|
|
||||||
if ((uint8_t)name[0] == K_SPECIAL) {
|
if ((uint8_t)name[0] == K_SPECIAL) {
|
||||||
fp->uf_name_exp = xmalloc(strlen(name) + 3);
|
len = namelen + 3;
|
||||||
STRCPY(fp->uf_name_exp, "<SNR>");
|
fp->uf_name_exp = xmalloc(len);
|
||||||
strcat(fp->uf_name_exp, fp->uf_name + 3);
|
snprintf(fp->uf_name_exp, len, "<SNR>%s", fp->uf_name + 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a lambda expression and get a Funcref from "*arg".
|
/// Parse a lambda expression and get a Funcref from "*arg".
|
||||||
@ -350,8 +372,9 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
garray_T newlines;
|
garray_T newlines;
|
||||||
|
|
||||||
char *name = get_lambda_name();
|
char *name = get_lambda_name();
|
||||||
|
size_t namelen = get_lambda_name_len();
|
||||||
|
|
||||||
fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
|
fp = alloc_ufunc(name, namelen);
|
||||||
pt = xcalloc(1, sizeof(partial_T));
|
pt = xcalloc(1, sizeof(partial_T));
|
||||||
|
|
||||||
ga_init(&newlines, (int)sizeof(char *), 1);
|
ga_init(&newlines, (int)sizeof(char *), 1);
|
||||||
@ -369,7 +392,6 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fp->uf_refcount = 1;
|
fp->uf_refcount = 1;
|
||||||
set_ufunc_name(fp, name);
|
|
||||||
hash_add(&func_hashtab, UF2HIKEY(fp));
|
hash_add(&func_hashtab, UF2HIKEY(fp));
|
||||||
fp->uf_args = newargs;
|
fp->uf_args = newargs;
|
||||||
ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
|
ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
|
||||||
@ -409,7 +431,10 @@ int get_lambda_tv(char **arg, typval_T *rettv, evalarg_T *evalarg)
|
|||||||
|
|
||||||
errret:
|
errret:
|
||||||
ga_clear_strings(&newargs);
|
ga_clear_strings(&newargs);
|
||||||
xfree(fp);
|
if (fp != NULL) {
|
||||||
|
xfree(fp->uf_name_exp);
|
||||||
|
xfree(fp);
|
||||||
|
}
|
||||||
xfree(pt);
|
xfree(pt);
|
||||||
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
|
if (evalarg != NULL && evalarg->eval_tofree == NULL) {
|
||||||
evalarg->eval_tofree = tofree;
|
evalarg->eval_tofree = tofree;
|
||||||
@ -627,33 +652,36 @@ static char *fname_trans_sid(const char *const name, char *const fname_buf, char
|
|||||||
int *const error)
|
int *const error)
|
||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
const int llen = eval_fname_script(name);
|
const char *script_name = name + eval_fname_script(name);
|
||||||
if (llen == 0) {
|
if (script_name == name) {
|
||||||
return (char *)name; // no prefix
|
return (char *)name; // no prefix
|
||||||
}
|
}
|
||||||
|
|
||||||
fname_buf[0] = (char)K_SPECIAL;
|
fname_buf[0] = (char)K_SPECIAL;
|
||||||
fname_buf[1] = (char)KS_EXTRA;
|
fname_buf[1] = (char)KS_EXTRA;
|
||||||
fname_buf[2] = KE_SNR;
|
fname_buf[2] = KE_SNR;
|
||||||
int i = 3;
|
size_t fname_buflen = 3;
|
||||||
if (eval_fname_sid(name)) { // "<SID>" or "s:"
|
if (!eval_fname_sid(name)) { // "<SID>" or "s:"
|
||||||
|
fname_buf[fname_buflen] = NUL;
|
||||||
|
} else {
|
||||||
if (current_sctx.sc_sid <= 0) {
|
if (current_sctx.sc_sid <= 0) {
|
||||||
*error = FCERR_SCRIPT;
|
*error = FCERR_SCRIPT;
|
||||||
} else {
|
} else {
|
||||||
snprintf(fname_buf + i, (size_t)(FLEN_FIXED + 1 - i), "%" PRId64 "_",
|
fname_buflen += (size_t)snprintf(fname_buf + fname_buflen,
|
||||||
(int64_t)current_sctx.sc_sid);
|
FLEN_FIXED + 1 - fname_buflen,
|
||||||
i = (int)strlen(fname_buf);
|
"%" PRIdSCID "_",
|
||||||
|
current_sctx.sc_sid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
size_t fnamelen = fname_buflen + strlen(script_name);
|
||||||
char *fname;
|
char *fname;
|
||||||
if ((size_t)i + strlen(name + llen) < FLEN_FIXED) {
|
if (fnamelen < FLEN_FIXED) {
|
||||||
STRCPY(fname_buf + i, name + llen);
|
STRCPY(fname_buf + fname_buflen, script_name);
|
||||||
fname = fname_buf;
|
fname = fname_buf;
|
||||||
} else {
|
} else {
|
||||||
fname = xmalloc((size_t)i + strlen(name + llen) + 1);
|
fname = xmalloc(fnamelen + 1);
|
||||||
*tofree = fname;
|
*tofree = fname;
|
||||||
memmove(fname, fname_buf, (size_t)i);
|
snprintf(fname, fnamelen + 1, "%s%s", fname_buf, script_name);
|
||||||
STRCPY(fname + i, name + llen);
|
|
||||||
}
|
}
|
||||||
return fname;
|
return fname;
|
||||||
}
|
}
|
||||||
@ -711,20 +739,20 @@ ufunc_T *find_func(const char *name)
|
|||||||
/// Copy the function name of "fp" to buffer "buf".
|
/// Copy the function name of "fp" to buffer "buf".
|
||||||
/// "buf" must be able to hold the function name plus three bytes.
|
/// "buf" must be able to hold the function name plus three bytes.
|
||||||
/// Takes care of script-local function names.
|
/// Takes care of script-local function names.
|
||||||
static void cat_func_name(char *buf, size_t buflen, ufunc_T *fp)
|
static int cat_func_name(char *buf, size_t bufsize, ufunc_T *fp)
|
||||||
{
|
{
|
||||||
int len = -1;
|
int len = -1;
|
||||||
size_t uflen = strlen(fp->uf_name);
|
size_t uflen = fp->uf_namelen;
|
||||||
assert(uflen > 0);
|
assert(uflen > 0);
|
||||||
|
|
||||||
if ((uint8_t)fp->uf_name[0] == K_SPECIAL && uflen > 3) {
|
if ((uint8_t)fp->uf_name[0] == K_SPECIAL && uflen > 3) {
|
||||||
len = snprintf(buf, buflen, "<SNR>%s", fp->uf_name + 3);
|
len = snprintf(buf, bufsize, "<SNR>%s", fp->uf_name + 3);
|
||||||
} else {
|
} else {
|
||||||
len = snprintf(buf, buflen, "%s", fp->uf_name);
|
len = snprintf(buf, bufsize, "%s", fp->uf_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
(void)len; // Avoid unused warning on release builds
|
|
||||||
assert(len > 0);
|
assert(len > 0);
|
||||||
|
return (len >= (int)bufsize) ? (int)bufsize - 1 : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a number variable "name" to dict "dp" with value "nr".
|
/// Add a number variable "name" to dict "dp" with value "nr".
|
||||||
@ -891,7 +919,6 @@ static void func_clear_items(ufunc_T *fp)
|
|||||||
ga_clear_strings(&(fp->uf_args));
|
ga_clear_strings(&(fp->uf_args));
|
||||||
ga_clear_strings(&(fp->uf_def_args));
|
ga_clear_strings(&(fp->uf_def_args));
|
||||||
ga_clear_strings(&(fp->uf_lines));
|
ga_clear_strings(&(fp->uf_lines));
|
||||||
XFREE_CLEAR(fp->uf_name_exp);
|
|
||||||
|
|
||||||
if (fp->uf_flags & FC_LUAREF) {
|
if (fp->uf_flags & FC_LUAREF) {
|
||||||
api_free_luaref(fp->uf_luaref);
|
api_free_luaref(fp->uf_luaref);
|
||||||
@ -930,6 +957,8 @@ static void func_free(ufunc_T *fp)
|
|||||||
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) {
|
if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) {
|
||||||
func_remove(fp);
|
func_remove(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XFREE_CLEAR(fp->uf_name_exp);
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -983,6 +1012,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
|
|||||||
bool islambda = false;
|
bool islambda = false;
|
||||||
char numbuf[NUMBUFLEN];
|
char numbuf[NUMBUFLEN];
|
||||||
char *name;
|
char *name;
|
||||||
|
size_t namelen;
|
||||||
typval_T *tv_to_free[MAX_FUNC_ARGS];
|
typval_T *tv_to_free[MAX_FUNC_ARGS];
|
||||||
int tv_to_free_len = 0;
|
int tv_to_free_len = 0;
|
||||||
proftime_T wait_start;
|
proftime_T wait_start;
|
||||||
@ -1104,23 +1134,25 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rett
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namelen = strlen(name);
|
||||||
} else {
|
} else {
|
||||||
if ((fp->uf_flags & FC_NOARGS) != 0) {
|
if ((fp->uf_flags & FC_NOARGS) != 0) {
|
||||||
// Bail out if no a: arguments used (in lambda).
|
// Bail out if no a: arguments used (in lambda).
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// "..." argument a:1, a:2, etc.
|
// "..." argument a:1, a:2, etc.
|
||||||
snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
|
namelen = (size_t)snprintf(numbuf, sizeof(numbuf), "%d", ai + 1);
|
||||||
name = numbuf;
|
name = numbuf;
|
||||||
}
|
}
|
||||||
if (fixvar_idx < FIXVAR_CNT && strlen(name) <= VAR_SHORT_LEN) {
|
if (fixvar_idx < FIXVAR_CNT && namelen <= VAR_SHORT_LEN) {
|
||||||
v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
|
v = (dictitem_T *)&fc->fc_fixvar[fixvar_idx++];
|
||||||
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
|
||||||
|
STRCPY(v->di_key, name);
|
||||||
} else {
|
} else {
|
||||||
v = xmalloc(sizeof(dictitem_T) + strlen(name));
|
v = tv_dict_item_alloc_len(name, namelen);
|
||||||
v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
|
v->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
|
||||||
}
|
}
|
||||||
STRCPY(v->di_key, name);
|
|
||||||
|
|
||||||
// Note: the values are copied directly to avoid alloc/free.
|
// Note: the values are copied directly to avoid alloc/free.
|
||||||
// "argvars" must have VAR_FIXED for v_lock.
|
// "argvars" must have VAR_FIXED for v_lock.
|
||||||
@ -2095,7 +2127,7 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
|
|||||||
len = (int)(end - lv.ll_name);
|
len = (int)(end - lv.ll_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t sid_buf_len = 0;
|
size_t sid_buflen = 0;
|
||||||
char sid_buf[20];
|
char sid_buf[20];
|
||||||
|
|
||||||
// Copy the function name to allocated memory.
|
// Copy the function name to allocated memory.
|
||||||
@ -2105,15 +2137,16 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
|
|||||||
lead = 0; // do nothing
|
lead = 0; // do nothing
|
||||||
} else if (lead > 0) {
|
} else if (lead > 0) {
|
||||||
lead = 3;
|
lead = 3;
|
||||||
if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid(*pp)) {
|
if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name))
|
||||||
|
|| eval_fname_sid(*pp)) {
|
||||||
// It's "s:" or "<SID>".
|
// It's "s:" or "<SID>".
|
||||||
if (current_sctx.sc_sid <= 0) {
|
if (current_sctx.sc_sid <= 0) {
|
||||||
emsg(_(e_usingsid));
|
emsg(_(e_usingsid));
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
sid_buf_len =
|
sid_buflen = (size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_",
|
||||||
(size_t)snprintf(sid_buf, sizeof(sid_buf), "%" PRIdSCID "_", current_sctx.sc_sid);
|
current_sctx.sc_sid);
|
||||||
lead += (int)sid_buf_len;
|
lead += (int)sid_buflen;
|
||||||
}
|
}
|
||||||
} else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, (int)lv.ll_name_len)) {
|
} else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, (int)lv.ll_name_len)) {
|
||||||
semsg(_("E128: Function name must start with a capital or \"s:\": %s"),
|
semsg(_("E128: Function name must start with a capital or \"s:\": %s"),
|
||||||
@ -2135,8 +2168,8 @@ char *trans_function_name(char **pp, bool skip, int flags, funcdict_T *fdp, part
|
|||||||
name[0] = (char)K_SPECIAL;
|
name[0] = (char)K_SPECIAL;
|
||||||
name[1] = (char)KS_EXTRA;
|
name[1] = (char)KS_EXTRA;
|
||||||
name[2] = KE_SNR;
|
name[2] = KE_SNR;
|
||||||
if (sid_buf_len > 0) { // If it's "<SID>"
|
if (sid_buflen > 0) { // If it's "<SID>"
|
||||||
memcpy(name + 3, sid_buf, sid_buf_len);
|
memcpy(name + 3, sid_buf, sid_buflen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
memmove(name + lead, lv.ll_name, (size_t)len);
|
memmove(name + lead, lv.ll_name, (size_t)len);
|
||||||
@ -2172,12 +2205,12 @@ char *get_scriptlocal_funcname(char *funcname)
|
|||||||
|
|
||||||
char sid_buf[25];
|
char sid_buf[25];
|
||||||
// Expand s: and <SID> prefix into <SNR>nr_<name>
|
// Expand s: and <SID> prefix into <SNR>nr_<name>
|
||||||
snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_",
|
size_t sid_buflen = (size_t)snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRIdSCID "_",
|
||||||
(int64_t)current_sctx.sc_sid);
|
current_sctx.sc_sid);
|
||||||
const int off = *funcname == 's' ? 2 : 5;
|
const int off = *funcname == 's' ? 2 : 5;
|
||||||
char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1);
|
size_t newnamesize = sid_buflen + strlen(funcname + off) + 1;
|
||||||
STRCPY(newname, sid_buf);
|
char *newname = xmalloc(newnamesize);
|
||||||
strcat(newname, funcname + off);
|
snprintf(newname, newnamesize, "%s%s", sid_buf, funcname + off);
|
||||||
|
|
||||||
return newname;
|
return newname;
|
||||||
}
|
}
|
||||||
@ -2249,6 +2282,7 @@ static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in
|
|||||||
int ret = FAIL;
|
int ret = FAIL;
|
||||||
bool is_heredoc = false;
|
bool is_heredoc = false;
|
||||||
char *heredoc_trimmed = NULL;
|
char *heredoc_trimmed = NULL;
|
||||||
|
size_t heredoc_trimmedlen = 0;
|
||||||
bool do_concat = true;
|
bool do_concat = true;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -2312,19 +2346,18 @@ static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in
|
|||||||
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
|
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
|
||||||
if (heredoc_trimmed == NULL
|
if (heredoc_trimmed == NULL
|
||||||
|| (is_heredoc && skipwhite(theline) == theline)
|
|| (is_heredoc && skipwhite(theline) == theline)
|
||||||
|| strncmp(theline, heredoc_trimmed,
|
|| strncmp(theline, heredoc_trimmed, heredoc_trimmedlen) == 0) {
|
||||||
strlen(heredoc_trimmed)) == 0) {
|
|
||||||
if (heredoc_trimmed == NULL) {
|
if (heredoc_trimmed == NULL) {
|
||||||
p = theline;
|
p = theline;
|
||||||
} else if (is_heredoc) {
|
} else if (is_heredoc) {
|
||||||
p = skipwhite(theline) == theline
|
p = skipwhite(theline) == theline ? theline : theline + heredoc_trimmedlen;
|
||||||
? theline : theline + strlen(heredoc_trimmed);
|
|
||||||
} else {
|
} else {
|
||||||
p = theline + strlen(heredoc_trimmed);
|
p = theline + heredoc_trimmedlen;
|
||||||
}
|
}
|
||||||
if (strcmp(p, skip_until) == 0) {
|
if (strcmp(p, skip_until) == 0) {
|
||||||
XFREE_CLEAR(skip_until);
|
XFREE_CLEAR(skip_until);
|
||||||
XFREE_CLEAR(heredoc_trimmed);
|
XFREE_CLEAR(heredoc_trimmed);
|
||||||
|
heredoc_trimmedlen = 0;
|
||||||
do_concat = true;
|
do_concat = true;
|
||||||
is_heredoc = false;
|
is_heredoc = false;
|
||||||
}
|
}
|
||||||
@ -2401,7 +2434,7 @@ static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in
|
|||||||
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
|
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
|
||||||
&& (!ASCII_ISALPHA(p[2])
|
&& (!ASCII_ISALPHA(p[2])
|
||||||
|| (p[2] == 's')))))) {
|
|| (p[2] == 's')))))) {
|
||||||
skip_until = xstrdup(".");
|
skip_until = xmemdupz(".", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
|
// heredoc: Check for ":python <<EOF", ":lua <<EOF", etc.
|
||||||
@ -2426,10 +2459,11 @@ static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in
|
|||||||
if (strncmp(p, "trim", 4) == 0) {
|
if (strncmp(p, "trim", 4) == 0) {
|
||||||
// Ignore leading white space.
|
// Ignore leading white space.
|
||||||
p = skipwhite(p + 4);
|
p = skipwhite(p + 4);
|
||||||
heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
|
heredoc_trimmedlen = (size_t)(skipwhite(theline) - theline);
|
||||||
|
heredoc_trimmed = xmemdupz(theline, heredoc_trimmedlen);
|
||||||
}
|
}
|
||||||
if (*p == NUL) {
|
if (*p == NUL) {
|
||||||
skip_until = xstrdup(".");
|
skip_until = xmemdupz(".", 1);
|
||||||
} else {
|
} else {
|
||||||
skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
|
skip_until = xmemdupz(p, (size_t)(skiptowhite(p) - p));
|
||||||
}
|
}
|
||||||
@ -2453,7 +2487,8 @@ static int get_function_body(exarg_T *eap, garray_T *newlines, char *line_arg_in
|
|||||||
if (strncmp(p, "trim", 4) == 0) {
|
if (strncmp(p, "trim", 4) == 0) {
|
||||||
// Ignore leading white space.
|
// Ignore leading white space.
|
||||||
p = skipwhite(p + 4);
|
p = skipwhite(p + 4);
|
||||||
heredoc_trimmed = xmemdupz(theline, (size_t)(skipwhite(theline) - theline));
|
heredoc_trimmedlen = (size_t)(skipwhite(theline) - theline);
|
||||||
|
heredoc_trimmed = xmemdupz(theline, heredoc_trimmedlen);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strncmp(p, "eval", 4) == 0) {
|
if (strncmp(p, "eval", 4) == 0) {
|
||||||
@ -2514,7 +2549,8 @@ void ex_function(exarg_T *eap)
|
|||||||
garray_T newlines;
|
garray_T newlines;
|
||||||
int varargs = false;
|
int varargs = false;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
ufunc_T *fp;
|
ufunc_T *fp = NULL;
|
||||||
|
bool free_fp = false;
|
||||||
bool overwrite = false;
|
bool overwrite = false;
|
||||||
funcdict_T fudi;
|
funcdict_T fudi;
|
||||||
static int func_nr = 0; // number for nameless function
|
static int func_nr = 0; // number for nameless function
|
||||||
@ -2773,6 +2809,7 @@ void ex_function(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there are no errors, add the function
|
// If there are no errors, add the function
|
||||||
|
size_t namelen = 0;
|
||||||
if (fudi.fd_dict == NULL) {
|
if (fudi.fd_dict == NULL) {
|
||||||
dictitem_T *v = find_var(name, strlen(name), &ht, false);
|
dictitem_T *v = find_var(name, strlen(name), &ht, false);
|
||||||
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
|
if (v != NULL && v->di_tv.v_type == VAR_FUNC) {
|
||||||
@ -2788,11 +2825,11 @@ void ex_function(exarg_T *eap)
|
|||||||
&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
|
&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
|
||||||
|| fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) {
|
|| fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) {
|
||||||
emsg_funcname(e_funcexts, name);
|
emsg_funcname(e_funcexts, name);
|
||||||
goto erret;
|
goto errret_keep;
|
||||||
}
|
}
|
||||||
if (fp->uf_calls > 0) {
|
if (fp->uf_calls > 0) {
|
||||||
emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name);
|
emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"), name);
|
||||||
goto erret;
|
goto errret_keep;
|
||||||
}
|
}
|
||||||
if (fp->uf_refcount > 1) {
|
if (fp->uf_refcount > 1) {
|
||||||
// This function is referenced somewhere, don't redefine it but
|
// This function is referenced somewhere, don't redefine it but
|
||||||
@ -2813,7 +2850,7 @@ void ex_function(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char numbuf[20];
|
char numbuf[NUMBUFLEN];
|
||||||
|
|
||||||
fp = NULL;
|
fp = NULL;
|
||||||
if (fudi.fd_newkey == NULL && !eap->forceit) {
|
if (fudi.fd_newkey == NULL && !eap->forceit) {
|
||||||
@ -2833,8 +2870,8 @@ void ex_function(exarg_T *eap)
|
|||||||
// Give the function a sequential number. Can only be used with a
|
// Give the function a sequential number. Can only be used with a
|
||||||
// Funcref!
|
// Funcref!
|
||||||
xfree(name);
|
xfree(name);
|
||||||
snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
|
namelen = (size_t)snprintf(numbuf, sizeof(numbuf), "%d", ++func_nr);
|
||||||
name = xstrdup(numbuf);
|
name = xmemdupz(numbuf, namelen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
@ -2858,7 +2895,10 @@ void ex_function(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
|
if (namelen == 0) {
|
||||||
|
namelen = strlen(name);
|
||||||
|
}
|
||||||
|
fp = alloc_ufunc(name, namelen);
|
||||||
|
|
||||||
if (fudi.fd_dict != NULL) {
|
if (fudi.fd_dict != NULL) {
|
||||||
if (fudi.fd_di == NULL) {
|
if (fudi.fd_di == NULL) {
|
||||||
@ -2867,6 +2907,7 @@ void ex_function(exarg_T *eap)
|
|||||||
if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
|
if (tv_dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) {
|
||||||
xfree(fudi.fd_di);
|
xfree(fudi.fd_di);
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
|
fp = NULL;
|
||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -2874,19 +2915,18 @@ void ex_function(exarg_T *eap)
|
|||||||
tv_clear(&fudi.fd_di->di_tv);
|
tv_clear(&fudi.fd_di->di_tv);
|
||||||
}
|
}
|
||||||
fudi.fd_di->di_tv.v_type = VAR_FUNC;
|
fudi.fd_di->di_tv.v_type = VAR_FUNC;
|
||||||
fudi.fd_di->di_tv.vval.v_string = xstrdup(name);
|
fudi.fd_di->di_tv.vval.v_string = xmemdupz(name, namelen);
|
||||||
|
|
||||||
// behave like "dict" was used
|
// behave like "dict" was used
|
||||||
flags |= FC_DICT;
|
flags |= FC_DICT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert the new function in the function list
|
// insert the new function in the function list
|
||||||
set_ufunc_name(fp, name);
|
|
||||||
if (overwrite) {
|
if (overwrite) {
|
||||||
hashitem_T *hi = hash_find(&func_hashtab, name);
|
hashitem_T *hi = hash_find(&func_hashtab, name);
|
||||||
hi->hi_key = UF2HIKEY(fp);
|
hi->hi_key = UF2HIKEY(fp);
|
||||||
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
|
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
|
||||||
xfree(fp);
|
free_fp = true;
|
||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
fp->uf_refcount = 1;
|
fp->uf_refcount = 1;
|
||||||
@ -2915,9 +2955,22 @@ void ex_function(exarg_T *eap)
|
|||||||
goto ret_free;
|
goto ret_free;
|
||||||
|
|
||||||
erret:
|
erret:
|
||||||
|
if (fp != NULL) {
|
||||||
|
// these were set to "newargs" and "default_args", which are cleared below
|
||||||
|
ga_init(&fp->uf_args, (int)sizeof(char *), 1);
|
||||||
|
ga_init(&fp->uf_def_args, (int)sizeof(char *), 1);
|
||||||
|
}
|
||||||
|
errret_2:
|
||||||
|
if (fp != NULL) {
|
||||||
|
XFREE_CLEAR(fp->uf_name_exp);
|
||||||
|
}
|
||||||
|
if (free_fp) {
|
||||||
|
xfree(fp);
|
||||||
|
fp = NULL;
|
||||||
|
}
|
||||||
|
errret_keep:
|
||||||
ga_clear_strings(&newargs);
|
ga_clear_strings(&newargs);
|
||||||
ga_clear_strings(&default_args);
|
ga_clear_strings(&default_args);
|
||||||
errret_2:
|
|
||||||
ga_clear_strings(&newlines);
|
ga_clear_strings(&newlines);
|
||||||
ret_free:
|
ret_free:
|
||||||
xfree(line_to_free);
|
xfree(line_to_free);
|
||||||
@ -3010,15 +3063,16 @@ char *get_user_func_name(expand_T *xp, int idx)
|
|||||||
return ""; // don't show dict and lambda functions
|
return ""; // don't show dict and lambda functions
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(fp->uf_name) + 4 >= IOSIZE) {
|
if (fp->uf_namelen + 4 >= IOSIZE) {
|
||||||
return fp->uf_name; // Prevent overflow.
|
return fp->uf_name; // Prevent overflow.
|
||||||
}
|
}
|
||||||
|
|
||||||
cat_func_name(IObuff, IOSIZE, fp);
|
int len = cat_func_name(IObuff, IOSIZE, fp);
|
||||||
if (xp->xp_context != EXPAND_USER_FUNC) {
|
if (xp->xp_context != EXPAND_USER_FUNC) {
|
||||||
xstrlcat(IObuff, "(", IOSIZE);
|
xstrlcpy(IObuff + len, "(", IOSIZE - (size_t)len);
|
||||||
if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) {
|
if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) {
|
||||||
xstrlcat(IObuff, ")", IOSIZE);
|
len++;
|
||||||
|
xstrlcpy(IObuff + len, ")", IOSIZE - (size_t)len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return IObuff;
|
return IObuff;
|
||||||
@ -3620,21 +3674,26 @@ char *get_return_cmd(void *rettv)
|
|||||||
{
|
{
|
||||||
char *s = NULL;
|
char *s = NULL;
|
||||||
char *tofree = NULL;
|
char *tofree = NULL;
|
||||||
|
size_t slen = 0;
|
||||||
|
|
||||||
if (rettv != NULL) {
|
if (rettv != NULL) {
|
||||||
tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
|
tofree = s = encode_tv2echo((typval_T *)rettv, NULL);
|
||||||
}
|
}
|
||||||
if (s == NULL) {
|
if (s == NULL) {
|
||||||
s = "";
|
s = "";
|
||||||
|
} else {
|
||||||
|
slen = strlen(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
xstrlcpy(IObuff, ":return ", IOSIZE);
|
xstrlcpy(IObuff, ":return ", IOSIZE);
|
||||||
xstrlcpy(IObuff + 8, s, IOSIZE - 8);
|
xstrlcpy(IObuff + 8, s, IOSIZE - 8);
|
||||||
if (strlen(s) + 8 >= IOSIZE) {
|
size_t IObufflen = 8 + slen;
|
||||||
|
if (IObufflen >= IOSIZE) {
|
||||||
STRCPY(IObuff + IOSIZE - 4, "...");
|
STRCPY(IObuff + IOSIZE - 4, "...");
|
||||||
|
IObufflen = IOSIZE - 1;
|
||||||
}
|
}
|
||||||
xfree(tofree);
|
xfree(tofree);
|
||||||
return xstrdup(IObuff);
|
return xstrnsave(IObuff, IObufflen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get next function line.
|
/// Get next function line.
|
||||||
@ -4077,7 +4136,8 @@ bool set_ref_in_func(char *name, ufunc_T *fp_in, int copyID)
|
|||||||
char *register_luafunc(LuaRef ref)
|
char *register_luafunc(LuaRef ref)
|
||||||
{
|
{
|
||||||
char *name = get_lambda_name();
|
char *name = get_lambda_name();
|
||||||
ufunc_T *fp = xcalloc(1, offsetof(ufunc_T, uf_name) + strlen(name) + 1);
|
size_t namelen = get_lambda_name_len();
|
||||||
|
ufunc_T *fp = alloc_ufunc(name, namelen);
|
||||||
|
|
||||||
fp->uf_refcount = 1;
|
fp->uf_refcount = 1;
|
||||||
fp->uf_varargs = true;
|
fp->uf_varargs = true;
|
||||||
@ -4086,7 +4146,6 @@ char *register_luafunc(LuaRef ref)
|
|||||||
fp->uf_script_ctx = current_sctx;
|
fp->uf_script_ctx = current_sctx;
|
||||||
fp->uf_luaref = ref;
|
fp->uf_luaref = ref;
|
||||||
|
|
||||||
STRCPY(fp->uf_name, name);
|
|
||||||
hash_add(&func_hashtab, UF2HIKEY(fp));
|
hash_add(&func_hashtab, UF2HIKEY(fp));
|
||||||
|
|
||||||
// coverity[leaked_storage]
|
// coverity[leaked_storage]
|
||||||
|
@ -7400,8 +7400,7 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum
|
|||||||
*errormsg = _(e_usingsid);
|
*errormsg = _(e_usingsid);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_",
|
snprintf(strbuf, sizeof(strbuf), "<SNR>%" PRIdSCID "_", current_sctx.sc_sid);
|
||||||
current_sctx.sc_sid);
|
|
||||||
result = strbuf;
|
result = strbuf;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -917,7 +917,7 @@ char *replace_termcodes(const char *const from, const size_t from_len, char **co
|
|||||||
result[dlen++] = (char)K_SPECIAL;
|
result[dlen++] = (char)K_SPECIAL;
|
||||||
result[dlen++] = (char)KS_EXTRA;
|
result[dlen++] = (char)KS_EXTRA;
|
||||||
result[dlen++] = KE_SNR;
|
result[dlen++] = KE_SNR;
|
||||||
snprintf(result + dlen, buf_len - dlen, "%" PRId64, (int64_t)sid);
|
snprintf(result + dlen, buf_len - dlen, "%" PRIdSCID, sid);
|
||||||
dlen += strlen(result + dlen);
|
dlen += strlen(result + dlen);
|
||||||
result[dlen++] = '_';
|
result[dlen++] = '_';
|
||||||
continue;
|
continue;
|
||||||
|
@ -421,12 +421,48 @@ func Test_func_def_error()
|
|||||||
call assert_fails('exe l', 'E717:')
|
call assert_fails('exe l', 'E717:')
|
||||||
|
|
||||||
" Define an autoload function with an incorrect file name
|
" Define an autoload function with an incorrect file name
|
||||||
call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript')
|
call writefile(['func foo#Bar()', 'return 1', 'endfunc'], 'Xscript', 'D')
|
||||||
call assert_fails('source Xscript', 'E746:')
|
call assert_fails('source Xscript', 'E746:')
|
||||||
call delete('Xscript')
|
|
||||||
|
|
||||||
" Try to list functions using an invalid search pattern
|
" Try to list functions using an invalid search pattern
|
||||||
call assert_fails('function /\%(/', 'E53:')
|
call assert_fails('function /\%(/', 'E53:')
|
||||||
|
|
||||||
|
" Use a script-local function to cover uf_name_exp.
|
||||||
|
func s:TestRedefine(arg1 = 1, arg2 = 10)
|
||||||
|
let caught_E122 = 0
|
||||||
|
try
|
||||||
|
func s:TestRedefine(arg1 = 1, arg2 = 10)
|
||||||
|
endfunc
|
||||||
|
catch /E122:/
|
||||||
|
let caught_E122 = 1
|
||||||
|
endtry
|
||||||
|
call assert_equal(1, caught_E122)
|
||||||
|
|
||||||
|
let caught_E127 = 0
|
||||||
|
try
|
||||||
|
func! s:TestRedefine(arg1 = 1, arg2 = 10)
|
||||||
|
endfunc
|
||||||
|
catch /E127:/
|
||||||
|
let caught_E127 = 1
|
||||||
|
endtry
|
||||||
|
call assert_equal(1, caught_E127)
|
||||||
|
|
||||||
|
" The failures above shouldn't cause heap-use-after-free here.
|
||||||
|
return [a:arg1 + a:arg2, expand('<stack>')]
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
let stacks = []
|
||||||
|
" Call the function twice.
|
||||||
|
" Failing to redefine a function shouldn't clear its argument list.
|
||||||
|
for i in range(2)
|
||||||
|
let [val, stack] = s:TestRedefine(1000)
|
||||||
|
call assert_equal(1010, val)
|
||||||
|
call assert_match(expand('<SID>') .. 'TestRedefine\[20\]$', stack)
|
||||||
|
call add(stacks, stack)
|
||||||
|
endfor
|
||||||
|
call assert_equal(stacks[0], stacks[1])
|
||||||
|
|
||||||
|
delfunc s:TestRedefine
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for deleting a function
|
" Test for deleting a function
|
||||||
@ -910,4 +946,36 @@ func Test_func_curly_brace_invalid_name()
|
|||||||
delfunc Fail
|
delfunc Fail
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_func_return_in_try_verbose()
|
||||||
|
func TryReturnList()
|
||||||
|
try
|
||||||
|
return [1, 2, 3]
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
func TryReturnNumber()
|
||||||
|
try
|
||||||
|
return 123
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
func TryReturnOverlongString()
|
||||||
|
try
|
||||||
|
return repeat('a', 9999)
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" This should not cause heap-use-after-free
|
||||||
|
call assert_match('\n:return \[1, 2, 3\] made pending\n',
|
||||||
|
\ execute('14verbose call TryReturnList()'))
|
||||||
|
" This should not cause stack-use-after-scope
|
||||||
|
call assert_match('\n:return 123 made pending\n',
|
||||||
|
\ execute('14verbose call TryReturnNumber()'))
|
||||||
|
" An overlong string is truncated
|
||||||
|
call assert_match('\n:return a\{100,}\.\.\.',
|
||||||
|
\ execute('14verbose call TryReturnOverlongString()'))
|
||||||
|
|
||||||
|
delfunc TryReturnList
|
||||||
|
delfunc TryReturnNumber
|
||||||
|
delfunc TryReturnOverlongString
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
Loading…
Reference in New Issue
Block a user