Skip to content

Commit f5fec05

Browse files
committed
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.
1 parent 88b79cb commit f5fec05

File tree

4 files changed

+35
-48
lines changed

4 files changed

+35
-48
lines changed

src/testdir/test_vim9_func.vim

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,19 @@ def TestCompilingError()
2929
enddef
3030
defcompile
3131
END
32-
writefile(lines, 'XTest_compile_error')
32+
writefile(lines, 'XTest_compile_error', 'D')
3333
var buf = g:RunVimInTerminal('-S XTest_compile_error',
3434
{rows: 10, wait_for_ruler: 0})
3535
g:WaitForAssert(() => assert_match('Error detected while compiling command line.*Fails.*Variable not found: nothing',
3636
g:Term_getlines(buf, range(1, 9))))
3737

3838
# clean up
3939
g:StopVimInTerminal(buf)
40-
delete('XTest_compile_error')
4140
enddef
4241

4342
def TestCompilingErrorInTry()
4443
var dir = 'Xcompdir/autoload'
45-
mkdir(dir, 'p')
44+
mkdir(dir, 'pR')
4645

4746
var lines =<< trim END
4847
vim9script
@@ -62,16 +61,14 @@ def TestCompilingErrorInTry()
6261
endtry
6362
END
6463
lines[1] = 'set rtp=' .. getcwd() .. '/Xcompdir'
65-
writefile(lines, 'XTest_compile_error')
64+
writefile(lines, 'XTest_compile_error', 'D')
6665

6766
var buf = g:RunVimInTerminal('-S XTest_compile_error', {rows: 10, wait_for_ruler: 0})
6867
g:WaitForAssert(() => assert_match('Error detected while compiling command line.*function script#OnlyCompiled.*Invalid command: invalid',
6968
g:Term_getlines(buf, range(1, 9))))
7069

7170
# clean up
7271
g:StopVimInTerminal(buf)
73-
delete('XTest_compile_error')
74-
delete('Xcompdir', 'rf')
7572
enddef
7673

7774
def Test_comment_error()
@@ -171,7 +168,7 @@ enddef
171168

172169
def Test_autoload_name_mismatch()
173170
var dir = 'Xnamedir/autoload'
174-
mkdir(dir, 'p')
171+
mkdir(dir, 'pR')
175172

176173
var lines =<< trim END
177174
vim9script
@@ -190,12 +187,11 @@ def Test_autoload_name_mismatch()
190187
v9.CheckScriptFailure(lines, 'E117:', 1)
191188

192189
&rtp = save_rtp
193-
delete('Xnamedir', 'rf')
194190
enddef
195191

196192
def Test_autoload_names()
197193
var dir = 'Xandir/autoload'
198-
mkdir(dir, 'p')
194+
mkdir(dir, 'pR')
199195

200196
var lines =<< trim END
201197
func foobar#function()
@@ -218,12 +214,11 @@ def Test_autoload_names()
218214
v9.CheckDefAndScriptSuccess(lines)
219215

220216
&rtp = save_rtp
221-
delete('Xandir', 'rf')
222217
enddef
223218

224219
def Test_autoload_error_in_script()
225220
var dir = 'Xaedir/autoload'
226-
mkdir(dir, 'p')
221+
mkdir(dir, 'pR')
227222

228223
var lines =<< trim END
229224
func scripterror#function()
@@ -264,7 +259,6 @@ def Test_autoload_error_in_script()
264259
assert_equal('yes', g:called_function)
265260

266261
&rtp = save_rtp
267-
delete('Xaedir', 'rf')
268262
enddef
269263

270264
def s:CallRecursive(n: number): number
@@ -1298,7 +1292,7 @@ def Test_call_wrong_args()
12981292
enddef
12991293
defcompile
13001294
END
1301-
writefile(lines, 'Xscript')
1295+
writefile(lines, 'Xscript', 'D')
13021296
didCatch = false
13031297
try
13041298
source Xscript
@@ -1308,8 +1302,6 @@ def Test_call_wrong_args()
13081302
didCatch = true
13091303
endtry
13101304
assert_true(didCatch)
1311-
1312-
delete('Xscript')
13131305
enddef
13141306

13151307
def Test_call_funcref_wrong_args()
@@ -2306,9 +2298,8 @@ def Test_vim9script_call()
23062298
'morelines',
23072299
name)
23082300
END
2309-
writefile(lines, 'Xcall.vim')
2301+
writefile(lines, 'Xcall.vim', 'D')
23102302
source Xcall.vim
2311-
delete('Xcall.vim')
23122303
enddef
23132304

23142305
def Test_vim9script_call_fail_decl()
@@ -2343,9 +2334,8 @@ def Test_vim9script_call_fail_const()
23432334
enddef
23442335
defcompile
23452336
END
2346-
writefile(lines, 'Xcall_const.vim')
2337+
writefile(lines, 'Xcall_const.vim', 'D')
23472338
assert_fails('source Xcall_const.vim', 'E46:', '', 1, 'MyFunc')
2348-
delete('Xcall_const.vim')
23492339

23502340
lines =<< trim END
23512341
const g:Aconst = 77
@@ -2385,11 +2375,9 @@ def Test_delfunc()
23852375
delfunc g:GoneSoon
23862376
CallGoneSoon()
23872377
END
2388-
writefile(lines, 'XToDelFunc')
2378+
writefile(lines, 'XToDelFunc', 'D')
23892379
assert_fails('so XToDelFunc', 'E933:', '', 1, 'CallGoneSoon')
23902380
assert_fails('so XToDelFunc', 'E933:', '', 1, 'CallGoneSoon')
2391-
2392-
delete('XToDelFunc')
23932381
enddef
23942382

23952383
func Test_free_dict_while_in_funcstack()
@@ -2454,10 +2442,8 @@ def Test_vim9script_func()
24542442
endfunc
24552443
Func('text')
24562444
END
2457-
writefile(lines, 'XVim9Func')
2445+
writefile(lines, 'XVim9Func', 'D')
24582446
so XVim9Func
2459-
2460-
delete('XVim9Func')
24612447
enddef
24622448

24632449
let s:funcResult = 0
@@ -2700,7 +2686,7 @@ def Test_error_reporting()
27002686
enddef
27012687
defcompile
27022688
END
2703-
writefile(lines, 'Xdef')
2689+
writefile(lines, 'Xdef', 'D')
27042690
try
27052691
source Xdef
27062692
assert_report('should have failed')
@@ -2749,8 +2735,6 @@ def Test_error_reporting()
27492735
v:throwpoint->assert_match('_Func, line 3$')
27502736
endtry
27512737
delfunc! g:Func
2752-
2753-
delete('Xdef')
27542738
enddef
27552739

27562740
def Test_deleted_function()
@@ -3421,14 +3405,12 @@ def s:TreeWalk(dir: string): list<any>
34213405
enddef
34223406

34233407
def Test_closure_in_map()
3424-
mkdir('XclosureDir/tdir', 'p')
3408+
mkdir('XclosureDir/tdir', 'pR')
34253409
writefile(['111'], 'XclosureDir/file1')
34263410
writefile(['222'], 'XclosureDir/file2')
34273411
writefile(['333'], 'XclosureDir/tdir/file3')
34283412

34293413
TreeWalk('XclosureDir')->assert_equal(['file1', 'file2', {tdir: ['file3']}])
3430-
3431-
delete('XclosureDir', 'rf')
34323414
enddef
34333415

34343416
def Test_invalid_function_name()
@@ -3532,12 +3514,11 @@ func Test_partial_call_fails()
35323514
var it = Iter(l)
35333515
echo it.__next__()
35343516
END
3535-
call writefile(lines, 'XpartialCall')
3517+
call writefile(lines, 'XpartialCall', 'D')
35363518
try
35373519
source XpartialCall
35383520
catch /E1248:/
35393521
endtry
3540-
call delete('XpartialCall')
35413522
endfunc
35423523

35433524
def Test_cmd_modifier()
@@ -3631,9 +3612,9 @@ def Test_did_emsg_reset()
36313612
nno <F3> <cmd>call <sid>Func()<cr>
36323613
feedkeys("\<F3>\e", 'xt')
36333614
END
3634-
writefile(lines, 'XemsgReset')
3615+
writefile(lines, 'XemsgReset', 'D')
36353616
assert_fails('so XemsgReset', ['E684:', 'E684:'], lines, 2)
3636-
delete('XemsgReset')
3617+
36373618
nunmap <F3>
36383619
au! BufWinLeave
36393620
enddef
@@ -3698,7 +3679,7 @@ def Test_cmdmod_silent_restored()
36983679
END
36993680
# can't use CheckScriptFailure, it ignores the :silent!
37003681
var fname = 'Xdefsilent'
3701-
writefile(lines, fname)
3682+
writefile(lines, fname, 'D')
37023683
var caught = 'no'
37033684
try
37043685
exe 'source ' .. fname
@@ -3707,7 +3688,6 @@ def Test_cmdmod_silent_restored()
37073688
assert_match('Func, line 4', v:throwpoint)
37083689
endtry
37093690
assert_equal('yes', caught)
3710-
delete(fname)
37113691
enddef
37123692

37133693
def Test_cmdmod_silent_nested()
@@ -3809,15 +3789,14 @@ def Run_Test_opfunc_error()
38093789
feedkeys('g@l', 'n')
38103790
feedkeys('llll')
38113791
END
3812-
call writefile(lines, 'XTest_opfunc_error')
3792+
call writefile(lines, 'XTest_opfunc_error', 'D')
38133793

38143794
var buf = g:RunVimInTerminal('-S XTest_opfunc_error', {rows: 6, wait_for_ruler: 0})
38153795
g:WaitForAssert(() => assert_match('Press ENTER', term_getline(buf, 6)))
38163796
g:WaitForAssert(() => assert_match('E684: List index out of range: 0', term_getline(buf, 5)))
38173797

38183798
# clean up
38193799
g:StopVimInTerminal(buf)
3820-
delete('XTest_opfunc_error')
38213800
enddef
38223801

38233802
" this was crashing on exit
@@ -3890,14 +3869,13 @@ def Test_script_local_other_script()
38903869
function s:close_cb(...)
38913870
endfunction
38923871
END
3893-
lines->writefile('Xlegacy.vim')
3872+
lines->writefile('Xlegacy.vim', 'D')
38943873
source Xlegacy.vim
38953874
g:LegacyJob()
38963875
g:LegacyJob()
38973876
g:LegacyJob()
38983877

38993878
delfunc g:LegacyJob
3900-
delete('Xlegacy.vim')
39013879
enddef
39023880

39033881
def Test_check_func_arg_types()

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,8 @@ static char *(features[]) =
703703

704704
static int included_patches[] =
705705
{ /* Add new patch number below this line */
706+
/**/
707+
440,
706708
/**/
707709
439,
708710
/**/

src/vim9execute.c

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -932,11 +932,17 @@ defer_command(int var_idx, int argcount, ectx_T *ectx)
932932
return FAIL;
933933

934934
func_tv = STACK_TV_BOT(-argcount - 1);
935-
// TODO: check type is a funcref
935+
if (func_tv->v_type != VAR_FUNC && func_tv->v_type != VAR_PARTIAL)
936+
{
937+
semsg(_(e_expected_str_but_got_str),
938+
"function or partial",
939+
vartype_name(func_tv->v_type));
940+
return FAIL;
941+
}
936942
list_set_item(l, 0, func_tv);
937943

938-
for (i = 1; i <= argcount; ++i)
939-
list_set_item(l, i, STACK_TV_BOT(-argcount + i - 1));
944+
for (i = 0; i < argcount; ++i)
945+
list_set_item(l, i + 1, STACK_TV_BOT(-argcount + i));
940946
ectx->ec_stack.ga_len -= argcount + 1;
941947
return OK;
942948
}
@@ -962,7 +968,7 @@ add_defer_function(char_u *name, int argcount, typval_T *argvars)
962968
return FAIL;
963969
}
964970

965-
l = add_defer_item(dfunc->df_defer_var_idx - 1, 1, current_ectx);
971+
l = add_defer_item(dfunc->df_defer_var_idx - 1, argcount, current_ectx);
966972
if (l == NULL)
967973
{
968974
vim_free(name);

src/vim9expr.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -833,10 +833,11 @@ compile_call(
833833
}
834834
}
835835

836-
if (STRCMP(name, "writefile") == 0 && argcount > 2)
836+
if ((STRCMP(name, "writefile") == 0 && argcount > 2)
837+
|| (STRCMP(name, "mkdir") == 0 && argcount > 1))
837838
{
838-
// May have the "D" flag, reserve a variable for a deferred
839-
// function call.
839+
// May have the "D" or "R" flag, reserve a variable for a
840+
// deferred function call.
840841
if (get_defer_var_idx(cctx) == 0)
841842
idx = -1;
842843
}

0 commit comments

Comments
 (0)