Patch 8.2.4650
Problem:    "import autoload" only works with using 'runtimepath'.
Solution:   Also support a relative and absolute file name.
Files:      runtime/doc/vim9.txt, src/structs.h, src/scriptfile.c,
            src/proto/scriptfile.pro, src/vim9script.c, src/vim9expr.c,
            src/vim9.h, src/vim9execute.c, src/vim9instr.c,
            src/proto/vim9instr.pro, src/vim.h, src/userfunc.c,
            src/proto/userfunc.pro, src/testdir/test_vim9_import.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.4649/runtime/doc/vim9.txt        2022-03-08 13:18:10.805020790 
+0000
--- runtime/doc/vim9.txt        2022-03-30 19:57:51.472168649 +0100
***************
*** 1529,1545 ****
        echo that
                .name  # Error!
  <                                                     *:import-cycle*
! The `import` commands are executed when encountered.  If that script (directly
! or indirectly) imports the current script, then items defined after the
! `import` won't be processed yet.  Therefore cyclic imports can exist, but may
! result in undefined items.
  
  
  Importing an autoload script ~
                                                        *vim9-autoload*
  For optimal startup speed, loading scripts should be postponed until they are
  actually needed.  Using the autoload mechanism is recommended:
! 
  1. In the plugin define user commands, functions and/or mappings that refer to
     items imported from an autoload script. >
        import autoload 'for/search.vim'
--- 1721,1739 ----
        echo that
                .name  # Error!
  <                                                     *:import-cycle*
! The `import` commands are executed when encountered.  If script A imports
! script B, and B (directly or indirectly) imports A, this will be skipped over.
! At this point items in A after "import B" will not have been processed and
! defined yet.  Therefore cyclic imports can exist and not result in an error
! directly, but may result in an error for items in A after "import B" not being
! defined.  This does not apply to autoload imports, see the next section.
  
  
  Importing an autoload script ~
                                                        *vim9-autoload*
  For optimal startup speed, loading scripts should be postponed until they are
  actually needed.  Using the autoload mechanism is recommended:
!                                                       *E1264*
  1. In the plugin define user commands, functions and/or mappings that refer to
     items imported from an autoload script. >
        import autoload 'for/search.vim'
***************
*** 1551,1557 ****
     The "autoload" argument to `:import` means that the script is not loaded
     until one of the items is actually used.  The script will be found under
     the "autoload" directory in 'runtimepath' instead of the "import"
!    directory.
  
  2. In the autoload script put the bulk of the code. >
        vim9script
--- 1745,1752 ----
     The "autoload" argument to `:import` means that the script is not loaded
     until one of the items is actually used.  The script will be found under
     the "autoload" directory in 'runtimepath' instead of the "import"
!    directory.  Alternatively a relative or absolute name can be used, see
!    below.
  
  2. In the autoload script put the bulk of the code. >
        vim9script
***************
*** 1565,1575 ****
     prefix is obtained from the file name, as you would to manually in a
     legacy autoload script.  Thus the exported function can be found with
     "for#search#Stuff", but you would normally use `import autoload` and not
!    use the prefix.
  
     You can split up the functionality and import other scripts from the
     autoload script as you like.  This way you can share code between plugins.
  
  For defining a mapping that uses the imported autoload script the special key
  |<ScriptCmd>| is useful.  It allows for a command in a mapping to use the
  script context of where the mapping was defined.
--- 1760,1779 ----
     prefix is obtained from the file name, as you would to manually in a
     legacy autoload script.  Thus the exported function can be found with
     "for#search#Stuff", but you would normally use `import autoload` and not
!    use the prefix (which has the side effect of loading the autoload script
!    when compiling a function that encounters this name).
  
     You can split up the functionality and import other scripts from the
     autoload script as you like.  This way you can share code between plugins.
  
+ Searching for the autoload script in all entries in 'runtimepath' can be a bit
+ slow.  If the plugin knows where the script is located, quite often a relative
+ path can be used.  This avoids the search and should be quite a bit faster.
+ Another advantage is that the script name does not need to be unique.  An
+ absolute path is also possible.  Examples: >
+       import autoload '../lib/implement.vim'
+       import autoload MyScriptsDir .. '/lib/implement.vim'
+ 
  For defining a mapping that uses the imported autoload script the special key
  |<ScriptCmd>| is useful.  It allows for a command in a mapping to use the
  script context of where the mapping was defined.
***************
*** 1577,1583 ****
  When compiling a `:def` function and a function in an autoload script is
  encountered, the script is not loaded until the `:def` function is called.
  This also means you get any errors only at runtime, since the argument and
! return types are not known yet.
  
  For testing the |test_override()| function can be used to have the
  `import autoload` load the script right away, so that the items and types can
--- 1781,1797 ----
  When compiling a `:def` function and a function in an autoload script is
  encountered, the script is not loaded until the `:def` function is called.
  This also means you get any errors only at runtime, since the argument and
! return types are not known yet.  If you would use the name with '#' characters
! then the autoload script IS loaded.
! 
! Be careful to not refer to an item in an autoload script that does trigger
! loading it unintentionally.  For example, when setting an option that takes a
! function name, make sure to use a string, not a function reference: >
!       import autoload 'qftf.vim'
!       &quickfixtextfunc = 'qftf.Func'  # autoload script NOT loaded
!       &quickfixtextfunc = qftf.Func    # autoload script IS loaded
! On the other hand, it can be useful to load the script early, at a time when
! any errors should be given.
  
  For testing the |test_override()| function can be used to have the
  `import autoload` load the script right away, so that the items and types can
*** ../vim-8.2.4649/src/structs.h       2022-03-30 10:14:41.485657271 +0100
--- src/structs.h       2022-03-30 14:21:51.012324633 +0100
***************
*** 1833,1839 ****
   */
  typedef struct
  {
!     char_u    *sn_name;
      int               sn_script_seq;      // latest sctx_T sc_seq value
  
      // "sn_vars" stores the s: variables currently valid.  When leaving a 
block
--- 1833,1839 ----
   */
  typedef struct
  {
!     char_u    *sn_name;           // full path of script file
      int               sn_script_seq;      // latest sctx_T sc_seq value
  
      // "sn_vars" stores the s: variables currently valid.  When leaving a 
block
***************
*** 1864,1872 ****
      char_u    *sn_save_cpo;   // 'cpo' value when :vim9script found
      char      sn_is_vimrc;    // .vimrc file, do not restore 'cpo'
  
!     // 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
      int               sn_pr_force;    // forceit: profile functions in this 
script
--- 1864,1875 ----
      char_u    *sn_save_cpo;   // 'cpo' value when :vim9script found
      char      sn_is_vimrc;    // .vimrc file, do not restore 'cpo'
  
!     // for a Vim9 script under "rtp/autoload/" this is "dir#scriptname#"
      char_u    *sn_autoload_prefix;
  
+     // TRUE for a script used with "import autoload './dirname/script.vim'"
+     int               sn_import_autoload;
+ 
  # ifdef FEAT_PROFILE
      int               sn_prof_on;     // TRUE when script is/was profiled
      int               sn_pr_force;    // forceit: profile functions in this 
script
*** ../vim-8.2.4649/src/scriptfile.c    2022-03-29 19:52:08.787653549 +0100
--- src/scriptfile.c    2022-03-30 14:55:27.953493854 +0100
***************
*** 251,257 ****
   * Find an already loaded script "name".
   * If found returns its script ID.  If not found returns -1.
   */
!     static int
  find_script_by_name(char_u *name)
  {
      int                   sid;
--- 251,257 ----
   * Find an already loaded script "name".
   * If found returns its script ID.  If not found returns -1.
   */
!     int
  find_script_by_name(char_u *name)
  {
      int                   sid;
***************
*** 320,325 ****
--- 320,340 ----
      return sid;
  }
  
+     int
+ get_new_scriptitem_for_fname(int *error, char_u *fname)
+ {
+     int sid = get_new_scriptitem(error);
+ 
+     if (*error == OK)
+     {
+       scriptitem_T *si = SCRIPT_ITEM(sid);
+ 
+       si->sn_name = vim_strsave(fname);
+       si->sn_state = SN_STATE_NOT_LOADED;
+     }
+     return sid;
+ }
+ 
      static void
  find_script_callback(char_u *fname, void *cookie)
  {
***************
*** 329,345 ****
  
      sid = find_script_by_name(fname);
      if (sid < 0)
-     {
        // script does not exist yet, create a new scriptitem
!       sid = get_new_scriptitem(&error);
!       if (error == OK)
!       {
!           scriptitem_T *si = SCRIPT_ITEM(sid);
! 
!           si->sn_name = vim_strsave(fname);
!           si->sn_state = SN_STATE_NOT_LOADED;
!       }
!     }
      *ret_sid = sid;
  }
  #endif
--- 344,351 ----
  
      sid = find_script_by_name(fname);
      if (sid < 0)
        // script does not exist yet, create a new scriptitem
!       sid = get_new_scriptitem_for_fname(&error, fname);
      *ret_sid = sid;
  }
  #endif
*** ../vim-8.2.4649/src/proto/scriptfile.pro    2022-03-21 19:45:13.200420997 
+0000
--- src/proto/scriptfile.pro    2022-03-30 14:55:31.673487843 +0100
***************
*** 6,11 ****
--- 6,13 ----
  estack_T *estack_pop(void);
  char_u *estack_sfile(estack_arg_T which);
  void ex_runtime(exarg_T *eap);
+ int find_script_by_name(char_u *name);
+ int get_new_scriptitem_for_fname(int *error, char_u *fname);
  int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u 
*fname, void *ck), void *cookie);
  int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u 
*fname, void *ck), void *cookie);
  int source_runtime(char_u *name, int flags);
*** ../vim-8.2.4649/src/vim9script.c    2022-03-28 15:22:31.490443719 +0100
--- src/vim9script.c    2022-03-30 15:02:11.104833760 +0100
***************
*** 384,389 ****
--- 384,421 ----
  }
  
  /*
+  * Part of "import" that handles a relative or absolute file name/
+  * Returns OK or FAIL.
+  */
+     static int
+ handle_import_fname(char_u *fname, int is_autoload, int *sid)
+ {
+     if (is_autoload)
+     {
+       scriptitem_T    *si;
+ 
+       *sid = find_script_by_name(fname);
+       if (*sid < 0)
+       {
+           int error = OK;
+ 
+           // script does not exist yet, create a new scriptitem
+           *sid = get_new_scriptitem_for_fname(&error, fname);
+           if (error == FAIL)
+               return FAIL;
+       }
+ 
+       si = SCRIPT_ITEM(*sid);
+       si->sn_import_autoload = TRUE;
+ 
+       // with testing override: load autoload script right away
+       if (!override_autoload || si->sn_state != SN_STATE_NOT_LOADED)
+           return OK;
+     }
+     return do_source(fname, FALSE, DOSO_NONE, sid);
+ }
+ 
+ /*
   * Handle an ":import" command and add the resulting imported_T to "gap", when
   * not NULL, or script "import_sid" sn_imports.
   * "cctx" is NULL at the script level.
***************
*** 442,466 ****
        char_u          *tail = gettail(si->sn_name);
        char_u          *from_name;
  
!       if (is_autoload)
!           res = FAIL;
!       else
!       {
! 
!           // Relative to current script: "./name.vim", "../../name.vim".
!           len = STRLEN(si->sn_name) - STRLEN(tail)
!                                               + STRLEN(tv.vval.v_string) + 2;
!           from_name = alloc((int)len);
!           if (from_name == NULL)
!               goto erret;
!           vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
!           add_pathsep(from_name);
!           STRCAT(from_name, tv.vval.v_string);
!           simplify_filename(from_name);
  
!           res = do_source(from_name, FALSE, DOSO_NONE, &sid);
!           vim_free(from_name);
!       }
      }
      else if (mch_isFullName(tv.vval.v_string)
  #ifdef BACKSLASH_IN_FILENAME
--- 474,491 ----
        char_u          *tail = gettail(si->sn_name);
        char_u          *from_name;
  
!       // Relative to current script: "./name.vim", "../../name.vim".
!       len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
!       from_name = alloc((int)len);
!       if (from_name == NULL)
!           goto erret;
!       vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
!       add_pathsep(from_name);
!       STRCAT(from_name, tv.vval.v_string);
!       simplify_filename(from_name);
  
!       res = handle_import_fname(from_name, is_autoload, &sid);
!       vim_free(from_name);
      }
      else if (mch_isFullName(tv.vval.v_string)
  #ifdef BACKSLASH_IN_FILENAME
***************
*** 471,480 ****
            )
      {
        // Absolute path: "/tmp/name.vim"
!       if (is_autoload)
!           res = FAIL;
!       else
!           res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
      }
      else if (is_autoload)
      {
--- 496,502 ----
            )
      {
        // Absolute path: "/tmp/name.vim"
!       res = handle_import_fname(tv.vval.v_string, is_autoload, &sid);
      }
      else if (is_autoload)
      {
***************
*** 677,682 ****
--- 699,710 ----
      svar_T    *sv;
      scriptitem_T *script = SCRIPT_ITEM(sid);
  
+     if (script->sn_import_autoload && script->sn_state == SN_STATE_NOT_LOADED)
+     {
+       if (do_source(script->sn_name, FALSE, DOSO_NONE, NULL) == FAIL)
+           return -1;
+     }
+ 
      // Find name in "script".
      idx = get_script_item_idx(sid, name, 0, cctx, cstack);
      if (idx >= 0)
*** ../vim-8.2.4649/src/vim9expr.c      2022-03-27 16:29:49.880153368 +0100
--- src/vim9expr.c      2022-03-30 16:49:42.100316230 +0100
***************
*** 313,318 ****
--- 313,339 ----
            vim_free(auto_name);
            done = TRUE;
        }
+       else if (si->sn_import_autoload && si->sn_state == SN_STATE_NOT_LOADED)
+       {
+           // "import autoload './dir/script.vim'" - load script first
+           res = generate_SOURCE(cctx, import->imp_sid);
+           if (res == OK)
+           {
+               // If a '(' follows it must be a function.  Otherwise we don't
+               // know, it can be "script.Func".
+               if (cc == '(' || paren_follows_after_expr)
+               {
+                   char_u sid_name[MAX_FUNC_NAME_LEN];
+ 
+                   func_name_with_sid(exp_name, import->imp_sid, sid_name);
+                   res = generate_PUSHFUNC(cctx, sid_name, &t_func_any);
+               }
+               else
+                   res = generate_OLDSCRIPT(cctx, ISN_LOADEXPORT, exp_name,
+                                                     import->imp_sid, &t_any);
+           }
+           done = TRUE;
+       }
        else
        {
            idx = find_exported(import->imp_sid, exp_name, &ufunc, &type,
*** ../vim-8.2.4649/src/vim9.h  2022-03-27 20:04:16.029188564 +0100
--- src/vim9.h  2022-03-30 17:10:56.817448503 +0100
***************
*** 28,33 ****
--- 28,35 ----
      ISN_ECHOERR,    // :echoerr with isn_arg.number items on top of stack
      ISN_RANGE,            // compute range from isn_arg.string, push to stack
      ISN_SUBSTITUTE, // :s command with expression
+ 
+     ISN_SOURCE,           // source autoload script, isn_arg.number is the 
script ID
      ISN_INSTR,            // instructions compiled from expression
  
      // get and set variables
***************
*** 43,48 ****
--- 45,51 ----
      ISN_LOADWDICT,  // push w: dict
      ISN_LOADTDICT,  // push t: dict
      ISN_LOADS,            // push s: variable isn_arg.loadstore
+     ISN_LOADEXPORT, // push exported variable isn_arg.loadstore
      ISN_LOADOUTER,  // push variable from outer scope isn_arg.outer
      ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
      ISN_LOADOPT,    // push option isn_arg.string
***************
*** 57,62 ****
--- 60,66 ----
      ISN_STOREW,           // pop into window-local variable isn_arg.string
      ISN_STORET,           // pop into tab-local variable isn_arg.string
      ISN_STORES,           // pop into script variable isn_arg.loadstore
+     ISN_STOREEXPORT, // pop into exported script variable isn_arg.loadstore
      ISN_STOREOUTER,  // pop variable into outer scope isn_arg.outer
      ISN_STORESCRIPT, // pop into script variable isn_arg.script
      ISN_STOREOPT,    // pop into option isn_arg.storeopt
*** ../vim-8.2.4649/src/vim9execute.c   2022-03-28 18:16:43.619673423 +0100
--- src/vim9execute.c   2022-03-30 20:27:54.479338636 +0100
***************
*** 1510,1515 ****
--- 1510,1522 ----
            emsg(_(e_script_variable_type_changed));
        return NULL;
      }
+ 
+     if (!sv->sv_export && sref->sref_sid != current_sctx.sc_sid)
+     {
+       if (dfunc != NULL)
+           semsg(_(e_item_not_exported_in_script_str), sv->sv_name);
+       return NULL;
+     }
      return sv;
  }
  
***************
*** 2623,2628 ****
--- 2630,2649 ----
                }
                break;
  
+           case ISN_SOURCE:
+               {
+                   scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number);
+ 
+                   if (si->sn_state == SN_STATE_NOT_LOADED)
+                   {
+                       SOURCING_LNUM = iptr->isn_lnum;
+                       if (do_source(si->sn_name, FALSE, DOSO_NONE, NULL)
+                                                                      == FAIL)
+                           goto on_error;
+                   }
+               }
+               break;
+ 
            // execute :substitute with an expression
            case ISN_SUBSTITUTE:
                {
***************
*** 2902,2912 ****
                }
                break;
  
!           // load s: variable in old script
            case ISN_LOADS:
                {
!                   hashtab_T   *ht = &SCRIPT_VARS(
!                                              iptr->isn_arg.loadstore.ls_sid);
                    char_u      *name = iptr->isn_arg.loadstore.ls_name;
                    dictitem_T  *di = find_var_in_ht(ht, 0, name, TRUE);
  
--- 2923,2934 ----
                }
                break;
  
!           // load s: variable in old script or autoload import
            case ISN_LOADS:
+           case ISN_LOADEXPORT:
                {
!                   int         sid = iptr->isn_arg.loadstore.ls_sid;
!                   hashtab_T   *ht = &SCRIPT_VARS(sid);
                    char_u      *name = iptr->isn_arg.loadstore.ls_name;
                    dictitem_T  *di = find_var_in_ht(ht, 0, name, TRUE);
  
***************
*** 2918,2923 ****
--- 2940,2964 ----
                    }
                    else
                    {
+                       if (iptr->isn_type == ISN_LOADEXPORT)
+                       {
+                           int idx = get_script_item_idx(sid, name, 0,
+                                                                  NULL, NULL);
+                           svar_T      *sv;
+ 
+                           if (idx >= 0)
+                           {
+                               sv = ((svar_T *)SCRIPT_ITEM(sid)
+                                                 ->sn_var_vals.ga_data) + idx;
+                               if (!sv->sv_export)
+                               {
+                                   SOURCING_LNUM = iptr->isn_lnum;
+                                   semsg(_(e_item_not_exported_in_script_str),
+                                                                        name);
+                                   goto on_error;
+                               }
+                           }
+                       }
                        if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                            goto theend;
                        copy_tv(&di->di_tv, STACK_TV_BOT(0));
***************
*** 3039,3058 ****
                *tv = *STACK_TV_BOT(0);
                break;
  
!           // store s: variable in old script
            case ISN_STORES:
                {
!                   hashtab_T   *ht = &SCRIPT_VARS(
!                                              iptr->isn_arg.loadstore.ls_sid);
                    char_u      *name = iptr->isn_arg.loadstore.ls_name;
!                   dictitem_T  *di = find_var_in_ht(ht, 0, name + 2, TRUE);
  
                    --ectx->ec_stack.ga_len;
                    if (di == NULL)
                        store_var(name, STACK_TV_BOT(0));
                    else
                    {
!                       SOURCING_LNUM = iptr->isn_lnum;
                        if (var_check_permission(di, name) == FAIL)
                        {
                            clear_tv(STACK_TV_BOT(0));
--- 3080,3127 ----
                *tv = *STACK_TV_BOT(0);
                break;
  
!           // store s: variable in old script or autoload import
            case ISN_STORES:
+           case ISN_STOREEXPORT:
                {
!                   int         sid = iptr->isn_arg.loadstore.ls_sid;
!                   hashtab_T   *ht = &SCRIPT_VARS(sid);
                    char_u      *name = iptr->isn_arg.loadstore.ls_name;
!                   dictitem_T  *di = find_var_in_ht(ht, 0,
!                                           iptr->isn_type == ISN_STORES
!                                                    ? name + 2 : name, TRUE);
  
                    --ectx->ec_stack.ga_len;
+                   SOURCING_LNUM = iptr->isn_lnum;
                    if (di == NULL)
+                   {
+                       if (iptr->isn_type == ISN_STOREEXPORT)
+                       {
+                           semsg(_(e_undefined_variable_str), name);
+                           goto on_error;
+                       }
                        store_var(name, STACK_TV_BOT(0));
+                   }
                    else
                    {
!                       if (iptr->isn_type == ISN_STOREEXPORT)
!                       {
!                           int idx = get_script_item_idx(sid, name, 0,
!                                                                  NULL, NULL);
! 
!                           if (idx >= 0)
!                           {
!                               svar_T  *sv = ((svar_T *)SCRIPT_ITEM(sid)
!                                                 ->sn_var_vals.ga_data) + idx;
! 
!                               if (!sv->sv_export)
!                               {
!                                   semsg(_(e_item_not_exported_in_script_str),
!                                                                        name);
!                                   goto on_error;
!                               }
!                           }
!                       }
                        if (var_check_permission(di, name) == FAIL)
                        {
                            clear_tv(STACK_TV_BOT(0));
***************
*** 5409,5419 ****
  #endif
                break;
            case ISN_INSTR:
                {
!                   smsg("%s%4d INSTR", pfx, current);
!                   list_instructions("    ", iptr->isn_arg.instr,
!                                                               INT_MAX, NULL);
!                   msg("     -------------");
                }
                break;
            case ISN_SUBSTITUTE:
--- 5478,5492 ----
  #endif
                break;
            case ISN_INSTR:
+               smsg("%s%4d INSTR", pfx, current);
+               list_instructions("    ", iptr->isn_arg.instr, INT_MAX, NULL);
+               msg("     -------------");
+               break;
+           case ISN_SOURCE:
                {
!                   scriptitem_T    *si = SCRIPT_ITEM(iptr->isn_arg.number);
! 
!                   smsg("%s%4d SOURCE %s", pfx, current, si->sn_name);
                }
                break;
            case ISN_SUBSTITUTE:
***************
*** 5500,5511 ****
                }
                break;
            case ISN_LOADS:
                {
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%s%4d LOADS s:%s from %s", pfx, current,
!                                iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
            case ISN_LOADAUTO:
--- 5573,5587 ----
                }
                break;
            case ISN_LOADS:
+           case ISN_LOADEXPORT:
                {
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%s%4d %s s:%s from %s", pfx, current,
!                           iptr->isn_type == ISN_LOADS ? "LOADS"
!                                                               : "LOADEXPORT",
!                           iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
            case ISN_LOADAUTO:
***************
*** 5586,5596 ****
                smsg("%s%4d STORET %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STORES:
                {
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%s%4d STORES %s in %s", pfx, current,
                                 iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
--- 5662,5675 ----
                smsg("%s%4d STORET %s", pfx, current, iptr->isn_arg.string);
                break;
            case ISN_STORES:
+           case ISN_STOREEXPORT:
                {
                    scriptitem_T *si = SCRIPT_ITEM(
                                               iptr->isn_arg.loadstore.ls_sid);
  
!                   smsg("%s%4d %s %s in %s", pfx, current,
!                               iptr->isn_type == ISN_STORES
!                                                   ? "STORES" : "STOREEXPORT",
                                 iptr->isn_arg.loadstore.ls_name, si->sn_name);
                }
                break;
*** ../vim-8.2.4649/src/vim9instr.c     2022-03-27 16:29:49.876153380 +0100
--- src/vim9instr.c     2022-03-30 20:40:15.387219798 +0100
***************
*** 1066,1072 ****
      isn_T     *isn;
  
      RETURN_OK_IF_SKIP(cctx);
!     if (isn_type == ISN_LOADS)
        isn = generate_instr_type(cctx, isn_type, type);
      else
        isn = generate_instr_drop(cctx, isn_type, 1);
--- 1066,1072 ----
      isn_T     *isn;
  
      RETURN_OK_IF_SKIP(cctx);
!     if (isn_type == ISN_LOADS || isn_type == ISN_LOADEXPORT)
        isn = generate_instr_type(cctx, isn_type, type);
      else
        isn = generate_instr_drop(cctx, isn_type, 1);
***************
*** 1728,1733 ****
--- 1728,1748 ----
  }
  
  /*
+  * Generate an ISN_SOURCE instruction.
+  */
+     int
+ generate_SOURCE(cctx_T *cctx, int sid)
+ {
+     isn_T     *isn;
+ 
+     if ((isn = generate_instr(cctx, ISN_SOURCE)) == NULL)
+       return FAIL;
+     isn->isn_arg.number = sid;
+ 
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_PUT instruction.
   */
      int
***************
*** 1913,1921 ****
            return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
        case dest_script:
            if (scriptvar_idx < 0)
                // "s:" may be included in the name.
!               return generate_OLDSCRIPT(cctx, ISN_STORES, name,
!                                                         scriptvar_sid, type);
            return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
                                           scriptvar_sid, scriptvar_idx, type);
        case dest_local:
--- 1928,1949 ----
            return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
        case dest_script:
            if (scriptvar_idx < 0)
+           {
+               isntype_T isn_type = ISN_STORES;
+ 
+               if (SCRIPT_ID_VALID(scriptvar_sid)
+                        && SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload)
+               {
+                   // "import autoload './dir/script.vim'" - load script first
+                   if (generate_SOURCE(cctx, scriptvar_sid) == FAIL)
+                       return FAIL;
+                   isn_type = ISN_STOREEXPORT;
+               }
+ 
                // "s:" may be included in the name.
!               return generate_OLDSCRIPT(cctx, isn_type, name,
!                                                     scriptvar_sid, type);
!           }
            return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
                                           scriptvar_sid, scriptvar_idx, type);
        case dest_local:
***************
*** 2062,2068 ****
--- 2090,2098 ----
            break;
  
        case ISN_LOADS:
+       case ISN_LOADEXPORT:
        case ISN_STORES:
+       case ISN_STOREEXPORT:
            vim_free(isn->isn_arg.loadstore.ls_name);
            break;
  
***************
*** 2089,2095 ****
                if (isn->isn_arg.funcref.fr_func_name == NULL)
                {
                    dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                          + isn->isn_arg.funcref.fr_dfunc_idx;
                    ufunc_T *ufunc = dfunc->df_ufunc;
  
                    if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
--- 2119,2125 ----
                if (isn->isn_arg.funcref.fr_func_name == NULL)
                {
                    dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                       + isn->isn_arg.funcref.fr_dfunc_idx;
                    ufunc_T *ufunc = dfunc->df_ufunc;
  
                    if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
***************
*** 2109,2118 ****
        case ISN_DCALL:
            {
                dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                                              + isn->isn_arg.dfunc.cdf_idx;
  
                if (dfunc->df_ufunc != NULL
!                              && func_name_refcount(dfunc->df_ufunc->uf_name))
                    func_ptr_unref(dfunc->df_ufunc);
            }
            break;
--- 2139,2148 ----
        case ISN_DCALL:
            {
                dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
!                   + isn->isn_arg.dfunc.cdf_idx;
  
                if (dfunc->df_ufunc != NULL
!                       && func_name_refcount(dfunc->df_ufunc->uf_name))
                    func_ptr_unref(dfunc->df_ufunc);
            }
            break;
***************
*** 2140,2146 ****
  
        case ISN_CMDMOD:
            vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod
!                                              ->cmod_filter_regmatch.regprog);
            vim_free(isn->isn_arg.cmdmod.cf_cmdmod);
            break;
  
--- 2170,2176 ----
  
        case ISN_CMDMOD:
            vim_regfree(isn->isn_arg.cmdmod.cf_cmdmod
!                   ->cmod_filter_regmatch.regprog);
            vim_free(isn->isn_arg.cmdmod.cf_cmdmod);
            break;
  
***************
*** 2243,2263 ****
        case ISN_STORE:
        case ISN_STOREINDEX:
        case ISN_STORENR:
!       case ISN_STOREOUTER:
!       case ISN_STORERANGE:
!       case ISN_STOREREG:
!       case ISN_STOREV:
!       case ISN_STRINDEX:
!       case ISN_STRSLICE:
!       case ISN_THROW:
!       case ISN_TRYCONT:
!       case ISN_UNLETINDEX:
!       case ISN_UNLETRANGE:
!       case ISN_UNPACK:
!       case ISN_USEDICT:
!           // nothing allocated
!           break;
!     }
  }
  
      void
--- 2273,2294 ----
        case ISN_STORE:
        case ISN_STOREINDEX:
        case ISN_STORENR:
!       case ISN_SOURCE:
!     case ISN_STOREOUTER:
!     case ISN_STORERANGE:
!     case ISN_STOREREG:
!     case ISN_STOREV:
!     case ISN_STRINDEX:
!     case ISN_STRSLICE:
!     case ISN_THROW:
!     case ISN_TRYCONT:
!     case ISN_UNLETINDEX:
!     case ISN_UNLETRANGE:
!     case ISN_UNPACK:
!     case ISN_USEDICT:
!       // nothing allocated
!       break;
! }
  }
  
      void
*** ../vim-8.2.4649/src/proto/vim9instr.pro     2022-03-27 16:29:49.876153380 
+0100
--- src/proto/vim9instr.pro     2022-03-30 15:26:07.158733162 +0100
***************
*** 54,59 ****
--- 54,60 ----
  int generate_STRINGMEMBER(cctx_T *cctx, char_u *name, size_t len);
  int generate_ECHO(cctx_T *cctx, int with_white, int count);
  int generate_MULT_EXPR(cctx_T *cctx, isntype_T isn_type, int count);
+ int generate_SOURCE(cctx_T *cctx, int sid);
  int generate_PUT(cctx_T *cctx, int regname, linenr_T lnum);
  int generate_EXEC_copy(cctx_T *cctx, isntype_T isntype, char_u *line);
  int generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *str);
*** ../vim-8.2.4649/src/vim.h   2022-03-28 15:22:31.490443719 +0100
--- src/vim.h   2022-03-30 15:36:18.057768667 +0100
***************
*** 1571,1576 ****
--- 1571,1579 ----
   */
  #define MAXMAPLEN   50
  
+ // maximum length of a function name, including SID and NUL
+ #define MAX_FUNC_NAME_LEN   200
+ 
  // Size in bytes of the hash used in the undo file.
  #define UNDO_HASH_SIZE 32
  
*** ../vim-8.2.4649/src/userfunc.c      2022-03-17 16:30:00.174908142 +0000
--- src/userfunc.c      2022-03-30 15:58:23.858836587 +0100
***************
*** 1884,1906 ****
  }
  
  /*
   * Find a function "name" in script "sid".
   */
      static ufunc_T *
  find_func_with_sid(char_u *name, int sid)
  {
      hashitem_T            *hi;
!     char_u        buffer[200];
  
      if (!SCRIPT_ID_VALID(sid))
        return NULL;    // not in a script
  
!     // A script-local function is stored as "<SNR>99_name".
!     buffer[0] = K_SPECIAL;
!     buffer[1] = KS_EXTRA;
!     buffer[2] = (int)KE_SNR;
!     vim_snprintf((char *)buffer + 3, sizeof(buffer) - 3, "%ld_%s",
!                                                             (long)sid, name);
      hi = hash_find(&func_hashtab, buffer);
      if (!HASHITEM_EMPTY(hi))
        return HI2UF(hi);
--- 1884,1916 ----
  }
  
  /*
+  * Concatenate the script ID and function name into  "<SNR>99_name".
+  * "buffer" must have size MAX_FUNC_NAME_LEN.
+  */
+     void
+ func_name_with_sid(char_u *name, int sid, char_u *buffer)
+ {
+     // A script-local function is stored as "<SNR>99_name".
+     buffer[0] = K_SPECIAL;
+     buffer[1] = KS_EXTRA;
+     buffer[2] = (int)KE_SNR;
+     vim_snprintf((char *)buffer + 3, MAX_FUNC_NAME_LEN - 3, "%ld_%s",
+                                                             (long)sid, name);
+ }
+ 
+ /*
   * Find a function "name" in script "sid".
   */
      static ufunc_T *
  find_func_with_sid(char_u *name, int sid)
  {
      hashitem_T            *hi;
!     char_u        buffer[MAX_FUNC_NAME_LEN];
  
      if (!SCRIPT_ID_VALID(sid))
        return NULL;    // not in a script
  
!     func_name_with_sid(name, sid, buffer);
      hi = hash_find(&func_hashtab, buffer);
      if (!HASHITEM_EMPTY(hi))
        return HI2UF(hi);
***************
*** 1914,1920 ****
  find_func_with_prefix(char_u *name, int sid)
  {
      hashitem_T            *hi;
!     char_u        buffer[200];
      scriptitem_T    *si;
  
      if (vim_strchr(name, AUTOLOAD_CHAR) != NULL)
--- 1924,1930 ----
  find_func_with_prefix(char_u *name, int sid)
  {
      hashitem_T            *hi;
!     char_u        buffer[MAX_FUNC_NAME_LEN];
      scriptitem_T    *si;
  
      if (vim_strchr(name, AUTOLOAD_CHAR) != NULL)
*** ../vim-8.2.4649/src/proto/userfunc.pro      2022-02-21 13:13:44.919693176 
+0000
--- src/proto/userfunc.pro      2022-03-30 15:41:26.061275045 +0100
***************
*** 8,13 ****
--- 8,14 ----
  void emsg_funcname(char *ermsg, char_u *name);
  int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, 
evalarg_T *evalarg, funcexe_T *funcexe);
  char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int 
*error);
+ void func_name_with_sid(char_u *name, int sid, char_u *buffer);
  ufunc_T *find_func_even_dead(char_u *name, int flags);
  ufunc_T *find_func(char_u *name, int is_global);
  int func_is_global(ufunc_T *ufunc);
*** ../vim-8.2.4649/src/testdir/test_vim9_import.vim    2022-03-21 
20:40:32.408367357 +0000
--- src/testdir/test_vim9_import.vim    2022-03-30 20:57:56.315691134 +0100
***************
*** 840,845 ****
--- 840,974 ----
    &rtp = save_rtp
  enddef
  
+ def Test_autoload_import_relative()
+   var lines =<< trim END
+       vim9script
+ 
+       g:loaded = 'yes'
+       export def RelFunc(): string
+         return 'relfunc'
+       enddef
+       def NotExported()
+         echo 'not'
+       enddef
+ 
+       export var someText = 'some text'
+       var notexp = 'bad'
+   END
+   writefile(lines, 'XimportRel.vim')
+   writefile(lines, 'XimportRel2.vim')
+   writefile(lines, 'XimportRel3.vim')
+ 
+   lines =<< trim END
+       vim9script
+       g:loaded = 'no'
+       import autoload './XimportRel.vim'
+       assert_equal('no', g:loaded)
+ 
+       def AFunc(): string
+         var res = ''
+         res ..= XimportRel.RelFunc()
+         res ..= '/'
+         res ..= XimportRel.someText
+         XimportRel.someText = 'from AFunc'
+         return res
+       enddef
+       # script not loaded when compiling
+       defcompile
+       assert_equal('no', g:loaded)
+ 
+       assert_equal('relfunc/some text', AFunc())
+       assert_equal('yes', g:loaded)
+       unlet g:loaded
+ 
+       assert_equal('from AFunc', XimportRel.someText)
+       XimportRel.someText = 'from script'
+       assert_equal('from script', XimportRel.someText)
+   END
+   v9.CheckScriptSuccess(lines)
+ 
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel.vim'
+       echo XimportRel.NotExported()
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: 
NotExported', 3)
+ 
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel.vim'
+       echo XimportRel.notexp
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 
3)
+ 
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel.vim'
+       XimportRel.notexp = 'bad'
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 
3)
+ 
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel.vim'
+       def Func()
+         echo XimportRel.NotExported()
+       enddef
+       Func()
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: 
NotExported', 1)
+ 
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel.vim'
+       def Func()
+         echo XimportRel.notexp
+       enddef
+       Func()
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 
1)
+ 
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel.vim'
+       def Func()
+         XimportRel.notexp = 'bad'
+       enddef
+       Func()
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 
1)
+ 
+   # does not fail if the script wasn't loaded yet
+   g:loaded = 'no'
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel2.vim'
+       def Func()
+         echo XimportRel2.notexp
+       enddef
+       defcompile
+   END
+   v9.CheckScriptSuccess(lines)
+   assert_equal('no', g:loaded)
+ 
+   # fails with a not loaded import
+   lines =<< trim END
+       vim9script
+       import autoload './XimportRel3.vim'
+       def Func()
+         XimportRel3.notexp = 'bad'
+       enddef
+       Func()
+   END
+   v9.CheckScriptFailure(lines, 'E1049: Item not exported in script: notexp', 
1)
+   assert_equal('yes', g:loaded)
+   unlet g:loaded
+ 
+   delete('XimportRel.vim')
+   delete('XimportRel2.vim')
+   delete('XimportRel3.vim')
+ enddef
+ 
  func Test_import_in_diffexpr()
    CheckExecutable diff
  
***************
*** 2379,2391 ****
        vim9script
        import autoload './doesNotExist.vim'
    END
!   v9.CheckScriptFailure(lines, 'E1264:')
  
    lines =<< trim END
        vim9script
        import autoload '/dir/doesNotExist.vim'
    END
!   v9.CheckScriptFailure(lines, 'E1264:')
  
    lines =<< trim END
        vim9script
--- 2508,2520 ----
        vim9script
        import autoload './doesNotExist.vim'
    END
!   v9.CheckScriptSuccess(lines)
  
    lines =<< trim END
        vim9script
        import autoload '/dir/doesNotExist.vim'
    END
!   v9.CheckScriptSuccess(lines)
  
    lines =<< trim END
        vim9script
*** ../vim-8.2.4649/src/testdir/test_vim9_disassemble.vim       2022-03-27 
16:29:49.880153368 +0100
--- src/testdir/test_vim9_disassemble.vim       2022-03-30 21:07:59.595912515 
+0100
***************
*** 318,323 ****
--- 318,363 ----
    &rtp = save_rtp
  enddef
  
+ def Test_disassemble_import_autoload()
+   writefile(['vim9script'], 'XimportAL.vim')
+ 
+   var lines =<< trim END
+       vim9script
+       import autoload './XimportAL.vim'
+ 
+       def AutoloadFunc()
+         echo XimportAL.SomeFunc()
+         echo XimportAL.someVar
+         XimportAL.someVar = "yes"
+       enddef
+ 
+       var res = execute('disass AutoloadFunc')
+       assert_match('<SNR>\d*_AutoloadFunc.*' ..
+             'echo XimportAL.SomeFunc()\_s*' ..
+             '\d SOURCE /home/mool/vim/vim82/src/testdir/XimportAL.vim\_s*' ..
+             '\d PUSHFUNC "<80><fd>R\d\+_SomeFunc"\_s*' ..
+             '\d PCALL top (argc 0)\_s*' ..
+             '\d PCALL end\_s*' ..
+             '\d ECHO 1\_s*' ..
+ 
+             'echo XimportAL.someVar\_s*' ..
+             '\d SOURCE .*/testdir/XimportAL.vim\_s*' ..
+             '\d LOADEXPORT s:someVar from .*/testdir/XimportAL.vim\_s*' ..
+             '\d ECHO 1\_s*' ..
+ 
+             'XimportAL.someVar = "yes"\_s*' ..
+             '\d\+ PUSHS "yes"\_s*' ..
+             '\d\+ SOURCE .*/testdir/XimportAL.vim\_s*' ..
+             '\d\+ STOREEXPORT someVar in .*/testdir/XimportAL.vim\_s*' ..
+ 
+             '\d\+ RETURN void',
+             res)
+   END
+   v9.CheckScriptSuccess(lines)
+ 
+   delete('XimportAL.vim')
+ enddef
+ 
  def s:ScriptFuncStore()
    var localnr = 1
    localnr = 2
*** ../vim-8.2.4649/src/version.c       2022-03-30 10:57:36.739346189 +0100
--- src/version.c       2022-03-30 14:20:17.028426263 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     4650,
  /**/

-- 
Two cows are standing together in a field.  One asks the other:
"So what do you think about this Mad Cow Disease?"
The other replies: "That doesn't concern me. I'm a helicopter."

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220330201301.11A3A1C13A3%40moolenaar.net.

Raspunde prin e-mail lui