To: vim_dev@googlegroups.com Subject: Patch 8.2.1865 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1865 Problem: Vim9: add() does not check type of argument. Solution: Inline the add() call. (closes #7160) Files: src/vim9.h, src/vim9compile.c, src/vim9execute.c, src/errors.h, src/testdir/test_vim9_func.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.1864/src/vim9.h 2020-10-03 22:51:42.890813408 +0200 --- src/vim9.h 2020-10-19 18:14:27.963998794 +0200 *************** *** 96,103 **** ISN_ENDTRY, // take entry off from ec_trystack // more expression operations ! ISN_ADDLIST, ! ISN_ADDBLOB, // operation with two arguments; isn_arg.op.op_type is exptype_T ISN_OPNR, --- 96,103 ---- ISN_ENDTRY, // take entry off from ec_trystack // more expression operations ! ISN_ADDLIST, // add two lists ! ISN_ADDBLOB, // add two blobs // operation with two arguments; isn_arg.op.op_type is exptype_T ISN_OPNR, *************** *** 120,125 **** --- 120,126 ---- ISN_CONCAT, ISN_STRINDEX, // [expr] string index ISN_STRSLICE, // [expr:expr] string slice + ISN_LISTAPPEND, // append to a list, like add() ISN_LISTINDEX, // [expr] list index ISN_LISTSLICE, // [expr:expr] list slice ISN_ANYINDEX, // [expr] runtime index *** ../vim-8.2.1864/src/vim9compile.c 2020-10-19 16:07:37.193322741 +0200 --- src/vim9compile.c 2020-10-19 18:35:28.577344915 +0200 *************** *** 1495,1500 **** --- 1495,1526 ---- } /* + * Generate an ISN_LISTAPPEND instruction. Works like add(). + * Argument count is already checked. + */ + static int + generate_LISTAPPEND(cctx_T *cctx) + { + garray_T *stack = &cctx->ctx_type_stack; + type_T *list_type; + type_T *item_type; + type_T *expected; + + // Caller already checked that list_type is a list. + list_type = ((type_T **)stack->ga_data)[stack->ga_len - 2]; + item_type = ((type_T **)stack->ga_data)[stack->ga_len - 1]; + expected = list_type->tt_member; + if (need_type(item_type, expected, -1, cctx, FALSE, FALSE) == FAIL) + return FAIL; + + if (generate_instr(cctx, ISN_LISTAPPEND) == NULL) + return FAIL; + + --stack->ga_len; // drop the argument + return OK; + } + + /* * Generate an ISN_DCALL or ISN_UCALL instruction. * Return FAIL if the number of arguments is wrong. */ *************** *** 2537,2543 **** // builtin function idx = find_internal_func(name); if (idx >= 0) ! res = generate_BCALL(cctx, idx, argcount, argcount_init == 1); else semsg(_(e_unknownfunc), namebuf); goto theend; --- 2563,2587 ---- // builtin function idx = find_internal_func(name); if (idx >= 0) ! { ! if (STRCMP(name, "add") == 0 && argcount == 2) ! { ! garray_T *stack = &cctx->ctx_type_stack; ! type_T *type = ((type_T **)stack->ga_data)[ ! stack->ga_len - 2]; ! ! // TODO: also check for VAR_BLOB ! if (type->tt_type == VAR_LIST) ! { ! // inline "add(list, item)" so that the type can be checked ! res = generate_LISTAPPEND(cctx); ! idx = -1; ! } ! } ! ! if (idx >= 0) ! res = generate_BCALL(cctx, idx, argcount, argcount_init == 1); ! } else semsg(_(e_unknownfunc), namebuf); goto theend; *************** *** 7656,7661 **** --- 7700,7706 ---- case ISN_FOR: case ISN_GETITEM: case ISN_JUMP: + case ISN_LISTAPPEND: case ISN_LISTINDEX: case ISN_LISTSLICE: case ISN_LOAD: *** ../vim-8.2.1864/src/vim9execute.c 2020-10-17 22:58:17.913317608 +0200 --- src/vim9execute.c 2020-10-19 18:59:06.617794199 +0200 *************** *** 2095,2100 **** --- 2095,2101 ---- || *skipwhite(tv->vval.v_string) == NUL) { vim_free(tv->vval.v_string); + SOURCING_LNUM = iptr->isn_lnum; emsg(_(e_throw_with_empty_string)); goto failed; } *************** *** 2282,2287 **** --- 2283,2289 ---- typval_T *tv1 = STACK_TV_BOT(-2); typval_T *tv2 = STACK_TV_BOT(-1); + // add two lists or blobs if (iptr->isn_type == ISN_ADDLIST) eval_addlist(tv1, tv2); else *************** *** 2291,2296 **** --- 2293,2317 ---- } break; + case ISN_LISTAPPEND: + { + typval_T *tv1 = STACK_TV_BOT(-2); + typval_T *tv2 = STACK_TV_BOT(-1); + list_T *l = tv1->vval.v_list; + + // add an item to a list + if (l == NULL) + { + SOURCING_LNUM = iptr->isn_lnum; + emsg(_(e_cannot_add_to_null_list)); + goto on_error; + } + if (list_append_tv(l, tv2) == FAIL) + goto failed; + --ectx.ec_stack.ga_len; + } + break; + // Computation with two arguments of unknown type case ISN_OPANY: { *************** *** 3410,3415 **** --- 3431,3437 ---- case ISN_CONCAT: smsg("%4d CONCAT", current); break; case ISN_STRINDEX: smsg("%4d STRINDEX", current); break; case ISN_STRSLICE: smsg("%4d STRSLICE", current); break; + case ISN_LISTAPPEND: smsg("%4d LISTAPPEND", current); break; case ISN_LISTINDEX: smsg("%4d LISTINDEX", current); break; case ISN_LISTSLICE: smsg("%4d LISTSLICE", current); break; case ISN_ANYINDEX: smsg("%4d ANYINDEX", current); break; *** ../vim-8.2.1864/src/errors.h 2020-10-16 23:16:43.459258200 +0200 --- src/errors.h 2020-10-19 18:36:55.749148167 +0200 *************** *** 282,285 **** --- 282,287 ---- INIT(= N_("E1128: } without {")); EXTERN char e_throw_with_empty_string[] INIT(= N_("E1129: Throw with empty string")); + EXTERN char e_cannot_add_to_null_list[] + INIT(= N_("E1130: Cannot add to null list")); #endif *** ../vim-8.2.1864/src/testdir/test_vim9_func.vim 2020-10-17 19:29:47.526935795 +0200 --- src/testdir/test_vim9_func.vim 2020-10-19 18:58:15.089922991 +0200 *************** *** 1772,1777 **** --- 1772,1795 ---- list2str(l, true)->assert_equal(s) enddef + def Test_list_add() + var l: list # defaults to empty list + add(l, 9) + assert_equal([9], l) + + var lines =<< trim END + var l: list + add(l, "x") + END + CheckDefFailure(lines, 'E1012:', 2) + + lines =<< trim END + var l: list = test_null_list() + add(l, 123) + END + CheckDefExecFailure(lines, 'E1130:', 2) + enddef + def SID(): number return expand('') ->matchstr('\zs\d\+\ze_$') *** ../vim-8.2.1864/src/testdir/test_vim9_disassemble.vim 2020-10-10 14:12:58.024646147 +0200 --- src/testdir/test_vim9_disassemble.vim 2020-10-19 18:53:39.646630997 +0200 *************** *** 273,278 **** --- 273,306 ---- res) enddef + def s:ListAdd() + var l: list = [] + add(l, 123) + add(l, g:aNumber) + enddef + + def Test_disassemble_list_add() + var res = execute('disass s:ListAdd') + assert_match('\d*_ListAdd\_s*' .. + 'var l: list = []\_s*' .. + '\d NEWLIST size 0\_s*' .. + '\d STORE $0\_s*' .. + 'add(l, 123)\_s*' .. + '\d LOAD $0\_s*' .. + '\d PUSHNR 123\_s*' .. + '\d LISTAPPEND\_s*' .. + '\d DROP\_s*' .. + 'add(l, g:aNumber)\_s*' .. + '\d LOAD $0\_s*' .. + '\d\+ LOADG g:aNumber\_s*' .. + '\d\+ CHECKTYPE number stack\[-1\]\_s*' .. + '\d\+ LISTAPPEND\_s*' .. + '\d\+ DROP\_s*' .. + '\d\+ PUSHNR 0\_s*' .. + '\d\+ RETURN', + res) + enddef + def s:ScriptFuncUnlet() g:somevar = "value" unlet g:somevar *************** *** 803,809 **** 'res->add(i)\_s*' .. '\d LOAD $0\_s*' .. '\d LOAD $2\_s*' .. ! '\d\+ BCALL add(argc 2)\_s*' .. '\d\+ DROP\_s*' .. 'endfor\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. --- 831,837 ---- 'res->add(i)\_s*' .. '\d LOAD $0\_s*' .. '\d LOAD $2\_s*' .. ! '\d\+ LISTAPPEND\_s*' .. '\d\+ DROP\_s*' .. 'endfor\_s*' .. '\d\+ JUMP -> \d\+\_s*' .. *** ../vim-8.2.1864/src/version.c 2020-10-19 16:07:37.193322741 +0200 --- src/version.c 2020-10-19 18:24:34.546489681 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 1865, /**/ -- The primary purpose of the DATA statement is to give names to constants; instead of referring to pi as 3.141592653589793 at every appearance, the variable PI can be given that value with a DATA statement and used instead of the longer form of the constant. This also simplifies modifying the program, should the value of pi change. -- FORTRAN manual for Xerox Computers /// 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 ///