changeset 22842:f2fbbb72ff28 v8.2.1968

patch 8.2.1968: Vim9: has() assumes a feature does not change dynamically Commit: https://github.com/vim/vim/commit/8cebd43e9774d2624af43ee5b86939886f2ba490 Author: Bram Moolenaar <Bram@vim.org> 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)
author Bram Moolenaar <Bram@vim.org>
date Sun, 08 Nov 2020 13:00:04 +0100
parents 079dd4d56017
children b80694c9bb40
files src/evalfunc.c src/proto/evalfunc.pro src/testdir/test_vim9_disassemble.vim src/version.c src/vim9compile.c
diffstat 5 files changed, 141 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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);
--- 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
--- 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,
--- 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];