summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2022-01-25 15:51:56 +0000
committerBram Moolenaar <Bram@vim.org>2022-01-25 15:51:56 +0000
commit06b77229ca704d00c4f138ed0377556e54d5851f (patch)
tree6b24e9838841943803574a424599d687be2c05f1
parentdc5490e2cbc8c16022a23b449b48c1bd0083f366 (diff)
downloadvim-git-06b77229ca704d00c4f138ed0377556e54d5851f.tar.gz
patch 8.2.4216: Vim9: cannot use a function from an autoload import directlyv8.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)
-rw-r--r--src/proto/vim9instr.pro1
-rw-r--r--src/testdir/test_vim9_disassemble.vim45
-rw-r--r--src/testdir/test_vim9_import.vim35
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h2
-rw-r--r--src/vim9execute.c163
-rw-r--r--src/vim9expr.c5
-rw-r--r--src/vim9instr.c18
8 files changed, 196 insertions, 75 deletions
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 04ac89c7a..be41d7c04 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -23,6 +23,7 @@ int generate_PUSHCHANNEL(cctx_T *cctx, channel_T *channel);
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);
diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim
index 3a7642f7c..8731052aa 100644
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -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,21 +287,35 @@ def s:ScriptFuncPush()
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
+ 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()
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index 506b0ee8d..7878788a0 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -703,6 +703,41 @@ def Test_use_autoload_import_partial_in_opfunc()
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
diff --git a/src/version.c b/src/version.c
index ddc34d864..f5f9a2ef9 100644
--- 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 */
/**/
+ 4216,
+/**/
4215,
/**/
4214,
diff --git a/src/vim9.h b/src/vim9.h
index 912af1f5d..45e97a2db 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -92,6 +92,8 @@ typedef enum {
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
diff --git a/src/vim9execute.c b/src/vim9execute.c
index e58da5b5e..1232b35bb 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2260,6 +2260,77 @@ execute_for(isn_T *iptr, ectx_T *ectx)
}
/*
+ * 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,68 +2843,14 @@ exec_instructions(ectx_T *ectx)
case ISN_LOADW:
case ISN_LOADT:
{
- dictitem_T *di = NULL;
- hashtab_T *ht = NULL;
- char namespace;
+ int res = load_namespace_var(ectx, iptr->isn_type, iptr);
- if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+ if (res == NOTDONE)
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);
+ if (res == FAIL)
goto on_error;
- }
- else
- {
- copy_tv(&di->di_tv, STACK_TV_BOT(0));
- ++ectx->ec_stack.ga_len;
- }
}
+
break;
// load autoload variable
@@ -3264,6 +3281,33 @@ exec_instructions(ectx_T *ectx)
}
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,6 +5640,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
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 ? "!" : "",
diff --git a/src/vim9expr.c b/src/vim9expr.c
index d5bb276b7..a56219fb3 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -307,11 +307,12 @@ compile_load_scriptvar(
char_u *auto_name = concat_str(si->sn_autoload_prefix, exp_name);
// autoload script must be loaded later, access by the autoload
- // name.
+ // 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_LOAD(cctx, ISN_LOADG, 0, auto_name, &t_any);
+ res = generate_AUTOLOAD(cctx, auto_name, &t_any);
vim_free(auto_name);
done = TRUE;
}
diff --git a/src/vim9instr.c b/src/vim9instr.c
index b6e28b0b9..d000c4acd 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -744,6 +744,23 @@ generate_PUSHFUNC(cctx_T *cctx, char_u *name, type_T *type)
}
/*
+ * 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,6 +1946,7 @@ delete_instr(isn_T *isn)
{
switch (isn->isn_type)
{
+ case ISN_AUTOLOAD:
case ISN_DEF:
case ISN_EXEC:
case ISN_EXECRANGE: