]> granicus.if.org Git - vim/commitdiff
patch 8.2.4053: Vim9: autoload mechanism doesn't fully work yet v8.2.4053
authorBram Moolenaar <Bram@vim.org>
Mon, 10 Jan 2022 18:08:00 +0000 (18:08 +0000)
committerBram Moolenaar <Bram@vim.org>
Mon, 10 Jan 2022 18:08:00 +0000 (18:08 +0000)
Problem:    Vim9: autoload mechanism doesn't fully work yet.
Solution:   Define functions and variables with their autoload name, add the
            prefix when calling a function, find the variable in the table of
            script variables.

src/evalvars.c
src/proto/scriptfile.pro
src/proto/vim9script.pro
src/scriptfile.c
src/structs.h
src/testdir/test_vim9_script.vim
src/userfunc.c
src/version.c
src/vim9script.c

index 1d5aedf4cc1362664660586625753378c6cc1765..9bd1cc14c658e8de0e3a1156985993c623abda16 100644 (file)
@@ -3339,10 +3339,12 @@ set_var_const(
     dictitem_T *di;
     typval_T   *dest_tv = NULL;
     char_u     *varname;
+    char_u     *name_tofree = NULL;
     hashtab_T  *ht = NULL;
     int                is_script_local;
     int                vim9script = in_vim9script();
     int                var_in_vim9script;
+    int                var_in_autoload = FALSE;
     int                flags = flags_arg;
     int                free_tv_arg = !copy;  // free tv_arg if not used
 
@@ -3353,13 +3355,34 @@ set_var_const(
        varname = name;
     }
     else
-       ht = find_var_ht(name, &varname);
+    {
+       if (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid)
+               && SCRIPT_ITEM(current_sctx.sc_sid)->sn_autoload_prefix != NULL
+               && is_export)
+       {
+           scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
+           size_t       len = STRLEN(name) + STRLEN(si->sn_autoload_prefix) + 1;
+
+           // In a vim9 autoload script an exported variable is put in the
+           // global namespace with the autoload prefix.
+           var_in_autoload = TRUE;
+           varname = alloc(len);
+           if (varname == NULL)
+               goto failed;
+           name_tofree = varname;
+           vim_snprintf((char *)varname, len, "%s%s",
+                                                si->sn_autoload_prefix, name);
+           ht = &globvarht;
+       }
+       else
+           ht = find_var_ht(name, &varname);
+    }
     if (ht == NULL || *varname == NUL)
     {
        semsg(_(e_illegal_variable_name_str), name);
        goto failed;
     }
-    is_script_local = ht == get_script_local_ht() || sid != 0;
+    is_script_local = ht == get_script_local_ht() || sid != 0 || var_in_autoload;
 
     if (vim9script
            && !is_script_local
@@ -3470,9 +3493,10 @@ set_var_const(
 
                // A Vim9 script-local variable is also present in sn_all_vars
                // and sn_var_vals.  It may set "type" from "tv".
-               if (var_in_vim9script)
-                   update_vim9_script_var(FALSE, di, flags, tv, &type,
-                                        (flags & ASSIGN_NO_MEMBER_TYPE) == 0);
+               if (var_in_vim9script || var_in_autoload)
+                   update_vim9_script_var(FALSE, di,
+                           var_in_autoload ? name : di->di_key, flags,
+                           tv, &type, (flags & ASSIGN_NO_MEMBER_TYPE) == 0);
            }
 
            // existing variable, need to clear the value
@@ -3550,10 +3574,11 @@ set_var_const(
                goto failed;
            }
 
-           // Make sure the variable name is valid.  In Vim9 script an autoload
-           // variable must be prefixed with "g:".
+           // Make sure the variable name is valid.  In Vim9 script an
+           // autoload variable must be prefixed with "g:" unless in an
+           // autoload script.
            if (!valid_varname(varname, -1, !vim9script
-                                              || STRNCMP(name, "g:", 2) == 0))
+                           || STRNCMP(name, "g:", 2) == 0 || var_in_autoload))
                goto failed;
 
            di = alloc(sizeof(dictitem_T) + STRLEN(varname));
@@ -3571,9 +3596,10 @@ set_var_const(
 
            // A Vim9 script-local variable is also added to sn_all_vars and
            // sn_var_vals. It may set "type" from "tv".
-           if (var_in_vim9script)
-               update_vim9_script_var(TRUE, di, flags, tv, &type,
-                                        (flags & ASSIGN_NO_MEMBER_TYPE) == 0);
+           if (var_in_vim9script || var_in_autoload)
+               update_vim9_script_var(TRUE, di,
+                       var_in_autoload ? name : di->di_key, flags,
+                             tv, &type, (flags & ASSIGN_NO_MEMBER_TYPE) == 0);
        }
 
        dest_tv = &di->di_tv;
@@ -3618,6 +3644,7 @@ set_var_const(
        item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
 
 failed:
+    vim_free(name_tofree);
     if (free_tv_arg)
        clear_tv(tv_arg);
 }
index 140d67bbbdbdc463c828e681283c576f15a92413..477dc3e6e1d693d26990ea7f121f02334909604d 100644 (file)
@@ -38,6 +38,7 @@ void ex_finish(exarg_T *eap);
 void do_finish(exarg_T *eap, int reanimate);
 int source_finished(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
 char_u *script_name_after_autoload(scriptitem_T *si);
+char_u *get_autoload_prefix(scriptitem_T *si);
 char_u *may_prefix_autoload(char_u *name);
 char_u *autoload_name(char_u *name);
 int script_autoload(char_u *name, int reload);
index 264482493ea95c9c8bcbce3b979e37799bee678d..46211ddb09f7f2f014a9ff1bde41c61df0a79f3b 100644 (file)
@@ -13,7 +13,7 @@ void mark_imports_for_reload(int sid);
 void ex_import(exarg_T *eap);
 int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose);
 char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
-void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type, int do_member);
+void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags, typval_T *tv, type_T **type, int do_member);
 void hide_script_var(scriptitem_T *si, int idx, int func_defined);
 svar_T *find_typval_in_script(typval_T *dest, scid_T sid);
 int check_script_var_type(svar_T *sv, typval_T *value, char_u *name, where_T where);
index 7680fd505fa42f78febea80d9c37cb42abff4a49..7c5da2e96ff88d4ab6fde98bd340355389b58603 100644 (file)
@@ -1711,6 +1711,7 @@ free_scriptnames(void)
 #  ifdef FEAT_PROFILE
        ga_clear(&si->sn_prl_ga);
 #  endif
+       vim_free(si->sn_autoload_prefix);
        vim_free(si);
     }
     ga_clear(&script_items);
@@ -2141,6 +2142,41 @@ script_name_after_autoload(scriptitem_T *si)
     return res;
 }
 
+/*
+ * For an autoload script "autoload/dir/script.vim" return the prefix
+ * "dir#script#" in allocated memory.
+ * Returns NULL if anything is wrong.
+ */
+    char_u *
+get_autoload_prefix(scriptitem_T *si)
+{
+    char_u *p = script_name_after_autoload(si);
+    char_u *prefix;
+
+    if (p == NULL)
+       return NULL;
+    prefix = vim_strsave(p);
+    if (prefix == NULL)
+       return NULL;
+
+    // replace all '/' with '#' and locate ".vim" at the end
+    for (p = prefix; *p != NUL; p += mb_ptr2len(p))
+    {
+       if (vim_ispathsep(*p))
+           *p = '#';
+       else if (STRCMP(p, ".vim") == 0)
+       {
+           p[0] = '#';
+           p[1] = NUL;
+           return prefix;
+       }
+    }
+
+    // did not find ".vim" at the end
+    vim_free(prefix);
+    return NULL;
+}
+
 /*
  * If in a Vim9 autoload script return "name" with the autoload prefix for the
  * script.  If successful "name" is freed, the returned name is allocated.
@@ -2153,37 +2189,28 @@ may_prefix_autoload(char_u *name)
     {
        scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
 
-       if (si->sn_is_autoload)
+       if (si->sn_autoload_prefix != NULL)
        {
-           char_u *p = script_name_after_autoload(si);
+           char_u  *basename = name;
+           size_t  len;
+           char_u  *res;
 
-           if (p != NULL)
+           if (*name == K_SPECIAL)
            {
-               char_u *tail = vim_strsave(p);
+               char_u *p = vim_strchr(name, '_');
 
-               if (tail != NULL)
-               {
-                   for (p = tail; *p != NUL; p += mb_ptr2len(p))
-                   {
-                       if (vim_ispathsep(*p))
-                           *p = '#';
-                       else if (STRCMP(p, ".vim"))
-                       {
-                           size_t  len = (p - tail) + STRLEN(name) + 2;
-                           char_u  *res = alloc(len);
+               // skip over "<SNR>99_"
+               if (p != NULL)
+                   basename = p + 1;
+           }
 
-                           if (res == NULL)
-                               break;
-                           *p = NUL;
-                           vim_snprintf((char *)res, len, "%s#%s", tail, name);
-                           vim_free(name);
-                           vim_free(tail);
-                           return res;
-                       }
-                   }
-               }
-               // did not find ".vim" at the end
-               vim_free(tail);
+           len = STRLEN(si->sn_autoload_prefix) + STRLEN(basename) + 2;
+           res = alloc(len);
+           if (res != NULL)
+           {
+               vim_snprintf((char *)res, len, "%s%s",
+                                            si->sn_autoload_prefix, basename);
+               return res;
            }
        }
     }
index fd4be4fe95be646897ac338148bd98727f747ea9..f733268835f80cc51b342c5a16a46ad0de964226 100644 (file)
@@ -1864,7 +1864,9 @@ typedef struct
     int                sn_state;       // SN_STATE_ values
     char_u     *sn_save_cpo;   // 'cpo' value when :vim9script found
     char       sn_is_vimrc;    // .vimrc file, do not restore 'cpo'
-    char       sn_is_autoload; // "vim9script autoload"
+
+    // for "vim9script autoload" this is "dir#scriptname#"
+    char_u     *sn_autoload_prefix;
 
 # ifdef FEAT_PROFILE
     int                sn_prof_on;     // TRUE when script is/was profiled
index 97a51a51c6bad2a092ac8fcddac238dd95691138..a907383f60798baac68eea4cd96cb53250882bd3 100644 (file)
@@ -3049,6 +3049,14 @@ def Test_vim9_autoload()
       assert_false(exists('g:prefixed_loaded'))
       assert_equal('test', prefixed.Gettest())
       assert_equal('yes', g:prefixed_loaded)
+      assert_equal('name', prefixed.name)
+  END
+  CheckScriptSuccess(lines)
+
+  # can also get the items by autoload name
+  lines =<< trim END
+      call assert_equal('test', prefixed#Gettest())
+      call assert_equal('name', prefixed#name)
   END
   CheckScriptSuccess(lines)
 
index ecd2e7c1ad6b7b687171c8ae1b1ea36f1082dfd3..a3ff38c0a1c1c0da9d536fb5f543f02d175e7d71 100644 (file)
@@ -4080,8 +4080,11 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
                eap->skip = TRUE;
        }
 
-//     if (is_export)
-//         name = may_prefix_autoload(name);
+       // For "export def FuncName()" in an autoload script the function name
+       // is stored with the legacy autoload name "dir#script#FuncName" so
+       // that it can also be found in legacy script.
+       if (is_export)
+           name = may_prefix_autoload(name);
     }
 
     // An error in a function call during evaluation of an expression in magic
index 5f8fc2a2380b241d4ff1bab2f6976c3a83a23231..6ab7271c9b8b04210ffbdea957abdafd50011c91 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4053,
 /**/
     4052,
 /**/
index a8b76bb6c470d85639a0c435ab0817cb82b1bffa..e5ed604972ac2300b6b5576581577e0a95539667 100644 (file)
@@ -132,7 +132,8 @@ ex_vim9script(exarg_T *eap UNUSED)
     }
     si->sn_state = SN_STATE_HAD_COMMAND;
 
-    si->sn_is_autoload = found_autoload;
+    // Store the prefix with the script.  It isused to find exported functions.
+    si->sn_autoload_prefix = get_autoload_prefix(si);
 
     current_sctx.sc_version = SCRIPT_VERSION_VIM9;
     si->sn_version = SCRIPT_VERSION_VIM9;
@@ -663,22 +664,37 @@ find_exported(
     }
     else
     {
+       size_t  len = STRLEN(name);
        char_u  buffer[200];
        char_u  *funcname;
 
-       // it could be a user function.
-       if (STRLEN(name) < sizeof(buffer) - 15)
+       // It could be a user function.  Normally this is stored as
+       // "<SNR>99_name".  For an autoload script a function is stored with
+       // the autoload prefix: "dir#script#name".
+       if (script->sn_autoload_prefix != NULL)
+           len += STRLEN(script->sn_autoload_prefix) + 2;
+       else
+           len += 15;
+
+       if (len < sizeof(buffer))
            funcname = buffer;
        else
        {
-           funcname = alloc(STRLEN(name) + 15);
+           funcname = alloc(len);
            if (funcname == NULL)
                return -1;
        }
-       funcname[0] = K_SPECIAL;
-       funcname[1] = KS_EXTRA;
-       funcname[2] = (int)KE_SNR;
-       sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
+       if (script->sn_autoload_prefix != NULL)
+       {
+           sprintf((char *)funcname, "%s%s", script->sn_autoload_prefix, name);
+       }
+       else
+       {
+           funcname[0] = K_SPECIAL;
+           funcname[1] = KS_EXTRA;
+           funcname[2] = (int)KE_SNR;
+           sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
+       }
        *ufunc = find_func(funcname, FALSE, NULL);
        if (funcname != buffer)
            vim_free(funcname);
@@ -782,6 +798,7 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
 update_vim9_script_var(
        int         create,
        dictitem_T  *di,
+       char_u      *name,
        int         flags,
        typval_T    *tv,
        type_T      **type,
@@ -801,7 +818,7 @@ update_vim9_script_var(
        if (ga_grow(&si->sn_var_vals, 1) == FAIL)
            return;
 
-       hi = hash_find(&si->sn_all_vars.dv_hashtab, di->di_key);
+       hi = hash_find(&si->sn_all_vars.dv_hashtab, name);
        if (!HASHITEM_EMPTY(hi))
        {
            // Variable with this name exists, either in this block or in
@@ -833,7 +850,7 @@ update_vim9_script_var(
            // svar_T and create a new sallvar_T.
            sv = ((svar_T *)si->sn_var_vals.ga_data) + si->sn_var_vals.ga_len;
            newsav = (sallvar_T *)alloc_clear(
-                                      sizeof(sallvar_T) + STRLEN(di->di_key));
+                                      sizeof(sallvar_T) + STRLEN(name));
            if (newsav == NULL)
                return;
 
@@ -843,7 +860,7 @@ update_vim9_script_var(
            sv->sv_export = is_export;
            newsav->sav_var_vals_idx = si->sn_var_vals.ga_len;
            ++si->sn_var_vals.ga_len;
-           STRCPY(&newsav->sav_key, di->di_key);
+           STRCPY(&newsav->sav_key, name);
            sv->sv_name = newsav->sav_key;
            newsav->sav_di = di;
            newsav->sav_block_id = si->sn_current_block_id;