To: vim_dev@googlegroups.com Subject: Patch 8.2.4216 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 + &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 TheFunc() + def TheFunc(): string + &operatorfunc = opfunc.Opfunc + return 'g@' + enddef + feedkeys("\l", 'xt') + assert_equal('yes', g:opfunc_called) + END + CheckScriptSuccess(lines) + + set opfunc= + bwipe! + delete('Xdir', 'rf') + nunmap &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('\d*_ScriptFuncPush.*' .. ! 'localbool = true.*' .. ! ' PUSH true.*' .. ! 'localspec = v:none.*' .. ! ' PUSH v:none.*' .. ! 'localblob = 0z1234.*' .. ! ' PUSHBLOB 0z1234.*', ! res) ! if has('float') ! assert_match('\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('\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 -- Bram@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 ///