# HG changeset patch # User Bram Moolenaar # Date 1592670604 -7200 # Node ID 1693ca87604958e63e3696860f15b0caa6b863ff # Parent 484ce6d147343448c695665306c9106a9b7c8f3a patch 8.2.1023: Vim9: redefining a function uses a new index every time Commit: https://github.com/vim/vim/commit/0cb5bcf5836de83f7d64fb01d3ce708caacaf66c Author: Bram Moolenaar Date: Sat Jun 20 18:19:09 2020 +0200 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. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -253,7 +253,7 @@ eval_expr_typval(typval_T *expr, typval_ return FAIL; 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, partial, rettv) == FAIL) diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -2628,7 +2628,7 @@ find_var_ht(char_u *name, char_u **varna if (*name == 'v') // v: variable return &vimvarht; 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" if (*name == 'a') // a: function argument diff --git a/src/proto/vim9compile.pro b/src/proto/vim9compile.pro --- a/src/proto/vim9compile.pro +++ b/src/proto/vim9compile.pro @@ -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); void set_function_type(ufunc_T *ufunc); 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); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1531,8 +1531,11 @@ struct blobvar_S typedef struct funccall_S funccall_T; // values used for "uf_dfunc_idx" -# define UF_NOT_COMPILED -2 -# define UF_TO_BE_COMPILED -1 +typedef enum { + UF_NOT_COMPILED, + UF_TO_BE_COMPILED, + UF_COMPILED +} def_status_T; /* * Structure to hold info for a user function. @@ -1543,7 +1546,8 @@ typedef struct int uf_flags; // FC_ flags int uf_calls; // nr of active calls 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_def_args; // default argument expressions diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -409,7 +409,7 @@ get_lambda_tv(char_u **arg, typval_T *re fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) goto errret; - fp->uf_dfunc_idx = UF_NOT_COMPILED; + fp->uf_def_status = UF_NOT_COMPILED; pt = ALLOC_CLEAR_ONE(partial_T); if (pt == NULL) goto errret; @@ -1001,7 +1001,7 @@ func_remove(ufunc_T *fp) { // When there is a def-function index do not actually remove the // 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; else hash_remove(&func_hashtab, hi); @@ -1046,7 +1046,7 @@ func_clear(ufunc_T *fp, int force) // clear this function func_clear_items(fp); funccal_unref(fp->uf_scoped, fp, force); - delete_def_function(fp); + clear_def_function(fp); } /* @@ -1074,7 +1074,8 @@ func_free(ufunc_T *fp, int force) func_clear_free(ufunc_T *fp, int force) { func_clear(fp, force); - func_free(fp, force); + if (force || fp->uf_dfunc_idx == 0) + func_free(fp, force); } @@ -1137,7 +1138,7 @@ call_user_func( ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1); 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); save_current_sctx = current_sctx; @@ -1662,7 +1663,7 @@ free_all_functions(void) // clear the def function index now fp = HI2UF(hi); 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 // supposed to be freed when no longer referenced. @@ -2058,7 +2059,7 @@ list_func_head(ufunc_T *fp, int indent) msg_start(); if (indent) msg_puts(" "); - if (fp->uf_dfunc_idx != UF_NOT_COMPILED) + if (fp->uf_def_status != UF_NOT_COMPILED) msg_puts("def "); else msg_puts("function "); @@ -2107,7 +2108,7 @@ list_func_head(ufunc_T *fp, int indent) } 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) { @@ -2624,7 +2625,7 @@ def_function(exarg_T *eap, char_u *name_ if (!got_int) { msg_putchar('\n'); - if (fp->uf_dfunc_idx != UF_NOT_COMPILED) + if (fp->uf_def_status != UF_NOT_COMPILED) msg_puts(" enddef"); else msg_puts(" endfunction"); @@ -3097,6 +3098,7 @@ def_function(exarg_T *eap, char_u *name_ fp->uf_profiling = FALSE; fp->uf_prof_initialized = FALSE; #endif + clear_def_function(fp); } } } @@ -3162,7 +3164,7 @@ def_function(exarg_T *eap, char_u *name_ fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) 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; if (fudi.fd_dict != NULL) @@ -3219,7 +3221,7 @@ def_function(exarg_T *eap, char_u *name_ { 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 SOURCING_LNUM = sourcing_lnum_top; @@ -3289,7 +3291,7 @@ def_function(exarg_T *eap, char_u *name_ SOURCING_LNUM = lnum_save; } else - fp->uf_dfunc_idx = UF_NOT_COMPILED; + fp->uf_def_status = UF_NOT_COMPILED; fp->uf_lines = newlines; if ((flags & FC_CLOSURE) != 0) @@ -3372,7 +3374,7 @@ ex_defcompile(exarg_T *eap UNUSED) --todo; ufunc = HI2UF(hi); 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); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -755,6 +755,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1023, +/**/ 1022, /**/ 1021, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1493,7 +1493,7 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu return FAIL; } - if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED) + if (ufunc->uf_def_status != UF_NOT_COMPILED) { int i; @@ -1517,16 +1517,16 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufu 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) return FAIL; } 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) 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_argcount = argcount; @@ -3042,7 +3042,7 @@ compile_lambda(char_u **arg, cctx_T *cct // Compile it into instructions. 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 FAIL; } @@ -4539,7 +4539,7 @@ compile_nested_function(exarg_T *eap, cc if (ufunc == 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) return NULL; @@ -6517,13 +6517,22 @@ theend: /* * 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 add_def_function(ufunc_T *ufunc) { 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". if (ga_grow(&def_functions, 1) == FAIL) return FAIL; @@ -6563,7 +6572,7 @@ compile_def_function(ufunc_T *ufunc, int // When using a function that was compiled before: Free old instructions. // 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) + ufunc->uf_dfunc_idx; @@ -7014,6 +7023,7 @@ compile_def_function(ufunc_T *ufunc, int dfunc->df_closure_count = cctx.ctx_closure_count; if (cctx.ctx_outer_used) ufunc->uf_flags |= FC_CLOSURE; + ufunc->uf_def_status = UF_COMPILED; } ret = OK; @@ -7033,7 +7043,7 @@ erret: if (!dfunc->df_deleted && ufunc->uf_dfunc_idx == def_functions.ga_len - 1) --def_functions.ga_len; - ufunc->uf_dfunc_idx = UF_NOT_COMPILED; + ufunc->uf_def_status = UF_NOT_COMPILED; while (cctx.ctx_scope != NULL) drop_scope(&cctx); @@ -7261,17 +7271,19 @@ delete_def_function_contents(dfunc_T *df } /* - * 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 -delete_def_function(ufunc_T *ufunc) -{ - if (ufunc->uf_dfunc_idx >= 0) +clear_def_function(ufunc_T *ufunc) +{ + if (ufunc->uf_dfunc_idx > 0) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; delete_def_function_contents(dfunc); + ufunc->uf_def_status = UF_NOT_COMPILED; } } diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -487,10 +487,10 @@ call_ufunc(ufunc_T *ufunc, int argcount, int error; 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) 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 // 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 #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 - || (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED + if (ufunc->uf_def_status == UF_NOT_COMPILED + || (ufunc->uf_def_status == UF_TO_BE_COMPILED && compile_def_function(ufunc, FALSE, NULL) == FAIL)) { if (called_emsg == called_emsg_before) @@ -2379,10 +2379,10 @@ ex_disassemble(exarg_T *eap) semsg(_("E1061: Cannot find function %s"), eap->arg); 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) return; - if (ufunc->uf_dfunc_idx < 0) + if (ufunc->uf_def_status != UF_COMPILED) { semsg(_("E1062: Function %s is not compiled"), eap->arg); return;