mirror of
https://github.com/neovim/neovim.git
synced 2026-07-16 06:10:39 +00:00
vim-patch:9.1.1943: Memory leak with :breakadd expr
Problem: Memory leak with :breakadd expr
Solution: Free debug_oldval and debug_newval before assigning to them.
Verify the existing (though confusing) :breakadd expr behavior
(zeertzjq).
It seems that :breakadd expr doesn't work as documented at all. This PR
only fixes the memory leak. The tests are for the existing behavior.
closes: vim/vim#18844
a474de64df
This commit is contained in:
@@ -828,17 +828,21 @@ static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *g
|
|||||||
typval_T *const tv = eval_expr_no_emsg(bp);
|
typval_T *const tv = eval_expr_no_emsg(bp);
|
||||||
if (tv != NULL) {
|
if (tv != NULL) {
|
||||||
if (bp->dbg_val == NULL) {
|
if (bp->dbg_val == NULL) {
|
||||||
|
xfree(debug_oldval);
|
||||||
debug_oldval = typval_tostring(NULL, true);
|
debug_oldval = typval_tostring(NULL, true);
|
||||||
bp->dbg_val = tv;
|
bp->dbg_val = tv;
|
||||||
|
xfree(debug_newval);
|
||||||
debug_newval = typval_tostring(bp->dbg_val, true);
|
debug_newval = typval_tostring(bp->dbg_val, true);
|
||||||
line = true;
|
line = true;
|
||||||
} else {
|
} else {
|
||||||
if (typval_compare(tv, bp->dbg_val, EXPR_IS, false) == OK
|
if (typval_compare(tv, bp->dbg_val, EXPR_IS, false) == OK
|
||||||
&& tv->vval.v_number == false) {
|
&& tv->vval.v_number == false) {
|
||||||
line = true;
|
line = true;
|
||||||
|
xfree(debug_oldval);
|
||||||
debug_oldval = typval_tostring(bp->dbg_val, true);
|
debug_oldval = typval_tostring(bp->dbg_val, true);
|
||||||
// Need to evaluate again, typval_compare() overwrites "tv".
|
// Need to evaluate again, typval_compare() overwrites "tv".
|
||||||
typval_T *const v = eval_expr_no_emsg(bp);
|
typval_T *const v = eval_expr_no_emsg(bp);
|
||||||
|
xfree(debug_newval);
|
||||||
debug_newval = typval_tostring(v, true);
|
debug_newval = typval_tostring(v, true);
|
||||||
tv_free(bp->dbg_val);
|
tv_free(bp->dbg_val);
|
||||||
bp->dbg_val = v;
|
bp->dbg_val = v;
|
||||||
@@ -846,7 +850,9 @@ static linenr_T debuggy_find(bool file, char *fname, linenr_T after, garray_T *g
|
|||||||
tv_free(tv);
|
tv_free(tv);
|
||||||
}
|
}
|
||||||
} else if (bp->dbg_val != NULL) {
|
} else if (bp->dbg_val != NULL) {
|
||||||
|
xfree(debug_oldval);
|
||||||
debug_oldval = typval_tostring(bp->dbg_val, true);
|
debug_oldval = typval_tostring(bp->dbg_val, true);
|
||||||
|
xfree(debug_newval);
|
||||||
debug_newval = typval_tostring(NULL, true);
|
debug_newval = typval_tostring(NULL, true);
|
||||||
tv_free(bp->dbg_val);
|
tv_free(bp->dbg_val);
|
||||||
bp->dbg_val = NULL;
|
bp->dbg_val = NULL;
|
||||||
|
|||||||
@@ -13,64 +13,112 @@ describe('debugger', function()
|
|||||||
local screen
|
local screen
|
||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
screen = Screen.new(999, 10)
|
screen = Screen.new(999, 7)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- oldtest: Test_Debugger_breakadd_expr()
|
-- oldtest: Test_Debugger_breakadd_expr()
|
||||||
|
-- This doesn't seem to work as documented. The breakpoint is not
|
||||||
|
-- triggered until the next function call.
|
||||||
it(':breakadd expr', function()
|
it(':breakadd expr', function()
|
||||||
write_file('XdebugBreakExpr.vim', 'let g:Xtest_var += 1')
|
write_file(
|
||||||
|
'XbreakExpr.vim',
|
||||||
|
[[
|
||||||
|
func Foo()
|
||||||
|
eval 1
|
||||||
|
eval 2
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
let g:Xtest_var += 1
|
||||||
|
call Foo()
|
||||||
|
let g:Xtest_var += 1
|
||||||
|
call Foo()]]
|
||||||
|
)
|
||||||
finally(function()
|
finally(function()
|
||||||
os.remove('XdebugBreakExpr.vim')
|
os.remove('XbreakExpr.vim')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
command('edit XdebugBreakExpr.vim')
|
command('edit XbreakExpr.vim')
|
||||||
command(':let g:Xtest_var = 10')
|
command(':let g:Xtest_var = 10')
|
||||||
command(':breakadd expr g:Xtest_var')
|
command(':breakadd expr g:Xtest_var')
|
||||||
feed(':source %<CR>')
|
local initial_screen = [[
|
||||||
screen:expect {
|
^func Foo(){MATCH: *}|
|
||||||
grid = [[
|
eval 1{MATCH: *}|
|
||||||
^let g:Xtest_var += 1{MATCH: *}|
|
eval 2{MATCH: *}|
|
||||||
{1:~{MATCH: *}}|*8
|
endfunc{MATCH: *}|
|
||||||
:source %{MATCH: *}|
|
{MATCH: *}|
|
||||||
]],
|
|
||||||
}
|
|
||||||
feed(':source %<CR>')
|
|
||||||
screen:expect {
|
|
||||||
grid = [[
|
|
||||||
let g:Xtest_var += 1{MATCH: *}|
|
let g:Xtest_var += 1{MATCH: *}|
|
||||||
{1:~{MATCH: *}}|
|
{MATCH: *}|
|
||||||
{3:{MATCH: *}}|
|
]]
|
||||||
Breakpoint in "{MATCH:.*}XdebugBreakExpr.vim" line 1{MATCH: *}|
|
screen:expect(initial_screen)
|
||||||
|
|
||||||
|
feed(':source %<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
Breakpoint in "Foo" line 1{MATCH: *}|
|
||||||
Entering Debug mode. Type "cont" to continue.{MATCH: *}|
|
Entering Debug mode. Type "cont" to continue.{MATCH: *}|
|
||||||
Oldval = "10"{MATCH: *}|
|
Oldval = "10"{MATCH: *}|
|
||||||
Newval = "11"{MATCH: *}|
|
Newval = "11"{MATCH: *}|
|
||||||
{MATCH:.*}XdebugBreakExpr.vim{MATCH: *}|
|
{MATCH:.*}XbreakExpr.vim[7]..function Foo{MATCH: *}|
|
||||||
line 1: let g:Xtest_var += 1{MATCH: *}|
|
line 1: eval 1{MATCH: *}|
|
||||||
>^{MATCH: *}|
|
>^{MATCH: *}|
|
||||||
]],
|
]])
|
||||||
}
|
|
||||||
feed('cont<CR>')
|
feed('cont<CR>')
|
||||||
screen:expect {
|
screen:expect([[
|
||||||
grid = [[
|
>cont{MATCH: *}|
|
||||||
^let g:Xtest_var += 1{MATCH: *}|
|
Breakpoint in "Foo" line 1{MATCH: *}|
|
||||||
{1:~{MATCH: *}}|*8
|
|
||||||
{MATCH: *}|
|
|
||||||
]],
|
|
||||||
}
|
|
||||||
feed(':source %<CR>')
|
|
||||||
screen:expect {
|
|
||||||
grid = [[
|
|
||||||
let g:Xtest_var += 1{MATCH: *}|
|
|
||||||
{1:~{MATCH: *}}|
|
|
||||||
{3:{MATCH: *}}|
|
|
||||||
Breakpoint in "{MATCH:.*}XdebugBreakExpr.vim" line 1{MATCH: *}|
|
|
||||||
Entering Debug mode. Type "cont" to continue.{MATCH: *}|
|
|
||||||
Oldval = "11"{MATCH: *}|
|
Oldval = "11"{MATCH: *}|
|
||||||
Newval = "12"{MATCH: *}|
|
Newval = "12"{MATCH: *}|
|
||||||
{MATCH:.*}XdebugBreakExpr.vim{MATCH: *}|
|
{MATCH:.*}XbreakExpr.vim[9]..function Foo{MATCH: *}|
|
||||||
line 1: let g:Xtest_var += 1{MATCH: *}|
|
line 1: eval 1{MATCH: *}|
|
||||||
>^{MATCH: *}|
|
>^{MATCH: *}|
|
||||||
]],
|
]])
|
||||||
}
|
feed('cont<CR>')
|
||||||
|
screen:expect(initial_screen)
|
||||||
|
|
||||||
|
-- Check the behavior without the g: prefix.
|
||||||
|
-- The Oldval and Newval don't look right here.
|
||||||
|
command(':breakdel *')
|
||||||
|
command(':breakadd expr Xtest_var')
|
||||||
|
feed(':source %<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
Breakpoint in "Foo" line 1{MATCH: *}|
|
||||||
|
Entering Debug mode. Type "cont" to continue.{MATCH: *}|
|
||||||
|
Oldval = "13"{MATCH: *}|
|
||||||
|
Newval = "(does not exist)"{MATCH: *}|
|
||||||
|
{MATCH:.*}XbreakExpr.vim[7]..function Foo{MATCH: *}|
|
||||||
|
line 1: eval 1{MATCH: *}|
|
||||||
|
>^{MATCH: *}|
|
||||||
|
]])
|
||||||
|
feed('cont<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
{MATCH:.*}XbreakExpr.vim[7]..function Foo{MATCH: *}|
|
||||||
|
line 1: eval 1{MATCH: *}|
|
||||||
|
>cont{MATCH: *}|
|
||||||
|
Breakpoint in "Foo" line 2{MATCH: *}|
|
||||||
|
{MATCH:.*}XbreakExpr.vim[7]..function Foo{MATCH: *}|
|
||||||
|
line 2: eval 2{MATCH: *}|
|
||||||
|
>^{MATCH: *}|
|
||||||
|
]])
|
||||||
|
feed('cont<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
>cont{MATCH: *}|
|
||||||
|
Breakpoint in "Foo" line 1{MATCH: *}|
|
||||||
|
Oldval = "14"{MATCH: *}|
|
||||||
|
Newval = "(does not exist)"{MATCH: *}|
|
||||||
|
{MATCH:.*}XbreakExpr.vim[9]..function Foo{MATCH: *}|
|
||||||
|
line 1: eval 1{MATCH: *}|
|
||||||
|
>^{MATCH: *}|
|
||||||
|
]])
|
||||||
|
feed('cont<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
{MATCH:.*}XbreakExpr.vim[9]..function Foo{MATCH: *}|
|
||||||
|
line 1: eval 1{MATCH: *}|
|
||||||
|
>cont{MATCH: *}|
|
||||||
|
Breakpoint in "Foo" line 2{MATCH: *}|
|
||||||
|
{MATCH:.*}XbreakExpr.vim[9]..function Foo{MATCH: *}|
|
||||||
|
line 2: eval 2{MATCH: *}|
|
||||||
|
>^{MATCH: *}|
|
||||||
|
]])
|
||||||
|
feed('cont<CR>')
|
||||||
|
screen:expect(initial_screen)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -364,35 +364,74 @@ func Test_Debugger_breakadd()
|
|||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" Test for expression breakpoint set using ":breakadd expr <expr>"
|
" Test for expression breakpoint set using ":breakadd expr <expr>"
|
||||||
|
" FIXME: This doesn't seem to work as documented. The breakpoint is not
|
||||||
|
" triggered until the next function call.
|
||||||
func Test_Debugger_breakadd_expr()
|
func Test_Debugger_breakadd_expr()
|
||||||
CheckRunVimInTerminal
|
CheckRunVimInTerminal
|
||||||
CheckCWD
|
CheckCWD
|
||||||
|
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
|
func Foo()
|
||||||
|
eval 1
|
||||||
|
eval 2
|
||||||
|
endfunc
|
||||||
|
|
||||||
let g:Xtest_var += 1
|
let g:Xtest_var += 1
|
||||||
|
call Foo()
|
||||||
|
let g:Xtest_var += 1
|
||||||
|
call Foo()
|
||||||
END
|
END
|
||||||
call writefile(lines, 'XdebugBreakExpr.vim', 'D')
|
call writefile(lines, 'XbreakExpr.vim', 'D')
|
||||||
|
|
||||||
" Start Vim in a terminal
|
" Start Vim in a terminal
|
||||||
let buf = RunVimInTerminal('XdebugBreakExpr.vim', {})
|
let buf = RunVimInTerminal('XbreakExpr.vim', {})
|
||||||
call s:RunDbgCmd(buf, ':let g:Xtest_var = 10')
|
call s:RunDbgCmd(buf, ':let g:Xtest_var = 10')
|
||||||
call s:RunDbgCmd(buf, ':breakadd expr g:Xtest_var')
|
call s:RunDbgCmd(buf, ':breakadd expr g:Xtest_var')
|
||||||
call s:RunDbgCmd(buf, ':source %')
|
|
||||||
let expected =<< trim eval END
|
let expected =<< trim eval END
|
||||||
Oldval = "10"
|
Oldval = "10"
|
||||||
Newval = "11"
|
Newval = "11"
|
||||||
{fnamemodify('XdebugBreakExpr.vim', ':p')}
|
{fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
|
||||||
line 1: let g:Xtest_var += 1
|
line 1: eval 1
|
||||||
END
|
END
|
||||||
call s:RunDbgCmd(buf, ':source %', expected)
|
call s:RunDbgCmd(buf, ':source %', expected)
|
||||||
call s:RunDbgCmd(buf, 'cont')
|
|
||||||
let expected =<< trim eval END
|
let expected =<< trim eval END
|
||||||
Oldval = "11"
|
Oldval = "11"
|
||||||
Newval = "12"
|
Newval = "12"
|
||||||
{fnamemodify('XdebugBreakExpr.vim', ':p')}
|
{fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
|
||||||
line 1: let g:Xtest_var += 1
|
line 1: eval 1
|
||||||
|
END
|
||||||
|
call s:RunDbgCmd(buf, 'cont', expected)
|
||||||
|
call s:RunDbgCmd(buf, 'cont')
|
||||||
|
|
||||||
|
" Check the behavior without the g: prefix.
|
||||||
|
" FIXME: The Oldval and Newval don't look right here.
|
||||||
|
call s:RunDbgCmd(buf, ':breakdel *')
|
||||||
|
call s:RunDbgCmd(buf, ':breakadd expr Xtest_var')
|
||||||
|
let expected =<< trim eval END
|
||||||
|
Oldval = "13"
|
||||||
|
Newval = "(does not exist)"
|
||||||
|
{fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
|
||||||
|
line 1: eval 1
|
||||||
END
|
END
|
||||||
call s:RunDbgCmd(buf, ':source %', expected)
|
call s:RunDbgCmd(buf, ':source %', expected)
|
||||||
|
let expected =<< trim eval END
|
||||||
|
{fnamemodify('XbreakExpr.vim', ':p')}[7]..function Foo
|
||||||
|
line 2: eval 2
|
||||||
|
END
|
||||||
|
call s:RunDbgCmd(buf, 'cont', expected)
|
||||||
|
let expected =<< trim eval END
|
||||||
|
Oldval = "14"
|
||||||
|
Newval = "(does not exist)"
|
||||||
|
{fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
|
||||||
|
line 1: eval 1
|
||||||
|
END
|
||||||
|
call s:RunDbgCmd(buf, 'cont', expected)
|
||||||
|
let expected =<< trim eval END
|
||||||
|
{fnamemodify('XbreakExpr.vim', ':p')}[9]..function Foo
|
||||||
|
line 2: eval 2
|
||||||
|
END
|
||||||
|
call s:RunDbgCmd(buf, 'cont', expected)
|
||||||
|
call s:RunDbgCmd(buf, 'cont')
|
||||||
|
|
||||||
call StopVimInTerminal(buf)
|
call StopVimInTerminal(buf)
|
||||||
endfunc
|
endfunc
|
||||||
|
|||||||
Reference in New Issue
Block a user