To: vim_dev@googlegroups.com Subject: Patch 8.2.4870 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4870 Problem: Vim9: expression in :substitute is not compiled. Solution: Use an INSTR instruction if possible. (closes #10334) Files: src/evalfunc.c, src/regexp.c, src/vim9execute.c, src/vim9expr.c, src/testdir/test_vim9_builtin.vim, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.4869/src/evalfunc.c 2022-05-04 15:40:16.032317666 +0100 --- src/evalfunc.c 2022-05-05 13:45:18.912728496 +0100 *************** *** 9966,9972 **** pat = tv_get_string_buf_chk(&argvars[1], patbuf); flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); ! if (argvars[2].v_type == VAR_FUNC || argvars[2].v_type == VAR_PARTIAL) expr = &argvars[2]; else sub = tv_get_string_buf_chk(&argvars[2], subbuf); --- 9966,9974 ---- pat = tv_get_string_buf_chk(&argvars[1], patbuf); flg = tv_get_string_buf_chk(&argvars[3], flagsbuf); ! if (argvars[2].v_type == VAR_FUNC ! || argvars[2].v_type == VAR_PARTIAL ! || argvars[2].v_type == VAR_INSTR) expr = &argvars[2]; else sub = tv_get_string_buf_chk(&argvars[2], subbuf); *** ../vim-8.2.4869/src/regexp.c 2022-04-23 11:03:55.093928150 +0100 --- src/regexp.c 2022-05-05 13:41:25.313028876 +0100 *************** *** 2004,2009 **** --- 2004,2013 ---- funcexe.fe_partial = partial; call_func(s, -1, &rettv, 1, argv, &funcexe); } + else if (expr->v_type == VAR_INSTR) + { + exe_typval_instr(expr, &rettv); + } if (matchList.sl_list.lv_len > 0) // fill_submatch_list() was called clear_submatch_list(&matchList); *** ../vim-8.2.4869/src/vim9execute.c 2022-04-28 12:00:45.109439279 +0100 --- src/vim9execute.c 2022-05-05 13:41:25.317028871 +0100 *************** *** 5010,5015 **** --- 5010,5019 ---- int save_iidx = ectx->ec_iidx; int res; + // Initialize rettv so that it is safe for caller to invoke clear_tv(rettv) + // even when the compilation fails. + rettv->v_type = VAR_UNKNOWN; + ectx->ec_instr = tv->vval.v_instr->instr_instr; res = exec_instructions(ectx); if (res == OK) *** ../vim-8.2.4869/src/vim9expr.c 2022-04-25 12:43:15.179819208 +0100 --- src/vim9expr.c 2022-05-05 13:52:05.048252933 +0100 *************** *** 567,578 **** /* * Compile a string in a ISN_PUSHS instruction into an ISN_INSTR. * Returns FAIL if compilation fails. */ static int ! compile_string(isn_T *isn, cctx_T *cctx) { ! char_u *s = isn->isn_arg.string; garray_T save_ga = cctx->ctx_instr; int expr_res; int trailing_error; --- 567,579 ---- /* * Compile a string in a ISN_PUSHS instruction into an ISN_INSTR. + * "str_offset" is the number of leading bytes to skip from the string. * Returns FAIL if compilation fails. */ static int ! compile_string(isn_T *isn, cctx_T *cctx, int str_offset) { ! char_u *s = isn->isn_arg.string + str_offset; garray_T save_ga = cctx->ctx_instr; int expr_res; int trailing_error; *************** *** 616,626 **** } /* * Compile the argument expressions. * "arg" points to just after the "(" and is advanced to after the ")" */ static int ! compile_arguments(char_u **arg, cctx_T *cctx, int *argcount, int is_searchpair) { char_u *p = *arg; char_u *whitep = *arg; --- 617,640 ---- } /* + * List of special functions for "compile_arguments". + */ + typedef enum { + CA_NOT_SPECIAL, + CA_SEARCHPAIR, // {skip} in searchpair() and searchpairpos() + CA_SUBSTITUTE, // {sub} in substitute(), when prefixed with \= + } ca_special_T; + + /* * Compile the argument expressions. * "arg" points to just after the "(" and is advanced to after the ")" */ static int ! compile_arguments( ! char_u **arg, ! cctx_T *cctx, ! int *argcount, ! ca_special_T special_fn) { char_u *p = *arg; char_u *whitep = *arg; *************** *** 647,660 **** return FAIL; ++*argcount; ! if (is_searchpair && *argcount == 5 && cctx->ctx_instr.ga_len == instr_count + 1) { isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count; // {skip} argument of searchpair() can be compiled if not empty if (isn->isn_type == ISN_PUSHS && *isn->isn_arg.string != NUL) ! compile_string(isn, cctx); } if (*p != ',' && *skipwhite(p) == ',') --- 661,685 ---- return FAIL; ++*argcount; ! if (special_fn == CA_SEARCHPAIR && *argcount == 5 && cctx->ctx_instr.ga_len == instr_count + 1) { isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count; // {skip} argument of searchpair() can be compiled if not empty if (isn->isn_type == ISN_PUSHS && *isn->isn_arg.string != NUL) ! compile_string(isn, cctx, 0); ! } ! else if (special_fn == CA_SUBSTITUTE && *argcount == 3 ! && cctx->ctx_instr.ga_len == instr_count + 1) ! { ! isn_T *isn = ((isn_T *)cctx->ctx_instr.ga_data) + instr_count; ! ! // {sub} argument of substitute() can be compiled if it starts ! // with \= ! if (isn->isn_type == ISN_PUSHS && isn->isn_arg.string[0] == '\\' ! && isn->isn_arg.string[1] == '=') ! compile_string(isn, cctx, 2); } if (*p != ',' && *skipwhite(p) == ',') *************** *** 706,712 **** int res = FAIL; int is_autoload; int has_g_namespace; ! int is_searchpair; imported_T *import; if (varlen >= sizeof(namebuf)) --- 731,737 ---- int res = FAIL; int is_autoload; int has_g_namespace; ! ca_special_T special_fn; imported_T *import; if (varlen >= sizeof(namebuf)) *************** *** 776,788 **** // We handle the "skip" argument of searchpair() and searchpairpos() // differently. ! is_searchpair = (varlen == 6 && STRNCMP(*arg, "search", 6) == 0) ! || (varlen == 9 && STRNCMP(*arg, "searchpos", 9) == 0) ! || (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0) ! || (varlen == 13 && STRNCMP(*arg, "searchpairpos", 13) == 0); *arg = skipwhite(*arg + varlen + 1); ! if (compile_arguments(arg, cctx, &argcount, is_searchpair) == FAIL) goto theend; is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL; --- 801,818 ---- // We handle the "skip" argument of searchpair() and searchpairpos() // differently. ! if ((varlen == 6 && STRNCMP(*arg, "search", 6) == 0) ! || (varlen == 9 && STRNCMP(*arg, "searchpos", 9) == 0) ! || (varlen == 10 && STRNCMP(*arg, "searchpair", 10) == 0) ! || (varlen == 13 && STRNCMP(*arg, "searchpairpos", 13) == 0)) ! special_fn = CA_SEARCHPAIR; ! else if (varlen == 10 && STRNCMP(*arg, "substitute", 10) == 0) ! special_fn = CA_SUBSTITUTE; ! else ! special_fn = CA_NOT_SPECIAL; *arg = skipwhite(*arg + varlen + 1); ! if (compile_arguments(arg, cctx, &argcount, special_fn) == FAIL) goto theend; is_autoload = vim_strchr(name, AUTOLOAD_CHAR) != NULL; *************** *** 1717,1723 **** type = get_type_on_stack(cctx, 0); *arg = skipwhite(p + 1); ! if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL) return FAIL; --- 1747,1753 ---- type = get_type_on_stack(cctx, 0); *arg = skipwhite(p + 1); ! if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL) return FAIL; if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL) return FAIL; *************** *** 1848,1854 **** expr_isn_end = cctx->ctx_instr.ga_len; *arg = skipwhite(*arg + 1); ! if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; // Move the instructions for the arguments to before the --- 1878,1885 ---- expr_isn_end = cctx->ctx_instr.ga_len; *arg = skipwhite(*arg + 1); ! if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) ! == FAIL) return FAIL; // Move the instructions for the arguments to before the *** ../vim-8.2.4869/src/testdir/test_vim9_builtin.vim 2022-05-04 15:40:16.032317666 +0100 --- src/testdir/test_vim9_builtin.vim 2022-05-05 13:41:25.317028871 +0100 *************** *** 4078,4083 **** --- 4078,4088 ---- v9.CheckDefAndScriptFailure(['substitute("a", 2, "1", "d")'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2']) v9.CheckDefAndScriptFailure(['substitute("a", "b", "1", 4)'], ['E1013: Argument 4: type mismatch, expected string but got number', 'E1174: String required for argument 4']) substitute('', '', '', '')->assert_equal('') + + var lines =<< trim END + assert_equal("4", substitute("3", '\d', '\=str2nr(submatch(0)) + 1', 'g')) + END + v9.CheckDefAndScriptSuccess(lines) enddef def Test_swapinfo() *** ../vim-8.2.4869/src/testdir/test_vim9_disassemble.vim 2022-04-28 12:00:45.109439279 +0100 --- src/testdir/test_vim9_disassemble.vim 2022-05-05 13:47:17.920584343 +0100 *************** *** 187,192 **** --- 187,212 ---- enddef + def s:SubstituteExpr() + substitute('a', 'b', '\=123', 'g') + enddef + + def Test_disassemble_substitute_expr() + var res = execute('disass s:SubstituteExpr') + assert_match('\d*_SubstituteExpr.*' .. + 'substitute(''a'', ''b'', ''\\=123'', ''g'')\_s*' .. + '\d PUSHS "a"\_s*' .. + '\d PUSHS "b"\_s*' .. + '\d INSTR\_s*' .. + ' 0 PUSHNR 123\_s*' .. + ' -------------\_s*' .. + '\d PUSHS "g"\_s*' .. + '\d BCALL substitute(argc 4)\_s*' .. + '\d DROP\_s*' .. + '\d RETURN void', + res) + enddef + def s:RedirVar() var result: string redir =>> result *** ../vim-8.2.4869/src/version.c 2022-05-05 12:20:24.359225499 +0100 --- src/version.c 2022-05-05 13:43:29.392866010 +0100 *************** *** 748,749 **** --- 748,751 ---- { /* Add new patch number below this line */ + /**/ + 4870, /**/ -- hundred-and-one symptoms of being an internet addict: 98. The Alta Vista administrators ask you what sites are missing in their index files. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///