diff src/testdir/test_normal.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 7eaf61a67d18
children 33d680d372aa
line wrap: on
line diff
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -448,110 +448,122 @@ func Test_opfunc_callback()
     let g:OpFuncArgs = [a:val, a:type]
   endfunc
 
-  " Test for using a function()
-  set opfunc=function('MyopFunc',\ [11])
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([11, 'char'], g:OpFuncArgs)
-
-  " Using a funcref variable to set 'operatorfunc'
-  let Fn = function('MyopFunc', [12])
-  let &opfunc = Fn
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([12, 'char'], g:OpFuncArgs)
-
-  " Using a string(funcref_variable) to set 'operatorfunc'
-  let Fn = function('MyopFunc', [13])
-  let &operatorfunc = string(Fn)
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([13, 'char'], g:OpFuncArgs)
-
-  " Test for using a funcref()
-  set operatorfunc=funcref('MyopFunc',\ [14])
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([14, 'char'], g:OpFuncArgs)
-
-  " Using a funcref variable to set 'operatorfunc'
-  let Fn = funcref('MyopFunc', [15])
-  let &opfunc = Fn
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([15, 'char'], g:OpFuncArgs)
-
-  " Using a string(funcref_variable) to set 'operatorfunc'
-  let Fn = funcref('MyopFunc', [16])
-  let &opfunc = string(Fn)
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([16, 'char'], g:OpFuncArgs)
-
-  " Test for using a lambda function using set
-  set opfunc={a\ ->\ MyopFunc(17,\ a)}
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([17, 'char'], g:OpFuncArgs)
-
-  " Test for using a lambda function using let
-  let &opfunc = {a -> MyopFunc(18, a)}
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([18, 'char'], g:OpFuncArgs)
-
-  " Set 'operatorfunc' to a string(lambda expression)
-  let &opfunc = '{a -> MyopFunc(19, a)}'
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([19, 'char'], g:OpFuncArgs)
-
-  " Set 'operatorfunc' to a variable with a lambda expression
-  let Lambda = {a -> MyopFunc(20, a)}
-  let &opfunc = Lambda
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([20, 'char'], g:OpFuncArgs)
-
-  " Set 'operatorfunc' to a string(variable with a lambda expression)
-  let Lambda = {a -> MyopFunc(21, a)}
-  let &opfunc = string(Lambda)
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([21, 'char'], g:OpFuncArgs)
-
-  " Try to use 'operatorfunc' after the function is deleted
-  func TmpOpFunc(type)
-    let g:OpFuncArgs = [22, a:type]
-  endfunc
-  let &opfunc = function('TmpOpFunc')
-  delfunc TmpOpFunc
-  call test_garbagecollect_now()
-  let g:OpFuncArgs = []
-  call assert_fails('normal! g@l', 'E117:')
-  call assert_equal([], g:OpFuncArgs)
-
-  " Try to use a function with two arguments for 'operatorfunc'
-  func! MyopFunc2(x, y)
-    let g:OpFuncArgs = [a:x, a:y]
-  endfunc
-  set opfunc=MyopFunc2
-  let g:OpFuncArgs = []
-  call assert_fails('normal! g@l', 'E119:')
-  call assert_equal([], g:OpFuncArgs)
-
-  " Try to use a lambda function with two arguments for 'operatorfunc'
-  let &opfunc = {a, b -> MyopFunc(23, b)}
-  let g:OpFuncArgs = []
-  call assert_fails('normal! g@l', 'E119:')
-  call assert_equal([], g:OpFuncArgs)
-
-  " Test for clearing the 'operatorfunc' option
-  set opfunc=''
-  set opfunc&
-
-  call assert_fails("set opfunc=function('abc')", "E700:")
-  call assert_fails("set opfunc=funcref('abc')", "E700:")
+  let lines =<< trim END
+    #" Test for using a function()
+    set opfunc=function('g:MyopFunc',\ [10])
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([10, 'char'], g:OpFuncArgs)
+
+    #" Using a funcref variable to set 'operatorfunc'
+    VAR Fn = function('g:MyopFunc', [11])
+    LET &opfunc = Fn
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([11, 'char'], g:OpFuncArgs)
+
+    #" Using a string(funcref_variable) to set 'operatorfunc'
+    LET Fn = function('g:MyopFunc', [12])
+    LET &operatorfunc = string(Fn)
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([12, 'char'], g:OpFuncArgs)
+
+    #" Test for using a funcref()
+    set operatorfunc=funcref('g:MyopFunc',\ [13])
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([13, 'char'], g:OpFuncArgs)
+
+    #" Using a funcref variable to set 'operatorfunc'
+    LET Fn = funcref('g:MyopFunc', [14])
+    LET &opfunc = Fn
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([14, 'char'], g:OpFuncArgs)
+
+    #" Using a string(funcref_variable) to set 'operatorfunc'
+    LET Fn = funcref('g:MyopFunc', [15])
+    LET &opfunc = string(Fn)
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([15, 'char'], g:OpFuncArgs)
+
+    #" Test for using a lambda function using set
+    VAR optval = "LSTART a LMIDDLE MyopFunc(16, a) LEND"
+    LET optval = substitute(optval, ' ', '\\ ', 'g')
+    exe "set opfunc=" .. optval
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([16, 'char'], g:OpFuncArgs)
+
+    #" Test for using a lambda function using LET
+    LET &opfunc = LSTART a LMIDDLE MyopFunc(17, a) LEND
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([17, 'char'], g:OpFuncArgs)
+
+    #" Set 'operatorfunc' to a string(lambda expression)
+    LET &opfunc = 'LSTART a LMIDDLE MyopFunc(18, a) LEND'
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([18, 'char'], g:OpFuncArgs)
+
+    #" Set 'operatorfunc' to a variable with a lambda expression
+    VAR Lambda = LSTART a LMIDDLE MyopFunc(19, a) LEND
+    LET &opfunc = Lambda
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([19, 'char'], g:OpFuncArgs)
+
+    #" Set 'operatorfunc' to a string(variable with a lambda expression)
+    LET Lambda = LSTART a LMIDDLE MyopFunc(20, a) LEND
+    LET &opfunc = string(Lambda)
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([20, 'char'], g:OpFuncArgs)
+
+    #" Try to use 'operatorfunc' after the function is deleted
+    func g:TmpOpFunc(type)
+      LET g:OpFuncArgs = [21, a:type]
+    endfunc
+    LET &opfunc = function('g:TmpOpFunc')
+    delfunc g:TmpOpFunc
+    call test_garbagecollect_now()
+    LET g:OpFuncArgs = []
+    call assert_fails('normal! g@l', 'E117:')
+    call assert_equal([], g:OpFuncArgs)
+
+    #" Try to use a function with two arguments for 'operatorfunc'
+    func MyopFunc2(x, y)
+      LET g:OpFuncArgs = [a:x, a:y]
+    endfunc
+    set opfunc=MyopFunc2
+    LET g:OpFuncArgs = []
+    call assert_fails('normal! g@l', 'E119:')
+    call assert_equal([], g:OpFuncArgs)
+
+    #" Try to use a lambda function with two arguments for 'operatorfunc'
+    LET &opfunc = LSTART a, b LMIDDLE MyopFunc(22, b) LEND
+    LET g:OpFuncArgs = []
+    call assert_fails('normal! g@l', 'E119:')
+    call assert_equal([], g:OpFuncArgs)
+
+    #" Test for clearing the 'operatorfunc' option
+    set opfunc=''
+    set opfunc&
+    call assert_fails("set opfunc=function('abc')", "E700:")
+    call assert_fails("set opfunc=funcref('abc')", "E700:")
+
+    #" set 'operatorfunc' to a non-existing function
+    LET &opfunc = function('g:MyopFunc', [23])
+    call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
+    call assert_fails("LET &opfunc = function('NonExistingFunc')", 'E700:')
+    LET g:OpFuncArgs = []
+    normal! g@l
+    call assert_equal([23, 'char'], g:OpFuncArgs)
+  END
+  call CheckTransLegacySuccess(lines)
 
   " Using Vim9 lambda expression in legacy context should fail
   set opfunc=(a)\ =>\ MyopFunc(24,\ a)
@@ -559,19 +571,24 @@ func Test_opfunc_callback()
   call assert_fails('normal! g@l', 'E117:')
   call assert_equal([], g:OpFuncArgs)
 
-  " set 'operatorfunc' to a non-existing function
-  let &opfunc = function('MyopFunc', [25])
-  call assert_fails("set opfunc=function('NonExistingFunc')", 'E700:')
-  call assert_fails("let &opfunc = function('NonExistingFunc')", 'E700:')
-  let g:OpFuncArgs = []
-  normal! g@l
-  call assert_equal([25, 'char'], g:OpFuncArgs)
+  " set 'operatorfunc' to a partial with dict. This used to cause a crash.
+  func SetOpFunc()
+    let operator = {'execute': function('OperatorExecute')}
+    let &opfunc = operator.execute
+  endfunc
+  func OperatorExecute(_) dict
+  endfunc
+  call SetOpFunc()
+  call test_garbagecollect_now()
+  set operatorfunc=
+  delfunc SetOpFunc
+  delfunc OperatorExecute
 
   " Vim9 tests
   let lines =<< trim END
     vim9script
 
-    # Test for using function()
+    # Test for using a def function with opfunc
     def g:Vim9opFunc(val: number, type: string): void
       g:OpFuncArgs = [val, type]
     enddef
@@ -579,33 +596,6 @@ func Test_opfunc_callback()
     g:OpFuncArgs = []
     normal! g@l
     assert_equal([60, 'char'], g:OpFuncArgs)
-
-    # Test for using a lambda
-    &opfunc = (a) => Vim9opFunc(61, a)
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([61, 'char'], g:OpFuncArgs)
-
-    # Test for using a string(lambda)
-    &opfunc = '(a) => Vim9opFunc(62, a)'
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([62, 'char'], g:OpFuncArgs)
-
-    # Test for using a variable with a lambda expression
-    var Fn: func = (a) => Vim9opFunc(63, a)
-    &opfunc = Fn
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([63, 'char'], g:OpFuncArgs)
-
-    # Test for using a string(variable with a lambda expression)
-    Fn = (a) => Vim9opFunc(64, a)
-    &opfunc = string(Fn)
-    g:OpFuncArgs = []
-    normal! g@l
-    assert_equal([64, 'char'], g:OpFuncArgs)
-    bw!
   END
   call CheckScriptSuccess(lines)