mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
vim-patch:7.4.2137
Problem: Using function() with a name will find another function when it is
redefined.
Solution: Add funcref(). Refer to lambda using a partial. Fix several
reference counting issues.
437bafe4c8
This commit is contained in:
parent
1f715ac1c1
commit
53fad45115
@ -1207,7 +1207,8 @@ function returns: >
|
|||||||
:let Bar = Foo(4)
|
:let Bar = Foo(4)
|
||||||
:echo Bar(6)
|
:echo Bar(6)
|
||||||
< 5
|
< 5
|
||||||
See also |:func-closure|.
|
See also |:func-closure|. Lambda and closure support can be checked with: >
|
||||||
|
if has('lambda')
|
||||||
|
|
||||||
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
|
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
|
||||||
:echo map([1, 2, 3], {idx, val -> val + 1})
|
:echo map([1, 2, 3], {idx, val -> val + 1})
|
||||||
@ -2014,8 +2015,10 @@ foldlevel({lnum}) Number fold level at {lnum}
|
|||||||
foldtext() String line displayed for closed fold
|
foldtext() String line displayed for closed fold
|
||||||
foldtextresult({lnum}) String text for closed fold at {lnum}
|
foldtextresult({lnum}) String text for closed fold at {lnum}
|
||||||
foreground() Number bring the Vim window to the foreground
|
foreground() Number bring the Vim window to the foreground
|
||||||
function({name} [, {arglist}] [, {dict}])
|
funcref({name} [, {arglist}] [, {dict}])
|
||||||
Funcref reference to function {name}
|
Funcref reference to function {name}
|
||||||
|
function({name} [, {arglist}] [, {dict}])
|
||||||
|
Funcref named reference to function {name}
|
||||||
garbagecollect([{atexit}]) none free memory, breaking cyclic references
|
garbagecollect([{atexit}]) none free memory, breaking cyclic references
|
||||||
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
|
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
|
||||||
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
|
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
|
||||||
@ -3622,12 +3625,31 @@ foreground() Move the Vim window to the foreground. Useful when sent from
|
|||||||
|remote_foreground()| instead.
|
|remote_foreground()| instead.
|
||||||
{only in the Win32 GUI and console version}
|
{only in the Win32 GUI and console version}
|
||||||
|
|
||||||
|
*funcref()*
|
||||||
|
funcref({name} [, {arglist}] [, {dict}])
|
||||||
|
Just like |function()|, but the returned Funcref will lookup
|
||||||
|
the function by reference, not by name. This matters when the
|
||||||
|
function {name} is redefined later.
|
||||||
|
|
||||||
|
Unlike |function()|, {name} must be an existing user function.
|
||||||
|
Also for autoloaded functions. {name} cannot be a builtin
|
||||||
|
function.
|
||||||
|
|
||||||
*function()* *E700* *E922* *E923*
|
*function()* *E700* *E922* *E923*
|
||||||
function({name} [, {arglist}] [, {dict}])
|
function({name} [, {arglist}] [, {dict}])
|
||||||
Return a |Funcref| variable that refers to function {name}.
|
Return a |Funcref| variable that refers to function {name}.
|
||||||
{name} can be a user defined function or an internal function.
|
{name} can be a user defined function or an internal function.
|
||||||
|
|
||||||
|
{name} can also be a Funcref or a partial. When it is a
|
||||||
|
partial the dict stored in it will be used and the {dict}
|
||||||
|
argument is not allowed. E.g.: >
|
||||||
|
let FuncWithArg = function(dict.Func, [arg])
|
||||||
|
let Broken = function(dict.Func, [arg], dict)
|
||||||
|
<
|
||||||
|
When using the Funcref the function will be found by {name},
|
||||||
|
also when it was redefined later. Use |funcref()| to keep the
|
||||||
|
same function.
|
||||||
|
|
||||||
When {arglist} or {dict} is present this creates a partial.
|
When {arglist} or {dict} is present this creates a partial.
|
||||||
That mans the argument list and/or the dictionary is stored in
|
That mans the argument list and/or the dictionary is stored in
|
||||||
the Funcref and will be used when the Funcref is called.
|
the Funcref and will be used when the Funcref is called.
|
||||||
@ -6101,6 +6123,7 @@ screenrow() *screenrow()*
|
|||||||
The result is a Number, which is the current screen row of the
|
The result is a Number, which is the current screen row of the
|
||||||
cursor. The top line has number one.
|
cursor. The top line has number one.
|
||||||
This function is mainly used for testing.
|
This function is mainly used for testing.
|
||||||
|
Alternatively you can use |winline()|.
|
||||||
|
|
||||||
Note: Same restrictions as with |screencol()|.
|
Note: Same restrictions as with |screencol()|.
|
||||||
|
|
||||||
@ -7922,6 +7945,7 @@ insert_expand Compiled with support for CTRL-X expansion commands in
|
|||||||
Insert mode.
|
Insert mode.
|
||||||
jumplist Compiled with |jumplist| support.
|
jumplist Compiled with |jumplist| support.
|
||||||
keymap Compiled with 'keymap' support.
|
keymap Compiled with 'keymap' support.
|
||||||
|
lambda Compiled with |lambda| support.
|
||||||
langmap Compiled with 'langmap' support.
|
langmap Compiled with 'langmap' support.
|
||||||
libcall Compiled with |libcall()| support.
|
libcall Compiled with |libcall()| support.
|
||||||
linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
|
linebreak Compiled with 'linebreak', 'breakat', 'showbreak' and
|
||||||
@ -8149,7 +8173,7 @@ See |:verbose-cmd| for more information.
|
|||||||
:endf[unction] The end of a function definition. Must be on a line
|
:endf[unction] The end of a function definition. Must be on a line
|
||||||
by its own, without other commands.
|
by its own, without other commands.
|
||||||
|
|
||||||
*:delf* *:delfunction* *E130* *E131*
|
*:delf* *:delfunction* *E130* *E131* *E933*
|
||||||
:delf[unction] {name} Delete function {name}.
|
:delf[unction] {name} Delete function {name}.
|
||||||
{name} can also be a |Dictionary| entry that is a
|
{name} can also be a |Dictionary| entry that is a
|
||||||
|Funcref|: >
|
|Funcref|: >
|
||||||
|
@ -31,8 +31,6 @@ typedef struct {
|
|||||||
#include "nvim/hashtab.h"
|
#include "nvim/hashtab.h"
|
||||||
// for dict_T
|
// for dict_T
|
||||||
#include "nvim/eval_defs.h"
|
#include "nvim/eval_defs.h"
|
||||||
// for proftime_T
|
|
||||||
#include "nvim/profile.h"
|
|
||||||
// for String
|
// for String
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
// for Map(K, V)
|
// for Map(K, V)
|
||||||
@ -90,7 +88,6 @@ typedef struct {
|
|||||||
typedef struct window_S win_T;
|
typedef struct window_S win_T;
|
||||||
typedef struct wininfo_S wininfo_T;
|
typedef struct wininfo_S wininfo_T;
|
||||||
typedef struct frame_S frame_T;
|
typedef struct frame_S frame_T;
|
||||||
typedef int scid_T; /* script ID */
|
|
||||||
|
|
||||||
// for struct memline (it needs memfile_T)
|
// for struct memline (it needs memfile_T)
|
||||||
#include "nvim/memline_defs.h"
|
#include "nvim/memline_defs.h"
|
||||||
|
430
src/nvim/eval.c
430
src/nvim/eval.c
@ -218,7 +218,7 @@ static int echo_attr = 0; /* attributes used for ":echo" */
|
|||||||
#define FC_DELETED 16 // :delfunction used while uf_refcount > 0
|
#define FC_DELETED 16 // :delfunction used while uf_refcount > 0
|
||||||
#define FC_REMOVED 32 // function redefined while uf_refcount > 0
|
#define FC_REMOVED 32 // function redefined while uf_refcount > 0
|
||||||
|
|
||||||
/* The names of packages that once were loaded are remembered. */
|
// The names of packages that once were loaded are remembered.
|
||||||
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
|
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
|
||||||
|
|
||||||
// List heads for garbage collection. Although there can be a reference loop
|
// List heads for garbage collection. Although there can be a reference loop
|
||||||
@ -232,37 +232,6 @@ static list_T *first_list = NULL; // list of all lists
|
|||||||
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
|
#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j]
|
||||||
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
|
#define FUNCLINE(fp, j) ((char_u **)(fp->uf_lines.ga_data))[j]
|
||||||
|
|
||||||
#define VAR_SHORT_LEN 20 /* short variable name length */
|
|
||||||
#define FIXVAR_CNT 12 /* number of fixed variables */
|
|
||||||
|
|
||||||
// structure to hold info for a function that is currently being executed.
|
|
||||||
|
|
||||||
struct funccall_S {
|
|
||||||
ufunc_T *func; /* function being called */
|
|
||||||
int linenr; /* next line to be executed */
|
|
||||||
int returned; /* ":return" used */
|
|
||||||
struct /* fixed variables for arguments */
|
|
||||||
{
|
|
||||||
dictitem_T var; /* variable (without room for name) */
|
|
||||||
char_u room[VAR_SHORT_LEN]; /* room for the name */
|
|
||||||
} fixvar[FIXVAR_CNT];
|
|
||||||
dict_T l_vars; // l: local function variables
|
|
||||||
dictitem_T l_vars_var; // variable for l: scope
|
|
||||||
dict_T l_avars; // a: argument variables
|
|
||||||
dictitem_T l_avars_var; // variable for a: scope
|
|
||||||
list_T l_varlist; // list for a:000
|
|
||||||
listitem_T l_listitems[MAX_FUNC_ARGS]; // listitems for a:000
|
|
||||||
typval_T *rettv; // return value
|
|
||||||
linenr_T breakpoint; // next line with breakpoint or zero
|
|
||||||
int dbg_tick; // debug_tick when breakpoint was set
|
|
||||||
int level; // top nesting level of executed function
|
|
||||||
proftime_T prof_child; // time spent in a child
|
|
||||||
funccall_T *caller; // calling function or NULL
|
|
||||||
int fc_refcount;
|
|
||||||
int fc_copyID; // for garbage collection
|
|
||||||
garray_T fc_funcs; // list of ufunc_T* which refer this
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Info used by a ":for" loop.
|
* Info used by a ":for" loop.
|
||||||
*/
|
*/
|
||||||
@ -273,15 +242,6 @@ typedef struct {
|
|||||||
list_T *fi_list; /* list being used */
|
list_T *fi_list; /* list being used */
|
||||||
} forinfo_T;
|
} forinfo_T;
|
||||||
|
|
||||||
/*
|
|
||||||
* Struct used by trans_function_name()
|
|
||||||
*/
|
|
||||||
typedef struct {
|
|
||||||
dict_T *fd_dict; /* Dictionary used */
|
|
||||||
char_u *fd_newkey; /* new key in "dict" in allocated memory */
|
|
||||||
dictitem_T *fd_di; /* Dictionary item used */
|
|
||||||
} funcdict_T;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* enum used by var_flavour()
|
* enum used by var_flavour()
|
||||||
*/
|
*/
|
||||||
@ -4960,6 +4920,15 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @return the function name of the partial.
|
||||||
|
char_u *partial_name(partial_T *pt)
|
||||||
|
{
|
||||||
|
if (pt->pt_name != NULL) {
|
||||||
|
return pt->pt_name;
|
||||||
|
}
|
||||||
|
return pt->pt_func->uf_name;
|
||||||
|
}
|
||||||
|
|
||||||
static void partial_free(partial_T *pt)
|
static void partial_free(partial_T *pt)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < pt->pt_argc; i++) {
|
for (int i = 0; i < pt->pt_argc; i++) {
|
||||||
@ -4967,8 +4936,12 @@ static void partial_free(partial_T *pt)
|
|||||||
}
|
}
|
||||||
xfree(pt->pt_argv);
|
xfree(pt->pt_argv);
|
||||||
dict_unref(pt->pt_dict);
|
dict_unref(pt->pt_dict);
|
||||||
func_unref(pt->pt_name);
|
if (pt->pt_name != NULL) {
|
||||||
xfree(pt->pt_name);
|
func_unref(pt->pt_name);
|
||||||
|
xfree(pt->pt_name);
|
||||||
|
} else {
|
||||||
|
func_ptr_unref(pt->pt_func);
|
||||||
|
}
|
||||||
xfree(pt);
|
xfree(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5232,12 +5205,12 @@ static bool func_equal(
|
|||||||
|
|
||||||
// empty and NULL function name considered the same
|
// empty and NULL function name considered the same
|
||||||
s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
|
s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
|
||||||
: tv1->vval.v_partial->pt_name;
|
: partial_name(tv1->vval.v_partial);
|
||||||
if (s1 != NULL && *s1 == NUL) {
|
if (s1 != NULL && *s1 == NUL) {
|
||||||
s1 = NULL;
|
s1 = NULL;
|
||||||
}
|
}
|
||||||
s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
|
s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
|
||||||
: tv2->vval.v_partial->pt_name;
|
: partial_name(tv2->vval.v_partial);
|
||||||
if (s2 != NULL && *s2 == NUL) {
|
if (s2 != NULL && *s2 == NUL) {
|
||||||
s2 = NULL;
|
s2 = NULL;
|
||||||
}
|
}
|
||||||
@ -6272,7 +6245,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
|
|||||||
|
|
||||||
// A partial does not have a copyID, because it cannot contain itself.
|
// A partial does not have a copyID, because it cannot contain itself.
|
||||||
if (pt != NULL) {
|
if (pt != NULL) {
|
||||||
abort = abort || set_ref_in_func(pt->pt_name, copyID);
|
abort = abort || set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
|
||||||
if (pt->pt_dict != NULL) {
|
if (pt->pt_dict != NULL) {
|
||||||
typval_T dtv;
|
typval_T dtv;
|
||||||
|
|
||||||
@ -6289,7 +6262,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case VAR_FUNC:
|
case VAR_FUNC:
|
||||||
abort = abort || set_ref_in_func(tv->vval.v_string, copyID);
|
abort = abort || set_ref_in_func(tv->vval.v_string, NULL, copyID);
|
||||||
break;
|
break;
|
||||||
case VAR_UNKNOWN:
|
case VAR_UNKNOWN:
|
||||||
case VAR_SPECIAL:
|
case VAR_SPECIAL:
|
||||||
@ -6307,21 +6280,23 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack,
|
|||||||
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
|
/// "ht_stack" is used to add hashtabs to be marked. Can be NULL.
|
||||||
///
|
///
|
||||||
/// @return TRUE if setting references failed somehow.
|
/// @return TRUE if setting references failed somehow.
|
||||||
int set_ref_in_func(char_u *name, int copyID)
|
int set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
|
||||||
{
|
{
|
||||||
ufunc_T *fp;
|
ufunc_T *fp = fp_in;
|
||||||
funccall_T *fc;
|
funccall_T *fc;
|
||||||
int error;
|
int error;
|
||||||
char_u fname_buf[FLEN_FIXED + 1];
|
char_u fname_buf[FLEN_FIXED + 1];
|
||||||
char_u *tofree = NULL;
|
char_u *tofree = NULL;
|
||||||
char_u *fname;
|
char_u *fname;
|
||||||
bool abort = false;
|
bool abort = false;
|
||||||
if (name == NULL) {
|
if (name == NULL && fp_in == NULL) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
|
if (fp_in == NULL) {
|
||||||
fp = find_func(fname);
|
fname = fname_trans_sid(name, fname_buf, &tofree, &error);
|
||||||
|
fp = find_func(fname);
|
||||||
|
}
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) {
|
for (fc = fp->uf_scoped; fc != NULL; fc = fc->func->uf_scoped) {
|
||||||
if (fc->fc_copyID != copyID) {
|
if (fc->fc_copyID != copyID) {
|
||||||
@ -7026,7 +7001,7 @@ static int register_closure(ufunc_T *fp) {
|
|||||||
ga_grow(¤t_funccal->fc_funcs, 1);
|
ga_grow(¤t_funccal->fc_funcs, 1);
|
||||||
((ufunc_T **)current_funccal->fc_funcs.ga_data)
|
((ufunc_T **)current_funccal->fc_funcs.ga_data)
|
||||||
[current_funccal->fc_funcs.ga_len++] = fp;
|
[current_funccal->fc_funcs.ga_len++] = fp;
|
||||||
func_ref(current_funccal->func->uf_name);
|
func_ptr_ref(current_funccal->func);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7041,7 +7016,6 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
ufunc_T *fp = NULL;
|
ufunc_T *fp = NULL;
|
||||||
int varargs;
|
int varargs;
|
||||||
int ret;
|
int ret;
|
||||||
char_u name[20];
|
|
||||||
char_u *start = skipwhite(*arg + 1);
|
char_u *start = skipwhite(*arg + 1);
|
||||||
char_u *s, *e;
|
char_u *s, *e;
|
||||||
static int lambda_no = 0;
|
static int lambda_no = 0;
|
||||||
@ -7092,6 +7066,8 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
int len, flags = 0;
|
int len, flags = 0;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
|
char_u name[20];
|
||||||
|
partial_T *pt;
|
||||||
|
|
||||||
snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
|
snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
|
||||||
|
|
||||||
@ -7099,6 +7075,11 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
goto errret;
|
goto errret;
|
||||||
}
|
}
|
||||||
|
pt = (partial_T *)xcalloc(1, (unsigned)(sizeof(partial_T)));
|
||||||
|
if (pt == NULL) {
|
||||||
|
xfree(fp);
|
||||||
|
goto errret;
|
||||||
|
}
|
||||||
|
|
||||||
ga_init(&newlines, (int)sizeof(char_u *), 1);
|
ga_init(&newlines, (int)sizeof(char_u *), 1);
|
||||||
ga_grow(&newlines, 1);
|
ga_grow(&newlines, 1);
|
||||||
@ -7138,8 +7119,10 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
|
|||||||
fp->uf_calls = 0;
|
fp->uf_calls = 0;
|
||||||
fp->uf_script_ID = current_SID;
|
fp->uf_script_ID = current_SID;
|
||||||
|
|
||||||
rettv->vval.v_string = vim_strsave(name);
|
pt->pt_func = fp;
|
||||||
rettv->v_type = VAR_FUNC;
|
pt->pt_refcount = 1;
|
||||||
|
rettv->vval.v_partial = pt;
|
||||||
|
rettv->v_type = VAR_PARTIAL;
|
||||||
}
|
}
|
||||||
eval_lavars_used = old_eval_lavars;
|
eval_lavars_used = old_eval_lavars;
|
||||||
return OK;
|
return OK;
|
||||||
@ -7300,6 +7283,8 @@ static char_u *deref_func_name(
|
|||||||
{
|
{
|
||||||
dictitem_T *v;
|
dictitem_T *v;
|
||||||
int cc;
|
int cc;
|
||||||
|
char_u *s;
|
||||||
|
|
||||||
if (partialp != NULL) {
|
if (partialp != NULL) {
|
||||||
*partialp = NULL;
|
*partialp = NULL;
|
||||||
}
|
}
|
||||||
@ -7313,8 +7298,9 @@ static char_u *deref_func_name(
|
|||||||
*lenp = 0;
|
*lenp = 0;
|
||||||
return (char_u *)""; /* just in case */
|
return (char_u *)""; /* just in case */
|
||||||
}
|
}
|
||||||
*lenp = (int)STRLEN(v->di_tv.vval.v_string);
|
s = v->di_tv.vval.v_string;
|
||||||
return v->di_tv.vval.v_string;
|
*lenp = (int)STRLEN(s);
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
|
if (v != NULL && v->di_tv.v_type == VAR_PARTIAL) {
|
||||||
@ -7327,8 +7313,9 @@ static char_u *deref_func_name(
|
|||||||
if (partialp != NULL) {
|
if (partialp != NULL) {
|
||||||
*partialp = pt;
|
*partialp = pt;
|
||||||
}
|
}
|
||||||
*lenp = (int)STRLEN(pt->pt_name);
|
s = partial_name(pt);
|
||||||
return pt->pt_name;
|
*lenp = (int)STRLEN(s);
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
return name;
|
return name;
|
||||||
@ -7421,6 +7408,7 @@ get_func_tv (
|
|||||||
#define ERROR_NONE 5
|
#define ERROR_NONE 5
|
||||||
#define ERROR_OTHER 6
|
#define ERROR_OTHER 6
|
||||||
#define ERROR_BOTH 7
|
#define ERROR_BOTH 7
|
||||||
|
#define ERROR_DELETED 8
|
||||||
#define FLEN_FIXED 40
|
#define FLEN_FIXED 40
|
||||||
|
|
||||||
/// In a script change <SID>name() and s:name() to K_SNR 123_name().
|
/// In a script change <SID>name() and s:name() to K_SNR 123_name().
|
||||||
@ -7552,12 +7540,14 @@ call_func(
|
|||||||
error = ERROR_UNKNOWN;
|
error = ERROR_UNKNOWN;
|
||||||
|
|
||||||
if (!builtin_function(rfname, -1)) {
|
if (!builtin_function(rfname, -1)) {
|
||||||
/*
|
// User defined function.
|
||||||
* User defined function.
|
if (partial != NULL && partial->pt_func != NULL) {
|
||||||
*/
|
fp = partial->pt_func;
|
||||||
fp = find_func(rfname);
|
} else {
|
||||||
|
fp = find_func(rfname);
|
||||||
|
}
|
||||||
|
|
||||||
/* Trigger FuncUndefined event, may load the function. */
|
// Trigger FuncUndefined event, may load the function.
|
||||||
if (fp == NULL
|
if (fp == NULL
|
||||||
&& apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL)
|
&& apply_autocmds(EVENT_FUNCUNDEFINED, rfname, rfname, TRUE, NULL)
|
||||||
&& !aborting()) {
|
&& !aborting()) {
|
||||||
@ -7570,7 +7560,9 @@ call_func(
|
|||||||
fp = find_func(rfname);
|
fp = find_func(rfname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fp != NULL) {
|
if (fp != NULL && (fp->uf_flags & FC_DELETED)) {
|
||||||
|
error = ERROR_DELETED;
|
||||||
|
} else if (fp != NULL) {
|
||||||
if (argv_func != NULL) {
|
if (argv_func != NULL) {
|
||||||
argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
|
argcount = argv_func(argcount, argvars, fp->uf_args.ga_len);
|
||||||
}
|
}
|
||||||
@ -7629,6 +7621,9 @@ call_func(
|
|||||||
case ERROR_UNKNOWN:
|
case ERROR_UNKNOWN:
|
||||||
emsg_funcname(N_("E117: Unknown function: %s"), name);
|
emsg_funcname(N_("E117: Unknown function: %s"), name);
|
||||||
break;
|
break;
|
||||||
|
case ERROR_DELETED:
|
||||||
|
emsg_funcname(N_("E933: Function was deleted: %s"), name);
|
||||||
|
break;
|
||||||
case ERROR_TOOMANY:
|
case ERROR_TOOMANY:
|
||||||
emsg_funcname(e_toomanyarg, name);
|
emsg_funcname(e_toomanyarg, name);
|
||||||
break;
|
break;
|
||||||
@ -8465,7 +8460,7 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
func = argvars[0].vval.v_string;
|
func = argvars[0].vval.v_string;
|
||||||
} else if (argvars[0].v_type == VAR_PARTIAL) {
|
} else if (argvars[0].v_type == VAR_PARTIAL) {
|
||||||
partial = argvars[0].vval.v_partial;
|
partial = argvars[0].vval.v_partial;
|
||||||
func = partial->pt_name;
|
func = partial_name(partial);
|
||||||
} else {
|
} else {
|
||||||
func = get_tv_string(&argvars[0]);
|
func = get_tv_string(&argvars[0]);
|
||||||
}
|
}
|
||||||
@ -9725,7 +9720,7 @@ static int filter_map_one(typval_T *tv, typval_T *expr, int map, int *remp)
|
|||||||
} else if (expr->v_type == VAR_PARTIAL) {
|
} else if (expr->v_type == VAR_PARTIAL) {
|
||||||
partial_T *partial = expr->vval.v_partial;
|
partial_T *partial = expr->vval.v_partial;
|
||||||
|
|
||||||
s = partial->pt_name;
|
s = partial_name(partial);
|
||||||
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
|
if (call_func(s, (int)STRLEN(s), &rettv, 2, argv, NULL,
|
||||||
0L, 0L, &dummy, true, partial, NULL) == FAIL) {
|
0L, 0L, &dummy, true, partial, NULL) == FAIL) {
|
||||||
goto theend;
|
goto theend;
|
||||||
@ -10002,15 +9997,14 @@ static void f_foreground(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void common_function(typval_T *argvars, typval_T *rettv,
|
||||||
* "function()" function
|
bool is_funcref, FunPtr fptr)
|
||||||
*/
|
|
||||||
static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|
||||||
{
|
{
|
||||||
char_u *s;
|
char_u *s;
|
||||||
char_u *name;
|
char_u *name;
|
||||||
bool use_string = false;
|
bool use_string = false;
|
||||||
partial_T *arg_pt = NULL;
|
partial_T *arg_pt = NULL;
|
||||||
|
char_u *trans_name = NULL;
|
||||||
|
|
||||||
if (argvars[0].v_type == VAR_FUNC) {
|
if (argvars[0].v_type == VAR_FUNC) {
|
||||||
// function(MyFunc, [arg], dict)
|
// function(MyFunc, [arg], dict)
|
||||||
@ -10019,17 +10013,28 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
&& argvars[0].vval.v_partial != NULL) {
|
&& argvars[0].vval.v_partial != NULL) {
|
||||||
// function(dict.MyFunc, [arg])
|
// function(dict.MyFunc, [arg])
|
||||||
arg_pt = argvars[0].vval.v_partial;
|
arg_pt = argvars[0].vval.v_partial;
|
||||||
s = arg_pt->pt_name;
|
s = partial_name(arg_pt);
|
||||||
} else {
|
} else {
|
||||||
// function('MyFunc', [arg], dict)
|
// function('MyFunc', [arg], dict)
|
||||||
s = get_tv_string(&argvars[0]);
|
s = get_tv_string(&argvars[0]);
|
||||||
use_string = true;
|
use_string = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL)
|
||||||
|
|| is_funcref)) {
|
||||||
|
name = s;
|
||||||
|
trans_name = trans_function_name(&name, false,
|
||||||
|
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD
|
||||||
|
| TFN_NO_DEREF, NULL, NULL);
|
||||||
|
if (name != NULL) {
|
||||||
|
s = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))) {
|
if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s))) {
|
||||||
EMSG2(_(e_invarg2), s);
|
EMSG2(_(e_invarg2), s);
|
||||||
} else if (use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL
|
} else if (trans_name != NULL
|
||||||
&& !function_exists(s, true)) {
|
&& (is_funcref ? find_func(trans_name) == NULL
|
||||||
|
: !translated_function_exists(trans_name))) {
|
||||||
// Don't check an autoload name for existence here.
|
// Don't check an autoload name for existence here.
|
||||||
EMSG2(_("E700: Unknown function: %s"), s);
|
EMSG2(_("E700: Unknown function: %s"), s);
|
||||||
} else {
|
} else {
|
||||||
@ -10071,7 +10076,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
if (argvars[dict_idx].v_type != VAR_DICT) {
|
if (argvars[dict_idx].v_type != VAR_DICT) {
|
||||||
EMSG(_("E922: expected a dict"));
|
EMSG(_("E922: expected a dict"));
|
||||||
xfree(name);
|
xfree(name);
|
||||||
return;
|
goto theend;
|
||||||
}
|
}
|
||||||
if (argvars[dict_idx].vval.v_dict == NULL) {
|
if (argvars[dict_idx].vval.v_dict == NULL) {
|
||||||
dict_idx = 0;
|
dict_idx = 0;
|
||||||
@ -10082,7 +10087,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
EMSG(_("E923: Second argument of function() must be "
|
EMSG(_("E923: Second argument of function() must be "
|
||||||
"a list or a dict"));
|
"a list or a dict"));
|
||||||
xfree(name);
|
xfree(name);
|
||||||
return;
|
goto theend;
|
||||||
}
|
}
|
||||||
list = argvars[arg_idx].vval.v_list;
|
list = argvars[arg_idx].vval.v_list;
|
||||||
if (list == NULL || list->lv_len == 0) {
|
if (list == NULL || list->lv_len == 0) {
|
||||||
@ -10090,7 +10095,7 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL) {
|
if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) {
|
||||||
partial_T *const pt = xcalloc(1, sizeof(*pt));
|
partial_T *const pt = xcalloc(1, sizeof(*pt));
|
||||||
|
|
||||||
// result is a VAR_PARTIAL
|
// result is a VAR_PARTIAL
|
||||||
@ -10103,18 +10108,17 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
if (pt->pt_argv == NULL) {
|
if (pt->pt_argv == NULL) {
|
||||||
xfree(pt);
|
xfree(pt);
|
||||||
xfree(name);
|
xfree(name);
|
||||||
return;
|
goto theend;
|
||||||
} else {
|
}
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i < arg_len; i++) {
|
for (; i < arg_len; i++) {
|
||||||
copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
|
copy_tv(&arg_pt->pt_argv[i], &pt->pt_argv[i]);
|
||||||
}
|
}
|
||||||
if (lv_len > 0) {
|
if (lv_len > 0) {
|
||||||
for (listitem_T *li = list->lv_first;
|
for (listitem_T *li = list->lv_first;
|
||||||
li != NULL;
|
li != NULL;
|
||||||
li = li->li_next) {
|
li = li->li_next) {
|
||||||
copy_tv(&li->li_tv, &pt->pt_argv[i++]);
|
copy_tv(&li->li_tv, &pt->pt_argv[i++]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10136,8 +10140,18 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pt->pt_refcount = 1;
|
pt->pt_refcount = 1;
|
||||||
pt->pt_name = name;
|
if (arg_pt != NULL && arg_pt->pt_func != NULL) {
|
||||||
func_ref(pt->pt_name);
|
pt->pt_func = arg_pt->pt_func;
|
||||||
|
func_ptr_ref(pt->pt_func);
|
||||||
|
xfree(name);
|
||||||
|
} else if (is_funcref) {
|
||||||
|
pt->pt_func = find_func(trans_name);
|
||||||
|
func_ptr_ref(pt->pt_func);
|
||||||
|
xfree(name);
|
||||||
|
} else {
|
||||||
|
pt->pt_name = name;
|
||||||
|
func_ref(name);
|
||||||
|
}
|
||||||
|
|
||||||
rettv->v_type = VAR_PARTIAL;
|
rettv->v_type = VAR_PARTIAL;
|
||||||
rettv->vval.v_partial = pt;
|
rettv->vval.v_partial = pt;
|
||||||
@ -10148,6 +10162,18 @@ static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
func_ref(name);
|
func_ref(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
theend:
|
||||||
|
xfree(trans_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void f_funcref(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
common_function(argvars, rettv, true, fptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void f_function(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
||||||
|
{
|
||||||
|
common_function(argvars, rettv, false, fptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// "garbagecollect()" function
|
/// "garbagecollect()" function
|
||||||
@ -10201,11 +10227,18 @@ static void f_get(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
|
|
||||||
if (pt != NULL) {
|
if (pt != NULL) {
|
||||||
char_u *what = get_tv_string(&argvars[1]);
|
char_u *what = get_tv_string(&argvars[1]);
|
||||||
|
char_u *n;
|
||||||
|
|
||||||
if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) {
|
if (STRCMP(what, "func") == 0 || STRCMP(what, "name") == 0) {
|
||||||
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
|
rettv->v_type = (*what == 'f' ? VAR_FUNC : VAR_STRING);
|
||||||
if (pt->pt_name != NULL) {
|
n = partial_name(pt);
|
||||||
rettv->vval.v_string = vim_strsave(pt->pt_name);
|
if (n == NULL) {
|
||||||
|
rettv->vval.v_string = NULL;
|
||||||
|
} else {
|
||||||
|
rettv->vval.v_string = vim_strsave(n);
|
||||||
|
if (rettv->v_type == VAR_FUNC) {
|
||||||
|
func_ref(rettv->vval.v_string);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (STRCMP(what, "dict") == 0) {
|
} else if (STRCMP(what, "dict") == 0) {
|
||||||
rettv->v_type = VAR_DICT;
|
rettv->v_type = VAR_DICT;
|
||||||
@ -16243,8 +16276,9 @@ static int item_compare2(const void *s1, const void *s2, bool keep_zero)
|
|||||||
if (partial == NULL) {
|
if (partial == NULL) {
|
||||||
func_name = sortinfo->item_compare_func;
|
func_name = sortinfo->item_compare_func;
|
||||||
} else {
|
} else {
|
||||||
func_name = partial->pt_name;
|
func_name = partial_name(partial);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
|
// Copy the values. This is needed to be able to set v_lock to VAR_FIXED
|
||||||
// in the copy without changing the original list items.
|
// in the copy without changing the original list items.
|
||||||
copy_tv(&si1->item->li_tv, &argv[0]);
|
copy_tv(&si1->item->li_tv, &argv[0]);
|
||||||
@ -17746,7 +17780,7 @@ static bool callback_call(Callback *callback, int argcount_in,
|
|||||||
|
|
||||||
case kCallbackPartial:
|
case kCallbackPartial:
|
||||||
partial = callback->data.partial;
|
partial = callback->data.partial;
|
||||||
name = partial->pt_name;
|
name = partial_name(partial);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kCallbackNone:
|
case kCallbackNone:
|
||||||
@ -19346,7 +19380,7 @@ handle_subscript(
|
|||||||
// Invoke the function. Recursive!
|
// Invoke the function. Recursive!
|
||||||
if (functv.v_type == VAR_PARTIAL) {
|
if (functv.v_type == VAR_PARTIAL) {
|
||||||
pt = functv.vval.v_partial;
|
pt = functv.vval.v_partial;
|
||||||
s = pt->pt_name;
|
s = partial_name(pt);
|
||||||
} else {
|
} else {
|
||||||
s = functv.vval.v_string;
|
s = functv.vval.v_string;
|
||||||
}
|
}
|
||||||
@ -19406,19 +19440,22 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
|
|||||||
&& rettv->vval.v_partial->pt_dict != NULL) {
|
&& rettv->vval.v_partial->pt_dict != NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
char_u *fname = rettv->v_type == VAR_FUNC || rettv->v_type == VAR_STRING
|
char_u *fname;
|
||||||
? rettv->vval.v_string
|
|
||||||
: rettv->vval.v_partial->pt_name;
|
|
||||||
char_u *tofree = NULL;
|
char_u *tofree = NULL;
|
||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
char_u fname_buf[FLEN_FIXED + 1];
|
char_u fname_buf[FLEN_FIXED + 1];
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
// Translate "s:func" to the stored function name.
|
if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial->pt_func != NULL) {
|
||||||
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
|
fp = rettv->vval.v_partial->pt_func;
|
||||||
|
} else {
|
||||||
fp = find_func(fname);
|
fname = rettv->v_type == VAR_FUNC ? rettv->vval.v_string
|
||||||
xfree(tofree);
|
: rettv->vval.v_partial->pt_name;
|
||||||
|
// Translate "s:func" to the stored function name.
|
||||||
|
fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
|
||||||
|
fp = find_func(fname);
|
||||||
|
xfree(tofree);
|
||||||
|
}
|
||||||
|
|
||||||
// Turn "dict.Func" into a partial for "Func" with "dict".
|
// Turn "dict.Func" into a partial for "Func" with "dict".
|
||||||
if (fp != NULL && (fp->uf_flags & FC_DICT)) {
|
if (fp != NULL && (fp->uf_flags & FC_DICT)) {
|
||||||
@ -19439,8 +19476,13 @@ static void set_selfdict(typval_T *rettv, dict_T *selfdict)
|
|||||||
// Partial: copy the function name, use selfdict and copy
|
// Partial: copy the function name, use selfdict and copy
|
||||||
// args. Can't take over name or args, the partial might
|
// args. Can't take over name or args, the partial might
|
||||||
// be referenced elsewhere.
|
// be referenced elsewhere.
|
||||||
pt->pt_name = vim_strsave(ret_pt->pt_name);
|
if (ret_pt->pt_name != NULL) {
|
||||||
func_ref(pt->pt_name);
|
pt->pt_name = vim_strsave(ret_pt->pt_name);
|
||||||
|
func_ref(pt->pt_name);
|
||||||
|
} else {
|
||||||
|
pt->pt_func = ret_pt->pt_func;
|
||||||
|
func_ptr_ref(pt->pt_func);
|
||||||
|
}
|
||||||
if (ret_pt->pt_argc > 0) {
|
if (ret_pt->pt_argc > 0) {
|
||||||
size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
|
size_t arg_size = sizeof(typval_T) * ret_pt->pt_argc;
|
||||||
pt->pt_argv = (typval_T *)xmalloc(arg_size);
|
pt->pt_argv = (typval_T *)xmalloc(arg_size);
|
||||||
@ -21286,11 +21328,19 @@ void ex_function(exarg_T *eap)
|
|||||||
name);
|
name);
|
||||||
goto erret;
|
goto erret;
|
||||||
}
|
}
|
||||||
/* redefine existing function */
|
if (fp->uf_refcount > 1) {
|
||||||
ga_clear_strings(&(fp->uf_args));
|
// This function is referenced somewhere, don't redefine it but
|
||||||
ga_clear_strings(&(fp->uf_lines));
|
// create a new one.
|
||||||
xfree(name);
|
(fp->uf_refcount)--;
|
||||||
name = NULL;
|
fp = NULL;
|
||||||
|
overwrite = true;
|
||||||
|
} else {
|
||||||
|
// redefine existing function
|
||||||
|
ga_clear_strings(&(fp->uf_args));
|
||||||
|
ga_clear_strings(&(fp->uf_lines))
|
||||||
|
xfree(name);
|
||||||
|
name = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char numbuf[20];
|
char numbuf[20];
|
||||||
@ -21366,12 +21416,15 @@ void ex_function(exarg_T *eap)
|
|||||||
|
|
||||||
/* insert the new function in the function list */
|
/* insert the new function in the function list */
|
||||||
STRCPY(fp->uf_name, name);
|
STRCPY(fp->uf_name, name);
|
||||||
if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
|
if (overwrite) {
|
||||||
xfree(fp);
|
hi = hash_find(&func_hashtab, name);
|
||||||
goto erret;
|
hi->hi_key = UF2HIKEY(fp);
|
||||||
|
} else if (hash_add(&func_hashtab, UF2HIKEY(fp)) == FAIL) {
|
||||||
|
xfree(fp);
|
||||||
|
goto erret;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
fp->uf_refcount = 1;
|
fp->uf_refcount = 1;
|
||||||
|
}
|
||||||
fp->uf_args = newargs;
|
fp->uf_args = newargs;
|
||||||
fp->uf_lines = newlines;
|
fp->uf_lines = newlines;
|
||||||
if ((flags & FC_CLOSURE) != 0) {
|
if ((flags & FC_CLOSURE) != 0) {
|
||||||
@ -21413,8 +21466,8 @@ ret_free:
|
|||||||
/// Advances "pp" to just after the function name (if no error).
|
/// Advances "pp" to just after the function name (if no error).
|
||||||
///
|
///
|
||||||
/// @return the function name in allocated memory, or NULL for failure.
|
/// @return the function name in allocated memory, or NULL for failure.
|
||||||
static char_u *
|
char_u *
|
||||||
trans_function_name (
|
trans_function_name(
|
||||||
char_u **pp,
|
char_u **pp,
|
||||||
int skip, /* only find the end, don't evaluate */
|
int skip, /* only find the end, don't evaluate */
|
||||||
int flags,
|
int flags,
|
||||||
@ -21479,7 +21532,7 @@ trans_function_name (
|
|||||||
fdp->fd_di = lv.ll_di;
|
fdp->fd_di = lv.ll_di;
|
||||||
}
|
}
|
||||||
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
|
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL) {
|
||||||
name = vim_strsave(lv.ll_tv->vval.v_string);
|
name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial));
|
||||||
*pp = end;
|
*pp = end;
|
||||||
} else if (lv.ll_tv->v_type == VAR_PARTIAL
|
} else if (lv.ll_tv->v_type == VAR_PARTIAL
|
||||||
&& lv.ll_tv->vval.v_partial != NULL) {
|
&& lv.ll_tv->vval.v_partial != NULL) {
|
||||||
@ -21676,11 +21729,9 @@ static void list_func_head(ufunc_T *fp, int indent)
|
|||||||
last_set_msg(fp->uf_script_ID);
|
last_set_msg(fp->uf_script_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Find a function by name, return pointer to it in ufuncs.
|
||||||
* Find a function by name, return pointer to it in ufuncs.
|
/// @return NULL for unknown function.
|
||||||
* Return NULL for unknown function.
|
ufunc_T *find_func(char_u *name)
|
||||||
*/
|
|
||||||
static ufunc_T *find_func(char_u *name)
|
|
||||||
{
|
{
|
||||||
hashitem_T *hi;
|
hashitem_T *hi;
|
||||||
|
|
||||||
@ -22098,8 +22149,34 @@ void ex_delfunction(exarg_T *eap)
|
|||||||
/* Delete the dict item that refers to the function, it will
|
/* Delete the dict item that refers to the function, it will
|
||||||
* invoke func_unref() and possibly delete the function. */
|
* invoke func_unref() and possibly delete the function. */
|
||||||
dictitem_remove(fudi.fd_dict, fudi.fd_di);
|
dictitem_remove(fudi.fd_dict, fudi.fd_di);
|
||||||
} else
|
} else {
|
||||||
func_free(fp);
|
// Normal functions (not numbered functions and lambdas) have a
|
||||||
|
// refcount of 1 for the entry in the hashtable. When deleting
|
||||||
|
// them and the refcount is more than one, it should be kept.
|
||||||
|
// Numbered functions and lambdas snould be kept if the refcount is
|
||||||
|
// one or more.
|
||||||
|
if (fp->uf_refcount > (isdigit(fp->uf_name[0])
|
||||||
|
|| fp->uf_name[0] == '<') ? 0 : 1) {
|
||||||
|
// Function is still referenced somewhere. Don't free it but
|
||||||
|
// do remove it from the hashtable.
|
||||||
|
func_remove(fp);
|
||||||
|
fp->uf_flags |= FC_DELETED;
|
||||||
|
fp->uf_refcount--;
|
||||||
|
} else {
|
||||||
|
func_free(fp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove the function from the function hashtable. If the function was
|
||||||
|
/// deleted while it still has references this was already done.
|
||||||
|
static void func_remove(ufunc_T *fp)
|
||||||
|
{
|
||||||
|
hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp));
|
||||||
|
|
||||||
|
if (!HASHITEM_EMPTY(hi)) {
|
||||||
|
hash_remove(&func_hashtab, hi);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22108,9 +22185,7 @@ void ex_delfunction(exarg_T *eap)
|
|||||||
*/
|
*/
|
||||||
static void func_free(ufunc_T *fp)
|
static void func_free(ufunc_T *fp)
|
||||||
{
|
{
|
||||||
hashitem_T *hi;
|
// clear this function
|
||||||
|
|
||||||
/* clear this function */
|
|
||||||
ga_clear_strings(&(fp->uf_args));
|
ga_clear_strings(&(fp->uf_args));
|
||||||
ga_clear_strings(&(fp->uf_lines));
|
ga_clear_strings(&(fp->uf_lines));
|
||||||
xfree(fp->uf_tml_count);
|
xfree(fp->uf_tml_count);
|
||||||
@ -22123,12 +22198,13 @@ static void func_free(ufunc_T *fp)
|
|||||||
func_remove(fp);
|
func_remove(fp);
|
||||||
}
|
}
|
||||||
funccal_unref(fp->uf_scoped, fp);
|
funccal_unref(fp->uf_scoped, fp);
|
||||||
|
func_remove(fp);
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unreference a Function: decrement the reference count and free it when it
|
* Unreference a Function: decrement the reference count and free it when it
|
||||||
* becomes zero. Only for numbered functions.
|
* becomes zero.
|
||||||
*/
|
*/
|
||||||
void func_unref(char_u *name)
|
void func_unref(char_u *name)
|
||||||
{
|
{
|
||||||
@ -22154,6 +22230,10 @@ void func_unref(char_u *name)
|
|||||||
// fail silently, when lambda function isn't found
|
// fail silently, when lambda function isn't found
|
||||||
fp = find_func(name);
|
fp = find_func(name);
|
||||||
}
|
}
|
||||||
|
if (fp == NULL && isdigit(*name)) {
|
||||||
|
EMSG2(_(e_intern2), "func_unref()");
|
||||||
|
}
|
||||||
|
|
||||||
if (fp != NULL && --fp->uf_refcount <= 0) {
|
if (fp != NULL && --fp->uf_refcount <= 0) {
|
||||||
// Only delete it when it's not being used. Otherwise it's done
|
// Only delete it when it's not being used. Otherwise it's done
|
||||||
// when "uf_calls" becomes zero.
|
// when "uf_calls" becomes zero.
|
||||||
@ -22163,10 +22243,12 @@ void func_unref(char_u *name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void user_func_unref(ufunc_T *fp)
|
/// Unreference a Function: decrement the reference count and free it when it
|
||||||
|
/// becomes zero.
|
||||||
|
void func_ptr_unref(ufunc_T *fp)
|
||||||
{
|
{
|
||||||
if (--fp->uf_refcount <= 0) {
|
if (fp != NULL && --fp->uf_refcount <= 0) {
|
||||||
// Only delete it when it's not being used. Otherwise it's done
|
// Only delete it when it's not being used. Otherwise it's done
|
||||||
// when "uf_calls" becomes zero.
|
// when "uf_calls" becomes zero.
|
||||||
if (fp->uf_calls == 0) {
|
if (fp->uf_calls == 0) {
|
||||||
func_free(fp);
|
func_free(fp);
|
||||||
@ -22174,36 +22256,35 @@ static void user_func_unref(ufunc_T *fp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Count a reference to a Function.
|
||||||
* Count a reference to a Function.
|
|
||||||
*/
|
|
||||||
void func_ref(char_u *name)
|
void func_ref(char_u *name)
|
||||||
{
|
{
|
||||||
ufunc_T *fp;
|
ufunc_T *fp;
|
||||||
|
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
fp = find_func(name);
|
||||||
|
if (fp != NULL) {
|
||||||
|
(fp->uf_refcount)++;
|
||||||
} else if (isdigit(*name)) {
|
} else if (isdigit(*name)) {
|
||||||
fp = find_func(name);
|
// Only give an error for a numbered function.
|
||||||
if (fp == NULL) {
|
// Fail silently, when named or lambda function isn't found.
|
||||||
EMSG2(_(e_intern2), "func_ref()");
|
EMSG2(_(e_intern2), "func_ref()");
|
||||||
} else {
|
|
||||||
(fp->uf_refcount)++;
|
|
||||||
}
|
|
||||||
} else if (STRNCMP(name, "<lambda>", 8) == 0) {
|
|
||||||
// fail silently, when lambda function isn't found.
|
|
||||||
fp = find_func(name);
|
|
||||||
if (fp != NULL) {
|
|
||||||
(fp->uf_refcount)++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/// Count a reference to a Function.
|
||||||
* Call a user function.
|
void func_ptr_ref(ufunc_T *fp)
|
||||||
*/
|
{
|
||||||
|
if (fp != NULL) {
|
||||||
|
(fp->uf_refcount)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a user function.
|
||||||
static void
|
static void
|
||||||
call_user_func (
|
call_user_func(
|
||||||
ufunc_T *fp, /* pointer to function */
|
ufunc_T *fp, /* pointer to function */
|
||||||
int argcount, /* nr of args */
|
int argcount, /* nr of args */
|
||||||
typval_T *argvars, /* arguments */
|
typval_T *argvars, /* arguments */
|
||||||
@ -22264,7 +22345,7 @@ call_user_func (
|
|||||||
fc->fc_refcount = 0;
|
fc->fc_refcount = 0;
|
||||||
fc->fc_copyID = 0;
|
fc->fc_copyID = 0;
|
||||||
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
|
ga_init(&fc->fc_funcs, sizeof(ufunc_T *), 1);
|
||||||
func_ref(fp->uf_name);
|
func_ptr_ref(fp);
|
||||||
|
|
||||||
if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
|
if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
|
||||||
islambda = true;
|
islambda = true;
|
||||||
@ -22579,9 +22660,7 @@ call_user_func (
|
|||||||
copy_tv(&li->li_tv, &li->li_tv);
|
copy_tv(&li->li_tv, &li->li_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name)
|
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) {
|
||||||
|| STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
|
|
||||||
&& fp->uf_refcount <= 0) {
|
|
||||||
// Function was unreferenced while being used, free it now.
|
// Function was unreferenced while being used, free it now.
|
||||||
func_free(fp);
|
func_free(fp);
|
||||||
}
|
}
|
||||||
@ -22605,33 +22684,32 @@ static void funccal_unref(funccall_T *fc, ufunc_T *fp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (--fc->fc_refcount <= 0) {
|
if (--fc->fc_refcount <= 0) {
|
||||||
for (pfc = &previous_funccal; *pfc != NULL; ) {
|
for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) {
|
||||||
if (fc == *pfc
|
if (fc == *pfc) {
|
||||||
&& fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
|
if (fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT
|
||||||
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
|
&& fc->l_vars.dv_refcount == DO_NOT_FREE_CNT
|
||||||
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
|
&& fc->l_avars.dv_refcount == DO_NOT_FREE_CNT) {
|
||||||
*pfc = fc->caller;
|
*pfc = fc->caller;
|
||||||
free_funccal(fc, true);
|
free_funccal(fc, true);
|
||||||
freed = true;
|
freed = true;
|
||||||
} else {
|
}
|
||||||
pfc = &(*pfc)->caller;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!freed) {
|
}
|
||||||
func_unref(fc->func->uf_name);
|
if (!freed) {
|
||||||
|
func_ptr_unref(fc->func);
|
||||||
|
|
||||||
if (fp != NULL) {
|
if (fp != NULL) {
|
||||||
for (i = 0; i < fc->fc_funcs.ga_len; i++) {
|
for (i = 0; i < fc->fc_funcs.ga_len; i++) {
|
||||||
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
|
if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) {
|
||||||
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
|
((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return TRUE if items in "fc" do not have "copyID". That means they are not
|
/// @return true if items in "fc" do not have "copyID". That means they are not
|
||||||
/// referenced from anywhere that is in use.
|
/// referenced from anywhere that is in use.
|
||||||
static int can_free_funccal(funccall_T *fc, int copyID)
|
static int can_free_funccal(funccall_T *fc, int copyID)
|
||||||
{
|
{
|
||||||
@ -22661,10 +22739,10 @@ free_funccal (
|
|||||||
if (fp->uf_scoped == fc) {
|
if (fp->uf_scoped == fc) {
|
||||||
fp->uf_scoped = NULL;
|
fp->uf_scoped = NULL;
|
||||||
}
|
}
|
||||||
func_unref(fc->func->uf_name);
|
func_ptr_unref(fc->func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ga_clear(&fc->fc_funcs):
|
ga_clear(&fc->fc_funcs);
|
||||||
|
|
||||||
// The a: variables typevals may not have been allocated, only free the
|
// The a: variables typevals may not have been allocated, only free the
|
||||||
// allocated variables.
|
// allocated variables.
|
||||||
@ -22680,7 +22758,7 @@ free_funccal (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func_unref(fc->func->uf_name);
|
func_ptr_unref(fc->func);
|
||||||
xfree(fc);
|
xfree(fc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,42 +12,6 @@
|
|||||||
|
|
||||||
// All user-defined functions are found in this hashtable.
|
// All user-defined functions are found in this hashtable.
|
||||||
extern hashtab_T func_hashtab;
|
extern hashtab_T func_hashtab;
|
||||||
|
|
||||||
typedef struct funccall_S funccall_T;
|
|
||||||
|
|
||||||
// Structure to hold info for a user function.
|
|
||||||
typedef struct ufunc ufunc_T;
|
|
||||||
|
|
||||||
struct ufunc {
|
|
||||||
int uf_varargs; ///< variable nr of arguments
|
|
||||||
int uf_flags;
|
|
||||||
int uf_calls; ///< nr of active calls
|
|
||||||
garray_T uf_args; ///< arguments
|
|
||||||
garray_T uf_lines; ///< function lines
|
|
||||||
int uf_profiling; ///< true when func is being profiled
|
|
||||||
// Profiling the function as a whole.
|
|
||||||
int uf_tm_count; ///< nr of calls
|
|
||||||
proftime_T uf_tm_total; ///< time spent in function + children
|
|
||||||
proftime_T uf_tm_self; ///< time spent in function itself
|
|
||||||
proftime_T uf_tm_children; ///< time spent in children this call
|
|
||||||
// Profiling the function per line.
|
|
||||||
int *uf_tml_count; ///< nr of times line was executed
|
|
||||||
proftime_T *uf_tml_total; ///< time spent in a line + children
|
|
||||||
proftime_T *uf_tml_self; ///< time spent in a line itself
|
|
||||||
proftime_T uf_tml_start; ///< start time for current line
|
|
||||||
proftime_T uf_tml_children; ///< time spent in children for this line
|
|
||||||
proftime_T uf_tml_wait; ///< start wait time for current line
|
|
||||||
int uf_tml_idx; ///< index of line being timed; -1 if none
|
|
||||||
int uf_tml_execed; ///< line being timed was executed
|
|
||||||
scid_T uf_script_ID; ///< ID of script where function was defined,
|
|
||||||
// used for s: variables
|
|
||||||
int uf_refcount; ///< for numbered function: reference count
|
|
||||||
funccall_T *uf_scoped; ///< l: local variables for closure
|
|
||||||
char_u uf_name[1]; ///< name of function (actually longer); can
|
|
||||||
// start with <SNR>123_ (<SNR> is K_SPECIAL
|
|
||||||
// KS_EXTRA KE_SNR)
|
|
||||||
};
|
|
||||||
|
|
||||||
// From user function to hashitem and back.
|
// From user function to hashitem and back.
|
||||||
EXTERN ufunc_T dumuf;
|
EXTERN ufunc_T dumuf;
|
||||||
#define UF2HIKEY(fp) ((fp)->uf_name)
|
#define UF2HIKEY(fp) ((fp)->uf_name)
|
||||||
@ -163,9 +127,6 @@ extern const list_T *eval_msgpack_type_lists[LAST_MSGPACK_TYPE + 1];
|
|||||||
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv,
|
typedef int (*ArgvFunc)(int current_argcount, typval_T *argv,
|
||||||
int called_func_argcount);
|
int called_func_argcount);
|
||||||
|
|
||||||
/// Maximum number of function arguments
|
|
||||||
#define MAX_FUNC_ARGS 20
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "eval.h.generated.h"
|
# include "eval.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -103,6 +103,7 @@ return {
|
|||||||
foldtext={},
|
foldtext={},
|
||||||
foldtextresult={args=1},
|
foldtextresult={args=1},
|
||||||
foreground={},
|
foreground={},
|
||||||
|
funcref={args={1, 3}},
|
||||||
['function']={args={1, 3}},
|
['function']={args={1, 3}},
|
||||||
garbagecollect={args={0, 1}},
|
garbagecollect={args={0, 1}},
|
||||||
get={args={2, 3}},
|
get={args={2, 3}},
|
||||||
|
@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "nvim/hashtab.h"
|
#include "nvim/hashtab.h"
|
||||||
#include "nvim/lib/queue.h"
|
#include "nvim/lib/queue.h"
|
||||||
|
#include "nvim/garray.h" // For garray_T
|
||||||
|
// for proftime_T
|
||||||
|
#include "nvim/profile.h"
|
||||||
|
#include "nvim/pos.h" // for linenr_T
|
||||||
|
|
||||||
typedef int varnumber_T;
|
typedef int varnumber_T;
|
||||||
typedef double float_T;
|
typedef double float_T;
|
||||||
@ -151,9 +155,86 @@ struct dictvar_S {
|
|||||||
QUEUE watchers; ///< Dictionary key watchers set by user code.
|
QUEUE watchers; ///< Dictionary key watchers set by user code.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef int scid_T; // script ID
|
||||||
|
typedef struct funccall_S funccall_T;
|
||||||
|
|
||||||
|
// Structure to hold info for a user function.
|
||||||
|
typedef struct ufunc ufunc_T;
|
||||||
|
|
||||||
|
struct ufunc {
|
||||||
|
int uf_varargs; ///< variable nr of arguments
|
||||||
|
int uf_flags;
|
||||||
|
int uf_calls; ///< nr of active calls
|
||||||
|
garray_T uf_args; ///< arguments
|
||||||
|
garray_T uf_lines; ///< function lines
|
||||||
|
int uf_profiling; ///< true when func is being profiled
|
||||||
|
// Profiling the function as a whole.
|
||||||
|
int uf_tm_count; ///< nr of calls
|
||||||
|
proftime_T uf_tm_total; ///< time spent in function + children
|
||||||
|
proftime_T uf_tm_self; ///< time spent in function itself
|
||||||
|
proftime_T uf_tm_children; ///< time spent in children this call
|
||||||
|
// Profiling the function per line.
|
||||||
|
int *uf_tml_count; ///< nr of times line was executed
|
||||||
|
proftime_T *uf_tml_total; ///< time spent in a line + children
|
||||||
|
proftime_T *uf_tml_self; ///< time spent in a line itself
|
||||||
|
proftime_T uf_tml_start; ///< start time for current line
|
||||||
|
proftime_T uf_tml_children; ///< time spent in children for this line
|
||||||
|
proftime_T uf_tml_wait; ///< start wait time for current line
|
||||||
|
int uf_tml_idx; ///< index of line being timed; -1 if none
|
||||||
|
int uf_tml_execed; ///< line being timed was executed
|
||||||
|
scid_T uf_script_ID; ///< ID of script where function was defined,
|
||||||
|
// used for s: variables
|
||||||
|
int uf_refcount; ///< for numbered function: reference count
|
||||||
|
funccall_T *uf_scoped; ///< l: local variables for closure
|
||||||
|
char_u uf_name[1]; ///< name of function (actually longer); can
|
||||||
|
// start with <SNR>123_ (<SNR> is K_SPECIAL
|
||||||
|
// KS_EXTRA KE_SNR)
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Maximum number of function arguments
|
||||||
|
#define MAX_FUNC_ARGS 20
|
||||||
|
#define VAR_SHORT_LEN 20 // short variable name length
|
||||||
|
#define FIXVAR_CNT 12 // number of fixed variables
|
||||||
|
|
||||||
|
// structure to hold info for a function that is currently being executed.
|
||||||
|
struct funccall_S {
|
||||||
|
ufunc_T *func; // function being called
|
||||||
|
int linenr; // next line to be executed
|
||||||
|
int returned; // ":return" used
|
||||||
|
struct // fixed variables for arguments
|
||||||
|
{
|
||||||
|
dictitem_T var; // variable (without room for name)
|
||||||
|
char_u room[VAR_SHORT_LEN]; // room for the name
|
||||||
|
} fixvar[FIXVAR_CNT];
|
||||||
|
dict_T l_vars; // l: local function variables
|
||||||
|
dictitem_T l_vars_var; // variable for l: scope
|
||||||
|
dict_T l_avars; // a: argument variables
|
||||||
|
dictitem_T l_avars_var; // variable for a: scope
|
||||||
|
list_T l_varlist; // list for a:000
|
||||||
|
listitem_T l_listitems[MAX_FUNC_ARGS]; // listitems for a:000
|
||||||
|
typval_T *rettv; // return value
|
||||||
|
linenr_T breakpoint; // next line with breakpoint or zero
|
||||||
|
int dbg_tick; // debug_tick when breakpoint was set
|
||||||
|
int level; // top nesting level of executed function
|
||||||
|
proftime_T prof_child; // time spent in a child
|
||||||
|
funccall_T *caller; // calling function or NULL
|
||||||
|
int fc_refcount;
|
||||||
|
int fc_copyID; // for garbage collection
|
||||||
|
garray_T fc_funcs; // list of ufunc_T* which refer this
|
||||||
|
};
|
||||||
|
|
||||||
|
// structure used by trans_function_name()
|
||||||
|
typedef struct {
|
||||||
|
dict_T *fd_dict; ///< Dictionary used.
|
||||||
|
char_u *fd_newkey; ///< New key in "dict" in allocated memory.
|
||||||
|
dictitem_T *fd_di; ///< Dictionary item used.
|
||||||
|
} funcdict_T;
|
||||||
|
|
||||||
struct partial_S {
|
struct partial_S {
|
||||||
int pt_refcount; ///< Reference count.
|
int pt_refcount; ///< Reference count.
|
||||||
char_u *pt_name; ///< Function name.
|
char_u *pt_name; ///< Function name; when NULL use pt_func->name.
|
||||||
|
ufunc_T *pt_func; ///< Function pointer; when NULL lookup function
|
||||||
|
///< with pt_name.
|
||||||
bool pt_auto; ///< when true the partial was created for using
|
bool pt_auto; ///< when true the partial was created for using
|
||||||
///< dict.member in handle_subscript().
|
///< dict.member in handle_subscript().
|
||||||
int pt_argc; ///< Number of arguments.
|
int pt_argc; ///< Number of arguments.
|
||||||
|
@ -6602,7 +6602,7 @@ static int vim_regsub_both(char_u *source, typval_T *expr, char_u *dest,
|
|||||||
} else if (expr->v_type == VAR_PARTIAL) {
|
} else if (expr->v_type == VAR_PARTIAL) {
|
||||||
partial_T *partial = expr->vval.v_partial;
|
partial_T *partial = expr->vval.v_partial;
|
||||||
|
|
||||||
s = partial->pt_name;
|
s = partial_name(partial);
|
||||||
call_func(s, (int)STRLEN(s), &rettv, 1, argv,
|
call_func(s, (int)STRLEN(s), &rettv, 1, argv,
|
||||||
fill_submatch_list, 0L, 0L, &dummy,
|
fill_submatch_list, 0L, 0L, &dummy,
|
||||||
true, partial, NULL);
|
true, partial, NULL);
|
||||||
|
@ -150,3 +150,18 @@ func Test_function_with_funcref()
|
|||||||
call assert_equal(v:t_string, s:fref('x'))
|
call assert_equal(v:t_string, s:fref('x'))
|
||||||
call assert_fails("call function('s:f')", 'E700:')
|
call assert_fails("call function('s:f')", 'E700:')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_funcref()
|
||||||
|
func! One()
|
||||||
|
return 1
|
||||||
|
endfunc
|
||||||
|
let OneByName = function('One')
|
||||||
|
let OneByRef = funcref('One')
|
||||||
|
func! One()
|
||||||
|
return 2
|
||||||
|
endfunc
|
||||||
|
call assert_equal(2, OneByName())
|
||||||
|
call assert_equal(1, OneByRef())
|
||||||
|
let OneByRef = funcref('One')
|
||||||
|
call assert_equal(2, OneByRef())
|
||||||
|
endfunc
|
||||||
|
@ -259,10 +259,10 @@ endfunction
|
|||||||
|
|
||||||
func Test_closure_refcount()
|
func Test_closure_refcount()
|
||||||
let g:Count = LambdaFoo()
|
let g:Count = LambdaFoo()
|
||||||
call test_garbagecollect_now()
|
call garbagecollect()
|
||||||
call assert_equal(1, g:Count())
|
call assert_equal(1, g:Count())
|
||||||
let g:Count2 = LambdaFoo()
|
let g:Count2 = LambdaFoo()
|
||||||
call test_garbagecollect_now()
|
call garbagecollect()
|
||||||
call assert_equal(1, g:Count2())
|
call assert_equal(1, g:Count2())
|
||||||
call assert_equal(2, g:Count())
|
call assert_equal(2, g:Count())
|
||||||
call assert_equal(3, g:Count2())
|
call assert_equal(3, g:Count2())
|
||||||
|
@ -303,7 +303,7 @@ static int included_patches[] = {
|
|||||||
// 2140 NA
|
// 2140 NA
|
||||||
// 2139,
|
// 2139,
|
||||||
// 2138 NA
|
// 2138 NA
|
||||||
// 2137,
|
2137,
|
||||||
2136,
|
2136,
|
||||||
// 2135,
|
// 2135,
|
||||||
2134,
|
2134,
|
||||||
|
Loading…
Reference in New Issue
Block a user