# HG changeset patch # User Bram Moolenaar # Date 1662894007 -7200 # Node ID a970b48c25a34cd148e948d71ebdc6d9a1de5683 # Parent 9f6908aa16a7f8a62abb7e704780d4480a91d789 patch 9.0.0440: crash when using mkdir() with "R" flag in compiled function Commit: https://github.com/vim/vim/commit/f5fec05c7fd0df4c934a838e82882e601dc920cb Author: Bram Moolenaar Date: Sun Sep 11 11:49:22 2022 +0100 patch 9.0.0440: crash when using mkdir() with "R" flag in compiled function Problem: Crash when using mkdir() with "R" flag in compiled function. Solution: Reserve a variable for deferred function calls. Handle more than one argument. diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -29,7 +29,7 @@ def TestCompilingError() enddef defcompile END - writefile(lines, 'XTest_compile_error') + writefile(lines, 'XTest_compile_error', 'D') var buf = g:RunVimInTerminal('-S XTest_compile_error', {rows: 10, wait_for_ruler: 0}) g:WaitForAssert(() => assert_match('Error detected while compiling command line.*Fails.*Variable not found: nothing', @@ -37,12 +37,11 @@ def TestCompilingError() # clean up g:StopVimInTerminal(buf) - delete('XTest_compile_error') enddef def TestCompilingErrorInTry() var dir = 'Xcompdir/autoload' - mkdir(dir, 'p') + mkdir(dir, 'pR') var lines =<< trim END vim9script @@ -62,7 +61,7 @@ def TestCompilingErrorInTry() endtry END lines[1] = 'set rtp=' .. getcwd() .. '/Xcompdir' - writefile(lines, 'XTest_compile_error') + writefile(lines, 'XTest_compile_error', 'D') var buf = g:RunVimInTerminal('-S XTest_compile_error', {rows: 10, wait_for_ruler: 0}) g:WaitForAssert(() => assert_match('Error detected while compiling command line.*function script#OnlyCompiled.*Invalid command: invalid', @@ -70,8 +69,6 @@ def TestCompilingErrorInTry() # clean up g:StopVimInTerminal(buf) - delete('XTest_compile_error') - delete('Xcompdir', 'rf') enddef def Test_comment_error() @@ -171,7 +168,7 @@ enddef def Test_autoload_name_mismatch() var dir = 'Xnamedir/autoload' - mkdir(dir, 'p') + mkdir(dir, 'pR') var lines =<< trim END vim9script @@ -190,12 +187,11 @@ def Test_autoload_name_mismatch() v9.CheckScriptFailure(lines, 'E117:', 1) &rtp = save_rtp - delete('Xnamedir', 'rf') enddef def Test_autoload_names() var dir = 'Xandir/autoload' - mkdir(dir, 'p') + mkdir(dir, 'pR') var lines =<< trim END func foobar#function() @@ -218,12 +214,11 @@ def Test_autoload_names() v9.CheckDefAndScriptSuccess(lines) &rtp = save_rtp - delete('Xandir', 'rf') enddef def Test_autoload_error_in_script() var dir = 'Xaedir/autoload' - mkdir(dir, 'p') + mkdir(dir, 'pR') var lines =<< trim END func scripterror#function() @@ -264,7 +259,6 @@ def Test_autoload_error_in_script() assert_equal('yes', g:called_function) &rtp = save_rtp - delete('Xaedir', 'rf') enddef def s:CallRecursive(n: number): number @@ -1298,7 +1292,7 @@ def Test_call_wrong_args() enddef defcompile END - writefile(lines, 'Xscript') + writefile(lines, 'Xscript', 'D') didCatch = false try source Xscript @@ -1308,8 +1302,6 @@ def Test_call_wrong_args() didCatch = true endtry assert_true(didCatch) - - delete('Xscript') enddef def Test_call_funcref_wrong_args() @@ -2306,9 +2298,8 @@ def Test_vim9script_call() 'morelines', name) END - writefile(lines, 'Xcall.vim') + writefile(lines, 'Xcall.vim', 'D') source Xcall.vim - delete('Xcall.vim') enddef def Test_vim9script_call_fail_decl() @@ -2343,9 +2334,8 @@ def Test_vim9script_call_fail_const() enddef defcompile END - writefile(lines, 'Xcall_const.vim') + writefile(lines, 'Xcall_const.vim', 'D') assert_fails('source Xcall_const.vim', 'E46:', '', 1, 'MyFunc') - delete('Xcall_const.vim') lines =<< trim END const g:Aconst = 77 @@ -2385,11 +2375,9 @@ def Test_delfunc() delfunc g:GoneSoon CallGoneSoon() END - writefile(lines, 'XToDelFunc') + writefile(lines, 'XToDelFunc', 'D') assert_fails('so XToDelFunc', 'E933:', '', 1, 'CallGoneSoon') assert_fails('so XToDelFunc', 'E933:', '', 1, 'CallGoneSoon') - - delete('XToDelFunc') enddef func Test_free_dict_while_in_funcstack() @@ -2454,10 +2442,8 @@ def Test_vim9script_func() endfunc Func('text') END - writefile(lines, 'XVim9Func') + writefile(lines, 'XVim9Func', 'D') so XVim9Func - - delete('XVim9Func') enddef let s:funcResult = 0 @@ -2700,7 +2686,7 @@ def Test_error_reporting() enddef defcompile END - writefile(lines, 'Xdef') + writefile(lines, 'Xdef', 'D') try source Xdef assert_report('should have failed') @@ -2749,8 +2735,6 @@ def Test_error_reporting() v:throwpoint->assert_match('_Func, line 3$') endtry delfunc! g:Func - - delete('Xdef') enddef def Test_deleted_function() @@ -3421,14 +3405,12 @@ def s:TreeWalk(dir: string): list enddef def Test_closure_in_map() - mkdir('XclosureDir/tdir', 'p') + mkdir('XclosureDir/tdir', 'pR') writefile(['111'], 'XclosureDir/file1') writefile(['222'], 'XclosureDir/file2') writefile(['333'], 'XclosureDir/tdir/file3') TreeWalk('XclosureDir')->assert_equal(['file1', 'file2', {tdir: ['file3']}]) - - delete('XclosureDir', 'rf') enddef def Test_invalid_function_name() @@ -3532,12 +3514,11 @@ func Test_partial_call_fails() var it = Iter(l) echo it.__next__() END - call writefile(lines, 'XpartialCall') + call writefile(lines, 'XpartialCall', 'D') try source XpartialCall catch /E1248:/ endtry - call delete('XpartialCall') endfunc def Test_cmd_modifier() @@ -3631,9 +3612,9 @@ def Test_did_emsg_reset() nno call Func() feedkeys("\\e", 'xt') END - writefile(lines, 'XemsgReset') + writefile(lines, 'XemsgReset', 'D') assert_fails('so XemsgReset', ['E684:', 'E684:'], lines, 2) - delete('XemsgReset') + nunmap au! BufWinLeave enddef @@ -3698,7 +3679,7 @@ def Test_cmdmod_silent_restored() END # can't use CheckScriptFailure, it ignores the :silent! var fname = 'Xdefsilent' - writefile(lines, fname) + writefile(lines, fname, 'D') var caught = 'no' try exe 'source ' .. fname @@ -3707,7 +3688,6 @@ def Test_cmdmod_silent_restored() assert_match('Func, line 4', v:throwpoint) endtry assert_equal('yes', caught) - delete(fname) enddef def Test_cmdmod_silent_nested() @@ -3809,7 +3789,7 @@ def Run_Test_opfunc_error() feedkeys('g@l', 'n') feedkeys('llll') END - call writefile(lines, 'XTest_opfunc_error') + call writefile(lines, 'XTest_opfunc_error', 'D') var buf = g:RunVimInTerminal('-S XTest_opfunc_error', {rows: 6, wait_for_ruler: 0}) g:WaitForAssert(() => assert_match('Press ENTER', term_getline(buf, 6))) @@ -3817,7 +3797,6 @@ def Run_Test_opfunc_error() # clean up g:StopVimInTerminal(buf) - delete('XTest_opfunc_error') enddef " this was crashing on exit @@ -3890,14 +3869,13 @@ def Test_script_local_other_script() function s:close_cb(...) endfunction END - lines->writefile('Xlegacy.vim') + lines->writefile('Xlegacy.vim', 'D') source Xlegacy.vim g:LegacyJob() g:LegacyJob() g:LegacyJob() delfunc g:LegacyJob - delete('Xlegacy.vim') enddef def Test_check_func_arg_types() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 440, +/**/ 439, /**/ 438, diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -932,11 +932,17 @@ defer_command(int var_idx, int argcount, return FAIL; func_tv = STACK_TV_BOT(-argcount - 1); - // TODO: check type is a funcref + if (func_tv->v_type != VAR_FUNC && func_tv->v_type != VAR_PARTIAL) + { + semsg(_(e_expected_str_but_got_str), + "function or partial", + vartype_name(func_tv->v_type)); + return FAIL; + } list_set_item(l, 0, func_tv); - for (i = 1; i <= argcount; ++i) - list_set_item(l, i, STACK_TV_BOT(-argcount + i - 1)); + for (i = 0; i < argcount; ++i) + list_set_item(l, i + 1, STACK_TV_BOT(-argcount + i)); ectx->ec_stack.ga_len -= argcount + 1; return OK; } @@ -962,7 +968,7 @@ add_defer_function(char_u *name, int arg return FAIL; } - l = add_defer_item(dfunc->df_defer_var_idx - 1, 1, current_ectx); + l = add_defer_item(dfunc->df_defer_var_idx - 1, argcount, current_ectx); if (l == NULL) { vim_free(name); diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -833,10 +833,11 @@ compile_call( } } - if (STRCMP(name, "writefile") == 0 && argcount > 2) + if ((STRCMP(name, "writefile") == 0 && argcount > 2) + || (STRCMP(name, "mkdir") == 0 && argcount > 1)) { - // May have the "D" flag, reserve a variable for a deferred - // function call. + // May have the "D" or "R" flag, reserve a variable for a + // deferred function call. if (get_defer_var_idx(cctx) == 0) idx = -1; }