aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJames McCoy <jamessan@jamessan.com>2016-12-12 10:53:33 -0500
committerGitHub <noreply@github.com>2016-12-12 10:53:32 -0500
commit988ab5804a16beafff26b487b0612cb7cf97ea09 (patch)
treee31b25acaf30023616d4f469af5d067befd30556 /src
parent75c18b6aaa8430596fa10466dc7918047b13ff2b (diff)
parent5e4eb18eb0242794c0b3a622f7acf0d3e6856c05 (diff)
downloadrneovim-988ab5804a16beafff26b487b0612cb7cf97ea09.tar.gz
rneovim-988ab5804a16beafff26b487b0612cb7cf97ea09.tar.bz2
rneovim-988ab5804a16beafff26b487b0612cb7cf97ea09.zip
Merge pull request #5529 from brcolow/vim-7.4.1559
Port partial patches from vim
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/private/helpers.c10
-rw-r--r--src/nvim/api/vim.c2
-rw-r--r--src/nvim/eval.c1383
-rw-r--r--src/nvim/eval.lua2
-rw-r--r--src/nvim/eval/encode.c67
-rw-r--r--src/nvim/eval/typval_encode.h9
-rw-r--r--src/nvim/eval_defs.h15
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/normal.c2
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/testdir/test_alot.vim1
-rw-r--r--src/nvim/testdir/test_expr.vim22
-rw-r--r--src/nvim/testdir/test_partial.vim330
-rw-r--r--src/nvim/testdir/test_timers.vim23
-rw-r--r--src/nvim/testdir/test_viml.vim81
-rw-r--r--src/nvim/version.c59
16 files changed, 1599 insertions, 411 deletions
diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c
index 72db7c0782..b004cfc7a1 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -360,6 +360,9 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
TYPVAL_ENCODE_CONV_NIL()
+#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
+ TYPVAL_ENCODE_CONV_NIL()
+
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
@@ -482,6 +485,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
#undef TYPVAL_ENCODE_CONV_NUMBER
#undef TYPVAL_ENCODE_CONV_FLOAT
#undef TYPVAL_ENCODE_CONV_FUNC
+#undef TYPVAL_ENCODE_CONV_PARTIAL
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
#undef TYPVAL_ENCODE_CONV_LIST_START
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
@@ -653,7 +657,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
if (!object_to_vim(item, &li->li_tv, err)) {
// cleanup
listitem_free(li);
- list_free(list, true);
+ list_free(list);
return false;
}
@@ -677,7 +681,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
api_set_error(err, Validation,
_("Empty dictionary keys aren't allowed"));
// cleanup
- dict_free(dict, true);
+ dict_free(dict);
return false;
}
@@ -686,7 +690,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
if (!object_to_vim(item.value, &di->di_tv, err)) {
// cleanup
dictitem_free(di);
- dict_free(dict, true);
+ dict_free(dict);
return false;
}
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c
index e59e955aed..b17b59f7a6 100644
--- a/src/nvim/api/vim.c
+++ b/src/nvim/api/vim.c
@@ -225,7 +225,7 @@ Object nvim_call_function(String fname, Array args, Error *err)
&rettv, (int) args.size, vim_args,
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
true,
- NULL);
+ NULL, NULL);
if (r == FAIL) {
api_set_error(err, Exception, _("Error calling function."));
}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 2a2f94f629..a205c37d6e 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -217,9 +217,11 @@ static int echo_attr = 0; /* attributes used for ":echo" */
/* The names of packages that once were loaded are remembered. */
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
-/* list heads for garbage collection */
-static dict_T *first_dict = NULL; /* list of all dicts */
-static list_T *first_list = NULL; /* list of all lists */
+// List heads for garbage collection. Although there can be a reference loop
+// from partial to dict to partial, we don't need to keep track of the partial,
+// since it will get freed when the dict is unused and gets freed.
+static dict_T *first_dict = NULL; // list of all dicts
+static list_T *first_list = NULL; // list of all lists
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
@@ -411,6 +413,21 @@ static struct vimvar {
static dictitem_T vimvars_var; // variable used for v:
#define vimvarht vimvardict.dv_hashtab
+typedef enum {
+ kCallbackNone,
+ kCallbackFuncref,
+ kCallbackPartial,
+} CallbackType;
+
+typedef struct {
+ union {
+ char_u *funcref;
+ partial_T *partial;
+ } data;
+ CallbackType type;
+} Callback;
+#define CALLBACK_NONE ((Callback){ .type = kCallbackNone })
+
typedef struct {
union {
LibuvProcess uv;
@@ -422,15 +439,14 @@ typedef struct {
bool exited;
bool rpc;
int refcount;
- ufunc_T *on_stdout, *on_stderr, *on_exit;
- dict_T *self;
+ Callback on_stdout, on_stderr, on_exit;
int *status_ptr;
uint64_t id;
MultiQueue *events;
} TerminalJobData;
typedef struct dict_watcher {
- ufunc_T *callback;
+ Callback callback;
char *key_pattern;
QUEUE node;
bool busy; // prevent recursion if the dict is changed in the callback
@@ -438,7 +454,7 @@ typedef struct dict_watcher {
typedef struct {
TerminalJobData *data;
- ufunc_T *callback;
+ Callback *callback;
const char *type;
list_T *received;
int status;
@@ -451,7 +467,7 @@ typedef struct {
int refcount;
long timeout;
bool stopped;
- ufunc_T *callback;
+ Callback callback;
} timer_T;
typedef void (*FunPtr)(void);
@@ -1208,7 +1224,7 @@ int call_vim_function(
rettv->v_type = VAR_UNKNOWN; /* clear_tv() uses this */
ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &doesrange, true, NULL);
+ &doesrange, true, NULL, NULL);
if (safe) {
--sandbox;
restore_funccal(save_funccalp);
@@ -2455,6 +2471,7 @@ static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
switch (tv1->v_type) {
case VAR_DICT:
case VAR_FUNC:
+ case VAR_PARTIAL:
case VAR_SPECIAL:
break;
@@ -2756,8 +2773,9 @@ void ex_call(exarg_T *eap)
typval_T rettv;
linenr_T lnum;
int doesrange;
- int failed = FALSE;
+ bool failed = false;
funcdict_T fudi;
+ partial_T *partial = NULL;
if (eap->skip) {
/* trans_function_name() doesn't work well when skipping, use eval0()
@@ -2770,7 +2788,7 @@ void ex_call(exarg_T *eap)
return;
}
- tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi);
+ tofree = trans_function_name(&arg, eap->skip, TFN_INT, &fudi, &partial);
if (fudi.fd_newkey != NULL) {
/* Still need to give an error message for missing key. */
EMSG2(_(e_dictkey), fudi.fd_newkey);
@@ -2784,9 +2802,12 @@ void ex_call(exarg_T *eap)
if (fudi.fd_dict != NULL)
++fudi.fd_dict->dv_refcount;
- /* If it is the name of a variable of type VAR_FUNC use its contents. */
+ // If it is the name of a variable of type VAR_FUNC or VAR_PARTIAL use its
+ // contents. For VAR_PARTIAL get its partial, unless we already have one
+ // from trans_function_name().
len = (int)STRLEN(tofree);
- name = deref_func_name(tofree, &len, FALSE);
+ name = deref_func_name(tofree, &len,
+ partial != NULL ? NULL : &partial, false);
/* Skip white space to allow ":call func ()". Not good, but required for
* backward compatibility. */
@@ -2817,9 +2838,9 @@ void ex_call(exarg_T *eap)
}
arg = startarg;
if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg,
- eap->line1, eap->line2, &doesrange,
- !eap->skip, fudi.fd_dict) == FAIL) {
- failed = TRUE;
+ eap->line1, eap->line2, &doesrange,
+ !eap->skip, partial, fudi.fd_dict) == FAIL) {
+ failed = true;
break;
}
@@ -3182,6 +3203,7 @@ static void item_lock(typval_T *tv, int deep, int lock)
case VAR_FLOAT:
case VAR_STRING:
case VAR_FUNC:
+ case VAR_PARTIAL:
case VAR_SPECIAL:
break;
case VAR_UNKNOWN:
@@ -3754,26 +3776,37 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate)
if (type == TYPE_NEQUAL)
n1 = !n1;
}
- } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC) {
- if (rettv->v_type != var2.v_type
- || (type != TYPE_EQUAL && type != TYPE_NEQUAL)) {
- if (rettv->v_type != var2.v_type)
- EMSG(_("E693: Can only compare Funcref with Funcref"));
- else
- EMSG(_("E694: Invalid operation for Funcrefs"));
+ } else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC
+ || rettv->v_type == VAR_PARTIAL
+ || var2.v_type == VAR_PARTIAL) {
+ if (type != TYPE_EQUAL && type != TYPE_NEQUAL) {
+ EMSG(_("E694: Invalid operation for Funcrefs"));
clear_tv(rettv);
clear_tv(&var2);
return FAIL;
+ }
+ if ((rettv->v_type == VAR_PARTIAL
+ && rettv->vval.v_partial == NULL)
+ || (var2.v_type == VAR_PARTIAL
+ && var2.vval.v_partial == NULL)) {
+ // when a partial is NULL assume not equal
+ n1 = false;
+ } else if (type_is) {
+ if (rettv->v_type == VAR_FUNC && var2.v_type == VAR_FUNC) {
+ // strings are considered the same if their value is
+ // the same
+ n1 = tv_equal(rettv, &var2, ic, false);
+ } else if (rettv->v_type == VAR_PARTIAL
+ && var2.v_type == VAR_PARTIAL) {
+ n1 = (rettv->vval.v_partial == var2.vval.v_partial);
+ } else {
+ n1 = false;
+ }
} else {
- /* Compare two Funcrefs for being equal or unequal. */
- if (rettv->vval.v_string == NULL
- || var2.vval.v_string == NULL)
- n1 = FALSE;
- else
- n1 = STRCMP(rettv->vval.v_string,
- var2.vval.v_string) == 0;
- if (type == TYPE_NEQUAL)
- n1 = !n1;
+ n1 = tv_equal(rettv, &var2, ic, false);
+ }
+ if (type == TYPE_NEQUAL) {
+ n1 = !n1;
}
}
/*
@@ -4308,14 +4341,15 @@ static int eval7(
ret = FAIL;
} else {
if (**arg == '(') { // recursive!
+ partial_T *partial;
// If "s" is the name of a variable of type VAR_FUNC
// use its contents.
- s = deref_func_name(s, &len, !evaluate);
+ s = deref_func_name(s, &len, &partial, !evaluate);
// Invoke the function.
ret = get_func_tv(s, len, rettv, arg,
- curwin->w_cursor.lnum, curwin->w_cursor.lnum,
- &len, evaluate, NULL);
+ curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+ &len, evaluate, partial, NULL);
// If evaluate is false rettv->v_type was not set in
// get_func_tv, but it's needed in handle_subscript() to parse
@@ -4418,7 +4452,8 @@ eval_index (
char_u *key = NULL;
switch (rettv->v_type) {
- case VAR_FUNC: {
+ case VAR_FUNC:
+ case VAR_PARTIAL: {
if (verbose) {
EMSG(_("E695: Cannot index a Funcref"));
}
@@ -4638,6 +4673,7 @@ eval_index (
break;
case VAR_SPECIAL:
case VAR_FUNC:
+ case VAR_PARTIAL:
case VAR_FLOAT:
case VAR_UNKNOWN:
break; // Not evaluating, skipping over subscript
@@ -4891,10 +4927,29 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
return OK;
}
-/*
- * Allocate a variable for a List and fill it from "*arg".
- * Return OK or FAIL.
- */
+static void partial_free(partial_T *pt)
+{
+ for (int i = 0; i < pt->pt_argc; i++) {
+ clear_tv(&pt->pt_argv[i]);
+ }
+ xfree(pt->pt_argv);
+ dict_unref(pt->pt_dict);
+ func_unref(pt->pt_name);
+ xfree(pt->pt_name);
+ xfree(pt);
+}
+
+/// Unreference a closure: decrement the reference count and free it when it
+/// becomes zero.
+void partial_unref(partial_T *pt)
+{
+ if (pt != NULL && --pt->pt_refcount <= 0) {
+ partial_free(pt);
+ }
+}
+
+/// Allocate a variable for a List and fill it from "*arg".
+/// Return OK or FAIL.
static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
{
list_T *l = NULL;
@@ -4928,8 +4983,9 @@ static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate)
if (**arg != ']') {
EMSG2(_("E697: Missing end of List ']': %s"), *arg);
failret:
- if (evaluate)
- list_free(l, TRUE);
+ if (evaluate) {
+ list_free(l);
+ }
return FAIL;
}
@@ -4973,47 +5029,48 @@ static list_T *rettv_list_alloc(typval_T *rettv)
return l;
}
-/*
- * Unreference a list: decrement the reference count and free it when it
- * becomes zero.
- */
-void list_unref(list_T *l)
-{
- if (l != NULL && --l->lv_refcount <= 0)
- list_free(l, TRUE);
+/// Unreference a list: decrement the reference count and free it when it
+/// becomes zero.
+void list_unref(list_T *l) {
+ if (l != NULL && --l->lv_refcount <= 0) {
+ list_free(l);
+ }
}
-/*
- * Free a list, including all items it points to.
- * Ignores the reference count.
- */
-void
-list_free (
- list_T *l,
- int recurse /* Free Lists and Dictionaries recursively. */
-)
-{
+/// Free a list, including all items it points to.
+/// Ignores the reference count.
+static void list_free_contents(list_T *l) {
listitem_T *item;
- /* Remove the list from the list of lists for garbage collection. */
- if (l->lv_used_prev == NULL)
- first_list = l->lv_used_next;
- else
- l->lv_used_prev->lv_used_next = l->lv_used_next;
- if (l->lv_used_next != NULL)
- l->lv_used_next->lv_used_prev = l->lv_used_prev;
-
for (item = l->lv_first; item != NULL; item = l->lv_first) {
- /* Remove the item before deleting it. */
+ // Remove the item before deleting it.
l->lv_first = item->li_next;
- if (recurse || (item->li_tv.v_type != VAR_LIST
- && item->li_tv.v_type != VAR_DICT))
- clear_tv(&item->li_tv);
+ clear_tv(&item->li_tv);
xfree(item);
}
+}
+
+static void list_free_list(list_T *l) {
+ // Remove the list from the list of lists for garbage collection.
+ if (l->lv_used_prev == NULL) {
+ first_list = l->lv_used_next;
+ } else {
+ l->lv_used_prev->lv_used_next = l->lv_used_next;
+ }
+ if (l->lv_used_next != NULL) {
+ l->lv_used_next->lv_used_prev = l->lv_used_prev;
+ }
+
xfree(l);
}
+void list_free(list_T *l) {
+ if (!in_free_unref_items) {
+ list_free_contents(l);
+ list_free_list(l);
+ }
+}
+
/*
* Allocate a list item.
* It is not initialized, don't forget to set v_lock.
@@ -5125,6 +5182,60 @@ dict_equal (
static int tv_equal_recurse_limit;
+static bool func_equal(
+ typval_T *tv1,
+ typval_T *tv2,
+ bool ic // ignore case
+) {
+ char_u *s1, *s2;
+ dict_T *d1, *d2;
+ int a1, a2;
+
+ // empty and NULL function name considered the same
+ s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
+ : tv1->vval.v_partial->pt_name;
+ if (s1 != NULL && *s1 == NUL) {
+ s1 = NULL;
+ }
+ s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
+ : tv2->vval.v_partial->pt_name;
+ if (s2 != NULL && *s2 == NUL) {
+ s2 = NULL;
+ }
+ if (s1 == NULL || s2 == NULL) {
+ if (s1 != s2) {
+ return false;
+ }
+ } else if (STRCMP(s1, s2) != 0) {
+ return false;
+ }
+
+ // empty dict and NULL dict is different
+ d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
+ d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
+ if (d1 == NULL || d2 == NULL) {
+ if (d1 != d2) {
+ return false;
+ }
+ } else if (!dict_equal(d1, d2, ic, true)) {
+ return false;
+ }
+
+ // empty list and no list considered the same
+ a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
+ a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
+ if (a1 != a2) {
+ return false;
+ }
+ for (int i = 0; i < a1; i++) {
+ if (!tv_equal(tv1->vval.v_partial->pt_argv + i,
+ tv2->vval.v_partial->pt_argv + i, ic, true)) {
+ return false;
+ }
+ }
+ return true;
+}
+
/*
* Return TRUE if "tv1" and "tv2" have the same value.
* Compares the items just like "==" would compare them, but strings and
@@ -5143,9 +5254,6 @@ tv_equal (
static int recursive_cnt = 0; /* catch recursive loops */
int r;
- if (tv1->v_type != tv2->v_type)
- return FALSE;
-
/* Catch lists and dicts that have an endless loop by limiting
* recursiveness to a limit. We guess they are equal then.
* A fixed limit has the problem of still taking an awful long time.
@@ -5159,6 +5267,20 @@ tv_equal (
return TRUE;
}
+ // For VAR_FUNC and VAR_PARTIAL only compare the function name.
+ if ((tv1->v_type == VAR_FUNC
+ || (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
+ && (tv2->v_type == VAR_FUNC
+ || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL))) {
+ recursive_cnt++;
+ r = func_equal(tv1, tv2, ic);
+ recursive_cnt--;
+ return r;
+ }
+ if (tv1->v_type != tv2->v_type) {
+ return false;
+ }
+
switch (tv1->v_type) {
case VAR_LIST:
++recursive_cnt;
@@ -5172,11 +5294,6 @@ tv_equal (
--recursive_cnt;
return r;
- case VAR_FUNC:
- return tv1->vval.v_string != NULL
- && tv2->vval.v_string != NULL
- && STRCMP(tv1->vval.v_string, tv2->vval.v_string) == 0;
-
case VAR_NUMBER:
return tv1->vval.v_number == tv2->vval.v_number;
@@ -5191,6 +5308,8 @@ tv_equal (
case VAR_SPECIAL:
return tv1->vval.v_special == tv2->vval.v_special;
+ case VAR_FUNC:
+ case VAR_PARTIAL:
case VAR_UNKNOWN:
// VAR_UNKNOWN can be the result of an invalid expression, let’s say it does
// not equal anything, not even self.
@@ -5804,7 +5923,17 @@ bool garbage_collect(void)
{
TerminalJobData *data;
map_foreach_value(jobs, data, {
- ABORTING(set_ref_dict)(data->self, copyID);
+ set_ref_in_callback(&data->on_stdout, copyID, NULL, NULL);
+ set_ref_in_callback(&data->on_stderr, copyID, NULL, NULL);
+ set_ref_in_callback(&data->on_exit, copyID, NULL, NULL);
+ })
+ }
+
+ // Timers
+ {
+ timer_T *timer;
+ map_foreach_value(timers, timer, {
+ set_ref_in_callback(&timer->callback, copyID, NULL, NULL);
})
}
@@ -5880,42 +6009,63 @@ bool garbage_collect(void)
/// @return true, if something was freed.
static int free_unref_items(int copyID)
{
+ dict_T *dd, *dd_next;
+ list_T *ll, *ll_next;
bool did_free = false;
+ // Let all "free" functions know that we are here. This means no
+ // dictionaries, lists, or jobs are to be freed, because we will
+ // do that here.
+ in_free_unref_items = true;
+
+ // PASS 1: free the contents of the items. We don't free the items
+ // themselves yet, so that it is possible to decrement refcount counters.
+
// Go through the list of dicts and free items without the copyID.
// Don't free dicts that are referenced internally.
- for (dict_T *dd = first_dict; dd != NULL; ) {
+ for (dict_T *dd = first_dict; dd != NULL; dd = dd->dv_used_next) {
if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) {
// Free the Dictionary and ordinary items it contains, but don't
// recurse into Lists and Dictionaries, they will be in the list
- // of dicts or list of lists. */
- dict_T *dd_next = dd->dv_used_next;
- dict_free(dd, FALSE);
+ // of dicts or list of lists.
+ dict_free_contents(dd);
did_free = true;
- dd = dd_next;
- } else {
- dd = dd->dv_used_next;
}
}
// Go through the list of lists and free items without the copyID.
// But don't free a list that has a watcher (used in a for loop), these
// are not referenced anywhere.
- for (list_T *ll = first_list; ll != NULL;) {
+ for (list_T *ll = first_list; ll != NULL; ll = ll->lv_used_next) {
if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
&& ll->lv_watch == NULL) {
// Free the List and ordinary items it contains, but don't recurse
// into Lists and Dictionaries, they will be in the list of dicts
// or list of lists.
- list_T* ll_next = ll->lv_used_next;
- list_free(ll, FALSE);
+ list_free_contents(ll);
did_free = true;
- ll = ll_next;
- } else {
- ll = ll->lv_used_next;
}
}
+ // PASS 2: free the items themselves.
+ for (dd = first_dict; dd != NULL; dd = dd_next) {
+ dd_next = dd->dv_used_next;
+ if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) {
+ dict_free_dict(dd);
+ }
+ }
+
+ for (ll = first_list; ll != NULL; ll = ll_next) {
+ ll_next = ll->lv_used_next;
+ if ((ll->lv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)
+ && ll->lv_watch == NULL) {
+ // Free the List and ordinary items it contains, but don't recurse
+ // into Lists and Dictionaries, they will be in the list of dicts
+ // or list of lists.
+ list_free_list(ll);
+ }
+ }
+ in_free_unref_items = false;
return did_free;
}
@@ -6033,6 +6183,24 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
*ht_stack = newitem;
}
}
+
+ QUEUE *w = NULL;
+ DictWatcher *watcher = NULL;
+ QUEUE_FOREACH(w, &dd->watchers) {
+ watcher = dictwatcher_node_data(w);
+ set_ref_in_callback(&watcher->callback, copyID, ht_stack, list_stack);
+ }
+ }
+ if (tv->v_type == VAR_PARTIAL) {
+ partial_T *pt = tv->vval.v_partial;
+ int i;
+
+ if (pt != NULL) {
+ for (i = 0; i < pt->pt_argc; i++) {
+ abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
+ ht_stack, list_stack);
+ }
+ }
}
break;
}
@@ -6058,6 +6226,26 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
break;
}
+ case VAR_PARTIAL: {
+ partial_T *pt = tv->vval.v_partial;
+
+ // A partial does not have a copyID, because it cannot contain itself.
+ if (pt != NULL) {
+ if (pt->pt_dict != NULL) {
+ typval_T dtv;
+
+ dtv.v_type = VAR_DICT;
+ dtv.vval.v_dict = pt->pt_dict;
+ abort = abort || set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
+ }
+
+ for (int i = 0; i < pt->pt_argc; i++) {
+ abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
+ ht_stack, list_stack);
+ }
+ }
+ break;
+ }
case VAR_FUNC:
case VAR_UNKNOWN:
case VAR_SPECIAL:
@@ -6181,31 +6369,18 @@ void dict_clear(dict_T *d)
*/
void dict_unref(dict_T *d)
{
- if (d != NULL && --d->dv_refcount <= 0)
- dict_free(d, TRUE);
+ if (d != NULL && --d->dv_refcount <= 0) {
+ dict_free(d);
+ }
}
-/*
- * Free a Dictionary, including all items it contains.
- * Ignores the reference count.
- */
-void
-dict_free (
- dict_T *d,
- int recurse /* Free Lists and Dictionaries recursively. */
-)
-{
+/// Free a Dictionary, including all items it contains.
+/// Ignores the reference count.
+static void dict_free_contents(dict_T *d) {
int todo;
hashitem_T *hi;
dictitem_T *di;
- /* Remove the dict from the list of dicts for garbage collection. */
- if (d->dv_used_prev == NULL)
- first_dict = d->dv_used_next;
- else
- d->dv_used_prev->dv_used_next = d->dv_used_next;
- if (d->dv_used_next != NULL)
- d->dv_used_next->dv_used_prev = d->dv_used_prev;
/* Lock the hashtab, we don't want it to resize while freeing items. */
hash_lock(&d->dv_hashtab);
@@ -6217,9 +6392,7 @@ dict_free (
* something recursive causing trouble. */
di = HI2DI(hi);
hash_remove(&d->dv_hashtab, hi);
- if (recurse || (di->di_tv.v_type != VAR_LIST
- && di->di_tv.v_type != VAR_DICT))
- clear_tv(&di->di_tv);
+ clear_tv(&di->di_tv);
xfree(di);
--todo;
}
@@ -6233,9 +6406,29 @@ dict_free (
}
hash_clear(&d->dv_hashtab);
+}
+
+static void dict_free_dict(dict_T *d) {
+ // Remove the dict from the list of dicts for garbage collection.
+ if (d->dv_used_prev == NULL) {
+ first_dict = d->dv_used_next;
+ } else {
+ d->dv_used_prev->dv_used_next = d->dv_used_next;
+ }
+ if (d->dv_used_next != NULL) {
+ d->dv_used_next->dv_used_prev = d->dv_used_prev;
+ }
+
xfree(d);
}
+void dict_free(dict_T *d) {
+ if (!in_free_unref_items) {
+ dict_free_contents(d);
+ dict_free_dict(d);
+ }
+}
+
/*
* Allocate a Dictionary item.
* The "key" is copied to the new item.
@@ -6477,49 +6670,28 @@ dictitem_T *dict_find(dict_T *d, char_u *key, int len)
/// @param[out] result The address where a pointer to the wanted callback
/// will be left.
/// @return true/false on success/failure.
-static bool get_dict_callback(dict_T *d, char *key, ufunc_T **result)
+static bool get_dict_callback(dict_T *d, char *key, Callback *result)
{
dictitem_T *di = dict_find(d, (uint8_t *)key, -1);
if (di == NULL) {
- *result = NULL;
+ result->type = kCallbackNone;
return true;
}
-
- if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING) {
- EMSG(_("Argument is not a function or function name"));
- *result = NULL;
- return false;
- }
- if ((*result = find_ufunc(di->di_tv.vval.v_string)) == NULL) {
+ if (di->di_tv.v_type != VAR_FUNC && di->di_tv.v_type != VAR_STRING
+ && di->di_tv.v_type != VAR_PARTIAL) {
+ EMSG(_("Argument is not a function or function name"));
+ result->type = kCallbackNone;
return false;
}
- (*result)->uf_refcount++;
- return true;
-}
-
-static ufunc_T *find_ufunc(uint8_t *name)
-{
- uint8_t *n = name;
- ufunc_T *rv = NULL;
- if (*n > '9' || *n < '0') {
- if ((n = trans_function_name(&n, false, TFN_INT|TFN_QUIET, NULL))) {
- rv = find_func(n);
- xfree(n);
- }
- } else {
- // dict function, name is already translated
- rv = find_func(n);
- }
-
- if (!rv) {
- EMSG2(_("Function %s doesn't exist"), name);
- return NULL;
- }
-
- return rv;
+ typval_T tv;
+ copy_tv(&di->di_tv, &tv);
+ set_selfdict(&tv, d);
+ bool res = callback_from_typval(result, &tv);
+ clear_tv(&tv);
+ return res;
}
/// Get a string item from a dictionary.
@@ -6642,8 +6814,9 @@ static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
if (**arg != '}') {
EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
failret:
- if (evaluate)
- dict_free(d, TRUE);
+ if (evaluate) {
+ dict_free(d);
+ }
return FAIL;
}
@@ -6794,14 +6967,20 @@ static VimLFuncDef *find_internal_func(const char *const name)
return find_internal_func_gperf(name, len);
}
-/*
- * Check if "name" is a variable of type VAR_FUNC. If so, return the function
- * name it contains, otherwise return "name".
- */
-static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload)
+/// Check if "name" is a variable of type VAR_FUNC. If so, return the function
+/// name it contains, otherwise return "name".
+/// If "partialp" is not NULL, and "name" is of type VAR_PARTIAL also set
+/// "partialp".
+static char_u *deref_func_name(
+ char_u *name, int *lenp,
+ partial_T **partialp, bool no_autoload
+)
{
dictitem_T *v;
int cc;
+ if (partialp != NULL) {
+ *partialp = NULL;
+ }
cc = name[*lenp];
name[*lenp] = NUL;
@@ -6816,6 +6995,20 @@ static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload)
return v->di_tv.vval.v_string;
}
+ if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
+ partial_T *pt = v->di_tv.vval.v_partial;
+
+ if (pt == NULL) {
+ *lenp = 0;
+ return (char_u *)""; // just in case
+ }
+ if (partialp != NULL) {
+ *partialp = pt;
+ }
+ *lenp = (int)STRLEN(pt->pt_name);
+ return pt->pt_name;
+ }
+
return name;
}
@@ -6833,7 +7026,8 @@ get_func_tv (
linenr_T lastline, /* last line of range */
int *doesrange, /* return: function handled range */
int evaluate,
- dict_T *selfdict /* Dictionary for "self" */
+ partial_T *partial, // for extra arguments
+ dict_T *selfdict // Dictionary for "self"
)
{
char_u *argp;
@@ -6845,10 +7039,11 @@ get_func_tv (
* Get the arguments.
*/
argp = *arg;
- while (argcount < MAX_FUNC_ARGS) {
- argp = skipwhite(argp + 1); /* skip the '(' or ',' */
- if (*argp == ')' || *argp == ',' || *argp == NUL)
+ while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc)) {
+ argp = skipwhite(argp + 1); // skip the '(' or ','
+ if (*argp == ')' || *argp == ',' || *argp == NUL) {
break;
+ }
if (eval1(&argp, &argvars[argcount], evaluate) == FAIL) {
ret = FAIL;
break;
@@ -6862,14 +7057,16 @@ get_func_tv (
else
ret = FAIL;
- if (ret == OK)
+ if (ret == OK) {
ret = call_func(name, len, rettv, argcount, argvars,
- firstline, lastline, doesrange, evaluate, selfdict);
- else if (!aborting()) {
- if (argcount == MAX_FUNC_ARGS)
+ firstline, lastline, doesrange, evaluate,
+ partial, selfdict);
+ } else if (!aborting()) {
+ if (argcount == MAX_FUNC_ARGS) {
emsg_funcname(N_("E740: Too many arguments for function %s"), name);
- else
+ } else {
emsg_funcname(N_("E116: Invalid arguments for function %s"), name);
+ }
}
while (--argcount >= 0)
@@ -6879,28 +7076,6 @@ get_func_tv (
return ret;
}
-
-/*
- * Call a function with its resolved parameters
- * Return FAIL when the function can't be called, OK otherwise.
- * Als