Patch 8.2.4216
Problem:    Vim9: cannot use a function from an autoload import directly.
Solution:   Add the AUTOLOAD instruction to figure out at runtime.
            (closes #9620)
Files:      src/vim9expr.c, src/vim9.h, src/vim9execute.c, src/vim9instr.c,
            src/proto/vim9instr.pro, src/testdir/test_vim9_import.vim,
            src/testdir/test_vim9_disassemble.vim


*** ../vim-8.2.4215/src/vim9expr.c      2022-01-20 17:35:45.577672159 +0000
--- src/vim9expr.c      2022-01-25 15:34:50.599932212 +0000
***************
*** 307,317 ****
            char_u  *auto_name = concat_str(si->sn_autoload_prefix, exp_name);
  
            // autoload script must be loaded later, access by the autoload
!           // name.
            if (cc == '(' || paren_follows_after_expr)
                res = generate_PUSHFUNC(cctx, auto_name, &t_func_any);
            else
!               res = generate_LOAD(cctx, ISN_LOADG, 0, auto_name, &t_any);
            vim_free(auto_name);
            done = TRUE;
        }
--- 307,318 ----
            char_u  *auto_name = concat_str(si->sn_autoload_prefix, exp_name);
  
            // autoload script must be loaded later, access by the autoload
!           // name.  If a '(' follows it must be a function.  Otherwise we
!           // don't know, it can be "script.Func".
            if (cc == '(' || paren_follows_after_expr)
                res = generate_PUSHFUNC(cctx, auto_name, &t_func_any);
            else
!               res = generate_AUTOLOAD(cctx, auto_name, &t_any);
            vim_free(auto_name);
            done = TRUE;
        }
*** ../vim-8.2.4215/src/vim9.h  2022-01-12 16:18:13.801613093 +0000
--- src/vim9.h  2022-01-25 15:05:25.470478384 +0000
***************
*** 92,97 ****
--- 92,99 ----
      ISN_NEWLIST,      // push list from stack items, size is isn_arg.number
      ISN_NEWDICT,      // push dict from stack items, size is isn_arg.number
  
+     ISN_AUTOLOAD,     // get item from autoload import, function or variable
+ 
      // function call
      ISN_BCALL,            // call builtin function isn_arg.bfunc
      ISN_DCALL,            // call def function isn_arg.dfunc
*** ../vim-8.2.4215/src/vim9execute.c   2022-01-23 20:00:38.724909590 +0000
--- src/vim9execute.c   2022-01-25 15:20:33.124749819 +0000
***************
*** 2260,2265 ****
--- 2260,2336 ----
  }
  
  /*
+  * Load instruction for w:/b:/g:/t: variable.
+  * "isn_type" is used instead of "iptr->isn_type".
+  */
+     static int
+ load_namespace_var(ectx_T *ectx, isntype_T isn_type, isn_T *iptr)
+ {
+     dictitem_T        *di = NULL;
+     hashtab_T *ht = NULL;
+     char      namespace;
+ 
+     if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+       return NOTDONE;
+     switch (isn_type)
+     {
+       case ISN_LOADG:
+           ht = get_globvar_ht();
+           namespace = 'g';
+           break;
+       case ISN_LOADB:
+           ht = &curbuf->b_vars->dv_hashtab;
+           namespace = 'b';
+           break;
+       case ISN_LOADW:
+           ht = &curwin->w_vars->dv_hashtab;
+           namespace = 'w';
+           break;
+       case ISN_LOADT:
+           ht = &curtab->tp_vars->dv_hashtab;
+           namespace = 't';
+           break;
+       default:  // Cannot reach here
+           return NOTDONE;
+     }
+     di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE);
+ 
+     if (di == NULL && ht == get_globvar_ht()
+                           && vim_strchr(iptr->isn_arg.string,
+                                       AUTOLOAD_CHAR) != NULL)
+     {
+       // Global variable has an autoload name, may still need
+       // to load the script.
+       if (script_autoload(iptr->isn_arg.string, FALSE))
+           di = find_var_in_ht(ht, 0,
+                                  iptr->isn_arg.string, TRUE);
+       if (did_emsg)
+           return FAIL;
+     }
+ 
+     if (di == NULL)
+     {
+       SOURCING_LNUM = iptr->isn_lnum;
+       if (vim_strchr(iptr->isn_arg.string,
+                                       AUTOLOAD_CHAR) != NULL)
+           // no check if the item exists in the script but
+           // isn't exported, it is too complicated
+           semsg(_(e_item_not_found_in_script_str),
+                                        iptr->isn_arg.string);
+       else
+           semsg(_(e_undefined_variable_char_str),
+                            namespace, iptr->isn_arg.string);
+       return FAIL;
+     }
+     else
+     {
+       copy_tv(&di->di_tv, STACK_TV_BOT(0));
+       ++ectx->ec_stack.ga_len;
+     }
+     return OK;
+ }
+ 
+ /*
   * Execute instructions in execution context "ectx".
   * Return OK or FAIL;
   */
***************
*** 2772,2839 ****
            case ISN_LOADW:
            case ISN_LOADT:
                {
!                   dictitem_T  *di = NULL;
!                   hashtab_T   *ht = NULL;
!                   char        namespace;
  
!                   if (GA_GROW_FAILS(&ectx->ec_stack, 1))
                        goto theend;
!                   switch (iptr->isn_type)
!                   {
!                       case ISN_LOADG:
!                           ht = get_globvar_ht();
!                           namespace = 'g';
!                           break;
!                       case ISN_LOADB:
!                           ht = &curbuf->b_vars->dv_hashtab;
!                           namespace = 'b';
!                           break;
!                       case ISN_LOADW:
!                           ht = &curwin->w_vars->dv_hashtab;
!                           namespace = 'w';
!                           break;
!                       case ISN_LOADT:
!                           ht = &curtab->tp_vars->dv_hashtab;
!                           namespace = 't';
!                           break;
!                       default:  // Cannot reach here
!                           goto theend;
!                   }
!                   di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE);
! 
!                   if (di == NULL && ht == get_globvar_ht()
!                                           && vim_strchr(iptr->isn_arg.string,
!                                                       AUTOLOAD_CHAR) != NULL)
!                   {
!                       // Global variable has an autoload name, may still need
!                       // to load the script.
!                       if (script_autoload(iptr->isn_arg.string, FALSE))
!                           di = find_var_in_ht(ht, 0,
!                                                  iptr->isn_arg.string, TRUE);
!                       if (did_emsg)
!                           goto on_error;
!                   }
! 
!                   if (di == NULL)
!                   {
!                       SOURCING_LNUM = iptr->isn_lnum;
!                       if (vim_strchr(iptr->isn_arg.string,
!                                                       AUTOLOAD_CHAR) != NULL)
!                           // no check if the item exists in the script but
!                           // isn't exported, it is too complicated
!                           semsg(_(e_item_not_found_in_script_str),
!                                                        iptr->isn_arg.string);
!                       else
!                           semsg(_(e_undefined_variable_char_str),
!                                            namespace, iptr->isn_arg.string);
                        goto on_error;
-                   }
-                   else
-                   {
-                       copy_tv(&di->di_tv, STACK_TV_BOT(0));
-                       ++ectx->ec_stack.ga_len;
-                   }
                }
                break;
  
            // load autoload variable
--- 2843,2856 ----
            case ISN_LOADW:
            case ISN_LOADT:
                {
!                   int res = load_namespace_var(ectx, iptr->isn_type, iptr);
  
!                   if (res == NOTDONE)
                        goto theend;
!                   if (res == FAIL)
                        goto on_error;
                }
+ 
                break;
  
            // load autoload variable
***************
*** 3264,3269 ****
--- 3281,3313 ----
                }
                break;
  
+           case ISN_AUTOLOAD:
+               {
+                   char_u  *name = iptr->isn_arg.string;
+ 
+                   (void)script_autoload(name, FALSE);
+                   if (find_func(name, TRUE))
+                   {
+                       if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+                           goto theend;
+                       tv = STACK_TV_BOT(0);
+                       tv->v_lock = 0;
+                       ++ectx->ec_stack.ga_len;
+                       tv->v_type = VAR_FUNC;
+                       tv->vval.v_string = vim_strsave(name);
+                   }
+                   else
+                   {
+                       int res = load_namespace_var(ectx, ISN_LOADG, iptr);
+ 
+                       if (res == NOTDONE)
+                           goto theend;
+                       if (res == FAIL)
+                           goto on_error;
+                   }
+               }
+               break;
+ 
            case ISN_UNLET:
                if (do_unlet(iptr->isn_arg.unlet.ul_name,
                                       iptr->isn_arg.unlet.ul_forceit) == FAIL)
***************
*** 5596,5601 ****
--- 5640,5648 ----
            case ISN_PUSHEXC:
                smsg("%s%4d PUSH v:exception", pfx, current);
                break;
+           case ISN_AUTOLOAD:
+               smsg("%s%4d AUTOLOAD %s", pfx, current, iptr->isn_arg.string);
+               break;
            case ISN_UNLET:
                smsg("%s%4d UNLET%s %s", pfx, current,
                        iptr->isn_arg.unlet.ul_forceit ? "!" : "",
*** ../vim-8.2.4215/src/vim9instr.c     2022-01-24 13:54:42.298380706 +0000
--- src/vim9instr.c     2022-01-25 15:22:22.906596772 +0000
***************
*** 744,749 ****
--- 744,766 ----
  }
  
  /*
+  * Generate an ISN_AUTOLOAD instruction.
+  */
+     int
+ generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type)
+ {
+     isn_T     *isn;
+ 
+     RETURN_OK_IF_SKIP(cctx);
+     if ((isn = generate_instr_type(cctx, ISN_AUTOLOAD, type)) == NULL)
+       return FAIL;
+     isn->isn_arg.string = vim_strsave(name);
+     if (isn->isn_arg.string == NULL)
+       return FAIL;
+     return OK;
+ }
+ 
+ /*
   * Generate an ISN_GETITEM instruction with "index".
   * "with_op" is TRUE for "+=" and other operators, the stack has the current
   * value below the list with values.
***************
*** 1929,1934 ****
--- 1946,1952 ----
  {
      switch (isn->isn_type)
      {
+       case ISN_AUTOLOAD:
        case ISN_DEF:
        case ISN_EXEC:
        case ISN_EXECRANGE:
*** ../vim-8.2.4215/src/proto/vim9instr.pro     2022-01-15 21:44:39.832970792 
+0000
--- src/proto/vim9instr.pro     2022-01-25 15:04:38.067396232 +0000
***************
*** 23,28 ****
--- 23,29 ----
  int generate_PUSHJOB(cctx_T *cctx, job_T *job);
  int generate_PUSHBLOB(cctx_T *cctx, blob_T *blob);
  int generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type);
+ int generate_AUTOLOAD(cctx_T *cctx, char_u *name, type_T *type);
  int generate_GETITEM(cctx_T *cctx, int index, int with_op);
  int generate_SLICE(cctx_T *cctx, int count);
  int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
*** ../vim-8.2.4215/src/testdir/test_vim9_import.vim    2022-01-24 
21:27:57.648085201 +0000
--- src/testdir/test_vim9_import.vim    2022-01-25 15:33:26.537580937 +0000
***************
*** 703,708 ****
--- 703,743 ----
    set opfunc=
    bwipe!
    delete('Xdir', 'rf')
+   nunmap <F3>
+   &rtp = save_rtp
+ enddef
+ 
+ def Test_set_opfunc_to_autoload_func_directly()
+   mkdir('Xdir/autoload', 'p')
+   var save_rtp = &rtp
+   exe 'set rtp^=' .. getcwd() .. '/Xdir'
+ 
+   var lines =<< trim END
+       vim9script
+       export def Opfunc(..._)
+         g:opfunc_called = 'yes'
+       enddef
+   END
+   writefile(lines, 'Xdir/autoload/opfunc.vim')
+ 
+   new
+   lines =<< trim END
+       vim9script
+       import autoload 'opfunc.vim'
+       nnoremap <expr> <F3> TheFunc()
+       def TheFunc(): string
+         &operatorfunc = opfunc.Opfunc
+         return 'g@'
+       enddef
+       feedkeys("\<F3>l", 'xt')
+       assert_equal('yes', g:opfunc_called)
+   END
+   CheckScriptSuccess(lines)
+ 
+   set opfunc=
+   bwipe!
+   delete('Xdir', 'rf')
+   nunmap <F3>
    &rtp = save_rtp
  enddef
  
*** ../vim-8.2.4215/src/testdir/test_vim9_disassemble.vim       2022-01-04 
15:54:34.596486122 +0000
--- src/testdir/test_vim9_disassemble.vim       2022-01-25 15:49:20.234358855 
+0000
***************
*** 1,6 ****
--- 1,7 ----
  " Test the :disassemble command, and compilation as a side effect
  
  source check.vim
+ source vim9.vim
  
  func NotCompiled()
    echo "not"
***************
*** 286,306 ****
  enddef
  
  def Test_disassemble_push()
!   var res = execute('disass s:ScriptFuncPush')
!   assert_match('<SNR>\d*_ScriptFuncPush.*' ..
!         'localbool = true.*' ..
!         ' PUSH true.*' ..
!         'localspec = v:none.*' ..
!         ' PUSH v:none.*' ..
!         'localblob = 0z1234.*' ..
!         ' PUSHBLOB 0z1234.*',
!         res)
!   if has('float')
!     assert_match('<SNR>\d*_ScriptFuncPush.*' ..
!           'localfloat = 1.234.*' ..
!           ' PUSHF 1.234.*',
!           res)
!   endif
  enddef
  
  def s:ScriptFuncStore()
--- 287,321 ----
  enddef
  
  def Test_disassemble_push()
!   mkdir('Xdir/autoload', 'p')
!   var save_rtp = &rtp
!   exe 'set rtp^=' .. getcwd() .. '/Xdir'
! 
!   var lines =<< trim END
!       vim9script
!   END
!   writefile(lines, 'Xdir/autoload/autoscript.vim')
! 
!   lines =<< trim END
!       vim9script
!       import autoload 'autoscript.vim'
! 
!       def s:AutoloadFunc()
!         &operatorfunc = autoscript.Opfunc
!       enddef
! 
!       var res = execute('disass s:AutoloadFunc')
!       assert_match('<SNR>\d*_AutoloadFunc.*' ..
!             '&operatorfunc = autoscript.Opfunc\_s*' ..
!             '0 AUTOLOAD autoscript#Opfunc\_s*' ..
!             '1 STOREFUNCOPT &operatorfunc\_s*' ..
!             '2 RETURN void',
!             res)
!   END
!   CheckScriptSuccess(lines)
! 
!   delete('Xdir', 'rf')
!   &rtp = save_rtp
  enddef
  
  def s:ScriptFuncStore()
*** ../vim-8.2.4215/src/version.c       2022-01-25 13:52:49.409429882 +0000
--- src/version.c       2022-01-25 15:01:30.003024888 +0000
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     4216,
  /**/

-- 
There are only two hard things in programming: Cache invalidation,
naming things and off-by-one errors.

 /// 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/20220125155314.B57531C042A%40moolenaar.net.

Raspunde prin e-mail lui