To: vim_dev@googlegroups.com Subject: Patch 8.2.1105 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1105 Problem: Insufficient test coverage for Lua. Solution: Add tests. (Yegappan Lakshmanan, closes #6368) Fix uncovered memory leak. Avoid unnecessary copy/free. Files: src/if_lua.c, src/testdir/test_lua.vim *** ../vim-8.2.1104/src/if_lua.c 2020-06-28 22:41:23.351349861 +0200 --- src/if_lua.c 2020-07-01 13:49:51.836829466 +0200 *************** *** 936,943 **** typval_T v; luaV_checktypval(L, 3, &v, "setting list item"); clear_tv(&li->li_tv); ! copy_tv(&v, &li->li_tv); ! clear_tv(&v); } } return 0; --- 936,942 ---- typval_T v; luaV_checktypval(L, 3, &v, "setting list item"); clear_tv(&li->li_tv); ! li->li_tv = v; } } return 0; *************** *** 1084,1090 **** dict_T *d = luaV_unbox(L, luaV_Dict, 1); char_u *key = (char_u *) luaL_checkstring(L, 2); dictitem_T *di; ! typval_T v; if (d->dv_lock) luaL_error(L, "dict is locked"); --- 1083,1089 ---- dict_T *d = luaV_unbox(L, luaV_Dict, 1); char_u *key = (char_u *) luaL_checkstring(L, 2); dictitem_T *di; ! typval_T tv; if (d->dv_lock) luaL_error(L, "dict is locked"); *************** *** 1094,1102 **** luaL_error(L, "empty key"); if (!lua_isnil(L, 3)) // read value? { ! luaV_checktypval(L, 3, &v, "setting dict item"); ! if (d->dv_scope == VAR_DEF_SCOPE && v.v_type == VAR_FUNC) luaL_error(L, "cannot assign funcref to builtin scope"); } di = dict_find(d, key, -1); if (di == NULL) // non-existing key? --- 1093,1104 ---- luaL_error(L, "empty key"); if (!lua_isnil(L, 3)) // read value? { ! luaV_checktypval(L, 3, &tv, "setting dict item"); ! if (d->dv_scope == VAR_DEF_SCOPE && tv.v_type == VAR_FUNC) ! { ! clear_tv(&tv); luaL_error(L, "cannot assign funcref to builtin scope"); + } } di = dict_find(d, key, -1); if (di == NULL) // non-existing key? *************** *** 1105,1114 **** --- 1107,1120 ---- return 0; di = dictitem_alloc(key); if (di == NULL) + { + clear_tv(&tv); return 0; + } if (dict_add(d, di) == FAIL) { vim_free(di); + clear_tv(&tv); return 0; } } *************** *** 1121,1130 **** dictitem_free(di); } else ! { ! copy_tv(&v, &di->di_tv); ! clear_tv(&v); ! } return 0; } --- 1127,1133 ---- dictitem_free(di); } else ! di->di_tv = tv; return 0; } *************** *** 1441,1447 **** curwin->w_cursor.lnum -= 1; check_cursor_col(); } ! else check_cursor(); changed_cline_bef_curs(); } invalidate_botline(); --- 1444,1451 ---- curwin->w_cursor.lnum -= 1; check_cursor_col(); } ! else ! check_cursor(); changed_cline_bef_curs(); } invalidate_botline(); *************** *** 1842,1849 **** lua_pushnil(L); return 1; } ! copy_tv(&v, &di->di_tv); ! clear_tv(&v); lua_pop(L, 2); // key copy and value } } --- 1846,1852 ---- lua_pushnil(L); return 1; } ! di->di_tv = v; lua_pop(L, 2); // key copy and value } } *************** *** 2398,2404 **** lua_replace(L, -2); // function -> body for (l = eap->line1; l <= eap->line2; l++) { ! // Check the line number, the command my have deleted lines. if (l > curbuf->b_ml.ml_line_count) break; --- 2401,2407 ---- lua_replace(L, -2); // function -> body for (l = eap->line1; l <= eap->line2; l++) { ! // Check the line number, the command may have deleted lines. if (l > curbuf->b_ml.ml_line_count) break; *** ../vim-8.2.1104/src/testdir/test_lua.vim 2020-06-28 22:41:23.351349861 +0200 --- src/testdir/test_lua.vim 2020-07-01 13:36:39.216161900 +0200 *************** *** 4,9 **** --- 4,18 ---- CheckFeature lua CheckFeature float + let s:luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.') + let s:major = str2nr(s:luaver[0]) + let s:minor = str2nr(s:luaver[1]) + if s:major < 5 || (s:major == 5 && s:minor < 3) + let s:lua_53_or_later = 0 + else + let s:lua_53_or_later = 1 + endif + func TearDown() " Run garbage collection after each test to exercise luaV_setref(). call test_garbagecollect_now() *************** *** 28,33 **** --- 37,59 ---- bwipe! endfunc + func Test_lua_luado() + new + call setline(1, ['one', 'two']) + luado return(linenr) + call assert_equal(['1', '2'], getline(1, '$')) + close! + + " Error cases + call assert_fails('luado string.format()', + \ "[string \"vim chunk\"]:1: bad argument #1 to 'format' (string expected, got no value)") + call assert_fails('luado func()', + \ s:lua_53_or_later + \ ? "[string \"vim chunk\"]:1: attempt to call a nil value (global 'func')" + \ : "[string \"vim chunk\"]:1: attempt to call global 'func' (a nil value)") + call assert_fails('luado error("failed")', "[string \"vim chunk\"]:1: failed") + endfunc + " Test vim.eval() func Test_lua_eval() " lua.eval with a number *************** *** 55,60 **** --- 81,104 ---- call assert_equal('blob', luaeval('vim.type(v)')) call assert_equal(0z00112233.deadbeef, luaeval('v')) + " lua.eval with a float + lua v = vim.eval('3.14') + call assert_equal('number', luaeval('vim.type(v)')) + call assert_equal(3.14, luaeval('v')) + + " lua.eval with a bool + lua v = vim.eval('v:true') + call assert_equal('number', luaeval('vim.type(v)')) + call assert_equal(1, luaeval('v')) + lua v = vim.eval('v:false') + call assert_equal('number', luaeval('vim.type(v)')) + call assert_equal(0, luaeval('v')) + + " lua.eval with a null + lua v = vim.eval('v:null') + call assert_equal('nil', luaeval('vim.type(v)')) + call assert_equal(v:null, luaeval('v')) + call assert_fails('lua v = vim.eval(nil)', \ "[string \"vim chunk\"]:1: bad argument #1 to 'eval' (string expected, got nil)") call assert_fails('lua v = vim.eval(true)', *************** *** 82,87 **** --- 126,138 ---- " Window 3 does not exist so vim.window(3) should return nil call assert_equal('nil', luaeval('tostring(vim.window(3))')) + call assert_fails("let n = luaeval('vim.window().xyz()')", + \ s:lua_53_or_later + \ ? "[string \"luaeval\"]:1: attempt to call a nil value (field 'xyz')" + \ : "[string \"luaeval\"]:1: attempt to call field 'xyz' (a nil value)") + call assert_fails('lua vim.window().xyz = 1', + \ "[string \"vim chunk\"]:1: invalid window property: `xyz'") + %bwipe! endfunc *************** *** 125,130 **** --- 176,190 ---- func Test_lua_call() call assert_equal(has('lua'), luaeval('vim.call("has", "lua")')) call assert_equal(printf("Hello %s", "vim"), luaeval('vim.call("printf", "Hello %s", "vim")')) + + " Error cases + call assert_fails("call luaeval('vim.call(\"min\", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21)')", + \ '[string "luaeval"]:1: Function called with too many arguments') + lua co = coroutine.create(function () print("hi") end) + call assert_fails("call luaeval('vim.call(\"type\", co)')", + \ '[string "luaeval"]:1: lua: cannot convert value') + lua co = nil + call assert_fails("call luaeval('vim.call(\"abc\")')", '[string "luaeval"]:1: lua: call_vim_function failed') endfunc " Test vim.fn.* *************** *** 245,250 **** --- 305,319 ---- lua vim.buffer():insert('4', 10) call assert_equal(['1', '2', '3', '4'], getline(1, '$')) + call assert_equal('4', luaeval('vim.buffer()[4]')) + call assert_equal(v:null, luaeval('vim.buffer()[5]')) + call assert_equal(v:null, luaeval('vim.buffer()[{}]')) + call assert_fails('lua vim.buffer():xyz()', + \ s:lua_53_or_later + \ ? "[string \"vim chunk\"]:1: attempt to call a nil value (method 'xyz')" + \ : "[string \"vim chunk\"]:1: attempt to call method 'xyz' (a nil value)") + call assert_fails('lua vim.buffer()[1] = {}', + \ '[string "vim chunk"]:1: wrong argument to change') bwipe! endfunc *************** *** 252,257 **** --- 321,327 ---- func Test_lua_buffer_delete() new call setline(1, ['1', '2', '3']) + call cursor(3, 1) lua vim.buffer()[2] = nil call assert_equal(['1', '3'], getline(1, '$')) *************** *** 331,340 **** --- 401,426 ---- lua l[6] = nil lua l:insert('first') lua l:insert('xx', 3) + call assert_fails('lua l:insert("xx", -20)', + \ '[string "vim chunk"]:1: invalid position') call assert_equal(['first', 124, 'abc', 'xx', v:true, v:false, v:null, {'a': 1, 'b': 2, 'c': 3}], l) lockvar 1 l call assert_fails('lua l:add("x")', '[string "vim chunk"]:1: list is locked') + call assert_fails('lua l:insert(2)', '[string "vim chunk"]:1: list is locked') + call assert_fails('lua l[9] = 1', '[string "vim chunk"]:1: list is locked') + + unlockvar l + let l = [1, 2] + lua ll = vim.eval('l') + let x = luaeval("ll[3]") + call assert_equal(v:null, x) + call assert_fails('let x = luaeval("ll:xyz(3)")', + \ s:lua_53_or_later + \ ? "[string \"luaeval\"]:1: attempt to call a nil value (method 'xyz')" + \ : "[string \"luaeval\"]:1: attempt to call method 'xyz' (a nil value)") + let y = luaeval("ll[{}]") + call assert_equal(v:null, y) lua l = nil endfunc *************** *** 354,364 **** endfunc func Test_lua_list_table_insert_remove() ! let luaver = split(split(luaeval('_VERSION'), ' ')[1], '\.') ! let major = str2nr(luaver[0]) ! let minor = str2nr(luaver[1]) ! ! if major < 5 || (major == 5 && minor < 3) throw 'Skipped: Lua version < 5.3' endif --- 440,446 ---- endfunc func Test_lua_list_table_insert_remove() ! if !s:lua_53_or_later throw 'Skipped: Lua version < 5.3' endif *************** *** 429,434 **** --- 511,517 ---- call assert_match('^dict: \%(0x\)\?\x\+$', luaeval('tostring(d)')) call assert_equal('abc', luaeval('d[1]')) + call assert_equal(v:null, luaeval('d[22]')) lua d[0] = 124 lua d[4] = nil *************** *** 436,441 **** --- 519,541 ---- lockvar 1 d call assert_fails('lua d[6] = 1', '[string "vim chunk"]:1: dict is locked') + unlockvar d + + " Error case + lua d = {} + lua d[''] = 10 + call assert_fails("let t = luaeval('vim.dict(d)')", + \ '[string "luaeval"]:1: table has empty key') + let d = {} + lua x = vim.eval('d') + call assert_fails("lua x[''] = 10", '[string "vim chunk"]:1: empty key') + lua x['a'] = nil + call assert_equal({}, d) + + " cannot assign funcrefs in the global scope + lua x = vim.eval('g:') + call assert_fails("lua x['min'] = vim.funcref('max')", + \ '[string "vim chunk"]:1: cannot assign funcref to builtin scope') lua d = nil endfunc *************** *** 493,498 **** --- 593,620 ---- call assert_fails('lua b:add(true)', '[string "vim chunk"]:1: string expected, got boolean') call assert_fails('lua b:add({})', '[string "vim chunk"]:1: string expected, got table') lua b = nil + + let b = 0z0102 + lua lb = vim.eval('b') + let n = luaeval('lb[1]') + call assert_equal(2, n) + let n = luaeval('lb[6]') + call assert_equal(v:null, n) + call assert_fails('let x = luaeval("lb:xyz(3)")', + \ s:lua_53_or_later + \ ? "[string \"luaeval\"]:1: attempt to call a nil value (method 'xyz')" + \ : "[string \"luaeval\"]:1: attempt to call method 'xyz' (a nil value)") + let y = luaeval("lb[{}]") + call assert_equal(v:null, y) + + lockvar b + call assert_fails('lua lb[1] = 2', '[string "vim chunk"]:1: blob is locked') + call assert_fails('lua lb:add("12")', '[string "vim chunk"]:1: blob is locked') + + " Error cases + lua t = {} + call assert_fails('lua b = vim.blob(t)', + \ '[string "vim chunk"]:1: string expected, got table') endfunc func Test_lua_funcref() *************** *** 506,511 **** --- 628,644 ---- lua msg = vim.funcref"tr"(msg, "|", " ") call assert_equal("funcref test OK", luaeval('msg')) + " Error cases + call assert_fails('lua f1 = vim.funcref("")', + \ '[string "vim chunk"]:1: invalid function name: ') + call assert_fails('lua f1 = vim.funcref("10")', + \ '[string "vim chunk"]:1: invalid function name: 10') + let fname = test_null_string() + call assert_fails('lua f1 = vim.funcref(fname)', + \ "[string \"vim chunk\"]:1: bad argument #1 to 'funcref' (string expected, got nil)") + call assert_fails('lua vim.funcref("abc")()', + \ '[string "vim chunk"]:1: cannot call funcref') + " dict funcref function Mylen() dict return len(self.data) *************** *** 619,624 **** --- 752,761 ---- \ '[string "luaeval"]:1: attempt to perform arithmetic on a nil value') call assert_fails("call luaeval(']')", \ "[string \"luaeval\"]:1: unexpected symbol near ']'") + lua co = coroutine.create(function () print("hi") end) + call assert_fails('let i = luaeval("co")', 'luaeval: cannot convert value') + lua co = nil + call assert_fails('let m = luaeval("{}")', 'luaeval: cannot convert value') endfunc " Test :luafile foo.lua *************** *** 664,669 **** --- 801,817 ---- bwipe! endfunc + " Test for dealing with strings containing newlines and null character + func Test_lua_string_with_newline() + let x = execute('lua print("Hello\nWorld")') + call assert_equal("\nHello\nWorld", x) + new + lua k = vim.buffer(vim.eval('bufnr()')) + lua k:insert("Hello\0World", 0) + call assert_equal(["Hello\nWorld", ''], getline(1, '$')) + close! + endfunc + func Test_lua_set_cursor() " Check that setting the cursor position works. new *** ../vim-8.2.1104/src/version.c 2020-07-01 13:15:21.414343245 +0200 --- src/version.c 2020-07-01 13:22:51.899924673 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1105, /**/ -- "I've been teaching myself to play the piano for about 5 years and now write most of my songs on it, mainly because I can never find any paper." Jeff Lynne, ELO's greatest hits /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///