Mercurial > vim
view src/testdir/test_source.vim @ 33815:08f9e1eac4cf v9.0.2123
patch 9.0.2123: Problem with initializing the length of range() lists
Commit: https://github.com/vim/vim/commit/df63da98d8dc284b1c76cfe1b17fa0acbd6094d8
Author: Christian Brabandt <cb@256bit.org>
Date: Thu Nov 23 20:14:28 2023 +0100
patch 9.0.2123: Problem with initializing the length of range() lists
Problem: Problem with initializing the length of range() lists
Solution: Set length explicitly when it shouldn't contain any items
range() may cause a wrong calculation of list length, which may later
then cause a segfault in list_find(). This is usually not a problem,
because range_list_materialize() calculates the length, when it
materializes the list.
In addition, in list_find() when the length of the range was wrongly
initialized, it may seem to be valid, so the check for list index
out-of-bounds will not be true, because it is called before the list is
actually materialized. And so we may eventually try to access a null
pointer, causing a segfault.
So this patch does 3 things:
- In f_range(), when we know that the list should be empty, explicitly
set the list->lv_len value to zero. This should happen, when
start is larger than end (in case the stride is positive) or
end is larger than start when the stride is negative.
This should fix the underlying issue properly. However,
- as a safety measure, let's check that the requested index is not
out of range one more time, after the list has been materialized
and return NULL in case it suddenly is.
- add a few more tests to verify the behaviour.
fixes: #13557
closes: #13563
Co-authored-by: Tim Pope <tpope@github.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Thu, 23 Nov 2023 20:30:07 +0100 |
parents | ae10b91ac6b3 |
children |
line wrap: on
line source
" Tests for the :source command. source check.vim source view_util.vim func Test_source_autocmd() call writefile([ \ 'let did_source = 1', \ ], 'Xsourced', 'D') au SourcePre *source* let did_source_pre = 1 au SourcePost *source* let did_source_post = 1 source Xsourced call assert_equal(g:did_source, 1) call assert_equal(g:did_source_pre, 1) call assert_equal(g:did_source_post, 1) au! SourcePre au! SourcePost unlet g:did_source unlet g:did_source_pre unlet g:did_source_post endfunc func Test_source_cmd() au SourceCmd *source* let did_source = expand('<afile>') au SourcePre *source* let did_source_pre = 2 au SourcePost *source* let did_source_post = 2 source Xsourced call assert_equal(g:did_source, 'Xsourced') call assert_false(exists('g:did_source_pre')) call assert_equal(g:did_source_post, 2) au! SourceCmd au! SourcePre au! SourcePost endfunc func Test_source_sandbox() new call writefile(["Ohello\<Esc>"], 'Xsourcehello', 'D') source! Xsourcehello | echo call assert_equal('hello', getline(1)) call assert_fails('sandbox source! Xsourcehello', 'E48:') bwipe! endfunc " When deleting a file and immediately creating a new one the inode may be " recycled. Vim should not recognize it as the same script. func Test_different_script() call writefile(['let s:var = "asdf"'], 'XoneScript', 'D') source XoneScript call writefile(['let g:var = s:var'], 'XtwoScript', 'D') call assert_fails('source XtwoScript', 'E121:') endfunc " When sourcing a vim script, shebang should be ignored. func Test_source_ignore_shebang() call writefile(['#!./xyzabc', 'let g:val=369'], 'Xsisfile.vim', 'D') source Xsisfile.vim call assert_equal(g:val, 369) endfunc " Test for expanding <sfile> in an autocmd and for <slnum> and <sflnum> func Test_source_autocmd_sfile() let code =<< trim [CODE] let g:SfileName = '' augroup sfiletest au! autocmd User UserAutoCmd let g:Sfile = '<sfile>:t' augroup END doautocmd User UserAutoCmd let g:Slnum = expand('<slnum>') let g:Sflnum = expand('<sflnum>') augroup! sfiletest [CODE] call writefile(code, 'Xscript.vim', 'D') source Xscript.vim call assert_equal('Xscript.vim', g:Sfile) call assert_equal('7', g:Slnum) call assert_equal('8', g:Sflnum) endfunc func Test_source_error() call assert_fails('scriptencoding utf-8', 'E167:') call assert_fails('finish', 'E168:') call assert_fails('scriptversion 2', 'E984:') call assert_fails('source!', 'E471:') new call setline(1, ['', '', '', '']) call assert_fails('1,3source Xscript.vim', 'E481:') call assert_fails('1,3source! Xscript.vim', 'E481:') bw! endfunc " Test for sourcing a script recursively func Test_nested_script() CheckRunVimInTerminal call writefile([':source! Xscript.vim', ''], 'Xscript.vim', 'D') let buf = RunVimInTerminal('', {'rows': 6}) call term_wait(buf) call term_sendkeys(buf, ":set noruler\n") call term_sendkeys(buf, ":source! Xscript.vim\n") call term_wait(buf) call WaitForAssert({-> assert_match('E22: Scripts nested too deep\s*', term_getline(buf, 6))}) call StopVimInTerminal(buf) endfunc " Test for sourcing a script from the current buffer func Test_source_buffer() new " Source a simple script let lines =<< trim END let a = "Test" let b = 20 let c = [1.1] END call setline(1, lines) source call assert_equal(['Test', 20, [1.1]], [g:a, g:b, g:c]) " Source a range of lines in the current buffer %d _ let lines =<< trim END let a = 10 let a += 20 let a += 30 let a += 40 END call setline(1, lines) .source call assert_equal(10, g:a) 3source call assert_equal(40, g:a) 2,3source call assert_equal(90, g:a) " Make sure the script line number is correct when sourcing a range of " lines. %d _ let lines =<< trim END Line 1 Line 2 func Xtestfunc() return expand("<sflnum>") endfunc Line 3 Line 4 END call setline(1, lines) 3,5source call assert_equal('4', Xtestfunc()) delfunc Xtestfunc " Source a script with line continuation lines %d _ let lines =<< trim END let m = [ \ 1, \ 2, \ ] call add(m, 3) END call setline(1, lines) source call assert_equal([1, 2, 3], g:m) " Source a script with line continuation lines and a comment %d _ let lines =<< trim END let m = [ "\ first entry \ 'a', "\ second entry \ 'b', \ ] " third entry call add(m, 'c') END call setline(1, lines) source call assert_equal(['a', 'b', 'c'], g:m) " Source an incomplete line continuation line %d _ let lines =<< trim END let k = [ \ END call setline(1, lines) call assert_fails('source', 'E697:') " Source a function with a for loop %d _ let lines =<< trim END let m = [] " test function func! Xtest() for i in range(5, 7) call add(g:m, i) endfor endfunc call Xtest() END call setline(1, lines) source call assert_equal([5, 6, 7], g:m) " Source an empty buffer %d _ source " test for script local functions and variables let lines =<< trim END let s:var1 = 10 func s:F1() let s:var1 += 1 return s:var1 endfunc func s:F2() endfunc let g:ScriptID = expand("<SID>") END call setline(1, lines) source call assert_true(g:ScriptID != '') call assert_true(exists('*' .. g:ScriptID .. 'F1')) call assert_true(exists('*' .. g:ScriptID .. 'F2')) call assert_equal(11, call(g:ScriptID .. 'F1', [])) " the same script ID should be used even if the buffer is sourced more than " once %d _ let lines =<< trim END let g:ScriptID = expand("<SID>") let g:Count += 1 END call setline(1, lines) let g:Count = 0 source call assert_true(g:ScriptID != '') let scid = g:ScriptID source call assert_equal(scid, g:ScriptID) call assert_equal(2, g:Count) source call assert_equal(scid, g:ScriptID) call assert_equal(3, g:Count) " test for the script line number %d _ let lines =<< trim END " comment let g:Slnum1 = expand("<slnum>") let i = 1 + \ 2 + "\ comment \ 3 let g:Slnum2 = expand("<slnum>") END call setline(1, lines) source call assert_equal('2', g:Slnum1) call assert_equal('7', g:Slnum2) " test for retaining the same script number across source calls let lines =<< trim END let g:ScriptID1 = expand("<SID>") let g:Slnum1 = expand("<slnum>") let l =<< trim END let g:Slnum2 = expand("<slnum>") let g:ScriptID2 = expand("<SID>") END new call setline(1, l) source bw! let g:ScriptID3 = expand("<SID>") let g:Slnum3 = expand("<slnum>") END call writefile(lines, 'Xscript', 'D') source Xscript call assert_true(g:ScriptID1 != g:ScriptID2) call assert_equal(g:ScriptID1, g:ScriptID3) call assert_equal('2', g:Slnum1) call assert_equal('1', g:Slnum2) call assert_equal('12', g:Slnum3) " test for sourcing a heredoc %d _ let lines =<< trim END let a = 1 let heredoc =<< trim DATA red green blue DATA let b = 2 END call setline(1, lines) source call assert_equal(['red', ' green', 'blue'], g:heredoc) " test for a while and for statement %d _ let lines =<< trim END let a = 0 let b = 1 while b <= 10 let a += 10 let b += 1 endwhile for i in range(5) let a += 10 endfor END call setline(1, lines) source call assert_equal(150, g:a) " test for sourcing the same buffer multiple times after changing a function %d _ let lines =<< trim END func Xtestfunc() return "one" endfunc END call setline(1, lines) source call assert_equal("one", Xtestfunc()) call setline(2, ' return "two"') source call assert_equal("two", Xtestfunc()) call setline(2, ' return "three"') source call assert_equal("three", Xtestfunc()) delfunc Xtestfunc " test for using try/catch %d _ let lines =<< trim END let Trace = '1' try let a1 = b1 catch let Trace ..= '2' finally let Trace ..= '3' endtry END call setline(1, lines) source call assert_equal("123", g:Trace) " test with the finish command %d _ let lines =<< trim END let g:Color = 'blue' finish let g:Color = 'green' END call setline(1, lines) source call assert_equal('blue', g:Color) " Test for the SourcePre and SourcePost autocmds augroup Xtest au! au SourcePre * let g:XsourcePre=4 \ | let g:XsourcePreFile = expand("<afile>") au SourcePost * let g:XsourcePost=6 \ | let g:XsourcePostFile = expand("<afile>") augroup END %d _ let lines =<< trim END let a = 1 END call setline(1, lines) source call assert_equal(4, g:XsourcePre) call assert_equal(6, g:XsourcePost) call assert_equal(':source buffer=' .. bufnr(), g:XsourcePreFile) call assert_equal(':source buffer=' .. bufnr(), g:XsourcePostFile) augroup Xtest au! augroup END augroup! Xtest %bw! endfunc " Test for sourcing a Vim9 script from the current buffer func Test_source_buffer_vim9() new " test for sourcing a Vim9 script %d _ let lines =<< trim END vim9script # check dict var x: number = 10 def g:Xtestfunc(): number return x enddef END call setline(1, lines) source call assert_equal(10, Xtestfunc()) " test for sourcing a vim9 script with line continuation %d _ let lines =<< trim END vim9script g:Str1 = "hello " .. "world" .. ", how are you?" g:Colors = [ 'red', # comment 'blue' ] g:Dict = { a: 22, # comment b: 33 } # calling a function with line continuation def Sum(...values: list<number>): number var sum: number = 0 for v in values sum += v endfor return sum enddef g:Total1 = Sum(10, 20, 30) var i: number = 0 while i < 10 # while loop i += 1 endwhile g:Count1 = i # for loop g:Count2 = 0 for j in range(10, 20) g:Count2 += i endfor g:Total2 = 10 + 20 - 5 g:Result1 = g:Total2 > 1 ? 'red' : 'blue' g:Str2 = 'x' ->repeat(10) ->trim() ->strpart(4) g:Result2 = g:Dict .a augroup Test au! au BufNewFile Xsubfile g:readFile = 1 | g:readExtra = 2 augroup END g:readFile = 0 g:readExtra = 0 new Xsubfile bwipe! augroup Test au! augroup END END call setline(1, lines) source call assert_equal("hello world, how are you?", g:Str1) call assert_equal(['red', 'blue'], g:Colors) call assert_equal(#{a: 22, b: 33}, g:Dict) call assert_equal(60, g:Total1) call assert_equal(10, g:Count1) call assert_equal(110, g:Count2) call assert_equal(25, g:Total2) call assert_equal('red', g:Result1) call assert_equal('xxxxxx', g:Str2) call assert_equal(22, g:Result2) call assert_equal(1, g:readFile) call assert_equal(2, g:readExtra) " test for sourcing the same buffer multiple times after changing a function %d _ let lines =<< trim END vim9script def g:Xtestfunc(): string return "one" enddef END call setline(1, lines) source call assert_equal("one", Xtestfunc()) call setline(3, ' return "two"') source call assert_equal("two", Xtestfunc()) call setline(3, ' return "three"') source call assert_equal("three", Xtestfunc()) delfunc Xtestfunc " Test for sourcing a range of lines. Make sure the script line number is " correct. %d _ let lines =<< trim END Line 1 Line 2 vim9script def g:Xtestfunc(): string return expand("<sflnum>") enddef Line 3 Line 4 END call setline(1, lines) 3,6source call assert_equal('5', Xtestfunc()) delfunc Xtestfunc " test for sourcing a heredoc %d _ let lines =<< trim END vim9script var a = 1 g:heredoc =<< trim DATA red green blue DATA var b = 2 END call setline(1, lines) source call assert_equal(['red', ' green', 'blue'], g:heredoc) " test for using the :vim9cmd modifier %d _ let lines =<< trim END first line g:Math = { pi: 3.12, e: 2.71828 } g:Editors = [ 'vim', # comment 'nano' ] last line END call setline(1, lines) vim9cmd :2,10source call assert_equal(#{pi: 3.12, e: 2.71828}, g:Math) call assert_equal(['vim', 'nano'], g:Editors) " '<,'> range before the cmd modifier works unlet g:Math unlet g:Editors exe "normal 6GV4j:vim9cmd source\<CR>" call assert_equal(['vim', 'nano'], g:Editors) unlet g:Editors " test for using try/catch %d _ let lines =<< trim END vim9script g:Trace = '1' try a1 = b1 catch g:Trace ..= '2' finally g:Trace ..= '3' endtry END call setline(1, lines) source call assert_equal('123', g:Trace) " test with the finish command %d _ let lines =<< trim END vim9script g:Color = 'red' finish g:Color = 'blue' END call setline(1, lines) source call assert_equal('red', g:Color) " test for ++clear argument to clear all the functions/variables %d _ let lines =<< trim END g:ScriptVarFound = exists("color") g:MyFuncFound = exists('*Myfunc') if g:MyFuncFound finish endif var color = 'blue' def Myfunc() enddef END call setline(1, lines) vim9cmd source call assert_false(g:MyFuncFound) call assert_false(g:ScriptVarFound) vim9cmd source call assert_true(g:MyFuncFound) call assert_true(g:ScriptVarFound) vim9cmd source ++clear call assert_false(g:MyFuncFound) call assert_false(g:ScriptVarFound) vim9cmd source ++clear call assert_false(g:MyFuncFound) call assert_false(g:ScriptVarFound) call assert_fails('vim9cmd source ++clearx', 'E475:') call assert_fails('vim9cmd source ++abcde', 'E484:') %bw! endfunc func Test_source_buffer_long_line() " This was reading past the end of the line. new norm300gr0 so bwipe! let lines =<< trim END new norm 10a0000000000ΓΈ00000000000 norm i0000000000000000000 silent! so END call writefile(lines, 'Xtest.vim', 'D') source Xtest.vim bwipe! endfunc func Test_source_buffer_with_NUL_char() " This was trying to use a line below the buffer. let lines =<< trim END if !exists('g:loaded') let g:loaded = 1 source endif END " Can't have a NL in heredoc let lines += ["silent! vim9 echo [0 \<NL> ? 'a' : 'b']"] call writefile(lines, 'XsourceNul', 'D') edit XsourceNul source bwipe! endfunc " vim: shiftwidth=2 sts=2 expandtab