# HG changeset patch # User Christian Brabandt # Date 1695834010 -7200 # Node ID 88fa56e88cd720ef7713be396d78d66a280f0d69 # Parent a9c7a9a8ccd2d4324e504d8875bae4b1bd6f866c patch 9.0.1944: Vim9: function instruction pointer invalidated Commit: https://github.com/vim/vim/commit/a76fbe6e00249d25fa2cfaf80ddaa360f0e1711d Author: Yegappan Lakshmanan Date: Wed Sep 27 18:51:43 2023 +0200 patch 9.0.1944: Vim9: function instruction pointer invalidated Problem: Vim9: function instruction pointer invalidated Solution: Use the funcref index instead of the instruction pointer closes: #13178 closes: #13196 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -45,7 +45,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isn int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type); int generate_NEWLIST(cctx_T *cctx, int count, int use_null); int generate_NEWDICT(cctx_T *cctx, int count, int use_null); -int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int fi, isn_T **isnp); +int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int fi, int *isn_idx); int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name); int generate_DEF(cctx_T *cctx, char_u *name, size_t len); int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where); diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -4636,6 +4636,56 @@ def Test_free_type_before_use() v9.CheckScriptSuccess(lines) enddef +" The following complicated script used to cause an internal error (E340) +" because the funcref instruction memory was referenced after the instruction +" memory was reallocated (Github issue #13178) +def Test_refer_funcref_instr_after_realloc() + var lines =<< trim END + vim9script + def A(d: bool) + var e = abs(0) + var f = &emoji + &emoji = true + if ['', '', '']->index('xxx') == 0 + eval 0 + 0 + endif + if &filetype == 'xxx' + var g = abs(0) + while g > 0 + if getline(g) == '' + break + endif + --g + endwhile + if g == 0 + return + endif + if d + feedkeys($'{g}G') + g = abs(0) + endif + var h = abs(0) + var i = abs(0) + var j = abs(0) + while j < 0 + if abs(0) < h && getline(j) != '' + break + endif + ++j + endwhile + feedkeys($'{g}G{j}G') + return + endif + def B() + enddef + def C() + enddef + enddef + A(false) + END + v9.CheckScriptSuccess(lines) +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1944, +/**/ 1943, /**/ 1942, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1029,7 +1029,7 @@ compile_nested_function(exarg_T *eap, cc ufunc_T *ufunc; int r = FAIL; compiletype_T compile_type; - isn_T *funcref_isn = NULL; + int funcref_isn_idx = -1; lvar_T *lvar = NULL; if (eap->forceit) @@ -1148,7 +1148,7 @@ compile_nested_function(exarg_T *eap, cc ASSIGN_CONST, ufunc->uf_func_type); if (lvar == NULL) goto theend; - if (generate_FUNCREF(cctx, ufunc, NULL, 0, &funcref_isn) == FAIL) + if (generate_FUNCREF(cctx, ufunc, NULL, 0, &funcref_isn_idx) == FAIL) goto theend; r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); } @@ -1178,8 +1178,12 @@ compile_nested_function(exarg_T *eap, cc #endif // If a FUNCREF instruction was generated, set the index after compiling. - if (funcref_isn != NULL && ufunc->uf_def_status == UF_COMPILED) + if (funcref_isn_idx != -1 && ufunc->uf_def_status == UF_COMPILED) + { + isn_T *funcref_isn = ((isn_T *)cctx->ctx_instr.ga_data) + + funcref_isn_idx; funcref_isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx; + } theend: vim_free(lambda_name); diff --git a/src/vim9instr.c b/src/vim9instr.c --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -1378,7 +1378,9 @@ generate_NEWDICT(cctx_T *cctx, int count * Generate an ISN_FUNCREF instruction. * For "obj.Method" "cl" is the class of the object (can be an interface or a * base class) and "fi" the index of the method on that class. - * "isnp" is set to the instruction, so that fr_dfunc_idx can be set later. + * "isn_idx" is set to the index of the instruction, so that fr_dfunc_idx can + * be set later. The index is used instead of a pointer to the instruction + * because the instruction memory can be reallocated. */ int generate_FUNCREF( @@ -1386,7 +1388,7 @@ generate_FUNCREF( ufunc_T *ufunc, class_T *cl, int fi, - isn_T **isnp) + int *isn_idx) { isn_T *isn; type_T *type; @@ -1397,8 +1399,9 @@ generate_FUNCREF( RETURN_OK_IF_SKIP(cctx); if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL) return FAIL; - if (isnp != NULL) - *isnp = isn; + if (isn_idx != NULL) + // save the index of the new instruction + *isn_idx = cctx->ctx_instr.ga_len - 1; has_vars = get_loop_var_info(cctx, &loopinfo); if (ufunc->uf_def_status == UF_NOT_COMPILED || has_vars || cl != NULL) @@ -1419,7 +1422,7 @@ generate_FUNCREF( extra->fre_func_name = vim_strsave(ufunc->uf_name); if (ufunc->uf_def_status != UF_NOT_COMPILED && cl == NULL) { - if (isnp == NULL && ufunc->uf_def_status == UF_TO_BE_COMPILED) + if (isn_idx == NULL && ufunc->uf_def_status == UF_TO_BE_COMPILED) // compile the function now, we need the uf_dfunc_idx value (void)compile_def_function(ufunc, FALSE, CT_NONE, NULL); isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;