Merge pull request #7762 from ZyX-I/remove-some-listitems

Remove some tv_list_item_…() functions
This commit is contained in:
Justin M. Keyes 2017-12-31 01:11:50 +01:00 committed by GitHub
commit 9ad557fb2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 613 additions and 459 deletions

View File

@ -787,16 +787,14 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
for (uint32_t i = 0; i < obj.data.array.size; i++) { for (uint32_t i = 0; i < obj.data.array.size; i++) {
Object item = obj.data.array.items[i]; Object item = obj.data.array.items[i];
listitem_T *li = tv_list_item_alloc(); typval_T li_tv;
if (!object_to_vim(item, TV_LIST_ITEM_TV(li), err)) { if (!object_to_vim(item, &li_tv, err)) {
// cleanup
tv_list_item_free(li);
tv_list_free(list); tv_list_free(list);
return false; return false;
} }
tv_list_append(list, li); tv_list_append_owned_tv(list, li_tv);
} }
tv_list_ref(list); tv_list_ref(list);

View File

@ -215,6 +215,15 @@ static garray_T ga_scripts = {0, 0, sizeof(scriptvar_T *), 4, NULL};
static int echo_attr = 0; /* attributes used for ":echo" */ static int echo_attr = 0; /* attributes used for ":echo" */
/// Describe data to return from find_some_match()
typedef enum {
kSomeMatch, ///< Data for match().
kSomeMatchEnd, ///< Data for matchend().
kSomeMatchList, ///< Data for matchlist().
kSomeMatchStr, ///< Data for matchstr().
kSomeMatchStrPos, ///< Data for matchstrpos().
} SomeMatchType;
/// trans_function_name() flags /// trans_function_name() flags
typedef enum { typedef enum {
TFN_INT = 1, ///< May use internal function name TFN_INT = 1, ///< May use internal function name
@ -1506,7 +1515,6 @@ ex_let_vars (
) )
{ {
char_u *arg = arg_start; char_u *arg = arg_start;
int i;
typval_T ltv; typval_T ltv;
if (*arg != '[') { if (*arg != '[') {
@ -1525,17 +1533,17 @@ ex_let_vars (
} }
list_T *const l = tv->vval.v_list; list_T *const l = tv->vval.v_list;
i = tv_list_len(l); const int len = tv_list_len(l);
if (semicolon == 0 && var_count < i) { if (semicolon == 0 && var_count < len) {
EMSG(_("E687: Less targets than List items")); EMSG(_("E687: Less targets than List items"));
return FAIL; return FAIL;
} }
if (var_count - semicolon > i) { if (var_count - semicolon > len) {
EMSG(_("E688: More targets than List items")); EMSG(_("E688: More targets than List items"));
return FAIL; return FAIL;
} }
// l may actually be NULL, but it should fail with E688 or even earlier if you // List l may actually be NULL, but it should fail with E688 or even earlier
// try to do ":let [] = v:_null_list". // if you try to do ":let [] = v:_null_list".
assert(l != NULL); assert(l != NULL);
listitem_T *item = tv_list_first(l); listitem_T *item = tv_list_first(l);
@ -1543,11 +1551,11 @@ ex_let_vars (
arg = skipwhite(arg + 1); arg = skipwhite(arg + 1);
arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]", arg = ex_let_one(arg, TV_LIST_ITEM_TV(item), true, (const char_u *)",;]",
nextchars); nextchars);
item = TV_LIST_ITEM_NEXT(l, item);
if (arg == NULL) { if (arg == NULL) {
return FAIL; return FAIL;
} }
item = TV_LIST_ITEM_NEXT(l, item);
arg = skipwhite(arg); arg = skipwhite(arg);
if (*arg == ';') { if (*arg == ';') {
/* Put the rest of the list (may be empty) in the var after ';'. /* Put the rest of the list (may be empty) in the var after ';'.
@ -1559,7 +1567,7 @@ ex_let_vars (
} }
ltv.v_type = VAR_LIST; ltv.v_type = VAR_LIST;
ltv.v_lock = 0; ltv.v_lock = VAR_UNLOCKED;
ltv.vval.v_list = rest_list; ltv.vval.v_list = rest_list;
tv_list_ref(rest_list); tv_list_ref(rest_list);
@ -2412,9 +2420,11 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) {
// Need to add an empty item. // Need to add an empty item.
tv_list_append_number(lp->ll_list, 0); tv_list_append_number(lp->ll_list, 0);
assert(TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li)); // ll_li may have become invalid after append, dont use it.
} lp->ll_li = tv_list_last(lp->ll_list); // Valid again.
} else {
lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
}
lp->ll_n1++; lp->ll_n1++;
} }
if (ri != NULL) { if (ri != NULL) {
@ -2887,28 +2897,25 @@ static int do_unlet_var(lval_T *const lp, char_u *const name_end, int forceit)
lp->ll_name_len))) { lp->ll_name_len))) {
return FAIL; return FAIL;
} else if (lp->ll_range) { } else if (lp->ll_range) {
listitem_T *li; // Delete a range of List items.
listitem_T *ll_li = lp->ll_li; listitem_T *const first_li = lp->ll_li;
int ll_n1 = lp->ll_n1; listitem_T *last_li = first_li;
for (;;) {
while (ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= ll_n1)) { listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock,
(const char *)lp->ll_name, (const char *)lp->ll_name,
lp->ll_name_len)) { lp->ll_name_len)) {
return false; return false;
} }
ll_li = li;
ll_n1++;
}
// Delete a range of List items.
while (lp->ll_li != NULL && (lp->ll_empty2 || lp->ll_n2 >= lp->ll_n1)) {
li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
tv_list_item_remove(lp->ll_list, lp->ll_li);
lp->ll_li = li; lp->ll_li = li;
lp->ll_n1++; lp->ll_n1++;
if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) {
break;
} else {
last_li = lp->ll_li;
} }
}
tv_list_remove_items(lp->ll_list, first_li, last_li);
} else { } else {
if (lp->ll_list != NULL) { if (lp->ll_list != NULL) {
// unlet a List item. // unlet a List item.
@ -4522,7 +4529,7 @@ eval_index (
item = tv_list_find(rettv->vval.v_list, n1); item = tv_list_find(rettv->vval.v_list, n1);
while (n1++ <= n2) { while (n1++ <= n2) {
tv_list_append_tv(l, TV_LIST_ITEM_TV(item)); tv_list_append_tv(l, TV_LIST_ITEM_TV(item));
item = TV_LIST_ITEM_NEXT(l, item); item = TV_LIST_ITEM_NEXT(rettv->vval.v_list, item);
} }
tv_clear(rettv); tv_clear(rettv);
rettv->v_type = VAR_LIST; rettv->v_type = VAR_LIST;
@ -4874,8 +4881,6 @@ void partial_unref(partial_T *pt)
static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
{ {
list_T *l = NULL; list_T *l = NULL;
typval_T tv;
listitem_T *item;
if (evaluate) { if (evaluate) {
l = tv_list_alloc(); l = tv_list_alloc();
@ -4883,13 +4888,13 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
*arg = skipwhite(*arg + 1); *arg = skipwhite(*arg + 1);
while (**arg != ']' && **arg != NUL) { while (**arg != ']' && **arg != NUL) {
if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ typval_T tv;
if (eval1(arg, &tv, evaluate) == FAIL) { // Recursive!
goto failret; goto failret;
}
if (evaluate) { if (evaluate) {
item = tv_list_item_alloc(); tv.v_lock = VAR_UNLOCKED;
*TV_LIST_ITEM_TV(item) = tv; tv_list_append_owned_tv(l, tv);
TV_LIST_ITEM_TV(item)->v_lock = VAR_UNLOCKED;
tv_list_append(l, item);
} }
if (**arg == ']') { if (**arg == ']') {
@ -8469,7 +8474,6 @@ static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what)
static void filter_map(typval_T *argvars, typval_T *rettv, int map) static void filter_map(typval_T *argvars, typval_T *rettv, int map)
{ {
typval_T *expr; typval_T *expr;
listitem_T *li, *nli;
list_T *l = NULL; list_T *l = NULL;
dictitem_T *di; dictitem_T *di;
hashtab_T *ht; hashtab_T *ht;
@ -8553,20 +8557,21 @@ static void filter_map(typval_T *argvars, typval_T *rettv, int map)
} else { } else {
vimvars[VV_KEY].vv_type = VAR_NUMBER; vimvars[VV_KEY].vv_type = VAR_NUMBER;
for (li = tv_list_first(l); li != NULL; li = nli) { for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map if (map
&& tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
TV_TRANSLATE)) { TV_TRANSLATE)) {
break; break;
} }
nli = TV_LIST_ITEM_NEXT(l, li);
vimvars[VV_KEY].vv_nr = idx; vimvars[VV_KEY].vv_nr = idx;
if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL if (filter_map_one(TV_LIST_ITEM_TV(li), expr, map, &rem) == FAIL
|| did_emsg) { || did_emsg) {
break; break;
} }
if (!map && rem) { if (!map && rem) {
tv_list_item_remove(l, li); li = tv_list_item_remove(l, li);
} else {
li = TV_LIST_ITEM_NEXT(l, li);
} }
idx++; idx++;
} }
@ -11441,40 +11446,38 @@ static void dict_list(typval_T *const tv, typval_T *const rettv,
tv_list_alloc_ret(rettv); tv_list_alloc_ret(rettv);
TV_DICT_ITER(tv->vval.v_dict, di, { TV_DICT_ITER(tv->vval.v_dict, di, {
listitem_T *const li = tv_list_item_alloc(); typval_T tv = { .v_lock = VAR_UNLOCKED };
tv_list_append(rettv->vval.v_list, li);
switch (what) { switch (what) {
case kDictListKeys: { case kDictListKeys: {
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; tv.v_type = VAR_STRING;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; tv.vval.v_string = vim_strsave(di->di_key);
TV_LIST_ITEM_TV(li)->vval.v_string = vim_strsave(di->di_key);
break; break;
} }
case kDictListValues: { case kDictListValues: {
tv_copy(&di->di_tv, TV_LIST_ITEM_TV(li)); tv_copy(&di->di_tv, &tv);
break; break;
} }
case kDictListItems: { case kDictListItems: {
// items() // items()
list_T *const sub_l = tv_list_alloc(); list_T *const sub_l = tv_list_alloc();
TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; tv.v_type = VAR_LIST;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; tv.vval.v_list = sub_l;
TV_LIST_ITEM_TV(li)->vval.v_list = sub_l;
tv_list_ref(sub_l); tv_list_ref(sub_l);
listitem_T *sub_li = tv_list_item_alloc(); tv_list_append_owned_tv(sub_l, (typval_T) {
tv_list_append(sub_l, sub_li); .v_type = VAR_STRING,
TV_LIST_ITEM_TV(sub_li)->v_type = VAR_STRING; .v_lock = VAR_UNLOCKED,
TV_LIST_ITEM_TV(sub_li)->v_lock = VAR_UNLOCKED; .vval.v_string = (char_u *)xstrdup((const char *)di->di_key),
TV_LIST_ITEM_TV(sub_li)->vval.v_string = vim_strsave(di->di_key); });
tv_list_append_tv(sub_l, &di->di_tv);
sub_li = tv_list_item_alloc();
tv_list_append(sub_l, sub_li);
tv_copy(&di->di_tv, TV_LIST_ITEM_TV(sub_li));
break; break;
} }
} }
tv_list_append_owned_tv(rettv->vval.v_list, tv);
}); });
} }
@ -12190,7 +12193,8 @@ static void f_mapcheck(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
static void find_some_match(typval_T *argvars, typval_T *rettv, int type) static void find_some_match(typval_T *const argvars, typval_T *const rettv,
const SomeMatchType type)
{ {
char_u *str = NULL; char_u *str = NULL;
long len = 0; long len = 0;
@ -12211,24 +12215,37 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
p_cpo = (char_u *)""; p_cpo = (char_u *)"";
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
if (type == 3 || type == 4) { switch (type) {
// type 3: return empty list when there are no matches. // matchlist(): return empty list when there are no matches.
// type 4: return ["", -1, -1, -1] case kSomeMatchList: {
tv_list_alloc_ret(rettv);
break;
}
// matchstrpos(): return ["", -1, -1, -1]
case kSomeMatchStrPos: {
tv_list_alloc_ret(rettv); tv_list_alloc_ret(rettv);
if (type == 4) {
tv_list_append_string(rettv->vval.v_list, "", 0); 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);
tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1);
tv_list_append_number(rettv->vval.v_list, -1); tv_list_append_number(rettv->vval.v_list, -1);
break;
} }
} else if (type == 2) { case kSomeMatchStr: {
rettv->v_type = VAR_STRING; rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL; rettv->vval.v_string = NULL;
break;
}
case kSomeMatch:
case kSomeMatchEnd: {
// Do nothing: zero is default.
break;
}
} }
if (argvars[0].v_type == VAR_LIST) { if (argvars[0].v_type == VAR_LIST) {
if ((l = argvars[0].vval.v_list) == NULL) if ((l = argvars[0].vval.v_list) == NULL) {
goto theend; goto theend;
}
li = tv_list_first(l); li = tv_list_first(l);
} else { } else {
expr = str = (char_u *)tv_get_string(&argvars[0]); expr = str = (char_u *)tv_get_string(&argvars[0]);
@ -12318,7 +12335,8 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
} }
if (match) { if (match) {
if (type == 4) { switch (type) {
case kSomeMatchStrPos: {
list_T *const ret_l = rettv->vval.v_list; list_T *const ret_l = rettv->vval.v_list;
listitem_T *li1 = tv_list_first(ret_l); listitem_T *li1 = tv_list_first(ret_l);
listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1); listitem_T *li2 = TV_LIST_ITEM_NEXT(ret_l, li1);
@ -12336,11 +12354,11 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
if (l != NULL) { if (l != NULL) {
TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx; TV_LIST_ITEM_TV(li2)->vval.v_number = (varnumber_T)idx;
} }
} else if (type == 3) { break;
int i; }
case kSomeMatchList: {
/* return list with matched string and submatches */ // Return list with matched string and submatches.
for (i = 0; i < NSUBEXP; ++i) { for (int i = 0; i < NSUBEXP; i++) {
if (regmatch.endp[i] == NULL) { if (regmatch.endp[i] == NULL) {
tv_list_append_string(rettv->vval.v_list, NULL, 0); tv_list_append_string(rettv->vval.v_list, NULL, 0);
} else { } else {
@ -12349,7 +12367,9 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
(regmatch.endp[i] - regmatch.startp[i])); (regmatch.endp[i] - regmatch.startp[i]));
} }
} }
} else if (type == 2) { break;
}
case kSomeMatchStr: {
// Return matched string. // Return matched string.
if (l != NULL) { if (l != NULL) {
tv_copy(TV_LIST_ITEM_TV(li), rettv); tv_copy(TV_LIST_ITEM_TV(li), rettv);
@ -12358,10 +12378,14 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
(const char *)regmatch.startp[0], (const char *)regmatch.startp[0],
(size_t)(regmatch.endp[0] - regmatch.startp[0])); (size_t)(regmatch.endp[0] - regmatch.startp[0]));
} }
} else if (l != NULL) { break;
}
case kSomeMatch:
case kSomeMatchEnd: {
if (l != NULL) {
rettv->vval.v_number = idx; rettv->vval.v_number = idx;
} else { } else {
if (type != 0) { if (type == kSomeMatch) {
rettv->vval.v_number = rettv->vval.v_number =
(varnumber_T)(regmatch.startp[0] - str); (varnumber_T)(regmatch.startp[0] - str);
} else { } else {
@ -12370,11 +12394,14 @@ static void find_some_match(typval_T *argvars, typval_T *rettv, int type)
} }
rettv->vval.v_number += (varnumber_T)(str - expr); rettv->vval.v_number += (varnumber_T)(str - expr);
} }
break;
}
}
} }
vim_regfree(regmatch.regprog); vim_regfree(regmatch.regprog);
} }
if (type == 4 && l == NULL) { if (type == kSomeMatchStrPos && l == NULL) {
// matchstrpos() without a list: drop the second item // matchstrpos() without a list: drop the second item
list_T *const ret_l = rettv->vval.v_list; list_T *const ret_l = rettv->vval.v_list;
tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l))); tv_list_item_remove(ret_l, TV_LIST_ITEM_NEXT(ret_l, tv_list_first(ret_l)));
@ -12390,7 +12417,7 @@ theend:
*/ */
static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
find_some_match(argvars, rettv, 1); find_some_match(argvars, rettv, kSomeMatch);
} }
/* /*
@ -12505,12 +12532,12 @@ static void f_matcharg(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
tv_list_alloc_ret(rettv); tv_list_alloc_ret(rettv);
int id = tv_get_number(&argvars[0]); const int id = tv_get_number(&argvars[0]);
if (id >= 1 && id <= 3) { if (id >= 1 && id <= 3) {
matchitem_T *m; matchitem_T *const m = (matchitem_T *)get_match(curwin, id);
if ((m = (matchitem_T *)get_match(curwin, id)) != NULL) { if (m != NULL) {
tv_list_append_string(rettv->vval.v_list, tv_list_append_string(rettv->vval.v_list,
(const char *)syn_id2name(m->hlg_id), -1); (const char *)syn_id2name(m->hlg_id), -1);
tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1); tv_list_append_string(rettv->vval.v_list, (const char *)m->pattern, -1);
@ -12535,7 +12562,7 @@ static void f_matchdelete(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/ */
static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
find_some_match(argvars, rettv, 0); find_some_match(argvars, rettv, kSomeMatchEnd);
} }
/* /*
@ -12543,7 +12570,7 @@ static void f_matchend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/ */
static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
find_some_match(argvars, rettv, 3); find_some_match(argvars, rettv, kSomeMatchList);
} }
/* /*
@ -12551,13 +12578,13 @@ static void f_matchlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
*/ */
static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchstr(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
find_some_match(argvars, rettv, 2); find_some_match(argvars, rettv, kSomeMatchStr);
} }
/// "matchstrpos()" function /// "matchstrpos()" function
static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_matchstrpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{ {
find_some_match(argvars, rettv, 4); find_some_match(argvars, rettv, kSomeMatchStrPos);
} }
/// Get maximal/minimal number value in a list or dictionary /// Get maximal/minimal number value in a list or dictionary
@ -12762,13 +12789,12 @@ static void f_msgpackparse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
goto f_msgpackparse_exit; goto f_msgpackparse_exit;
} }
if (result == MSGPACK_UNPACK_SUCCESS) { if (result == MSGPACK_UNPACK_SUCCESS) {
listitem_T *li = tv_list_item_alloc(); typval_T tv = { .v_type = VAR_UNKNOWN };
TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; if (msgpack_to_vim(unpacked.data, &tv) == FAIL) {
tv_list_append(ret_list, li);
if (msgpack_to_vim(unpacked.data, TV_LIST_ITEM_TV(li)) == FAIL) {
EMSG2(_(e_invarg2), "Failed to convert msgpack string"); EMSG2(_(e_invarg2), "Failed to convert msgpack string");
goto f_msgpackparse_exit; goto f_msgpackparse_exit;
} }
tv_list_append_owned_tv(ret_list, tv);
} }
if (result == MSGPACK_UNPACK_CONTINUE) { if (result == MSGPACK_UNPACK_CONTINUE) {
if (rlret == OK) { if (rlret == OK) {
@ -12995,9 +13021,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
long prevlen = 0; /* length of data in prev */ long prevlen = 0; /* length of data in prev */
long prevsize = 0; /* size of prev buffer */ long prevsize = 0; /* size of prev buffer */
long maxline = MAXLNUM; long maxline = MAXLNUM;
long cnt = 0;
char_u *p; /* position in buf */
char_u *start; /* start of current line */
if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[1].v_type != VAR_UNKNOWN) {
if (strcmp(tv_get_string(&argvars[1]), "b") == 0) { if (strcmp(tv_get_string(&argvars[1]), "b") == 0) {
@ -13009,6 +13032,7 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
tv_list_alloc_ret(rettv); tv_list_alloc_ret(rettv);
list_T *const l = rettv->vval.v_list;
// Always open the file in binary mode, library functions have a mind of // Always open the file in binary mode, library functions have a mind of
// their own about CR-LF conversion. // their own about CR-LF conversion.
@ -13018,19 +13042,20 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; return;
} }
while (cnt < maxline || maxline < 0) { while (maxline < 0 || tv_list_len(l) < maxline) {
readlen = (int)fread(buf, 1, io_size, fd); readlen = (int)fread(buf, 1, io_size, fd);
/* This for loop processes what was read, but is also entered at end // This for loop processes what was read, but is also entered at end
* of file so that either: // of file so that either:
* - an incomplete line gets written // - an incomplete line gets written
* - a "binary" file gets an empty line at the end if it ends in a // - a "binary" file gets an empty line at the end if it ends in a
* newline. */ // newline.
char_u *p; // Position in buf.
char_u *start; // Start of current line.
for (p = buf, start = buf; for (p = buf, start = buf;
p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary)); p < buf + readlen || (readlen <= 0 && (prevlen > 0 || binary));
++p) { p++) {
if (*p == '\n' || readlen <= 0) { if (*p == '\n' || readlen <= 0) {
listitem_T *li;
char_u *s = NULL; char_u *s = NULL;
size_t len = p - start; size_t len = p - start;
@ -13057,22 +13082,32 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
prevlen = prevsize = 0; prevlen = prevsize = 0;
} }
li = tv_list_item_alloc(); tv_list_append_owned_tv(l, (typval_T) {
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; .v_type = VAR_STRING,
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; .v_lock = VAR_UNLOCKED,
TV_LIST_ITEM_TV(li)->vval.v_string = s; .vval.v_string = s,
tv_list_append(rettv->vval.v_list, li); });
start = p + 1; /* step over newline */ start = p + 1; // Step over newline.
if ((++cnt >= maxline && maxline >= 0) || readlen <= 0) if (maxline < 0) {
if (tv_list_len(l) > -maxline) {
assert(tv_list_len(l) == 1 + (-maxline));
tv_list_item_remove(l, tv_list_first(l));
}
} else if (tv_list_len(l) >= maxline) {
assert(tv_list_len(l) == maxline);
break; break;
} else if (*p == NUL) }
if (readlen <= 0) {
break;
}
} else if (*p == NUL) {
*p = '\n'; *p = '\n';
/* Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this // Check for utf8 "bom"; U+FEFF is encoded as EF BB BF. Do this
* when finding the BF and check the previous two bytes. */ // when finding the BF and check the previous two bytes.
else if (*p == 0xbf && enc_utf8 && !binary) { } else if (*p == 0xbf && !binary) {
/* Find the two bytes before the 0xbf. If p is at buf, or buf // Find the two bytes before the 0xbf. If p is at buf, or buf + 1,
* + 1, these may be in the "prev" string. */ // these may be in the "prev" string.
char_u back1 = p >= buf + 1 ? p[-1] char_u back1 = p >= buf + 1 ? p[-1]
: prevlen >= 1 ? prev[prevlen - 1] : NUL; : prevlen >= 1 ? prev[prevlen - 1] : NUL;
char_u back2 = p >= buf + 2 ? p[-2] char_u back2 = p >= buf + 2 ? p[-2]
@ -13106,8 +13141,9 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
} /* for */ } /* for */
if ((cnt >= maxline && maxline >= 0) || readlen <= 0) if ((maxline >= 0 && tv_list_len(l) >= maxline) || readlen <= 0) {
break; break;
}
if (start < p) { if (start < p) {
/* There's part of a line in buf, store it in "prev". */ /* There's part of a line in buf, store it in "prev". */
if (p - start + prevlen >= prevsize) { if (p - start + prevlen >= prevsize) {
@ -13131,17 +13167,6 @@ static void f_readfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} }
} /* while */ } /* while */
/*
* For a negative line count use only the lines at the end of the file,
* free the rest.
*/
if (maxline < 0)
while (cnt > -maxline) {
tv_list_item_remove(rettv->vval.v_list,
tv_list_first(rettv->vval.v_list));
cnt--;
}
xfree(prev); xfree(prev);
fclose(fd); fclose(fd);
} }
@ -13291,7 +13316,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else { } else {
if (argvars[2].v_type == VAR_UNKNOWN) { if (argvars[2].v_type == VAR_UNKNOWN) {
// Remove one item, return its value. // Remove one item, return its value.
tv_list_remove_items(l, item, item); tv_list_drop_items(l, item, item);
*rettv = *TV_LIST_ITEM_TV(item); *rettv = *TV_LIST_ITEM_TV(item);
xfree(item); xfree(item);
} else { } else {
@ -14310,11 +14335,7 @@ static void f_serverlist(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// Copy addrs into a linked list. // Copy addrs into a linked list.
list_T *l = tv_list_alloc_ret(rettv); list_T *l = tv_list_alloc_ret(rettv);
for (size_t i = 0; i < n; i++) { for (size_t i = 0; i < n; i++) {
listitem_T *li = tv_list_item_alloc(); tv_list_append_allocated_string(l, addrs[i]);
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED;
TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)addrs[i];
tv_list_append(l, li);
} }
xfree(addrs); xfree(addrs);
} }
@ -15120,12 +15141,6 @@ static void f_sockconnect(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
} }
/// struct used in the array that's given to qsort()
typedef struct {
listitem_T *item;
int idx;
} sortItem_T;
/// struct storing information about current sort /// struct storing information about current sort
typedef struct { typedef struct {
int item_compare_ic; int item_compare_ic;
@ -15146,8 +15161,8 @@ static sortinfo_T *sortinfo = NULL;
*/ */
static int item_compare(const void *s1, const void *s2, bool keep_zero) static int item_compare(const void *s1, const void *s2, bool keep_zero)
{ {
sortItem_T *const si1 = (sortItem_T *)s1; ListSortItem *const si1 = (ListSortItem *)s1;
sortItem_T *const si2 = (sortItem_T *)s2; ListSortItem *const si2 = (ListSortItem *)s2;
typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item); typval_T *const tv1 = TV_LIST_ITEM_TV(si1->item);
typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item); typval_T *const tv2 = TV_LIST_ITEM_TV(si2->item);
@ -15241,7 +15256,7 @@ static int item_compare_not_keeping_zero(const void *s1, const void *s2)
static int item_compare2(const void *s1, const void *s2, bool keep_zero) static int item_compare2(const void *s1, const void *s2, bool keep_zero)
{ {
sortItem_T *si1, *si2; ListSortItem *si1, *si2;
int res; int res;
typval_T rettv; typval_T rettv;
typval_T argv[3]; typval_T argv[3];
@ -15254,8 +15269,8 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
return 0; return 0;
} }
si1 = (sortItem_T *)s1; si1 = (ListSortItem *)s1;
si2 = (sortItem_T *)s2; si2 = (ListSortItem *)s2;
if (partial == NULL) { if (partial == NULL) {
func_name = sortinfo->item_compare_func; func_name = sortinfo->item_compare_func;
@ -15312,7 +15327,7 @@ static int item_compare2_not_keeping_zero(const void *s1, const void *s2)
*/ */
static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort) static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
{ {
sortItem_T *ptrs; ListSortItem *ptrs;
long len; long len;
long i; long i;
@ -15401,43 +15416,22 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
} }
} }
/* Make an array with each entry pointing to an item in the List. */ // Make an array with each entry pointing to an item in the List.
ptrs = xmalloc((size_t)(len * sizeof (sortItem_T))); ptrs = xmalloc((size_t)(len * sizeof(ListSortItem)));
i = 0;
if (sort) { if (sort) {
// sort(): ptrs will be the list to sort.
TV_LIST_ITER(l, li, {
ptrs[i].item = li;
ptrs[i].idx = i;
i++;
});
info.item_compare_func_err = false; info.item_compare_func_err = false;
// Test the compare function. tv_list_item_sort(l, ptrs,
if ((info.item_compare_func != NULL ((info.item_compare_func == NULL
|| info.item_compare_partial != NULL) && info.item_compare_partial == NULL)
&& item_compare2_not_keeping_zero(&ptrs[0], &ptrs[1]) ? item_compare_not_keeping_zero
== ITEM_COMPARE_FAIL) { : item_compare2_not_keeping_zero),
&info.item_compare_func_err);
if (info.item_compare_func_err) {
EMSG(_("E702: Sort compare function failed")); EMSG(_("E702: Sort compare function failed"));
} else {
// Sort the array with item pointers.
qsort(ptrs, (size_t)len, sizeof (sortItem_T),
(info.item_compare_func == NULL
&& info.item_compare_partial == NULL ?
item_compare_not_keeping_zero :
item_compare2_not_keeping_zero));
if (!info.item_compare_func_err) {
// Clear the list and append the items in the sorted order.
tv_list_clear(l);
for (i = 0; i < len; i++) {
tv_list_append(l, ptrs[i].item);
}
}
} }
} else { } else {
int (*item_compare_func_ptr)(const void *, const void *); ListSorter item_compare_func_ptr;
// f_uniq(): ptrs will be a stack of items to remove. // f_uniq(): ptrs will be a stack of items to remove.
info.item_compare_func_err = false; info.item_compare_func_err = false;
@ -15450,18 +15444,17 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
int idx = 0; int idx = 0;
for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l)) for (listitem_T *li = TV_LIST_ITEM_NEXT(l, tv_list_first(l))
; li != NULL ; li != NULL;) {
; li = TV_LIST_ITEM_NEXT(l, li)) {
listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li); listitem_T *const prev_li = TV_LIST_ITEM_PREV(l, li);
if (item_compare_func_ptr(&prev_li, &li) == 0) { if (item_compare_func_ptr(&prev_li, &li) == 0) {
if (info.item_compare_func_err) { if (info.item_compare_func_err) {
EMSG(_("E882: Uniq compare function failed")); EMSG(_("E882: Uniq compare function failed"));
break; break;
} }
tv_list_item_remove(l, li); li = tv_list_item_remove(l, li);
li = tv_list_find(l, idx);
} else { } else {
idx++; idx++;
li = TV_LIST_ITEM_NEXT(l, li);
} }
} }
} }
@ -15619,12 +15612,7 @@ static void f_spellsuggest(typval_T *argvars, typval_T *rettv, FunPtr fptr)
for (int i = 0; i < ga.ga_len; i++) { for (int i = 0; i < ga.ga_len; i++) {
char *p = ((char **)ga.ga_data)[i]; char *p = ((char **)ga.ga_data)[i];
tv_list_append_allocated_string(rettv->vval.v_list, p);
listitem_T *const li = tv_list_item_alloc();
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING;
TV_LIST_ITEM_TV(li)->v_lock = VAR_LOCKED;
TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)p;
tv_list_append(rettv->vval.v_list, li);
} }
ga_clear(&ga); ga_clear(&ga);
} }

View File

@ -127,9 +127,7 @@ static inline int json_decoder_pop(ValuesStackItem obj,
return FAIL; return FAIL;
} }
assert(last_container.special_val == NULL); assert(last_container.special_val == NULL);
listitem_T *obj_li = tv_list_item_alloc(); tv_list_append_owned_tv(last_container.container.vval.v_list, obj.val);
*TV_LIST_ITEM_TV(obj_li) = obj.val;
tv_list_append(last_container.container.vval.v_list, obj_li);
} else if (last_container.stack_index == kv_size(*stack) - 2) { } else if (last_container.stack_index == kv_size(*stack) - 2) {
if (!obj.didcolon) { if (!obj.didcolon) {
EMSG2(_("E474: Expected colon before dictionary value: %s"), EMSG2(_("E474: Expected colon before dictionary value: %s"),
@ -154,12 +152,8 @@ static inline int json_decoder_pop(ValuesStackItem obj,
} else { } else {
list_T *const kv_pair = tv_list_alloc(); list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(last_container.special_val, kv_pair); tv_list_append_list(last_container.special_val, kv_pair);
listitem_T *const key_li = tv_list_item_alloc(); tv_list_append_owned_tv(kv_pair, key.val);
*TV_LIST_ITEM_TV(key_li) = key.val; tv_list_append_owned_tv(kv_pair, obj.val);
tv_list_append(kv_pair, key_li);
listitem_T *const val_li = tv_list_item_alloc();
*TV_LIST_ITEM_TV(val_li) = obj.val;
tv_list_append(kv_pair, val_li);
} }
} else { } else {
// Object with key only // Object with key only
@ -1047,10 +1041,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
.vval = { .v_list = list }, .vval = { .v_list = list },
}; };
for (size_t i = 0; i < mobj.via.array.size; i++) { for (size_t i = 0; i < mobj.via.array.size; i++) {
listitem_T *const li = tv_list_item_alloc(); // Not populated yet, need to create list item to push.
TV_LIST_ITEM_TV(li)->v_type = VAR_UNKNOWN; tv_list_append_owned_tv(list, (typval_T) { .v_type = VAR_UNKNOWN });
tv_list_append(list, li); if (msgpack_to_vim(mobj.via.array.ptr[i],
if (msgpack_to_vim(mobj.via.array.ptr[i], TV_LIST_ITEM_TV(li)) TV_LIST_ITEM_TV(tv_list_last(list)))
== FAIL) { == FAIL) {
return FAIL; return FAIL;
} }
@ -1095,20 +1089,20 @@ msgpack_to_vim_generic_map: {}
for (size_t i = 0; i < mobj.via.map.size; i++) { 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();
tv_list_append_list(list, kv_pair); tv_list_append_list(list, kv_pair);
listitem_T *const key_li = tv_list_item_alloc();
TV_LIST_ITEM_TV(key_li)->v_type = VAR_UNKNOWN; typval_T key_tv = { .v_type = VAR_UNKNOWN };
tv_list_append(kv_pair, key_li); if (msgpack_to_vim(mobj.via.map.ptr[i].key, &key_tv) == FAIL) {
listitem_T *const val_li = tv_list_item_alloc(); tv_clear(&key_tv);
TV_LIST_ITEM_TV(val_li)->v_type = VAR_UNKNOWN;
tv_list_append(kv_pair, val_li);
if (msgpack_to_vim(mobj.via.map.ptr[i].key, TV_LIST_ITEM_TV(key_li))
== FAIL) {
return FAIL; return FAIL;
} }
if (msgpack_to_vim(mobj.via.map.ptr[i].val, TV_LIST_ITEM_TV(val_li)) tv_list_append_owned_tv(kv_pair, key_tv);
== FAIL) {
typval_T val_tv = { .v_type = VAR_UNKNOWN };
if (msgpack_to_vim(mobj.via.map.ptr[i].val, &val_tv) == FAIL) {
tv_clear(&val_tv);
return FAIL; return FAIL;
} }
tv_list_append_owned_tv(kv_pair, val_tv);
} }
break; break;
} }

View File

@ -3,6 +3,7 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include <stdbool.h> #include <stdbool.h>
@ -52,35 +53,29 @@ const char *const tv_empty_string = "";
/// and specifically set lv_lock. /// and specifically set lv_lock.
/// ///
/// @return [allocated] new list item. /// @return [allocated] new list item.
listitem_T *tv_list_item_alloc(void) static listitem_T *tv_list_item_alloc(void)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC
{ {
return xmalloc(sizeof(listitem_T)); return xmalloc(sizeof(listitem_T));
} }
/// Free a list item
///
/// Also clears the value. Does not touch watchers.
///
/// @param[out] item Item to free.
void tv_list_item_free(listitem_T *const item)
FUNC_ATTR_NONNULL_ALL
{
tv_clear(TV_LIST_ITEM_TV(item));
xfree(item);
}
/// Remove a list item from a List and free it /// Remove a list item from a List and free it
/// ///
/// Also clears the value. /// Also clears the value.
/// ///
/// @param[out] l List to remove item from. /// @param[out] l List to remove item from.
/// @param[in,out] item Item to remove. /// @param[in,out] item Item to remove.
void tv_list_item_remove(list_T *const l, listitem_T *const item) ///
/// @return Pointer to the list item just after removed one, NULL if removed
/// item was the last one.
listitem_T *tv_list_item_remove(list_T *const l, listitem_T *const item)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
tv_list_remove_items(l, item, item); listitem_T *const next_item = TV_LIST_ITEM_NEXT(l, item);
tv_list_item_free(item); tv_list_drop_items(l, item, item);
tv_clear(TV_LIST_ITEM_TV(item));
xfree(item);
return next_item;
} }
//{{{2 List watchers //{{{2 List watchers
@ -267,7 +262,7 @@ void tv_list_unref(list_T *const l)
/// @param[out] l List to remove from. /// @param[out] l List to remove from.
/// @param[in] item First item to remove. /// @param[in] item First item to remove.
/// @param[in] item2 Last item to remove. /// @param[in] item2 Last item to remove.
void tv_list_remove_items(list_T *const l, listitem_T *const item, void tv_list_drop_items(list_T *const l, listitem_T *const item,
listitem_T *const item2) listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
@ -290,6 +285,23 @@ void tv_list_remove_items(list_T *const l, listitem_T *const item,
l->lv_idx_item = NULL; l->lv_idx_item = NULL;
} }
/// Like tv_list_drop_items, but also frees all removed items
void tv_list_remove_items(list_T *const l, listitem_T *const item,
listitem_T *const item2)
FUNC_ATTR_NONNULL_ALL
{
tv_list_drop_items(l, item, item2);
for (listitem_T *li = item;;) {
tv_clear(TV_LIST_ITEM_TV(li));
listitem_T *const nli = li->li_next;
xfree(li);
if (li == item2) {
break;
}
li = nli;
}
}
/// Move items "item" to "item2" from list "l" to the end of the list "tgt_l" /// Move items "item" to "item2" from list "l" to the end of the list "tgt_l"
/// ///
/// @param[out] l List to move from. /// @param[out] l List to move from.
@ -302,7 +314,7 @@ void tv_list_move_items(list_T *const l, listitem_T *const item,
const int cnt) const int cnt)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
tv_list_remove_items(l, item, item2); tv_list_drop_items(l, item, item2);
item->li_prev = tgt_l->lv_last; item->li_prev = tgt_l->lv_last;
item2->li_next = NULL; item2->li_next = NULL;
if (tgt_l->lv_last == NULL) { if (tgt_l->lv_last == NULL) {
@ -393,19 +405,30 @@ void tv_list_append_tv(list_T *const l, typval_T *const tv)
tv_list_append(l, li); tv_list_append(l, li);
} }
/// Like tv_list_append_tv(), but tv is moved to a list
///
/// This means that it is no longer valid to use contents of the typval_T after
/// function exits.
void tv_list_append_owned_tv(list_T *const l, typval_T tv)
FUNC_ATTR_NONNULL_ALL
{
listitem_T *const li = tv_list_item_alloc();
*TV_LIST_ITEM_TV(li) = tv;
tv_list_append(l, li);
}
/// Append a list to a list as one item /// Append a list to a list as one item
/// ///
/// @param[out] l List to append to. /// @param[out] l List to append to.
/// @param[in,out] itemlist List to append. Reference count is increased. /// @param[in,out] itemlist List to append. Reference count is increased.
void tv_list_append_list(list_T *const list, list_T *const itemlist) void tv_list_append_list(list_T *const l, list_T *const itemlist)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
listitem_T *const li = tv_list_item_alloc(); tv_list_append_owned_tv(l, (typval_T) {
.v_type = VAR_LIST,
TV_LIST_ITEM_TV(li)->v_type = VAR_LIST; .v_lock = VAR_UNLOCKED,
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; .vval.v_list = itemlist,
TV_LIST_ITEM_TV(li)->vval.v_list = itemlist; });
tv_list_append(list, li);
tv_list_ref(itemlist); tv_list_ref(itemlist);
} }
@ -413,15 +436,14 @@ void tv_list_append_list(list_T *const list, list_T *const itemlist)
/// ///
/// @param[out] l List to append to. /// @param[out] l List to append to.
/// @param[in,out] dict Dictionary to append. Reference count is increased. /// @param[in,out] dict Dictionary to append. Reference count is increased.
void tv_list_append_dict(list_T *const list, dict_T *const dict) void tv_list_append_dict(list_T *const l, dict_T *const dict)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
listitem_T *const li = tv_list_item_alloc(); tv_list_append_owned_tv(l, (typval_T) {
.v_type = VAR_DICT,
TV_LIST_ITEM_TV(li)->v_type = VAR_DICT; .v_lock = VAR_UNLOCKED,
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; .vval.v_dict = dict,
TV_LIST_ITEM_TV(li)->vval.v_dict = dict; });
tv_list_append(list, li);
if (dict != NULL) { if (dict != NULL) {
dict->dv_refcount++; dict->dv_refcount++;
} }
@ -438,14 +460,15 @@ void tv_list_append_string(list_T *const l, const char *const str,
const ssize_t len) const ssize_t len)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
if (str == NULL) { tv_list_append_owned_tv(l, (typval_T) {
assert(len == 0 || len == -1); .v_type = VAR_STRING,
tv_list_append_allocated_string(l, NULL); .v_lock = VAR_UNLOCKED,
} else { .vval.v_string = (str == NULL
tv_list_append_allocated_string(l, (len >= 0 ? NULL
: (len >= 0
? xmemdupz(str, (size_t)len) ? xmemdupz(str, (size_t)len)
: xstrdup(str))); : xstrdup(str))),
} });
} }
/// Append given string to the list /// Append given string to the list
@ -457,12 +480,11 @@ void tv_list_append_string(list_T *const l, const char *const str,
void tv_list_append_allocated_string(list_T *const l, char *const str) void tv_list_append_allocated_string(list_T *const l, char *const str)
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_ARG(1)
{ {
listitem_T *const li = tv_list_item_alloc(); tv_list_append_owned_tv(l, (typval_T) {
.v_type = VAR_STRING,
tv_list_append(l, li); .v_lock = VAR_UNLOCKED,
TV_LIST_ITEM_TV(li)->v_type = VAR_STRING; .vval.v_string = (char_u *)str,
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; });
TV_LIST_ITEM_TV(li)->vval.v_string = (char_u *)str;
} }
/// Append number to the list /// Append number to the list
@ -472,11 +494,11 @@ void tv_list_append_allocated_string(list_T *const l, char *const str)
/// listitem_T. /// listitem_T.
void tv_list_append_number(list_T *const l, const varnumber_T n) void tv_list_append_number(list_T *const l, const varnumber_T n)
{ {
listitem_T *const li = tv_list_item_alloc(); tv_list_append_owned_tv(l, (typval_T) {
TV_LIST_ITEM_TV(li)->v_type = VAR_NUMBER; .v_type = VAR_NUMBER,
TV_LIST_ITEM_TV(li)->v_lock = VAR_UNLOCKED; .v_lock = VAR_UNLOCKED,
TV_LIST_ITEM_TV(li)->vval.v_number = n; .vval.v_number = n,
tv_list_append(l, li); });
} }
//{{{2 Operations on the whole list //{{{2 Operations on the whole list
@ -736,6 +758,47 @@ void tv_list_reverse(list_T *const l)
l->lv_idx = l->lv_len - l->lv_idx - 1; l->lv_idx = l->lv_len - l->lv_idx - 1;
} }
// FIXME Add unit tests for tv_list_item_sort().
/// Sort list using libc qsort
///
/// @param[in,out] l List to sort, will be sorted in-place.
/// @param ptrs Preallocated array of items to sort, must have at least
/// tv_list_len(l) entries. Should not be initialized.
/// @param[in] item_compare_func Function used to compare list items.
/// @param errp Location where information about whether error occurred is
/// saved by item_compare_func. If boolean there appears to be
/// true list will not be modified. Must be initialized to false
/// by the caller.
void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs,
const ListSorter item_compare_func,
bool *errp)
FUNC_ATTR_NONNULL_ARG(3, 4)
{
const int len = tv_list_len(l);
if (len <= 1) {
return;
}
int i = 0;
TV_LIST_ITER(l, li, {
ptrs[i].item = li;
ptrs[i].idx = i;
i++;
});
// Sort the array with item pointers.
qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func);
if (!(*errp)) {
// Clear the list and append the items in the sorted order.
l->lv_first = NULL;
l->lv_last = NULL;
l->lv_idx_item = NULL;
l->lv_len = 0;
for (i = 0; i < len; i++) {
tv_list_append(l, ptrs[i].item);
}
}
}
//{{{2 Indexing/searching //{{{2 Indexing/searching
/// Locate item with a given index in a list and return it /// Locate item with a given index in a list and return it

View File

@ -296,6 +296,14 @@ typedef struct list_stack_S {
struct list_stack_S *prev; struct list_stack_S *prev;
} list_stack_T; } list_stack_T;
/// Structure representing one list item, used for sort array.
typedef struct {
listitem_T *item; ///< Sorted list item.
int idx; ///< Sorted list item index.
} ListSortItem;
typedef int (*ListSorter)(const void *, const void *);
// In a hashtab item "hi_key" points to "di_key" in a dictitem. // In a hashtab item "hi_key" points to "di_key" in a dictitem.
// This avoids adding a pointer to the hashtab item. // This avoids adding a pointer to the hashtab item.
@ -403,20 +411,6 @@ static inline list_T *tv_list_latest_copy(const list_T *const l)
return l->lv_copylist; return l->lv_copylist;
} }
/// Clear the list without freeing anything at all
///
/// For use in sort() which saves items to a separate array and readds them back
/// after sorting via a number of tv_list_append() calls.
///
/// @param[out] l List to clear.
static inline void tv_list_clear(list_T *const l)
{
l->lv_first = NULL;
l->lv_last = NULL;
l->lv_idx_item = NULL;
l->lv_len = 0;
}
static inline int tv_list_uidx(const list_T *const l, int n) static inline int tv_list_uidx(const list_T *const l, int n)
REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT; REAL_FATTR_PURE REAL_FATTR_WARN_UNUSED_RESULT;

View File

@ -212,19 +212,27 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
const char *s = lua_tolstring(lstate, -2, &len); const char *s = lua_tolstring(lstate, -2, &len);
if (cur.special) { if (cur.special) {
list_T *const kv_pair = tv_list_alloc(); list_T *const kv_pair = tv_list_alloc();
tv_list_append_list(cur.tv->vval.v_list, kv_pair);
listitem_T *const key = tv_list_item_alloc(); typval_T s_tv = decode_string(s, len, kTrue, false, false);
*TV_LIST_ITEM_TV(key) = decode_string(s, len, kTrue, false, false); if (s_tv.v_type == VAR_UNKNOWN) {
tv_list_append(kv_pair, key);
if (TV_LIST_ITEM_TV(key)->v_type == VAR_UNKNOWN) {
ret = false; ret = false;
tv_list_unref(kv_pair); tv_list_unref(kv_pair);
continue; continue;
} }
listitem_T *const val = tv_list_item_alloc(); tv_list_append_owned_tv(kv_pair, s_tv);
tv_list_append(kv_pair, val);
// Value: not populated yet, need to create list item to push.
tv_list_append_owned_tv(kv_pair, (typval_T) {
.v_type = VAR_UNKNOWN,
});
kv_push(stack, cur); kv_push(stack, cur);
cur = (TVPopStackItem) { TV_LIST_ITEM_TV(val), false, false, 0 }; tv_list_append_list(cur.tv->vval.v_list, kv_pair);
cur = (TVPopStackItem) {
.tv = TV_LIST_ITEM_TV(tv_list_last(kv_pair)),
.container = false,
.special = false,
.idx = 0,
};
} else { } else {
dictitem_T *const di = tv_dict_item_alloc_len(s, len); dictitem_T *const di = tv_dict_item_alloc_len(s, len);
if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) {
@ -244,10 +252,18 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv)
lua_pop(lstate, 2); lua_pop(lstate, 2);
continue; continue;
} }
listitem_T *const li = tv_list_item_alloc(); // Not populated yet, need to create list item to push.
tv_list_append(cur.tv->vval.v_list, li); tv_list_append_owned_tv(cur.tv->vval.v_list, (typval_T) {
.v_type = VAR_UNKNOWN,
});
kv_push(stack, cur); kv_push(stack, cur);
cur = (TVPopStackItem) { TV_LIST_ITEM_TV(li), false, false, 0 }; // TODO(ZyX-I): Use indexes, here list item *will* be reallocated.
cur = (TVPopStackItem) {
.tv = TV_LIST_ITEM_TV(tv_list_last(cur.tv->vval.v_list)),
.container = false,
.special = false,
.idx = 0,
};
} }
} }
assert(!cur.container); assert(!cur.container);

View File

@ -7,7 +7,7 @@ local ffi = helpers.ffi
local eq = helpers.eq local eq = helpers.eq
local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h', local eval = cimport('./src/nvim/eval.h', './src/nvim/eval/typval.h',
'./src/nvim/hashtab.h') './src/nvim/hashtab.h', './src/nvim/memory.h')
local null_string = {[true]='NULL string'} local null_string = {[true]='NULL string'}
local null_list = {[true]='NULL list'} local null_list = {[true]='NULL list'}
@ -24,10 +24,19 @@ local nil_value = {[true]='nil'}
local lua2typvalt local lua2typvalt
local function tv_list_item_alloc()
return ffi.cast('listitem_T*', eval.xmalloc(ffi.sizeof('listitem_T')))
end
local function tv_list_item_free(li)
eval.tv_clear(li.li_tv)
eval.xfree(li)
end
local function li_alloc(nogc) local function li_alloc(nogc)
local gcfunc = eval.tv_list_item_free local gcfunc = tv_list_item_free
if nogc then gcfunc = nil end if nogc then gcfunc = nil end
local li = ffi.gc(eval.tv_list_item_alloc(), gcfunc) local li = ffi.gc(tv_list_item_alloc(), gcfunc)
li.li_next = nil li.li_next = nil
li.li_prev = nil li.li_prev = nil
li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED} li.li_tv = {v_type=eval.VAR_UNKNOWN, v_lock=eval.VAR_UNLOCKED}
@ -41,7 +50,7 @@ local function populate_list(l, lua_l, processed)
processed[lua_l] = l processed[lua_l] = l
for i = 1, #lua_l do for i = 1, #lua_l do
local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil) local item_tv = ffi.gc(lua2typvalt(lua_l[i], processed), nil)
local item_li = eval.tv_list_item_alloc() local item_li = tv_list_item_alloc()
item_li.li_tv = item_tv item_li.li_tv = item_tv
eval.tv_list_append(l, item_li) eval.tv_list_append(l, item_li)
end end
@ -533,6 +542,7 @@ return {
typvalt=typvalt, typvalt=typvalt,
li_alloc=li_alloc, li_alloc=li_alloc,
tv_list_item_free=tv_list_item_free,
dict_iter=dict_iter, dict_iter=dict_iter,
list_iter=list_iter, list_iter=list_iter,

View File

@ -41,6 +41,7 @@ local tbl2callback = eval_helpers.tbl2callback
local dict_watchers = eval_helpers.dict_watchers local dict_watchers = eval_helpers.dict_watchers
local concat_tables = global_helpers.concat_tables local concat_tables = global_helpers.concat_tables
local map = global_helpers.map
local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h', local lib = cimport('./src/nvim/eval/typval.h', './src/nvim/memory.h',
'./src/nvim/mbyte.h', './src/nvim/garray.h', './src/nvim/mbyte.h', './src/nvim/garray.h',
@ -80,8 +81,6 @@ local function get_alloc_rets(exp_log, res)
return exp_log return exp_log
end end
local to_cstr_nofree = function(v) return lib.xstrdup(v) end
local alloc_log = alloc_log_new() local alloc_log = alloc_log_new()
before_each(function() before_each(function()
@ -121,87 +120,6 @@ end
describe('typval.c', function() describe('typval.c', function()
describe('list', function() describe('list', function()
describe('item', function() describe('item', function()
describe('alloc()/free()', function()
itp('works', function()
local li = li_alloc(true)
neq(nil, li)
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.freed(li),
})
end)
itp('also frees the value', function()
local li
local s
local l
local tv
li = li_alloc(true)
li.li_tv.v_type = lib.VAR_NUMBER
li.li_tv.vval.v_number = 10
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.freed(li),
})
li = li_alloc(true)
li.li_tv.v_type = lib.VAR_FLOAT
li.li_tv.vval.v_float = 10.5
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.freed(li),
})
li = li_alloc(true)
li.li_tv.v_type = lib.VAR_STRING
li.li_tv.vval.v_string = nil
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.freed(alloc_log.null),
a.freed(li),
})
li = li_alloc(true)
li.li_tv.v_type = lib.VAR_STRING
s = to_cstr_nofree('test')
li.li_tv.vval.v_string = s
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.str(s, #('test')),
a.freed(s),
a.freed(li),
})
li = li_alloc(true)
li.li_tv.v_type = lib.VAR_LIST
l = ffi.gc(list(), nil)
l.lv_refcount = 2
li.li_tv.vval.v_list = l
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.list(l),
a.freed(li),
})
eq(1, l.lv_refcount)
li = li_alloc(true)
tv = lua2typvalt({})
tv.vval.v_dict.dv_refcount = 2
li.li_tv = tv
lib.tv_list_item_free(li)
alloc_log:check({
a.li(li),
a.dict(tv.vval.v_dict),
a.freed(li),
})
eq(1, tv.vval.v_dict.dv_refcount)
end)
end)
describe('remove()', function() describe('remove()', function()
itp('works', function() itp('works', function()
local l = list(1, 2, 3, 4, 5, 6, 7) local l = list(1, 2, 3, 4, 5, 6, 7)
@ -218,24 +136,63 @@ describe('typval.c', function()
a.li(lis[7]), a.li(lis[7]),
}) })
lib.tv_list_item_remove(l, lis[1]) eq(lis[2], lib.tv_list_item_remove(l, lis[1]))
alloc_log:check({ alloc_log:check({
a.freed(table.remove(lis, 1)), a.freed(table.remove(lis, 1)),
}) })
eq(lis, list_items(l)) eq(lis, list_items(l))
lib.tv_list_item_remove(l, lis[6]) eq(lis[7], lib.tv_list_item_remove(l, lis[6]))
alloc_log:check({ alloc_log:check({
a.freed(table.remove(lis)), a.freed(table.remove(lis)),
}) })
eq(lis, list_items(l)) eq(lis, list_items(l))
lib.tv_list_item_remove(l, lis[3]) eq(lis[4], lib.tv_list_item_remove(l, lis[3]))
alloc_log:check({ alloc_log:check({
a.freed(table.remove(lis, 3)), a.freed(table.remove(lis, 3)),
}) })
eq(lis, list_items(l)) eq(lis, list_items(l))
end) end)
itp('also frees the value', function()
local l = list('a', 'b', 'c', 'd')
neq(nil, l)
local lis = list_items(l)
alloc_log:check({
a.list(l),
a.str(lis[1].li_tv.vval.v_string, 1),
a.li(lis[1]),
a.str(lis[2].li_tv.vval.v_string, 1),
a.li(lis[2]),
a.str(lis[3].li_tv.vval.v_string, 1),
a.li(lis[3]),
a.str(lis[4].li_tv.vval.v_string, 1),
a.li(lis[4]),
})
local strings = map(function(li) return li.li_tv.vval.v_string end,
lis)
eq(lis[2], lib.tv_list_item_remove(l, lis[1]))
alloc_log:check({
a.freed(table.remove(strings, 1)),
a.freed(table.remove(lis, 1)),
})
eq(lis, list_items(l))
eq(lis[3], lib.tv_list_item_remove(l, lis[2]))
alloc_log:check({
a.freed(table.remove(strings, 2)),
a.freed(table.remove(lis, 2)),
})
eq(lis, list_items(l))
eq(nil, lib.tv_list_item_remove(l, lis[2]))
alloc_log:check({
a.freed(table.remove(strings, 2)),
a.freed(table.remove(lis, 2)),
})
eq(lis, list_items(l))
end)
itp('works and adjusts watchers correctly', function() itp('works and adjusts watchers correctly', function()
local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil) local l = ffi.gc(list(1, 2, 3, 4, 5, 6, 7), nil)
neq(nil, l) neq(nil, l)
@ -257,19 +214,19 @@ describe('typval.c', function()
a.li(lis[7]), a.li(lis[7]),
}) })
lib.tv_list_item_remove(l, lis[4]) eq(lis[5], lib.tv_list_item_remove(l, lis[4]))
alloc_log:check({a.freed(lis[4])}) alloc_log:check({a.freed(lis[4])})
eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
lib.tv_list_item_remove(l, lis[2]) eq(lis[3], lib.tv_list_item_remove(l, lis[2]))
alloc_log:check({a.freed(lis[2])}) alloc_log:check({a.freed(lis[2])})
eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) eq({lis[1], lis[5], lis[7]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
lib.tv_list_item_remove(l, lis[7]) eq(nil, lib.tv_list_item_remove(l, lis[7]))
alloc_log:check({a.freed(lis[7])}) alloc_log:check({a.freed(lis[7])})
eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) eq({lis[1], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
lib.tv_list_item_remove(l, lis[1]) eq(lis[3], lib.tv_list_item_remove(l, lis[1]))
alloc_log:check({a.freed(lis[1])}) alloc_log:check({a.freed(lis[1])})
eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) eq({lis[3], lis[5], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
@ -449,7 +406,7 @@ describe('typval.c', function()
}) })
end) end)
end) end)
describe('remove_items()', function() describe('drop_items()', function()
itp('works', function() itp('works', function()
local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}) local l_tv = lua2typvalt({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13})
local l = l_tv.vval.v_list local l = l_tv.vval.v_list
@ -462,21 +419,92 @@ describe('typval.c', function()
} }
alloc_log:clear() alloc_log:clear()
lib.tv_list_remove_items(l, lis[1], lis[3]) lib.tv_list_drop_items(l, lis[1], lis[3])
eq({4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, typvalt2lua(l_tv)) eq({4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, typvalt2lua(l_tv))
eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item}) eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
lib.tv_list_remove_items(l, lis[11], lis[13]) lib.tv_list_drop_items(l, lis[11], lis[13])
eq({4, 5, 6, 7, 8, 9, 10}, typvalt2lua(l_tv)) eq({4, 5, 6, 7, 8, 9, 10}, typvalt2lua(l_tv))
eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
lib.tv_list_remove_items(l, lis[6], lis[8]) lib.tv_list_drop_items(l, lis[6], lis[8])
eq({4, 5, 9, 10}, typvalt2lua(l_tv)) eq({4, 5, 9, 10}, typvalt2lua(l_tv))
eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil}) eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
lib.tv_list_drop_items(l, lis[4], lis[10])
eq(empty_list, typvalt2lua(l_tv))
eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil})
lib.tv_list_watch_remove(l, lws[1])
lib.tv_list_watch_remove(l, lws[2])
lib.tv_list_watch_remove(l, lws[3])
alloc_log:check({})
end)
end)
describe('remove_items()', function()
itp('works', function()
local l_tv = lua2typvalt({'1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13'})
local l = l_tv.vval.v_list
local lis = list_items(l)
local strings = map(function(li) return li.li_tv.vval.v_string end, lis)
-- Three watchers: pointing to first, middle and last elements.
local lws = {
list_watch(l, lis[1]),
list_watch(l, lis[7]),
list_watch(l, lis[13]),
}
alloc_log:clear()
lib.tv_list_remove_items(l, lis[1], lis[3])
eq({'4', '5', '6', '7', '8', '9', '10', '11', '12', '13'}, typvalt2lua(l_tv))
eq({lis[4], lis[7], lis[13]}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item})
alloc_log:check({
a.freed(strings[1]),
a.freed(lis[1]),
a.freed(strings[2]),
a.freed(lis[2]),
a.freed(strings[3]),
a.freed(lis[3]),
})
lib.tv_list_remove_items(l, lis[11], lis[13])
eq({'4', '5', '6', '7', '8', '9', '10'}, typvalt2lua(l_tv))
eq({lis[4], lis[7], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
alloc_log:check({
a.freed(strings[11]),
a.freed(lis[11]),
a.freed(strings[12]),
a.freed(lis[12]),
a.freed(strings[13]),
a.freed(lis[13]),
})
lib.tv_list_remove_items(l, lis[6], lis[8])
eq({'4', '5', '9', '10'}, typvalt2lua(l_tv))
eq({lis[4], lis[9], nil}, {lws[1].lw_item, lws[2].lw_item, lws[3].lw_item == nil and nil})
alloc_log:check({
a.freed(strings[6]),
a.freed(lis[6]),
a.freed(strings[7]),
a.freed(lis[7]),
a.freed(strings[8]),
a.freed(lis[8]),
})
lib.tv_list_remove_items(l, lis[4], lis[10]) lib.tv_list_remove_items(l, lis[4], lis[10])
eq(empty_list, typvalt2lua(l_tv)) eq(empty_list, typvalt2lua(l_tv))
eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil}) eq({true, true, true}, {lws[1].lw_item == nil, lws[2].lw_item == nil, lws[3].lw_item == nil})
alloc_log:check({
a.freed(strings[4]),
a.freed(lis[4]),
a.freed(strings[5]),
a.freed(lis[5]),
a.freed(strings[9]),
a.freed(lis[9]),
a.freed(strings[10]),
a.freed(lis[10]),
})
lib.tv_list_watch_remove(l, lws[1]) lib.tv_list_watch_remove(l, lws[1])
lib.tv_list_watch_remove(l, lws[2]) lib.tv_list_watch_remove(l, lws[2])
@ -678,6 +706,66 @@ describe('typval.c', function()
eq({int(-100500), int(100500)}, typvalt2lua(l_tv)) eq({int(-100500), int(100500)}, typvalt2lua(l_tv))
end) end)
end) end)
describe('tv()', function()
itp('works', function()
local l_tv = lua2typvalt(empty_list)
local l = l_tv.vval.v_list
local l_l_tv = lua2typvalt(empty_list)
alloc_log:clear()
local l_l = l_l_tv.vval.v_list
eq(1, l_l.lv_refcount)
lib.tv_list_append_tv(l, l_l_tv)
eq(2, l_l.lv_refcount)
eq(l_l, l.lv_first.li_tv.vval.v_list)
alloc_log:check({
a.li(l.lv_first),
})
local l_s_tv = lua2typvalt('test')
alloc_log:check({
a.str(l_s_tv.vval.v_string, 'test'),
})
lib.tv_list_append_tv(l, l_s_tv)
alloc_log:check({
a.li(l.lv_last),
a.str(l.lv_last.li_tv.vval.v_string, 'test'),
})
eq({empty_list, 'test'}, typvalt2lua(l_tv))
end)
end)
describe('owned tv()', function()
itp('works', function()
local l_tv = lua2typvalt(empty_list)
local l = l_tv.vval.v_list
local l_l_tv = lua2typvalt(empty_list)
alloc_log:clear()
local l_l = l_l_tv.vval.v_list
eq(1, l_l.lv_refcount)
lib.tv_list_append_owned_tv(l, l_l_tv)
eq(1, l_l.lv_refcount)
l_l.lv_refcount = l_l.lv_refcount + 1
eq(l_l, l.lv_first.li_tv.vval.v_list)
alloc_log:check({
a.li(l.lv_first),
})
local l_s_tv = ffi.gc(lua2typvalt('test'), nil)
alloc_log:check({
a.str(l_s_tv.vval.v_string, 'test'),
})
lib.tv_list_append_owned_tv(l, l_s_tv)
eq(l_s_tv.vval.v_string, l.lv_last.li_tv.vval.v_string)
l_s_tv.vval.v_string = nil
alloc_log:check({
a.li(l.lv_last),
})
eq({empty_list, 'test'}, typvalt2lua(l_tv))
end)
end)
end) end)
describe('copy()', function() describe('copy()', function()
local function tv_list_copy(...) local function tv_list_copy(...)

View File

@ -650,8 +650,6 @@ local function itp_child(wr, func)
collectgarbage('stop') collectgarbage('stop')
child_sethook(wr) child_sethook(wr)
err, emsg = pcall(func) err, emsg = pcall(func)
collectgarbage('restart')
collectgarbage()
debug.sethook() debug.sethook()
end end
emsg = tostring(emsg) emsg = tostring(emsg)
@ -662,14 +660,15 @@ local function itp_child(wr, func)
end end
sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg)) sc.write(wr, ('-\n%05u\n%s'):format(#emsg, emsg))
deinit() deinit()
sc.close(wr)
sc.exit(1)
else else
sc.write(wr, '+\n') sc.write(wr, '+\n')
deinit() deinit()
sc.close(wr)
sc.exit(0)
end end
collectgarbage('restart')
collectgarbage()
sc.write(wr, '$\n')
sc.close(wr)
sc.exit(err and 0 or 1)
end end
local function check_child_err(rd) local function check_child_err(rd)
@ -690,36 +689,18 @@ local function check_child_err(rd)
break break
end end
trace[#trace + 1] = traceline trace[#trace + 1] = traceline
table.remove(trace, maxtrace + 1) if #trace > maxtrace then
table.remove(trace, 1)
end
end end
local res = sc.read(rd, 2) local res = sc.read(rd, 2)
if #res ~= 2 then if #res == 2 then
local error local err = ''
if #trace == 0 then if res ~= '+\n' then
error = '\nTest crashed, no trace available\n'
else
error = '\nTest crashed, trace:\n' .. tracehelp
for i = 1, #trace do
error = error .. trace[i]
end
end
if not did_traceline then
error = error .. '\nNo end of trace occurred'
end
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
if not cc_err then
error = error .. '\ncheck_cores failed: ' .. cc_emsg
end
assert.just_fail(error)
end
if res == '+\n' then
return
end
eq('-\n', res) eq('-\n', res)
local len_s = sc.read(rd, 5) local len_s = sc.read(rd, 5)
local len = tonumber(len_s) local len = tonumber(len_s)
neq(0, len) neq(0, len)
local err = ''
if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then
err = '\nTest failed, trace:\n' .. tracehelp err = '\nTest failed, trace:\n' .. tracehelp
for _, traceline in ipairs(trace) do for _, traceline in ipairs(trace) do
@ -727,8 +708,30 @@ local function check_child_err(rd)
end end
end end
err = err .. sc.read(rd, len + 1) err = err .. sc.read(rd, len + 1)
end
local eres = sc.read(rd, 2)
if eres ~= '$\n' then
if #trace == 0 then
err = '\nTest crashed, no trace available\n'
else
err = '\nTest crashed, trace:\n' .. tracehelp
for i = 1, #trace do
err = err .. trace[i]
end
end
if not did_traceline then
err = err .. '\nNo end of trace occurred'
end
local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true)
if not cc_err then
err = err .. '\ncheck_cores failed: ' .. cc_emsg
end
end
if err ~= '' then
assert.just_fail(err) assert.just_fail(err)
end end
end
end
local function itp_parent(rd, pid, allow_failure) local function itp_parent(rd, pid, allow_failure)
local err, emsg = pcall(check_child_err, rd) local err, emsg = pcall(check_child_err, rd)