1
0
forked from aniani/vim

patch 8.2.1023: Vim9: redefining a function uses a new index every time

Problem:    Vim9: redefining a function uses a new index every time.
Solution:   When redefining a function clear the contents and re-use the
            index.
This commit is contained in:
Bram Moolenaar 2020-06-20 18:19:09 +02:00
parent 845e0ee594
commit 0cb5bcf583
8 changed files with 57 additions and 37 deletions

View File

@ -253,7 +253,7 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
return FAIL; return FAIL;
if (partial->pt_func != NULL if (partial->pt_func != NULL
&& partial->pt_func->uf_dfunc_idx != UF_NOT_COMPILED) && partial->pt_func->uf_def_status != UF_NOT_COMPILED)
{ {
if (call_def_function(partial->pt_func, argc, argv, if (call_def_function(partial->pt_func, argc, argv,
partial, rettv) == FAIL) partial, rettv) == FAIL)

View File

@ -2628,7 +2628,7 @@ find_var_ht(char_u *name, char_u **varname)
if (*name == 'v') // v: variable if (*name == 'v') // v: variable
return &vimvarht; return &vimvarht;
if (get_current_funccal() != NULL if (get_current_funccal() != NULL
&& get_current_funccal()->func->uf_dfunc_idx == UF_NOT_COMPILED) && get_current_funccal()->func->uf_def_status == UF_NOT_COMPILED)
{ {
// a: and l: are only used in functions defined with ":function" // a: and l: are only used in functions defined with ":function"
if (*name == 'a') // a: function argument if (*name == 'a') // a: function argument

View File

@ -14,6 +14,6 @@ int check_vim9_unlet(char_u *name);
int compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx); int compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx);
void set_function_type(ufunc_T *ufunc); void set_function_type(ufunc_T *ufunc);
void delete_instr(isn_T *isn); void delete_instr(isn_T *isn);
void delete_def_function(ufunc_T *ufunc); void clear_def_function(ufunc_T *ufunc);
void free_def_functions(void); void free_def_functions(void);
/* vim: set ft=c : */ /* vim: set ft=c : */

View File

@ -1531,8 +1531,11 @@ struct blobvar_S
typedef struct funccall_S funccall_T; typedef struct funccall_S funccall_T;
// values used for "uf_dfunc_idx" // values used for "uf_dfunc_idx"
# define UF_NOT_COMPILED -2 typedef enum {
# define UF_TO_BE_COMPILED -1 UF_NOT_COMPILED,
UF_TO_BE_COMPILED,
UF_COMPILED
} def_status_T;
/* /*
* Structure to hold info for a user function. * Structure to hold info for a user function.
@ -1543,7 +1546,8 @@ typedef struct
int uf_flags; // FC_ flags int uf_flags; // FC_ flags
int uf_calls; // nr of active calls int uf_calls; // nr of active calls
int uf_cleared; // func_clear() was already called int uf_cleared; // func_clear() was already called
int uf_dfunc_idx; // UF_NOT_COMPILED, UF_TO_BE_COMPILED or >= 0 def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc.
int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED
garray_T uf_args; // arguments, including optional arguments garray_T uf_args; // arguments, including optional arguments
garray_T uf_def_args; // default argument expressions garray_T uf_def_args; // default argument expressions

View File

@ -409,7 +409,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (fp == NULL) if (fp == NULL)
goto errret; goto errret;
fp->uf_dfunc_idx = UF_NOT_COMPILED; fp->uf_def_status = UF_NOT_COMPILED;
pt = ALLOC_CLEAR_ONE(partial_T); pt = ALLOC_CLEAR_ONE(partial_T);
if (pt == NULL) if (pt == NULL)
goto errret; goto errret;
@ -1001,7 +1001,7 @@ func_remove(ufunc_T *fp)
{ {
// When there is a def-function index do not actually remove the // When there is a def-function index do not actually remove the
// function, so we can find the index when defining the function again. // function, so we can find the index when defining the function again.
if (fp->uf_dfunc_idx >= 0) if (fp->uf_def_status == UF_COMPILED)
fp->uf_flags |= FC_DEAD; fp->uf_flags |= FC_DEAD;
else else
hash_remove(&func_hashtab, hi); hash_remove(&func_hashtab, hi);
@ -1046,7 +1046,7 @@ func_clear(ufunc_T *fp, int force)
// clear this function // clear this function
func_clear_items(fp); func_clear_items(fp);
funccal_unref(fp->uf_scoped, fp, force); funccal_unref(fp->uf_scoped, fp, force);
delete_def_function(fp); clear_def_function(fp);
} }
/* /*
@ -1074,6 +1074,7 @@ func_free(ufunc_T *fp, int force)
func_clear_free(ufunc_T *fp, int force) func_clear_free(ufunc_T *fp, int force)
{ {
func_clear(fp, force); func_clear(fp, force);
if (force || fp->uf_dfunc_idx == 0)
func_free(fp, force); func_free(fp, force);
} }
@ -1137,7 +1138,7 @@ call_user_func(
ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1); ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp); func_ptr_ref(fp);
if (fp->uf_dfunc_idx != UF_NOT_COMPILED) if (fp->uf_def_status != UF_NOT_COMPILED)
{ {
estack_push_ufunc(fp, 1); estack_push_ufunc(fp, 1);
save_current_sctx = current_sctx; save_current_sctx = current_sctx;
@ -1662,7 +1663,7 @@ free_all_functions(void)
// clear the def function index now // clear the def function index now
fp = HI2UF(hi); fp = HI2UF(hi);
fp->uf_flags &= ~FC_DEAD; fp->uf_flags &= ~FC_DEAD;
fp->uf_dfunc_idx = UF_NOT_COMPILED; fp->uf_def_status = UF_NOT_COMPILED;
// Only free functions that are not refcounted, those are // Only free functions that are not refcounted, those are
// supposed to be freed when no longer referenced. // supposed to be freed when no longer referenced.
@ -2058,7 +2059,7 @@ list_func_head(ufunc_T *fp, int indent)
msg_start(); msg_start();
if (indent) if (indent)
msg_puts(" "); msg_puts(" ");
if (fp->uf_dfunc_idx != UF_NOT_COMPILED) if (fp->uf_def_status != UF_NOT_COMPILED)
msg_puts("def "); msg_puts("def ");
else else
msg_puts("function "); msg_puts("function ");
@ -2107,7 +2108,7 @@ list_func_head(ufunc_T *fp, int indent)
} }
msg_putchar(')'); msg_putchar(')');
if (fp->uf_dfunc_idx != UF_NOT_COMPILED) if (fp->uf_def_status != UF_NOT_COMPILED)
{ {
if (fp->uf_ret_type != &t_void) if (fp->uf_ret_type != &t_void)
{ {
@ -2624,7 +2625,7 @@ def_function(exarg_T *eap, char_u *name_arg)
if (!got_int) if (!got_int)
{ {
msg_putchar('\n'); msg_putchar('\n');
if (fp->uf_dfunc_idx != UF_NOT_COMPILED) if (fp->uf_def_status != UF_NOT_COMPILED)
msg_puts(" enddef"); msg_puts(" enddef");
else else
msg_puts(" endfunction"); msg_puts(" endfunction");
@ -3097,6 +3098,7 @@ def_function(exarg_T *eap, char_u *name_arg)
fp->uf_profiling = FALSE; fp->uf_profiling = FALSE;
fp->uf_prof_initialized = FALSE; fp->uf_prof_initialized = FALSE;
#endif #endif
clear_def_function(fp);
} }
} }
} }
@ -3162,7 +3164,7 @@ def_function(exarg_T *eap, char_u *name_arg)
fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (fp == NULL) if (fp == NULL)
goto erret; goto erret;
fp->uf_dfunc_idx = eap->cmdidx == CMD_def ? UF_TO_BE_COMPILED fp->uf_def_status = eap->cmdidx == CMD_def ? UF_TO_BE_COMPILED
: UF_NOT_COMPILED; : UF_NOT_COMPILED;
if (fudi.fd_dict != NULL) if (fudi.fd_dict != NULL)
@ -3219,7 +3221,7 @@ def_function(exarg_T *eap, char_u *name_arg)
{ {
int lnum_save = SOURCING_LNUM; int lnum_save = SOURCING_LNUM;
fp->uf_dfunc_idx = UF_TO_BE_COMPILED; fp->uf_def_status = UF_TO_BE_COMPILED;
// error messages are for the first function line // error messages are for the first function line
SOURCING_LNUM = sourcing_lnum_top; SOURCING_LNUM = sourcing_lnum_top;
@ -3289,7 +3291,7 @@ def_function(exarg_T *eap, char_u *name_arg)
SOURCING_LNUM = lnum_save; SOURCING_LNUM = lnum_save;
} }
else else
fp->uf_dfunc_idx = UF_NOT_COMPILED; fp->uf_def_status = UF_NOT_COMPILED;
fp->uf_lines = newlines; fp->uf_lines = newlines;
if ((flags & FC_CLOSURE) != 0) if ((flags & FC_CLOSURE) != 0)
@ -3372,7 +3374,7 @@ ex_defcompile(exarg_T *eap UNUSED)
--todo; --todo;
ufunc = HI2UF(hi); ufunc = HI2UF(hi);
if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
&& ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED) && ufunc->uf_def_status == UF_TO_BE_COMPILED)
{ {
compile_def_function(ufunc, FALSE, NULL); compile_def_function(ufunc, FALSE, NULL);

View File

@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1023,
/**/ /**/
1022, 1022,
/**/ /**/

View File

@ -1493,7 +1493,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
return FAIL; return FAIL;
} }
if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED) if (ufunc->uf_def_status != UF_NOT_COMPILED)
{ {
int i; int i;
@ -1517,16 +1517,16 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
return FAIL; return FAIL;
} }
} }
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED) if (ufunc->uf_def_status == UF_TO_BE_COMPILED)
if (compile_def_function(ufunc, TRUE, NULL) == FAIL) if (compile_def_function(ufunc, TRUE, NULL) == FAIL)
return FAIL; return FAIL;
} }
if ((isn = generate_instr(cctx, if ((isn = generate_instr(cctx,
ufunc->uf_dfunc_idx != UF_NOT_COMPILED ? ISN_DCALL ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
: ISN_UCALL)) == NULL) : ISN_UCALL)) == NULL)
return FAIL; return FAIL;
if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED) if (ufunc->uf_def_status != UF_NOT_COMPILED)
{ {
isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx; isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
isn->isn_arg.dfunc.cdf_argcount = argcount; isn->isn_arg.dfunc.cdf_argcount = argcount;
@ -3042,7 +3042,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
// Compile it into instructions. // Compile it into instructions.
compile_def_function(ufunc, TRUE, cctx); compile_def_function(ufunc, TRUE, cctx);
if (ufunc->uf_dfunc_idx >= 0) if (ufunc->uf_def_status == UF_COMPILED)
return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx); return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx);
return FAIL; return FAIL;
} }
@ -4539,7 +4539,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
if (ufunc == NULL) if (ufunc == NULL)
return NULL; return NULL;
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, TRUE, cctx) == FAIL) && compile_def_function(ufunc, TRUE, cctx) == FAIL)
return NULL; return NULL;
@ -6517,13 +6517,22 @@ theend:
/* /*
* Add a function to the list of :def functions. * Add a function to the list of :def functions.
* This "sets ufunc->uf_dfunc_idx" but the function isn't compiled yet. * This sets "ufunc->uf_dfunc_idx" but the function isn't compiled yet.
*/ */
static int static int
add_def_function(ufunc_T *ufunc) add_def_function(ufunc_T *ufunc)
{ {
dfunc_T *dfunc; dfunc_T *dfunc;
if (def_functions.ga_len == 0)
{
// The first position is not used, so that a zero uf_dfunc_idx means it
// wasn't set.
if (ga_grow(&def_functions, 1) == FAIL)
return FAIL;
++def_functions.ga_len;
}
// Add the function to "def_functions". // Add the function to "def_functions".
if (ga_grow(&def_functions, 1) == FAIL) if (ga_grow(&def_functions, 1) == FAIL)
return FAIL; return FAIL;
@ -6563,7 +6572,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
// When using a function that was compiled before: Free old instructions. // When using a function that was compiled before: Free old instructions.
// Otherwise add a new entry in "def_functions". // Otherwise add a new entry in "def_functions".
if (ufunc->uf_dfunc_idx >= 0) if (ufunc->uf_dfunc_idx > 0)
{ {
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx; + ufunc->uf_dfunc_idx;
@ -7014,6 +7023,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
dfunc->df_closure_count = cctx.ctx_closure_count; dfunc->df_closure_count = cctx.ctx_closure_count;
if (cctx.ctx_outer_used) if (cctx.ctx_outer_used)
ufunc->uf_flags |= FC_CLOSURE; ufunc->uf_flags |= FC_CLOSURE;
ufunc->uf_def_status = UF_COMPILED;
} }
ret = OK; ret = OK;
@ -7033,7 +7043,7 @@ erret:
if (!dfunc->df_deleted if (!dfunc->df_deleted
&& ufunc->uf_dfunc_idx == def_functions.ga_len - 1) && ufunc->uf_dfunc_idx == def_functions.ga_len - 1)
--def_functions.ga_len; --def_functions.ga_len;
ufunc->uf_dfunc_idx = UF_NOT_COMPILED; ufunc->uf_def_status = UF_NOT_COMPILED;
while (cctx.ctx_scope != NULL) while (cctx.ctx_scope != NULL)
drop_scope(&cctx); drop_scope(&cctx);
@ -7261,17 +7271,19 @@ delete_def_function_contents(dfunc_T *dfunc)
} }
/* /*
* When a user function is deleted, delete any associated def function. * When a user function is deleted, clear the contents of any associated def
* function. The position in def_functions can be re-used.
*/ */
void void
delete_def_function(ufunc_T *ufunc) clear_def_function(ufunc_T *ufunc)
{ {
if (ufunc->uf_dfunc_idx >= 0) if (ufunc->uf_dfunc_idx > 0)
{ {
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx; + ufunc->uf_dfunc_idx;
delete_def_function_contents(dfunc); delete_def_function_contents(dfunc);
ufunc->uf_def_status = UF_NOT_COMPILED;
} }
} }

View File

@ -487,10 +487,10 @@ call_ufunc(ufunc_T *ufunc, int argcount, ectx_T *ectx, isn_T *iptr)
int error; int error;
int idx; int idx;
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL) && compile_def_function(ufunc, FALSE, NULL) == FAIL)
return FAIL; return FAIL;
if (ufunc->uf_dfunc_idx >= 0) if (ufunc->uf_def_status == UF_COMPILED)
{ {
// The function has been compiled, can call it quickly. For a function // The function has been compiled, can call it quickly. For a function
// that was defined later: we can call it directly next time. // that was defined later: we can call it directly next time.
@ -671,8 +671,8 @@ call_def_function(
// Like STACK_TV_VAR but use the outer scope // Like STACK_TV_VAR but use the outer scope
#define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + ectx.ec_outer_frame + STACK_FRAME_SIZE + idx) #define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) + ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
if (ufunc->uf_dfunc_idx == UF_NOT_COMPILED if (ufunc->uf_def_status == UF_NOT_COMPILED
|| (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED || (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL)) && compile_def_function(ufunc, FALSE, NULL) == FAIL))
{ {
if (called_emsg == called_emsg_before) if (called_emsg == called_emsg_before)
@ -2379,10 +2379,10 @@ ex_disassemble(exarg_T *eap)
semsg(_("E1061: Cannot find function %s"), eap->arg); semsg(_("E1061: Cannot find function %s"), eap->arg);
return; return;
} }
if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED if (ufunc->uf_def_status == UF_TO_BE_COMPILED
&& compile_def_function(ufunc, FALSE, NULL) == FAIL) && compile_def_function(ufunc, FALSE, NULL) == FAIL)
return; return;
if (ufunc->uf_dfunc_idx < 0) if (ufunc->uf_def_status != UF_COMPILED)
{ {
semsg(_("E1062: Function %s is not compiled"), eap->arg); semsg(_("E1062: Function %s is not compiled"), eap->arg);
return; return;