Merge #7806 from ZyX-I/list-stat

Add a way to collect list usage statistics
This commit is contained in:
Justin M. Keyes 2018-01-15 23:35:20 +01:00 committed by GitHub
commit de0a9548f7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 427 additions and 205 deletions

View File

@ -277,6 +277,8 @@ else()
set(DEBUG 0)
endif()
option(LOG_LIST_ACTIONS "Add list actions logging" OFF)
add_definitions(-DINCLUDE_GENERATED_DECLARATIONS)
if(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_NAME STREQUAL "Linux")

View File

@ -62,6 +62,7 @@
#ifndef UNIT_TESTING
#cmakedefine HAVE_JEMALLOC
#cmakedefine LOG_LIST_ACTIONS
#endif
#cmakedefine HAVE_BE64TOH

View File

@ -783,7 +783,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
break;
case kObjectTypeArray: {
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc((ptrdiff_t)obj.data.array.size);
for (uint32_t i = 0; i < obj.data.array.size; i++) {
Object item = obj.data.array.items[i];

View File

@ -4,6 +4,7 @@
#include "nvim/api/ui.h"
#include "nvim/channel.h"
#include "nvim/eval.h"
#include "nvim/eval/encode.h"
#include "nvim/event/socket.h"
#include "nvim/msgpack_rpc/channel.h"
#include "nvim/msgpack_rpc/server.h"
@ -522,32 +523,21 @@ err:
return 0;
}
/// NB: mutates buf in place!
static list_T *buffer_to_tv_list(char *buf, size_t count)
/// Convert binary byte array to a readfile()-style list
///
/// @param[in] buf Array to convert.
/// @param[in] len Array length.
///
/// @return [allocated] Converted list.
static inline list_T *buffer_to_tv_list(const char *const buf, const size_t len)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE
{
list_T *ret = tv_list_alloc();
char *ptr = buf;
size_t remaining = count;
size_t off = 0;
while (off < remaining) {
// append the line
if (ptr[off] == NL) {
tv_list_append_string(ret, ptr, (ssize_t)off);
size_t skip = off + 1;
ptr += skip;
remaining -= skip;
off = 0;
continue;
}
if (ptr[off] == NUL) {
// Translate NUL to NL
ptr[off] = NL;
}
off++;
}
tv_list_append_string(ret, ptr, (ssize_t)off);
return ret;
list_T *const l = tv_list_alloc(kListLenMayKnow);
// Empty buffer should be represented by [''], encode_list_write() thinks
// empty list is fine for the case.
tv_list_append_string(l, "", 0);
encode_list_write(l, buf, len);
return l;
}
// vimscript job callbacks must be executed on Nvim main loop

View File

@ -568,7 +568,7 @@ void eval_init(void)
dict_T *const msgpack_types_dict = tv_dict_alloc();
for (size_t i = 0; i < ARRAY_SIZE(msgpack_type_names); i++) {
list_T *const type_list = tv_list_alloc();
list_T *const type_list = tv_list_alloc(0);
tv_list_set_lock(type_list, VAR_FIXED);
tv_list_ref(type_list);
dictitem_T *const di = tv_dict_item_alloc(msgpack_type_names[i]);
@ -591,7 +591,7 @@ void eval_init(void)
dict_T *v_event = tv_dict_alloc();
v_event->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_EVENT, v_event);
set_vim_var_list(VV_ERRORS, tv_list_alloc());
set_vim_var_list(VV_ERRORS, tv_list_alloc(kListLenUnknown));
set_vim_var_nr(VV_STDERR, CHAN_STDERR);
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
@ -1546,6 +1546,7 @@ ex_let_vars (
assert(l != NULL);
listitem_T *item = tv_list_first(l);
size_t rest_len = tv_list_len(l);
while (*arg != ']') {
arg = skipwhite(arg + 1);
arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]",
@ -1553,13 +1554,14 @@ ex_let_vars (
if (arg == NULL) {
return FAIL;
}
rest_len--;
item = TV_LIST_ITEM_NEXT(l, item);
arg = skipwhite(arg);
if (*arg == ';') {
/* Put the rest of the list (may be empty) in the var after ';'.
* Create a new list for this. */
list_T *const rest_list = tv_list_alloc();
list_T *const rest_list = tv_list_alloc(rest_len);
while (item != NULL) {
tv_list_append_tv(rest_list, TV_LIST_ITEM_TV(item));
item = TV_LIST_ITEM_NEXT(l, item);
@ -4512,7 +4514,7 @@ eval_index (
if (!empty2 && (n2 < 0 || n2 + 1 < n1)) {
n2 = -1;
}
l = tv_list_alloc();
l = tv_list_alloc(n2 - n1 + 1);
item = tv_list_find(rettv->vval.v_list, n1);
while (n1++ <= n2) {
tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
@ -4870,7 +4872,7 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
list_T *l = NULL;
if (evaluate) {
l = tv_list_alloc();
l = tv_list_alloc(kListLenShouldKnow);
}
*arg = skipwhite(*arg + 1);
@ -6666,7 +6668,7 @@ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
rettv->v_type = VAR_STRING;
} else {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, ARGCOUNT);
for (idx = 0; idx < ARGCOUNT; idx++) {
tv_list_append_string(rettv->vval.v_list,
(const char *)alist_name(&ARGLIST[idx]), -1);
@ -6776,7 +6778,7 @@ static void assert_error(garray_T *gap)
if (vp->vv_type != VAR_LIST || vimvars[VV_ERRORS].vv_list == NULL) {
// Make sure v:errors is a list.
set_vim_var_list(VV_ERRORS, tv_list_alloc());
set_vim_var_list(VV_ERRORS, tv_list_alloc(1));
}
tv_list_append_string(vimvars[VV_ERRORS].vv_list,
(const char *)gap->ga_data, (ptrdiff_t)gap->ga_len);
@ -8225,7 +8227,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
result = eval_vars((char_u *)s, (char_u *)s, &len, NULL, &errormsg, NULL);
emsg_off--;
if (rettv->v_type == VAR_LIST) {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, (result != NULL));
if (result != NULL) {
tv_list_append_string(rettv->vval.v_list, (const char *)result, -1);
}
@ -8248,8 +8250,8 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ExpandOne(&xpc, (char_u *)s, NULL, options,
WILD_ALL);
} else {
tv_list_alloc_ret(rettv);
ExpandOne(&xpc, (char_u *)s, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list,
(const char *)xpc.xp_files[i], -1);
@ -8266,7 +8268,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "menu_get(path [, modes])" function
static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
int modes = MENU_ALL_MODES;
if (argvars[1].v_type == VAR_STRING) {
const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]);
@ -8427,7 +8429,7 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
}
if (count < 0) {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenUnknown);
}
if (*fname != NUL && !error) {
@ -9108,7 +9110,7 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (strcmp(what, "args") == 0) {
rettv->v_type = VAR_LIST;
if (tv_list_alloc_ret(rettv) != NULL) {
if (tv_list_alloc_ret(rettv, pt->pt_argc) != NULL) {
for (int i = 0; i < pt->pt_argc; i++) {
tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]);
}
@ -9132,8 +9134,10 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
/// Returns information about signs placed in a buffer as list of dicts.
static void get_buffer_signs(buf_T *buf, list_T *l)
static list_T *get_buffer_signs(buf_T *buf)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
list_T *const l = tv_list_alloc(kListLenMayKnow);
for (signlist_T *sign = buf->b_signlist; sign; sign = sign->next) {
dict_T *const d = tv_dict_alloc();
@ -9144,6 +9148,7 @@ static void get_buffer_signs(buf_T *buf, list_T *l)
tv_list_append_dict(l, d);
}
return l;
}
/// Returns buffer options, variables and other attributes in a dictionary.
@ -9167,7 +9172,7 @@ static dict_T *get_buffer_info(buf_T *buf)
tv_dict_add_dict(dict, S_LEN("variables"), buf->b_vars);
// List of windows displaying this buffer
list_T *const windows = tv_list_alloc();
list_T *const windows = tv_list_alloc(kListLenMayKnow);
FOR_ALL_TAB_WINDOWS(tp, wp) {
if (wp->w_buffer == buf) {
tv_list_append_number(windows, (varnumber_T)wp->handle);
@ -9177,9 +9182,7 @@ static dict_T *get_buffer_info(buf_T *buf)
if (buf->b_signlist != NULL) {
// List of signs placed in this buffer
list_T *const signs = tv_list_alloc();
get_buffer_signs(buf, signs);
tv_dict_add_list(dict, S_LEN("signs"), signs);
tv_dict_add_list(dict, S_LEN("signs"), get_buffer_signs(buf));
}
return dict;
@ -9193,7 +9196,7 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool sel_buflisted = false;
bool sel_bufloaded = false;
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
// List of all the buffers or selected buffers
if (argvars[0].v_type == VAR_DICT) {
@ -9252,35 +9255,31 @@ static void f_getbufinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void get_buffer_lines(buf_T *buf, linenr_T start, linenr_T end, int retlist, typval_T *rettv)
{
char_u *p;
rettv->v_type = VAR_STRING;
rettv->v_type = (retlist ? VAR_LIST : VAR_STRING);
rettv->vval.v_string = NULL;
if (retlist) {
tv_list_alloc_ret(rettv);
if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0 || end < start) {
tv_list_alloc_ret(rettv, 0);
return;
}
if (buf == NULL || buf->b_ml.ml_mfp == NULL || start < 0)
return;
if (!retlist) {
if (start >= 1 && start <= buf->b_ml.ml_line_count)
p = ml_get_buf(buf, start, FALSE);
else
p = (char_u *)"";
rettv->vval.v_string = vim_strsave(p);
} else {
if (end < start)
return;
if (start < 1)
if (retlist) {
if (start < 1) {
start = 1;
if (end > buf->b_ml.ml_line_count)
}
if (end > buf->b_ml.ml_line_count) {
end = buf->b_ml.ml_line_count;
}
tv_list_alloc_ret(rettv, end - start + 1);
while (start <= end) {
tv_list_append_string(rettv->vval.v_list,
(const char *)ml_get_buf(buf, start++, false), -1);
}
} else {
rettv->v_type = VAR_STRING;
rettv->vval.v_string = ((start >= 1 && start <= buf->b_ml.ml_line_count)
? vim_strsave(ml_get_buf(buf, start, false))
: NULL);
}
}
@ -9605,8 +9604,8 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr)
theend:
pat = addstar(xpc.xp_pattern, xpc.xp_pattern_len, xpc.xp_context);
tv_list_alloc_ret(rettv);
ExpandOne(&xpc, pat, NULL, options, WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
@ -9900,7 +9899,7 @@ static void f_getline(typval_T *argvars, typval_T *rettv, FunPtr fptr)
const linenr_T lnum = tv_get_lnum(argvars);
if (argvars[1].v_type == VAR_UNKNOWN) {
end = 0;
end = lnum;
retlist = false;
} else {
end = tv_get_lnum(&argvars[1]);
@ -9914,7 +9913,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg,
typval_T *rettv)
{
if (what_arg->v_type == VAR_UNKNOWN) {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (is_qf || wp != NULL) {
(void)get_errorlist(wp, -1, rettv->vval.v_list);
}
@ -9949,7 +9948,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
matchitem_T *cur = curwin->w_match_head;
int i;
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
while (cur != NULL) {
dict_T *dict = tv_dict_alloc();
if (cur->match.regprog == NULL) {
@ -9962,7 +9961,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (llpos->lnum == 0) {
break;
}
list_T *l = tv_list_alloc();
list_T *const l = tv_list_alloc(1 + (llpos->col > 0 ? 2 : 0));
tv_list_append_number(l, (varnumber_T)llpos->lnum);
if (llpos->col > 0) {
tv_list_append_number(l, (varnumber_T)llpos->col);
@ -10011,7 +10010,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos)
fp = var2fpos(&argvars[0], true, &fnum);
}
list_T *l = tv_list_alloc_ret(rettv);
list_T *const l = tv_list_alloc_ret(rettv, 4 + (!!getcurpos));
tv_list_append_number(l, (fnum != -1) ? (varnumber_T)fnum : (varnumber_T)0);
tv_list_append_number(l, ((fp != NULL)
? (varnumber_T)fp->lnum
@ -10084,10 +10083,10 @@ static void f_getreg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (return_list) {
rettv->v_type = VAR_LIST;
rettv->vval.v_list =
rettv->vval.v_list =
get_reg_contents(regname, (arg2 ? kGRegExprSrc : 0) | kGRegList);
if (rettv->vval.v_list == NULL) {
rettv->vval.v_list = tv_list_alloc();
rettv->vval.v_list = tv_list_alloc(0);
}
tv_list_ref(rettv->vval.v_list);
} else {
@ -10137,7 +10136,7 @@ static dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx)
tv_dict_add_nr(dict, S_LEN("tabnr"), tp_idx);
list_T *const l = tv_list_alloc();
list_T *const l = tv_list_alloc(kListLenMayKnow);
FOR_ALL_WINDOWS_IN_TAB(wp, tp) {
tv_list_append_number(l, (varnumber_T)wp->handle);
}
@ -10154,7 +10153,9 @@ static void f_gettabinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tabpage_T *tparg = NULL;
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, (argvars[0].v_type == VAR_UNKNOWN
? 1
: kListLenMayKnow));
if (argvars[0].v_type != VAR_UNKNOWN) {
// Information about one tab page
@ -10253,7 +10254,7 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
win_T *wparg = NULL;
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (argvars[0].v_type != VAR_UNKNOWN) {
wparg = win_id2wp(argvars);
@ -10478,9 +10479,9 @@ static void f_glob(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_string = ExpandOne(
&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options, WILD_ALL);
} else {
tv_list_alloc_ret(rettv);
ExpandOne(&xpc, (char_u *)tv_get_string(&argvars[0]), NULL, options,
WILD_ALL_KEEP);
tv_list_alloc_ret(rettv, xpc.xp_numfiles);
for (int i = 0; i < xpc.xp_numfiles; i++) {
tv_list_append_string(rettv->vval.v_list, (const char *)xpc.xp_files[i],
-1);
@ -10529,7 +10530,7 @@ static void f_globpath(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (rettv->v_type == VAR_STRING) {
rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n");
} else {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
tv_list_append_string(rettv->vval.v_list,
((const char **)(ga.ga_data))[i], -1);
@ -11429,7 +11430,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
return;
}
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict));
TV_DICT_ITER(tv->vval.v_dict, di, {
typval_T tv = { .v_lock = VAR_UNLOCKED };
@ -11446,7 +11447,7 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
}
case kDictListItems: {
// items()
list_T *const sub_l = tv_list_alloc();
list_T *const sub_l = tv_list_alloc(2);
tv.v_type = VAR_LIST;
tv.vval.v_list = sub_l;
tv_list_ref(sub_l);
@ -11776,7 +11777,7 @@ static void f_jobwait(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
list_T *rv = tv_list_alloc();
list_T *const rv = tv_list_alloc(tv_list_len(args));
// restore the parent queue for any jobs still alive
for (i = 0; i < tv_list_len(args); i++) {
@ -12204,12 +12205,12 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv,
switch (type) {
// matchlist(): return empty list when there are no matches.
case kSomeMatchList: {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
break;
}
// matchstrpos(): return ["", -1, -1, -1]
case kSomeMatchStrPos: {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, 4);
tv_list_append_string(rettv->vval.v_list, "", 0);
tv_list_append_number(rettv->vval.v_list, -1);
tv_list_append_number(rettv->vval.v_list, -1);
@ -12516,10 +12517,12 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/
static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv);
const int id = tv_get_number(&argvars[0]);
tv_list_alloc_ret(rettv, (id >= 1 && id <= 3
? 2
: 0));
if (id >= 1 && id <= 3) {
matchitem_T *const m = (matchitem_T *)get_match(curwin, id);
@ -12705,8 +12708,8 @@ static void f_msgpackdump(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackdump()");
return;
}
list_T *ret_list = tv_list_alloc_ret(rettv);
list_T *list = argvars[0].vval.v_list;
list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
list_T *const list = argvars[0].vval.v_list;
msgpack_packer *lpacker = msgpack_packer_new(ret_list, &encode_list_write);
const char *const msg = _("msgpackdump() argument, index %i");
// Assume that translation will not take more then 4 times more space
@ -12730,8 +12733,8 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
EMSG2(_(e_listarg), "msgpackparse()");
return;
}
list_T *ret_list = tv_list_alloc_ret(rettv);
const list_T *list = argvars[0].vval.v_list;
list_T *const ret_list = tv_list_alloc_ret(rettv, kListLenMayKnow);
const list_T *const list = argvars[0].vval.v_list;
if (tv_list_len(list) == 0) {
return;
}
@ -12986,7 +12989,7 @@ static void f_range(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (stride > 0 ? end + 1 < start : end - 1 > start) {
emsgf(_("E727: Start past end"));
} else {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, (end - start) / stride);
for (i = start; stride > 0 ? i <= end : i >= end; i += stride) {
tv_list_append_number(rettv->vval.v_list, (varnumber_T)i);
}
@ -13017,8 +13020,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
tv_list_alloc_ret(rettv);
list_T *const l = rettv->vval.v_list;
list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown);
// Always open the file in binary mode, library functions have a mind of
// their own about CR-LF conversion.
@ -13231,7 +13233,7 @@ static void f_reltime(typval_T *argvars, typval_T *rettv, FunPtr fptr)
STATIC_ASSERT(sizeof(u.prof) == sizeof(u) && sizeof(u.split) == sizeof(u),
"type punning will produce incorrect results on this platform");
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, 2);
tv_list_append_number(rettv->vval.v_list, u.split.high);
tv_list_append_number(rettv->vval.v_list, u.split.low);
}
@ -13324,7 +13326,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (li == NULL) { // Didn't find "item2" after "item".
emsgf(_(e_invrange));
} else {
tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv), cnt);
tv_list_move_items(l, item, item2, tv_list_alloc_ret(rettv, cnt),
cnt);
}
}
}
@ -13354,7 +13357,7 @@ static void f_repeat(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
varnumber_T n = tv_get_number(&argvars[1]);
if (argvars[0].v_type == VAR_LIST) {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, (n > 0) * n * tv_list_len(argvars[0].vval.v_list));
while (n-- > 0) {
tv_list_extend(rettv->vval.v_list, argvars[0].vval.v_list, NULL);
}
@ -14124,7 +14127,7 @@ static void f_searchpairpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
int lnum = 0;
int col = 0;
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, 2);
if (searchpair_cmn(argvars, &match_pos) > 0) {
lnum = match_pos.lnum;
@ -14292,18 +14295,14 @@ do_searchpair (
static void f_searchpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
pos_T match_pos;
int lnum = 0;
int col = 0;
int n;
int flags = 0;
tv_list_alloc_ret(rettv);
const int n = search_cmn(argvars, &match_pos, &flags);
n = search_cmn(argvars, &match_pos, &flags);
if (n > 0) {
lnum = match_pos.lnum;
col = match_pos.col;
}
tv_list_alloc_ret(rettv, 2 + (!!(flags & SP_SUBPAT)));
const int lnum = (n > 0 ? match_pos.lnum : 0);
const int col = (n > 0 ? match_pos.col : 0);
tv_list_append_number(rettv->vval.v_list, (varnumber_T)lnum);
tv_list_append_number(rettv->vval.v_list, (varnumber_T)col);
@ -14319,7 +14318,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char **addrs = server_address_list(&n);
// Copy addrs into a linked list.
list_T *l = tv_list_alloc_ret(rettv);
list_T *const l = tv_list_alloc_ret(rettv, n);
for (size_t i = 0; i < n; i++) {
tv_list_append_allocated_string(l, addrs[i]);
}
@ -14715,7 +14714,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dictitem_T *const di = tv_dict_find(d, S_LEN("pattern"));
if (di == NULL) {
if (s == NULL) {
s = tv_list_alloc();
s = tv_list_alloc(9);
}
// match from matchaddpos()
@ -15531,8 +15530,6 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
hlf_T attr = HLF_COUNT;
size_t len = 0;
tv_list_alloc_ret(rettv);
if (argvars[0].v_type == VAR_UNKNOWN) {
// Find the start and length of the badly spelled word.
len = spell_move_to(curwin, FORWARD, true, true, &attr);
@ -15557,6 +15554,7 @@ static void f_spellbadword(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
assert(len <= INT_MAX);
tv_list_alloc_ret(rettv, 2);
tv_list_append_string(rettv->vval.v_list, word, len);
tv_list_append_string(rettv->vval.v_list,
(attr == HLF_SPB ? "bad"
@ -15573,35 +15571,36 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
bool typeerr = false;
int maxcount;
garray_T ga;
garray_T ga = GA_EMPTY_INIT_VALUE;
bool need_capital = false;
tv_list_alloc_ret(rettv);
if (curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) {
const char *const str = tv_get_string(&argvars[0]);
if (argvars[1].v_type != VAR_UNKNOWN) {
maxcount = tv_get_number_chk(&argvars[1], &typeerr);
if (maxcount <= 0) {
return;
goto f_spellsuggest_return;
}
if (argvars[2].v_type != VAR_UNKNOWN) {
need_capital = tv_get_number_chk(&argvars[2], &typeerr);
if (typeerr) {
return;
goto f_spellsuggest_return;
}
}
} else
} else {
maxcount = 25;
}
spell_suggest_list(&ga, (char_u *)str, maxcount, need_capital, false);
for (int i = 0; i < ga.ga_len; i++) {
char *p = ((char **)ga.ga_data)[i];
tv_list_append_allocated_string(rettv->vval.v_list, p);
}
ga_clear(&ga);
}
f_spellsuggest_return:
tv_list_alloc_ret(rettv, (ptrdiff_t)ga.ga_len);
for (int i = 0; i < ga.ga_len; i++) {
char *const p = ((char **)ga.ga_data)[i];
tv_list_append_allocated_string(rettv->vval.v_list, p);
}
ga_clear(&ga);
}
static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
@ -15633,10 +15632,11 @@ static void f_split(typval_T *argvars, typval_T *rettv, FunPtr fptr)
pat = "[\\x01- ]\\+";
}
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
if (typeerr)
if (typeerr) {
return;
}
regmatch.regprog = vim_regcomp((char_u *)pat, RE_MAGIC + RE_STRING);
if (regmatch.regprog != NULL) {
@ -16263,7 +16263,6 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
memset(str, NUL, sizeof(str));
tv_list_alloc_ret(rettv);
if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum)) && curwin->w_p_cole > 0) {
(void)syn_get_id(curwin, lnum, col, false, NULL, false);
@ -16276,14 +16275,12 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, FunPtr fptr)
cchar = lcs_conceal;
}
if (cchar != NUL) {
if (has_mbyte)
(*mb_char2bytes)(cchar, str);
else
str[0] = cchar;
utf_char2bytes(cchar, str);
}
}
}
tv_list_alloc_ret(rettv, 3);
tv_list_append_number(rettv->vval.v_list, (syntax_flags & HL_CONCEAL) != 0);
// -1 to auto-determine strlen
tv_list_append_string(rettv->vval.v_list, (const char *)str, -1);
@ -16306,7 +16303,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, FunPtr fptr)
&& lnum <= curbuf->b_ml.ml_line_count
&& col >= 0
&& (size_t)col <= STRLEN(ml_get(lnum))) {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
(void)syn_get_id(curwin, lnum, col, false, NULL, true);
int id;
@ -16322,7 +16319,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty)
if (!keepempty && str[len - 1] == NL) {
len--;
}
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(kListLenMayKnow);
encode_list_write(list, str, len);
return list;
}
@ -16368,7 +16365,7 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv,
if (res == NULL) {
if (retlist) {
// return an empty list when there's no output
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, 0);
} else {
rettv->vval.v_string = (char_u *) xstrdup("");
}
@ -16434,7 +16431,7 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
}
if (wp != NULL) {
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
while (wp != NULL) {
tv_list_append_number(rettv->vval.v_list, wp->w_buffer->b_fnum);
wp = wp->w_next;
@ -16532,7 +16529,7 @@ static void f_tagfiles(typval_T *argvars, typval_T *rettv, FunPtr fptr)
char *fname;
tagname_T tn;
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenUnknown);
fname = xmalloc(MAXPATHL);
bool first = true;
@ -16561,8 +16558,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[1].v_type != VAR_UNKNOWN) {
fname = tv_get_string(&argvars[1]);
}
(void)get_tags(tv_list_alloc_ret(rettv), (char_u *)tag_pattern,
(char_u *)fname);
(void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown),
(char_u *)tag_pattern, (char_u *)fname);
}
/*
@ -16668,6 +16665,18 @@ static void f_test_garbagecollect_now(typval_T *argvars,
garbage_collect(true);
}
// "test_write_list_log()" function
static void f_test_write_list_log(typval_T *const argvars,
typval_T *const rettv,
FunPtr fptr)
{
const char *const fname = tv_get_string_chk(&argvars[0]);
if (fname == NULL) {
return;
}
list_write_log(fname);
}
bool callback_from_typval(Callback *const callback, typval_T *const arg)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
@ -16803,7 +16812,9 @@ static void add_timer_info_all(typval_T *rettv)
/// "timer_info([timer])" function
static void f_timer_info(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, (argvars[0].v_type != VAR_UNKNOWN
? 1
: timers->table->n_occupied));
if (argvars[0].v_type != VAR_UNKNOWN) {
if (argvars[0].v_type != VAR_NUMBER) {
EMSG(_(e_number_exp));
@ -17163,7 +17174,6 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_alloc_ret(rettv);
dict_T *dict = rettv->vval.v_dict;
list_T *list;
tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)curbuf->b_u_synced);
tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)curbuf->b_u_seq_last);
@ -17173,9 +17183,7 @@ static void f_undotree(typval_T *argvars, typval_T *rettv, FunPtr fptr)
tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)curbuf->b_u_time_cur);
tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)curbuf->b_u_save_nr_cur);
list = tv_list_alloc();
u_eval_tree(curbuf->b_u_oldhead, list);
tv_dict_add_list(dict, S_LEN("entries"), list);
tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(curbuf->b_u_oldhead));
}
/*
@ -17234,7 +17242,7 @@ static void f_wildmenumode(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_findbuf()" function
static void f_win_findbuf(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv);
tv_list_alloc_ret(rettv, kListLenMayKnow);
win_findbuf(argvars, rettv->vval.v_list);
}
@ -17253,8 +17261,7 @@ static void f_win_gotoid(typval_T *argvars, typval_T *rettv, FunPtr fptr)
/// "win_id2tabwin()" function
static void f_win_id2tabwin(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
tv_list_alloc_ret(rettv);
win_id2tabwin(argvars, rettv->vval.v_list);
win_id2tabwin(argvars, rettv);
}
/// "win_id2win()" function
@ -22367,7 +22374,7 @@ static void script_host_eval(char *name, typval_T *argvars, typval_T *rettv)
return;
}
list_T *args = tv_list_alloc();
list_T *args = tv_list_alloc(1);
tv_list_append_string(args, (const char *)argvars[0].vval.v_string, -1);
*rettv = eval_call_provider(name, "eval", args);
}

View File

@ -313,6 +313,7 @@ return {
tempname={},
termopen={args={1, 2}},
test_garbagecollect_now={},
test_write_list_log={args=1},
timer_info={args={0,1}},
timer_pause={args=2},
timer_start={args={2,3}},

View File

@ -150,7 +150,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
}
obj_di->di_tv = obj.val;
} else {
list_T *const kv_pair = tv_list_alloc();
list_T *const kv_pair = tv_list_alloc(2);
tv_list_append_list(last_container.special_val, kv_pair);
tv_list_append_owned_tv(kv_pair, key.val);
tv_list_append_owned_tv(kv_pair, obj.val);
@ -221,13 +221,18 @@ static inline int json_decoder_pop(ValuesStackItem obj,
/// Create a new special dictionary that ought to represent a MAP
///
/// @param[out] ret_tv Address where new special dictionary is saved.
/// @param[in] len Expected number of items to be populated before list
/// becomes accessible from VimL. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. @see ListLenSpecials.
///
/// @return [allocated] list which should contain key-value pairs. Return value
/// may be safely ignored.
list_T *decode_create_map_special_dict(typval_T *const ret_tv)
list_T *decode_create_map_special_dict(typval_T *const ret_tv,
const ptrdiff_t len)
FUNC_ATTR_NONNULL_ALL
{
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(len);
tv_list_ref(list);
create_special_dict(ret_tv, kMPMap, ((typval_T) {
.v_type = VAR_LIST,
@ -263,7 +268,7 @@ typval_T decode_string(const char *const s, const size_t len,
? ((s != NULL) && (memchr(s, NUL, len) != NULL))
: (bool)hasnul);
if (really_hasnul) {
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(kListLenMayKnow);
tv_list_ref(list);
typval_T tv;
create_special_dict(&tv, binary ? kMPBinary : kMPString, ((typval_T) {
@ -843,7 +848,7 @@ json_decode_string_cycle_start:
break;
}
case '[': {
list_T *list = tv_list_alloc();
list_T *list = tv_list_alloc(kListLenMayKnow);
tv_list_ref(list);
typval_T tv = {
.v_type = VAR_LIST,
@ -864,7 +869,7 @@ json_decode_string_cycle_start:
list_T *val_list = NULL;
if (next_map_special) {
next_map_special = false;
val_list = decode_create_map_special_dict(&tv);
val_list = decode_create_map_special_dict(&tv, kListLenMayKnow);
} else {
dict_T *dict = tv_dict_alloc();
dict->dv_refcount++;
@ -964,7 +969,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_number = (varnumber_T) mobj.via.u64 },
};
} else {
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(4);
tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
@ -987,7 +992,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_number = (varnumber_T) mobj.via.i64 },
};
} else {
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(4);
tv_list_ref(list);
create_special_dict(rettv, kMPInteger, ((typval_T) {
.v_type = VAR_LIST,
@ -1033,7 +1038,7 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
break;
}
case MSGPACK_OBJECT_ARRAY: {
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc((ptrdiff_t)mobj.via.array.size);
tv_list_ref(list);
*rettv = (typval_T) {
.v_type = VAR_LIST,
@ -1085,9 +1090,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
break;
msgpack_to_vim_generic_map: {}
list_T *const list = decode_create_map_special_dict(rettv);
list_T *const list = decode_create_map_special_dict(
rettv, (ptrdiff_t)mobj.via.map.size);
for (size_t i = 0; i < mobj.via.map.size; i++) {
list_T *const kv_pair = tv_list_alloc();
list_T *const kv_pair = tv_list_alloc(2);
tv_list_append_list(list, kv_pair);
typval_T key_tv = { .v_type = VAR_UNKNOWN };
@ -1107,10 +1113,10 @@ msgpack_to_vim_generic_map: {}
break;
}
case MSGPACK_OBJECT_EXT: {
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(2);
tv_list_ref(list);
tv_list_append_number(list, mobj.via.ext.type);
list_T *const ext_val_list = tv_list_alloc();
list_T *const ext_val_list = tv_list_alloc(kListLenMayKnow);
tv_list_append_list(list, ext_val_list);
create_special_dict(rettv, kMPExt, ((typval_T) {
.v_type = VAR_LIST,

View File

@ -31,6 +31,7 @@
#include "nvim/message.h"
// TODO(ZyX-I): Move line_breakcheck out of misc1
#include "nvim/misc1.h" // For line_breakcheck
#include "nvim/os/fileio.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "eval/typval.c.generated.h"
@ -45,6 +46,70 @@ bool tv_in_free_unref_items = false;
const char *const tv_empty_string = "";
//{{{1 Lists
//{{{2 List log
#ifdef LOG_LIST_ACTIONS
ListLog *list_log_first = NULL;
ListLog *list_log_last = NULL;
/// Write list log to the given file
///
/// @param[in] fname File to write log to. Will be appended to if already
/// present.
void list_write_log(const char *const fname)
FUNC_ATTR_NONNULL_ALL
{
FileDescriptor fp;
const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600);
if (fo_ret != 0) {
emsgf(_("E5142: Failed to open file %s: %s"), fname, os_strerror(fo_ret));
return;
}
for (ListLog *chunk = list_log_first; chunk != NULL;) {
for (size_t i = 0; i < chunk->size; i++) {
char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2];
// act : hex " c:" len "[]" "\n\0"
const ListLogEntry entry = chunk->entries[i];
const size_t snp_len = (size_t)snprintf(
buf, sizeof(buf),
"%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR
"\n",
entry.action, entry.l, entry.len, entry.li1, entry.li2);
assert(snp_len + 1 == sizeof(buf));
const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len);
if (fw_ret != (ptrdiff_t)snp_len) {
assert(fw_ret < 0);
if (i) {
memmove(chunk->entries, chunk->entries + i,
sizeof(chunk->entries[0]) * (chunk->size - i));
chunk->size -= i;
}
emsgf(_("E5143: Failed to write to file %s: %s"),
fname, os_strerror((int)fw_ret));
return;
}
}
list_log_first = chunk->next;
xfree(chunk);
chunk = list_log_first;
}
const int fc_ret = file_close(&fp, true);
if (fc_ret != 0) {
emsgf(_("E5144: Failed to close file %s: %s"), fname, os_strerror(fc_ret));
}
}
#ifdef EXITFREE
/// Free list log
void list_free_log(void)
{
for (ListLog *chunk = list_log_first; chunk != NULL;) {
list_log_first = chunk->next;
xfree(chunk);
chunk = list_log_first;
}
}
#endif
#endif
//{{{2 List item
/// Allocate a list item
@ -132,8 +197,14 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item)
///
/// Caller should take care of the reference count.
///
/// @param[in] len Expected number of items to be populated before list
/// becomes accessible from VimL. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. Currently does nothing.
/// @see ListLenSpecials.
///
/// @return [allocated] new list.
list_T *tv_list_alloc(void)
list_T *tv_list_alloc(const ptrdiff_t len)
FUNC_ATTR_NONNULL_RET
{
list_T *const list = xcalloc(1, sizeof(list_T));
@ -145,6 +216,7 @@ list_T *tv_list_alloc(void)
list->lv_used_prev = NULL;
list->lv_used_next = gc_first_list;
gc_first_list = list;
list_log(list, NULL, (void *)(uintptr_t)len, "alloc");
return list;
}
@ -174,6 +246,8 @@ void tv_list_init_static10(staticList10_T *const sl)
li->li_prev = li - 1;
li->li_next = li + 1;
}
list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1],
"s10init");
#undef SL_SIZE
}
@ -185,6 +259,7 @@ void tv_list_init_static(list_T *const l)
{
memset(l, 0, sizeof(*l));
l->lv_refcount = DO_NOT_FREE_CNT;
list_log(l, NULL, NULL, "sinit");
}
/// Free items contained in a list
@ -193,6 +268,7 @@ void tv_list_init_static(list_T *const l)
void tv_list_free_contents(list_T *const l)
FUNC_ATTR_NONNULL_ALL
{
list_log(l, NULL, NULL, "freecont");
for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) {
// Remove the item before deleting it.
l->lv_first = item->li_next;
@ -222,6 +298,7 @@ void tv_list_free_list(list_T *const l)
if (l->lv_used_next != NULL) {
l->lv_used_next->lv_used_prev = l->lv_used_prev;
}
list_log(l, NULL, NULL, "freelist");
xfree(l);
}
@ -266,6 +343,7 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item,
listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
list_log(l, item, item2, "drop");
// Notify watchers.
for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) {
l->lv_len--;
@ -283,6 +361,7 @@ void tv_list_drop_items(list_T *const l, listitem_T *const item,
item->li_prev->li_next = item2->li_next;
}
l->lv_idx_item = NULL;
list_log(l, l->lv_first, l->lv_last, "afterdrop");
}
/// Like tv_list_drop_items, but also frees all removed items
@ -290,6 +369,7 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item,
listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
list_log(l, item, item2, "remove");
tv_list_drop_items(l, item, item2);
for (listitem_T *li = item;;) {
tv_clear(TV_LIST_ITEM_TV(li));
@ -314,6 +394,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item,
const int cnt)
FUNC_ATTR_NONNULL_ALL
{
list_log(l, item, item2, "move");
tv_list_drop_items(l, item, item2);
item->li_prev = tgt_l->lv_last;
item2->li_next = NULL;
@ -324,6 +405,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item,
}
tgt_l->lv_last = item2;
tgt_l->lv_len += cnt;
list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt");
}
/// Insert list item
@ -352,6 +434,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni,
}
item->li_prev = ni;
l->lv_len++;
list_log(l, ni, item, "insert");
}
}
@ -378,6 +461,7 @@ void tv_list_insert_tv(list_T *const l, typval_T *const tv,
void tv_list_append(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
list_log(l, item, NULL, "append");
if (l->lv_last == NULL) {
// empty list
l->lv_first = item;
@ -521,7 +605,7 @@ list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig,
return NULL;
}
list_T *copy = tv_list_alloc();
list_T *copy = tv_list_alloc(tv_list_len(orig));
tv_list_ref(copy);
if (copyID != 0) {
// Do this before adding the items, because one of the items may
@ -741,6 +825,7 @@ void tv_list_reverse(list_T *const l)
if (tv_list_len(l) <= 1) {
return;
}
list_log(l, NULL, NULL, "reverse");
#define SWAP(a, b) \
do { \
tmp = a; \
@ -779,6 +864,7 @@ void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
if (len <= 1) {
return;
}
list_log(l, NULL, NULL, "sort");
int i = 0;
TV_LIST_ITER(l, li, {
ptrs[i].item = li;
@ -867,6 +953,7 @@ listitem_T *tv_list_find(list_T *const l, int n)
// Cache the used index.
l->lv_idx = idx;
l->lv_idx_item = item;
list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find");
return item;
}
@ -1817,12 +1904,16 @@ void tv_dict_set_keys_readonly(dict_T *const dict)
/// Also sets reference count.
///
/// @param[out] ret_tv Structure where list is saved.
/// @param[in] len Expected number of items to be populated before list
/// becomes accessible from VimL. It is still valid to
/// underpopulate a list, value only controls how many elements
/// will be allocated in advance. @see ListLenSpecials.
///
/// @return [allocated] pointer to the created list.
list_T *tv_list_alloc_ret(typval_T *const ret_tv)
list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len)
FUNC_ATTR_NONNULL_ALL
{
list_T *const l = tv_list_alloc();
list_T *const l = tv_list_alloc(len);
ret_tv->vval.v_list = l;
ret_tv->v_type = VAR_LIST;
ret_tv->v_lock = VAR_UNLOCKED;

View File

@ -20,6 +20,9 @@
#include "nvim/gettext.h"
#include "nvim/message.h"
#include "nvim/macros.h"
#ifdef LOG_LIST_ACTIONS
# include "nvim/memory.h"
#endif
/// Type used for VimL VAR_NUMBER values
typedef int64_t varnumber_T;
@ -31,6 +34,25 @@ typedef double float_T;
/// Refcount for dict or list that should not be freed
enum { DO_NOT_FREE_CNT = (INT_MAX / 2) };
/// Additional values for tv_list_alloc() len argument
enum {
/// List length is not known in advance
///
/// To be used when there is neither a way to know how many elements will be
/// needed nor are any educated guesses.
kListLenUnknown = -1,
/// List length *should* be known, but is actually not
///
/// All occurrences of this value should be eventually removed. This is for
/// the case when the only reason why list length is not known is that it
/// would be hard to code without refactoring, but refactoring is needed.
kListLenShouldKnow = -2,
/// List length may be known in advance, but it requires too much effort
///
/// To be used when it looks impractical to determine list length.
kListLenMayKnow = -3,
} ListLenSpecials;
/// Maximal possible value of varnumber_T variable
#define VARNUMBER_MAX INT64_MAX
#define UVARNUMBER_MAX UINT64_MAX
@ -304,6 +326,96 @@ typedef struct {
typedef int (*ListSorter)(const void *, const void *);
#ifdef LOG_LIST_ACTIONS
/// List actions log entry
typedef struct {
uintptr_t l; ///< List log entry belongs to.
uintptr_t li1; ///< First list item log entry belongs to, if applicable.
uintptr_t li2; ///< Second list item log entry belongs to, if applicable.
int len; ///< List length when log entry was created.
const char *action; ///< Logged action.
} ListLogEntry;
typedef struct list_log ListLog;
/// List actions log
struct list_log {
ListLog *next; ///< Next chunk or NULL.
size_t capacity; ///< Number of entries in current chunk.
size_t size; ///< Current chunk size.
ListLogEntry entries[]; ///< Actual log entries.
};
extern ListLog *list_log_first; ///< First list log chunk, NULL if missing
extern ListLog *list_log_last; ///< Last list log chunk
static inline ListLog *list_log_alloc(const size_t size)
REAL_FATTR_ALWAYS_INLINE REAL_FATTR_WARN_UNUSED_RESULT;
/// Allocate a new log chunk and update globals
///
/// @param[in] size Number of entries in a new chunk.
///
/// @return [allocated] Newly allocated chunk.
static inline ListLog *list_log_new(const size_t size)
{
ListLog *ret = xmalloc(offsetof(ListLog, entries)
+ size * sizeof(ret->entries[0]));
ret->size = 0;
ret->capacity = size;
ret->next = NULL;
if (list_log_first == NULL) {
list_log_first = ret;
} else {
list_log_last->next = ret;
}
list_log_last = ret;
return ret;
}
static inline void list_log(const list_T *const l,
const listitem_T *const li1,
const listitem_T *const li2,
const char *const action)
REAL_FATTR_ALWAYS_INLINE;
/// Add new entry to log
///
/// If last chunk was filled it uses twice as much memory to allocate the next
/// chunk.
///
/// @param[in] l List to which entry belongs.
/// @param[in] li1 List item 1.
/// @param[in] li2 List item 2, often used for integers and not list items.
/// @param[in] action Logged action.
static inline void list_log(const list_T *const l,
const listitem_T *const li1,
const listitem_T *const li2,
const char *const action)
{
ListLog *tgt;
if (list_log_first == NULL) {
tgt = list_log_new(128);
} else if (list_log_last->size == list_log_last->capacity) {
tgt = list_log_new(list_log_last->capacity * 2);
} else {
tgt = list_log_last;
}
tgt->entries[tgt->size++] = (ListLogEntry) {
.l = (uintptr_t)l,
.li1 = (uintptr_t)li1,
.li2 = (uintptr_t)li2,
.len = (l == NULL ? 0 : l->lv_len),
.action = action,
};
}
#else
# define list_log(...)
# define list_write_log(...)
# define list_free_log()
#endif
// In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item.
@ -377,6 +489,7 @@ static inline int tv_list_len(const list_T *const l)
/// @param[in] l List to check.
static inline int tv_list_len(const list_T *const l)
{
list_log(l, NULL, NULL, "len");
if (l == NULL) {
return 0;
}
@ -460,8 +573,10 @@ static inline listitem_T *tv_list_first(const list_T *const l)
static inline listitem_T *tv_list_first(const list_T *const l)
{
if (l == NULL) {
list_log(l, NULL, NULL, "first");
return NULL;
}
list_log(l, l->lv_first, NULL, "first");
return l->lv_first;
}
@ -476,8 +591,10 @@ static inline listitem_T *tv_list_last(const list_T *const l)
static inline listitem_T *tv_list_last(const list_T *const l)
{
if (l == NULL) {
list_log(l, NULL, NULL, "last");
return NULL;
}
list_log(l, l->lv_last, NULL, "last");
return l->lv_last;
}
@ -545,6 +662,7 @@ extern bool tv_in_free_unref_items;
#define _TV_LIST_ITER_MOD(modifier, l, li, code) \
do { \
modifier list_T *const l_ = (l); \
list_log(l_, NULL, NULL, "iter" #modifier); \
if (l_ != NULL) { \
for (modifier listitem_T *li = l_->lv_first; \
li != NULL; li = li->li_next) { \

View File

@ -3758,7 +3758,7 @@ static void script_host_execute(char *name, exarg_T *eap)
char *const script = script_get(eap, &len);
if (script != NULL) {
list_T *const args = tv_list_alloc();
list_T *const args = tv_list_alloc(3);
// script
tv_list_append_allocated_string(args, script);
// current range
@ -3773,7 +3773,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
uint8_t buffer[MAXPATHL];
vim_FullName((char *)eap->arg, (char *)buffer, sizeof(buffer), false);
list_T *args = tv_list_alloc();
list_T *args = tv_list_alloc(3);
// filename
tv_list_append_string(args, (const char *)buffer, -1);
// current range
@ -3784,7 +3784,7 @@ static void script_host_execute_file(char *name, exarg_T *eap)
static void script_host_do_range(char *name, exarg_T *eap)
{
list_T *args = tv_list_alloc();
list_T *args = tv_list_alloc(3);
tv_list_append_number(args, (int)eap->line1);
tv_list_append_number(args, (int)eap->line2);
tv_list_append_string(args, (const char *)eap->arg, -1);

View File

@ -211,7 +211,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
size_t len;
const char *s = lua_tolstring(lstate, -2, &len);
if (cur.special) {
list_T *const kv_pair = tv_list_alloc();
list_T *const kv_pair = tv_list_alloc(2);
typval_T s_tv = decode_string(s, len, kTrue, false, false);
if (s_tv.v_type == VAR_UNKNOWN) {
@ -321,7 +321,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
switch (table_props.type) {
case kObjectTypeArray: {
cur.tv->v_type = VAR_LIST;
cur.tv->vval.v_list = tv_list_alloc();
cur.tv->vval.v_list = tv_list_alloc((ptrdiff_t)table_props.maxidx);
tv_list_ref(cur.tv->vval.v_list);
if (table_props.maxidx != 0) {
cur.container = true;
@ -338,7 +338,8 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
} else {
cur.special = table_props.has_string_with_nul;
if (table_props.has_string_with_nul) {
decode_create_map_special_dict(cur.tv);
decode_create_map_special_dict(
cur.tv, (ptrdiff_t)table_props.string_keys_num);
assert(cur.tv->v_type == VAR_DICT);
dictitem_T *const val_di = tv_dict_find(cur.tv->vval.v_dict,
S_LEN("_VAL"));

View File

@ -412,7 +412,7 @@ int main(int argc, char **argv)
}
// It's better to make v:oldfiles an empty list than NULL.
if (get_vim_var_list(VV_OLDFILES) == NULL) {
set_vim_var_list(VV_OLDFILES, tv_list_alloc());
set_vim_var_list(VV_OLDFILES, tv_list_alloc(0));
}
/*

View File

@ -559,6 +559,7 @@ void time_to_bytes(time_t time_, uint8_t buf[8])
#include "nvim/tag.h"
#include "nvim/window.h"
#include "nvim/os/os.h"
#include "nvim/eval/typval.h"
/*
* Free everything that we allocated.
@ -692,6 +693,7 @@ void free_all_mem(void)
free_screenlines();
clear_hl_tables();
list_free_log();
}
#endif

View File

@ -714,7 +714,7 @@ static dict_T *menu_get_recursive(const vimmenu_T *menu, int modes)
}
} else {
// visit recursively all children
list_T *children_list = tv_list_alloc();
list_T *const children_list = tv_list_alloc(kListLenMayKnow);
for (menu = menu->children; menu != NULL; menu = menu->next) {
dict_T *dic = menu_get_recursive(menu, modes);
if (tv_dict_len(dict) > 0) {

View File

@ -2564,7 +2564,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
dict_T *dict = get_vim_var_dict(VV_EVENT);
// the yanked text
list_T *list = tv_list_alloc();
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
@ -4854,7 +4854,7 @@ static void *get_reg_wrap_one_line(char_u *s, int flags)
if (!(flags & kGRegList)) {
return s;
}
list_T *const list = tv_list_alloc();
list_T *const list = tv_list_alloc(1);
tv_list_append_allocated_string(list, (char *)s);
return list;
}
@ -4904,7 +4904,7 @@ void *get_reg_contents(int regname, int flags)
return NULL;
if (flags & kGRegList) {
list_T *list = tv_list_alloc();
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(list, (const char *)reg->y_array[i], -1);
}
@ -5593,7 +5593,7 @@ static bool get_clipboard(int name, yankreg_T **target, bool quiet)
}
free_register(reg);
list_T *const args = tv_list_alloc();
list_T *const args = tv_list_alloc(1);
const char regname = (char)name;
tv_list_append_string(args, &regname, 1);
@ -5712,15 +5712,13 @@ static void set_clipboard(int name, yankreg_T *reg)
return;
}
list_T *lines = tv_list_alloc();
list_T *const lines = tv_list_alloc(
(ptrdiff_t)reg->y_size + (reg->y_type != kMTCharWise));
for (size_t i = 0; i < reg->y_size; i++) {
tv_list_append_string(lines, (const char *)reg->y_array[i], -1);
}
list_T *args = tv_list_alloc();
tv_list_append_list(args, lines);
char regtype;
switch (reg->y_type) {
case kMTLineWise: {
@ -5741,10 +5739,11 @@ static void set_clipboard(int name, yankreg_T *reg)
assert(false);
}
}
tv_list_append_string(args, &regtype, 1);
const char regname = (char)name;
tv_list_append_string(args, &regname, 1);
list_T *args = tv_list_alloc(3);
tv_list_append_list(args, lines);
tv_list_append_string(args, &regtype, 1);
tv_list_append_string(args, ((char[]) { (char)name }), 1);
(void)eval_call_provider("clipboard", "set", args);
}

View File

@ -39,9 +39,9 @@
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
/// writing to the file at once is not supported, so either
/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
/// kFileWriteOnly or kFileReadOnly is required.
/// @param[in] mode Permissions for the newly created file (ignored if flags
/// does not have FILE_CREATE\*).
/// does not have kFileCreate\*).
///
/// @return Error code (@see os_strerror()) or 0.
int file_open(FileDescriptor *const ret_fp, const char *const fname,
@ -120,7 +120,7 @@ int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr)
/// @param[in] fname File name to open.
/// @param[in] flags Flags, @see FileOpenFlags.
/// @param[in] mode Permissions for the newly created file (ignored if flags
/// does not have FILE_CREATE\*).
/// does not have kFileCreate\*).
///
/// @return [allocated] Opened file or NULL in case of error.
FileDescriptor *file_open_new(int *const error, const char *const fname,

View File

@ -4193,7 +4193,7 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict)
}
}
if ((status == OK) && (flags & QF_GETLIST_ITEMS)) {
list_T *l = tv_list_alloc();
list_T *l = tv_list_alloc(kListLenMayKnow);
(void)get_errorlist(wp, qf_idx, l);
tv_dict_add_list(retdict, S_LEN("items"), l);
}

View File

@ -7044,7 +7044,7 @@ list_T *reg_submatch_list(int no)
colnr_T scol = rsm.sm_mmatch->startpos[no].col;
colnr_T ecol = rsm.sm_mmatch->endpos[no].col;
list = tv_list_alloc();
list = tv_list_alloc(elnum - slnum + 1);
s = (const char *)reg_getline_submatch(slnum) + scol;
if (slnum == elnum) {
@ -7063,7 +7063,7 @@ list_T *reg_submatch_list(int no)
if (s == NULL || rsm.sm_match->endp[no] == NULL) {
return NULL;
}
list = tv_list_alloc();
list = tv_list_alloc(1);
tv_list_append_string(list, s, (const char *)rsm.sm_match->endp[no] - s);
}

View File

@ -1217,7 +1217,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags)
khash_t(fnamebufs) fname_bufs = KHASH_EMPTY_TABLE(fnamebufs);
khash_t(strset) oldfiles_set = KHASH_EMPTY_TABLE(strset);
if (get_old_files && (oldfiles_list == NULL || force)) {
oldfiles_list = tv_list_alloc();
oldfiles_list = tv_list_alloc(kListLenUnknown);
set_vim_var_list(VV_OLDFILES, oldfiles_list);
}
ShaDaReadResult srni_ret;

View File

@ -667,7 +667,7 @@ do_tag (
fname = xmalloc(MAXPATHL + 1);
cmd = xmalloc(CMDBUFFSIZE + 1);
list = tv_list_alloc();
list = tv_list_alloc(num_matches);
for (i = 0; i < num_matches; ++i) {
int len, cmd_len;

View File

@ -2941,17 +2941,20 @@ bool curbufIsChanged(void)
&& (curbuf->b_changed || file_ff_differs(curbuf, true)));
}
/*
* For undotree(): Append the list of undo blocks at "first_uhp" to "list".
* Recursive.
*/
void u_eval_tree(u_header_T *first_uhp, list_T *list)
/// Append the list of undo blocks to a newly allocated list
///
/// For use in undotree(). Recursive.
///
/// @param[in] first_uhp Undo blocks list to start with.
///
/// @return [allocated] List with a representation of undo blocks.
list_T *u_eval_tree(const u_header_T *const first_uhp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
{
u_header_T *uhp = first_uhp;
dict_T *dict;
list_T *const list = tv_list_alloc(kListLenMayKnow);
while (uhp != NULL) {
dict = tv_dict_alloc();
for (const u_header_T *uhp = first_uhp; uhp != NULL; uhp = uhp->uh_prev.ptr) {
dict_T *const dict = tv_dict_alloc();
tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq);
tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time);
if (uhp == curbuf->b_u_newhead) {
@ -2965,14 +2968,12 @@ void u_eval_tree(u_header_T *first_uhp, list_T *list)
}
if (uhp->uh_alt_next.ptr != NULL) {
list_T *alt_list = tv_list_alloc();
// Recursive call to add alternate undo tree.
u_eval_tree(uhp->uh_alt_next.ptr, alt_list);
tv_dict_add_list(dict, S_LEN("alt"), alt_list);
tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(uhp->uh_alt_next.ptr));
}
tv_list_append_dict(list, dict);
uhp = uhp->uh_prev.ptr;
}
return list;
}

View File

@ -5921,13 +5921,15 @@ void win_get_tabwin(handle_T id, int *tabnr, int *winnr)
}
}
void win_id2tabwin(typval_T *argvars, list_T *list)
void win_id2tabwin(typval_T *const argvars, typval_T *const rettv)
{
int winnr = 1;
int tabnr = 1;
handle_T id = (handle_T)tv_get_number(&argvars[0]);
win_get_tabwin(id, &tabnr, &winnr);
list_T *const list = tv_list_alloc_ret(rettv, 2);
tv_list_append_number(list, tabnr);
tv_list_append_number(list, winnr);
}

View File

@ -312,7 +312,7 @@ local lua2typvalt_type_tab = {
processed[l].lv_refcount = processed[l].lv_refcount + 1
return typvalt(eval.VAR_LIST, {v_list=processed[l]})
end
local lst = populate_list(eval.tv_list_alloc(), l, processed)
local lst = populate_list(eval.tv_list_alloc(#l), l, processed)
return typvalt(eval.VAR_LIST, {v_list=lst})
end,
[dict_type] = function(l, processed)
@ -433,7 +433,8 @@ local function int(n)
end
local function list(...)
return populate_list(ffi.gc(eval.tv_list_alloc(), eval.tv_list_unref),
return populate_list(ffi.gc(eval.tv_list_alloc(select('#', ...)),
eval.tv_list_unref),
{...}, {})
end

View File

@ -2407,7 +2407,7 @@ describe('typval.c', function()
describe('list ret()', function()
itp('works', function()
local rettv = typvalt(lib.VAR_UNKNOWN)
local l = lib.tv_list_alloc_ret(rettv)
local l = lib.tv_list_alloc_ret(rettv, 0)
eq(empty_list, typvalt2lua(rettv))
eq(rettv.vval.v_list, l)
end)