aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/typval.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval/typval.c')
-rw-r--r--src/nvim/eval/typval.c99
1 files changed, 95 insertions, 4 deletions
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index ac6c8c8aa6..c8b550f902 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -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;