# HG changeset patch # User Bram Moolenaar # Date 1604836804 -3600 # Node ID f2fbbb72ff28f6735dae5fd4c9ecbf0ae9c8f384 # Parent 079dd4d56017497a713db9e906b12a9d39c288b9 patch 8.2.1968: Vim9: has() assumes a feature does not change dynamically Commit: https://github.com/vim/vim/commit/8cebd43e9774d2624af43ee5b86939886f2ba490 Author: Bram Moolenaar Date: Sun Nov 8 12:49:47 2020 +0100 patch 8.2.1968: Vim9: has() assumes a feature does not change dynamically Problem: Vim9: has() assumes a feature does not change dynamically. Solution: Check whether a feature may change dynamically. (closes https://github.com/vim/vim/issues/7265) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -5484,6 +5484,73 @@ f_has(typval_T *argvars, typval_T *rettv } /* + * Return TRUE if "feature" can change later. + * Also when checking for the feature has side effects, such as loading a DLL. + */ + int +dynamic_feature(char_u *feature) +{ + return (feature == NULL +#if defined(FEAT_BEVAL) && defined(FEAT_GUI_MSWIN) + || STRICMP(feature, "balloon_multiline") == 0 +#endif +#if defined(FEAT_GUI) && defined(FEAT_BROWSE) + || (STRICMP(feature, "browse") == 0 && !gui.in_use) +#endif +#ifdef VIMDLL + || STRICMP(feature, "filterpipe") == 0 +#endif +#if defined(FEAT_GUI) && !defined(ALWAYS_USE_GUI) + // this can only change on Unix where the ":gui" command could be + // used. + || (STRICMP(feature, "gui_running") == 0 && !gui.in_use) +#endif +#if defined(USE_ICONV) && defined(DYNAMIC_ICONV) + || STRICMP(feature, "iconv") == 0 +#endif +#ifdef DYNAMIC_LUA + || STRICMP(feature, "lua") == 0 +#endif +#ifdef FEAT_MOUSE_GPM + || (STRICMP(feature, "mouse_gpm_enabled") == 0 && !gpm_enabled()) +#endif +#ifdef DYNAMIC_MZSCHEME + || STRICMP(feature, "mzscheme") == 0 +#endif +#ifdef FEAT_NETBEANS_INTG + || STRICMP(feature, "netbeans_enabled") == 0 +#endif +#ifdef DYNAMIC_PERL + || STRICMP(feature, "perl") == 0 +#endif +#ifdef DYNAMIC_PYTHON + || STRICMP(feature, "python") == 0 +#endif +#ifdef DYNAMIC_PYTHON3 + || STRICMP(feature, "python3") == 0 +#endif +#if defined(DYNAMIC_PYTHON) || defined(DYNAMIC_PYTHON3) + || STRICMP(feature, "pythonx") == 0 +#endif +#ifdef DYNAMIC_RUBY + || STRICMP(feature, "ruby") == 0 +#endif +#ifdef FEAT_SYN_HL + || STRICMP(feature, "syntax_items") == 0 +#endif +#ifdef DYNAMIC_TCL + || STRICMP(feature, "tcl") == 0 +#endif + // once "starting" is zero it will stay that way + || (STRICMP(feature, "vim_starting") == 0 && starting != 0) + || STRICMP(feature, "multi_byte_encoding") == 0 +#if defined(FEAT_TERMINAL) && defined(MSWIN) + || STRICMP(feature, "conpty") == 0 +#endif + ); +} + +/* * "haslocaldir()" function */ static void diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -16,6 +16,7 @@ win_T *get_optional_window(typval_T *arg void execute_redir_str(char_u *value, int value_len); void execute_common(typval_T *argvars, typval_T *rettv, int arg_off); void f_has(typval_T *argvars, typval_T *rettv); +int dynamic_feature(char_u *feature); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); void range_list_materialize(list_T *list); float_T vim_round(float_T f); diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -629,6 +629,14 @@ def HasSomething() endif enddef +def HasGuiRunning() + if has("gui_running") + echo "yes" + else + echo "no" + endif +enddef + def Test_disassemble_const_expr() assert_equal("\nyes", execute('HasEval()')) var instr = execute('disassemble HasEval') @@ -676,6 +684,67 @@ def Test_disassemble_const_expr() assert_notmatch('PUSHS "something"', instr) assert_notmatch('PUSHS "less"', instr) assert_notmatch('JUMP', instr) + + var result: string + var instr_expected: string + if has('gui') + if has('gui_running') + # GUI already running, always returns "yes" + result = "\nyes" + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + ' echo "yes"\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d ECHO 1\_s*' .. + 'else\_s*' .. + ' echo "no"\_s*' .. + 'endif' + else + result = "\nno" + if has('unix') + # GUI not running but can start later, call has() + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + '\d PUSHS "gui_running"\_s*' .. + '\d BCALL has(argc 1)\_s*' .. + '\d JUMP_IF_FALSE -> \d\_s*' .. + ' echo "yes"\_s*' .. + '\d PUSHS "yes"\_s*' .. + '\d ECHO 1\_s*' .. + 'else\_s*' .. + '\d JUMP -> \d\_s*' .. + ' echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif' + else + # GUI not running, always return "no" + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + ' echo "yes"\_s*' .. + 'else\_s*' .. + ' echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif' + endif + endif + else + # GUI not supported, always return "no" + result = "\nno" + instr_expected = 'HasGuiRunning.*' .. + 'if has("gui_running")\_s*' .. + ' echo "yes"\_s*' .. + 'else\_s*' .. + ' echo "no"\_s*' .. + '\d PUSHS "no"\_s*' .. + '\d ECHO 1\_s*' .. + 'endif' + endif + + assert_equal(result, execute('HasGuiRunning()')) + instr = execute('disassemble HasGuiRunning') + assert_match(instr_expected, instr) enddef def ReturnInIf(): string diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1968, +/**/ 1967, /**/ 1966, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2620,7 +2620,8 @@ compile_call( else if (*s == '\'') (void)eval_lit_string(&s, &argvars[0], TRUE); s = skipwhite(s); - if (*s == ')' && argvars[0].v_type == VAR_STRING) + if (*s == ')' && argvars[0].v_type == VAR_STRING + && !dynamic_feature(argvars[0].vval.v_string)) { typval_T *tv = &ppconst->pp_tv[ppconst->pp_used];