# HG changeset patch # User Bram Moolenaar # Date 1585689304 -7200 # Node ID 846fbbacce3a4535b95d9557bcc580be12f327fa # Parent e517915a786d1807b3f62160f60d57196569eb50 patch 8.2.0487: Vim9: compiling not sufficiently tested Commit: https://github.com/vim/vim/commit/bd5da371aafe5a2207065643502f4d1ff6b286c7 Author: Bram Moolenaar Date: Tue Mar 31 23:13:10 2020 +0200 patch 8.2.0487: Vim9: compiling not sufficiently tested Problem: Vim9: compiling not sufficiently tested. Solution: Add more tests. Fix bug with PCALL. diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -224,6 +224,31 @@ def Test_disassemble_call() enddef +def EchoArg(arg: string): string + return arg +enddef +def RefThis(): func + return function('EchoArg') +enddef +def s:ScriptPCall() + RefThis()("text") +enddef + +def Test_disassemble_pcall() + let res = execute('disass s:ScriptPCall') + assert_match('\d\+_ScriptPCall.*' + \ .. 'RefThis()("text").*' + \ .. '\d DCALL RefThis(argc 0).*' + \ .. '\d PUSHS "text".*' + \ .. '\d PCALL top (argc 1).*' + \ .. '\d PCALL end.*' + \ .. '\d DROP.*' + \ .. '\d PUSHNR 0.*' + \ .. '\d RETURN.*' + \, res) +enddef + + def FuncWithForwardCall(): string return DefinedLater("yes") enddef 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 @@ -260,9 +260,10 @@ func TakesOneArg(arg) echo a:arg endfunc -def Test_call_wrong_arg_count() +def Test_call_wrong_args() call CheckDefFailure(['TakesOneArg()'], 'E119:') call CheckDefFailure(['TakesOneArg(11, 22)'], 'E118:') + call CheckDefFailure(['bufnr(xxx)'], 'E1001:') enddef " Default arg and varargs @@ -1029,6 +1030,14 @@ def Test_while_loop() assert_equal('1_3_', result) enddef +def Test_for_loop_fails() + call CheckDefFailure(['for # in range(5)'], 'E690:') + call CheckDefFailure(['for i In range(5)'], 'E690:') + call CheckDefFailure(['let x = 5', 'for x in range(5)'], 'E1023:') + call CheckScriptFailure(['def Func(arg)', 'for arg in range(5)', 'enddef'], 'E1006:') + call CheckDefFailure(['for i in "text"'], 'E1024:') +enddef + def Test_interrupt_loop() let caught = false let x = 0 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -739,6 +739,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 487, +/**/ 486, /**/ 485, diff --git a/src/vim9.h b/src/vim9.h --- a/src/vim9.h +++ b/src/vim9.h @@ -57,6 +57,7 @@ typedef enum { ISN_DCALL, // call def function isn_arg.dfunc ISN_UCALL, // call user function or funcref/partial isn_arg.ufunc ISN_PCALL, // call partial, use isn_arg.pfunc + ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set ISN_RETURN, // return, result is on top of stack ISN_FUNCREF, // push a function ref to dfunc isn_arg.number @@ -256,7 +257,7 @@ struct dfunc_S { // Functions defined with :def are stored in this growarray. // They are never removed, so that they can be found by index. // Deleted functions have the df_deleted flag set. -garray_T def_functions = {0, 0, sizeof(dfunc_T), 50, NULL}; +garray_T def_functions = {0, 0, sizeof(dfunc_T), 200, NULL}; #else extern garray_T def_functions; #endif diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1196,6 +1196,11 @@ generate_PCALL(cctx_T *cctx, int argcoun // drop the funcref/partial, get back the return value ((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_any; + // If partial is above the arguments it must be cleared and replaced with + // the return value. + if (at_top && generate_instr(cctx, ISN_PCALL_END) == NULL) + return FAIL; + return OK; } @@ -5200,7 +5205,7 @@ compile_def_function(ufunc_T *ufunc, int p = (*ea.cmd == '&' || *ea.cmd == '$' || *ea.cmd == '@') ? ea.cmd + 1 : ea.cmd; p = to_name_end(p, TRUE); - if ((p > ea.cmd && *p != NUL) || *p == '(') + if (p > ea.cmd && *p != NUL) { int oplen; int heredoc; @@ -5538,6 +5543,7 @@ delete_instr(isn_T *isn) case ISN_OPFLOAT: case ISN_OPANY: case ISN_PCALL: + case ISN_PCALL_END: case ISN_PUSHF: case ISN_PUSHNR: case ISN_PUSHBOOL: diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -345,7 +345,7 @@ call_by_name(char_u *name, int argcount, static int call_partial(typval_T *tv, int argcount, ectx_T *ectx) { - char_u *name; + char_u *name = NULL; int called_emsg_before = called_emsg; if (tv->v_type == VAR_PARTIAL) @@ -356,9 +356,9 @@ call_partial(typval_T *tv, int argcount, return call_ufunc(pt->pt_func, argcount, ectx, NULL); name = pt->pt_name; } - else + else if (tv->v_type == VAR_FUNC) name = tv->vval.v_string; - if (call_by_name(name, argcount, ectx, NULL) == FAIL) + if (name == NULL || call_by_name(name, argcount, ectx, NULL) == FAIL) { if (called_emsg == called_emsg_before) semsg(_(e_unknownfunc), name); @@ -421,7 +421,6 @@ call_def_function( typval_T *tv; int idx; int ret = FAIL; - dfunc_T *dfunc; int defcount = ufunc->uf_args.ga_len - argc; // Get pointer to item in the stack. @@ -467,13 +466,17 @@ call_def_function( ++ectx.ec_stack.ga_len; } - // Reserve space for local variables. - dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; - for (idx = 0; idx < dfunc->df_varcount; ++idx) - STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN; - ectx.ec_stack.ga_len += dfunc->df_varcount; + { + // Reserve space for local variables. + dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + + ufunc->uf_dfunc_idx; - ectx.ec_instr = dfunc->df_instr; + for (idx = 0; idx < dfunc->df_varcount; ++idx) + STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN; + ectx.ec_stack.ga_len += dfunc->df_varcount; + + ectx.ec_instr = dfunc->df_instr; + } // Decide where to start execution, handles optional arguments. init_instr_idx(ufunc, argc, &ectx); @@ -1022,16 +1025,16 @@ call_def_function( clear_tv(&partial); if (r == FAIL) goto failed; + } + break; - if (pfunc->cpf_top) - { - // Get the funcref from the stack, overwrite with the - // return value. - clear_tv(tv); - --ectx.ec_stack.ga_len; - *STACK_TV_BOT(-1) = *STACK_TV_BOT(0); - } - } + case ISN_PCALL_END: + // PCALL finished, arguments have been consumed and replaced by + // the return value. Now clear the funcref from the stack, + // and move the return value in its place. + --ectx.ec_stack.ga_len; + clear_tv(STACK_TV_BOT(-1)); + *STACK_TV_BOT(-1) = *STACK_TV_BOT(0); break; // call a user defined function or funcref/partial @@ -1078,6 +1081,7 @@ call_def_function( case ISN_FUNCREF: { partial_T *pt = NULL; + dfunc_T *dfunc; pt = ALLOC_CLEAR_ONE(partial_T); if (pt == NULL) @@ -2005,6 +2009,9 @@ ex_disassemble(exarg_T *eap) cpfunc->cpf_top ? " top" : "", cpfunc->cpf_argcount); } break; + case ISN_PCALL_END: + smsg("%4d PCALL end", current); + break; case ISN_RETURN: smsg("%4d RETURN", current); break;