diff options
| author | James McCoy <jamessan@jamessan.com> | 2016-12-12 10:53:33 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-12-12 10:53:32 -0500 |
| commit | 988ab5804a16beafff26b487b0612cb7cf97ea09 (patch) | |
| tree | e31b25acaf30023616d4f469af5d067befd30556 /src | |
| parent | 75c18b6aaa8430596fa10466dc7918047b13ff2b (diff) | |
| parent | 5e4eb18eb0242794c0b3a622f7acf0d3e6856c05 (diff) | |
| download | rneovim-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.c | 10 | ||||
| -rw-r--r-- | src/nvim/api/vim.c | 2 | ||||
| -rw-r--r-- | src/nvim/eval.c | 1383 | ||||
| -rw-r--r-- | src/nvim/eval.lua | 2 | ||||
| -rw-r--r-- | src/nvim/eval/encode.c | 67 | ||||
| -rw-r--r-- | src/nvim/eval/typval_encode.h | 9 | ||||
| -rw-r--r-- | src/nvim/eval_defs.h | 15 | ||||
| -rw-r--r-- | src/nvim/globals.h | 2 | ||||
| -rw-r--r-- | src/nvim/normal.c | 2 | ||||
| -rw-r--r-- | src/nvim/tag.c | 2 | ||||
| -rw-r--r-- | src/nvim/testdir/test_alot.vim | 1 | ||||
| -rw-r--r-- | src/nvim/testdir/test_expr.vim | 22 | ||||
| -rw-r--r-- | src/nvim/testdir/test_partial.vim | 330 | ||||
| -rw-r--r-- | src/nvim/testdir/test_timers.vim | 23 | ||||
| -rw-r--r-- | src/nvim/testdir/test_viml.vim | 81 | ||||
| -rw-r--r-- | src/nvim/version.c | 59 |
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 |