# HG changeset patch # User Bram Moolenaar # Date 1640379603 -3600 # Node ID 9c9b8d95b05fa261a8a97632dafb27baeb689c23 # Parent 7d4b0da6a26b4ea88668521bc93858251b86b325 patch 8.2.3889: duplicate code for translating script-local function name Commit: https://github.com/vim/vim/commit/e7f4abd38b6e05100c699900c8f87281e363beb2 Author: Yegappan Lakshmanan Date: Fri Dec 24 20:47:38 2021 +0000 patch 8.2.3889: duplicate code for translating script-local function name Problem: Duplicate code for translating script-local function name. Solution: Move the code to get_scriptlocal_funcname(). (Yegappan Lakshmanan, closes #9393) diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4050,22 +4050,11 @@ common_function(typval_T *argvars, typva list_T *list = NULL; if (STRNCMP(s, "s:", 2) == 0 || STRNCMP(s, "", 5) == 0) - { - char sid_buf[25]; - int off = *s == 's' ? 2 : 5; - // Expand s: and into nr_, so that the function can // also be called from another script. Using trans_function_name() // would also work, but some plugins depend on the name being // printable text. - sprintf(sid_buf, "%ld_", (long)current_sctx.sc_sid); - name = alloc(STRLEN(sid_buf) + STRLEN(s + off) + 1); - if (name != NULL) - { - STRCPY(name, sid_buf); - STRCAT(name, s + off); - } - } + name = get_scriptlocal_funcname(s); else name = vim_strsave(s); diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -4450,7 +4450,18 @@ get_callback(typval_T *arg) r = FAIL; else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) { - // Note that we don't make a copy of the string. + if (arg->v_type == VAR_STRING) + { + char_u *name; + + name = get_scriptlocal_funcname(arg->vval.v_string); + if (name != NULL) + { + vim_free(arg->vval.v_string); + arg->vval.v_string = name; + } + } + res.cb_name = arg->vval.v_string; func_ref(res.cb_name); } diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -7205,33 +7205,8 @@ option_set_callback_func(char_u *optval // Lambda expression or a funcref tv = eval_expr(optval, NULL); else - { // treat everything else as a function name string - - // Function name starting with "s:" are supported only in a vimscript - // context. - if (STRNCMP(optval, "s:", 2) == 0) - { - char sid_buf[25]; - char_u *funcname; - - if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) - { - emsg(_(e_using_sid_not_in_script_context)); - return FAIL; - } - // Expand s: prefix into nr_ - sprintf(sid_buf, "%ld_", (long)current_sctx.sc_sid); - funcname = alloc(STRLEN(sid_buf) + STRLEN(optval + 2) + 1); - if (funcname == NULL) - return FAIL; - STRCPY(funcname, sid_buf); - STRCAT(funcname, optval + 2); - tv = alloc_string_tv(funcname); - } - else - tv = alloc_string_tv(vim_strsave(optval)); - } + tv = alloc_string_tv(vim_strsave(optval)); if (tv == NULL) return FAIL; diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -35,6 +35,7 @@ int call_func(char_u *funcname, int len, char_u *printable_func_name(ufunc_T *fp); char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type); char_u *untrans_function_name(char_u *name); +char_u *get_scriptlocal_funcname(char_u *funcname); char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi); void list_functions(regmatch_T *regmatch); ufunc_T *define_function(exarg_T *eap, char_u *name_arg); diff --git a/src/testdir/test_expr.vim b/src/testdir/test_expr.vim --- a/src/testdir/test_expr.vim +++ b/src/testdir/test_expr.vim @@ -639,6 +639,21 @@ func Test_funcref() call assert_fails('echo test_null_function()->funcref()', 'E475: Invalid argument: NULL') endfunc +" Test for calling function() and funcref() outside of a Vim script context. +func Test_function_outside_script() + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim') + call RunVim([], [], "-c \"echo function('s:abc')\" -S Xverify.vim") + call assert_match('E81: Using not in a', readfile('Xtest.out')[0]) + call RunVim([], [], "-c \"echo funcref('s:abc')\" -S Xverify.vim") + call assert_match('E81: Using not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + call delete('Xverify.vim') +endfunc + func Test_setmatches() let lines =<< trim END hi def link 1 Comment diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -642,6 +642,18 @@ func Test_opfunc_callback() END call CheckScriptSuccess(lines) + " setting 'opfunc' to a script local function outside of a script context + " should fail + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim') + call RunVim([], [], "-c \"set opfunc=s:abc\" -S Xverify.vim") + call assert_match('E81: Using not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + call delete('Xverify.vim') + " cleanup set opfunc& delfunc OpFunc1 diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3876,6 +3876,46 @@ untrans_function_name(char_u *name) } /* + * If the 'funcname' starts with "s:" or "", then expands it to the + * current script ID and returns the expanded function name. The caller should + * free the returned name. If not called from a script context or the function + * name doesn't start with these prefixes, then returns NULL. + * This doesn't check whether the script-local function exists or not. + */ + char_u * +get_scriptlocal_funcname(char_u *funcname) +{ + char sid_buf[25]; + int off; + char_u *newname; + + if (funcname == NULL) + return NULL; + + if (STRNCMP(funcname, "s:", 2) != 0 + && STRNCMP(funcname, "", 5) != 0) + // The function name is not a script-local function name + return NULL; + + if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) + { + emsg(_(e_using_sid_not_in_script_context)); + return NULL; + } + // Expand s: prefix into nr_ + vim_snprintf(sid_buf, sizeof(sid_buf), "%ld_", + (long)current_sctx.sc_sid); + off = *funcname == 's' ? 2 : 5; + newname = alloc(STRLEN(sid_buf) + STRLEN(funcname + off) + 1); + if (newname == NULL) + return NULL; + STRCPY(newname, sid_buf); + STRCAT(newname, funcname + off); + + return newname; +} + +/* * Call trans_function_name(), except that a lambda is returned as-is. * Returns the name in allocated memory. */ diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -750,6 +750,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3889, +/**/ 3888, /**/ 3887,