To: vim_dev@googlegroups.com Subject: Patch 8.2.1870 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1870 Problem: Vim9: no need to keep all script variables. Solution: Only keep script variables when a function was defined that could use them. Fix freeing static string on exit. Files: src/vim9script.c, src/proto/vim9script.pro, src/structs.h, src/ex_eval.c, src/userfunc.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1869/src/vim9script.c 2020-10-15 12:46:38.733199522 +0200 --- src/vim9script.c 2020-10-20 14:21:10.515579929 +0200 *************** *** 51,58 **** if (STRCMP(p_cpo, CPO_VIM) != 0) { ! si->sn_save_cpo = p_cpo; ! p_cpo = vim_strsave((char_u *)CPO_VIM); } } --- 51,58 ---- if (STRCMP(p_cpo, CPO_VIM) != 0) { ! si->sn_save_cpo = vim_strsave(p_cpo); ! set_option_value((char_u *)"cpo", 0L, (char_u *)CPO_VIM, 0); } } *************** *** 569,576 **** } /* ! * Vim9 part of adding a script variable: add it to sn_all_vars and ! * sn_var_vals. * When "type" is NULL use "tv" for the type. */ void --- 569,576 ---- } /* ! * Vim9 part of adding a script variable: add it to sn_all_vars (lookup by name ! * with a hashtable) and sn_var_vals (lookup by index). * When "type" is NULL use "tv" for the type. */ void *************** *** 628,636 **** /* * Hide a script variable when leaving a block. * "idx" is de index in sn_var_vals. */ void ! hide_script_var(scriptitem_T *si, int idx) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; hashtab_T *script_ht = get_script_local_ht(); --- 628,638 ---- /* * Hide a script variable when leaving a block. * "idx" is de index in sn_var_vals. + * When "func_defined" is non-zero then a function was defined in this block, + * the variable may be accessed by it. Otherwise the variable can be cleared. */ void ! hide_script_var(scriptitem_T *si, int idx, int func_defined) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + idx; hashtab_T *script_ht = get_script_local_ht(); *************** *** 639,644 **** --- 641,647 ---- hashitem_T *all_hi; // Remove a variable declared inside the block, if it still exists. + // If it was added in a nested block it will already have been removed. // The typval is moved into the sallvar_T. script_hi = hash_find(script_ht, sv->sv_name); all_hi = hash_find(all_ht, sv->sv_name); *************** *** 646,664 **** { dictitem_T *di = HI2DI(script_hi); sallvar_T *sav = HI2SAV(all_hi); // There can be multiple entries with the same name in different // blocks, find the right one. while (sav != NULL && sav->sav_var_vals_idx != idx) sav = sav->sav_next; if (sav != NULL) { ! sav->sav_tv = di->di_tv; ! di->di_tv.v_type = VAR_UNKNOWN; ! sav->sav_flags = di->di_flags; ! sav->sav_di = NULL; delete_var(script_ht, script_hi); - sv->sv_tv = &sav->sav_tv; } } } --- 649,684 ---- { dictitem_T *di = HI2DI(script_hi); sallvar_T *sav = HI2SAV(all_hi); + sallvar_T *sav_prev = NULL; // There can be multiple entries with the same name in different // blocks, find the right one. while (sav != NULL && sav->sav_var_vals_idx != idx) + { + sav_prev = sav; sav = sav->sav_next; + } if (sav != NULL) { ! if (func_defined) ! { ! // move the typval from the dictitem to the sallvar ! sav->sav_tv = di->di_tv; ! di->di_tv.v_type = VAR_UNKNOWN; ! sav->sav_flags = di->di_flags; ! sav->sav_di = NULL; ! sv->sv_tv = &sav->sav_tv; ! } ! else ! { ! if (sav_prev == NULL) ! hash_remove(all_ht, all_hi); ! else ! sav_prev->sav_next = sav->sav_next; ! sv->sv_name = NULL; ! vim_free(sav); ! } delete_var(script_ht, script_hi); } } } *** ../vim-8.2.1869/src/proto/vim9script.pro 2020-10-15 12:46:38.733199522 +0200 --- src/proto/vim9script.pro 2020-10-20 13:09:35.996167733 +0200 *************** *** 9,15 **** char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type); ! void hide_script_var(scriptitem_T *si, int idx); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); --- 9,15 ---- char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); void add_vim9_script_var(dictitem_T *di, typval_T *tv, type_T *type); ! void hide_script_var(scriptitem_T *si, int idx, int func_defined); void free_all_script_vars(scriptitem_T *si); svar_T *find_typval_in_script(typval_T *dest); int check_script_var_type(typval_T *dest, typval_T *value, char_u *name); *** ../vim-8.2.1869/src/structs.h 2020-10-15 12:46:38.729199532 +0200 --- src/structs.h 2020-10-20 12:57:35.518235616 +0200 *************** *** 917,922 **** --- 917,924 ---- # define CSF_SILENT 0x1000 // "emsg_silent" reset by ":try" // Note that CSF_ELSE is only used when CSF_TRY and CSF_WHILE are unset // (an ":if"), and CSF_SILENT is only used when CSF_TRY is set. + // + #define CSF_FUNC_DEF 0x2000 // a function was defined in this block /* * What's pending for being reactivated at the ":endtry" of this try *** ../vim-8.2.1869/src/ex_eval.c 2020-10-15 12:46:38.733199522 +0200 --- src/ex_eval.c 2020-10-20 13:46:59.173728456 +0200 *************** *** 930,945 **** { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); int i; for (i = cstack->cs_script_var_len[cstack->cs_idx]; i < si->sn_var_vals.ga_len; ++i) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i; if (sv->sv_name != NULL) // Remove a variable declared inside the block, if it still ! // exists, from sn_vars and move the value into sn_all_vars. ! hide_script_var(si, i); } // TODO: is this needed? --- 930,951 ---- { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); int i; + int func_defined = + cstack->cs_flags[cstack->cs_idx] & CSF_FUNC_DEF; for (i = cstack->cs_script_var_len[cstack->cs_idx]; i < si->sn_var_vals.ga_len; ++i) { svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i; + // sv_name is set to NULL if it was already removed. This happens + // when it was defined in an inner block and no functions were + // defined there. if (sv->sv_name != NULL) // Remove a variable declared inside the block, if it still ! // exists, from sn_vars and move the value into sn_all_vars ! // if "func_defined" is non-zero. ! hide_script_var(si, i, func_defined); } // TODO: is this needed? *** ../vim-8.2.1869/src/userfunc.c 2020-10-15 12:46:38.733199522 +0200 --- src/userfunc.c 2020-10-20 14:07:39.257994178 +0200 *************** *** 3471,3497 **** if (eap->cmdidx == CMD_def) { ! 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; ! if (eap->cstack != NULL && eap->cstack->cs_idx >= 0) { ! int count = eap->cstack->cs_idx + 1; // The block context may be needed for script variables declared in ! // a block visible now but not when the function is compiled. fp->uf_block_ids = ALLOC_MULT(int, count); if (fp->uf_block_ids != NULL) { ! mch_memmove(fp->uf_block_ids, eap->cstack->cs_block_id, sizeof(int) * count); fp->uf_block_depth = count; } ! // TODO: set flag in each block to indicate a function was defined } // parse the argument types --- 3471,3505 ---- if (eap->cmdidx == CMD_def) { ! int lnum_save = SOURCING_LNUM; ! cstack_T *cstack = eap->cstack; fp->uf_def_status = UF_TO_BE_COMPILED; // error messages are for the first function line SOURCING_LNUM = sourcing_lnum_top; ! if (cstack != NULL && cstack->cs_idx >= 0) { ! int count = cstack->cs_idx + 1; ! int i; // The block context may be needed for script variables declared in ! // a block that is visible now but not when the function is called ! // later. fp->uf_block_ids = ALLOC_MULT(int, count); if (fp->uf_block_ids != NULL) { ! mch_memmove(fp->uf_block_ids, cstack->cs_block_id, sizeof(int) * count); fp->uf_block_depth = count; } ! ! // Set flag in each block to indicate a function was defined. This ! // is used to keep the variable when leaving the block, see ! // hide_script_var(). ! for (i = 0; i <= cstack->cs_idx; ++i) ! cstack->cs_flags[i] |= CSF_FUNC_DEF; } // parse the argument types *** ../vim-8.2.1869/src/testdir/test_vim9_script.vim 2020-10-17 22:04:04.118833463 +0200 --- src/testdir/test_vim9_script.vim 2020-10-20 14:10:57.777404737 +0200 *************** *** 296,301 **** --- 296,320 ---- delete('Xdidit') enddef + def Test_block_local_vars_with_func() + var lines =<< trim END + vim9script + if true + var foo = 'foo' + if true + var bar = 'bar' + def Func(): list + return [foo, bar] + enddef + endif + endif + # function is compiled here, after blocks have finished, can still access + # "foo" and "bar" + assert_equal(['foo', 'bar'], Func()) + END + CheckScriptSuccess(lines) + enddef + func g:NoSuchFunc() echo 'none' endfunc *** ../vim-8.2.1869/src/version.c 2020-10-19 23:01:42.507664521 +0200 --- src/version.c 2020-10-20 14:22:16.879381525 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1870, /**/ -- From "know your smileys": [:-) Frankenstein's monster /// 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 ///