To: vim_dev@googlegroups.com Subject: Patch 8.2.1023 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1023 Problem: Vim9: redefining a function uses a new index every time. Solution: When redefining a function clear the contents and re-use the index. Files: src/vim9compile.c, src/proto/vim9compile.pro, src/userfunc.c, src/structs.h, src/eval.c, src/evalvars.c, src/vim9execute.c *** ../vim-8.2.1022/src/vim9compile.c 2020-06-20 13:28:59.789336842 +0200 --- src/vim9compile.c 2020-06-20 18:08:25.626738128 +0200 *************** *** 1493,1499 **** return FAIL; } ! if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED) { int i; --- 1493,1499 ---- return FAIL; } ! if (ufunc->uf_def_status != UF_NOT_COMPILED) { int i; *************** *** 1517,1532 **** return FAIL; } } ! if (ufunc->uf_dfunc_idx == 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 : ISN_UCALL)) == NULL) return FAIL; ! if (ufunc->uf_dfunc_idx != UF_NOT_COMPILED) { isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx; isn->isn_arg.dfunc.cdf_argcount = argcount; --- 1517,1532 ---- return FAIL; } } ! 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_def_status != UF_NOT_COMPILED ? ISN_DCALL : ISN_UCALL)) == NULL) return FAIL; ! 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,3048 **** // Compile it into instructions. compile_def_function(ufunc, TRUE, cctx); ! if (ufunc->uf_dfunc_idx >= 0) return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx); return FAIL; } --- 3042,3048 ---- // Compile it into instructions. compile_def_function(ufunc, TRUE, cctx); ! if (ufunc->uf_def_status == UF_COMPILED) return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx); return FAIL; } *************** *** 4539,4545 **** if (ufunc == NULL) return NULL; ! if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED && compile_def_function(ufunc, TRUE, cctx) == FAIL) return NULL; --- 4539,4545 ---- if (ufunc == NULL) return NULL; ! if (ufunc->uf_def_status == UF_TO_BE_COMPILED && compile_def_function(ufunc, TRUE, cctx) == FAIL) return NULL; *************** *** 6517,6529 **** /* * Add a function to the list of :def functions. ! * This "sets ufunc->uf_dfunc_idx" but the function isn't compiled yet. */ static int add_def_function(ufunc_T *ufunc) { dfunc_T *dfunc; // Add the function to "def_functions". if (ga_grow(&def_functions, 1) == FAIL) return FAIL; --- 6517,6538 ---- /* * Add a function to the list of :def functions. ! * 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,6569 **** // 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) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; --- 6572,6578 ---- // 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) { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; *************** *** 7014,7019 **** --- 7023,7029 ---- 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,7039 **** if (!dfunc->df_deleted && ufunc->uf_dfunc_idx == def_functions.ga_len - 1) --def_functions.ga_len; ! ufunc->uf_dfunc_idx = UF_NOT_COMPILED; while (cctx.ctx_scope != NULL) drop_scope(&cctx); --- 7043,7049 ---- if (!dfunc->df_deleted && ufunc->uf_dfunc_idx == def_functions.ga_len - 1) --def_functions.ga_len; ! ufunc->uf_def_status = UF_NOT_COMPILED; while (cctx.ctx_scope != NULL) drop_scope(&cctx); *************** *** 7261,7277 **** } /* ! * When a user function is deleted, delete any associated def function. */ void ! delete_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); } } --- 7271,7289 ---- } /* ! * When a user function is deleted, clear the contents of any associated def ! * function. The position in def_functions can be re-used. */ void ! 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; } } *** ../vim-8.2.1022/src/proto/vim9compile.pro 2020-06-13 19:00:06.887160162 +0200 --- src/proto/vim9compile.pro 2020-06-20 17:09:03.149877236 +0200 *************** *** 14,19 **** 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 free_def_functions(void); /* vim: set ft=c : */ --- 14,19 ---- 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 clear_def_function(ufunc_T *ufunc); void free_def_functions(void); /* vim: set ft=c : */ *** ../vim-8.2.1022/src/userfunc.c 2020-06-12 22:59:07.274097177 +0200 --- src/userfunc.c 2020-06-20 18:03:30.735597023 +0200 *************** *** 409,415 **** fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) goto errret; ! fp->uf_dfunc_idx = UF_NOT_COMPILED; pt = ALLOC_CLEAR_ONE(partial_T); if (pt == NULL) goto errret; --- 409,415 ---- fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) goto errret; ! fp->uf_def_status = UF_NOT_COMPILED; pt = ALLOC_CLEAR_ONE(partial_T); if (pt == NULL) goto errret; *************** *** 1001,1007 **** { // 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) fp->uf_flags |= FC_DEAD; else hash_remove(&func_hashtab, hi); --- 1001,1007 ---- { // 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_def_status == UF_COMPILED) fp->uf_flags |= FC_DEAD; else hash_remove(&func_hashtab, hi); *************** *** 1046,1052 **** // clear this function func_clear_items(fp); funccal_unref(fp->uf_scoped, fp, force); ! delete_def_function(fp); } /* --- 1046,1052 ---- // clear this function func_clear_items(fp); funccal_unref(fp->uf_scoped, fp, force); ! clear_def_function(fp); } /* *************** *** 1074,1080 **** func_clear_free(ufunc_T *fp, int force) { func_clear(fp, force); ! func_free(fp, force); } --- 1074,1081 ---- func_clear_free(ufunc_T *fp, int force) { func_clear(fp, force); ! if (force || fp->uf_dfunc_idx == 0) ! func_free(fp, force); } *************** *** 1137,1143 **** ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1); func_ptr_ref(fp); ! if (fp->uf_dfunc_idx != UF_NOT_COMPILED) { estack_push_ufunc(fp, 1); save_current_sctx = current_sctx; --- 1138,1144 ---- ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1); func_ptr_ref(fp); ! if (fp->uf_def_status != UF_NOT_COMPILED) { estack_push_ufunc(fp, 1); save_current_sctx = current_sctx; *************** *** 1662,1668 **** // clear the def function index now fp = HI2UF(hi); fp->uf_flags &= ~FC_DEAD; ! fp->uf_dfunc_idx = UF_NOT_COMPILED; // Only free functions that are not refcounted, those are // supposed to be freed when no longer referenced. --- 1663,1669 ---- // clear the def function index now fp = HI2UF(hi); fp->uf_flags &= ~FC_DEAD; ! 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,2064 **** msg_start(); if (indent) msg_puts(" "); ! if (fp->uf_dfunc_idx != UF_NOT_COMPILED) msg_puts("def "); else msg_puts("function "); --- 2059,2065 ---- msg_start(); if (indent) msg_puts(" "); ! if (fp->uf_def_status != UF_NOT_COMPILED) msg_puts("def "); else msg_puts("function "); *************** *** 2107,2113 **** } msg_putchar(')'); ! if (fp->uf_dfunc_idx != UF_NOT_COMPILED) { if (fp->uf_ret_type != &t_void) { --- 2108,2114 ---- } msg_putchar(')'); ! if (fp->uf_def_status != UF_NOT_COMPILED) { if (fp->uf_ret_type != &t_void) { *************** *** 2624,2630 **** if (!got_int) { msg_putchar('\n'); ! if (fp->uf_dfunc_idx != UF_NOT_COMPILED) msg_puts(" enddef"); else msg_puts(" endfunction"); --- 2625,2631 ---- if (!got_int) { msg_putchar('\n'); ! if (fp->uf_def_status != UF_NOT_COMPILED) msg_puts(" enddef"); else msg_puts(" endfunction"); *************** *** 3097,3102 **** --- 3098,3104 ---- fp->uf_profiling = FALSE; fp->uf_prof_initialized = FALSE; #endif + clear_def_function(fp); } } } *************** *** 3162,3168 **** 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 : UF_NOT_COMPILED; if (fudi.fd_dict != NULL) --- 3164,3170 ---- fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) goto erret; ! fp->uf_def_status = eap->cmdidx == CMD_def ? UF_TO_BE_COMPILED : UF_NOT_COMPILED; if (fudi.fd_dict != NULL) *************** *** 3219,3225 **** { int lnum_save = SOURCING_LNUM; ! fp->uf_dfunc_idx = UF_TO_BE_COMPILED; // error messages are for the first function line SOURCING_LNUM = sourcing_lnum_top; --- 3221,3227 ---- { int lnum_save = SOURCING_LNUM; ! fp->uf_def_status = UF_TO_BE_COMPILED; // error messages are for the first function line SOURCING_LNUM = sourcing_lnum_top; *************** *** 3289,3295 **** SOURCING_LNUM = lnum_save; } else ! fp->uf_dfunc_idx = UF_NOT_COMPILED; fp->uf_lines = newlines; if ((flags & FC_CLOSURE) != 0) --- 3291,3297 ---- SOURCING_LNUM = lnum_save; } else ! fp->uf_def_status = UF_NOT_COMPILED; fp->uf_lines = newlines; if ((flags & FC_CLOSURE) != 0) *************** *** 3372,3378 **** --todo; ufunc = HI2UF(hi); if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid ! && ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED) { compile_def_function(ufunc, FALSE, NULL); --- 3374,3380 ---- --todo; ufunc = HI2UF(hi); if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid ! && ufunc->uf_def_status == UF_TO_BE_COMPILED) { compile_def_function(ufunc, FALSE, NULL); *** ../vim-8.2.1022/src/structs.h 2020-06-10 22:11:59.922786692 +0200 --- src/structs.h 2020-06-20 17:23:33.607530399 +0200 *************** *** 1531,1538 **** typedef struct funccall_S funccall_T; // values used for "uf_dfunc_idx" ! # define UF_NOT_COMPILED -2 ! # define UF_TO_BE_COMPILED -1 /* * Structure to hold info for a user function. --- 1531,1541 ---- typedef struct funccall_S funccall_T; // values used for "uf_dfunc_idx" ! typedef enum { ! UF_NOT_COMPILED, ! UF_TO_BE_COMPILED, ! UF_COMPILED ! } def_status_T; /* * Structure to hold info for a user function. *************** *** 1543,1549 **** 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 garray_T uf_args; // arguments, including optional arguments garray_T uf_def_args; // default argument expressions --- 1546,1553 ---- int uf_flags; // FC_ flags int uf_calls; // nr of active calls int uf_cleared; // func_clear() was already called ! 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 *** ../vim-8.2.1022/src/eval.c 2020-06-16 11:34:38.314223444 +0200 --- src/eval.c 2020-06-20 17:18:11.624401510 +0200 *************** *** 253,259 **** return FAIL; if (partial->pt_func != NULL ! && partial->pt_func->uf_dfunc_idx != UF_NOT_COMPILED) { if (call_def_function(partial->pt_func, argc, argv, partial, rettv) == FAIL) --- 253,259 ---- return FAIL; if (partial->pt_func != NULL ! && partial->pt_func->uf_def_status != UF_NOT_COMPILED) { if (call_def_function(partial->pt_func, argc, argv, partial, rettv) == FAIL) *** ../vim-8.2.1022/src/evalvars.c 2020-06-19 18:34:10.989945091 +0200 --- src/evalvars.c 2020-06-20 17:18:20.972376257 +0200 *************** *** 2628,2634 **** if (*name == 'v') // v: variable return &vimvarht; if (get_current_funccal() != NULL ! && get_current_funccal()->func->uf_dfunc_idx == UF_NOT_COMPILED) { // a: and l: are only used in functions defined with ":function" if (*name == 'a') // a: function argument --- 2628,2634 ---- if (*name == 'v') // v: variable return &vimvarht; if (get_current_funccal() != NULL ! && 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 *** ../vim-8.2.1022/src/vim9execute.c 2020-06-19 18:34:10.989945091 +0200 --- src/vim9execute.c 2020-06-20 17:23:06.527603748 +0200 *************** *** 487,496 **** int error; int idx; ! if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED && compile_def_function(ufunc, FALSE, NULL) == FAIL) return FAIL; ! if (ufunc->uf_dfunc_idx >= 0) { // The function has been compiled, can call it quickly. For a function // that was defined later: we can call it directly next time. --- 487,496 ---- int error; int idx; ! if (ufunc->uf_def_status == UF_TO_BE_COMPILED && compile_def_function(ufunc, FALSE, NULL) == FAIL) return FAIL; ! 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,678 **** // 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 && compile_def_function(ufunc, FALSE, NULL) == FAIL)) { if (called_emsg == called_emsg_before) --- 671,678 ---- // 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_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,2388 **** semsg(_("E1061: Cannot find function %s"), eap->arg); return; } ! if (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED && compile_def_function(ufunc, FALSE, NULL) == FAIL) return; ! if (ufunc->uf_dfunc_idx < 0) { semsg(_("E1062: Function %s is not compiled"), eap->arg); return; --- 2379,2388 ---- semsg(_("E1061: Cannot find function %s"), eap->arg); return; } ! if (ufunc->uf_def_status == UF_TO_BE_COMPILED && compile_def_function(ufunc, FALSE, NULL) == FAIL) return; ! if (ufunc->uf_def_status != UF_COMPILED) { semsg(_("E1062: Function %s is not compiled"), eap->arg); return; *** ../vim-8.2.1022/src/version.c 2020-06-20 16:05:29.016185239 +0200 --- src/version.c 2020-06-20 18:17:42.265122310 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1023, /**/ -- ROBIN: The what? ARTHUR: The Holy Hand Grenade of Antioch. 'Tis one of the sacred relics Brother Maynard always carries with him. ALL: Yes. Of course. ARTHUR: (shouting) Bring up the Holy Hand Grenade! "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///