Patch 8.2.4216

8 views
Skip to first unread message

Bram Moolenaar

unread,
Jan 25, 2022, 10:53:18 AM1/25/22
to vim...@googlegroups.com

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 -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages