diff src/testdir/test_iminsert.vim @ 26518:13ba00ef7687 v8.2.3788

patch 8.2.3788: lambda for option that is a function may be freed Commit: https://github.com/vim/vim/commit/6ae8fae8696623b527c7fb22567f6a3705b2f0dd Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Sun Dec 12 16:26:44 2021 +0000 patch 8.2.3788: lambda for option that is a function may be freed Problem: Lambda for option that is a function may be garbage collected. Solution: Set a reference in the funcref. (Yegappan Lakshmanan, closes #9330)
author Bram Moolenaar <Bram@vim.org>
date Sun, 12 Dec 2021 17:30:04 +0100
parents 65b4109a4297
children 33d680d372aa
line wrap: on
line diff
--- a/src/testdir/test_iminsert.vim
+++ b/src/testdir/test_iminsert.vim
@@ -119,83 +119,142 @@ func Test_imactivatefunc_imstatusfunc_ca
     let g:IMstatusfunc_called += 1
     return 1
   endfunc
-  let g:IMactivatefunc_called = 0
-  let g:IMstatusfunc_called = 0
   set iminsert=2
 
-  " Test for using a function()
-  set imactivatefunc=function('IMactivatefunc1')
-  set imstatusfunc=function('IMstatusfunc1')
-  normal! i
+  let lines =<< trim END
+    LET g:IMactivatefunc_called = 0
+    LET g:IMstatusfunc_called = 0
+
+    #" Test for using a function()
+    set imactivatefunc=function('g:IMactivatefunc1')
+    set imstatusfunc=function('g:IMstatusfunc1')
+    normal! i
 
-  " Using a funcref variable to set 'completefunc'
-  let Fn1 = function('IMactivatefunc1')
-  let &imactivatefunc = Fn1
-  let Fn2 = function('IMstatusfunc1')
-  let &imstatusfunc = Fn2
-  normal! i
+    #" Using a funcref variable to set 'completefunc'
+    VAR Fn1 = function('g:IMactivatefunc1')
+    LET &imactivatefunc = Fn1
+    VAR Fn2 = function('g:IMstatusfunc1')
+    LET &imstatusfunc = Fn2
+    normal! i
 
-  " Using a string(funcref variable) to set 'completefunc'
-  let &imactivatefunc = string(Fn1)
-  let &imstatusfunc = string(Fn2)
-  normal! i
+    #" Using a string(funcref variable) to set 'completefunc'
+    LET &imactivatefunc = string(Fn1)
+    LET &imstatusfunc = string(Fn2)
+    normal! i
+
+    #" Test for using a funcref()
+    set imactivatefunc=funcref('g:IMactivatefunc1')
+    set imstatusfunc=funcref('g:IMstatusfunc1')
+    normal! i
 
-  " Test for using a funcref()
-  set imactivatefunc=funcref('IMactivatefunc1')
-  set imstatusfunc=funcref('IMstatusfunc1')
-  normal! i
+    #" Using a funcref variable to set 'imactivatefunc'
+    LET Fn1 = funcref('g:IMactivatefunc1')
+    LET &imactivatefunc = Fn1
+    LET Fn2 = funcref('g:IMstatusfunc1')
+    LET &imstatusfunc = Fn2
+    normal! i
+
+    #" Using a string(funcref variable) to set 'imactivatefunc'
+    LET &imactivatefunc = string(Fn1)
+    LET &imstatusfunc = string(Fn2)
+    normal! i
 
-  " Using a funcref variable to set 'imactivatefunc'
-  let Fn1 = funcref('IMactivatefunc1')
-  let &imactivatefunc = Fn1
-  let Fn2 = funcref('IMstatusfunc1')
-  let &imstatusfunc = Fn2
-  normal! i
+    #" Test for using a lambda function
+    VAR optval = "LSTART a LMIDDLE IMactivatefunc1(a) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set imactivatefunc=" .. optval
+    LET optval = "LSTART LMIDDLE IMstatusfunc1() LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set imstatusfunc=" .. optval
+    normal! i
 
-  " Using a string(funcref variable) to set 'imactivatefunc'
-  let &imactivatefunc = string(Fn1)
-  let &imstatusfunc = string(Fn2)
-  normal! i
+    #" Set 'imactivatefunc' and 'imstatusfunc' to a lambda expression
+    LET &imactivatefunc = LSTART a LMIDDLE IMactivatefunc1(a) LEND
+    LET &imstatusfunc = LSTART LMIDDLE IMstatusfunc1() LEND
+    normal! i
+
+    #" Set 'imactivatefunc' and 'imstatusfunc' to a string(lambda expression)
+    LET &imactivatefunc = 'LSTART a LMIDDLE IMactivatefunc1(a) LEND'
+    LET &imstatusfunc = 'LSTART LMIDDLE IMstatusfunc1() LEND'
+    normal! i
+
+    #" Set 'imactivatefunc' 'imstatusfunc' to a variable with a lambda
+    #" expression
+    VAR Lambda1 = LSTART a LMIDDLE IMactivatefunc1(a) LEND
+    VAR Lambda2 = LSTART LMIDDLE IMstatusfunc1() LEND
+    LET &imactivatefunc = Lambda1
+    LET &imstatusfunc = Lambda2
+    normal! i
 
-  " Test for using a lambda function
-  set imactivatefunc={a\ ->\ IMactivatefunc1(a)}
-  set imstatusfunc={\ ->\ IMstatusfunc1()}
-  normal! i
+    #" Set 'imactivatefunc' 'imstatusfunc' to a string(variable with a lambda
+    #" expression)
+    LET &imactivatefunc = string(Lambda1)
+    LET &imstatusfunc = string(Lambda2)
+    normal! i
+
+    #" Test for clearing the 'completefunc' option
+    set imactivatefunc='' imstatusfunc=''
+    set imactivatefunc& imstatusfunc&
 
-  " Set 'imactivatefunc' and 'imstatusfunc' to a lambda expression
-  let &imactivatefunc = {a -> IMactivatefunc1(a)}
-  let &imstatusfunc = { -> IMstatusfunc1()}
-  normal! i
+    set imactivatefunc=g:IMactivatefunc1
+    set imstatusfunc=g:IMstatusfunc1
+    call assert_fails("set imactivatefunc=function('abc')", "E700:")
+    call assert_fails("set imstatusfunc=function('abc')", "E700:")
+    call assert_fails("set imactivatefunc=funcref('abc')", "E700:")
+    call assert_fails("set imstatusfunc=funcref('abc')", "E700:")
+    call assert_fails("LET &imstatusfunc = function('abc')", "E700:")
+    call assert_fails("LET &imactivatefunc = function('abc')", "E700:")
+    normal! i
 
-  " Set 'imactivatefunc' and 'imstatusfunc' to a string(lambda expression)
-  let &imactivatefunc = '{a -> IMactivatefunc1(a)}'
-  let &imstatusfunc = '{ -> IMstatusfunc1()}'
-  normal! i
+    #" set 'imactivatefunc' and 'imstatusfunc' to a non-existing function
+    set imactivatefunc=IMactivatefunc1
+    set imstatusfunc=IMstatusfunc1
+    call assert_fails("set imactivatefunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("set imstatusfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &imactivatefunc = function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &imstatusfunc = function('NonExistingFunc')", 'E700:')
+    normal! i
+
+    call assert_equal(13, g:IMactivatefunc_called)
+    call assert_equal(26, g:IMstatusfunc_called)
+  END
+  call CheckLegacyAndVim9Success(lines)
 
-  " Set 'imactivatefunc' 'imstatusfunc' to a variable with a lambda expression
-  let Lambda1 = {a -> IMactivatefunc1(a)}
-  let Lambda2 = { -> IMstatusfunc1()}
-  let &imactivatefunc = Lambda1
-  let &imstatusfunc = Lambda2
-  normal! i
+  " Using Vim9 lambda expression in legacy context should fail
+  set imactivatefunc=(a)\ =>\ IMactivatefunc1(a)
+  set imstatusfunc=IMstatusfunc1
+  call assert_fails('normal! i', 'E117:')
+  set imactivatefunc=IMactivatefunc1
+  set imstatusfunc=()\ =>\ IMstatusfunc1(a)
+  call assert_fails('normal! i', 'E117:')
 
-  " Set 'imactivatefunc' 'imstatusfunc' to a string(variable with a lambda
-  " expression)
-  let &imactivatefunc = string(Lambda1)
-  let &imstatusfunc = string(Lambda2)
-  normal! i
-
-  " Test for clearing the 'completefunc' option
-  set imactivatefunc='' imstatusfunc=''
-  set imactivatefunc& imstatusfunc&
-
-  call assert_fails("set imactivatefunc=function('abc')", "E700:")
-  call assert_fails("set imstatusfunc=function('abc')", "E700:")
-  call assert_fails("set imactivatefunc=funcref('abc')", "E700:")
-  call assert_fails("set imstatusfunc=funcref('abc')", "E700:")
-
-  call assert_equal(11, g:IMactivatefunc_called)
-  call assert_equal(22, g:IMstatusfunc_called)
+  " set 'imactivatefunc' and 'imstatusfunc' to a partial with dict. This used
+  " to cause a crash.
+  func SetIMFunc()
+    let params1 = {'activate': function('g:DictActivateFunc')}
+    let params2 = {'status': function('g:DictStatusFunc')}
+    let &imactivatefunc = params1.activate
+    let &imstatusfunc = params2.status
+  endfunc
+  func g:DictActivateFunc(_) dict
+  endfunc
+  func g:DictStatusFunc(_) dict
+  endfunc
+  call SetIMFunc()
+  new
+  call SetIMFunc()
+  bw
+  call test_garbagecollect_now()
+  new
+  set imactivatefunc=
+  set imstatusfunc=
+  wincmd w
+  set imactivatefunc=
+  set imstatusfunc=
+  :%bw!
+  delfunc g:DictActivateFunc
+  delfunc g:DictStatusFunc
+  delfunc SetIMFunc
 
   " Vim9 tests
   let lines =<< trim END
@@ -217,59 +276,12 @@ func Test_imactivatefunc_imstatusfunc_ca
     set imstatusfunc=function('IMstatusfunc1')
     normal! i
 
-    # Test for using a lambda
-    &imactivatefunc = '(a) => IMactivatefunc1(a)'
-    &imstatusfunc = '() => IMstatusfunc1()'
-    normal! i
-
-    # Test for using a variable with a lambda expression
-    var Fn1: func = (active) => {
-           g:IMactivatefunc_called += 1
-           return 1
-        }
-    var Fn2: func = () => {
-           g:IMstatusfunc_called += 1
-           return 1
-        }
-    &imactivatefunc = Fn1
-    &imstatusfunc = Fn2
-    normal! i
-
-    # Test for using a string(variable with a lambda expression)
-    &imactivatefunc = string(Fn1)
-    &imstatusfunc = string(Fn2)
-    normal! i
-
-    assert_equal(4, g:IMactivatefunc_called)
-    assert_equal(8, g:IMstatusfunc_called)
-
     set iminsert=0
     set imactivatefunc=
     set imstatusfunc=
   END
   call CheckScriptSuccess(lines)
 
-  " Using Vim9 lambda expression in legacy context should fail
-  set imactivatefunc=(a)\ =>\ IMactivatefunc1(a)
-  set imstatusfunc=IMstatusfunc1
-  call assert_fails('normal! i', 'E117:')
-  set imactivatefunc=IMactivatefunc1
-  set imstatusfunc=()\ =>\ IMstatusfunc1(a)
-  call assert_fails('normal! i', 'E117:')
-
-  " set 'imactivatefunc' and 'imstatusfunc' to a non-existing function
-  set imactivatefunc=IMactivatefunc1
-  set imstatusfunc=IMstatusfunc1
-  call assert_fails("set imactivatefunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("set imstatusfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &imactivatefunc = function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &imstatusfunc = function('NonExistingFunc')", 'E700:')
-  let g:IMactivatefunc_called = 0
-  let g:IMstatusfunc_called = 0
-  normal! i
-  call assert_equal(2, g:IMactivatefunc_called)
-  call assert_equal(2, g:IMstatusfunc_called)
-
   " cleanup
   delfunc IMactivatefunc1
   delfunc IMstatusfunc1