# HG changeset patch # User Bram Moolenaar # Date 1581013808 -3600 # Node ID 9c8b803fe5982a003b1dda184e88495e5e7780fa # Parent f49020e6bf129cd8d3abed95122816f5f73fd461 patch 8.2.0223: some instructions not yet tested Commit: https://github.com/vim/vim/commit/5cab73f8cca46d831fb9337b176493da2a55ed5d Author: Bram Moolenaar Date: Thu Feb 6 19:25:19 2020 +0100 patch 8.2.0223: some instructions not yet tested Problem: Some instructions not yet tested. Solution: Disassemble more instructions. Move tests to a new file. Compile call to s:function(). diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -6,6 +6,7 @@ int get_lambda_tv(char_u **arg, typval_T char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, int no_autoload); void emsg_funcname(char *ermsg, char_u *name); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, funcexe_T *funcexe); +char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error); ufunc_T *find_func(char_u *name, cctx_T *cctx); int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); void save_funccal(funccal_entry_T *entry); @@ -26,7 +27,6 @@ int has_varargs(ufunc_T *ufunc); int function_exists(char_u *name, int no_deref); char_u *get_expanded_name(char_u *name, int check); char_u *get_user_func_name(expand_T *xp, int idx); -void clean_script_functions(int sid); void ex_delfunction(exarg_T *eap); void func_unref(char_u *name); void func_ptr_unref(ufunc_T *fp); diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -268,6 +268,7 @@ NEW_TESTS = \ test_utf8 \ test_utf8_comparisons \ test_vartabs \ + test_vim9_disassemble \ test_vim9_expr \ test_vim9_script \ test_viminfo \ @@ -470,6 +471,7 @@ NEW_TESTS_RES = \ test_user_func.res \ test_usercommands.res \ test_vartabs.res \ + test_vim9_disassemble.res \ test_vim9_expr.res \ test_vim9_script.res \ test_viminfo.res \ diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim new file mode 100644 --- /dev/null +++ b/src/testdir/test_vim9_disassemble.vim @@ -0,0 +1,220 @@ +" Test the :disassemble command, and compilation as a side effect + +func NotCompiled() + echo "not" +endfunc + +let s:scriptvar = 4 +let g:globalvar = 'g' + +def s:ScriptFuncLoad(arg: string) + let local = 1 + buffers + echo arg + echo local + echo v:version + echo s:scriptvar + echo g:globalvar + echo &tabstop + echo $ENVVAR + echo @z +enddef + +def Test_disassembleLoad() + assert_fails('disass NoFunc', 'E1061:') + assert_fails('disass NotCompiled', 'E1062:') + + let res = execute('disass s:ScriptFuncLoad') + assert_match('\d*_ScriptFuncLoad.*' + \ .. 'buffers.*' + \ .. ' EXEC \+buffers.*' + \ .. ' LOAD arg\[-1\].*' + \ .. ' LOAD $0.*' + \ .. ' LOADV v:version.*' + \ .. ' LOADS s:scriptvar from .*test_vim9_disassemble.vim.*' + \ .. ' LOADG g:globalvar.*' + \ .. ' LOADENV $ENVVAR.*' + \ .. ' LOADREG @z.*' + \, res) +enddef + +def s:ScriptFuncPush() + let localbool = true + let localspec = v:none + let localblob = 0z1234 + if has('float') + let localfloat = 1.234 + endif +enddef + +def Test_disassemblePush() + let res = execute('disass s:ScriptFuncPush') + assert_match('\d*_ScriptFuncPush.*' + \ .. 'localbool = true.*' + \ .. ' PUSH v: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() + let localnr = 1 + localnr = 2 + let localstr = 'abc' + localstr = 'xyz' + v:char = 'abc' + s:scriptvar = 'sv' + g:globalvar = 'gv' + &tabstop = 8 + $ENVVAR = 'ev' + @z = 'rv' +enddef + +def Test_disassembleStore() + let res = execute('disass s:ScriptFuncStore') + assert_match('\d*_ScriptFuncStore.*' + \ .. 'localnr = 2.*' + \ .. ' STORE 2 in $0.*' + \ .. 'localstr = ''xyz''.*' + \ .. ' STORE $1.*' + \ .. 'v:char = ''abc''.*' + \ .. 'STOREV v:char.*' + \ .. 's:scriptvar = ''sv''.*' + \ .. ' STORES s:scriptvar in .*test_vim9_disassemble.vim.*' + \ .. 'g:globalvar = ''gv''.*' + \ .. ' STOREG g:globalvar.*' + \ .. '&tabstop = 8.*' + \ .. ' STOREOPT &tabstop.*' + \ .. '$ENVVAR = ''ev''.*' + \ .. ' STOREENV $ENVVAR.*' + \ .. '@z = ''rv''.*' + \ .. ' STOREREG @z.*' + \, res) +enddef + +def s:ScriptFuncTry() + try + echo 'yes' + catch /fail/ + echo 'no' + finally + echo 'end' + endtry +enddef + +def Test_disassembleTry() + let res = execute('disass s:ScriptFuncTry') + assert_match('\d*_ScriptFuncTry.*' + \ .. 'try.*' + \ .. 'TRY catch -> \d\+, finally -> \d\+.*' + \ .. 'catch /fail/.*' + \ .. ' JUMP -> \d\+.*' + \ .. ' PUSH v:exception.*' + \ .. ' PUSHS "fail".*' + \ .. ' COMPARESTRING =\~.*' + \ .. ' JUMP_IF_FALSE -> \d\+.*' + \ .. ' CATCH.*' + \ .. 'finally.*' + \ .. ' PUSHS "end".*' + \ .. 'endtry.*' + \ .. ' ENDTRY.*' + \, res) +enddef + +def s:ScriptFuncNew() + let ll = [1, "two", 333] + let dd = #{one: 1, two: "val"} +enddef + +def Test_disassembleNew() + let res = execute('disass s:ScriptFuncNew') + assert_match('\d*_ScriptFuncNew.*' + \ .. 'let ll = \[1, "two", 333].*' + \ .. 'PUSHNR 1.*' + \ .. 'PUSHS "two".*' + \ .. 'PUSHNR 333.*' + \ .. 'NEWLIST size 3.*' + \ .. 'let dd = #{one: 1, two: "val"}.*' + \ .. 'PUSHS "one".*' + \ .. 'PUSHNR 1.*' + \ .. 'PUSHS "two".*' + \ .. 'PUSHS "val".*' + \ .. 'NEWDICT size 2.*' + \, res) +enddef + +def FuncWithArg(arg) + echo arg +enddef + +func UserFunc() + echo 'nothing' +endfunc + +func UserFuncWithArg(arg) + echo a:arg +endfunc + +def s:ScriptFuncCall(): string + changenr() + char2nr("abc") + Test_disassembleNew() + FuncWithArg(343) + ScriptFuncNew() + s:ScriptFuncNew() + UserFunc() + UserFuncWithArg("foo") + let FuncRef = function("UserFunc") + FuncRef() + let FuncRefWithArg = function("UserFuncWithArg") + FuncRefWithArg("bar") + return "yes" +enddef + +def Test_disassembleCall() + let res = execute('disass s:ScriptFuncCall') + assert_match('\d\+_ScriptFuncCall.*' + \ .. 'changenr().*' + \ .. ' BCALL changenr(argc 0).*' + \ .. 'char2nr("abc").*' + \ .. ' PUSHS "abc".*' + \ .. ' BCALL char2nr(argc 1).*' + \ .. 'Test_disassembleNew().*' + \ .. ' DCALL Test_disassembleNew(argc 0).*' + \ .. 'FuncWithArg(343).*' + \ .. ' PUSHNR 343.*' + \ .. ' DCALL FuncWithArg(argc 1).*' + \ .. 'ScriptFuncNew().*' + \ .. ' DCALL \d\+_ScriptFuncNew(argc 0).*' + \ .. 's:ScriptFuncNew().*' + \ .. ' DCALL \d\+_ScriptFuncNew(argc 0).*' + \ .. 'UserFunc().*' + \ .. ' UCALL UserFunc(argc 0).*' + \ .. 'UserFuncWithArg("foo").*' + \ .. ' PUSHS "foo".*' + \ .. ' UCALL UserFuncWithArg(argc 1).*' + \ .. 'let FuncRef = function("UserFunc").*' + \ .. 'FuncRef().*' + \ .. ' LOAD $\d.*' + \ .. ' PCALL (argc 0).*' + \ .. 'let FuncRefWithArg = function("UserFuncWithArg").*' + \ .. 'FuncRefWithArg("bar").*' + \ .. ' PUSHS "bar".*' + \ .. ' LOAD $\d.*' + \ .. ' PCALL (argc 1).*' + \ .. 'return "yes".*' + \ .. ' PUSHS "yes".*' + \ .. ' RETURN.*' + \, res) +enddef + + +" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -489,215 +489,5 @@ def Test_compile_const_expr() assert_notmatch('JUMP', instr) enddef -func NotCompiled() - echo "not" -endfunc - -let s:scriptvar = 4 -let g:globalvar = 'g' - -def s:ScriptFuncLoad(arg: string) - let local = 1 - buffers - echo arg - echo local - echo v:version - echo s:scriptvar - echo g:globalvar - echo &tabstop - echo $ENVVAR - echo @z -enddef - -def Test_disassembleLoad() - assert_fails('disass NoFunc', 'E1061:') - assert_fails('disass NotCompiled', 'E1062:') - - let res = execute('disass s:ScriptFuncLoad') - assert_match('\d*_ScriptFuncLoad.*' - \ .. 'buffers.*' - \ .. ' EXEC \+buffers.*' - \ .. ' LOAD arg\[-1\].*' - \ .. ' LOAD $0.*' - \ .. ' LOADV v:version.*' - \ .. ' LOADS s:scriptvar from .*test_vim9_script.vim.*' - \ .. ' LOADG g:globalvar.*' - \ .. ' LOADENV $ENVVAR.*' - \ .. ' LOADREG @z.*' - \, res) -enddef - -def s:ScriptFuncPush() - let localbool = true - let localspec = v:none - let localblob = 0z1234 - if has('float') - let localfloat = 1.234 - endif -enddef - -def Test_disassemblePush() - let res = execute('disass s:ScriptFuncPush') - assert_match('\d*_ScriptFuncPush.*' - \ .. 'localbool = true.*' - \ .. ' PUSH v: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() - let localnr = 1 - localnr = 2 - let localstr = 'abc' - localstr = 'xyz' - v:char = 'abc' - s:scriptvar = 'sv' - g:globalvar = 'gv' - &tabstop = 8 - $ENVVAR = 'ev' - @z = 'rv' -enddef - -def Test_disassembleStore() - let res = execute('disass s:ScriptFuncStore') - assert_match('\d*_ScriptFuncStore.*' - \ .. 'localnr = 2.*' - \ .. ' STORE 2 in $0.*' - \ .. 'localstr = ''xyz''.*' - \ .. ' STORE $1.*' - \ .. 'v:char = ''abc''.*' - \ .. 'STOREV v:char.*' - \ .. 's:scriptvar = ''sv''.*' - \ .. ' STORES s:scriptvar in .*test_vim9_script.vim.*' - \ .. 'g:globalvar = ''gv''.*' - \ .. ' STOREG g:globalvar.*' - \ .. '&tabstop = 8.*' - \ .. ' STOREOPT &tabstop.*' - \ .. '$ENVVAR = ''ev''.*' - \ .. ' STOREENV $ENVVAR.*' - \ .. '@z = ''rv''.*' - \ .. ' STOREREG @z.*' - \, res) -enddef - -def s:ScriptFuncTry() - try - echo 'yes' - catch /fail/ - echo 'no' - finally - echo 'end' - endtry -enddef - -def Test_disassembleTry() - let res = execute('disass s:ScriptFuncTry') - assert_match('\d*_ScriptFuncTry.*' - \ .. 'try.*' - \ .. 'TRY catch -> \d\+, finally -> \d\+.*' - \ .. 'catch /fail/.*' - \ .. ' JUMP -> \d\+.*' - \ .. ' PUSH v:exception.*' - \ .. ' PUSHS "fail".*' - \ .. ' COMPARESTRING =\~.*' - \ .. ' JUMP_IF_FALSE -> \d\+.*' - \ .. ' CATCH.*' - \ .. 'finally.*' - \ .. ' PUSHS "end".*' - \ .. 'endtry.*' - \ .. ' ENDTRY.*' - \, res) -enddef - -def s:ScriptFuncNew() - let ll = [1, "two", 333] - let dd = #{one: 1, two: "val"} -enddef - -def Test_disassembleNew() - let res = execute('disass s:ScriptFuncNew') - assert_match('\d*_ScriptFuncNew.*' - \ .. 'let ll = \[1, "two", 333].*' - \ .. 'PUSHNR 1.*' - \ .. 'PUSHS "two".*' - \ .. 'PUSHNR 333.*' - \ .. 'NEWLIST size 3.*' - \ .. 'let dd = #{one: 1, two: "val"}.*' - \ .. 'PUSHS "one".*' - \ .. 'PUSHNR 1.*' - \ .. 'PUSHS "two".*' - \ .. 'PUSHS "val".*' - \ .. 'NEWDICT size 2.*' - \, res) -enddef - -def FuncWithArg(arg) - echo arg -enddef - -func UserFunc() - echo 'nothing' -endfunc - -func UserFuncWithArg(arg) - echo a:arg -endfunc - -def s:ScriptFuncCall(): string - changenr() - char2nr("abc") - Test_disassembleNew() - FuncWithArg(343) - UserFunc() - UserFuncWithArg("foo") - let FuncRef = function("UserFunc") - FuncRef() - let FuncRefWithArg = function("UserFuncWithArg") - FuncRefWithArg("bar") - return "yes" -enddef - -def Test_disassembleCall() - let res = execute('disass s:ScriptFuncCall') - assert_match('\d*_ScriptFuncCall.*' - \ .. 'changenr().*' - \ .. ' BCALL changenr(argc 0).*' - \ .. 'char2nr("abc").*' - \ .. ' PUSHS "abc".*' - \ .. ' BCALL char2nr(argc 1).*' - \ .. 'Test_disassembleNew().*' - \ .. ' DCALL Test_disassembleNew(argc 0).*' - \ .. 'FuncWithArg(343).*' - \ .. ' PUSHNR 343.*' - \ .. ' DCALL FuncWithArg(argc 1).*' - \ .. 'UserFunc().*' - \ .. ' UCALL UserFunc(argc 0).*' - \ .. 'UserFuncWithArg("foo").*' - \ .. ' PUSHS "foo".*' - \ .. ' UCALL UserFuncWithArg(argc 1).*' - \ .. 'let FuncRef = function("UserFunc").*' - \ .. 'FuncRef().*' - \ .. ' LOAD $\d.*' - \ .. ' PCALL (argc 0).*' - \ .. 'let FuncRefWithArg = function("UserFuncWithArg").*' - \ .. 'FuncRefWithArg("bar").*' - \ .. ' PUSHS "bar".*' - \ .. ' LOAD $\d.*' - \ .. ' PCALL (argc 1).*' - \ .. 'return "yes".*' - \ .. ' PUSHS "yes".*' - \ .. ' RETURN.*' - \, res) -enddef - " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -573,8 +573,6 @@ get_func_tv( return ret; } -#define FLEN_FIXED 40 - /* * Return TRUE if "p" starts with "" or "s:". * Only works if eval_fname_script() returned non-zero for "p"! @@ -591,7 +589,7 @@ eval_fname_sid(char_u *p) * Use "fname_buf[FLEN_FIXED + 1]" when it fits, otherwise allocate memory * (slow). */ - static char_u * + char_u * fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) { int llen; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -743,6 +743,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 223, +/**/ 222, /**/ 221, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2574,6 +2574,9 @@ typedef enum { #define FCERR_DELETED 7 #define FCERR_NOTMETHOD 8 // function cannot be used as a method +// fixed buffer length for fname_trans_sid() +#define FLEN_FIXED 40 + // flags for find_name_end() #define FNE_INCL_BR 1 // include [] in name #define FNE_CHECK_START 2 // check name starts with valid character diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1686,49 +1686,60 @@ compile_call(char_u **arg, size_t varlen char_u *p; int argcount = argcount_init; char_u namebuf[100]; + char_u fname_buf[FLEN_FIXED + 1]; + char_u *tofree = NULL; + int error = FCERR_NONE; ufunc_T *ufunc; + int res = FAIL; if (varlen >= sizeof(namebuf)) { semsg(_("E1011: name too long: %s"), name); return FAIL; } - vim_strncpy(namebuf, name, varlen); + vim_strncpy(namebuf, *arg, varlen); + name = fname_trans_sid(namebuf, fname_buf, &tofree, &error); *arg = skipwhite(*arg + varlen + 1); if (compile_arguments(arg, cctx, &argcount) == FAIL) - return FAIL; - - if (ASCII_ISLOWER(*name)) + goto theend; + + if (ASCII_ISLOWER(*name) && name[1] != ':') { int idx; // builtin function - idx = find_internal_func(namebuf); + idx = find_internal_func(name); if (idx >= 0) - return generate_BCALL(cctx, idx, argcount); + { + res = generate_BCALL(cctx, idx, argcount); + goto theend; + } semsg(_(e_unknownfunc), namebuf); } - // User defined function or variable must start with upper case. - if (!ASCII_ISUPPER(*name)) - { - semsg(_("E1012: Invalid function name: %s"), namebuf); - return FAIL; - } - // If we can find the function by name generate the right call. - ufunc = find_func(namebuf, cctx); + ufunc = find_func(name, cctx); if (ufunc != NULL) - return generate_CALL(cctx, ufunc, argcount); + { + res = generate_CALL(cctx, ufunc, argcount); + goto theend; + } // If the name is a variable, load it and use PCALL. p = namebuf; if (compile_load(&p, namebuf + varlen, cctx, FALSE) == OK) - return generate_PCALL(cctx, argcount, FALSE); + { + res = generate_PCALL(cctx, argcount, FALSE); + goto theend; + } // The function may be defined only later. Need to figure out at runtime. - return generate_UCALL(cctx, namebuf, argcount); + res = generate_UCALL(cctx, name, argcount); + +theend: + vim_free(tofree); + return res; } // like NAMESPACE_CHAR but with 'a' and 'l'.